From 04a1d7ef80950264fbe8f4dedc1ef896f79b55eb Mon Sep 17 00:00:00 2001 From: Courtney Peverley Date: Fri, 25 Aug 2023 11:07:52 -0600 Subject: [PATCH 1/5] add new host cap interface to check if a variable is already a scheme constituent --- scripts/constituents.py | 24 +++++++++++++++++++++--- scripts/host_cap.py | 15 +++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/scripts/constituents.py b/scripts/constituents.py index 7a60af04..7aba9e61 100644 --- a/scripts/constituents.py +++ b/scripts/constituents.py @@ -456,9 +456,9 @@ def write_constituent_use_statements(cap, suite_list, indent): @staticmethod def write_host_routines(cap, host, reg_funcname, init_funcname, num_const_funcname, - copy_in_funcname, copy_out_funcname, const_obj_name, - const_names_name, const_indices_name, const_array_func, - advect_array_func, prop_array_func, + query_const_funcname, copy_in_funcname, copy_out_funcname, + const_obj_name, const_names_name, const_indices_name, + const_array_func, advect_array_func, prop_array_func, const_index_func, suite_list, err_vars): """Write out the host model routine which will instantiate constituent fields for all the constituents in . @@ -466,6 +466,7 @@ def write_host_routines(cap, host, reg_funcname, init_funcname, num_const_funcna Also write out the following routines: : Initialize constituent data : Number of constituents + : Check if standard name matches existing constituent : Collect constituent fields for host : Update constituent fields from host : Return a pointer to the constituent array @@ -634,6 +635,23 @@ def write_host_routines(cap, host, reg_funcname, init_funcname, num_const_funcna call_str = "call {}%num_constituents(num_flds, advected=advected, {})" cap.write(call_str.format(const_obj_name, obj_err_callstr), 2) cap.write("end {}".format(substmt), 1) + # Write query_consts routine + substmt = f"subroutine {query_const_funcname}" + cap.write("", 0) + cap.write(f"{substmt}(var_name, constituent_exists, {err_dummy_str})", 1) + cap.comment(f"Return constituent_exists = true iff var_name appears in {host.name}_model_const_stdnames", 2) + cap.write("", 0) + cap.write("character(len=*), intent(in) :: var_name", 2) + cap.write("logical, intent(out) :: constituent_exists", 2) + for evar in err_vars: + evar.write_def(cap, 2, host, dummy=True, add_intent="out") + # end for + cap.write("constituent_exists = .false.", 2) + cap.write(f"if (any({host.name}_model_const_stdnames == var_name)) then", 2) + cap.write("constituent_exists = .true.", 3) + cap.write("end if", 2) + cap.write("", 0) + cap.write(f"end {substmt}", 1) # Write copy_in routine substmt = "subroutine {}".format(copy_in_funcname) cap.write("", 0) diff --git a/scripts/host_cap.py b/scripts/host_cap.py index d833c649..4cebb517 100644 --- a/scripts/host_cap.py +++ b/scripts/host_cap.py @@ -109,6 +109,14 @@ def constituent_num_consts_funcname(host_model): Because this is a user interface API function, the name is fixed.""" return "{}_ccpp_number_constituents".format(host_model.name) +############################################################################### +def query_scheme_constituents_funcname(host_model): +############################################################################### + """Return the name of the function to return True if the standard name + passed in matches an existing constituent + Because this is a user interface API function, the name is fixed.""" + return "{}_ccpp_is_scheme_constituent".format(host_model.name) + ############################################################################### def constituent_copyin_subname(host_model): ############################################################################### @@ -449,6 +457,8 @@ def write_host_cap(host_model, api, module_name, output_dir, run_env): cap.write("public :: {}".format(init_name), 1) numconsts_name = constituent_num_consts_funcname(host_model) cap.write("public :: {}".format(numconsts_name), 1) + queryconsts_name = query_scheme_constituents_funcname(host_model) + cap.write("public :: {}".format(queryconsts_name), 1) copyin_name = constituent_copyin_subname(host_model) cap.write("public :: {}".format(copyin_name), 1) copyout_name = constituent_copyout_subname(host_model) @@ -598,8 +608,9 @@ def write_host_cap(host_model, api, module_name, output_dir, run_env): const_names_name = constituent_model_const_stdnames(host_model) const_indices_name = constituent_model_const_indices(host_model) ConstituentVarDict.write_host_routines(cap, host_model, reg_name, init_name, - numconsts_name, copyin_name, - copyout_name, const_obj_name, + numconsts_name, queryconsts_name, + copyin_name, copyout_name, + const_obj_name, const_names_name, const_indices_name, const_array_func, From 48041d88f688167653fff593dba31eb4e8bb083e Mon Sep 17 00:00:00 2001 From: Courtney Peverley Date: Fri, 25 Aug 2023 12:55:08 -0600 Subject: [PATCH 2/5] add testing --- scripts/constituents.py | 4 +- test/advection_test/cld_ice.meta | 2 + test/advection_test/test_host.F90 | 69 +++++++++++++++++++++++++++++-- 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/scripts/constituents.py b/scripts/constituents.py index 5be80721..4fdebee4 100644 --- a/scripts/constituents.py +++ b/scripts/constituents.py @@ -641,8 +641,8 @@ def write_host_routines(cap, host, reg_funcname, init_funcname, num_const_funcna cap.write(f"{substmt}(var_name, constituent_exists, {err_dummy_str})", 1) cap.comment(f"Return constituent_exists = true iff var_name appears in {host.name}_model_const_stdnames", 2) cap.write("", 0) - cap.write("character(len=*), intent(in) :: var_name", 2) - cap.write("logical, intent(out) :: constituent_exists", 2) + cap.write("character(len=*), intent(in) :: var_name", 2) + cap.write("logical, intent(out) :: constituent_exists", 2) for evar in err_vars: evar.write_def(cap, 2, host, dummy=True, add_intent="out") # end for diff --git a/test/advection_test/cld_ice.meta b/test/advection_test/cld_ice.meta index f66888e0..010fb419 100644 --- a/test/advection_test/cld_ice.meta +++ b/test/advection_test/cld_ice.meta @@ -44,6 +44,7 @@ [ cld_ice_array ] standard_name = cloud_ice_dry_mixing_ratio advected = .true. + default_value = 0.0_kind_phys units = kg kg-1 dimensions = (horizontal_loop_extent, vertical_layer_dimension) type = real | kind = kind_phys @@ -76,6 +77,7 @@ [ cld_ice_array ] standard_name = cloud_ice_dry_mixing_ratio advected = .true. + default_value = 0.0_kind_phys units = kg kg-1 dimensions = (horizontal_dimension, vertical_layer_dimension) type = real | kind = kind_phys diff --git a/test/advection_test/test_host.F90 b/test/advection_test/test_host.F90 index a93b59f4..86de7cd7 100644 --- a/test/advection_test/test_host.F90 +++ b/test/advection_test/test_host.F90 @@ -222,6 +222,7 @@ subroutine test_host(retval, test_suites) use test_host_mod, only: init_data, compare_data use test_host_mod, only: ncols, pver use test_host_ccpp_cap, only: test_host_ccpp_register_constituents + use test_host_ccpp_cap, only: test_host_ccpp_is_scheme_constituent use test_host_ccpp_cap, only: test_host_ccpp_initialize_constituents use test_host_ccpp_cap, only: test_host_ccpp_number_constituents use test_host_ccpp_cap, only: test_host_constituents_array @@ -245,11 +246,14 @@ subroutine test_host(retval, test_suites) integer :: num_suites integer :: num_advected ! Num advected species logical :: const_log + logical :: is_constituent + logical :: has_default character(len=128), allocatable :: suite_names(:) character(len=256) :: const_str character(len=512) :: errmsg integer :: errflg real(kind_phys), pointer :: const_ptr(:,:,:) + real(kind_phys) :: default_value type(ccpp_constituent_prop_ptr_t), pointer :: const_props(:) character(len=*), parameter :: subname = 'test_host' @@ -280,8 +284,29 @@ subroutine test_host(retval, test_suites) return end if - ! Register the constituents to find out what needs advecting - call host_constituents(1)%instantiate(std_name="specific_humidity", & + errflg = 0 + errmsg = '' + + ! Check that is_scheme_constituent works as expected + call test_host_ccpp_is_scheme_constituent('specific_humidity', & + is_constituent, errflg, errmsg) + call check_errflg(subname//"_ccpp_is_scheme_constituent", errflg, errmsg) + ! specific_humidity should not be an existing constituent + if (is_constituent) then + write(6, *) "ERROR: specific humidity is already a constituent" + end if + call test_host_ccpp_is_scheme_constituent('cloud_ice_dry_mixing_ratio', & + is_constituent, errflg, errmsg) + call check_errflg(subname//"_ccpp_is_scheme_constituent", errflg, errmsg) + ! cloud_ice_dry_mixing_ratio should be an existing constituent + if (.not. is_constituent) then + write(6, *) "ERROR: cloud_ice_dry_mixing ratio not found in ", & + "host cap constituent list" + end if + + + ! Register the constituents to find out what needs advecting + call host_constituents(1)%instantiate(std_name="specific_humidity", & long_name="Specific humidity", units="kg kg-1", & vertical_dim="vertical_layer_dimension", advected=.true., & errcode=errflg, errmsg=errmsg) @@ -385,7 +410,45 @@ subroutine test_host(retval, test_suites) end if end if - ! Use the suite information to setup the run + if (errflg == 0) then + call const_props(index_liq)%has_default(has_default, errflg, errmsg) + if (errflg /= 0) then + write(6, '(a,i0,a,i0,/,a)') "ERROR: Error, ", errflg, " trying ", & + "to check for default for cld_liq index = ", index_liq, trim(errmsg) + end if + end if + if (errflg == 0) then + if (has_default) then + write(6, *) "ERROR: cloud liquid mass_mixing_ratio should not have default but does" + end if + end if + if (errflg == 0) then + call const_props(index_ice)%has_default(has_default, errflg, errmsg) + if (errflg /= 0) then + write(6, '(a,i0,a,i0,/,a)') "ERROR: Error, ", errflg, " trying ", & + "to check for default for cld_ice index = ", index_ice, trim(errmsg) + end if + end if + if (errflg == 0) then + if (.not. has_default) then + write(6, *) "ERROR: cloud ice mass_mixing_ratio should have default but doesn't" + end if + end if + if (errflg == 0) then + call const_props(index_ice)%default_value(default_value, errflg, errmsg) + if (errflg /= 0) then + write(6, '(a,i0,a,i0,/,a)') "ERROR: Error, ", errflg, " trying ", & + "to grab default for cld_ice index = ", index_ice, trim(errmsg) + end if + end if + if (errflg == 0) then + if (default_value /= 0.0_kind_phys) then + write(6, *) "ERROR: cloud ice mass_mixing_ratio default is ", default_value, & + " but should be 0.0" + end if + end if + + ! Use the suite information to setup the run do sind = 1, num_suites if (errflg == 0) then call test_host_ccpp_physics_initialize( & From 6819f2e18022f30be962d90f19a47e3506a017ec Mon Sep 17 00:00:00 2001 From: Courtney Peverley Date: Mon, 28 Aug 2023 15:19:30 -0600 Subject: [PATCH 3/5] fix check_errflg call --- test/advection_test/test_host.F90 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/advection_test/test_host.F90 b/test/advection_test/test_host.F90 index a1fb9147..d28422c7 100644 --- a/test/advection_test/test_host.F90 +++ b/test/advection_test/test_host.F90 @@ -299,14 +299,16 @@ subroutine test_host(retval, test_suites) ! Check that is_scheme_constituent works as expected call test_host_ccpp_is_scheme_constituent('specific_humidity', & is_constituent, errflg, errmsg) - call check_errflg(subname//"_ccpp_is_scheme_constituent", errflg, errmsg) + call check_errflg(subname//"_ccpp_is_scheme_constituent", errflg, & + errmsg, errflg_final) ! specific_humidity should not be an existing constituent if (is_constituent) then write(6, *) "ERROR: specific humidity is already a constituent" end if call test_host_ccpp_is_scheme_constituent('cloud_ice_dry_mixing_ratio', & is_constituent, errflg, errmsg) - call check_errflg(subname//"_ccpp_is_scheme_constituent", errflg, errmsg) + call check_errflg(subname//"_ccpp_is_scheme_constituent", errflg, & + errmsg, errflg_final) ! cloud_ice_dry_mixing_ratio should be an existing constituent if (.not. is_constituent) then write(6, *) "ERROR: cloud_ice_dry_mixing ratio not found in ", & From 87f74cbebfeec4012ee763d42ffedd821648763f Mon Sep 17 00:00:00 2001 From: Courtney Peverley Date: Tue, 29 Aug 2023 10:58:44 -0600 Subject: [PATCH 4/5] address reviewer comments --- scripts/constituents.py | 6 +++--- scripts/host_cap.py | 14 +++++++------- test/advection_test/test_host.F90 | 10 +++++++--- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/scripts/constituents.py b/scripts/constituents.py index 9a7489a5..c89dc415 100644 --- a/scripts/constituents.py +++ b/scripts/constituents.py @@ -637,10 +637,10 @@ def write_host_routines(cap, host, reg_funcname, init_funcname, num_const_funcna cap.write("end {}".format(substmt), 1) # Write query_consts routine substmt = f"subroutine {query_const_funcname}" - cap.write("", 0) + blank_line() cap.write(f"{substmt}(var_name, constituent_exists, {err_dummy_str})", 1) cap.comment(f"Return constituent_exists = true iff var_name appears in {host.name}_model_const_stdnames", 2) - cap.write("", 0) + blank_line() cap.write("character(len=*), intent(in) :: var_name", 2) cap.write("logical, intent(out) :: constituent_exists", 2) for evar in err_vars: @@ -650,7 +650,7 @@ def write_host_routines(cap, host, reg_funcname, init_funcname, num_const_funcna cap.write(f"if (any({host.name}_model_const_stdnames == var_name)) then", 2) cap.write("constituent_exists = .true.", 3) cap.write("end if", 2) - cap.write("", 0) + blank_line() cap.write(f"end {substmt}", 1) # Write copy_in routine substmt = "subroutine {}".format(copy_in_funcname) diff --git a/scripts/host_cap.py b/scripts/host_cap.py index 35f7fae1..d2b3066f 100644 --- a/scripts/host_cap.py +++ b/scripts/host_cap.py @@ -115,7 +115,7 @@ def query_scheme_constituents_funcname(host_model): """Return the name of the function to return True if the standard name passed in matches an existing constituent Because this is a user interface API function, the name is fixed.""" - return "{}_ccpp_is_scheme_constituent".format(host_model.name) + return f"{host_model.name}_ccpp_is_scheme_constituent" ############################################################################### def constituent_copyin_subname(host_model): @@ -452,17 +452,17 @@ def write_host_cap(host_model, api, module_name, output_dir, run_env): API.declare_inspection_interfaces(cap) # Write the host-model interfaces for constituents reg_name = constituent_register_subname(host_model) - cap.write("public :: {}".format(reg_name), 1) + cap.write(f"public :: {reg_name}", 1) init_name = constituent_initialize_subname(host_model) - cap.write("public :: {}".format(init_name), 1) + cap.write(f"public :: {init_name}", 1) numconsts_name = constituent_num_consts_funcname(host_model) - cap.write("public :: {}".format(numconsts_name), 1) + cap.write(f"public :: {numconsts_name}", 1) queryconsts_name = query_scheme_constituents_funcname(host_model) - cap.write("public :: {}".format(queryconsts_name), 1) + cap.write(f"public :: {queryconsts_name}", 1) copyin_name = constituent_copyin_subname(host_model) - cap.write("public :: {}".format(copyin_name), 1) + cap.write(f"public :: {copyin_name}", 1) copyout_name = constituent_copyout_subname(host_model) - cap.write("public :: {}".format(copyout_name), 1) + cap.write(f"public :: {copyout_name}", 1) const_array_func = constituent_model_consts(host_model) cap.write(f"public :: {const_array_func}", 1) advect_array_func = constituent_model_advected_consts(host_model) diff --git a/test/advection_test/test_host.F90 b/test/advection_test/test_host.F90 index d28422c7..49a3c602 100644 --- a/test/advection_test/test_host.F90 +++ b/test/advection_test/test_host.F90 @@ -304,6 +304,7 @@ subroutine test_host(retval, test_suites) ! specific_humidity should not be an existing constituent if (is_constituent) then write(6, *) "ERROR: specific humidity is already a constituent" + errflg_final = -1 !Notify test script that a failure occurred end if call test_host_ccpp_is_scheme_constituent('cloud_ice_dry_mixing_ratio', & is_constituent, errflg, errmsg) @@ -313,6 +314,7 @@ subroutine test_host(retval, test_suites) if (.not. is_constituent) then write(6, *) "ERROR: cloud_ice_dry_mixing ratio not found in ", & "host cap constituent list" + errflg_final = -1 !Notify test script that a failure occurred end if @@ -512,7 +514,7 @@ subroutine test_host(retval, test_suites) !Check that setting a constituent's default value works as expected call const_props(index_liq)%has_default(has_default, errflg, errmsg) if (errflg /= 0) then - write(6, '(a,i0,a,i0,/,a)') "ERROR: Error, ", errflg, " trying ", & + write(6, '(a,i0,2a,i0,/,a)') "ERROR: Error, ", errflg, " trying ", & "to check for default for cld_liq index = ", index_liq, trim(errmsg) errflg_final = -1 !Notify test script that a failure occurred end if @@ -527,13 +529,14 @@ subroutine test_host(retval, test_suites) end if call const_props(index_ice)%has_default(has_default, errflg, errmsg) if (errflg /= 0) then - write(6, '(a,i0,a,i0,/,a)') "ERROR: Error, ", errflg, " trying ", & + write(6, '(a,i0,2a,i0,/,a)') "ERROR: Error, ", errflg, " trying ", & "to check for default for cld_ice index = ", index_ice, trim(errmsg) errflg_final = -1 !Notify test script that a failure occurred end if if (errflg == 0) then if (.not. has_default) then write(6, *) "ERROR: cloud ice mass_mixing_ratio should have default but doesn't" + errflg_final = -1 !Notify test script that a failure occurred end if else !Reset error flag to continue testing other properties: @@ -541,7 +544,7 @@ subroutine test_host(retval, test_suites) end if call const_props(index_ice)%default_value(default_value, errflg, errmsg) if (errflg /= 0) then - write(6, '(a,i0,a,i0,/,a)') "ERROR: Error, ", errflg, " trying ", & + write(6, '(a,i0,2a,i0,/,a)') "ERROR: Error, ", errflg, " trying ", & "to grab default for cld_ice index = ", index_ice, trim(errmsg) errflg_final = -1 !Notify test script that a failure occurred end if @@ -549,6 +552,7 @@ subroutine test_host(retval, test_suites) if (default_value /= 0.0_kind_phys) then write(6, *) "ERROR: cloud ice mass_mixing_ratio default is ", default_value, & " but should be 0.0" + errflg_final = -1 !Notify test script that a failure occurred end if else !Reset error flag to continue testing other properties: From 65824a5cab6a6f0c3d977d0bb086ae47bacb02fb Mon Sep 17 00:00:00 2001 From: Courtney Peverley Date: Tue, 29 Aug 2023 11:01:32 -0600 Subject: [PATCH 5/5] fix function calls --- scripts/constituents.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/constituents.py b/scripts/constituents.py index c89dc415..cd0fa2cd 100644 --- a/scripts/constituents.py +++ b/scripts/constituents.py @@ -637,10 +637,10 @@ def write_host_routines(cap, host, reg_funcname, init_funcname, num_const_funcna cap.write("end {}".format(substmt), 1) # Write query_consts routine substmt = f"subroutine {query_const_funcname}" - blank_line() + cap.blank_line() cap.write(f"{substmt}(var_name, constituent_exists, {err_dummy_str})", 1) cap.comment(f"Return constituent_exists = true iff var_name appears in {host.name}_model_const_stdnames", 2) - blank_line() + cap.blank_line() cap.write("character(len=*), intent(in) :: var_name", 2) cap.write("logical, intent(out) :: constituent_exists", 2) for evar in err_vars: @@ -650,7 +650,7 @@ def write_host_routines(cap, host, reg_funcname, init_funcname, num_const_funcna cap.write(f"if (any({host.name}_model_const_stdnames == var_name)) then", 2) cap.write("constituent_exists = .true.", 3) cap.write("end if", 2) - blank_line() + cap.blank_line() cap.write(f"end {substmt}", 1) # Write copy_in routine substmt = "subroutine {}".format(copy_in_funcname)