Skip to content

Commit 79d2fa5

Browse files
Updates and fixes for ensemble runs (#311)
Updates and fixes for ensemble runs: - Allow MOM_input template to have INST_SUFFIX as an expandable variable - Fix multi instance ERI tests by adding inst_suffix to initial conditions (restart) files - Also, add a new test suite called fast_mom that solely runs 3- and 10-degree versions of the tx2_3v2 grid configuration via testmods.
1 parent 42dc926 commit 79d2fa5

File tree

16 files changed

+223
-98
lines changed

16 files changed

+223
-98
lines changed

.gitmodules

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
path = MOM6
33
url = https://github.com/NCAR/MOM6.git
44
fxDONOTUSEurl = https://github.com/NCAR/MOM6.git
5-
fxtag = dev/ncar_260310
5+
fxtag = dev/ncar_260311
66
fxrequired = AlwaysRequired
77

88
[submodule "stochastic_physics"]

cime_config/MOM_RPS/FType_MOM_params.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,22 +102,33 @@ def _read_MOM_input(input_path):
102102

103103
return _data
104104

105-
def write(self, output_path, output_format, case=None, def_params=None):
105+
def write(
106+
self, output_path, output_format, case=None, def_params=None, inst_suffix=""
107+
):
106108
if output_format == "MOM_input":
107109
assert case != None, "Must provide a case object to write out MOM_input"
108-
self._write_MOM_input(output_path, case)
110+
self._write_MOM_input(output_path, case, inst_suffix)
109111
elif output_format == "MOM_override":
112+
assert (
113+
not inst_suffix
114+
), "MOM_override format does not support INST_SUFFIX argument"
110115
assert (
111116
def_params != None
112117
), "Must provide a def_params object to write out MOM_override"
113118
self._write_MOM_override(output_path, def_params)
114119

115-
def _write_MOM_input(self, output_path, case):
120+
def _write_MOM_input(self, output_path, case, inst_suffix=""):
116121
"""writes a MOM_input file from a given json or yaml parameter file in accordance with
117122
the guards and additional parameters that are passed."""
118123

119124
# From the general template (MOM_input.yaml), reduce a custom MOM_input for this case
120-
self.reduce(lambda varname: case.get_value(varname))
125+
def expand_func(varname):
126+
if varname == "INST_SUFFIX":
127+
return inst_suffix
128+
else:
129+
return case.get_value(varname)
130+
131+
self.reduce(expand_func)
121132

122133
# 2. Now, write MOM_input
123134

cime_config/buildnml

Lines changed: 66 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import os, shutil, sys, re
1313
import logging
14+
from pathlib import Path
1415

1516
CIMEROOT = os.environ.get("CIMEROOT")
1617
if CIMEROOT is None:
@@ -45,15 +46,15 @@ def prep_input(case, inst_suffixes):
4546
input.nml, and mom.input_data_list, inside the run directory. If any of these input files are provided
4647
in SourceMods, those versions will be copied to run directory instead."""
4748

48-
Buildconf = case.get_value("CASEBUILD")
49-
comp_root_dir_ocn = case.get_value("COMP_ROOT_DIR_OCN")
50-
caseroot = case.get_value("CASEROOT")
51-
casename = case.get_value("CASE")
52-
rundir = case.get_value("RUNDIR")
53-
momconfdir = os.path.join(caseroot, "Buildconf", "momconf")
54-
SourceMods_dir = os.path.join(caseroot, "SourceMods", "src.mom")
49+
Buildconf = Path(case.get_value("CASEBUILD"))
50+
comp_root_dir_ocn = Path(case.get_value("COMP_ROOT_DIR_OCN"))
51+
caseroot = Path(case.get_value("CASEROOT"))
52+
srcroot = Path(case.get_value("SRCROOT"))
53+
rundir = Path(case.get_value("RUNDIR"))
54+
momconfdir = caseroot / "Buildconf" / "momconf"
55+
SourceMods_dir = caseroot / "SourceMods" / "src.mom"
5556
SourceMods_listdir = os.listdir(SourceMods_dir)
56-
srcroot = case.get_value("SRCROOT")
57+
casename = case.get_value("CASE")
5758
ocn_grid = case.get_value("OCN_GRID")
5859
run_type = case.get_value("RUN_TYPE")
5960
continue_run = case.get_value("CONTINUE_RUN")
@@ -69,57 +70,55 @@ def prep_input(case, inst_suffixes):
6970
)
7071

7172
# Make sure that rundir and momconf directories exist. If not, make them:
72-
if not os.path.exists(rundir):
73+
if not rundir.exists():
7374
os.makedirs(rundir)
74-
if not os.path.exists(momconfdir):
75+
if not momconfdir.exists():
7576
os.makedirs(momconfdir)
7677

7778
# Parse json files and create MOM6 input files in rundir
78-
json_templates_dir = os.path.join(comp_root_dir_ocn, "param_templates", "json")
79+
json_templates_dir = comp_root_dir_ocn / "param_templates" / "json"
7980

8081
# 1. Create MOM_input:
81-
MOM_input_template = os.path.join(json_templates_dir, "MOM_input.json")
82-
MOM_input_rundir = os.path.join(rundir, f"MOM_input{inst_suffixes[0]}")
8382
if multi_instance:
8483
# don't allow separate MOM_input files for separate instances
8584
assert not any(
8685
[re.match("MOM_input_+\d", filename) for filename in SourceMods_listdir]
8786
), "Cannot provide separate instances of MOM_input"
8887
if "MOM_input" in SourceMods_listdir:
89-
shutil.copy(os.path.join(SourceMods_dir, "MOM_input"), MOM_input_rundir)
88+
for inst_suffix in inst_suffixes:
89+
shutil.copy(SourceMods_dir / "MOM_input", rundir / f"MOM_input{inst_suffix}")
9090
else:
91-
# Create MOM_input in rundir using template
92-
MOM_input = FType_MOM_params.from_json(MOM_input_template)
93-
MOM_input.write(
94-
output_path=MOM_input_rundir, output_format="MOM_input", case=case
95-
)
96-
# If multi-instance, create MOM_input copies for each instance
97-
for inst_suffix in inst_suffixes[1:]:
98-
shutil.copy(MOM_input_rundir, os.path.join(rundir, f"MOM_input{inst_suffix}"))
99-
91+
# Create MOM_input in rundir using the defaults template
92+
for inst_suffix in inst_suffixes:
93+
MOM_input = FType_MOM_params.from_json(json_templates_dir / "MOM_input.json")
94+
MOM_input.write(
95+
output_path = rundir / f"MOM_input{inst_suffix}",
96+
output_format = "MOM_input",
97+
case = case,
98+
inst_suffix = inst_suffix
99+
)
100+
100101
# 2. Create MOM_override:
101102
for inst_suffix in inst_suffixes:
102103
user_nl_mom = FType_MOM_params.from_MOM_input(
103-
os.path.join(caseroot, f"user_nl_mom{inst_suffix}")
104+
caseroot / f"user_nl_mom{inst_suffix}"
104105
)
105106
if f"MOM_override{inst_suffix}" in SourceMods_listdir:
106107
assert (
107108
len(user_nl_mom.data) == 0
108109
), "Cannot provide parameter changes via both SourceMods and user_nl_mom!"
109110
shutil.copy(
110-
os.path.join(SourceMods_dir, f"MOM_override{inst_suffix}"),
111-
os.path.join(rundir, f"MOM_override{inst_suffix}"),
111+
SourceMods_dir / f"MOM_override{inst_suffix}",
112+
rundir / f"MOM_override{inst_suffix}",
112113
)
113114
else:
114115
init_MOM_override(rundir, inst_suffix)
115116
process_user_nl_mom(case, inst_suffix)
116117

117118
# 3. Read in final versions of MOM_input and MOM_override, so as to use them when inferring
118119
# values of expandable variables in the templates of subsequent MOM6 input files.
119-
MOM_input_final = FType_MOM_params.from_MOM_input(MOM_input_rundir)
120-
MOM_override_final = FType_MOM_params.from_MOM_input(
121-
os.path.join(rundir, f"MOM_override{inst_suffixes[0]}")
122-
)
120+
MOM_input_final = FType_MOM_params.from_MOM_input(rundir / f"MOM_input{inst_suffixes[0]}")
121+
MOM_override_final = FType_MOM_params.from_MOM_input(rundir / f"MOM_override{inst_suffixes[0]}")
123122
MOM_input_final.append(MOM_override_final)
124123
# Need to know value of USE_MARBL_TRACERS from MOM_input
125124
if "USE_MARBL_TRACERS" in MOM_input_final._data["Global"]:
@@ -138,22 +137,16 @@ def prep_input(case, inst_suffixes):
138137
append_status(f"xmlchange via MOM buildnml: CPL_I2O_PER_CAT={CPL_I20_PER_CAT_new}",
139138
"CaseStatus")
140139

141-
142140
# 4. Create input.nml:
143-
input_nml_template = os.path.join(json_templates_dir, "input_nml.json")
144-
input_nml_srcmod = os.path.join(SourceMods_dir, "input.nml")
145-
input_nml_rundir = os.path.join(rundir, "input.nml")
146141
if "input.nml" in SourceMods_listdir:
147-
shutil.copy(input_nml_srcmod, input_nml_rundir)
142+
shutil.copy(SourceMods_dir / "input.nml", rundir / "input.nml")
148143
else:
149-
input_nml = FType_input_nml.from_json(input_nml_template)
150-
input_nml.write(input_nml_rundir, case)
144+
input_nml = FType_input_nml.from_json(json_templates_dir / "input_nml.json")
145+
input_nml.write(rundir / "input.nml", case)
151146

152147
# 5. Create mom.input_data_list:
153-
input_data_list_template = os.path.join(json_templates_dir, "input_data_list.json")
154-
input_data_list_buildconf = os.path.join(Buildconf, "mom.input_data_list")
155-
input_data_list = FType_input_data_list.from_json(input_data_list_template)
156-
input_data_list.write(input_data_list_buildconf, case, MOM_input_final)
148+
input_data_list = FType_input_data_list.from_json(json_templates_dir / "input_data_list.json")
149+
input_data_list.write(Buildconf / "mom.input_data_list", case, MOM_input_final)
157150

158151
# 6. Create marbl_in (if case is set up with ocean BGC)
159152
if use_MARBL:
@@ -180,41 +173,38 @@ def prep_input(case, inst_suffixes):
180173
abio_dic_on = marbl_tracer_opt_dict.get("ABIO_DIC_ON", False)
181174

182175
# Use MARBL-provided tool to generate marbl_in
183-
MARBL_dir = os.path.join(srcroot, "components", "mom", "externals", "MARBL")
184-
MARBL_settings = MARBL_settings_for_MOM(MARBL_dir, caseroot, base_bio_on, abio_dic_on,
176+
MARBL_dir = srcroot / "components" / "mom" / "externals" / "MARBL"
177+
MARBL_settings = MARBL_settings_for_MOM(str(MARBL_dir), caseroot, base_bio_on, abio_dic_on,
185178
ocn_grid, run_type, continue_run, marbl_config)
186-
MARBL_settings.write_settings_file(os.path.join(rundir, "marbl_in"))
179+
MARBL_settings.write_settings_file(rundir / "marbl_in")
187180

188181
# 7. Create diag_table:
189-
diag_table_rundir = os.path.join(rundir, "diag_table")
190-
unresolved_diag_table_confdir = os.path.join(momconfdir, "diag_table.unresolved")
182+
unresolved_diag_table_confdir = momconfdir / "diag_table.unresolved"
191183
if "diag_table" in SourceMods_listdir:
192184
# A resolved diag_table is provided in SourceMods. Directly copy it to rundir.
193185
expect(
194186
"diag_table.unresolved" not in SourceMods_listdir,
195187
"Cannot provide both resolved and unresolved diag_table in SourceMods!",
196188
)
197-
diag_table_srcmod = os.path.join(SourceMods_dir, "diag_table")
198-
shutil.copy(diag_table_srcmod, diag_table_rundir)
189+
diag_table_srcmod = SourceMods_dir / "diag_table"
190+
shutil.copy(diag_table_srcmod, rundir / "diag_table")
199191
# remove unresolved diag_table to avoid conflicting resolved and unresolved versions
200-
if os.path.exists(unresolved_diag_table_confdir):
192+
if unresolved_diag_table_confdir.exists():
201193
os.remove(unresolved_diag_table_confdir)
202194
else:
203195
if "diag_table.unresolved" in SourceMods_listdir:
204196
# An unresolved diag_table is provided in SourceMods. Directly copy it to momconf.
205-
unresolved_diag_table_srcmod = os.path.join(
206-
SourceMods_dir, "diag_table.unresolved"
207-
)
197+
unresolved_diag_table_srcmod = SourceMods_dir / "diag_table.unresolved"
208198
shutil.copy(unresolved_diag_table_srcmod, unresolved_diag_table_confdir)
209199
else:
210200
# Create an unresolved diag_table in momconf using the template
211-
diag_table_template = os.path.join(json_templates_dir, "diag_table.json")
201+
diag_table_template = json_templates_dir / "diag_table.json"
212202
unresolved_diag_table = FType_diag_table.from_json(diag_table_template)
213203
if use_MARBL:
214204
# Make sure that momconfdir exists. If not, make it:
215-
if not os.path.exists(momconfdir):
205+
if not momconfdir.exists():
216206
os.makedirs(momconfdir)
217-
MARBL_diag_table_json = os.path.join(momconfdir, "diag_table_MARBL.json")
207+
MARBL_diag_table_json = momconfdir / "diag_table_MARBL.json"
218208
# For now, user has no control over lMARBL_output_all
219209
# TODO: mimic what is done in POP and allow it via user_nl_mom?
220210
lMARBL_output_all = case.get_value("TEST")
@@ -247,7 +237,7 @@ def prep_input(case, inst_suffixes):
247237
)
248238
# Resolve unresolved diag_table in momconf and write it to rundir
249239
FType_diag_table.resolve(
250-
unresolved_diag_table_confdir, diag_table_rundir, casename
240+
unresolved_diag_table_confdir, rundir / "diag_table", casename
251241
)
252242

253243
# Sanity checks after all input files are generated.
@@ -256,7 +246,7 @@ def prep_input(case, inst_suffixes):
256246

257247
def init_MOM_override(rundir, inst_suffix):
258248
# Create an empty MOM_override:
259-
with open(os.path.join(rundir, f"MOM_override{inst_suffix}"), "w") as MOM_override:
249+
with open(rundir / f"MOM_override{inst_suffix}", "w") as MOM_override:
260250
MOM_override.write(
261251
"! WARNING: DO NOT EDIT this file! Any user change made in this file will be\n"
262252
+ "! overriden. This file is automatically generated. MOM6 parameter\n"
@@ -267,50 +257,46 @@ def init_MOM_override(rundir, inst_suffix):
267257

268258
def process_user_nl_mom(case, inst_suffix):
269259
"""Calls the appropriate MOM_RPS functions to parse user_nl_mom and create MOM_override."""
270-
caseroot = case.get_value("CASEROOT")
271-
rundir = case.get_value("RUNDIR")
260+
caseroot = Path(case.get_value("CASEROOT"))
261+
rundir = Path(case.get_value("RUNDIR"))
272262

273-
user_nl_mom = FType_MOM_params.from_MOM_input(
274-
os.path.join(caseroot, f"user_nl_mom{inst_suffix}")
275-
)
263+
user_nl_mom = FType_MOM_params.from_MOM_input(caseroot / f"user_nl_mom{inst_suffix}")
276264

277265
# copy the user_nl_mom parameters into MOM_override:
278266
if len(user_nl_mom.data) > 0:
279267

280268
# check if a copy of MOM_override is provided in SourceMods:
281-
SourceMods_dir = os.path.join(caseroot, "SourceMods", "src.mom")
269+
SourceMods_dir = caseroot / "SourceMods" / "src.mom"
282270
if f"MOM_override{inst_suffix}" in os.listdir(SourceMods_dir):
283271
raise SystemExit(
284272
"ERROR: Cannot provide parameter changes via both SourceMods and user_nl_mom!"
285273
)
286274

287275
# parse the MOM_input file staged in rundir:
288-
MOM_input_rundir = FType_MOM_params.from_MOM_input(
289-
os.path.join(rundir, f"MOM_input{inst_suffix}")
290-
)
276+
MOM_input_rundir = FType_MOM_params.from_MOM_input(rundir / f"MOM_input{inst_suffix}")
291277

292278
# Write MOM_override (based on data from user_nl_mom)
293279
user_nl_mom.write(
294-
output_path=os.path.join(rundir, f"MOM_override{inst_suffix}"),
280+
output_path=rundir / f"MOM_override{inst_suffix}",
295281
output_format="MOM_override",
296282
def_params=MOM_input_rundir,
297283
)
298284

299285

300286
def _copy_input_files(case, dest_dir, inst_suffixes):
301287
"""Saves copies of MOM6 input files in momconf directory for the record."""
302-
rundir = case.get_value("RUNDIR")
303-
if not os.path.isdir(dest_dir):
304-
os.makedirs(dest_dir)
288+
rundir = Path(case.get_value("RUNDIR"))
289+
if not dest_dir.exists():
290+
dest_dir.mkdir(parents=True, exist_ok=True)
305291

306292
for inst_suffix in inst_suffixes:
307293
for filename in ["MOM_input", "MOM_override"]:
308-
shutil.copy(os.path.join(rundir, filename + inst_suffix), dest_dir)
294+
shutil.copy(rundir / f"{filename}{inst_suffix}", dest_dir)
309295
for filename in [
310296
"diag_table",
311297
"input.nml",
312298
]:
313-
shutil.copy(os.path.join(rundir, filename), dest_dir)
299+
shutil.copy(rundir / filename, dest_dir)
314300

315301
def generate_diag_table_MARBL(caseroot, MARBL_settings, SourceMods_dir, momconfdir, MARBL_dir, MARBL_diag_table_json,
316302
marbl_hist_vert_grid, lMARBL_output_all, lMARBL_output_alt_co2, ice_ncat, diag_mode):
@@ -322,8 +308,8 @@ def generate_diag_table_MARBL(caseroot, MARBL_settings, SourceMods_dir, momconfd
322308
(If MARBL_diagnostics is in SourceMods, use that version instead.)
323309
"""
324310
# 1. Check for diag_table_MARBL.json in SourceMods
325-
srcmods_version = os.path.join(SourceMods_dir, "diag_table_MARBL.json")
326-
if os.path.isfile(srcmods_version):
311+
srcmods_version = SourceMods_dir / "diag_table_MARBL.json"
312+
if srcmods_version.is_file():
327313
shutil.copy(srcmods_version, MARBL_diag_table_json)
328314
return
329315

@@ -336,9 +322,9 @@ def generate_diag_table_MARBL(caseroot, MARBL_settings, SourceMods_dir, momconfd
336322
MARBL_diagnostics = MARBL_diagnostics_for_MOM(MARBL_dir, caseroot, MARBL_settings)
337323

338324
# (b) Check for file in SourceMods, otherwise generate it
339-
srcmods_version = os.path.join(SourceMods_dir, "MARBL_diagnostics")
340-
MARBL_diags_buildconf = os.path.join(momconfdir, "MARBL_diagnostics")
341-
if os.path.isfile(srcmods_version):
325+
srcmods_version = SourceMods_dir / "MARBL_diagnostics"
326+
MARBL_diags_buildconf = momconfdir / "MARBL_diagnostics"
327+
if srcmods_version.is_file():
342328
shutil.copy(srcmods_version, MARBL_diags_buildconf)
343329
else:
344330
# (i) Create MARBL_diagnostics with MOM-generated diagnostics
@@ -457,12 +443,12 @@ def buildnml(case, caseroot, compname):
457443
prep_input(case, inst_suffixes)
458444

459445
# save copies of input files in momconf
460-
caseroot = case.get_value("CASEROOT")
461-
momconfdir = os.path.join(caseroot, "Buildconf", "momconf")
446+
caseroot = Path(case.get_value("CASEROOT"))
447+
momconfdir = caseroot / "Buildconf" / "momconf"
462448
_copy_input_files(case, momconfdir, inst_suffixes)
463449

464450
# save copies of input files in CaseDocs
465-
casedocsdir = os.path.join(caseroot, "CaseDocs")
451+
casedocsdir = caseroot / "CaseDocs"
466452
_copy_input_files(case, casedocsdir, inst_suffixes)
467453

468454

0 commit comments

Comments
 (0)