Skip to content

Commit

Permalink
Allow registry fields with initial_value to not error on not found in…
Browse files Browse the repository at this point in the history
… input data (#365)

Tag name (required for release branches):
Originator(s): @jimmielin (with extensive guidance from @peverwhee)

Description (include the issue title, and the keyword ['closes',
'fixes', 'resolves'] followed by the issue number):
* Fixes #359 

Should enable CAM7 / CAM4 regression tests to run again despite no PBL
scheme, if `pbuf_pblh` includes
`<initial_value>0.0_kind_phys</initial_value>` in registry.xml -
separate PR will include this.

Describe any changes made to build system: 
* Adds `vars_init_value` in generation of physics_inputs.F90 to record
which variables have an `<initial_value>` set in `registry.xml`

Describe any changes made to the namelist: N/A

List any changes to the defaults for the input datasets (e.g. boundary
datasets): N/A

List all files eliminated and why: N/A

List all files added and what they do:
```
A       test/unit/python/sample_files/write_init_files/phys_vars_init_check_initial_value.F90
A       test/unit/python/sample_files/write_init_files/physics_inputs_initial_value.F90
A       test/unit/python/sample_files/write_init_files/simple_reg_initial_value.xml
  - new test sample files for write_init_files
```

List all existing files that have been modified, and describe the
changes:
(Helpful git command: `git diff --name-status
development...<your_branch_name>`)
```
M       cime_config/cam_autogen.py
M       cime_config/cam_build_cache.py
M       cime_config/cam_config.py
M       src/data/generate_registry_data.py
  - pass variables having initial_value element in registry.xml

M       src/data/write_init_files.py
  - add error_on_not_found=.false. to variables having initial_value element
  - or write info message for variables that cannot be read (i.e., no horizontal dimension)

M       test/unit/python/sample_files/build_cache_files/update_reg_build_cache.xml
M       test/unit/python/test_build_cache.py
M       test/unit/python/test_cam_autogen.py
M       test/unit/python/test_registry.py
M       test/unit/python/test_write_init_files.py
  - fix compatibility of existing tests with new vars_init_value argument
  - add test_simple_initial_value_write_init unit test
```


If there are new failures (compared to the
`test/existing-test-failures.txt` file),
have them OK'd by the gatekeeper, note them here, and add them to the
file.
If there are baseline differences, include the test and the reason for
the
diff. What is the nature of the change? Roundoff?

derecho/intel/aux_sima:

derecho/gnu/aux_sima:

If this changes climate describe any run(s) done to evaluate the new
climate in enough detail that it(they) could be reproduced:

CAM-SIMA date used for the baseline comparison tests if different than
latest:
  • Loading branch information
jimmielin authored Feb 24, 2025
1 parent 28a4c36 commit a244a84
Show file tree
Hide file tree
Showing 13 changed files with 910 additions and 92 deletions.
12 changes: 7 additions & 5 deletions cime_config/cam_autogen.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ def generate_registry(data_search, build_cache, atm_root, bldroot,
gen_fort_indent, source_mods_dir, atm_root,
logger=_LOGGER, schema_paths=data_search,
error_on_no_validate=True)
retcode, reg_file_list, ic_names, registry_constituents = retvals
retcode, reg_file_list, ic_names, registry_constituents, vars_init_value = retvals
# Raise error if gen_registry failed:
if retcode != 0:
emsg = "ERROR:Unable to generate CAM data structures from {}, err = {}"
Expand All @@ -406,15 +406,16 @@ def generate_registry(data_search, build_cache, atm_root, bldroot,
# Save build details in the build cache
reg_file_paths = [x.file_path for x in reg_file_list if x.file_path]
build_cache.update_registry(gen_reg_file, registry_files, dycore,
reg_file_paths, ic_names, registry_constituents)
reg_file_paths, ic_names, registry_constituents, vars_init_value)
else:
# If we did not run the registry generator, retrieve info from cache
reg_file_paths = build_cache.reg_file_list()
ic_names = build_cache.ic_names()
registry_constituents = build_cache.constituents()
vars_init_value = build_cache.vars_init_value()
# End if

return genreg_dir, do_gen_registry, reg_file_paths, ic_names, registry_constituents
return genreg_dir, do_gen_registry, reg_file_paths, ic_names, registry_constituents, vars_init_value

###############################################################################
def generate_physics_suites(build_cache, preproc_defs, host_name,
Expand Down Expand Up @@ -659,7 +660,8 @@ def generate_physics_suites(build_cache, preproc_defs, host_name,
###############################################################################
def generate_init_routines(build_cache, bldroot, force_ccpp, force_init,
source_mods_dir, gen_fort_indent,
cap_database, ic_names, registry_constituents):
cap_database, ic_names, registry_constituents,
vars_init_value):
###############################################################################
"""
Generate the host model initialization source code files
Expand Down Expand Up @@ -697,7 +699,7 @@ def generate_init_routines(build_cache, bldroot, force_ccpp, force_init,
# within write_init_files (so that write_init_files can be the place
# where the source include files are stored).
source_paths = [source_mods_dir, _REG_GEN_DIR]
retmsg = write_init_files(cap_database, ic_names, registry_constituents,
retmsg = write_init_files(cap_database, ic_names, registry_constituents, vars_init_value,
init_dir, _find_file, source_paths,
gen_fort_indent, _LOGGER)

Expand Down
14 changes: 13 additions & 1 deletion cime_config/cam_build_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ def __init__(self, build_cache):
self.__reg_gen_files = []
self.__ic_names = {}
self.__constituents = []
self.__vars_init_value = []
if os.path.exists(build_cache):
# Initialize build cache state
_, cache = read_xml_file(build_cache)
Expand Down Expand Up @@ -257,6 +258,9 @@ def __init__(self, build_cache):
stdname = item.get('standard_name')
itext = clean_xml_text(item)
self.__constituents.append(itext)
elif item.tag == 'vars_init_value_entry':
itext = clean_xml_text(item)
self.__vars_init_value.append(itext)
else:
emsg = "ERROR: Unknown registry tag, '{}'"
raise ValueError(emsg.format(item.tag))
Expand Down Expand Up @@ -318,7 +322,7 @@ def __init__(self, build_cache):
# end if

def update_registry(self, gen_reg_file, registry_source_files,
dycore, reg_file_list, ic_names, constituents):
dycore, reg_file_list, ic_names, constituents, vars_init_value):
"""Replace the registry cache data with input data
"""
self.__dycore = dycore
Expand All @@ -334,6 +338,7 @@ def update_registry(self, gen_reg_file, registry_source_files,
# and should already be of type dict:
self.__ic_names = ic_names
self.__constituents = constituents
self.__vars_init_value = vars_init_value

def update_ccpp(self, suite_definition_files, scheme_files, host_files,
xml_files, namelist_meta_files, namelist_groups,
Expand Down Expand Up @@ -410,6 +415,9 @@ def write(self):
const_entry = ET.SubElement(registry, 'constituent_entry')
const_entry.text = stdname
# end for
for stdname in self.__vars_init_value:
var_entry = ET.SubElement(registry, 'vars_init_value_entry')
var_entry.text = stdname
# CCPP
ccpp = ET.SubElement(new_cache, 'CCPP')
for sfile in self.__sdfs.values():
Expand Down Expand Up @@ -617,5 +625,9 @@ def constituents(self):
"""Return a copy of the registry constituents list"""
return list(self.__constituents)

def vars_init_value(self):
"""Return a copy of the list of variables with initial_value"""
return list(self.__vars_init_value)

#############
# End of file
5 changes: 3 additions & 2 deletions cime_config/cam_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -854,7 +854,7 @@ def generate_cam_src(self, gen_fort_indent):
retvals = generate_registry(data_search, build_cache, self.__atm_root,
self.__bldroot, source_mods_dir,
dyn, gen_fort_indent)
reg_dir, force_ccpp, reg_files, ic_names, registry_constituents = retvals
reg_dir, force_ccpp, reg_files, ic_names, registry_constituents, vars_init_value = retvals

#Add registry path to config object:
reg_dir_desc = "Location of auto-generated registry code."
Expand Down Expand Up @@ -887,7 +887,8 @@ def generate_cam_src(self, gen_fort_indent):
init_dir = generate_init_routines(build_cache, self.__bldroot,
force_ccpp, force_init,
source_mods_dir, gen_fort_indent,
capgen_db, ic_names, registry_constituents)
capgen_db, ic_names, registry_constituents,
vars_init_value)

#Add registry path to config object:
init_dir_desc = "Location of auto-generated physics initialization code."
Expand Down
32 changes: 29 additions & 3 deletions src/data/generate_registry_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -1758,6 +1758,30 @@ def _create_constituent_list(registry):
# end for
return constituent_list

###############################################################################
def _create_variables_with_initial_value_list(registry):
###############################################################################
"""
Create a list of all variables with initial_value defined in the registry.
To be used by write_init_files.py to allow these variables to
not error when not found in the initial conditions file.
"""
vars_init_value_list = []
for section in registry:
if section.tag == 'file':
for obj in section:
if obj.tag == 'variable':
for subobj in obj:
if subobj.tag == 'initial_value':
stdname = obj.get('standard_name')
vars_init_value_list.append(stdname)
# end if (only if initial_value node is found)
# end for
# end if (ignore other node types)
# end for
# end if (ignore other node types)
# end for
return vars_init_value_list

###############################################################################
def gen_registry(registry_file, dycore, outdir, indent,
Expand All @@ -1767,7 +1791,7 @@ def gen_registry(registry_file, dycore, outdir, indent,
"""Parse a registry XML file and generate source code and metadata.
<dycore> is the name of the dycore for DP coupling specialization.
<config> is a dictionary containing other configuration items for
souce code customization.
source code customization.
Source code and metadata is output to <outdir>.
<src_mod> is the location of the builds SourceMods/src.cam directory
<src_root> is the top of the component tree
Expand Down Expand Up @@ -1819,7 +1843,8 @@ def gen_registry(registry_file, dycore, outdir, indent,
retcode = 1
files = None
ic_names = None
constituents = None
registry_constituents = None
vars_init_value = None
else:
library_name = registry.get('name')
emsg = f"Parsing registry, {library_name}"
Expand All @@ -1830,9 +1855,10 @@ def gen_registry(registry_file, dycore, outdir, indent,
# See comment in _create_ic_name_dict
ic_names = _create_ic_name_dict(registry)
registry_constituents = _create_constituent_list(registry)
vars_init_value = _create_variables_with_initial_value_list(registry)
retcode = 0 # Throw exception on error
# end if
return retcode, files, ic_names, registry_constituents
return retcode, files, ic_names, registry_constituents, vars_init_value

def main():
"""Function to execute when module called as a script"""
Expand Down
24 changes: 18 additions & 6 deletions src/data/write_init_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
#Main function
##############

def write_init_files(cap_database, ic_names, registry_constituents, outdir,
file_find_func, source_paths, indent, logger,
def write_init_files(cap_database, ic_names, registry_constituents, vars_init_value,
outdir, file_find_func, source_paths, indent, logger,
phys_check_filename=None, phys_input_filename=None):

"""
Expand Down Expand Up @@ -239,7 +239,8 @@ def write_init_files(cap_database, ic_names, registry_constituents, outdir,

# Write physics_read_data subroutine:
write_phys_read_subroutine(outfile, host_dict, host_vars, host_imports,
phys_check_fname_str, constituent_set)
phys_check_fname_str, constituent_set,
vars_init_value)

outfile.blank_line()

Expand Down Expand Up @@ -810,7 +811,8 @@ def get_dimension_info(hvar):
return vdim_name, legal_dims, fail_reason

def write_phys_read_subroutine(outfile, host_dict, host_vars, host_imports,
phys_check_fname_str, constituent_set):
phys_check_fname_str, constituent_set,
vars_init_value):

"""
Write the "physics_read_data" subroutine, which
Expand Down Expand Up @@ -866,10 +868,20 @@ def write_phys_read_subroutine(outfile, host_dict, host_vars, host_imports,
if levnm is not None:
call_str += f"'{levnm}', "
# end if
call_str += f"timestep, {var_locname})"
err_on_not_found_string = ""
if var_stdname in vars_init_value:
# if initial value is available, do not throw error when not found in initial condition file.
err_on_not_found_string = ", error_on_not_found=.false."
# end if
call_str += f"timestep, {var_locname}{err_on_not_found_string})"
else:
call_str = f"call endrun('Cannot read {var_locname} from file'" + \
# if initial value is assigned, then it can be ignored
if var_stdname in vars_init_value:
call_str = f"if(masterproc) write(iulog,*) '{var_locname} already has an initial_value, using it now. It also cannot be read from file: {reason}'"
else:
call_str = f"call endrun('Cannot read {var_locname} from file'" + \
f"//', {reason}')"
# end if
# end if

# Add string to dictionary:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<ic_name_entry standard_name="Only_had_a">brain</ic_name_entry>
<constituent_entry>cnst_1</constituent_entry>
<constituent_entry>cnst_2</constituent_entry>
<vars_init_value_entry>Only_had_a_brain</vars_init_value_entry>
</registry>
<CCPP>
<SDF file_path="/behind/curtain/suite_wizard.xml" hash="ade45dea32a6f279957735596a147efddd061f9c"/>
Expand Down
Loading

0 comments on commit a244a84

Please sign in to comment.