diff --git a/scripts/ccpp_suite.py b/scripts/ccpp_suite.py index 7fa5e88a..130a828c 100644 --- a/scripts/ccpp_suite.py +++ b/scripts/ccpp_suite.py @@ -78,6 +78,20 @@ class Suite(VarDictionary): __scheme_template = '{}' + # The var_type_vals are bits to describe the status of a variable in + # each phase with a single number. + # Each phase is 2 bits with the following table: + # 0: Variable not present in this phase + # 1: Variable is intent(in) in this phase + # 2: Variable is intent(out) in this phase + # 3: Variable is intent(inout) in this phase + __var_type_vals = [f"{phase}_{bit}_bitpos" + for phase in CCPP_STATE_MACH.transitions() + for bit in ("input", "output")] + # __struct_element is a bit for variables which are part of a DDT + __struct_element = "struct_element" + __var_type_vals.append(__struct_element) + def __init__(self, filename, api, run_env): """Initialize this Suite object from the SDF, . serves as the Suite's parent.""" @@ -111,6 +125,11 @@ def __init__(self, filename, api, run_env): # end if # Parse the SDF self.parse(run_env) + # For completeness, we initilize these here, however, + # they will receive correct values at the end of analyze + self.__regular_vars = None + self.__parent_vars = None + self.__elem_vars = None @property def name(self): @@ -261,6 +280,78 @@ def groups(self): """Get the list of groups in this suite.""" return self.__groups + def _interface_vars_dict(self, check_dict, ddt_lib): + """Collect the input, output, and inout variables for each phase + of this suite.""" + parent = self.parent + # Collect all the suite variables, by type + regular_vars = {} # Fortran intrinsics + parent_vars = {} # E.g., DDT + elem_vars = {} # Intrinsic vars inside a DDT + for part in self.groups: + phase = part.phase() + for var in part.call_list.variable_list(): + stdname = var.get_prop_value("standard_name") + intent = var.get_prop_value("intent") + protected = var.get_prop_value("protected") + if (parent is not None) and (not protected): + pvar = parent.find_variable(standard_name=stdname) + if pvar is not None: + protected = pvar.get_prop_value("protected") + # end if + # end if + elements = var.intrinsic_elements(check_dict=check_dict, + ddt_lib=ddt_lib) + if (intent == 'in') and (not protected): + if isinstance(elements, list): + self._add_vars_to_dict(parent_vars, stdname, + phase, "input") + self._add_vars_to_dict(elem_vars, elements, + phase, "input") + else: + self._add_vars_to_dict(regular_vars, stdname, + phase, "input") + # end if + elif intent == 'inout': + if isinstance(elements, list): + self._add_vars_to_dict(parent_vars, stdname, + phase, "input") + self._add_vars_to_dict(elem_vars, elements, + phase, "input") + self._add_vars_to_dict(parent_vars, stdname, + phase, "output") + self._add_vars_to_dict(elem_vars, elements, + phase, "output") + else: + self._add_vars_to_dict(regular_vars, stdname, + phase, "input") + self._add_vars_to_dict(regular_vars, stdname, + phase, "output") + # end if + elif intent == 'out': + if isinstance(elements, list): + self._add_vars_to_dict(parent_vars, stdname, + phase, "output") + self._add_vars_to_dict(elem_vars, elements, + phase, "output") + else: + self._add_vars_to_dict(regular_vars, stdname, + phase, "output") + # end if + # end if + # end for + # end for + # Check for overlap (there should not be any) + reg_set = set(regular_vars.keys()) + par_set = set(parent_vars.keys()) + if not reg_set.isdisjoint(par_set): + overlap = reg_set.intersection(par_set) + emsg = (f"Suite {self.name}: Standard name overlap with: " + + f"{', '.join(overlap)}.") + raise CCPPError(emsg) + # end if + return regular_vars, parent_vars, elem_vars + def find_variable(self, standard_name=None, source_var=None, any_scope=True, clone=None, search_call_list=False, loop_subst=False): @@ -446,6 +537,9 @@ def analyze(self, host_model, scheme_library, ddt_library, run_env): # end if # end for # end for + # Finally, collect usage dictionaries of Suite variables + args = self._interface_vars_dict(host_model, ddt_library) + self.__regular_vars, self.__parent_vars, self.__elem_vars = args def is_run_group(self, group): """Method to separate out run-loop groups from special initial @@ -485,7 +579,208 @@ def constituent_dictionary(self): """Return the constituent dictionary for this suite""" return self.parent - def write(self, output_dir, run_env): + @staticmethod + def _add_vars_to_dict(vdict, stdname, phase, inout): + # Add a presence flag to the set in + # Add a new set if is not in + # If is a list, apply this function to each element + if isinstance(stdname, list): + for var in stdname: + self._add_vars_to_dict(vdict, var, phase, inout) + # end for + else: + if not stdname in vdict: + vdict[stdname] = set() + # end if + vdict[stdname].add(f"{phase}_{inout}_bitpos") + # end if + + def req_vars_subname(self): + """Return the name of the required variables subroutine for this suite""" + return f"suite_{self.name}_variables" + + def write_req_vars_sub(self, ofile, errvars): + """Write the required variables subroutine""" + bitfield_funcname = "find_bitfld_val" + varmatch_funcname = "var_match" + # Find error variables (only support old style for now) + errmsg_var = None + errcode_var= None + for evar in errvars: + if evar.get_prop_value('standard_name') == 'ccpp_error_code': + errcode_var = evar + elif evar.get_prop_value('standard_name') == 'ccpp_error_message': + errmsg_var = evar + else: + raise ParseInternalError("Unsupported error variable") + # end if + # end for + if (errmsg_var == None) or (errcode_var == None): + emsg = "Could not find error variable names: errvars = " + emsg += f"{', '.join([str(x) for x in errvars])}" + raise ParseInternalError(emsg) + # end if + errmsg = errmsg_var.get_prop_value('local_name') + errcode = errcode_var.get_prop_value('local_name') + inargs = f"variable_list, {errmsg}, {errcode}" + inargs += ", input_vars, output_vars, phases, struct_elements" + ofile.blank_line() + ofile.write(f"subroutine {self.req_vars_subname()}({inargs})", 1) + ofile.write("! Dummy arguments", 2) + vtype = "character(len=*)" + oline = f"{vtype}, allocatable, intent(out) :: variable_list(:)" + ofile.write(oline, 2) + errmsg_var.write_def(ofile, 2, self, + extra_space=11, dummy=True, add_intent="out") + errcode_var.write_def(ofile, 2, self, + extra_space=11, dummy=True, add_intent="out") + oline = "logical, optional, intent(in) :: input_vars" + ofile.write(oline, 2) + oline = "logical, optional, intent(in) :: output_vars" + ofile.write(oline, 2) + oline = "character(len=*), optional, intent(in) :: phases(:)" + ofile.write(oline, 2) + oline = f"logical,{' '*9} optional, intent(in) :: struct_elements" + ofile.write(oline, 2) + ofile.write("! Local variables", 2) + spc = 37 + ofile.write(f"logical {' '*spc}:: input_vars_use", 2) + ofile.write(f"logical {' '*spc}:: output_vars_use", 2) + ofile.write(f"logical {' '*spc}:: struct_elements_use", 2) + mlen = max([len(x.phase()) for x in self.groups]) + mspc = ' '*15 + ofile.write(f"character(len={mlen}), allocatable{mspc}:: phases_use(:)", + 2) + ofile.write(f"integer {' '*spc}:: num_vars", 2) + ofile.write(f"integer {' '*spc}:: var_index", 2) + ofile.write(f"integer(int_kind) {' '*(spc - 10)}:: var_mask", 2) + ofile.write(f"integer(int_kind) {' '*(spc - 10)}:: ptype_val", 2) + ofile.write(f"integer {' '*spc}:: out_index", 2) + ofile.write(f"integer {' '*spc}:: ierr", 2) + ofile.blank_line() + ofile.write(f"{errcode} = 0", 2) + ofile.write(f"{errmsg} = ''", 2) + ofile.write("if (present(input_vars)) then", 2) + ofile.write("input_vars_use = input_vars", 3) + ofile.write("else", 2) + ofile.write("input_vars_use = .true.", 3) + ofile.write("end if", 2) + ofile.write("if (present(output_vars)) then", 2) + ofile.write("output_vars_use = output_vars", 3) + ofile.write("else", 2) + ofile.write("output_vars_use = .true.", 3) + ofile.write("end if", 2) + ofile.write("if (present(phases)) then", 2) + ofile.write("allocate(phases_use(size(phases, 1)), stat=ierr)", 3) + ofile.write("if (ierr /= 0) then", 3) + ofile.write(f"{errcode} = ierr", 4) + ofile.write(f"{errmsg} = 'Unable to allocate phases_use'", 4) + ofile.write("return", 4) + ofile.write("end if", 3) + ofile.write("phases_use(:) = phases(:)", 3) + ofile.write("else", 2) + pnames = [f"\"{x.phase()}{' '*(mlen - len(x.phase()))}\"" + for x in self.groups] + ofile.write(f"allocate(phases_use({len(pnames)}), stat=ierr)", 3) + ofile.write("if (ierr /= 0) then", 3) + ofile.write(f"{errcode} = ierr", 4) + ofile.write(f"{errmsg} = 'Unable to allocate phases_use'", 4) + ofile.write("return", 4) + ofile.write("end if", 3) + ofile.write(f"phases_use(:) = (/ {', '.join(pnames)} /)", 3) + ofile.write("end if", 2) + ofile.write("if (present(struct_elements)) then", 2) + ofile.write("struct_elements_use = struct_elements", 3) + ofile.write("else", 2) + ofile.write("struct_elements_use = .true.", 3) + ofile.write("end if", 2) + ofile.comment("Find the correct variable mask based on phase and type", + 2) + ofile.write("var_mask = 0_int_kind", 2) + ofile.write("do var_index = 1, size(phases_use, 1)", 2) + ofile.write("if (input_vars_use) then", 3) + args = f"(phases_use(var_index), 'input')" + ofile.write(f"ptype_val = {bitfield_funcname}{args}", 4) + ofile.write("if (ptype_val == 0_int_kind) then", 4) + ofile.write(f"{errcode} = 1", 5) + emsg = "'No bitval for input phase = '//trim(phases_use(var_index))" + ofile.write(f"{errmsg} = {emsg}", 5) + ofile.write("return", 5) + ofile.write("end if", 4) + ofile.write("var_mask = var_mask + ptype_val", 4) + ofile.write("end if", 3) + ofile.write("if (output_vars_use) then", 3) + args = f"(phases_use(var_index), 'output')" + ofile.write(f"ptype_val = {bitfield_funcname}{args}", 4) + ofile.write("if (ptype_val == 0_int_kind) then", 4) + ofile.write(f"{errcode} = 1", 5) + emsg = "'No bitval for output phase = '//trim(phases_use(var_index))" + ofile.write(f"{errmsg} = {emsg}", 5) + ofile.write("return", 5) + ofile.write("end if", 4) + ofile.write("var_mask = var_mask + ptype_val", 4) + ofile.write("end if", 3) + ofile.write("end do", 2) + ofile.comment("Find the number of variables that match the pattern", 2) + ofile.write("num_vars = 0", 2) + ofile.write("do var_index = 1, size(suite_bitvals, 1)", 2) + ofile.write(f"if ({varmatch_funcname}(suite_bitvals(var_index))) then", + 3) + ofile.write("num_vars = num_vars + 1", 4) + ofile.write("end if", 3) + ofile.write("end do", 2) + ofile.write("allocate(variable_list(num_vars), stat=ierr)", 2) + ofile.write("if (ierr /= 0) then", 2) + ofile.write(f"{errcode} = ierr", 3) + ofile.write(f"{errmsg} = 'Unable to allocate variable_list'", 3) + ofile.write("return", 3) + ofile.write("end if", 2) + ofile.write("out_index = 0", 2) + ofile.write("do var_index = 1, size(suite_allvars, 1)", 2) + ofile.write(f"if ({varmatch_funcname}(suite_bitvals(var_index))) then", + 3) + ofile.write("out_index = out_index + 1", 4) + ofile.write("variable_list(out_index) = suite_allvars(var_index)", 4) + ofile.write("end if", 3) + ofile.write("end do", 2) + ofile.blank_line() + ofile.write("contains", 1) + ofile.blank_line() + ofile.comment("Find the bit value for this phase and inout type", 2) + int_type = "integer(int_kind)" + ofile.write(f"{int_type} function {bitfield_funcname}(phase, iotype)", 2) + ofile.write("character(len=*), intent(in) :: phase", 3) + ofile.write("character(len=*), intent(in) :: iotype", 3) + ofile.write("character(len=:), allocatable :: ptype_name", 3) + ofile.write("ptype_name = trim(phase)//'_'//trim(iotype)//'_bitpos'", 3) + ofile.write("select case(trim(ptype_name))", 3) + for vtype in self.__var_type_vals: + if vtype == self.__struct_element: + continue + # end if + ofile.write(f"case ('{vtype}')", 3) + ofile.write(f"{bitfield_funcname} = {vtype}", 4) + # end for + ofile.write("case default", 3) + ofile.comment("Error, return 0: Caller must trap", 4) + ofile.write(f"{bitfield_funcname} = 0", 4) + ofile.write("end select", 3) + ofile.write(f"end function {bitfield_funcname}", 2) + ofile.blank_line() + ofile.write(f"logical function {varmatch_funcname}(var_bfield)", 2) + ofile.write("integer(int_kind), intent(in) :: var_bfield", 3) + ofile.write(f"{varmatch_funcname} = .false.", 3) + etest = "if (struct_elements_use .or. " + etest += "(iand(struct_element, var_bfield) == 0_int_kind)) then" + ofile.write(etest, 3) + ofile.write(f"{varmatch_funcname} = iand(var_bfield, var_mask) /= 0", 4) + ofile.write("end if", 3) + ofile.write(f"end function {varmatch_funcname}", 2) + ofile.blank_line() + ofile.write(f"end subroutine {self.req_vars_subname()}", 1) + ofile.blank_line() + + def write(self, output_dir, run_env, check_dict, ddt_lib): """Create caps for all groups in the suite and for the entire suite (calling the group caps one after another)""" # Set name of module and filename of cap @@ -501,6 +796,18 @@ def write(self, output_dir, run_env): "CCPP Suite Cap for {}".format(self.name), self.module) as outfile: # Write module 'use' statements here + # For proper conversion of binary literals + bitmax = len(self.__var_type_vals) + 1 + if bitmax >= 64: + raise ParseInternalError("Not enough bits for Suite var types") + # end if + if bitmax >= 32: + int_str = "INT64" + else: + int_str = "INT32" + # end if + outfile.write(f"use ISO_FORTRAN_ENV, only: int_kind => {int_str}", + 1) outfile.write('use {}'.format(KINDS_MODULE), 1) # Look for any DDT types self.__ddt_library.write_ddt_use_statements(self.values(), @@ -516,8 +823,9 @@ def write(self, output_dir, run_env): outfile.write(line.format(css_var_name=var_name, state=var_state), 1) for group in self.__groups: - outfile.write('public :: {}'.format(group.name), 1) + outfile.write(f"public :: {group.name}", 1) # end for + outfile.write(f"public :: {self.req_vars_subname()}", 1) # Declare constituent public interfaces const_dict.declare_public_interfaces(outfile, 1) # Declare constituent private suite interfaces and data @@ -526,6 +834,66 @@ def write(self, output_dir, run_env): for svar in self.keys(): self[svar].write_def(outfile, 1, self, allocatable=True) # end for + # Declare suite variable variables and parameters + int_type = f"integer(int_kind)" + bitpos = 1 # Position in value where 1 is least significant + bitfld = 1 # Value of binary digit at bitpos + valbits = {} # Remember bitfld of each entry type + mspc = max([len(x) for x in self.__var_type_vals]) + vtype_names = [] + # First, the bitpos parameters + for vtype in self.__var_type_vals: + vfld = f"{vtype}{' '*(mspc - len(vtype))}" + bitstr = f"int(b'{bitfld:0{bitmax}b}', int_kind)" + outfile.write(f"{int_type}, parameter :: {vfld} = {bitstr}", 1) + valbits[f"{vtype}"] = bitfld + vtype_names.append(f'"{vfld}"') + bitpos += 1 + bitfld *= 2 + # end for + # Now some strings so we can find the right pos + oline = f"character(len={mspc}), private :: vartype_names" + oline += f"({len(valbits)}) = (/ {', '.join(vtype_names)} /)" + outfile.write(oline, 1) + # Collect all the Suite variables in one list + allvars = set() + allvars.update(self.__regular_vars.keys()) + allvars.update(self.__parent_vars.keys()) + allvars.update(self.__elem_vars.keys()) + # Change allvars to a list for a consistent ordering + allvars = sorted(allvars) + mspc = max([len(x) for x in allvars]) + nvars = len(allvars) + # Declare an array of all this Suite's variable standard names + decl = f"character(len={mspc}), private :: suite_allvars({nvars}) = " + vlist = ', '.join([f"\"{x}{' '*(mspc - len(x))}\"" for x in allvars]) + decl += f"(/ {vlist} /)" + outfile.write(decl, 1) + + # Declare a bitfield for each variable with phase and type info + decl = f"{int_type}, private :: suite_bitvals({nvars}) = " + vlist = [] + for stdname in allvars: + if stdname in self.__regular_vars: + var = self.__regular_vars[stdname] + varval = 0 + elif stdname in self.__parent_vars: + var = self.__parent_vars[stdname] + varval = 0 + elif stdname in self.__elem_vars: + var = self.__elem_vars[stdname] + varval = valbits[self.__struct_element] + else: + raise ParseInternalError(f"No var for '{stdname}'?") + # end if + # Compute value of variable + for entry in var: + varval += valbits[entry] + # end for + vlist.append(f"int(b'{varval:0{bitmax}b}', int_kind)") + # end for + decl += f"(/ {', '.join(vlist)} /)" + outfile.write(decl, 1) outfile.end_module_header() for group in self.__groups: if group.name in self._beg_groups: @@ -541,6 +909,8 @@ def write(self, output_dir, run_env): # end for err_vars = self.find_error_variables(any_scope=True, clone_as_out=True) + # Write the required variables subroutine for this suite + self.write_req_vars_sub(outfile, err_vars) # Write the constituent properties interface const_dict.write_constituent_routines(outfile, 1, self.name, err_vars) @@ -687,7 +1057,8 @@ def write(self, output_dir, run_env): api_filenames = list() # Write out the suite files for suite in self.suites: - out_file_name = suite.write(output_dir, run_env) + out_file_name = suite.write(output_dir, run_env, + self.parent, self.__ddt_lib) api_filenames.append(out_file_name) # end for return api_filenames @@ -760,308 +1131,47 @@ def write_suite_part_list_sub(self, ofile, errmsg_name, errcode_name): def write_req_vars_sub(self, ofile, errmsg_name, errcode_name): """Write the required variables subroutine""" oline = "suite_name, variable_list, {errmsg}, {errcode}" - oline += ", input_vars, output_vars, struct_elements" + oline += ", input_vars, output_vars, phases, struct_elements" inargs = oline.format(errmsg=errmsg_name, errcode=errcode_name) ofile.write("\nsubroutine {}({})".format(API.__vars_fname, inargs), 1) + # Declare use statements for suite varlist routines + mlen = max([len(x.module) for x in self.suites]) + for suite in self.suites: + mod = f"{suite.module}{' '*(mlen - len(suite.module))}" + ofile.write(f"use {mod}, only: {suite.req_vars_subname()}", 2) + # end for ofile.write("! Dummy arguments", 2) oline = "character(len=*), intent(in) :: suite_name" ofile.write(oline, 2) oline = "character(len=*), allocatable, intent(out) :: variable_list(:)" ofile.write(oline, 2) - self._errmsg_var.write_def(ofile, 2, self, extra_space=22) - self._errcode_var.write_def(ofile, 2, self, extra_space=22) - oline = "logical, optional, intent(in) :: input_vars" + self._errmsg_var.write_def(ofile, 2, self, extra_space=11, + dummy=True, add_intent="out") + self._errcode_var.write_def(ofile, 2, self, extra_space=11, + dummy=True, add_intent="out") + oline = "logical, optional, intent(in) :: input_vars" ofile.write(oline, 2) - oline = "logical, optional, intent(in) :: output_vars" + oline = "logical, optional, intent(in) :: output_vars" ofile.write(oline, 2) - oline = "logical, optional, intent(in) :: struct_elements" + oline = "character(len=*), optional, intent(in) :: phases(:)" ofile.write(oline, 2) - ofile.write("! Local variables", 2) - ofile.write("logical {}:: input_vars_use".format(' '*34), 2) - ofile.write("logical {}:: output_vars_use".format(' '*34), 2) - ofile.write("logical {}:: struct_elements_use".format(' '*34), 2) - ofile.write("integer {}:: num_vars".format(' '*34), 2) - ofile.write("", 0) - ename = self._errcode_var.get_prop_value('local_name') - ofile.write("{} = 0".format(ename), 2) - ename = self._errmsg_var.get_prop_value('local_name') - ofile.write("{} = ''".format(ename), 2) - ofile.write("if (present(input_vars)) then", 2) - ofile.write("input_vars_use = input_vars", 3) - ofile.write("else", 2) - ofile.write("input_vars_use = .true.", 3) - ofile.write("end if", 2) - ofile.write("if (present(output_vars)) then", 2) - ofile.write("output_vars_use = output_vars", 3) - ofile.write("else", 2) - ofile.write("output_vars_use = .true.", 3) - ofile.write("end if", 2) - ofile.write("if (present(struct_elements)) then", 2) - ofile.write("struct_elements_use = struct_elements", 3) - ofile.write("else", 2) - ofile.write("struct_elements_use = .true.", 3) - ofile.write("end if", 2) + oline = "logical, optional, intent(in) :: struct_elements" + ofile.write(oline, 2) + ofile.blank_line() + ecname = self._errcode_var.get_prop_value('local_name') + ofile.write(f"{ecname} = 0", 2) + emname = self._errmsg_var.get_prop_value('local_name') + ofile.write(f"{emname} = ''", 2) else_str = '' for suite in self.suites: - parent = suite.parent - # Collect all the suite variables - oline = "{}if(trim(suite_name) == '{}') then" - input_vars = [set(), set(), set()] # leaves, arrays, leaf elements - inout_vars = [set(), set(), set()] # leaves, arrays, leaf elements - output_vars = [set(), set(), set()] # leaves, arrays, leaf elements - const_initialized_in_physics = {} - for part in suite.groups: - for var in part.call_list.variable_list(): - phase = part.phase() - stdname = var.get_prop_value("standard_name") - intent = var.get_prop_value("intent") - protected = var.get_prop_value("protected") - constituent = var.is_constituent() - if stdname not in const_initialized_in_physics: - const_initialized_in_physics[stdname] = False - if (parent is not None) and (not protected): - pvar = parent.find_variable(standard_name=stdname) - if pvar is not None: - protected = pvar.get_prop_value("protected") - # end if - # end if - elements = var.intrinsic_elements(check_dict=self.parent, - ddt_lib=self.__ddt_lib) - if (intent == 'in') and (not protected) and (not const_initialized_in_physics[stdname]): - if isinstance(elements, list): - input_vars[1].add(stdname) - input_vars[2].update(elements) - else: - input_vars[0].add(stdname) - # end if - elif intent == 'inout' and (not const_initialized_in_physics[stdname]): - if isinstance(elements, list): - inout_vars[1].add(stdname) - inout_vars[2].update(elements) - else: - inout_vars[0].add(stdname) - # end if - elif intent == 'out' and phase != 'initialize' and constituent and not const_initialized_in_physics[stdname]: - # constituents HAVE to be initialized in the init phase because the dycore needs to advect them - emsg = "constituent variable '{}' cannot be initialized in the '{}' phase" - raise CCPPError(emsg.format(stdname, phase)) - elif intent == 'out' and constituent and phase == 'initialize': - const_initialized_in_physics[stdname] = True - elif intent == 'out': - if isinstance(elements, list): - output_vars[1].add(stdname) - output_vars[2].update(elements) - else: - output_vars[0].add(stdname) - # end if - # end if - # end for - # end for - # Figure out how many total variables to return and allocate - # variable_list to that size - ofile.write(oline.format(else_str, suite.name), 2) - ofile.write("if (input_vars_use .and. output_vars_use) then", 3) - have_elems = input_vars[2] or inout_vars[2] or output_vars[2] - if have_elems: - ofile.write("if (struct_elements_use) then", 4) - numvars = len(input_vars[0] | input_vars[2] | inout_vars[0] | - inout_vars[2] | output_vars[0] | output_vars[2]) - ofile.write("num_vars = {}".format(numvars), 5) - ofile.write("else", 4) - # end if - numvars = len(input_vars[0] | input_vars[1] | inout_vars[0] | - inout_vars[1] | output_vars[0] | output_vars[1]) - ofile.write("num_vars = {}".format(numvars), 5 if have_elems else 4) - if have_elems: - ofile.write("end if", 4) - # end if - ofile.write("else if (input_vars_use) then", 3) - have_elems = input_vars[2] or inout_vars[2] - if have_elems: - ofile.write("if (struct_elements_use) then", 4) - numvars = len(input_vars[0] | input_vars[2] | - inout_vars[0] | inout_vars[2]) - ofile.write("num_vars = {}".format(numvars), 5) - ofile.write("else", 4) - # end if - numvars = len(input_vars[0] | input_vars[1] | - inout_vars[0] | inout_vars[1]) - ofile.write("num_vars = {}".format(numvars), 5 if have_elems else 4) - if have_elems: - ofile.write("end if", 4) - # end if - ofile.write("else if (output_vars_use) then", 3) - have_elems = inout_vars[2] or output_vars[2] - if have_elems: - ofile.write("if (struct_elements_use) then", 4) - numvars = len(inout_vars[0] | inout_vars[2] | - output_vars[0] | output_vars[2]) - ofile.write("num_vars = {}".format(numvars), 5) - ofile.write("else", 4) - # end if - numvars = len(inout_vars[0] | inout_vars[1] | - output_vars[0] | output_vars[1]) - ofile.write("num_vars = {}".format(numvars), 5 if have_elems else 4) - if have_elems: - ofile.write("end if", 4) - # end if - ofile.write("else", 3) - ofile.write("num_vars = 0", 4) - ofile.write("end if", 3) - ofile.write("allocate(variable_list(num_vars))", 3) - # Now, fill in the variable_list array - # Start with inout variables - elem_start = 1 - leaf_start = 1 - leaf_written_set = inout_vars[0].copy() - elem_written_set = inout_vars[0].copy() - leaf_list = sorted(inout_vars[0]) - if inout_vars[0] or inout_vars[1] or inout_vars[2]: - ofile.write("if (input_vars_use .or. output_vars_use) then", 3) - API.write_var_set_loop(ofile, 'variable_list', leaf_list, 4, - add_allocate=False, - start_index=leaf_start) - # end if - leaf_start += len(leaf_list) - elem_start += len(leaf_list) - # elements which have not been written out - elem_list = sorted(inout_vars[2] - elem_written_set) - elem_written_set = elem_written_set | inout_vars[2] - leaf_list = sorted(inout_vars[1] - leaf_written_set) - leaf_written_set = leaf_written_set | inout_vars[1] - if elem_list or leaf_list: - ofile.write("if (struct_elements_use) then", 4) - API.write_var_set_loop(ofile, 'variable_list', elem_list, 5, - add_allocate=False, - start_index=elem_start) - elem_start += len(elem_list) - ofile.write("num_vars = {}".format(elem_start - 1), 5) - ofile.write("else", 4) - API.write_var_set_loop(ofile, 'variable_list', leaf_list, 5, - add_allocate=False, - start_index=leaf_start) - leaf_start += len(leaf_list) - ofile.write("num_vars = {}".format(leaf_start - 1), 5) - ofile.write("end if", 4) - else: - ofile.write("num_vars = {}".format(len(leaf_written_set)), - 4 if leaf_written_set else 3) - # end if - if inout_vars[0] or inout_vars[1] or inout_vars[2]: - ofile.write("end if", 3) - # end if - # Write input variables - leaf_list = sorted(input_vars[0] - leaf_written_set) - # Are there any output variables which are also input variables - # (e.g., for a different part (group) of the suite)? - # We need to collect them now in case is selected - # but not . - leaf_cross_set = output_vars[0] & input_vars[0] - simp_cross_set = (output_vars[1] & input_vars[1]) - leaf_cross_set - elem_cross_set = (output_vars[2] & input_vars[2]) - leaf_cross_set - # Subtract the variables which have already been written out - leaf_cross_list = sorted(leaf_cross_set - leaf_written_set) - simp_cross_list = sorted(simp_cross_set - leaf_written_set) - elem_cross_list = sorted(elem_cross_set - elem_written_set) - # Next move back to processing the input variables - leaf_written_set = leaf_written_set | input_vars[0] - elem_list = sorted(input_vars[2] - elem_written_set) - elem_written_set = elem_written_set | input_vars[0] | input_vars[2] - have_inputs = elem_list or leaf_list - if have_inputs: - ofile.write("if (input_vars_use) then", 3) - # elements which have not been written out - # end if - API.write_var_set_loop(ofile, 'variable_list', leaf_list, 4, - add_allocate=False, start_var="num_vars", - start_index=1) - if leaf_list: - ofile.write("num_vars = num_vars + {}".format(len(leaf_list)), - 4) - # end if - leaf_start += len(leaf_list) - elem_start += len(leaf_list) - leaf_list = input_vars[1].difference(leaf_written_set) - leaf_written_set.union(input_vars[1]) - if elem_list or leaf_list: - ofile.write("if (struct_elements_use) then", 4) - API.write_var_set_loop(ofile, 'variable_list', elem_list, 5, - add_allocate=False, - start_index=elem_start) - elem_start += len(elem_list) - 1 - ofile.write("num_vars = {}".format(elem_start), 5) - ofile.write("else", 4) - API.write_var_set_loop(ofile, 'variable_list', leaf_list, 5, - add_allocate=False, - start_index=leaf_start) - leaf_start += len(leaf_list) - 1 - ofile.write("num_vars = {}".format(leaf_start), 5) - ofile.write("end if", 4) - # end if - if have_inputs: - ofile.write("end if", 3) - # end if - # Write output variables - leaf_list = sorted(output_vars[0].difference(leaf_written_set)) - leaf_written_set = leaf_written_set.union(output_vars[0]) - elem_written_set = elem_written_set.union(output_vars[0]) - elem_list = sorted(output_vars[2].difference(elem_written_set)) - elem_written_set = elem_written_set.union(output_vars[2]) - have_outputs = elem_list or leaf_list - if have_outputs: - ofile.write("if (output_vars_use) then", 3) - # end if - leaf_start = 1 - API.write_var_set_loop(ofile, 'variable_list', leaf_list, 4, - add_allocate=False, start_var="num_vars", - start_index=leaf_start) - leaf_start += len(leaf_list) - elem_start = leaf_start - leaf_list = output_vars[1].difference(leaf_written_set) - leaf_written_set.union(output_vars[1]) - if elem_list or leaf_list: - ofile.write("if (struct_elements_use) then", 4) - API.write_var_set_loop(ofile, 'variable_list', elem_list, 5, - add_allocate=False, start_var="num_vars", - start_index=elem_start) - elem_start += len(elem_list) - ofile.write("else", 4) - API.write_var_set_loop(ofile, 'variable_list', leaf_list, 5, - add_allocate=False, start_var="num_vars", - start_index=leaf_start) - leaf_start += len(leaf_list) - ofile.write("end if", 4) - # end if - if leaf_cross_list or elem_cross_list: - ofile.write("if (.not. input_vars_use) then", 4) - API.write_var_set_loop(ofile, 'variable_list', leaf_cross_list, - 5, add_allocate=False, - start_var="num_vars", - start_index=leaf_start) - leaf_start += len(leaf_cross_list) - elem_start += len(leaf_cross_list) - if elem_cross_list or simp_cross_list: - ofile.write("if (struct_elements_use) then", 5) - API.write_var_set_loop(ofile, 'variable_list', - elem_cross_list, 6, - add_allocate=False, - start_var="num_vars", - start_index=elem_start) - elem_start += len(elem_list) - ofile.write("else", 5) - API.write_var_set_loop(ofile, 'variable_list', - leaf_cross_list, 6, - add_allocate=False, - start_var="num_vars", - start_index=leaf_start) - leaf_start += len(leaf_list) - ofile.write("end if", 5) - # end if - ofile.write("end if", 4) - if have_outputs: - ofile.write("end if", 3) - # end if + oline = f"{else_str}if(trim(suite_name) == '{suite.name}') then" + ofile.write(oline, 2) else_str = 'else ' + args = ["variable_list", emname, ecname, "input_vars=input_vars", + "output_vars=output_vars", "phases=phases", + "struct_elements=struct_elements"] + ofile.write(f"call {suite.req_vars_subname()}({', '.join(args)})", + 3) # end for ofile.write("else", 2) emsg = "write({errmsg}, '(3a)')".format(errmsg=errmsg_name) diff --git a/scripts/constituents.py b/scripts/constituents.py index 7a60af04..f7d09b2e 100644 --- a/scripts/constituents.py +++ b/scripts/constituents.py @@ -112,7 +112,7 @@ def find_variable(self, standard_name=None, source_var=None, # end for newdims.append(':'.join(new_dnames)) # end for - var = source_var.clone({'dimensions' : newdims}, remove_intent=False, + var = source_var.clone({'dimensions' : newdims}, remove_intent=True, source_type=self.__constituent_type) self.add_variable(var, self.__run_env) return var diff --git a/scripts/metavar.py b/scripts/metavar.py index e2a22e24..7549f9ad 100755 --- a/scripts/metavar.py +++ b/scripts/metavar.py @@ -1004,15 +1004,15 @@ def write_def(self, outfile, indent, wdict, allocatable=False, intent = None # end if if protected and allocatable: - errmsg = 'Cannot create allocatable variable from protected, {}' + errmsg = "Cannot create allocatable variable from protected, {}" raise CCPPError(errmsg.format(name)) # end if if dummy and (intent is None): if add_intent is not None: intent = add_intent else: - errmsg = " is missing for dummy argument, {}" - raise CCPPError(errmsg.format(name)) + errmsg = f" is missing for dummy argument, {name}" + raise CCPPError(errmsg) # end if # end if if protected and dummy: @@ -1025,7 +1025,12 @@ def write_def(self, outfile, indent, wdict, allocatable=False, # end if elif intent is not None: alloval = self.get_prop_value('allocatable') - if (intent.lower()[-3:] == 'out') and alloval: + if alloval: + if intent.lower() == 'in': + # We should not have allocatable, intent(in), makes no sense + errmsg = f"{name} ({stdname}) is allocatable and intent(in)" + raise CCPPError(errmsg) + # end if intent_str = f"allocatable, intent({intent})" else: intent_str = f"intent({intent}){' '*(5 - len(intent))}" @@ -1064,7 +1069,7 @@ def write_def(self, outfile, indent, wdict, allocatable=False, cspc = 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, sname=stdname), indent) diff --git a/test/var_action_test/run_test b/test/var_action_test/run_test index 2b4db0ac..d26cee1d 100755 --- a/test/var_action_test/run_test +++ b/test/var_action_test/run_test @@ -6,7 +6,7 @@ scriptdir="$( cd $( dirname $0 ); pwd -P )" ## ## Option default values ## -defdir="ct_build" +defdir="va_build" build_dir="${currdir}/${defdir}" cleanup="PASS" # Other supported options are ALWAYS and NEVER verbosity=0