Skip to content

Commit aedfa37

Browse files
authored
Merge pull request #45 from NorESMhub/feature/new_timeseries
global_mean_timeseries: obs support and optional full-range time series
2 parents a319cdd + fe82eca commit aedfa37

8 files changed

Lines changed: 443 additions & 81 deletions

CONFIG_REFERENCE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ Settings shared by all runs.
111111
| `central_longitude` | optional | `180` | Center longitude for lat/lon maps. |
112112
| `num_procs` | optional | `1` | Processors for parallel steps. `"*"` = all on the node. Does **not** affect SE regrid (that runs serially). |
113113
| `redo_plot` | optional | `false` | `true` = remake plots even if they exist. |
114+
| `global_mean_ts_full_range` | optional | `false` | `true` = also generate time series over the **full available year range** (2-D variables only) for the `global_mean_timeseries` drift plot, so its model line(s) span the whole run rather than just the `start_year``end_year` climo window. Only takes effect when `global_mean_timeseries` is in `plotting_scripts`; applies to the test case(s) and the baseline (skipped for obs, which has no time series). Cases whose full range already equals the climo range are reused (no extra pass). The full-range series are written to a sibling `s<start>-e<end>` subdirectory of `cam_ts_loc`. |
114115

115116
---
116117

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
user: '<user name>'
2-
case: '<case name>'
3-
nick: '<case nickname>'
4-
case_base: 'baseline case name>'
5-
nick_base: 'baseline case nickname>'
1+
user: 'mvertens'
2+
case: 'n1850.ne16pg3_tn14.noresm3_0_beta22.i486.2026-07-31'
3+
nick: 'testcase'
4+
case_base: 'n1850.ne16pg3_tn14.noresm3_0_beta22.bdmc2_1p2.20260730'
5+
nick_base: 'refcase'
66

77
# baseic diagnostic info
88
diag_basic_info:
99
compare_obs: false
10+
global_mean_ts_full_range: true
1011
obs_source: "ERA5_1deg" # match an obs_name; omit to use each variable's default
1112
create_html: true
1213
defaults_overlay_file: adf_variable_defaults_noresm.yaml
@@ -31,8 +32,8 @@ diag_cam_climo:
3132
case_nickname: ${nick}
3233
cam_hist_loc: /nird/datalake/NS9560K/noresm3/cases/${diag_cam_climo.cam_case_name}/atm/hist
3334
cam_climo_loc: /scratch/${user}/noresm3/ADF/${diag_cam_climo.cam_case_name}/atm/proc/climo
34-
start_year: 960
35-
end_year: 969
35+
start_year: 1140
36+
end_year: 1149
3637
cam_ts_done: false
3738
cam_ts_save: true
3839
cam_overwrite_ts: false
@@ -50,8 +51,8 @@ diag_cam_baseline_climo:
5051
case_nickname: ${nick_base}
5152
cam_hist_loc: /nird/datalake/NS9560K/noresm3/cases/${diag_cam_baseline_climo.cam_case_name}/atm/hist
5253
cam_climo_loc: /scratch/${user}/noresm3/ADF/${diag_cam_baseline_climo.cam_case_name}/atm/proc/climo
53-
start_year: 960
54-
end_year: 969
54+
start_year: 1200
55+
end_year: 1209
5556
cam_ts_done: false
5657
cam_ts_save: false
5758
cam_overwrite_ts: false

config_noresm_template_obs.yaml

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
user: '<user name>'
2+
case: '<case name>'
3+
nick: '<case nickname>'
4+
5+
# baseic diagnostic info
6+
diag_basic_info:
7+
compare_obs: true
8+
global_mean_ts_full_range: true
9+
obs_source: "ERA5_1deg" # match an obs_name; omit to use each variable's default
10+
create_html: true
11+
defaults_overlay_file: adf_variable_defaults_noresm.yaml
12+
obs_data_loc: /nird/datalake/NS16000B/ADF-obs
13+
cam_regrid_loc: /scratch/${user}/noresm3/${diag_cam_climo.cam_case_name}/atm/proc/tseries/regrid
14+
cam_overwrite_regrid: false
15+
cam_se_grid: ne16
16+
cam_se_weight_file_ne16: /nird/datalake/NS9560K/diagnostics/land_xesmf_diag_data/map_ne16pg3_to_1.9x2.5_nomask_scripgrids_c250425.nc
17+
cam_se_weight_file_ne30: /nird/datalake/NS9560K/diagnostics/land_xesmf_diag_data/map_ne30pg3_to_0.5x0.5_nomask_aave_da_c180515.nc
18+
cam_diag_plot_loc: /nird/datalake/NS9560K/www/diagnostics/noresm/${user}/${case}/ADF
19+
plot_press_levels: [200,500,850]
20+
central_longitude: 180
21+
num_procs: 8
22+
redo_plot: true
23+
24+
# info for target diagnostic case
25+
diag_cam_climo:
26+
hist_str: cam.h0a
27+
calc_cam_climo: true
28+
cam_overwrite_climo: false
29+
cam_case_name: ${case}
30+
case_nickname: ${nick}
31+
cam_hist_loc: /nird/datalake/NS9560K/noresm3/cases/${diag_cam_climo.cam_case_name}/atm/hist
32+
cam_climo_loc: /scratch/${user}/noresm3/ADF/${diag_cam_climo.cam_case_name}/atm/proc/climo
33+
start_year: 960
34+
end_year: 969
35+
cam_ts_done: false
36+
cam_ts_save: true
37+
cam_overwrite_ts: false
38+
cam_ts_loc: /scratch/${user}/noresm3/ADF/${diag_cam_climo.cam_case_name}/atm/proc/tseries
39+
tem_hist_str: cam.h4
40+
cam_tem_loc: /scratch/${user}/noresm3/ADF/${diag_cam_climo.cam_case_name}/tem/
41+
overwrite_tem: false
42+
43+
# don't run cvdp
44+
diag_cvdp_info:
45+
cvdp_run: false
46+
47+
# don't run mdtf
48+
diag_mdtf_info:
49+
mdtf_run: false
50+
51+
# any of the scripts in scripts/averaging without the .py extension
52+
time_averaging_scripts:
53+
- create_climo_files
54+
55+
# any of the scripts in scripts/regridding without the .py extension
56+
regridding_scripts:
57+
- regrid_and_vert_interp
58+
59+
# any of the scripts in scripts/analysis without the .py extension
60+
analysis_scripts:
61+
- amwg_table
62+
#- ENSO_acrossRuns
63+
#- aerosol_gas_tables
64+
65+
# any of the scripts in scripts/plotting without the .py extension
66+
plotting_scripts:
67+
- global_mean_timeseries
68+
- global_latlon_map
69+
- zonal_mean
70+
- meridional_mean
71+
- cam_taylor_diagram
72+
# - global_latlon_vect_map
73+
# - polar_map
74+
# - qbo
75+
# - ozone_diagnostics
76+
# - enso_comparison_plots
77+
# - tape_recorder
78+
# - tem
79+
# - regional_map_multicase #To use this please un-comment and fill-out
80+
#the "region_multicase" section below
81+
82+
# variables to diagnose and plot
83+
diag_var_list:
84+
- AODVIS
85+
- mmr_BC
86+
- mmr_DUST
87+
- mmr_OM
88+
- mmr_SALT
89+
- mmr_SULFATE
90+
# - D550_BC
91+
# - D550_DU
92+
# - D550_POM
93+
# - D550_SO4
94+
# - D550_SS
95+
- cb_SULFATE
96+
- cb_isoprene
97+
- cb_monoterp
98+
- cb_DUST
99+
- cb_DMS
100+
- cb_BC
101+
- cb_OM
102+
- cb_H2O2
103+
- cb_H2SO4
104+
- cb_SALT
105+
- cb_SO2
106+
- SFmonoterp
107+
- SFisoprene
108+
- SFSS
109+
- SFSO4
110+
- SFSO2_net
111+
- SFOM
112+
- SFBC
113+
- SFDMS
114+
- SFH2O2
115+
- SFH2SO4
116+
- CLDHGH
117+
- CLDICE
118+
- CLDLIQ
119+
- CLDLOW
120+
- CLDMED
121+
- CLDTOT
122+
- CLOUD
123+
- RESTOM
124+
- ALBEDOSURF
125+
- TOTCF
126+
- FLNS
127+
- FLNSC
128+
- FLNT
129+
- FLNTC
130+
- FLUS
131+
- FLDS
132+
- FSDS
133+
- FSDSC
134+
- FSNS
135+
- FSNT
136+
- FSNTC
137+
- FSUS
138+
- LHFLX
139+
- LWCF
140+
- PBLH
141+
- PRECT
142+
- PS
143+
- PSL
144+
- QFLX
145+
- RELHUM
146+
- SHFLX
147+
- SWCF
148+
- T
149+
- TAUX
150+
- TAUY
151+
- TGCLDIWP
152+
- TGCLDLWP
153+
- TMQ
154+
- TREFHT
155+
- TS
156+
- U
157+
- U10
158+
- ICEFRAC
159+
- OCNFRAC
160+
- LANDFRAC
161+
- ALBEDO
162+
- ALBEDOC
163+
164+
# <Add more variables here.>
165+
166+
# Options for multi-case regional contour plots (./plotting/regional_map_multicase.py)
167+
# for region_time_option - if calendar, will look for specified years, if zeroanchor
168+
# will use a nyears starting from year_offset from the beginning of timeseries
169+
# region_multicase:
170+
# region_spec: [slat, nlat, wlon, elon]
171+
# region_time_option: <calendar | zeroanchor>
172+
# region_start_year:
173+
# region_end_year:
174+
# region_nyear:
175+
# region_year_offset:
176+
# region_month: <NULL means look for season>
177+
# region_season: <NULL means use annual mean>
178+
# region_variables: <list of variables to try to use; allows for a subset of the total diag variables>

lib/adf_dataset.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,24 @@ def _hist_strs_for_reference(self):
127127
# Time series files
128128
#------------------
129129
# Test case(s)
130-
def get_timeseries_file(self, case, field, hist_str=None):
130+
def get_timeseries_file(self, case, field, hist_str=None, full_range=False):
131131
"""Return list of test time series files.
132132
133133
If hist_str is given, restrict the search to that history stream
134134
(time series files are named {case}.{hist_str}.{field}.*.nc).
135+
136+
If full_range is True, look in the full-range year-stamped subdirectory
137+
(a sibling of the climo-range one) written by
138+
create_time_series(full_range=True) -- used by the global-mean drift plot.
135139
"""
136140
# list of paths (could be multiple cases)
137141
ts_locs = self.adf.get_cam_info("cam_ts_loc", required=True)
138142
caseindex = (self.case_names).index(case)
139143
ts_loc = Path(ts_locs[caseindex])
144+
if full_range:
145+
syr = self.adf.climo_yrs["syears_all"][caseindex]
146+
eyr = self.adf.climo_yrs["eyears_all"][caseindex]
147+
ts_loc = ts_loc.parent / f"s{syr}-e{eyr}"
140148
if hist_str:
141149
ts_filenames = f'{case}.{hist_str}.{field}.*nc'
142150
else:
@@ -145,16 +153,23 @@ def get_timeseries_file(self, case, field, hist_str=None):
145153
return ts_files
146154

147155
# Reference case (baseline/obs)
148-
def get_ref_timeseries_file(self, field, hist_str=None):
156+
def get_ref_timeseries_file(self, field, hist_str=None, full_range=False):
149157
"""Return list of reference time series files.
150158
151159
If hist_str is given, restrict the search to that history stream.
160+
161+
If full_range is True, look in the full-range year-stamped subdirectory
162+
written by create_time_series(baseline=True, full_range=True).
152163
"""
153164
if self.adf.compare_obs:
154165
warnings.warn("\t WARNING: ADF does not currently expect "
155166
"observational time series files.")
156167
return None
157168
ts_loc = Path(self.adf.get_baseline_info("cam_ts_loc", required=True))
169+
if full_range:
170+
syr = self.adf.climo_yrs["syear_baseline_all"]
171+
eyr = self.adf.climo_yrs["eyear_baseline_all"]
172+
ts_loc = ts_loc.parent / f"s{syr}-e{eyr}"
158173
if hist_str:
159174
ts_filenames = f'{self.ref_case_label}.{hist_str}.{field}.*nc'
160175
else:

lib/adf_diag.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,9 +323,17 @@ def __function_caller(
323323

324324
#########
325325

326-
def create_time_series(self, baseline=False):
326+
def create_time_series(self, baseline=False, full_range=False):
327327
"""
328328
Generate time series versions of the CAM history file data.
329+
330+
If ``full_range`` is True, generate time series over the *full available*
331+
year range (``climo_yrs[..._all]``) into a separate year-stamped
332+
directory, and only for 2-D variables. This is the optional pass used by
333+
the ``global_mean_ts_full_range`` time series so the global-mean drift
334+
plot spans the whole run rather than just the climo window (see
335+
``run_adf_diag``). Cases whose full range already equals the climo range
336+
are skipped (the normal pass already produced those files).
329337
"""
330338

331339
#Notify user that script has started:
@@ -369,6 +377,24 @@ def call_ncrcat(cmd):
369377
hist_str_list = self.hist_string["test_hist_str"]
370378
# End if
371379

380+
# For the optional "full range" pass, override the years and output
381+
# directories: use the full available range (climo_yrs[..._all]) and a
382+
# separate year-stamped subdirectory (sibling of the climo-range one).
383+
# The climo years are kept so cases whose full range already equals the
384+
# climo range can be skipped below.
385+
climo_syears = start_years
386+
climo_eyears = end_years
387+
if full_range:
388+
if baseline:
389+
start_years = [self.climo_yrs["syear_baseline_all"]]
390+
end_years = [self.climo_yrs["eyear_baseline_all"]]
391+
else:
392+
start_years = self.climo_yrs["syears_all"]
393+
end_years = self.climo_yrs["eyears_all"]
394+
ts_dirs = [os.path.join(os.path.dirname(str(d)), f"s{sy}-e{ey}")
395+
for d, sy, ey in zip(ts_dirs, start_years, end_years)]
396+
# End if
397+
372398
# Read hist_str (component.hist_num) from the yaml file, or set to default
373399
dmsg = f"reading from {hist_str_list} files"
374400
self.debug_log(dmsg)
@@ -397,6 +423,16 @@ def call_ncrcat(cmd):
397423
start_year = start_years[case_idx]
398424
end_year = end_years[case_idx]
399425

426+
# Full-range reuse: if the full available range already equals the
427+
# climo range for this case, the normal (climo) pass already wrote
428+
# these time series -- skip to avoid duplicating them.
429+
if full_range and (str(climo_syears[case_idx]) == str(start_year)
430+
and str(climo_eyears[case_idx]) == str(end_year)):
431+
print(f"\tNOTE: full range for '{case_name}' equals the climo range; "
432+
"reusing the existing time series.")
433+
continue
434+
# End if
435+
400436
# Create path object for the CAM history file(s) location:
401437
starting_location = Path(cam_hist_locs[case_idx])
402438

@@ -534,6 +570,16 @@ def call_ncrcat(cmd):
534570
# Loop over CAM history variables:
535571
ts_output_files = [] # native SE time-series files to regrid to lat/lon
536572
for var in diag_var_list:
573+
# In the full-range pass, only 2-D variables are needed (the
574+
# global-mean time series plot skips 3-D fields), so skip any
575+
# variable that carries a vertical dimension.
576+
if full_range and var in hist_file_ds.variables:
577+
_vdims = hist_file_ds[var].dims
578+
if ("lev" in _vdims) or ("ilev" in _vdims):
579+
continue
580+
#End if
581+
#End if
582+
537583
# Notify user of new time series file:
538584
print(f"\t - time series for {var}")
539585

0 commit comments

Comments
 (0)