Skip to content

Commit

Permalink
Added test using a DDT host object to pass information (#591)
Browse files Browse the repository at this point in the history
Added test using a DDT host object to pass information
Fix problems so that test passes
Improve formatting for readability

User interface changes?: No

Fixes: #589 

Testing:
  test removed: None
  unit tests: Pass
  system tests: Pass, added DDT host object test
  manual testing: Ran doctests, examined generated code for system tests

---------

Co-authored-by: Steve Goldhaber <stevenng@met.no>
Co-authored-by: Dom Heinzeller <dom.heinzeller@icloud.com>
  • Loading branch information
3 people authored Dec 11, 2024
1 parent 0345a66 commit 48443af
Show file tree
Hide file tree
Showing 35 changed files with 2,829 additions and 71 deletions.
94 changes: 57 additions & 37 deletions scripts/ccpp_suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -647,9 +647,15 @@ def __init__(self, sdfs, host_model, scheme_headers, run_env):
# Secondary level is by phase
scheme_library = {}
# First, process DDT headers
all_ddts = [d for d in scheme_headers if d.header_type == 'ddt']
ddt_titles = [d.title for d in all_ddts]
for ddt_title in self.host_model.ddt_lib:
if ddt_title not in ddt_titles:
all_ddts.append(self.host_model.ddt_lib[ddt_title])
# end if
# end for
self.__ddt_lib = DDTLibrary('{}_api'.format(self.host_model.name),
run_env, ddts=[d for d in scheme_headers
if d.header_type == 'ddt'])
run_env, ddts=all_ddts)
for header in [d for d in scheme_headers if d.header_type != 'ddt']:
if header.header_type != 'scheme':
errmsg = "{} is an unknown CCPP API metadata header type, {}"
Expand Down Expand Up @@ -677,15 +683,14 @@ def __init__(self, sdfs, host_model, scheme_headers, run_env):
# end for
# We will need the correct names for errmsg and errcode
evar = self.host_model.find_variable(standard_name='ccpp_error_message')
subst_dict = {'intent':'out'}
if evar is not None:
self._errmsg_var = evar.clone(subst_dict)
self._errmsg_var = evar
else:
raise CCPPError('Required variable, ccpp_error_message, not found')
# end if
evar = self.host_model.find_variable(standard_name='ccpp_error_code')
if evar is not None:
self._errcode_var = evar.clone(subst_dict)
self._errcode_var = evar
else:
raise CCPPError('Required variable, ccpp_error_code, not found')
# end if
Expand Down Expand Up @@ -737,15 +742,25 @@ def write(self, output_dir, run_env):
@classmethod
def declare_inspection_interfaces(cls, ofile):
"""Declare the API interfaces for the suite inquiry functions"""
ofile.write("public :: {}".format(API.__suite_fname), 1)
ofile.write("public :: {}".format(API.__part_fname), 1)
ofile.write("public :: {}".format(API.__vars_fname), 1)
ofile.write("public :: {}".format(API.__schemes_fname), 1)

def get_errinfo_names(self):
"""Return a tuple of error output local names"""
errmsg_name = self._errmsg_var.get_prop_value('local_name')
errcode_name = self._errcode_var.get_prop_value('local_name')
ofile.write(f"public :: {API.__suite_fname}", 1)
ofile.write(f"public :: {API.__part_fname}", 1)
ofile.write(f"public :: {API.__vars_fname}", 1)
ofile.write(f"public :: {API.__schemes_fname}", 1)

def get_errinfo_names(self, base_only=False):
"""Return a tuple of error output local names.
If base_only==True, return only the name string of the variable.
If base_only=False, return the local name as a full reference.
If the error variables are intrinsic variables, this makes no
difference, however, for a DDT variable, the full reference is
<ddt_name>%<errvar_name> while the local name is just <errvar_name>."""
if base_only:
errmsg_name = self._errmsg_var.get_prop_value('local_name')
errcode_name = self._errcode_var.get_prop_value('local_name')
else:
errmsg_name = self._errmsg_var.call_string(self)
errcode_name = self._errcode_var.call_string(self)
# end if
return (errmsg_name, errcode_name)

@staticmethod
Expand All @@ -756,29 +771,29 @@ def write_var_set_loop(ofile, varlist_name, var_list, indent,
beginning at <start_index>.
"""
if add_allocate:
ofile.write("allocate({}({}))".format(varlist_name, len(var_list)),
indent)
ofile.write(f"allocate({varlist_name}({len(var_list)}))", indent)
# end if
for ind, var in enumerate(sorted(var_list)):
if start_var:
ind_str = "{} + {}".format(start_var, ind + start_index)
ind_str = f"{start_var} + {ind + start_index}"
else:
ind_str = "{}".format(ind + start_index)
ind_str = f"{ind + start_index}"
# end if
ofile.write("{}({}) = '{}'".format(varlist_name, ind_str, var),
indent)
ofile.write(f"{varlist_name}({ind_str}) = '{var}'", indent)
# end for

def write_suite_part_list_sub(self, ofile, errmsg_name, errcode_name):
"""Write the suite-part list subroutine"""
inargs = f"suite_name, part_list, {errmsg_name}, {errcode_name}"
ofile.write(f"subroutine {API.__part_fname}({inargs})", 1)
oline = "character(len=*), intent(in) :: suite_name"
oline = "character(len=*), intent(in) :: suite_name"
ofile.write(oline, 2)
oline = "character(len=*), allocatable, intent(out) :: part_list(:)"
oline = "character(len=*), allocatable, intent(out) :: part_list(:)"
ofile.write(oline, 2)
self._errmsg_var.write_def(ofile, 2, self)
self._errcode_var.write_def(ofile, 2, self)
self._errmsg_var.write_def(ofile, 2, self, dummy=True, add_intent="out",
extra_space=11)
self._errcode_var.write_def(ofile, 2, self, dummy=True, add_intent="out",
extra_space=11)
else_str = ''
ename = self._errcode_var.get_prop_value('local_name')
ofile.write(f"{ename} = 0", 2)
Expand All @@ -805,17 +820,19 @@ def write_req_vars_sub(self, ofile, errmsg_name, errcode_name):
inargs = oline.format(errmsg=errmsg_name, errcode=errcode_name)
ofile.write("\nsubroutine {}({})".format(API.__vars_fname, inargs), 1)
ofile.write("! Dummy arguments", 2)
oline = "character(len=*), intent(in) :: suite_name"
oline = "character(len=*), intent(in) :: suite_name"
ofile.write(oline, 2)
oline = "character(len=*), allocatable, intent(out) :: variable_list(:)"
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, dummy=True,
add_intent="out", extra_space=11)
self._errcode_var.write_def(ofile, 2, self, dummy=True,
add_intent="out", extra_space=11)
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 = "logical, optional, intent(in) :: struct_elements"
ofile.write(oline, 2)
ofile.write("! Local variables", 2)
ofile.write("logical {}:: input_vars_use".format(' '*34), 2)
Expand Down Expand Up @@ -1120,12 +1137,14 @@ def write_suite_schemes_sub(self, ofile, errmsg_name, errcode_name):
inargs = oline.format(errmsg=errmsg_name, errcode=errcode_name)
ofile.write("\nsubroutine {}({})".format(API.__schemes_fname,
inargs), 1)
oline = "character(len=*), intent(in) :: suite_name"
oline = "character(len=*), intent(in) :: suite_name"
ofile.write(oline, 2)
oline = "character(len=*), allocatable, intent(out) :: scheme_list(:)"
oline = "character(len=*), allocatable, intent(out) :: scheme_list(:)"
ofile.write(oline, 2)
self._errmsg_var.write_def(ofile, 2, self)
self._errcode_var.write_def(ofile, 2, self)
self._errmsg_var.write_def(ofile, 2, self, dummy=True,
add_intent="out", extra_space=11)
self._errcode_var.write_def(ofile, 2, self, dummy=True,
add_intent="out", extra_space=11)
else_str = ''
ename = self._errcode_var.get_prop_value('local_name')
ofile.write("{} = 0".format(ename), 2)
Expand Down Expand Up @@ -1153,16 +1172,17 @@ def write_suite_schemes_sub(self, ofile, errmsg_name, errcode_name):

def write_inspection_routines(self, ofile):
"""Write the list_suites and list_suite_parts subroutines"""
errmsg_name, errcode_name = self.get_errinfo_names()
errmsg_name, errcode_name = self.get_errinfo_names(base_only=True)
ofile.write("subroutine {}(suites)".format(API.__suite_fname), 1)
nsuites = len(self.suites)
oline = "character(len=*), allocatable, intent(out) :: suites(:)"
oline = "character(len=*), allocatable, intent(out) :: suites(:)"
ofile.write(oline, 2)
ofile.write("\nallocate(suites({}))".format(nsuites), 2)
for ind, suite in enumerate(self.suites):
ofile.write("suites({}) = '{}'".format(ind+1, suite.name), 2)
# end for
ofile.write("end subroutine {}".format(API.__suite_fname), 1)
ofile.blank_line()
# Write out the suite part list subroutine
self.write_suite_part_list_sub(ofile, errmsg_name, errcode_name)
# Write out the suite required variable subroutine
Expand Down
10 changes: 6 additions & 4 deletions scripts/constituents.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,10 +491,11 @@ def write_host_routines(cap, host, reg_funcname, init_funcname, num_const_funcna
ConstituentVarDict.write_constituent_use_statements(cap, suite_list, 2)
cap.blank_line()
cap.comment("Dummy arguments", 2)
cap.write(f"type({CONST_PROP_TYPE}), target, intent(in) :: " + \
cap.write(f"type({CONST_PROP_TYPE}), target, intent(in) :: " + \
"host_constituents(:)", 2)
for evar in err_vars:
evar.write_def(cap, 2, host, dummy=True, add_intent="out")
evar.write_def(cap, 2, host, dummy=True,
add_intent="out", extra_space=25)
# end for
cap.comment("Local variables", 2)
spc = ' '*37
Expand Down Expand Up @@ -546,7 +547,7 @@ def write_host_routines(cap, host, reg_funcname, init_funcname, num_const_funcna
cap.write("end if", 3)
cap.write("end do", 2)
# end for

# Register suite constituents
for suite in suite_list:
errvar_str = ConstituentVarDict.__errcode_callstr(herrcode,
Expand Down Expand Up @@ -745,7 +746,8 @@ def write_host_routines(cap, host, reg_funcname, init_funcname, num_const_funcna
cap.write("character(len=*), intent(in) :: stdname", 2)
cap.write("integer, intent(out) :: const_index", 2)
for evar in err_vars:
evar.write_def(cap, 2, host, dummy=True, add_intent="out")
evar.write_def(cap, 2, host, dummy=True,
add_intent="out", extra_space=1)
# end for
cap.blank_line()
cap.write(f"call {const_obj_name}%const_index(const_index, " + \
Expand Down
22 changes: 16 additions & 6 deletions scripts/ddt_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,21 @@ def call_string(self, var_dict, loop_vars=None):
# end if
return call_str

def write_def(self, outfile, indent, ddict, allocatable=False, dummy=False):
def write_def(self, outfile, indent, ddict, allocatable=False, target=False,
dummy=False, add_intent=None, extra_space=0, public=False):
"""Write the definition line for this DDT.
The type of this declaration is the type of the Var at the
end of the chain of references."""
if self.field is None:
super().write_def(outfile, indent, ddict,
allocatable=allocatable, dummy=dummy)
allocatable=allocatable, target=target, dummy=dummy,
add_intent=add_intent, extra_space=extra_space,
public=public)
else:
self.field.write_def(outfile, indent, ddict,
allocatable=allocatable, dummy=dummy)
allocatable=allocatable, target=target,
dummy=dummy, add_intent=add_intent,
extra_space=extra_space, public=public)
# end if

@staticmethod
Expand Down Expand Up @@ -214,8 +219,8 @@ def __init__(self, name, run_env, ddts=None):
# end if
if ddt.title in self:
errmsg = "Duplicate DDT, {}, found{}, original{}"
ctx = context_string(ddt.source.context)
octx = context_string(self[ddt.title].source.context)
ctx = context_string(ddt.context)
octx = context_string(self[ddt.title].context)
raise CCPPError(errmsg.format(ddt.title, ctx, octx))
# end if
if run_env.verbose:
Expand Down Expand Up @@ -324,14 +329,19 @@ def write_ddt_use_statements(self, variable_list, outfile, indent, pad=0):

@property
def name(self):
"Return the name of this DDT library"
"""Return the name of this DDT library"""
return self.__name

@property
def run_env(self):
"""Return the CCPPFrameworkEnv object for this DDT library"""
return self.__run_env

@property
def max_mod_name_len(self):
"""Return the maximum module name length of this DDT library's modules"""
return self.__max_mod_name_len

###############################################################################
if __name__ == "__main__":
# pylint: disable=ungrouped-imports
Expand Down
14 changes: 7 additions & 7 deletions scripts/host_cap.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,12 +523,12 @@ def suite_part_call_list(host_model, const_dict, suite_part, subst_loop_vars,
# end if
# end for
if hvar is None:
errmsg = 'No host model variable for {} in {}'
raise CCPPError(errmsg.format(stdname, suite_part.name))
errmsg = f"No host model variable for {stdname} in {suite_part.name}"
raise CCPPError(errmsg)
# End if
if stdname not in CCPP_CONSTANT_VARS:
lname = var_dict.var_call_string(hvar, loop_vars=loop_vars)
hmvars.append("{}={}".format(sp_lname, lname))
hmvars.append(f"{sp_lname}={lname}")
# End if
# End for
return ', '.join(hmvars)
Expand Down Expand Up @@ -563,7 +563,7 @@ def write_host_cap(host_model, api, module_name, output_dir, run_env):
cap.write(f"use {CONST_DDT_MOD}, {mspc}only: {CONST_DDT_NAME}", 1)
cap.write(f"use {CONST_DDT_MOD}, {mspc}only: {CONST_PROP_TYPE}", 1)
cap.write_preamble()
max_suite_len = 0
max_suite_len = host_model.ddt_lib.max_mod_name_len
for suite in api.suites:
max_suite_len = max(max_suite_len, len(suite.module))
# End for
Expand Down Expand Up @@ -697,12 +697,12 @@ def write_host_cap(host_model, api, module_name, output_dir, run_env):
pad=max_suite_len)

cap.write("", 1)
# Write out dummy arguments
# Write out dummy argument definitions
for var in apivars:
var.write_def(cap, 2, host_model)
var.write_def(cap, 2, host_model, dummy=True)
# End for
for var in hdvars:
var.write_def(cap, 2, host_model)
var.write_def(cap, 2, host_model, dummy=True)
# End for
for var in host_local_vars.variable_list():
var.write_def(cap, 2, host_model,
Expand Down
3 changes: 3 additions & 0 deletions scripts/host_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ def __init__(self, meta_tables, name_in, run_env):
# End for
loop_vars = header.variable_list(std_vars=False,
loop_vars=True, consts=False)
loop_vars.extend(self.__ddt_dict.variable_list(std_vars=False,
loop_vars=True,
consts=False))
if loop_vars:
# loop_vars are part of the host-model interface call
# at run time. As such, they override the host-model
Expand Down
Loading

0 comments on commit 48443af

Please sign in to comment.