diff --git a/scripts/common.py b/scripts/common.py index 3484a3c2..84520222 100755 --- a/scripts/common.py +++ b/scripts/common.py @@ -148,6 +148,21 @@ def isstring(s): """Return true if a variable is a string""" return isinstance(s, str) +def insert_plus_sign_for_positive_exponents(string): + """Parse a string (a unit string) and insert plus (+) signs + for positive exponents where needed""" + # Break up the string by spaces + items = string.split() + # Identify units with positive exponents + # without a plus sign (m2 instead of m+2). + pattern = re.compile(r"([a-zA-Z]+)([0-9]+)") + for index, item in enumerate(items): + match = pattern.match(item) + if match: + items[index] = "+".join(match.groups()) + # Recombine items to string + return " ".join(items) + def string_to_python_identifier(string): """Replaces forbidden characters in strings with standard substitutions so that the result is a valid Python object (variable, function) name. diff --git a/scripts/conversion_tools/unit_conversion.py b/scripts/conversion_tools/unit_conversion.py index b9afa144..7fbf41a2 100755 --- a/scripts/conversion_tools/unit_conversion.py +++ b/scripts/conversion_tools/unit_conversion.py @@ -176,3 +176,23 @@ def W_m_minus_2__to__erg_cm_minus_2_s_minus_1(): def erg_cm_minus_2_s_minus_1__to__W_m_minus_2(): """Convert erg per square centimeter and second to Watt per square meter""" return '1.0E-3{kind}*{var}' + +#################### +# Equivalent units # +#################### + +def m_plus_2_s_minus_2__to__J_kg_minus_1(): + """Equivalent units""" + return '{var}' + +def J_kg_minus_1__to__m_plus_2_s_minus_2(): + """Equivalent units""" + return '{var}' + +def V_A__to__W(): + """Equivalent units""" + return '{var}' + +def W__to__V_A(): + """Equivalent units""" + return '{var}' diff --git a/scripts/metadata_parser.py b/scripts/metadata_parser.py index d1277ebe..e8c3f987 100755 --- a/scripts/metadata_parser.py +++ b/scripts/metadata_parser.py @@ -10,6 +10,7 @@ from common import encode_container, CCPP_STAGES from common import CCPP_ERROR_CODE_VARIABLE, CCPP_ERROR_MSG_VARIABLE +from common import insert_plus_sign_for_positive_exponents from mkcap import Var sys.path.append(os.path.join(os.path.split(__file__)[0], 'fortran_tools')) @@ -230,9 +231,14 @@ def read_new_metadata(filename, module_name, table_name, scheme_name = None, sub #kind = new_var.get_prop_value('kind') # *DH 20210812 + # Workaround to support units with positive exponents with + # and without a plus (+) sign. Internally, we convert all + # units from capgen to the "+"-format (i.e. "m2 s-2" --> "m+2 s-2") + units = insert_plus_sign_for_positive_exponents(new_var.get_prop_value('units')) + var = Var(standard_name = standard_name, long_name = new_var.get_prop_value('long_name') + legacy_note, - units = new_var.get_prop_value('units'), + units = units, local_name = new_var.get_prop_value('local_name'), type = new_var.get_prop_value('type').lower(), dimensions = dimensions, diff --git a/scripts/parse_tools/parse_checkers.py b/scripts/parse_tools/parse_checkers.py index 70d44c92..101bae3d 100755 --- a/scripts/parse_tools/parse_checkers.py +++ b/scripts/parse_tools/parse_checkers.py @@ -16,7 +16,8 @@ _NON_LEADING_ZERO_NUM = "[1-9]\d*" _CHAR_WITH_UNDERSCORE = "([a-zA-Z]+_[a-zA-Z]+)+" _NEGATIVE_NON_LEADING_ZERO_NUM = f"[-]{_NON_LEADING_ZERO_NUM}" -_UNIT_EXPONENT = f"({_NEGATIVE_NON_LEADING_ZERO_NUM}|{_NON_LEADING_ZERO_NUM})" +_POSITIVE_NON_LEADING_ZERO_NUM = f"[+]{_NON_LEADING_ZERO_NUM}" +_UNIT_EXPONENT = f"({_NEGATIVE_NON_LEADING_ZERO_NUM}|{_POSITIVE_NON_LEADING_ZERO_NUM}|{_NON_LEADING_ZERO_NUM})" _UNIT_REGEX = f"[a-zA-Z]+{_UNIT_EXPONENT}?" _UNITS_REGEX = f"^({_CHAR_WITH_UNDERSCORE}|{_UNIT_REGEX}(\s{_UNIT_REGEX})*|{_UNITLESS_REGEX})$" _UNITS_RE = re.compile(_UNITS_REGEX) @@ -29,6 +30,10 @@ def check_units(test_val, prop_dict, error): 'm s-1' >>> check_units('kg m-3', None, True) 'kg m-3' + >>> check_units('m2 s-2', None, True) + 'm2 s-2' + >>> check_units('m+2 s-2', None, True) + 'm+2 s-2' >>> check_units('1', None, True) '1' >>> check_units('', None, False) diff --git a/scripts/var_props.py b/scripts/var_props.py index fcbed74c..52800ba4 100755 --- a/scripts/var_props.py +++ b/scripts/var_props.py @@ -852,6 +852,20 @@ class VarCompatObj: "var_stdname", "real", "kind_phys", "km",['horizontal_dimension', 'vertical_layer_dimension'], "var2_lname", True, \ _DOCTEST_RUNENV).reverse_transform("var1_lname", "var2_lname", ('i','k'), ('i','nk-k+1')) 'var1_lname(i,nk-k+1) = 1.0E+3_kind_phys*var2_lname(i,k)' + + # Test that a 2-D var with equivalent units works and that it + # skips any unit transformations + >>> VarCompatObj("var_stdname", "real", "kind_phys", "m2 s-2", ['horizontal_dimension'], "var1_lname", False, \ + "var_stdname", "real", "kind_phys", "J kg-1", ['horizontal_dimension'], "var2_lname", False, \ + _DOCTEST_RUNENV).forward_transform("var1_lname", "var2_lname", 'i', 'i') + 'var1_lname(i) = var2_lname(i)' + + # Test that a 2-D var with identical units works and that it + # skips any unit transformations + >>> VarCompatObj("var_stdname", "real", "kind_phys", "m2 s-2", ['horizontal_dimension'], "var1_lname", False, \ + "var_stdname", "real", "kind_phys", "m+2 s-2", ['horizontal_dimension'], "var2_lname", False, \ + _DOCTEST_RUNENV).forward_transform("var1_lname", "var2_lname", 'i', 'i') + 'var1_lname(i) = var2_lname(i)' """ def __init__(self, var1_stdname, var1_type, var1_kind, var1_units, @@ -960,20 +974,27 @@ def __init__(self, var1_stdname, var1_type, var1_kind, var1_units, # A tendency variable's units should be " s-1" tendency_split_units = var1_units.split('s-1')[0].strip() if tendency_split_units != var2_units: - # We don't currently support unit conversions for tendency variables - emsg = f"\nMismatch tendency variable units '{var1_units}'" - emsg += f" for variable '{var1_stdname}'." - emsg += " No variable transforms supported for tendencies." - emsg += f" Tendency units should be '{var2_units} s-1' to match state variable." - self.__equiv = False - self.__compat = False - incompat_reason.append(emsg) + # We don't currently support unit conversions for tendency variables, + # but we can check if the units are identical or equivalent + unit_transforms = self._get_unit_convstrs(tendency_split_units, + var2_units) + if not unit_transforms == (None, None): + emsg = f"\nMismatch tendency variable units '{var1_units}'" + emsg += f" for variable '{var1_stdname}'." + emsg += " No variable transforms supported for tendencies." + emsg += f" Tendency units should be '{var2_units} s-1' to match state variable." + self.__equiv = False + self.__compat = False + incompat_reason.append(emsg) # end if elif var1_units != var2_units: # Try to find a set of unit conversions - self.__equiv = False - self.__unit_transforms = self._get_unit_convstrs(var1_units, - var2_units) + unit_transforms = self._get_unit_convstrs(var1_units, + var2_units) + # Handle equivalent or identical units = (None, None) + if not unit_transforms == (None, None): + self.__equiv = False + self.__unit_transforms = unit_transforms # end if # end if if self.__compat: @@ -1148,7 +1169,8 @@ def _get_kind_convstrs(self, var1_kind, var2_kind, run_env): def _get_unit_convstrs(self, var1_units, var2_units): """Attempt to retrieve the forward and reverse unit transformations for transforming a variable in to / from a variable in - . + . Return (None, None) if units are equivalent or identical + after parsing (this can happen when comparing m2 and m+2). # Initial setup >>> from parse_tools import init_log, set_log_to_null @@ -1177,6 +1199,14 @@ def _get_unit_convstrs(self, var1_units, var2_units): ('1.0E+3{kind}*{var}', '1.0E-3{kind}*{var}') >>> _DOCTEST_VCOMPAT._get_unit_convstrs('C', 'K') ('{var}+273.15{kind}', '{var}-273.15{kind}') + >>> _DOCTEST_VCOMPAT._get_unit_convstrs('V A', 'W') + (None, None) + >>> _DOCTEST_VCOMPAT._get_unit_convstrs('m2 s-2', 'J kg-1') + (None, None) + >>> _DOCTEST_VCOMPAT._get_unit_convstrs('m+2 s-2', 'J kg-1') + (None, None) + >>> _DOCTEST_VCOMPAT._get_unit_convstrs('m+2 s-2', 'm2 s-2') + (None, None) # Try an invalid conversion >>> _DOCTEST_VCOMPAT._get_unit_convstrs('1', 'none') #doctest: +ELLIPSIS @@ -1192,6 +1222,11 @@ def _get_unit_convstrs(self, var1_units, var2_units): """ u1_str = self.units_to_string(var1_units, self.__v1_context) u2_str = self.units_to_string(var2_units, self.__v2_context) + # If u1_str and u2_str are identical, for example after parsing + # "m2 s-2" and "m+2 s-2", return (None, None) to signal that + # the units are in fact identical + if u1_str == u2_str: + return (None, None) unit_conv_str = "{0}__to__{1}".format(u1_str, u2_str) try: forward_transform = getattr(unit_conversion, unit_conv_str)() @@ -1210,7 +1245,11 @@ def _get_unit_convstrs(self, var1_units, var2_units): self.__stdname, context=self.__v1_context)) # end if - return (forward_transform, reverse_transform) + # For equivalent units, return (None, None) + if forward_transform == '{var}' and reverse_transform == '{var}': + return (None, None) + else: + return (forward_transform, reverse_transform) def _get_dim_transforms(self, var1_dims, var2_dims): """Attempt to find forward and reverse permutations for transforming a @@ -1355,8 +1394,17 @@ def units_to_string(self, units, context=None): """Replace variable unit description with string that is a legal Python identifier. If the resulting string is a Python keyword, raise an exception.""" - # Replace each whitespace with an underscore - string = units.replace(" ","_") + # Start with breaking up the string by spaces + items = units.split() + # Identify units with positive exponents + # without a plus sign (m2 instead of m+2). + pattern = re.compile(r"([a-zA-Z]+)([0-9]+)") + for index, item in enumerate(items): + match = pattern.match(item) + if match: + items[index] = "+".join(match.groups()) + # Combine list into string using underscores + string = "_".join(items) # Replace each minus sign with '_minus_' string = string.replace("-","_minus_") # Replace each plus sign with '_plus_' @@ -1458,7 +1506,7 @@ def has_unit_transforms(self): and arguments to produce code to transform one variable into the correct units of the other. """ - return self.__unit_transforms is not None + return self.__unit_transforms is not None and self.__unit_transforms[0] def __bool__(self): """Return True if this object describes two Var objects which are diff --git a/test/unit_tests/test_var_transforms.py b/test/unit_tests/test_var_transforms.py index d0b5800e..7a2592b3 100755 --- a/test/unit_tests/test_var_transforms.py +++ b/test/unit_tests/test_var_transforms.py @@ -428,27 +428,47 @@ def test_compatible_tendency_variable(self): self.assertFalse(compat.has_dim_transforms) self.assertFalse(compat.has_unit_transforms) + def test_compatible_tendency_variable_equivalent_units(self): + """Test that a given tendency variable is compatible with + its corresponding state variable""" + real_array1 = self._new_var('real_stdname1', 'V A', + ['horizontal_dimension', + 'vertical_layer_dimension'], + 'real', vkind='kind_phys') + real_array2 = self._new_var('tendency_of_real_stdname1', 'W s-1', + ['horizontal_dimension', + 'vertical_layer_dimension'], + 'real', vkind='kind_phys') + compat = real_array2.compatible(real_array1, self.__run_env, is_tend=True) + self.assertIsInstance(compat, VarCompatObj, + msg=self.__inst_emsg.format(type(compat))) + self.assertTrue(compat) + self.assertTrue(compat.compat) + self.assertEqual(compat.incompat_reason, '') + self.assertFalse(compat.has_kind_transforms) + self.assertFalse(compat.has_dim_transforms) + self.assertFalse(compat.has_unit_transforms) + def test_incompatible_tendency_variable(self): """Test that the correct error is returned when a given tendency variable has inconsistent units vs the state variable""" - real_array1 = self._new_var('real_stdname1', 'C', + real_array1 = self._new_var('real_stdname1', 'm', ['horizontal_dimension', 'vertical_layer_dimension'], 'real', vkind='kind_phys') - real_array2 = self._new_var('tendency_of_real_stdname1', 'C kg s-1', + real_array2 = self._new_var('tendency_of_real_stdname1', 'cm s-1', ['horizontal_dimension', 'vertical_layer_dimension'], 'real', vkind='kind_phys') compat = real_array2.compatible(real_array1, self.__run_env, is_tend=True) self.assertIsInstance(compat, VarCompatObj, msg=self.__inst_emsg.format(type(compat))) - #Verify correct error message returned - emsg = "\nMismatch tendency variable units 'C kg s-1' for variable 'tendency_of_real_stdname1'. No variable transforms supported for tendencies. Tendency units should be 'C s-1' to match state variable." + # Verify correct error message returned + emsg = "\nMismatch tendency variable units 'cm s-1' for variable 'tendency_of_real_stdname1'. No variable transforms supported for tendencies. Tendency units should be 'm s-1' to match state variable." self.assertEqual(compat.incompat_reason, emsg) self.assertFalse(compat.has_kind_transforms) self.assertFalse(compat.has_dim_transforms) self.assertFalse(compat.has_unit_transforms) - #Verify correct error message returned if __name__ == "__main__": diff --git a/test/var_compatibility_test/effr_calc.F90 b/test/var_compatibility_test/effr_calc.F90 index 0bef2949..0b626c16 100644 --- a/test/var_compatibility_test/effr_calc.F90 +++ b/test/var_compatibility_test/effr_calc.F90 @@ -37,7 +37,8 @@ end subroutine effr_calc_init !! 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) + has_graupel, scalar_var, tke_inout, tke2_inout, & + errmsg, errflg) integer, intent(in) :: ncol integer, intent(in) :: nlev @@ -53,6 +54,9 @@ subroutine effr_calc_run(ncol, nlev, effrr_in, effrg_in, ncg_in, nci_out, & character(len=512), intent(out) :: errmsg integer, intent(out) :: errflg real(kind_phys), intent(out),optional :: ncl_out(:,:) + real(kind_phys), intent(inout) :: tke_inout + real(kind_phys), intent(inout) :: tke2_inout + !---------------------------------------------------------------- real(kind_phys), parameter :: re_qc_min = 2.5 ! microns diff --git a/test/var_compatibility_test/effr_calc.meta b/test/var_compatibility_test/effr_calc.meta index ec074f4d..c3733f13 100644 --- a/test/var_compatibility_test/effr_calc.meta +++ b/test/var_compatibility_test/effr_calc.meta @@ -130,6 +130,22 @@ type = real kind = kind_phys intent = inout +[ tke_inout ] + standard_name = turbulent_kinetic_energy + long_name = turbulent_kinetic_energy + units = m2 s-2 + dimensions = () + type = real + kind = kind_phys + intent = inout +[ tke2_inout ] + standard_name = turbulent_kinetic_energy2 + long_name = turbulent_kinetic_energy2 + units = m+2 s-2 + dimensions = () + 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/var_compatibility_test/run_test b/test/var_compatibility_test/run_test index be8f1f6c..83a1f07e 100755 --- a/test/var_compatibility_test/run_test +++ b/test/var_compatibility_test/run_test @@ -152,9 +152,10 @@ required_vars_var_compatibility="${required_vars_var_compatibility},scalar_varia required_vars_var_compatibility="${required_vars_var_compatibility},scalar_variable_for_testing_c" required_vars_var_compatibility="${required_vars_var_compatibility},scheme_order_in_suite" required_vars_var_compatibility="${required_vars_var_compatibility},shortwave_radiation_fluxes" +required_vars_var_compatibility="${required_vars_var_compatibility},turbulent_kinetic_energy" +required_vars_var_compatibility="${required_vars_var_compatibility},turbulent_kinetic_energy2" required_vars_var_compatibility="${required_vars_var_compatibility},vertical_layer_dimension" 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" @@ -172,6 +173,8 @@ input_vars_var_compatibility="${input_vars_var_compatibility},scalar_variable_fo input_vars_var_compatibility="${input_vars_var_compatibility},scalar_variable_for_testing_c" input_vars_var_compatibility="${input_vars_var_compatibility},scheme_order_in_suite" input_vars_var_compatibility="${input_vars_var_compatibility},shortwave_radiation_fluxes" +input_vars_var_compatibility="${input_vars_var_compatibility},turbulent_kinetic_energy" +input_vars_var_compatibility="${input_vars_var_compatibility},turbulent_kinetic_energy2" 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" @@ -183,6 +186,8 @@ output_vars_var_compatibility="${output_vars_var_compatibility},longwave_radiati output_vars_var_compatibility="${output_vars_var_compatibility},scalar_variable_for_testing" output_vars_var_compatibility="${output_vars_var_compatibility},scheme_order_in_suite" output_vars_var_compatibility="${output_vars_var_compatibility},shortwave_radiation_fluxes" +output_vars_var_compatibility="${output_vars_var_compatibility},turbulent_kinetic_energy" +output_vars_var_compatibility="${output_vars_var_compatibility},turbulent_kinetic_energy2" ## ## Run a database report and check the return string diff --git a/test/var_compatibility_test/test_host.F90 b/test/var_compatibility_test/test_host.F90 index 2ff05eb7..65d06827 100644 --- a/test/var_compatibility_test/test_host.F90 +++ b/test/var_compatibility_test/test_host.F90 @@ -351,13 +351,15 @@ program test character(len=cs), target :: test_parts1(1) = (/ 'radiation ' /) - character(len=cm), target :: test_invars1(15) = (/ & + character(len=cm), target :: test_invars1(17) = (/ & '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 ', & + 'turbulent_kinetic_energy ', & + 'turbulent_kinetic_energy2 ', & 'scalar_variable_for_testing_a ', & 'scalar_variable_for_testing_b ', & 'scalar_variable_for_testing_c ', & @@ -368,7 +370,7 @@ program test 'shortwave_radiation_fluxes ', & 'longwave_radiation_fluxes '/) - character(len=cm), target :: test_outvars1(11) = (/ & + character(len=cm), target :: test_outvars1(13) = (/ & 'ccpp_error_code ', & 'ccpp_error_message ', & 'effective_radius_of_stratiform_cloud_ice_particle ', & @@ -378,10 +380,12 @@ program test 'cloud_ice_number_concentration ', & 'scalar_variable_for_testing ', & 'scheme_order_in_suite ', & + 'turbulent_kinetic_energy ', & + 'turbulent_kinetic_energy2 ', & 'shortwave_radiation_fluxes ', & - 'longwave_radiation_fluxes '/) + 'longwave_radiation_fluxes '/) - character(len=cm), target :: test_reqvars1(19) = (/ & + character(len=cm), target :: test_reqvars1(21) = (/ & 'ccpp_error_code ', & 'ccpp_error_message ', & 'effective_radius_of_stratiform_cloud_rain_particle ', & @@ -392,6 +396,8 @@ program test 'cloud_graupel_number_concentration ', & 'cloud_ice_number_concentration ', & 'scalar_variable_for_testing ', & + 'turbulent_kinetic_energy ', & + 'turbulent_kinetic_energy2 ', & 'scalar_variable_for_testing_a ', & 'scalar_variable_for_testing_b ', & 'scalar_variable_for_testing_c ', & @@ -400,7 +406,7 @@ program test 'flag_indicating_cloud_microphysics_has_graupel ', & 'flag_indicating_cloud_microphysics_has_ice ', & 'shortwave_radiation_fluxes ', & - 'longwave_radiation_fluxes '/) + 'longwave_radiation_fluxes '/) 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 964c88e7..32b7821f 100644 --- a/test/var_compatibility_test/test_host_data.F90 +++ b/test/var_compatibility_test/test_host_data.F90 @@ -23,10 +23,10 @@ module test_host_data real(kind_phys) :: scalar_var real(kind_phys) :: scalar_varA real(kind_phys) :: scalar_varB + real(kind_phys) :: tke, tke2 integer :: scalar_varC integer :: scheme_order integer :: num_subcycles - end type physics_state public :: physics_state diff --git a/test/var_compatibility_test/test_host_data.meta b/test/var_compatibility_test/test_host_data.meta index b507f11e..b931a6c1 100644 --- a/test/var_compatibility_test/test_host_data.meta +++ b/test/var_compatibility_test/test_host_data.meta @@ -60,6 +60,20 @@ dimensions = () type = real kind = kind_phys +[ tke ] + standard_name = turbulent_kinetic_energy + long_name = turbulent_kinetic_energy + units = J kg-1 + dimensions = () + type = real + kind = kind_phys +[ tke2 ] + standard_name = turbulent_kinetic_energy2 + long_name = turbulent_kinetic_energy2 + units = m2 s-2 + dimensions = () + type = real + kind = kind_phys [fluxSW] standard_name = shortwave_radiation_fluxes long_name = shortwave radiation fluxes diff --git a/test/var_compatibility_test/test_host_mod.F90 b/test/var_compatibility_test/test_host_mod.F90 index 51e36a83..5cd4846a 100644 --- a/test/var_compatibility_test/test_host_mod.F90 +++ b/test/var_compatibility_test/test_host_mod.F90 @@ -40,6 +40,8 @@ subroutine init_data() phys_state%effri = 5.0E-5 ! 50 microns, in meter phys_state%nci = 80 endif + phys_state%tke = 10.0 !J kg-1 + phys_state%tke2 = 42.0 !J kg-1 end subroutine init_data @@ -50,6 +52,7 @@ logical function compare_data() real(kind_phys), parameter :: effri_expected = 7.5E-5 ! 75 microns, in meter real(kind_phys), parameter :: effrs_expected = 5.1E-4 ! 510 microns, in meter real(kind_phys), parameter :: scalar_expected = 2.0E3 ! 2 km, in meter + real(kind_phys), parameter :: tke_expected = 10.0 ! 10 J kg-1 real(kind_phys), parameter :: tolerance = 1.0E-6 ! used as scaling factor for expected value compare_data = .true. @@ -84,6 +87,11 @@ logical function compare_data() compare_data = .false. end if + if (abs( phys_state%tke - tke_expected) > tolerance*tke_expected) then + write(6, '(a,e16.7,a,e16.7)') 'Error: max diff of tke from expected value exceeds tolerance: ', & + abs( phys_state%tke - tke_expected), ' > ', tolerance*tke_expected + compare_data = .false. + end if end function compare_data end module test_host_mod diff --git a/test/var_compatibility_test/test_reports.py b/test/var_compatibility_test/test_reports.py index 4fd2863f..03202a7b 100755 --- a/test/var_compatibility_test/test_reports.py +++ b/test/var_compatibility_test/test_reports.py @@ -76,6 +76,8 @@ def usage(errmsg=None): "effective_radius_of_stratiform_cloud_graupel", "cloud_graupel_number_concentration", "scalar_variable_for_testing", + "turbulent_kinetic_energy", + "turbulent_kinetic_energy2", "scalar_variable_for_testing_a", "scalar_variable_for_testing_b", "scalar_variable_for_testing_c", @@ -91,6 +93,9 @@ def usage(errmsg=None): "effective_radius_of_stratiform_cloud_snow_particle", "cloud_ice_number_concentration", "effective_radius_of_stratiform_cloud_rain_particle", + "turbulent_kinetic_energy", + "turbulent_kinetic_energy2", + "scalar_variable_for_testing", "scalar_variable_for_testing", "shortwave_radiation_fluxes", "longwave_radiation_fluxes", diff --git a/test_prebuild/test_unit_conv/data.F90 b/test_prebuild/test_unit_conv/data.F90 index f53c107e..c926122b 100644 --- a/test_prebuild/test_unit_conv/data.F90 +++ b/test_prebuild/test_unit_conv/data.F90 @@ -11,12 +11,13 @@ module data private public ncols, nspecies - public cdata, data_array, opt_array_flag + public cdata, data_array, data_array2, opt_array_flag integer, parameter :: ncols = 4 integer, parameter :: nspecies = 2 type(ccpp_t), target :: cdata real(kind_phys), dimension(1:ncols,1:nspecies) :: data_array + real(kind_phys), dimension(1:ncols) :: data_array2 logical :: opt_array_flag end module data diff --git a/test_prebuild/test_unit_conv/data.meta b/test_prebuild/test_unit_conv/data.meta index 895cd6c2..e709e4fc 100644 --- a/test_prebuild/test_unit_conv/data.meta +++ b/test_prebuild/test_unit_conv/data.meta @@ -37,6 +37,13 @@ dimensions = (horizontal_loop_extent) type = real kind = kind_phys +[data_array2] + standard_name = data_array2 + long_name = data array 2 in module + units = m2 s-2 + dimensions = (horizontal_loop_extent) + type = real + kind = kind_phys [opt_array_flag] standard_name = flag_for_opt_array long_name = flag for passing optional data array diff --git a/test_prebuild/test_unit_conv/main.F90 b/test_prebuild/test_unit_conv/main.F90 index 3f7ee103..3eb6462e 100644 --- a/test_prebuild/test_unit_conv/main.F90 +++ b/test_prebuild/test_unit_conv/main.F90 @@ -4,7 +4,7 @@ program test_unit_conv use ccpp_types, only: ccpp_t use data, only: ncols, nspecies - use data, only: cdata, data_array, opt_array_flag + use data, only: cdata, data_array, data_array2, opt_array_flag use ccpp_static_api, only: ccpp_physics_init, & ccpp_physics_timestep_init, & @@ -30,6 +30,7 @@ program test_unit_conv cdata%thrd_cnt = 1 data_array = 1.0_8 + data_array2 = 42.0_8 opt_array_flag = .true. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/test_prebuild/test_unit_conv/unit_conv_scheme_1.F90 b/test_prebuild/test_unit_conv/unit_conv_scheme_1.F90 index d9488789..9ef178ff 100644 --- a/test_prebuild/test_unit_conv/unit_conv_scheme_1.F90 +++ b/test_prebuild/test_unit_conv/unit_conv_scheme_1.F90 @@ -13,16 +13,18 @@ module unit_conv_scheme_1 !! This is for unit testing only real(kind_phys), parameter :: target_value = 1.0_kind_phys + real(kind_phys), parameter :: target_value2 = 42.0_kind_phys contains !! \section arg_table_unit_conv_scheme_1_run Argument Table !! \htmlinclude unit_conv_scheme_1_run.html !! - subroutine unit_conv_scheme_1_run(data_array, data_array_opt, errmsg, errflg) + subroutine unit_conv_scheme_1_run(data_array, data_array2, data_array_opt, errmsg, errflg) character(len=*), intent(out) :: errmsg integer, intent(out) :: errflg real(kind_phys), intent(inout) :: data_array(:) + real(kind_phys), intent(inout) :: data_array2(:) real(kind_phys), intent(inout), optional :: data_array_opt(:) ! Initialize CCPP error handling variables @@ -31,11 +33,19 @@ subroutine unit_conv_scheme_1_run(data_array, data_array_opt, errmsg, errflg) ! Check values in data array write(error_unit,'(a,e12.4)') 'In unit_conv_scheme_1_run: checking min/max values of data array to be approximately ', target_value if (minval(data_array)<0.99*target_value .or. maxval(data_array)>1.01*target_value) then - write(errmsg,'(3(a,e12.4),a)') "Error in unit_conv_scheme_1_run, expected values of approximately ", & + write(errmsg,'(3(a,e12.4),a)') "Error in unit_conv_scheme_1_run, expected values for data_array of approximately ", & target_value, " but got [ ", minval(data_array), " : ", maxval(data_array), " ]" errflg = 1 return end if + ! Check values in data array2 + write(error_unit,'(a,e12.4)') 'In unit_conv_scheme_1_run: checking min/max values of data array 2 to be approximately ', target_value2 + if (minval(data_array2)<0.99*target_value2 .or. maxval(data_array2)>1.01*target_value2) then + write(errmsg,'(3(a,e12.4),a)') "Error in unit_conv_scheme_1_run, expected values for data array 2 of approximately ", & + target_value2, " but got [ ", minval(data_array2), " : ", maxval(data_array2), " ]" + errflg = 1 + return + end if ! Check for presence of optional data array, then check its values write(error_unit,'(a)') 'In unit_conv_scheme_1_run: checking for presence of optional data array' if (.not. present(data_array_opt)) then diff --git a/test_prebuild/test_unit_conv/unit_conv_scheme_1.meta b/test_prebuild/test_unit_conv/unit_conv_scheme_1.meta index 91f22142..befb19bd 100644 --- a/test_prebuild/test_unit_conv/unit_conv_scheme_1.meta +++ b/test_prebuild/test_unit_conv/unit_conv_scheme_1.meta @@ -30,6 +30,14 @@ type = real kind = kind_phys intent = inout +[data_array2] + standard_name = data_array2 + long_name = data array in J kg-1 + units = J kg-1 + dimensions = (horizontal_loop_extent) + type = real + kind = kind_phys + intent = inout [data_array_opt] standard_name = data_array_opt long_name = optional data array in m diff --git a/test_prebuild/test_unit_conv/unit_conv_scheme_2.F90 b/test_prebuild/test_unit_conv/unit_conv_scheme_2.F90 index 6b64402c..66f07d93 100644 --- a/test_prebuild/test_unit_conv/unit_conv_scheme_2.F90 +++ b/test_prebuild/test_unit_conv/unit_conv_scheme_2.F90 @@ -13,16 +13,18 @@ module unit_conv_scheme_2 !! This is for unit testing only real(kind_phys), parameter :: target_value = 1.0E-3_kind_phys + real(kind_phys), parameter :: target_value2 = 42.0_kind_phys contains !! \section arg_table_unit_conv_scheme_2_run Argument Table !! \htmlinclude unit_conv_scheme_2_run.html !! - subroutine unit_conv_scheme_2_run(data_array, data_array_opt, errmsg, errflg) + subroutine unit_conv_scheme_2_run(data_array, data_array2, data_array_opt, errmsg, errflg) character(len=*), intent(out) :: errmsg integer, intent(out) :: errflg real(kind_phys), intent(inout) :: data_array(:) + real(kind_phys), intent(inout) :: data_array2(:) real(kind_phys), intent(inout), optional :: data_array_opt(:) ! Initialize CCPP error handling variables @@ -36,6 +38,14 @@ subroutine unit_conv_scheme_2_run(data_array, data_array_opt, errmsg, errflg) errflg = 1 return end if + ! Check values in data array2 + write(error_unit,'(a,e12.4)') 'In unit_conv_scheme_2_run: checking min/max values of data array 2 to be approximately ', target_value2 + if (minval(data_array2)<0.99*target_value2 .or. maxval(data_array2)>1.01*target_value2) then + write(errmsg,'(3(a,e12.4),a)') "Error in unit_conv_scheme_2_run, expected values for data array 2 of approximately ", & + target_value2, " but got [ ", minval(data_array2), " : ", maxval(data_array2), " ]" + errflg = 1 + return + end if ! Check for presence of optional data array, then check its values write(error_unit,'(a)') 'In unit_conv_scheme_2_run: checking for presence of optional data array' if (.not. present(data_array_opt)) then diff --git a/test_prebuild/test_unit_conv/unit_conv_scheme_2.meta b/test_prebuild/test_unit_conv/unit_conv_scheme_2.meta index 534e3abe..68e4b063 100644 --- a/test_prebuild/test_unit_conv/unit_conv_scheme_2.meta +++ b/test_prebuild/test_unit_conv/unit_conv_scheme_2.meta @@ -30,6 +30,14 @@ type = real kind = kind_phys intent = inout +[data_array2] + standard_name = data_array2 + long_name = data array in m+2 s-2 + units = m+2 s-2 + dimensions = (horizontal_loop_extent) + type = real + kind = kind_phys + intent = inout [data_array_opt] standard_name = data_array_opt long_name = optional data array in km