diff --git a/scripts/ccpp_capgen.py b/scripts/ccpp_capgen.py index cbd46599..042f6d16 100755 --- a/scripts/ccpp_capgen.py +++ b/scripts/ccpp_capgen.py @@ -314,6 +314,17 @@ def compare_fheader_to_mheader(meta_header, fort_header, logger): lname = mvar.get_prop_value('local_name') arrayref = is_arrayspec(lname) fvar, find = find_var_in_list(lname, flist) + # Check for consistency between optional variables in metadata and + # optional variables in fortran. Error if optional attribute is + # missing from fortran declaration. + mopt = mvar.get_prop_value('optional') + if find and mopt: + fopt = fvar.get_prop_value('optional') + if (not fopt): + errmsg = 'Missing optional attribute in fortran declaration for variable {}, in file {}' + errors_found = add_error(errors_found, errmsg.format(mname,title)) + # end if + # end if if mind >= flen: if arrayref: # Array reference, variable not in Fortran table diff --git a/scripts/ccpp_datafile.py b/scripts/ccpp_datafile.py index 1fd5f830..3c5092ed 100755 --- a/scripts/ccpp_datafile.py +++ b/scripts/ccpp_datafile.py @@ -654,7 +654,7 @@ def _new_var_entry(parent, var, full_entry=True): "diagnostic_name", "diagnostic_name_fixed", "kind", "persistence", "polymorphic", "protected", "state_variable", "type", "units", "molar_mass", - "advected", "top_at_one"]) + "advected", "top_at_one", "optional"]) prop_list.extend(Var.constituent_property_names()) # end if ventry = ET.SubElement(parent, "var") diff --git a/scripts/fortran_tools/parse_fortran.py b/scripts/fortran_tools/parse_fortran.py index 9aa4de7a..e7d3c495 100644 --- a/scripts/fortran_tools/parse_fortran.py +++ b/scripts/fortran_tools/parse_fortran.py @@ -676,7 +676,7 @@ def parse_fortran_var_decl(line, source, run_env): >>> parse_fortran_var_decl("integer :: foo = 0", ParseSource('foo.F90', 'module', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('local_name') 'foo' >>> parse_fortran_var_decl("integer :: foo", ParseSource('foo.F90', 'module', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('optional') - + False >>> parse_fortran_var_decl("integer, optional :: foo", ParseSource('foo.F90', 'module', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('optional') 'True' >>> parse_fortran_var_decl("integer, dimension(:) :: foo", ParseSource('foo.F90', 'module', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('dimensions') diff --git a/scripts/metavar.py b/scripts/metavar.py index 3eb9cd79..2974f1db 100755 --- a/scripts/metavar.py +++ b/scripts/metavar.py @@ -150,9 +150,9 @@ class Var: >>> Var({'local_name' : 'foo', 'standard_name' : 'hi_mom', 'units' : 'm s-1', 'dimensions' : '()', 'type' : 'real', 'intent' : 'ino'}, ParseSource('vname', 'SCHEME', ParseContext()), _MVAR_DUMMY_RUN_ENV) #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ParseSyntaxError: Invalid intent variable property, 'ino', at :1 - >>> Var({'local_name' : 'foo', 'standard_name' : 'hi_mom', 'units' : 'm s-1', 'dimensions' : '()', 'type' : 'real', 'intent' : 'in', 'optional' : 'false'}, ParseSource('vname', 'SCHEME', ParseContext()), _MVAR_DUMMY_RUN_ENV) #doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ParseSyntaxError: Invalid variable property name, 'optional', at :1 + >>> Var({'local_name' : 'foo', 'standard_name' : 'hi_mom', 'units' : 'm s-1', 'dimensions' : '()', 'type' : 'real', 'intent' : 'in', 'optional' : 'false'}, ParseSource('vname', 'SCHEME', ParseContext()), _MVAR_DUMMY_RUN_ENV) #doctest: +ELLIPSIS + + # Check that two variables that differ in their units - m vs km - are compatible >>> Var({'local_name' : 'foo', 'standard_name' : 'hi_mom', 'units' : 'm', \ 'dimensions' : '()', 'type' : 'real', 'intent' : 'in'}, \ @@ -207,6 +207,8 @@ class Var: default_in=False), VariableProperty('top_at_one', bool, optional_in=True, default_in=False), + VariableProperty('optional', bool, optional_in=True, + default_in=False), VariableProperty('target', bool, optional_in=True, default_in=False)] @@ -1025,7 +1027,7 @@ def conditional(self, vdicts): vars_needed.append(dvar) return (conditional, vars_needed) - def write_def(self, outfile, indent, wdict, allocatable=False, + def write_def(self, outfile, indent, wdict, allocatable=False, target=False, dummy=False, add_intent=None, extra_space=0, public=False): """Write the definition line for the variable to . If is True, include the variable's intent. @@ -1077,11 +1079,15 @@ def write_def(self, outfile, indent, wdict, allocatable=False, raise CCPPError(errmsg.format(name)) # end if # end if + optional = self.get_prop_value('optional') if protected and dummy: intent_str = 'intent(in) ' elif allocatable: if dimstr or polymorphic: - intent_str = 'allocatable ' + intent_str = 'allocatable ' + if target: + intent_str = 'allocatable,' + intent_str += ' target' else: intent_str = ' '*13 # end if @@ -1089,11 +1095,14 @@ def write_def(self, outfile, indent, wdict, allocatable=False, alloval = self.get_prop_value('allocatable') if (intent.lower()[-3:] == 'out') and alloval: intent_str = f"allocatable, intent({intent})" + elif optional: + intent_str = f"intent({intent}),{' '*(5 - len(intent))}" + intent_str += 'target, optional ' else: intent_str = f"intent({intent}){' '*(5 - len(intent))}" # end if elif not dummy: - intent_str = '' + intent_str = ' '*20 else: intent_str = ' '*13 # end if @@ -1111,26 +1120,40 @@ def write_def(self, outfile, indent, wdict, allocatable=False, extra_space -= len(targ) if self.is_ddt(): if polymorphic: - dstr = "class({kind}){cspc}{intent} :: {name}{dims} ! {sname}" - cspc = comma + ' '*(extra_space + 12 - len(kind)) + dstr = "class({kind}){cspace}{intent} :: {name}{dims}" + cspace = comma + ' '*(extra_space + 12 - len(kind)) else: - dstr = "type({kind}){cspc}{intent} :: {name}{dims} ! {sname}" - cspc = comma + ' '*(extra_space + 13 - len(kind)) + dstr = "type({kind}){cspace}{intent} :: {name}{dims}" + cspace = comma + ' '*(extra_space + 13 - len(kind)) # end if else: if kind: - dstr = "{type}({kind}){cspc}{intent} :: {name}{dims} ! {sname}" - cspc = comma + ' '*(extra_space + 17 - len(vtype) - len(kind)) + dstr = "{type}({kind}){cspace}{intent} :: {name}{dims}" + cspace = comma + ' '*(extra_space + 17 - len(vtype) - len(kind)) else: - dstr = "{type}{cspc}{intent} :: {name}{dims} ! {sname}" - cspc = comma + ' '*(extra_space + 19 - len(vtype)) + dstr = "{type}{cspace}{intent} :: {name}{dims}" + cspace = comma + ' '*(extra_space + 19 - len(vtype)) # end if # end if - outfile.write(dstr.format(type=vtype, kind=kind, intent=intent_str, - name=name, dims=dimstr, cspc=cspc, + name=name, dims=dimstr, cspace=cspace, sname=stdname), indent) + def write_ptr_def(self, outfile, indent, name, kind, dimstr, vtype, extra_space=0): + """Write the definition line for local null pointer declaration to .""" + comma = ', ' + if kind: + dstr = "{type}({kind}){cspace}pointer :: {name}{dims}{cspace2} => null()" + cspace = comma + ' '*(extra_space + 20 - len(vtype) - len(kind)) + cspace2 = ' '*(20 -len(name) - len(dimstr)) + else: + dstr = "{type}{cspace}pointer :: {name}{dims}{cspace2} => null()" + cspace = comma + ' '*(extra_space + 22 - len(vtype)) + cspace2 = ' '*(20 -len(name) - len(dimstr)) + # end if + outfile.write(dstr.format(type=vtype, kind=kind, name=name, dims=dimstr, + cspace=cspace, cspace2=cspace2), indent) + def is_ddt(self): """Return True iff is a DDT type.""" return not self.__intrinsic diff --git a/scripts/suite_objects.py b/scripts/suite_objects.py index 951e5c86..37978d54 100755 --- a/scripts/suite_objects.py +++ b/scripts/suite_objects.py @@ -143,6 +143,11 @@ def call_string(self, cldicts=None, is_func_call=False, subname=None): raise CCPPError(errmsg.format(stdname, clnames)) # end if lname = dvar.get_prop_value('local_name') + # Optional variables in the caps are associated with + # local pointers of _ptr + if dvar.get_prop_value('optional'): + lname = dummy+'_ptr' + # end if else: cldict = None aref = var.array_ref(local_name=dummy) @@ -871,10 +876,20 @@ def match_variable(self, var, run_env): new_vdims = list() new_dict_dims = dict_dims match = True + # end if # Create compatability object, containing any necessary forward/reverse # transforms from and compat_obj = var.compatible(dict_var, run_env) - + # If variable is defined as "inactive" by the host, ensure that + # this variable is declared as "optional" by the scheme. If + # not satisfied, return error. + host_var_active = dict_var.get_prop_value('active') + scheme_var_optional = var.get_prop_value('optional') + if (not scheme_var_optional and host_var_active.lower() != '.true.'): + errmsg = "Non optional scheme arguments for conditionally allocatable variables" + sname = dict_var.get_prop_value('standard_name') + errmsg += ", {}".format(sname) + raise CCPPError(errmsg) # end if # Add the variable to the parent call tree if dict_dims == new_dict_dims: @@ -1083,6 +1098,7 @@ def __init__(self, scheme_xml, context, parent, run_env): self.__var_debug_checks = list() self.__forward_transforms = list() self.__reverse_transforms = list() + self.__optional_vars = list() super().__init__(name, context, parent, run_env, active_call_list=True) def update_group_call_list_variable(self, var): @@ -1212,10 +1228,21 @@ def analyze(self, phase, group, scheme_library, suite_vars, level): # end if # end if # Are there any forward/reverse transforms for this variable? + has_transform = False if compat_obj is not None and (compat_obj.has_vert_transforms or compat_obj.has_unit_transforms or compat_obj.has_kind_transforms): self.add_var_transform(var, compat_obj, vert_dim) + has_transform = True + # end if + + # Is this a conditionally allocated variable? + # If so, declare localpointer varaible. This is needed to + # pass inactive (not present) status through the caps. + if var.get_prop_value('optional'): + newvar_ptr = var.clone(var.get_prop_value('local_name')+'_ptr') + self.__optional_vars.append([dict_var, var, newvar_ptr, has_transform]) + # end if # end for if self.needs_vertical is not None: @@ -1415,9 +1442,9 @@ def write_var_debug_check(self, var, internal_var, cldicts, outfile, errcode, er tmp_indent = indent + 1 outfile.write(f"if {conditional} then", indent) # end if - outfile.write(f"! Assign value of {local_name} to {internal_var}", tmp_indent) + outfile.write(f"! Assign value of {local_name} to {internal_var_lname}", tmp_indent) outfile.write(f"{internal_var_lname} = {local_name}", tmp_indent) - outfile.write('',indent) + outfile.write('',tmp_indent) if conditional != '.true.': outfile.write(f"end if", indent) # end if @@ -1511,14 +1538,47 @@ def write_var_debug_check(self, var, internal_var, cldicts, outfile, errcode, er tmp_indent = indent + 1 outfile.write(f"if {conditional} then", indent) # end if - outfile.write(f"! Assign lower/upper bounds of {local_name} to {internal_var}", tmp_indent+1) - outfile.write(f"{internal_var_lname} = {local_name}{lbound_string}", tmp_indent+1) - outfile.write(f"{internal_var_lname} = {local_name}{ubound_string}", tmp_indent+1) + outfile.write(f"! Assign lower/upper bounds of {local_name} to {internal_var_lname}", tmp_indent) + outfile.write(f"{internal_var_lname} = {local_name}{lbound_string}", tmp_indent) + outfile.write(f"{internal_var_lname} = {local_name}{ubound_string}", tmp_indent) if conditional != '.true.': outfile.write(f"end if", indent) # end if outfile.write('',indent) + def associate_optional_var(self, dict_var, var, var_ptr, has_transform, cldicts, indent, outfile): + """Write local pointer association for optional variables.""" + if (dict_var): + (conditional, _) = dict_var.conditional(cldicts) + if (has_transform): + lname = var.get_prop_value('local_name')+'_local' + else: + lname = var.get_prop_value('local_name') + # end if + lname_ptr = var_ptr.get_prop_value('local_name') + outfile.write(f"if {conditional} then", indent) + outfile.write(f"{lname_ptr} => {lname}", indent+1) + outfile.write(f"end if", indent) + # end if + + def assign_pointer_to_var(self, dict_var, var, var_ptr, has_transform, cldicts, indent, outfile): + """Assign local pointer to variable.""" + if (dict_var): + intent = var.get_prop_value('intent') + if (intent == 'out' or intent == 'inout'): + (conditional, _) = dict_var.conditional(cldicts) + if (has_transform): + lname = var.get_prop_value('local_name')+'_local' + else: + lname = var.get_prop_value('local_name') + # end if + lname_ptr = var_ptr.get_prop_value('local_name') + outfile.write(f"if {conditional} then", indent) + outfile.write(f"{lname} = {lname_ptr}", indent+1) + outfile.write(f"end if", indent) + # end if + # end if + def add_var_transform(self, var, compat_obj, vert_dim): """Register any variable transformation needed by for this Scheme. For any transformation identified in , create dummy variable @@ -1610,6 +1670,7 @@ def write_var_transform(self, var, dummy, rindices, lindices, compat_obj, rvar_lname=dummy, lvar_indices=rindices, rvar_indices=lindices) + # end if outfile.write(stmt, indent) def write(self, outfile, errcode, errmsg, indent): @@ -1623,14 +1684,29 @@ def write(self, outfile, errcode, errmsg, indent): my_args = self.call_list.call_string(cldicts=cldicts, is_func_call=True, subname=self.subroutine_name) - + # outfile.write('', indent) outfile.write('if ({} == 0) then'.format(errcode), indent) - + # # Write debug checks (operating on variables # coming from the group's call list) + # + if self.__var_debug_checks: + outfile.write('! ##################################################################', indent+1) + outfile.comment('Begin debug tests', indent+1) + outfile.write('! ##################################################################', indent+1) + outfile.write('', indent+1) + # end if for (var, internal_var) in self.__var_debug_checks: stmt = self.write_var_debug_check(var, internal_var, cldicts, outfile, errcode, errmsg, indent+1) + # end for + if self.__var_debug_checks: + outfile.write('! ##################################################################', indent+1) + outfile.comment('End debug tests', indent+1) + outfile.write('! ##################################################################', indent+1) + outfile.write('', indent+1) + # end if + # # Write any reverse (pre-Scheme) transforms. if len(self.__reverse_transforms) > 0: outfile.comment('Compute reverse (pre-scheme) transforms', indent+1) @@ -1638,13 +1714,39 @@ def write(self, outfile, errcode, errmsg, indent): for (dummy, var, rindices, lindices, compat_obj) in self.__reverse_transforms: tstmt = self.write_var_transform(var, dummy, rindices, lindices, compat_obj, outfile, indent+1, False) # end for + outfile.write('',indent+1) + # + # Associate any conditionally allocated variables. + # + if self.__optional_vars: + outfile.write('! Associate conditional variables', indent+1) + # end if + for (dict_var, var, var_ptr, has_transform) in self.__optional_vars: + tstmt = self.associate_optional_var(dict_var, var, var_ptr, has_transform, cldicts, indent+1, outfile) + # end for + # # Write the scheme call. + # stmt = 'call {}({})' outfile.write('',indent+1) outfile.write('! Call scheme', indent+1) outfile.write(stmt.format(self.subroutine_name, my_args), indent+1) - # Write any forward (post-Scheme) transforms. outfile.write('',indent+1) + # + # Copy any local pointers. + # + first_ptr_declaration=True + for (dict_var, var, var_ptr, has_transform) in self.__optional_vars: + if first_ptr_declaration: + outfile.write('! Copy any local pointers to dummy/local variables', indent+1) + first_ptr_declaration=False + # end if + tstmt = self.assign_pointer_to_var(dict_var, var, var_ptr, has_transform, cldicts, indent+1, outfile) + # end for + outfile.write('',indent+1) + # + # Write any forward (post-Scheme) transforms. + # if len(self.__forward_transforms) > 0: outfile.comment('Compute forward (post-scheme) transforms', indent+1) # end if @@ -2184,26 +2286,84 @@ def write(self, outfile, host_arglist, indent, const_mod, group_type = 'run' # Allocate for entire run # end if # Collect information on local variables - subpart_vars = {} + subpart_allocate_vars = {} + subpart_optional_vars = {} + subpart_scalar_vars = {} allocatable_var_set = set() + optional_var_set = set() + pointer_var_set = list() + inactive_var_set = set() for item in [self]:# + self.parts: for var in item.declarations(): lname = var.get_prop_value('local_name') - if lname in subpart_vars: - if subpart_vars[lname][0].compatible(var, self.run_env): + sname = var.get_prop_value('standard_name') + if (lname in subpart_allocate_vars) or (lname in subpart_optional_vars) or (lname in subpart_scalar_vars): + if subpart_allocate_vars[lname][0].compatible(var, self.run_env): pass # We already are going to declare this variable else: errmsg = "Duplicate Group variable, {}" raise ParseInternalError(errmsg.format(lname)) # end if else: - subpart_vars[lname] = (var, item) + opt_var = var.get_prop_value('optional') dims = var.get_dimensions() if (dims is not None) and dims: - allocatable_var_set.add(lname) + if opt_var: + if (self.call_list.find_variable(standard_name=sname)): + subpart_optional_vars[lname] = (var, item, opt_var) + optional_var_set.add(lname) + else: + inactive_var_set.add(var) + # end if + else: + subpart_allocate_vars[lname] = (var, item, opt_var) + allocatable_var_set.add(lname) + # end if + else: + subpart_scalar_vars[lname] = (var, item, opt_var) # end if # end if # end for + # All optional dummy variables within group need to have + # an associated pointer array declared. + for cvar in self.call_list.variable_list(): + opt_var = cvar.get_prop_value('optional') + if opt_var: + name = cvar.get_prop_value('local_name')+'_ptr' + kind = cvar.get_prop_value('kind') + dims = cvar.get_dimensions() + if cvar.is_ddt(): + vtype = 'type' + else: + vtype = cvar.get_prop_value('type') + # end if + if dims: + dimstr = '(:' + ',:'*(len(dims) - 1) + ')' + else: + dimstr = '' + # end if + pointer_var_set.append([name,kind,dimstr,vtype]) + # end if + # end for + # Any optional arguments that are not requested by the host need to have + # a local null pointer passed from the group to the scheme. + for ivar in inactive_var_set: + name = ivar.get_prop_value('local_name')+'_ptr' + kind = ivar.get_prop_value('kind') + dims = ivar.get_dimensions() + if ivar.is_ddt(): + vtype = 'type' + else: + vtype = ivar.get_prop_value('type') + # end if + if dims: + dimstr = '(:' + ',:'*(len(dims) - 1) + ')' + else: + dimstr = '' + # end if + pointer_var_set.append([name,kind,dimstr,vtype]) + # end for + # end for # First, write out the subroutine header subname = self.name @@ -2228,7 +2388,7 @@ def write(self, outfile, host_arglist, indent, const_mod, call_vars = self.call_list.variable_list() self._ddt_library.write_ddt_use_statements(call_vars, outfile, indent+1, pad=modmax) - decl_vars = [x[0] for x in subpart_vars.values()] + decl_vars = [x[0] for x in subpart_allocate_vars.values()] self._ddt_library.write_ddt_use_statements(decl_vars, outfile, indent+1, pad=modmax) outfile.write('', 0) @@ -2240,14 +2400,39 @@ def write(self, outfile, host_arglist, indent, const_mod, self.run_env.logger.debug(msg.format(self.name, call_vars)) # end if self.call_list.declare_variables(outfile, indent+1, dummy=True) - if subpart_vars: + # DECLARE local variables + if subpart_allocate_vars or subpart_scalar_vars or subpart_optional_vars: outfile.write('\n! Local Variables', indent+1) - # Write out local variables - for key in subpart_vars: - var = subpart_vars[key][0] - spdict = subpart_vars[key][1] + # end if + # Scalars + for key in subpart_scalar_vars: + var = subpart_scalar_vars[key][0] + spdict = subpart_scalar_vars[key][1] + target = subpart_scalar_vars[key][2] + var.write_def(outfile, indent+1, spdict, + allocatable=False, target=target) + # end for + # Allocatable arrays + for key in subpart_allocate_vars: + var = subpart_allocate_vars[key][0] + spdict = subpart_allocate_vars[key][1] + target = subpart_allocate_vars[key][2] var.write_def(outfile, indent+1, spdict, - allocatable=(key in allocatable_var_set)) + allocatable=(key in allocatable_var_set), + target=target) + # end for + # Target arrays. + for key in subpart_optional_vars: + var = subpart_optional_vars[key][0] + spdict = subpart_optional_vars[key][1] + target = subpart_optional_vars[key][2] + var.write_def(outfile, indent+1, spdict, + allocatable=(key in optional_var_set), + target=target) + # end for + # Pointer variables + for (name, kind, dim, vtype) in pointer_var_set: + var.write_ptr_def(outfile, indent+1, name, kind, dim, vtype) # end for outfile.write('', 0) # Get error variable names @@ -2269,29 +2454,41 @@ def write(self, outfile, host_arglist, indent, const_mod, raise CCPPError(errmsg.format(self.name)) # end if # Initialize error variables + outfile.write("! Initialize ccpp error handling", 2) outfile.write("{} = 0".format(errcode), 2) outfile.write("{} = ''".format(errmsg), 2) + outfile.write("",2) # end if # Output threaded region check (except for run phase) if not self.run_phase(): + outfile.write("! Output threaded region check ",indent+1) Group.__thread_check.write(outfile, indent, {'phase' : self.phase(), 'errcode' : errcode, 'errmsg' : errmsg}) # Check state machine + outfile.write("! Check state machine",indent+1) self._phase_check_stmts.write(outfile, indent, {'errcode' : errcode, 'errmsg' : errmsg, 'funcname' : self.name}) # Allocate local arrays + outfile.write('\n! Allocate local arrays', indent+1) alloc_stmt = "allocate({}({}))" for lname in allocatable_var_set: - var = subpart_vars[lname][0] + var = subpart_allocate_vars[lname][0] + dims = var.get_dimensions() + alloc_str = self.allocate_dim_str(dims, var.context) + outfile.write(alloc_stmt.format(lname, alloc_str), indent+1) + # end for + for lname in optional_var_set: + var = subpart_optional_vars[lname][0] dims = var.get_dimensions() alloc_str = self.allocate_dim_str(dims, var.context) outfile.write(alloc_stmt.format(lname, alloc_str), indent+1) # end for # Allocate suite vars if allocate: + outfile.write('\n! Allocate suite_vars', indent+1) for svar in suite_vars.variable_list(): dims = svar.get_dimensions() if dims: @@ -2314,12 +2511,26 @@ def write(self, outfile, host_arglist, indent, const_mod, # end for # Write the scheme and subcycle calls for item in self.parts: - item.write(outfile, errcode, errmsg, indent + 1) + item.write(outfile, errcode, errmsg, indent + 1) # end for # Deallocate local arrays + if allocatable_var_set: + outfile.write('\n! Deallocate local arrays', indent+1) + # end if for lname in allocatable_var_set: - outfile.write('deallocate({})'.format(lname), indent+1) + outfile.write('if (allocated({})) {} deallocate({})'.format(lname,' '*(20-len(lname)),lname), indent+1) + # end for + for lname in optional_var_set: + outfile.write('if (allocated({})) {} deallocate({})'.format(lname,' '*(20-len(lname)),lname), indent+1) # end for + # Nullify local pointers + if pointer_var_set: + outfile.write('\n! Nullify local pointers', indent+1) + # end if + for (name, kind, dim, vtype) in pointer_var_set: + #cspace = ' '*(15-len(name)) + outfile.write('if (associated({})) {} nullify({})'.format(name,' '*(15-len(name)),name), indent+1) + # end fo # Deallocate suite vars if deallocate: for svar in suite_vars.variable_list(): diff --git a/test/capgen_test/temp_adjust.F90 b/test/capgen_test/temp_adjust.F90 index 356a86d1..5aba4c0b 100644 --- a/test/capgen_test/temp_adjust.F90 +++ b/test/capgen_test/temp_adjust.F90 @@ -22,7 +22,7 @@ subroutine temp_adjust_run(foo, timestep, temp_prev, temp_layer, qv, ps, & integer, intent(in) :: foo real(kind_phys), intent(in) :: timestep - real(kind_phys), intent(inout) :: qv(:) + real(kind_phys), intent(inout),optional :: qv(:) real(kind_phys), intent(inout) :: ps(:) REAL(kind_phys), intent(in) :: temp_prev(:) REAL(kind_phys), intent(inout) :: temp_layer(foo) @@ -42,7 +42,7 @@ subroutine temp_adjust_run(foo, timestep, temp_prev, temp_layer, qv, ps, & do col_index = 1, foo temp_layer(col_index) = temp_layer(col_index) + temp_prev(col_index) - qv(col_index) = qv(col_index) + 1.0_kind_phys + if (present(qv)) qv(col_index) = qv(col_index) + 1.0_kind_phys end do if (present(innie) .and. present(outie) .and. present(optsie)) then outie = innie * optsie diff --git a/test/capgen_test/temp_adjust.meta b/test/capgen_test/temp_adjust.meta index fc03a434..17eabcdb 100644 --- a/test/capgen_test/temp_adjust.meta +++ b/test/capgen_test/temp_adjust.meta @@ -41,6 +41,7 @@ kind = kind_phys intent = inout diagnostic_name_fixed = Q + optional = True [ ps ] standard_name = surface_air_pressure state_variable = true diff --git a/test/var_compatibility_test/effr_calc.F90 b/test/var_compatibility_test/effr_calc.F90 index 3a6caab0..6dbbb722 100644 --- a/test/var_compatibility_test/effr_calc.F90 +++ b/test/var_compatibility_test/effr_calc.F90 @@ -15,21 +15,24 @@ module effr_calc !> \section arg_table_effr_calc_run Argument Table !! \htmlinclude arg_table_effr_calc_run.html !! - subroutine effr_calc_run(ncol, nlev, effrr_in, effrg_in, effrl_inout, & - effri_out, effrs_inout, has_graupel, scalar_var, & - errmsg, errflg) + subroutine effr_calc_run(ncol, nlev, effrr_in, effrg_in, ncg_in, nci_out, & + effrl_inout, effri_out, effrs_inout, ncl_out, & + has_graupel, scalar_var, errmsg, errflg) integer, intent(in) :: ncol integer, intent(in) :: nlev real(kind_phys), intent(in) :: effrr_in(:,:) - real(kind_phys), intent(in) :: effrg_in(:,:) + real(kind_phys), intent(in),optional :: effrg_in(:,:) + real(kind_phys), intent(in),optional :: ncg_in(:,:) + real(kind_phys), intent(out),optional :: nci_out(:,:) real(kind_phys), intent(inout) :: effrl_inout(:,:) - real(kind_phys), intent(out) :: effri_out(:,:) + real(kind_phys), intent(out),optional :: effri_out(:,:) real(8),intent(inout) :: effrs_inout(:,:) logical, intent(in) :: has_graupel real(kind_phys), intent(inout) :: scalar_var character(len=512), intent(out) :: errmsg integer, intent(out) :: errflg + real(kind_phys), intent(out),optional :: ncl_out(:,:) !---------------------------------------------------------------- real(kind_phys), parameter :: re_qc_min = 2.5 ! microns @@ -37,14 +40,18 @@ subroutine effr_calc_run(ncol, nlev, effrr_in, effrg_in, effrl_inout, & real(kind_phys), parameter :: re_qi_avg = 75. ! microns real(kind_phys) :: effrr_local(ncol,nlev) real(kind_phys) :: effrg_local(ncol,nlev) + real(kind_phys) :: ncg_in_local(ncol,nlev) + real(kind_phys) :: nci_out_local(ncol,nlev) errmsg = '' errflg = 0 effrr_local = effrr_in - if (has_graupel) effrg_local = effrg_in + if (present(effrg_in)) effrg_local = effrg_in + if (present(ncg_in)) ncg_in_local = ncg_in + if (present(nci_out)) nci_out_local = nci_out effrl_inout = min(max(effrl_inout,re_qc_min),re_qc_max) - effri_out = re_qi_avg + if (present(effri_out)) effri_out = re_qi_avg effrs_inout = effrs_inout + 10.0 ! in micrometer scalar_var = 2.0 ! in km diff --git a/test/var_compatibility_test/effr_calc.meta b/test/var_compatibility_test/effr_calc.meta index d1712f3a..73c36ace 100644 --- a/test/var_compatibility_test/effr_calc.meta +++ b/test/var_compatibility_test/effr_calc.meta @@ -25,7 +25,7 @@ type = real kind = kind_phys intent = in - top_at_one = .true. + top_at_one = True [effrg_in] standard_name = effective_radius_of_stratiform_cloud_graupel long_name = effective radius of cloud graupel in micrometer @@ -34,6 +34,25 @@ type = real kind = kind_phys intent = in + optional = True +[ncg_in] + standard_name = cloud_graupel_number_concentration + long_name = number concentration of cloud graupel + units = kg-1 + dimensions = (horizontal_loop_extent,vertical_layer_dimension) + type = real + kind = kind_phys + intent = in + optional = True +[nci_out] + standard_name = cloud_ice_number_concentration + long_name = number concentration of cloud ice + units = kg-1 + dimensions = (horizontal_loop_extent,vertical_layer_dimension) + type = real + kind = kind_phys + intent = out + optional = True [effrl_inout] standard_name = effective_radius_of_stratiform_cloud_liquid_water_particle long_name = effective radius of cloud liquid water particle in micrometer @@ -50,6 +69,7 @@ type = real kind = kind_phys intent = out + optional = True [effrs_inout] standard_name = effective_radius_of_stratiform_cloud_snow_particle long_name = effective radius of cloud snow particle in micrometer @@ -58,7 +78,16 @@ type = real kind = 8 intent = inout - top_at_one = .true. + top_at_one = True +[ncl_out] + standard_name = cloud_liquid_number_concentration + long_name = number concentration of cloud liquid + units = kg-1 + dimensions = (horizontal_loop_extent,vertical_layer_dimension) + type = real + kind = kind_phys + intent = out + optional = True [has_graupel] standard_name = flag_indicating_cloud_microphysics_has_graupel long_name = flag indicating that the cloud microphysics produces graupel diff --git a/test/var_compatibility_test/run_test b/test/var_compatibility_test/run_test index 76ad982e..a5edac37 100755 --- a/test/var_compatibility_test/run_test +++ b/test/var_compatibility_test/run_test @@ -131,33 +131,41 @@ module_list="effr_calc" #dependencies="" suite_list="var_compatibility_suite" required_vars_var_compatibility="ccpp_error_code,ccpp_error_message" +required_vars_var_compatibility="${required_vars_var_compatibility},cloud_graupel_number_concentration" +required_vars_var_compatibility="${required_vars_var_compatibility},cloud_ice_number_concentration" required_vars_var_compatibility="${required_vars_var_compatibility},effective_radius_of_stratiform_cloud_graupel" required_vars_var_compatibility="${required_vars_var_compatibility},effective_radius_of_stratiform_cloud_ice_particle" required_vars_var_compatibility="${required_vars_var_compatibility},effective_radius_of_stratiform_cloud_liquid_water_particle" required_vars_var_compatibility="${required_vars_var_compatibility},effective_radius_of_stratiform_cloud_rain_particle" required_vars_var_compatibility="${required_vars_var_compatibility},effective_radius_of_stratiform_cloud_snow_particle" required_vars_var_compatibility="${required_vars_var_compatibility},flag_indicating_cloud_microphysics_has_graupel" +required_vars_var_compatibility="${required_vars_var_compatibility},flag_indicating_cloud_microphysics_has_ice" required_vars_var_compatibility="${required_vars_var_compatibility},horizontal_dimension" required_vars_var_compatibility="${required_vars_var_compatibility},horizontal_loop_begin" required_vars_var_compatibility="${required_vars_var_compatibility},horizontal_loop_end" required_vars_var_compatibility="${required_vars_var_compatibility},scalar_variable_for_testing" required_vars_var_compatibility="${required_vars_var_compatibility},vertical_layer_dimension" -input_vars_var_compatibility="effective_radius_of_stratiform_cloud_graupel" +input_vars_var_compatibility="cloud_graupel_number_concentration" +#input_vars_var_compatibility="${input_vars_var_compatibility},cloud_ice_number_concentration" +input_vars_var_compatibility="${input_vars_var_compatibility},effective_radius_of_stratiform_cloud_graupel" input_vars_var_compatibility="${input_vars_var_compatibility},effective_radius_of_stratiform_cloud_liquid_water_particle" input_vars_var_compatibility="${input_vars_var_compatibility},effective_radius_of_stratiform_cloud_rain_particle" input_vars_var_compatibility="${input_vars_var_compatibility},effective_radius_of_stratiform_cloud_snow_particle" input_vars_var_compatibility="${input_vars_var_compatibility},flag_indicating_cloud_microphysics_has_graupel" +input_vars_var_compatibility="${input_vars_var_compatibility},flag_indicating_cloud_microphysics_has_ice" input_vars_var_compatibility="${input_vars_var_compatibility},horizontal_dimension" input_vars_var_compatibility="${input_vars_var_compatibility},horizontal_loop_begin" input_vars_var_compatibility="${input_vars_var_compatibility},horizontal_loop_end" input_vars_var_compatibility="${input_vars_var_compatibility},scalar_variable_for_testing" input_vars_var_compatibility="${input_vars_var_compatibility},vertical_layer_dimension" output_vars_var_compatibility="ccpp_error_code,ccpp_error_message" +output_vars_var_compatibility="${output_vars_var_compatibility},cloud_ice_number_concentration" output_vars_var_compatibility="${output_vars_var_compatibility},effective_radius_of_stratiform_cloud_ice_particle" output_vars_var_compatibility="${output_vars_var_compatibility},effective_radius_of_stratiform_cloud_liquid_water_particle" output_vars_var_compatibility="${output_vars_var_compatibility},effective_radius_of_stratiform_cloud_snow_particle" output_vars_var_compatibility="${output_vars_var_compatibility},scalar_variable_for_testing" + ## ## Run a database report and check the return string ## $1 is the report program file diff --git a/test/var_compatibility_test/test_host.F90 b/test/var_compatibility_test/test_host.F90 index 6e170607..3a805a8e 100644 --- a/test/var_compatibility_test/test_host.F90 +++ b/test/var_compatibility_test/test_host.F90 @@ -351,23 +351,26 @@ program test character(len=cs), target :: test_parts1(1) = (/ 'radiation ' /) - character(len=cm), target :: test_invars1(6) = (/ & + character(len=cm), target :: test_invars1(8) = (/ & 'effective_radius_of_stratiform_cloud_rain_particle ', & 'effective_radius_of_stratiform_cloud_liquid_water_particle', & 'effective_radius_of_stratiform_cloud_snow_particle ', & 'effective_radius_of_stratiform_cloud_graupel ', & + 'cloud_graupel_number_concentration ', & 'scalar_variable_for_testing ', & - 'flag_indicating_cloud_microphysics_has_graupel '/) + 'flag_indicating_cloud_microphysics_has_graupel ', & + 'flag_indicating_cloud_microphysics_has_ice '/) - character(len=cm), target :: test_outvars1(6) = (/ & + character(len=cm), target :: test_outvars1(7) = (/ & 'ccpp_error_code ', & 'ccpp_error_message ', & 'effective_radius_of_stratiform_cloud_ice_particle ', & 'effective_radius_of_stratiform_cloud_liquid_water_particle', & 'effective_radius_of_stratiform_cloud_snow_particle ', & + 'cloud_ice_number_concentration ', & 'scalar_variable_for_testing ' /) - character(len=cm), target :: test_reqvars1(9) = (/ & + character(len=cm), target :: test_reqvars1(12) = (/ & 'ccpp_error_code ', & 'ccpp_error_message ', & 'effective_radius_of_stratiform_cloud_rain_particle ', & @@ -375,8 +378,11 @@ program test 'effective_radius_of_stratiform_cloud_liquid_water_particle', & 'effective_radius_of_stratiform_cloud_snow_particle ', & 'effective_radius_of_stratiform_cloud_graupel ', & + 'cloud_graupel_number_concentration ', & + 'cloud_ice_number_concentration ', & 'scalar_variable_for_testing ', & - 'flag_indicating_cloud_microphysics_has_graupel '/) + 'flag_indicating_cloud_microphysics_has_graupel ', & + 'flag_indicating_cloud_microphysics_has_ice '/) type(suite_info) :: test_suites(1) logical :: run_okay diff --git a/test/var_compatibility_test/test_host_data.F90 b/test/var_compatibility_test/test_host_data.F90 index 1980f95a..9d0ca306 100644 --- a/test/var_compatibility_test/test_host_data.F90 +++ b/test/var_compatibility_test/test_host_data.F90 @@ -9,7 +9,9 @@ module test_host_data effrr, & ! effective radius of cloud rain effrl, & ! effective radius of cloud liquid water effri, & ! effective radius of cloud ice - effrg ! effective radius of cloud graupel + effrg, & ! effective radius of cloud graupel + ncg, & ! number concentration of cloud graupel + nci ! number concentration of cloud ice real(kind_phys) :: scalar_var end type physics_state @@ -17,11 +19,12 @@ module test_host_data contains - subroutine allocate_physics_state(cols, levels, state, has_graupel) + subroutine allocate_physics_state(cols, levels, state, has_graupel, has_ice) integer, intent(in) :: cols integer, intent(in) :: levels type(physics_state), intent(out) :: state logical, intent(in) :: has_graupel + logical, intent(in) :: has_ice if (allocated(state%effrr)) then deallocate(state%effrr) @@ -33,16 +36,30 @@ subroutine allocate_physics_state(cols, levels, state, has_graupel) end if allocate(state%effrl(cols, levels)) - if (allocated(state%effri)) then - deallocate(state%effri) - end if - allocate(state%effri(cols, levels)) + if (has_ice) then + if (allocated(state%effri)) then + deallocate(state%effri) + end if + allocate(state%effri(cols, levels)) + endif if (has_graupel) then if (allocated(state%effrg)) then deallocate(state%effrg) end if allocate(state%effrg(cols, levels)) + + if (allocated(state%ncg)) then + deallocate(state%ncg) + end if + allocate(state%ncg(cols, levels)) + endif + + if (has_ice) then + if (allocated(state%nci)) then + deallocate(state%nci) + end if + allocate(state%nci(cols, levels)) endif end subroutine allocate_physics_state diff --git a/test/var_compatibility_test/test_host_data.meta b/test/var_compatibility_test/test_host_data.meta index 01ad7a4a..d3bca89b 100644 --- a/test/var_compatibility_test/test_host_data.meta +++ b/test/var_compatibility_test/test_host_data.meta @@ -25,6 +25,7 @@ dimensions = (horizontal_dimension,vertical_layer_dimension) type = real kind = kind_phys + active = (flag_indicating_cloud_microphysics_has_ice) [effrg] standard_name = effective_radius_of_stratiform_cloud_graupel long_name = effective radius of cloud graupel in meter @@ -33,6 +34,24 @@ type = real kind = kind_phys active = (flag_indicating_cloud_microphysics_has_graupel) +[ncg] + standard_name = cloud_graupel_number_concentration + long_name = number concentration of cloud graupel + units = kg-1 + dimensions = (horizontal_dimension,vertical_layer_dimension) + type = real + kind = kind_phys + intent = in + active = (flag_indicating_cloud_microphysics_has_graupel) +[nci] + standard_name = cloud_ice_number_concentration + long_name = number concentration of cloud ice + units = kg-1 + dimensions = (horizontal_dimension,vertical_layer_dimension) + type = real + kind = kind_phys + intent = in + active = (flag_indicating_cloud_microphysics_has_ice) [scalar_var] standard_name = scalar_variable_for_testing long_name = unused scalar variable diff --git a/test/var_compatibility_test/test_host_mod.F90 b/test/var_compatibility_test/test_host_mod.F90 index 877e3432..ca1d2014 100644 --- a/test/var_compatibility_test/test_host_mod.F90 +++ b/test/var_compatibility_test/test_host_mod.F90 @@ -13,7 +13,8 @@ module test_host_mod integer, parameter :: pver = 4 type(physics_state) :: phys_state real(kind_phys) :: effrs(ncols, pver) - logical, parameter :: mp_has_graupel = .true. + logical, parameter :: has_ice = .true. + logical, parameter :: has_graupel = .true. public :: init_data public :: compare_data @@ -23,14 +24,18 @@ module test_host_mod subroutine init_data() ! Allocate and initialize state - call allocate_physics_state(ncols, pver, phys_state, mp_has_graupel) + call allocate_physics_state(ncols, pver, phys_state, has_graupel, has_ice) phys_state%effrr = 1.0E-3 ! 1000 microns, in meter phys_state%effrl = 1.0E-4 ! 100 microns, in meter - phys_state%effri = 5.0E-5 ! 50 microns, in meter phys_state%scalar_var = 1.0 ! in m effrs = 5.0E-4 ! 500 microns, in meter - if (mp_has_graupel) then + if (has_graupel) then phys_state%effrg = 2.5E-4 ! 250 microns, in meter + phys_state%ncg = 40 + endif + if (has_ice) then + phys_state%effri = 5.0E-5 ! 50 microns, in meter + phys_state%nci = 80 endif end subroutine init_data diff --git a/test/var_compatibility_test/test_host_mod.meta b/test/var_compatibility_test/test_host_mod.meta index 7789dbed..51a2f5c3 100644 --- a/test/var_compatibility_test/test_host_mod.meta +++ b/test/var_compatibility_test/test_host_mod.meta @@ -28,7 +28,13 @@ dimensions = (horizontal_dimension,vertical_layer_dimension) type = real kind = kind_phys -[mp_has_graupel] +[has_ice] + standard_name = flag_indicating_cloud_microphysics_has_ice + long_name = flag indicating that the cloud microphysics produces ice + units = flag + dimensions = () + type = logical +[has_graupel] standard_name = flag_indicating_cloud_microphysics_has_graupel long_name = flag indicating that the cloud microphysics produces graupel units = flag diff --git a/test/var_compatibility_test/test_reports.py b/test/var_compatibility_test/test_reports.py index fd5e5203..aaf08953 100755 --- a/test/var_compatibility_test/test_reports.py +++ b/test/var_compatibility_test/test_reports.py @@ -72,12 +72,15 @@ def usage(errmsg=None): "effective_radius_of_stratiform_cloud_rain_particle", "effective_radius_of_stratiform_cloud_snow_particle", "effective_radius_of_stratiform_cloud_graupel", + "cloud_graupel_number_concentration", "scalar_variable_for_testing", - "flag_indicating_cloud_microphysics_has_graupel"] + "flag_indicating_cloud_microphysics_has_graupel", + "flag_indicating_cloud_microphysics_has_ice"] _OUTPUT_VARS_VAR_ACTION = ["ccpp_error_code", "ccpp_error_message", "effective_radius_of_stratiform_cloud_ice_particle", "effective_radius_of_stratiform_cloud_liquid_water_particle", "effective_radius_of_stratiform_cloud_snow_particle", + "cloud_ice_number_concentration", "scalar_variable_for_testing"] _REQUIRED_VARS_VAR_ACTION = _INPUT_VARS_VAR_ACTION + _OUTPUT_VARS_VAR_ACTION