From 748984ba3b7ac35209455cd6a936041a9228bd6f Mon Sep 17 00:00:00 2001 From: Dustin Swales Date: Mon, 17 Mar 2025 21:46:48 +0000 Subject: [PATCH 1/6] Address some issue with DEBUG mdoe --- scripts/metavar.py | 6 +- scripts/suite_objects.py | 105 +++++++++++++++++++-------- test/capgen_test/run_test | 13 +++- test/capgen_test/temp_set.F90 | 8 +- test/capgen_test/temp_set.meta | 22 ++++++ test/capgen_test/test_host.F90 | 14 +++- test/capgen_test/test_host_data.F90 | 13 +++- test/capgen_test/test_host_data.meta | 9 ++- test/capgen_test/test_host_mod.F90 | 6 +- test/capgen_test/test_host_mod.meta | 25 ++++++- test/capgen_test/test_reports.py | 14 +++- 11 files changed, 183 insertions(+), 52 deletions(-) diff --git a/scripts/metavar.py b/scripts/metavar.py index 8f283df6..cdfc30ed 100755 --- a/scripts/metavar.py +++ b/scripts/metavar.py @@ -210,7 +210,9 @@ class Var: VariableProperty('optional', bool, optional_in=True, default_in=False), VariableProperty('target', bool, optional_in=True, - default_in=False)] + default_in=False), + VariableProperty('lower_bound', bool, optional_in=True, + default_in=False)] # XXgoldyXX: v debug only __to_add = VariableProperty('valid_values', str, @@ -708,7 +710,7 @@ def call_string(self, var_dict, loop_vars=None): lname = "" for item in dim.split(':'): if item: - dvar = var_dict.find_variable(standard_name=item, + dvar = var_dict.find_variable(standard_name=item.lower(), any_scope=False) if dvar is None: try: diff --git a/scripts/suite_objects.py b/scripts/suite_objects.py index 13d2c6a2..76c879d1 100755 --- a/scripts/suite_objects.py +++ b/scripts/suite_objects.py @@ -1360,18 +1360,32 @@ def add_var_debug_check(self, var): if not ':' in dim: dim_var = self.find_variable(standard_name=dim) if not dim_var: - raise Exception(f"No dimension with standard name '{dim}'") - self.update_group_call_list_variable(dim_var) + # DJS2025: To allow for numerical dimensions in metadata. + if not dim.isnumeric(): + raise Exception(f"No dimension with standard name '{dim}'") + # end if + else: + self.update_group_call_list_variable(dim_var) + # end if else: (ldim, udim) = dim.split(":") ldim_var = self.find_variable(standard_name=ldim) if not ldim_var: - raise Exception(f"No dimension with standard name '{ldim}'") + # DJS2025: To allow for numerical dimensions in metadata. + if not ldim.isnumeric(): + raise Exception(f"No dimension with standard name '{ldim}'") + # end if + # end if self.update_group_call_list_variable(ldim_var) udim_var = self.find_variable(standard_name=udim) if not udim_var: - raise Exception(f"No dimension with standard name '{udim}'") - self.update_group_call_list_variable(udim_var) + # DJS2025: To allow for numerical dimensions in metadata. + if not udim.isnumeric(): + raise Exception(f"No dimension with standard name '{udim}'") + # end if + else: + self.update_group_call_list_variable(udim_var) + # end if # Add the variable to the list of variables to check. Record which internal_var to use. self.__var_debug_checks.append([var, internal_var]) @@ -1436,6 +1450,7 @@ def write_var_debug_check(self, var, internal_var, cldicts, outfile, errcode, er dimensions = var.get_dimensions() active = var.get_prop_value('active') allocatable = var.get_prop_value('allocatable') + vtype = var.get_prop_value('type') # Need the local name from the group call list, # from the locally-defined variables of the group, @@ -1524,18 +1539,30 @@ def write_var_debug_check(self, var, internal_var, cldicts, outfile, errcode, er for var_dict in cldicts: dvar = var_dict.find_variable(standard_name=ldim, any_scope=False) if dvar is not None: + ldim_lname = dvar.get_prop_value('local_name') break if not dvar: - raise Exception(f"No variable with standard name '{ldim}' in cldicts") - ldim_lname = dvar.get_prop_value('local_name') + # DJS2025: To allow for numerical dimensions in metadata. + if ldim.isnumeric(): + ldim_lname = ldim + else: + raise Exception(f"No variable with standard name '{ldim}' in cldicts") + # endif + # endif # Get dimension for upper bound for var_dict in cldicts: dvar = var_dict.find_variable(standard_name=udim, any_scope=False) if dvar is not None: + udim_lname = dvar.get_prop_value('local_name') break if not dvar: - raise Exception(f"No variable with standard name '{udim}' in cldicts") - udim_lname = dvar.get_prop_value('local_name') + # DJS2025: To allow for numerical dimensions in metadata. + if udim.isnumeric(): + udim_lname = udim + else: + raise Exception(f"No variable with standard name '{udim}' in cldicts") + # end if + # end if # Assemble dimensions and bounds for size checking dim_length = f'{udim_lname}-{ldim_lname}+1' dim_string = ":" @@ -1554,38 +1581,52 @@ def write_var_debug_check(self, var, internal_var, cldicts, outfile, errcode, er ubound_string = '(' + ','.join(ubound_strings) + ')' # Write size check - tmp_indent = indent - if conditional != '.true.': - tmp_indent = indent + 1 - outfile.write(f"if {conditional} then", indent) - # end if - outfile.write(f"! Check size of array {local_name}", tmp_indent) - outfile.write(f"if (size({local_name}{dim_string}) /= {array_size}) then", tmp_indent) - outfile.write(f"write({errmsg}, '(a)') 'In group {self.__group.name} before {self.__subroutine_name}:'", tmp_indent+1) - outfile.write(f"write({errmsg}, '(2(a,i8))') 'for array {local_name}, expected size ', {array_size}, ' but got ', size({local_name})", tmp_indent+1) - outfile.write(f"{errcode} = 1", tmp_indent+1) - outfile.write(f"return", tmp_indent+1) - outfile.write(f"end if", tmp_indent) - if conditional != '.true.': - outfile.write(f"end if", indent) - # end if - outfile.write('',indent) - - # Assign lower/upper bounds to internal_var (scalar) if intent is not out - if not intent == 'out': - internal_var_lname = internal_var.get_prop_value('local_name') + # - Only for types int and real. + if (vtype == "integer") or (vtype == "real"): tmp_indent = indent if conditional != '.true.': 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_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) + outfile.write(f"! Check size of array {local_name}", tmp_indent) + outfile.write(f"if (size({local_name}{dim_string}) /= {array_size}) then", tmp_indent) + outfile.write(f"write({errmsg}, '(a)') 'In group {self.__group.name} before {self.__subroutine_name}:'", tmp_indent+1) + outfile.write(f"write({errmsg}, '(2(a,i8))') 'for array {local_name}, expected size ', {array_size}, ' but got ', size({local_name})", tmp_indent+1) + outfile.write(f"{errcode} = 1", tmp_indent+1) + outfile.write(f"return", tmp_indent+1) + outfile.write(f"end if", tmp_indent) if conditional != '.true.': outfile.write(f"end if", indent) # end if outfile.write('',indent) + # end if + + # Write array bounds check. + # Assign lower/upper bounds to internal_var (scalar) + # - If intent is not out. + # - Only for types int and real. + # - Skip test for assumed-shape arrays with a lower bounds other than one. + lower_bound = svar.get_prop_value('lower_bound') + if lower_bound == True: + lmsg = "Skipping error check for {}. Cant perfrom this check when lower-bounds is not 1." + self.run_env.logger.info(lmsg.format(local_name)) + elif (vtype == "integer") or (vtype == "real"): + if not intent == 'out': + internal_var_lname = internal_var.get_prop_value('local_name') + tmp_indent = indent + if conditional != '.true.': + 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_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) + # endif + # end if def associate_optional_var(self, dict_var, var, var_ptr, has_transform, cldicts, indent, outfile): """Write local pointer association for optional variables.""" diff --git a/test/capgen_test/run_test b/test/capgen_test/run_test index 5b0f4fe8..b99f6f2e 100755 --- a/test/capgen_test/run_test +++ b/test/capgen_test/run_test @@ -153,12 +153,16 @@ required_vars_temp="${required_vars_temp},horizontal_dimension" required_vars_temp="${required_vars_temp},horizontal_loop_begin" required_vars_temp="${required_vars_temp},horizontal_loop_end" required_vars_temp="${required_vars_temp},index_of_water_vapor_specific_humidity" +required_vars_temp="${required_vars_temp},lower_bound_of_vertical_dimension_of_soil" required_vars_temp="${required_vars_temp},number_of_tracers" required_vars_temp="${required_vars_temp},potential_temperature" required_vars_temp="${required_vars_temp},potential_temperature_at_interface" required_vars_temp="${required_vars_temp},potential_temperature_increment" +required_vars_temp="${required_vars_temp},soil_levels" required_vars_temp="${required_vars_temp},surface_air_pressure" +required_vars_temp="${required_vars_temp},temperature_at_diagnostic_levels" required_vars_temp="${required_vars_temp},time_step_for_physics" +required_vars_temp="${required_vars_temp},upper_bound_of_vertical_dimension_of_soil" required_vars_temp="${required_vars_temp},vertical_interface_dimension" required_vars_temp="${required_vars_temp},vertical_layer_dimension" required_vars_temp="${required_vars_temp},water_vapor_specific_humidity" @@ -168,11 +172,16 @@ input_vars_temp="${input_vars_temp},horizontal_dimension" input_vars_temp="${input_vars_temp},horizontal_loop_begin" input_vars_temp="${input_vars_temp},horizontal_loop_end" input_vars_temp="${input_vars_temp},index_of_water_vapor_specific_humidity" +input_vars_temp="${input_vars_temp},lower_bound_of_vertical_dimension_of_soil" input_vars_temp="${input_vars_temp},number_of_tracers" input_vars_temp="${input_vars_temp},potential_temperature" input_vars_temp="${input_vars_temp},potential_temperature_at_interface" input_vars_temp="${input_vars_temp},potential_temperature_increment" -input_vars_temp="${input_vars_temp},surface_air_pressure,time_step_for_physics" +input_vars_temp="${input_vars_temp},soil_levels" +input_vars_temp="${input_vars_temp},surface_air_pressure" +input_vars_temp="${input_vars_temp},temperature_at_diagnostic_levels" +input_vars_temp="${input_vars_temp},time_step_for_physics" +input_vars_temp="${input_vars_temp},upper_bound_of_vertical_dimension_of_soil" input_vars_temp="${input_vars_temp},vertical_interface_dimension" input_vars_temp="${input_vars_temp},vertical_layer_dimension" input_vars_temp="${input_vars_temp},water_vapor_specific_humidity" @@ -180,7 +189,9 @@ output_vars_temp="ccpp_error_code,ccpp_error_message" output_vars_temp="${output_vars_temp},coefficients_for_interpolation" output_vars_temp="${output_vars_temp},potential_temperature" output_vars_temp="${output_vars_temp},potential_temperature_at_interface" +output_vars_temp="${output_vars_temp},soil_levels" output_vars_temp="${output_vars_temp},surface_air_pressure" +output_vars_temp="${output_vars_temp},temperature_at_diagnostic_levels" output_vars_temp="${output_vars_temp},water_vapor_specific_humidity" ## diff --git a/test/capgen_test/temp_set.F90 b/test/capgen_test/temp_set.F90 index a780433f..a0cb5e64 100644 --- a/test/capgen_test/temp_set.F90 +++ b/test/capgen_test/temp_set.F90 @@ -18,17 +18,19 @@ MODULE temp_set !> \section arg_table_temp_set_run Argument Table !! \htmlinclude arg_table_temp_set_run.html !! - SUBROUTINE temp_set_run(ncol, lev, timestep, temp_level, temp, ps, & - to_promote, promote_pcnst, errmsg, errflg) + SUBROUTINE temp_set_run(ncol, lev, timestep, temp_level, temp_diag, temp, ps, & + to_promote, promote_pcnst, slev_lbound, soil_levs, errmsg, errflg) !---------------------------------------------------------------- IMPLICIT NONE !---------------------------------------------------------------- - integer, intent(in) :: ncol, lev + integer, intent(in) :: ncol, lev, slev_lbound REAL(kind_phys), intent(out) :: temp(:,:) real(kind_phys), intent(in) :: timestep real(kind_phys), intent(in) :: ps(:) REAL(kind_phys), INTENT(inout) :: temp_level(:, :) + real(kind_phys), intent(inout) :: temp_diag(:,:) + real(kind_phys), intent(inout) :: soil_levs(slev_lbound:) real(kind_phys), intent(out) :: to_promote(:, :) real(kind_phys), intent(out) :: promote_pcnst(:) character(len=512), intent(out) :: errmsg diff --git a/test/capgen_test/temp_set.meta b/test/capgen_test/temp_set.meta index b6c403ce..c7339e5c 100644 --- a/test/capgen_test/temp_set.meta +++ b/test/capgen_test/temp_set.meta @@ -32,6 +32,13 @@ type = real kind = kind_phys intent = inout +[ temp_diag ] + standard_name = temperature_at_diagnostic_levels + units = K + dimensions = (horizontal_loop_extent, 6) + type = real + kind = kind_phys + intent = inout [ temp ] standard_name = potential_temperature units = K @@ -61,6 +68,21 @@ type = real kind = kind_phys intent = out +[ slev_lbound ] + standard_name = lower_bound_of_vertical_dimension_of_soil + type = integer + units = count + dimensions = () + intent = in +[ soil_levs ] + standard_name = soil_levels + long_name = soil levels + units = cm + dimensions = (lower_bound_of_vertical_dimension_of_soil:upper_bound_of_vertical_dimension_of_soil) + type = real + kind = kind_phys + intent = inout + lower_bound = True [ errmsg ] standard_name = ccpp_error_message long_name = Error message for error handling in CCPP diff --git a/test/capgen_test/test_host.F90 b/test/capgen_test/test_host.F90 index 8f716322..4291ad1f 100644 --- a/test/capgen_test/test_host.F90 +++ b/test/capgen_test/test_host.F90 @@ -318,7 +318,7 @@ subroutine test_host(retval, test_suites) if (errflg /= 0) then write(6, '(3a)') trim(test_suites(sind)%suite_name), ': ', & trim(errmsg) - exit +1 exit end if end do end do ! End time step loop @@ -368,23 +368,27 @@ program test character(len=cs), target :: test_parts1(2) = (/ 'physics1 ', & 'physics2 ' /) character(len=cs), target :: test_parts2(1) = (/ 'data_prep ' /) - character(len=cm), target :: test_invars1(7) = (/ & + character(len=cm), target :: test_invars1(9) = (/ & 'potential_temperature ', & 'potential_temperature_at_interface ', & 'coefficients_for_interpolation ', & 'surface_air_pressure ', & 'water_vapor_specific_humidity ', & 'potential_temperature_increment ', & + 'soil_levels ', & + 'temperature_at_diagnostic_levels ', & 'time_step_for_physics ' /) - character(len=cm), target :: test_outvars1(7) = (/ & + character(len=cm), target :: test_outvars1(9) = (/ & 'potential_temperature ', & 'potential_temperature_at_interface ', & 'coefficients_for_interpolation ', & 'surface_air_pressure ', & 'water_vapor_specific_humidity ', & + 'soil_levels ', & + 'temperature_at_diagnostic_levels ', & 'ccpp_error_code ', & 'ccpp_error_message ' /) - character(len=cm), target :: test_reqvars1(9) = (/ & + character(len=cm), target :: test_reqvars1(11) = (/ & 'potential_temperature ', & 'potential_temperature_at_interface ', & 'coefficients_for_interpolation ', & @@ -392,6 +396,8 @@ program test 'water_vapor_specific_humidity ', & 'potential_temperature_increment ', & 'time_step_for_physics ', & + 'soil_levels ', & + 'temperature_at_diagnostic_levels ', & 'ccpp_error_code ', & 'ccpp_error_message ' /) diff --git a/test/capgen_test/test_host_data.F90 b/test/capgen_test/test_host_data.F90 index 0c7cdea1..1b0a45c1 100644 --- a/test/capgen_test/test_host_data.F90 +++ b/test/capgen_test/test_host_data.F90 @@ -9,12 +9,12 @@ module test_host_data !! \htmlinclude arg_table_physics_state.html type physics_state real(kind_phys), dimension(:), allocatable :: & - ps ! surface pressure + ps, & ! surface pressure + soil_levs ! soil temperature (cm) real(kind_phys), dimension(:,:), allocatable :: & u, & ! zonal wind (m/s) v, & ! meridional wind (m/s) pmid ! midpoint pressure (Pa) - real(kind_phys), dimension(:,:,:),allocatable :: & q ! constituent mixing ratio (kg/kg moist or dry air depending on type) end type physics_state @@ -24,10 +24,11 @@ module test_host_data contains - subroutine allocate_physics_state(cols, levels, constituents, state) + subroutine allocate_physics_state(cols, levels, constituents, lbnd_slev, ubnd_slev, state) integer, intent(in) :: cols integer, intent(in) :: levels integer, intent(in) :: constituents + integer, intent(in) :: lbnd_slev, ubnd_slev type(physics_state), intent(out) :: state if (allocated(state%ps)) then @@ -50,6 +51,10 @@ subroutine allocate_physics_state(cols, levels, constituents, state) deallocate(state%q) end if allocate(state%q(cols, levels, constituents)) - + if (allocated(state%soil_levs)) then + deallocate(state%soil_levs) + end if + allocate(state%soil_levs(lbnd_slev:ubnd_slev)) + end subroutine allocate_physics_state end module test_host_data diff --git a/test/capgen_test/test_host_data.meta b/test/capgen_test/test_host_data.meta index df4b92b4..0e73c060 100644 --- a/test/capgen_test/test_host_data.meta +++ b/test/capgen_test/test_host_data.meta @@ -35,6 +35,13 @@ kind = kind_phys units = Pa dimensions = (horizontal_dimension, vertical_layer_dimension) +[ soil_levs ] + standard_name = soil_levels + long_name = soil levels + units = cm + dimensions = (lower_bound_of_vertical_dimension_of_soil:upper_bound_of_vertical_dimension_of_soil) + type = real + kind = kind_phys [ q ] standard_name = constituent_mixing_ratio state_variable = true @@ -42,7 +49,7 @@ kind = kind_phys units = kg kg-1 moist or dry air depending on type dimensions = (horizontal_dimension, vertical_layer_dimension, number_of_tracers) -[ q(:,:,index_of_water_vapor_specific_humidity) ] +[ q(:,:,index_of_water_vapor_specific_HUMidity) ] standard_name = water_vapor_specific_humidity state_variable = true type = real diff --git a/test/capgen_test/test_host_mod.F90 b/test/capgen_test/test_host_mod.F90 index 2ce1fbd6..70302e3c 100644 --- a/test/capgen_test/test_host_mod.F90 +++ b/test/capgen_test/test_host_mod.F90 @@ -13,11 +13,15 @@ module test_host_mod integer, parameter :: pver = 5 integer, parameter :: pverP = 6 integer, parameter :: pcnst = 2 + integer, parameter :: slevs = 4 + integer, parameter :: slev_lbound = -3 + integer, parameter :: slev_ubound = 0 integer, parameter :: DiagDimStart = 2 integer, parameter :: index_qv = 1 logical, parameter :: config_var = .true. real(kind_phys), allocatable :: temp_midpoints(:,:) real(kind_phys) :: temp_interfaces(ncols, pverP) + real(kind_phys) :: temp_diag(ncols,6) real(kind_phys) :: coeffs(ncols) real(kind_phys), dimension(DiagDimStart:ncols, DiagDimStart:pver) :: & diag1, & @@ -56,7 +60,7 @@ subroutine init_data() end do end do ! Allocate and initialize state - call allocate_physics_state(ncols, pver, pcnst, phys_state) + call allocate_physics_state(ncols, pver, pcnst, slev_lbound, slev_ubound, phys_state) do cind = 1, pcnst do lev = 1, pver offsize = ((cind - 1) * (ncols * pver)) + ((lev - 1) * ncols) diff --git a/test/capgen_test/test_host_mod.meta b/test/capgen_test/test_host_mod.meta index ae0abd05..ea562835 100644 --- a/test/capgen_test/test_host_mod.meta +++ b/test/capgen_test/test_host_mod.meta @@ -5,7 +5,7 @@ name = test_host_mod type = module [ index_qv ] - standard_name = index_of_water_vapor_specific_humidity + standard_name = index_of_water_vapor_specific_HUMidity units = index type = integer protected = True @@ -40,6 +40,24 @@ units = count protected = True dimensions = () +[ slevs ] + standard_name = vertical_dimension_of_soil + type = integer + units = count + protected = True + dimensions = () +[ slev_lbound] + standard_name = lower_bound_of_vertical_dimension_of_soil + type = integer + units = count + protected = True + dimensions = () +[ slev_ubound] + standard_name = upper_bound_of_vertical_dimension_of_soil + type = integer + units = count + protected = True + dimensions = () [ DiagDimStart ] standard_name = first_index_of_diag_fields type = integer @@ -56,6 +74,11 @@ units = K dimensions = (horizontal_dimension, vertical_interface_dimension) type = real | kind = kind_phys +[ temp_diag ] + standard_name = temperature_at_diagnostic_levels + units = K + dimensions = (horizontal_dimension, 6) + type = real | kind = kind_phys [ diag1 ] standard_name = diagnostic_stuff_type_1 long_name = This is just a test field diff --git a/test/capgen_test/test_reports.py b/test/capgen_test/test_reports.py index 499ab22d..53f18d1c 100644 --- a/test/capgen_test/test_reports.py +++ b/test/capgen_test/test_reports.py @@ -81,6 +81,8 @@ def usage(errmsg=None): _PROT_VARS_TEMP = ["horizontal_loop_begin", "horizontal_loop_end", "horizontal_dimension", "vertical_layer_dimension", "number_of_tracers", + "lower_bound_of_vertical_dimension_of_soil", + "upper_bound_of_vertical_dimension_of_soil", "configuration_variable", # Added for --debug "index_of_water_vapor_specific_humidity", @@ -91,18 +93,24 @@ def usage(errmsg=None): "coefficients_for_interpolation", "potential_temperature_increment", "surface_air_pressure", "time_step_for_physics", - "water_vapor_specific_humidity"] + "water_vapor_specific_humidity", + "soil_levels", + "temperature_at_diagnostic_levels"] _INPUT_VARS_TEMP = ["potential_temperature", "potential_temperature_at_interface", "coefficients_for_interpolation", "potential_temperature_increment", "surface_air_pressure", "time_step_for_physics", - "water_vapor_specific_humidity"] + "water_vapor_specific_humidity", + "soil_levels", + "temperature_at_diagnostic_levels"] _OUTPUT_VARS_TEMP = ["ccpp_error_code", "ccpp_error_message", "potential_temperature", "potential_temperature_at_interface", "coefficients_for_interpolation", - "surface_air_pressure", "water_vapor_specific_humidity"] + "surface_air_pressure", "water_vapor_specific_humidity", + "soil_levels", + "temperature_at_diagnostic_levels"] def fields_string(field_type, field_list, sep): """Create an error string for field(s), . From bfd06773f2b1f79e7e6d3d83caa2d98202c5afa1 Mon Sep 17 00:00:00 2001 From: Dustin Swales Date: Tue, 18 Mar 2025 08:18:58 -0600 Subject: [PATCH 2/6] Apply suggestions from code review Co-authored-by: Dom Heinzeller --- scripts/suite_objects.py | 2 +- test/capgen_test/test_host.F90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/suite_objects.py b/scripts/suite_objects.py index 76c879d1..19aeaae8 100755 --- a/scripts/suite_objects.py +++ b/scripts/suite_objects.py @@ -1608,7 +1608,7 @@ def write_var_debug_check(self, var, internal_var, cldicts, outfile, errcode, er # - Skip test for assumed-shape arrays with a lower bounds other than one. lower_bound = svar.get_prop_value('lower_bound') if lower_bound == True: - lmsg = "Skipping error check for {}. Cant perfrom this check when lower-bounds is not 1." + lmsg = "Skipping error check for {}. Can't perfrom this check when lower-bounds is not 1." self.run_env.logger.info(lmsg.format(local_name)) elif (vtype == "integer") or (vtype == "real"): if not intent == 'out': diff --git a/test/capgen_test/test_host.F90 b/test/capgen_test/test_host.F90 index 4291ad1f..dede965c 100644 --- a/test/capgen_test/test_host.F90 +++ b/test/capgen_test/test_host.F90 @@ -318,7 +318,7 @@ subroutine test_host(retval, test_suites) if (errflg /= 0) then write(6, '(3a)') trim(test_suites(sind)%suite_name), ': ', & trim(errmsg) -1 exit + exit end if end do end do ! End time step loop From 46cd64602e83c1109eac5ad7ffff973ef1b1372a Mon Sep 17 00:00:00 2001 From: Dustin Swales Date: Tue, 22 Apr 2025 18:16:46 +0000 Subject: [PATCH 3/6] Refactored array bounds check to check length of each dimension. --- scripts/metavar.py | 4 +-- scripts/suite_objects.py | 41 ++++++++++++++++++++--------- test/capgen_test/run_test | 9 ++++--- test/capgen_test/temp_set.F90 | 5 +++- test/capgen_test/temp_set.meta | 9 ++++++- test/capgen_test/test_host.F90 | 15 ++++++----- test/capgen_test/test_host_mod.F90 | 1 + test/capgen_test/test_host_mod.meta | 6 +++++ test/capgen_test/test_reports.py | 9 ++++--- 9 files changed, 69 insertions(+), 30 deletions(-) diff --git a/scripts/metavar.py b/scripts/metavar.py index cdfc30ed..1f320de4 100755 --- a/scripts/metavar.py +++ b/scripts/metavar.py @@ -210,9 +210,7 @@ class Var: VariableProperty('optional', bool, optional_in=True, default_in=False), VariableProperty('target', bool, optional_in=True, - default_in=False), - VariableProperty('lower_bound', bool, optional_in=True, - default_in=False)] + default_in=False)] # XXgoldyXX: v debug only __to_add = VariableProperty('valid_values', str, diff --git a/scripts/suite_objects.py b/scripts/suite_objects.py index 3b739eaa..dd6e81d4 100755 --- a/scripts/suite_objects.py +++ b/scripts/suite_objects.py @@ -1511,6 +1511,8 @@ def write_var_debug_check(self, var, internal_var, cldicts, outfile, errcode, er dim_strings = [] lbound_strings = [] ubound_strings = [] + dim_lengths = [] + local_names = [] for dim in dimensions: if not ':' in dim: # In capgen, any true dimension (that is not a single index) does @@ -1573,7 +1575,9 @@ def write_var_debug_check(self, var, internal_var, cldicts, outfile, errcode, er lbound_strings.append(lbound_string) ubound_strings.append(ubound_string) array_size = f'{array_size}*({dim_length})' - + dim_lengths.append(dim_length) + local_names.append(local_name) + # end for # Various strings needed to get the right size # and lower/upper bound of the array dim_string = '(' + ','.join(dim_strings) + ')' @@ -1601,26 +1605,37 @@ def write_var_debug_check(self, var, internal_var, cldicts, outfile, errcode, er outfile.write('',indent) # end if - # Write array bounds check. - # Assign lower/upper bounds to internal_var (scalar) + # Write size check for each dimension in array. # - If intent is not out. # - Only for types int and real. - # - Skip test for assumed-shape arrays with a lower bounds other than one. - lower_bound = svar.get_prop_value('lower_bound') - if lower_bound == True: - lmsg = "Skipping error check for {}. Can't perfrom this check when lower-bounds is not 1." - self.run_env.logger.info(lmsg.format(local_name)) - elif (vtype == "integer") or (vtype == "real"): + if (vtype == "integer") or (vtype == "real"): if not intent == 'out': - internal_var_lname = internal_var.get_prop_value('local_name') tmp_indent = indent if conditional != '.true.': 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_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) + ndims = len(dim_lengths) + # Loop through all dimensions in var and check if length of each dimension + # is the correct size. + for index, dim_length in enumerate(dim_lengths): + array_ref = '(' + # Dimension(s) before current rank to be checked. + array_ref += '1,'*(index) + # Dimension to check. + array_ref += dim_strings[index] + # Dimension(s) after current rank to be checked. + array_ref += ',1'*(ndims-(index+1)) + array_ref += ')' + # + outfile.write(f"! Check length of {local_names[index]}{array_ref}", tmp_indent) + outfile.write(f"if (size({local_names[index]}{array_ref}) /= {dim_length}) then ", tmp_indent) + outfile.write(f"write({errmsg}, '(a)') 'In group {self.__group.name} before {self.__subroutine_name}:'", tmp_indent+1) + outfile.write(f"write({errmsg}, '(2(a,i8))') 'for array {local_names[index]}{array_ref}, expected size ', {dim_length}, ' but got ', size({local_names[index]}{array_ref})", tmp_indent+1) + outfile.write(f"{errcode} = 1", tmp_indent+1) + outfile.write(f"return", tmp_indent+1) + outfile.write(f"end if", tmp_indent) + # end for if conditional != '.true.': outfile.write(f"end if", indent) # end if diff --git a/test/capgen_test/run_test b/test/capgen_test/run_test index b99f6f2e..99ffc6a2 100755 --- a/test/capgen_test/run_test +++ b/test/capgen_test/run_test @@ -146,7 +146,8 @@ input_vars_ddt="${input_vars_ddt},model_times,number_of_model_times" input_vars_ddt="${input_vars_ddt},surface_air_pressure" output_vars_ddt="ccpp_error_code,ccpp_error_message" output_vars_ddt="${output_vars_ddt},model_times,number_of_model_times,surface_air_pressure" -required_vars_temp="ccpp_error_code,ccpp_error_message" +required_vars_temp="array_variable_for_testing" +required_vars_temp="${required_vars_temp},ccpp_error_code,ccpp_error_message" required_vars_temp="${required_vars_temp},coefficients_for_interpolation" required_vars_temp="${required_vars_temp},configuration_variable" required_vars_temp="${required_vars_temp},horizontal_dimension" @@ -166,7 +167,8 @@ required_vars_temp="${required_vars_temp},upper_bound_of_vertical_dimension_of_s required_vars_temp="${required_vars_temp},vertical_interface_dimension" required_vars_temp="${required_vars_temp},vertical_layer_dimension" required_vars_temp="${required_vars_temp},water_vapor_specific_humidity" -input_vars_temp="coefficients_for_interpolation" +input_vars_temp="array_variable_for_testing" +input_vars_temp="${input_vars_temp},coefficients_for_interpolation" input_vars_temp="${input_vars_temp},configuration_variable" input_vars_temp="${input_vars_temp},horizontal_dimension" input_vars_temp="${input_vars_temp},horizontal_loop_begin" @@ -185,7 +187,8 @@ input_vars_temp="${input_vars_temp},upper_bound_of_vertical_dimension_of_soil" input_vars_temp="${input_vars_temp},vertical_interface_dimension" input_vars_temp="${input_vars_temp},vertical_layer_dimension" input_vars_temp="${input_vars_temp},water_vapor_specific_humidity" -output_vars_temp="ccpp_error_code,ccpp_error_message" +output_vars_temp="array_variable_for_testing" +output_vars_temp="${output_vars_temp},ccpp_error_code,ccpp_error_message" output_vars_temp="${output_vars_temp},coefficients_for_interpolation" output_vars_temp="${output_vars_temp},potential_temperature" output_vars_temp="${output_vars_temp},potential_temperature_at_interface" diff --git a/test/capgen_test/temp_set.F90 b/test/capgen_test/temp_set.F90 index a0cb5e64..d000b1ab 100644 --- a/test/capgen_test/temp_set.F90 +++ b/test/capgen_test/temp_set.F90 @@ -19,7 +19,7 @@ MODULE temp_set !! \htmlinclude arg_table_temp_set_run.html !! SUBROUTINE temp_set_run(ncol, lev, timestep, temp_level, temp_diag, temp, ps, & - to_promote, promote_pcnst, slev_lbound, soil_levs, errmsg, errflg) + to_promote, promote_pcnst, slev_lbound, soil_levs, var_array, errmsg, errflg) !---------------------------------------------------------------- IMPLICIT NONE !---------------------------------------------------------------- @@ -31,6 +31,7 @@ SUBROUTINE temp_set_run(ncol, lev, timestep, temp_level, temp_diag, temp, ps, & REAL(kind_phys), INTENT(inout) :: temp_level(:, :) real(kind_phys), intent(inout) :: temp_diag(:,:) real(kind_phys), intent(inout) :: soil_levs(slev_lbound:) + real(kind_phys), intent(inout) :: var_array(:,:,:,:) real(kind_phys), intent(out) :: to_promote(:, :) real(kind_phys), intent(out) :: promote_pcnst(:) character(len=512), intent(out) :: errmsg @@ -58,6 +59,8 @@ SUBROUTINE temp_set_run(ncol, lev, timestep, temp_level, temp_diag, temp, ps, & end do end do + var_array(:,:,:,:) = 1._kind_phys + END SUBROUTINE temp_set_run !> \section arg_table_temp_set_init Argument Table diff --git a/test/capgen_test/temp_set.meta b/test/capgen_test/temp_set.meta index c7339e5c..adfe1532 100644 --- a/test/capgen_test/temp_set.meta +++ b/test/capgen_test/temp_set.meta @@ -82,7 +82,14 @@ type = real kind = kind_phys intent = inout - lower_bound = True +[ var_array ] + standard_name = array_variable_for_testing + long_name = array variable for testing + units = none + dimensions = (horizontal_dimension,2,4,6) + type = real + kind = kind_phys + intent = inout [ errmsg ] standard_name = ccpp_error_message long_name = Error message for error handling in CCPP diff --git a/test/capgen_test/test_host.F90 b/test/capgen_test/test_host.F90 index dede965c..fd31b145 100644 --- a/test/capgen_test/test_host.F90 +++ b/test/capgen_test/test_host.F90 @@ -368,7 +368,7 @@ program test character(len=cs), target :: test_parts1(2) = (/ 'physics1 ', & 'physics2 ' /) character(len=cs), target :: test_parts2(1) = (/ 'data_prep ' /) - character(len=cm), target :: test_invars1(9) = (/ & + character(len=cm), target :: test_invars1(10) = (/ & 'potential_temperature ', & 'potential_temperature_at_interface ', & 'coefficients_for_interpolation ', & @@ -377,8 +377,9 @@ program test 'potential_temperature_increment ', & 'soil_levels ', & 'temperature_at_diagnostic_levels ', & - 'time_step_for_physics ' /) - character(len=cm), target :: test_outvars1(9) = (/ & + 'time_step_for_physics ', & + 'array_variable_for_testing ' /) + character(len=cm), target :: test_outvars1(10) = (/ & 'potential_temperature ', & 'potential_temperature_at_interface ', & 'coefficients_for_interpolation ', & @@ -387,8 +388,9 @@ program test 'soil_levels ', & 'temperature_at_diagnostic_levels ', & 'ccpp_error_code ', & - 'ccpp_error_message ' /) - character(len=cm), target :: test_reqvars1(11) = (/ & + 'ccpp_error_message ', & + 'array_variable_for_testing ' /) + character(len=cm), target :: test_reqvars1(12) = (/ & 'potential_temperature ', & 'potential_temperature_at_interface ', & 'coefficients_for_interpolation ', & @@ -399,7 +401,8 @@ program test 'soil_levels ', & 'temperature_at_diagnostic_levels ', & 'ccpp_error_code ', & - 'ccpp_error_message ' /) + 'ccpp_error_message ', & + 'array_variable_for_testing ' /) character(len=cm), target :: test_invars2(3) = (/ & 'model_times ', & diff --git a/test/capgen_test/test_host_mod.F90 b/test/capgen_test/test_host_mod.F90 index 70302e3c..f2586a77 100644 --- a/test/capgen_test/test_host_mod.F90 +++ b/test/capgen_test/test_host_mod.F90 @@ -23,6 +23,7 @@ module test_host_mod real(kind_phys) :: temp_interfaces(ncols, pverP) real(kind_phys) :: temp_diag(ncols,6) real(kind_phys) :: coeffs(ncols) + real(kind_phys) :: var_array(ncols,2,4,6) real(kind_phys), dimension(DiagDimStart:ncols, DiagDimStart:pver) :: & diag1, & diag2 diff --git a/test/capgen_test/test_host_mod.meta b/test/capgen_test/test_host_mod.meta index ea562835..08627af0 100644 --- a/test/capgen_test/test_host_mod.meta +++ b/test/capgen_test/test_host_mod.meta @@ -125,3 +125,9 @@ units = none dimensions = (horizontal_dimension) type = real | kind = kind_phys +[ var_array ] + standard_name = array_variable_for_testing + long_name = array variable for testing + units = none + dimensions = (horizontal_dimension,2,4,6) + type = real | kind = kind_phys diff --git a/test/capgen_test/test_reports.py b/test/capgen_test/test_reports.py index 53f18d1c..fb38a4dc 100644 --- a/test/capgen_test/test_reports.py +++ b/test/capgen_test/test_reports.py @@ -95,7 +95,8 @@ def usage(errmsg=None): "surface_air_pressure", "time_step_for_physics", "water_vapor_specific_humidity", "soil_levels", - "temperature_at_diagnostic_levels"] + "temperature_at_diagnostic_levels", + "array_variable_for_testing"] _INPUT_VARS_TEMP = ["potential_temperature", "potential_temperature_at_interface", "coefficients_for_interpolation", @@ -103,14 +104,16 @@ def usage(errmsg=None): "surface_air_pressure", "time_step_for_physics", "water_vapor_specific_humidity", "soil_levels", - "temperature_at_diagnostic_levels"] + "temperature_at_diagnostic_levels", + "array_variable_for_testing"] _OUTPUT_VARS_TEMP = ["ccpp_error_code", "ccpp_error_message", "potential_temperature", "potential_temperature_at_interface", "coefficients_for_interpolation", "surface_air_pressure", "water_vapor_specific_humidity", "soil_levels", - "temperature_at_diagnostic_levels"] + "temperature_at_diagnostic_levels", + "array_variable_for_testing"] def fields_string(field_type, field_list, sep): """Create an error string for field(s), . From 23776a14e472acf2e5aaeb51fb1421b93adf507e Mon Sep 17 00:00:00 2001 From: Dustin Swales Date: Thu, 24 Apr 2025 14:47:22 +0000 Subject: [PATCH 4/6] Add test to scheme for bounds. --- test/capgen_test/temp_set.F90 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/capgen_test/temp_set.F90 b/test/capgen_test/temp_set.F90 index d000b1ab..760fbf25 100644 --- a/test/capgen_test/temp_set.F90 +++ b/test/capgen_test/temp_set.F90 @@ -41,6 +41,7 @@ SUBROUTINE temp_set_run(ncol, lev, timestep, temp_level, temp_diag, temp, ps, & integer :: col_index integer :: lev_index + real(kind_phys) :: internal_scalar_var errmsg = '' errflg = 0 @@ -61,6 +62,10 @@ SUBROUTINE temp_set_run(ncol, lev, timestep, temp_level, temp_diag, temp, ps, & var_array(:,:,:,:) = 1._kind_phys + ! + internal_scalar_var = soil_levs(slev_lbound) + internal_scalar_var = soil_levs(0) + END SUBROUTINE temp_set_run !> \section arg_table_temp_set_init Argument Table From 99c81c8bb48cae73a285ccc612d3291e74fb494b Mon Sep 17 00:00:00 2001 From: Dustin Swales Date: Tue, 13 May 2025 14:58:27 +0000 Subject: [PATCH 5/6] Remove initials in comments --- scripts/suite_objects.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/suite_objects.py b/scripts/suite_objects.py index dd6e81d4..11e571b7 100755 --- a/scripts/suite_objects.py +++ b/scripts/suite_objects.py @@ -1360,7 +1360,7 @@ def add_var_debug_check(self, var): if not ':' in dim: dim_var = self.find_variable(standard_name=dim) if not dim_var: - # DJS2025: To allow for numerical dimensions in metadata. + # To allow for numerical dimensions in metadata. if not dim.isnumeric(): raise Exception(f"No dimension with standard name '{dim}'") # end if @@ -1371,7 +1371,7 @@ def add_var_debug_check(self, var): (ldim, udim) = dim.split(":") ldim_var = self.find_variable(standard_name=ldim) if not ldim_var: - # DJS2025: To allow for numerical dimensions in metadata. + # To allow for numerical dimensions in metadata. if not ldim.isnumeric(): raise Exception(f"No dimension with standard name '{ldim}'") # end if @@ -1379,7 +1379,7 @@ def add_var_debug_check(self, var): self.update_group_call_list_variable(ldim_var) udim_var = self.find_variable(standard_name=udim) if not udim_var: - # DJS2025: To allow for numerical dimensions in metadata. + # To allow for numerical dimensions in metadata. if not udim.isnumeric(): raise Exception(f"No dimension with standard name '{udim}'") # end if @@ -1544,7 +1544,7 @@ def write_var_debug_check(self, var, internal_var, cldicts, outfile, errcode, er ldim_lname = dvar.get_prop_value('local_name') break if not dvar: - # DJS2025: To allow for numerical dimensions in metadata. + # To allow for numerical dimensions in metadata. if ldim.isnumeric(): ldim_lname = ldim else: @@ -1558,7 +1558,7 @@ def write_var_debug_check(self, var, internal_var, cldicts, outfile, errcode, er udim_lname = dvar.get_prop_value('local_name') break if not dvar: - # DJS2025: To allow for numerical dimensions in metadata. + # To allow for numerical dimensions in metadata. if udim.isnumeric(): udim_lname = udim else: From f20d0d7e1d2fefa44257b0a1dd2545f97a3cc6b1 Mon Sep 17 00:00:00 2001 From: Dustin Swales Date: Tue, 13 May 2025 15:35:37 +0000 Subject: [PATCH 6/6] Address Issue 590 --- scripts/suite_objects.py | 51 +++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/scripts/suite_objects.py b/scripts/suite_objects.py index 11e571b7..7100d479 100755 --- a/scripts/suite_objects.py +++ b/scripts/suite_objects.py @@ -1594,8 +1594,9 @@ def write_var_debug_check(self, var, internal_var, cldicts, outfile, errcode, er # end if outfile.write(f"! Check size of array {local_name}", tmp_indent) outfile.write(f"if (size({local_name}{dim_string}) /= {array_size}) then", tmp_indent) - outfile.write(f"write({errmsg}, '(a)') 'In group {self.__group.name} before {self.__subroutine_name}:'", tmp_indent+1) - outfile.write(f"write({errmsg}, '(2(a,i8))') 'for array {local_name}, expected size ', {array_size}, ' but got ', size({local_name})", tmp_indent+1) + outfile.write(f"write({errmsg}, '(2(a,i8))') 'In group {self.__group.name} before "\ + f"{self.__subroutine_name}: for array {local_name}, expected size ', "\ + f"{array_size}, ' but got ', size({local_name})", tmp_indent+1) outfile.write(f"{errcode} = 1", tmp_indent+1) outfile.write(f"return", tmp_indent+1) outfile.write(f"end if", tmp_indent) @@ -1616,26 +1617,32 @@ def write_var_debug_check(self, var, internal_var, cldicts, outfile, errcode, er outfile.write(f"if {conditional} then", indent) # end if ndims = len(dim_lengths) - # Loop through all dimensions in var and check if length of each dimension - # is the correct size. - for index, dim_length in enumerate(dim_lengths): - array_ref = '(' - # Dimension(s) before current rank to be checked. - array_ref += '1,'*(index) - # Dimension to check. - array_ref += dim_strings[index] - # Dimension(s) after current rank to be checked. - array_ref += ',1'*(ndims-(index+1)) - array_ref += ')' - # - outfile.write(f"! Check length of {local_names[index]}{array_ref}", tmp_indent) - outfile.write(f"if (size({local_names[index]}{array_ref}) /= {dim_length}) then ", tmp_indent) - outfile.write(f"write({errmsg}, '(a)') 'In group {self.__group.name} before {self.__subroutine_name}:'", tmp_indent+1) - outfile.write(f"write({errmsg}, '(2(a,i8))') 'for array {local_names[index]}{array_ref}, expected size ', {dim_length}, ' but got ', size({local_names[index]}{array_ref})", tmp_indent+1) - outfile.write(f"{errcode} = 1", tmp_indent+1) - outfile.write(f"return", tmp_indent+1) - outfile.write(f"end if", tmp_indent) - # end for + + # Loop through dimensions in var and check if length of each dimension + # is the correct size. Skip for 1D variables. + if (ndims > 1): + for index, dim_length in enumerate(dim_lengths): + array_ref = '(' + # Dimension(s) before current rank to be checked. + array_ref += '1,'*(index) + # Dimension to check. + array_ref += dim_strings[index] + # Dimension(s) after current rank to be checked. + array_ref += ',1'*(ndims-(index+1)) + array_ref += ')' + # + outfile.write(f"! Check length of {local_names[index]}{array_ref}", tmp_indent) + outfile.write(f"if (size({local_names[index]}{array_ref}) /= {dim_length}) then ", \ + tmp_indent) + outfile.write(f"write({errmsg}, '(2(a,i8))') 'In group {self.__group.name} before " \ + f"{self.__subroutine_name}: for array {local_names[index]}{array_ref}, "\ + f"expected size ', {dim_length}, ' but got ', " \ + f"size({local_names[index]}{array_ref})", tmp_indent+1) + outfile.write(f"{errcode} = 1", tmp_indent+1) + outfile.write(f"return", tmp_indent+1) + outfile.write(f"end if", tmp_indent) + # end for + #end if if conditional != '.true.': outfile.write(f"end if", indent) # end if