Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove optional arguments from CCPP (capgen metadata parser and ccpp_prebuild scripts) #408

Merged
merged 2 commits into from
Nov 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 1 addition & 94 deletions scripts/ccpp_prebuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ def import_config(configfile, builddir):
config['caps_sourcefile'] = ccpp_prebuild_config.CAPS_SOURCEFILE.format(build_dir=builddir)
config['caps_dir'] = ccpp_prebuild_config.CAPS_DIR.format(build_dir=builddir)
config['suites_dir'] = ccpp_prebuild_config.SUITES_DIR
config['optional_arguments'] = ccpp_prebuild_config.OPTIONAL_ARGUMENTS
config['host_model'] = ccpp_prebuild_config.HOST_MODEL_IDENTIFIER
config['html_vartable_file'] = ccpp_prebuild_config.HTML_VARTABLE_FILE.format(build_dir=builddir)
config['latex_vartable_file'] = ccpp_prebuild_config.LATEX_VARTABLE_FILE.format(build_dir=builddir)
Expand Down Expand Up @@ -387,95 +386,9 @@ def generate_list_of_schemes_and_dependencies_to_compile(schemes_in_files, depen
# Remove duplicates
return (success, list(set(schemes_and_dependencies_to_compile)))

def check_optional_arguments(metadata, arguments, optional_arguments):
"""Check if for each subroutine with optional arguments, an entry exists in the
optional_arguments dictionary. This is required to generate the caps correctly
and to assess whether the variable is required from the host model. Optional
arguments that are not requested by the model as specified in the dictionary
optional_arguments are deleted from the list of requested data individually
for each subroutine."""
logging.info('Checking optional arguments in physics schemes ...')
success = True

# First make sure that the CCPP prebuild config entry doesn't contain any variables that are unknown
# (by standard name), or that it lists variables as optional arguments that aren't declared as optional
for scheme_name in optional_arguments.keys():
# Skip modules that have been filtered out (because they are not used by the selected suites, for example)
if scheme_name in arguments.keys():
for subroutine_name in optional_arguments[scheme_name].keys():
# If optional arguments are listed individually, check each of them
if isinstance(optional_arguments[scheme_name][subroutine_name], list):
for var_name in optional_arguments[scheme_name][subroutine_name]:
if not var_name in arguments[scheme_name][subroutine_name]:
raise Exception("Explicitly requested optional argument '{}' not known to {}/{}".format(
var_name, scheme_name, subroutine_name))
else:
for var in metadata[var_name][:]:
for item in var.container.split(' '):
subitems = item.split('_')
if subitems[0] == 'MODULE':
module_name_test = '_'.join(subitems[1:])
elif subitems[0] == 'SCHEME':
scheme_name_test = '_'.join(subitems[1:])
elif subitems[0] == 'SUBROUTINE':
subroutine_name_test = '_'.join(subitems[1:])
else:
success = False
logging.error('Invalid identifier {0} in container value {1} of requested variable {2}'.format(
subitems[0], var.container, var_name))
if scheme_name_test == scheme_name and subroutine_name_test == subroutine_name and not var.optional in ['t', 'T']:
raise Exception("Variable {} in {}/{}".format(var_name, scheme_name, subroutine_name) + \
" is not an optional argument, but listed as such in the CCPP prebuild config")

for var_name in sorted(metadata.keys()):
# The notation metadata[var_name][:] is a convenient way to make a copy
# of the metadata[var_name] list, which allows removing items as we go
for var in metadata[var_name][:]:
if var.optional in ['t', 'T']:
for item in var.container.split(' '):
subitems = item.split('_')
if subitems[0] == 'MODULE':
module_name = '_'.join(subitems[1:])
elif subitems[0] == 'SCHEME':
scheme_name = '_'.join(subitems[1:])
elif subitems[0] == 'SUBROUTINE':
subroutine_name = '_'.join(subitems[1:])
else:
raise Exception('Invalid identifier {0} in container value {1} of requested variable {2}'.format(
subitems[0], var.container, var_name))
if not scheme_name in optional_arguments.keys() or not \
subroutine_name in optional_arguments[scheme_name].keys():
raise Exception('No entry found in optional_arguments dictionary for optional argument ' + \
'{0} to subroutine {1} in module {2}'.format(var_name, subroutine_name, scheme_name))
if type(optional_arguments[scheme_name][subroutine_name]) is list:
if var_name in optional_arguments[scheme_name][subroutine_name]:
logging.debug('Optional argument {0} to subroutine {1} in module {2} is required, keep in list'.format(
var_name, subroutine_name, scheme_name))
else:
logging.debug('Optional argument {0} to subroutine {1} in module {2} is not required, remove from list'.format(
var_name, subroutine_name, scheme_name))
# Remove this var instance from list of var instances for this var_name
metadata[var_name].remove(var)
# Remove var_name from list of calling arguments for that subroutine
# (unless that module has been filtered out because none of the suites uses it)
if scheme_name in arguments.keys():
arguments[scheme_name][subroutine_name].remove(var_name)
elif optional_arguments[scheme_name][subroutine_name] == 'all':
logging.debug('optional argument {0} to subroutine {1} in module {2} is required, keep in list'.format(
var_name, subroutine_name, scheme_name))

# If metadata[var_name] is now empty, i.e. the variable is not
# requested at all by the model, remove the entry from metadata
if not metadata[var_name]:
del metadata[var_name]

return (success, metadata, arguments)

def compare_metadata(metadata_define, metadata_request):
"""Compare the requested metadata to the defined one. For each requested entry, a
single (i.e. non-ambiguous entry) must be present in the defined entries. All optional
arguments that are still in the list of required variables for a scheme are needed,
since they were checked in the routine check_optional_arguments beforehand."""
single (i.e. non-ambiguous entry) must be present in the defined entries."""

logging.info('Comparing metadata for requested and provided variables ...')
success = True
Expand Down Expand Up @@ -796,12 +709,6 @@ def main():
if not success:
raise Exception('Call to generate_list_of_schemes_and_dependencies_to_compile failed.')

# Process optional arguments based on configuration in above dictionary optional_arguments
(success, metadata_request, arguments_request) = check_optional_arguments(metadata_request,arguments_request,
config['optional_arguments'])
if not success:
raise Exception('Call to check_optional_arguments failed.')

# Create a LaTeX table with all variables requested by the pool of physics and/or provided by the host model
success = metadata_to_latex(metadata_define, metadata_request, config['host_model'], config['latex_vartable_file'])
if not success:
Expand Down
6 changes: 1 addition & 5 deletions scripts/metadata_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@
# commented out, no check is performed. This is the case for 'type' and 'kind' right now, since models use their
# own derived data types and kind types.
VALID_ITEMS = {
'header' : ['local_name', 'standard_name', 'long_name', 'units', 'rank', 'type', 'kind', 'intent', 'optional'],
'header' : ['local_name', 'standard_name', 'long_name', 'units', 'rank', 'type', 'kind', 'intent'],
#'type' : ['character', 'integer', 'real', ...],
#'kind' : ['default', 'kind_phys', ...],
'intent' : ['none', 'in', 'out', 'inout'],
'optional' : ['T', 'F'],
}

# Mandatory variables that every scheme needs to have
Expand All @@ -41,7 +40,6 @@
rank = '',
kind = 'len=*',
intent = 'out',
optional = 'F',
active = 'T',
),
'ccpp_error_flag' : Var(local_name = 'ierr',
Expand All @@ -53,7 +51,6 @@
rank = '',
kind = '',
intent = 'out',
optional = 'F',
active = 'T',
),
}
Expand Down Expand Up @@ -222,7 +219,6 @@ def read_new_metadata(filename, module_name, table_name, scheme_name = None, sub
container = container,
kind = kind,
intent = new_var.get_prop_value('intent'),
optional = 'T' if new_var.get_prop_value('optional') else 'F',
active = active,
)
# Check for duplicates in same table
Expand Down
7 changes: 4 additions & 3 deletions scripts/metavar.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ class Var(object):

>>> Var.get_prop('dimensions').valid_value(['Bob', 'Ray'])
['Bob', 'Ray']
>>> Var.get_prop('active')
>>> Var.get_prop('active').valid_value('.true.')
'.true.'
>>> Var.get_prop('active').valid_value('flag_for_aerosol_physics')
'flag_for_aerosol_physics'
Expand Down Expand Up @@ -490,6 +490,9 @@ class Var(object):
>>> Var({'local_name' : 'foo', 'standard_name' : 'hi_mom', 'units' : 'm/s', 'dimensions' : '()', 'type' : 'real', 'intent' : 'ino'}, ParseSource('vname', 'SCHEME', ParseContext())) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
ParseSyntaxError: Invalid intent variable property, 'ino', at <standard input>:1
>>> Var({'local_name' : 'foo', 'standard_name' : 'hi_mom', 'units' : 'm/s', 'dimensions' : '()', 'type' : 'real', 'intent' : 'in', 'optional' : 'false'}, ParseSource('vname', 'SCHEME', ParseContext())) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
ParseSyntaxError: Invalid variable property name, 'optional', at <standard input>:1
"""

## Prop lists below define all the allowed CCPP Metadata attributes
Expand All @@ -512,8 +515,6 @@ class Var(object):
default_fn_in=default_kind_val),
VariableProperty('state_variable', bool,
optional_in=True, default_in=False),
VariableProperty('optional', bool,
optional_in=True, default_in=False),
VariableProperty('protected', bool,
optional_in=True, default_in=False),
VariableProperty('allocatable', bool,
Expand Down
13 changes: 0 additions & 13 deletions scripts/mkcap.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ def __init__(self, **kwargs):
self._container = None
self._kind = None
self._intent = None
self._optional = None
self._active = None
self._target = None
self._actions = { 'in' : None, 'out' : None }
Expand Down Expand Up @@ -123,17 +122,6 @@ def intent(self, value):
raise ValueError('Invalid value {0} for variable property intent'.format(value))
self._intent = value

@property
def optional(self):
'''Get the optional attribute of the variable.'''
return self._optional

@optional.setter
def optional(self, value):
if not value in ['T', 'F']:
raise ValueError('Invalid value {0} for variable property optional'.format(value))
self._optional = value

@property
def active(self):
'''Get the active attribute of the variable.'''
Expand Down Expand Up @@ -281,7 +269,6 @@ def print_debug(self):
rank = {s.rank} *
kind = {s.kind} *
intent = {s.intent}
optional = {s.optional}
active = {s.active}
target = {s.target}
container = {s.container}
Expand Down
1 change: 0 additions & 1 deletion scripts/mkstatic.py
Original file line number Diff line number Diff line change
Expand Up @@ -1047,7 +1047,6 @@ def write(self, metadata_request, metadata_define, arguments, debug):
# of host model variables and set necessary default values
var = copy.deepcopy(metadata_define[var_standard_name][0])
var.intent = 'in'
var.optional = 'F'

if not var_standard_name in local_vars.keys():
# The full name of the variable as known to the host model
Expand Down
2 changes: 1 addition & 1 deletion scripts/parse_tools/parse_checkers.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def check_units(test_val, prop_dict, error):
# DH* 20210812
# Temporary workaround to convert unit 'none' (used for
# dimensionless quantities in ccpp-physics/UFS/SCM) to '1'
if test_val.lower() == 'none':
if test_val and test_val.lower() == 'none':
test_val = '1'
# *DH 20210812

Expand Down