From d380d5ddf06f505a07417e4deca5164f19714d15 Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Thu, 26 Jun 2025 17:28:05 -0400 Subject: [PATCH 01/11] Allow reading fields with a constituent dimension --- src/data/write_init_files.py | 79 +++- src/physics/utils/physics_data.F90 | 195 +++++++++ .../datatable_constituent_dim.xml | 393 +++++++++++++++++ .../phys_vars_init_check_constituent_dim.F90 | 245 +++++++++++ .../physics_inputs_constituent_dim.F90 | 411 ++++++++++++++++++ .../physics_types_simple_constituent_dim.F90 | 120 +++++ .../physics_types_simple_constituent_dim.meta | 27 ++ .../phys_vars_init_check_constituent_dim.F90 | 245 +++++++++++ .../write_init_files/physics_inputs_4D.F90 | 7 +- .../write_init_files/physics_inputs_bvd.F90 | 7 +- .../write_init_files/physics_inputs_cnst.F90 | 7 +- .../physics_inputs_constituent_dim.F90 | 404 +++++++++++++++++ .../write_init_files/physics_inputs_ddt.F90 | 7 +- .../write_init_files/physics_inputs_ddt2.F90 | 7 +- .../physics_inputs_ddt_array.F90 | 7 +- .../physics_inputs_host_var.F90 | 7 +- .../physics_inputs_initial_value.F90 | 7 +- .../write_init_files/physics_inputs_mf.F90 | 7 +- .../physics_inputs_no_horiz.F90 | 7 +- .../write_init_files/physics_inputs_noreq.F90 | 7 +- .../write_init_files/physics_inputs_param.F90 | 7 +- .../physics_inputs_protect.F90 | 7 +- .../physics_inputs_scalar.F90 | 7 +- .../physics_inputs_simple.F90 | 7 +- .../write_init_files/simple_reg.xml | 2 +- .../simple_reg_constituent_dim.xml | 29 ++ .../temp_adjust_constituent_dim.F90 | 87 ++++ .../temp_adjust_constituent_dim.meta | 98 +++++ test/unit/python/test_write_init_files.py | 87 +++- 29 files changed, 2478 insertions(+), 49 deletions(-) create mode 100644 test/unit/python/sample_files/datatable_constituent_dim.xml create mode 100644 test/unit/python/sample_files/phys_vars_init_check_constituent_dim.F90 create mode 100644 test/unit/python/sample_files/physics_inputs_constituent_dim.F90 create mode 100644 test/unit/python/sample_files/physics_types_simple_constituent_dim.F90 create mode 100644 test/unit/python/sample_files/physics_types_simple_constituent_dim.meta create mode 100644 test/unit/python/sample_files/write_init_files/phys_vars_init_check_constituent_dim.F90 create mode 100644 test/unit/python/sample_files/write_init_files/physics_inputs_constituent_dim.F90 create mode 100644 test/unit/python/sample_files/write_init_files/simple_reg_constituent_dim.xml create mode 100644 test/unit/python/sample_files/write_init_files/temp_adjust_constituent_dim.F90 create mode 100644 test/unit/python/sample_files/write_init_files/temp_adjust_constituent_dim.meta diff --git a/src/data/write_init_files.py b/src/data/write_init_files.py index 314859d54..6029a2a15 100644 --- a/src/data/write_init_files.py +++ b/src/data/write_init_files.py @@ -739,12 +739,19 @@ def get_dimension_info(hvar): - The local variable name of the vertical dimension (or None) - True if has one dimension which is a horizontal dimension or if has two dimensions (horizontal and vertical) + - Flag if any dimensions are number_of_ccpp_constituents and needs + reading separate variables by constituent and reassembled into + host model indices. """ vdim_name = None legal_dims = False fail_reason = "" + has_constituent_read = False + dims = hvar.get_dimensions() levnm = hvar.has_vertical_dimension() + has_constituent_dim = any('number_of_ccpp_constituents' in dim for dim in dims) + # is only 'legal' for 2 or 3 dimensional fields (i.e., 1 or 2 # dimensional variables). The second dimension must be vertical. # XXgoldyXX: If we ever need to read scalars, it would have to be @@ -785,6 +792,19 @@ def get_dimension_info(hvar): # end if suff = "; " # end if + + # A special case where any dimensions include number_of_ccpp_constituents, + # in this case the variable needs to be suffixed by _ + constituent name + # and read and reassembled separately into host model constituent indices + # based on constituent name. + # + # In this case, override legal_dims as this case will be handled separately + if has_constituent_dim: + has_constituent_read = True + legal_dims = True + fail_reason = "" + # end if + if legal_dims and levnm: # should be legal, find the correct local name for the # vertical dimension @@ -808,7 +828,8 @@ def get_dimension_info(hvar): raise ValueError(f"Vertical dimension, '{levnm}', not found") # end if # end if - return vdim_name, legal_dims, fail_reason + + return vdim_name, legal_dims, fail_reason, has_constituent_read def write_phys_read_subroutine(outfile, host_dict, host_vars, host_imports, phys_check_fname_str, constituent_set, @@ -850,7 +871,7 @@ def write_phys_read_subroutine(outfile, host_dict, host_vars, host_imports, call_string_key = f"case ('{var_stdname}')" # Extract vertical level variable: - levnm, call_read_field, reason = get_dimension_info(hvar) + levnm, call_read_field, reason, has_constituent_read = get_dimension_info(hvar) if hvar.get_prop_value('protected'): call_read_field = False if reason: @@ -860,20 +881,39 @@ def write_phys_read_subroutine(outfile, host_dict, host_vars, host_imports, # end if lvar = hvar.get_prop_value('local_name') reason += f"{suff}{lvar} is a protected variable" + # end if + # Set "read_field" call string: if call_read_field: - # Replace vertical dimension with local name - call_str = "call read_field(file, " + \ - f"'{var_stdname}', input_var_names(:,name_idx), " - if levnm is not None: - call_str += f"'{levnm}', " - # end if - err_on_not_found_string = "" - if var_stdname in vars_init_value: - # if initial value is available, do not throw error when not found in initial condition file. - err_on_not_found_string = ", error_on_not_found=.false." + if has_constituent_read: + # Special case for constituent-dimension variables. + call_str = f"call read_constituent_dimensioned_field(const_props, file, '{var_stdname}', input_var_names(:,name_idx), " + if levnm is not None: + call_str += f"'{levnm}', " + # end if + + initial_value_string = "" + if var_stdname in vars_init_value: + # if initial value is available, pass it to the read routine + # so it can be used for this variable when data for some + # constituent cannot be found. + initial_value_string = f", initial_value={vars_init_value[var_stdname]}_kind_phys" + # end if + call_str += f"timestep, {var_locname}{initial_value_string})" + else: + # Replace vertical dimension with local name + call_str = "call read_field(file, " + \ + f"'{var_stdname}', input_var_names(:,name_idx), " + if levnm is not None: + call_str += f"'{levnm}', " + # end if + err_on_not_found_string = "" + if var_stdname in vars_init_value: + # if initial value is available, do not throw error when not found in initial condition file. + err_on_not_found_string = ", error_on_not_found=.false." + # end if + call_str += f"timestep, {var_locname}{err_on_not_found_string})" # end if - call_str += f"timestep, {var_locname}{err_on_not_found_string})" else: # if initial value is assigned, then it can be ignored if var_stdname in vars_init_value: @@ -903,7 +943,8 @@ def write_phys_read_subroutine(outfile, host_dict, host_vars, host_imports, ["shr_kind_mod", ["SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX"]], ["physics_data", ["read_field", "find_input_name_idx", "no_exist_idx", "init_mark_idx", - "prot_no_init_idx", "const_idx"]], + "prot_no_init_idx", "const_idx", + "read_constituent_dimensioned_field"]], ["cam_ccpp_cap", ["ccpp_physics_suite_variables", "cam_constituents_array", "cam_model_const_properties"]], @@ -967,8 +1008,13 @@ def write_phys_read_subroutine(outfile, host_dict, host_vars, host_imports, outfile.write("logical :: use_init_variables", 2) outfile.blank_line() + # Prepare constituent properties pointer for later usage: + outfile.comment("Get constituent properties pointer:", 2) + outfile.write("const_props => cam_model_const_properties()", 2) + outfile.blank_line() + # Initialize variables: - outfile.comment("Initalize missing and non-initialized variables strings:", + outfile.comment("Initialize missing and non-initialized variables strings:", 2) outfile.write("missing_required_vars = ' '", 2) outfile.write("protected_non_init_vars = ' '", 2) @@ -1103,7 +1149,6 @@ def write_phys_read_subroutine(outfile, host_dict, host_vars, host_imports, # Read in constituent data outfile.comment("Read in constituent variables if not using init variables", 2) outfile.write("field_data_ptr => cam_constituents_array()", 2) - outfile.write("const_props => cam_model_const_properties()", 2) outfile.blank_line() outfile.comment("Iterate over all registered constituents", 2) outfile.write("do constituent_idx = 1, size(const_props)", 2) @@ -1191,7 +1236,7 @@ def write_phys_check_subroutine(outfile, host_dict, host_vars, host_imports, call_string_key = f"case ('{var_stdname}')" # Extract vertical level variable: - levnm, call_check_field, reason = get_dimension_info(hvar) + levnm, call_check_field, reason, has_constituent_read = get_dimension_info(hvar) # Set "check_field" call string: if call_check_field: diff --git a/src/physics/utils/physics_data.F90 b/src/physics/utils/physics_data.F90 index 4699fc0df..97a4f7e40 100644 --- a/src/physics/utils/physics_data.F90 +++ b/src/physics/utils/physics_data.F90 @@ -26,6 +26,10 @@ module physics_data module procedure check_field_3d end interface check_field + interface read_constituent_dimensioned_field + module procedure read_constituent_dimensioned_field_2d + end interface read_constituent_dimensioned_field + !============================================================================== CONTAINS !============================================================================== @@ -325,6 +329,197 @@ subroutine read_field_3d(file, std_name, var_names, vcoord_name, & end if end subroutine read_field_3d + subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, base_var_names, timestep, field_array, initial_value) + use shr_assert_mod, only: shr_assert_in_domain + use shr_sys_mod, only: shr_sys_flush + use pio, only: file_desc_t, var_desc_t + use spmd_utils, only: masterproc + use cam_pio_utils, only: cam_pio_find_var + use cam_abortutils, only: endrun + use cam_logfile, only: iulog + use cam_field_read, only: cam_read_field + use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t + use phys_vars_init_check, only: mark_as_read_from_file + use phys_vars_init_check, only: phys_var_stdnames, input_var_names, phys_var_num + + ! Dummy arguments + type(ccpp_constituent_prop_ptr_t), intent(in) :: const_props(:) ! Constituent properties + type(file_desc_t), intent(inout) :: file + character(len=*), intent(in) :: std_name ! Standard name of base variable. + character(len=*), intent(in) :: base_var_names(:) ! "Base" name(s) used to construct variable name (base_constname) + integer, intent(in) :: timestep ! Timestep to read [count] + real(kind_phys), intent(inout) :: field_array(:,:) ! Output field array (ncol, pcnst) + real(kind_phys), optional, intent(in) :: initial_value ! Default value if not found + + ! Local variables + logical :: var_found + character(len=128) :: constituent_name + character(len=256) :: file_var_name + character(len=256) :: found_name + character(len=512) :: missing_vars + type(var_desc_t) :: vardesc + real(kind_phys), allocatable :: buffer(:) + integer :: const_idx, base_idx + integer :: ierr + real(kind_phys) :: default_value + logical :: has_initial_value + logical :: any_missing + + ! For construction of constituent short name mapping + character(len=128), allocatable :: constituent_short_names(:) + character(len=128) :: constituent_std_name + integer :: n + integer :: const_input_idx + + character(len=*), parameter :: subname = 'read_constituent_dimensioned_field: ' + + ! Check if initial value was provided + has_initial_value = present(initial_value) + + ! Initialize tracking variables + any_missing = .false. + missing_vars = '' + + ! Allocate temporary buffer + allocate(buffer(size(field_array, 1)), stat=ierr) + if (ierr /= 0) then + call endrun(subname//'Failed to allocate buffer') + end if + + !REMOVECAM: + ! Because the constituent properties pointer contains standard names, and not input constituent names + ! (e.g., Q, CLDLIQ, ...) which are used in the input file names, + ! we have to construct a mapping of the standard names to the short input IC file names + ! When CAM is retired and only standard names are used for constituents, this mapping can be removed. + allocate(constituent_short_names(size(const_props)), stat=ierr) + if (ierr /= 0) then + call endrun(subname//'Failed to allocate constituent_short_names') + end if + + const_shortmap_loop: do const_idx = 1, size(const_props) + ! Get constituent standard name. + call const_props(const_idx)%standard_name(constituent_std_name) + + ! Check if constituent standard name is in the registry to look up its IC name + ! n.b. this assumes that the first IC name is the short name + const_input_idx = -1 + phys_inputvar_loop: do n = 1, phys_var_num + if (trim(phys_var_stdnames(n)) == trim(constituent_std_name)) then + const_input_idx = n + exit phys_inputvar_loop + end if + end do + + if (const_input_idx > 0) then + ! Use the first entry from the input_var_names -- assumed to be short name. + constituent_short_names(const_idx) = trim(input_var_names(1, const_input_idx)) + else + ! Use the standard name itself if not found in registry. + constituent_short_names(const_idx) = trim(constituent_std_name) + end if + end do const_shortmap_loop + !END REMOVECAM + + ! Initialize field array to default value (only if initial_value provided) + if (has_initial_value) then + field_array(:,:) = initial_value + end if + + ! Loop through all possible base names to find correct base name. + ! Note this assumes that the same base name is used for all constituents. + ! i.e., there cannot be something like cam_in_cflx_Q & cflx_CLDLIQ in one file. + base_idx_loop: do base_idx = 1, size(base_var_names) + ! Loop through all constituents + const_idx_loop: do const_idx = 1, size(const_props) + ! Get constituent short name + constituent_name = constituent_short_names(const_idx) + + ! Create file variable name: _ + file_var_name = trim(base_var_names(base_idx)) // '_' // trim(constituent_name) + + ! Try to find variable in file + var_found = .false. + call cam_pio_find_var(file, [file_var_name], found_name, vardesc, var_found) + + if(var_found) then + exit base_idx_loop + endif + end do const_idx_loop + end do base_idx_loop + + ! Once base_idx is identified, use it in the actual constituent loop: + const_read_loop: do const_idx = 1, size(const_props) + ! Get constituent short name + constituent_name = constituent_short_names(const_idx) + + ! Create file variable name: _ + file_var_name = trim(base_var_names(base_idx)) // '_' // trim(constituent_name) + + ! Try to find variable in file + var_found = .false. + call cam_pio_find_var(file, [file_var_name], found_name, vardesc, var_found) + + if (var_found) then + ! Read the variable + if (masterproc) then + write(iulog, *) 'Reading constituent-dimensioned input field, ', trim(found_name) + call shr_sys_flush(iulog) + end if + + call cam_read_field(found_name, file, buffer, var_found, timelevel=timestep) + + if (var_found) then + ! Copy to correct constituent index in field array + field_array(:, const_idx) = buffer(:) + + ! Check for NaN values + call shr_assert_in_domain(field_array(:, const_idx), is_nan=.false., & + varname=trim(found_name), & + msg=subname//'NaN found in '//trim(found_name)) + else + ! Failed to read even though variable was found + any_missing = .true. + if (len_trim(missing_vars) > 0) then + missing_vars = trim(missing_vars) // ', ' // trim(file_var_name) + else + missing_vars = trim(file_var_name) + end if + end if + else + ! Variable not found in file + any_missing = .true. + if (len_trim(missing_vars) > 0) then + missing_vars = trim(missing_vars) // ', ' // trim(file_var_name) + else + missing_vars = trim(file_var_name) + end if + + if (has_initial_value) then + ! Use default value (already set above) + + if (masterproc) then + write(iulog, *) 'Constituent-dimensioned field ', trim(file_var_name), & + ' not found, using default value for constituent ', trim(constituent_name) + call shr_sys_flush(iulog) + end if + end if + end if + end do const_read_loop + + ! Check if we should fail due to missing variables + if (any_missing .and. .not. has_initial_value) then + call endrun(subname//'Required constituent-dimensioned variables not found: ' // trim(missing_vars)) + end if + + ! Mark the base variable as read from file (only if no errors) + call mark_as_read_from_file(std_name) + + ! Clean up + deallocate(constituent_short_names) + deallocate(buffer) + + end subroutine read_constituent_dimensioned_field_2d + subroutine check_field_2d(file, var_names, timestep, current_value, & stdname, min_difference, min_relative_value, is_first, diff_found) use pio, only: file_desc_t, var_desc_t diff --git a/test/unit/python/sample_files/datatable_constituent_dim.xml b/test/unit/python/sample_files/datatable_constituent_dim.xml new file mode 100644 index 000000000..287922248 --- /dev/null +++ b/test/unit/python/sample_files/datatable_constituent_dim.xml @@ -0,0 +1,393 @@ + + + + /Users/hplin/devel/CAM-SIMA/test/unit/python/tmp/write_init_files/ccpp_kinds.F90 + /Users/hplin/devel/CAM-SIMA/ccpp_framework/src/ccpp_constituent_prop_mod.F90 + /Users/hplin/devel/CAM-SIMA/ccpp_framework/src/ccpp_scheme_utils.F90 + /Users/hplin/devel/CAM-SIMA/ccpp_framework/src/ccpp_hashable.F90 + /Users/hplin/devel/CAM-SIMA/ccpp_framework/src/ccpp_hash_table.F90 + + + /Users/hplin/devel/CAM-SIMA/test/unit/python/tmp/write_init_files/cam_ccpp_cap.F90 + + + /Users/hplin/devel/CAM-SIMA/test/unit/python/tmp/write_init_files/ccpp_simple_suite_cap.F90 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cam_ddt_vars ccpp_physics_api cam_constituents + + + host + simple_sub + + + host + simple_sub + + + host + simple_sub + + + host + simple_sub + + + host + simple_sub + + + host + simple_sub + + + host + simple_sub + + + ccpp_constant_one:horizontal_dimension ccpp_constant_one:vertical_layer_dimension + module + physics_types_simple_constituent_dim + + + ccpp_constant_one:horizontal_dimension + module + physics_types_simple_constituent_dim + + + ccpp_constant_one:horizontal_dimension + module + physics_types_simple_constituent_dim + + + ccpp_constant_one:horizontal_dimension ccpp_constant_one:number_of_ccpp_constituents + module + physics_types_simple_constituent_dim + + + module + cam_ccpp_cap + + + + + suite_simple_constituents + + + + simple_suite_register simple_suite_initialize simple_suite_finalize simple_suite_timestep_initial simple_suite_timestep_final simple_suite_physics + + + + + + + + + group + simple_suite_register + + + group + simple_suite_register + + + + + temp_adjust + + + + + + group + simple_suite_initialize + + + group + simple_suite_initialize + + + + + + + scheme + temp_adjust_init + + + scheme + temp_adjust_init + + + + + + + scheme + temp_adjust_init + + + scheme + temp_adjust_init + + + + + + + + + + group + simple_suite_timestep_initial + + + group + simple_suite_timestep_initial + + + + + TimeSplit + + + local + ccpp_api + + + + + + + group + simple_suite_physics + + + group + simple_suite_physics + + + group + simple_suite_physics + + + group + simple_suite_physics + + + group + simple_suite_physics + + + horizontal_loop_begin:horizontal_loop_end ccpp_constant_one:vertical_layer_dimension + group + simple_suite_physics + + + horizontal_loop_begin:horizontal_loop_end + group + simple_suite_physics + + + horizontal_loop_begin:horizontal_loop_end ccpp_constant_one:number_of_ccpp_constituents + group + simple_suite_physics + + + group + simple_suite_physics + + + + + temp_adjust + + + + + + scheme + temp_adjust_run + + + scheme + temp_adjust_run + + + horizontal_loop_begin:horizontal_loop_end ccpp_constant_one:vertical_layer_dimension + scheme + temp_adjust_run + + + horizontal_loop_begin:horizontal_loop_end + scheme + temp_adjust_run + + + horizontal_loop_begin:horizontal_loop_end ccpp_constant_one:number_of_ccpp_constituents + scheme + temp_adjust_run + + + scheme + temp_adjust_run + + + scheme + temp_adjust_run + + + scheme + temp_adjust_run + + + + + + + scheme + temp_adjust_run + + + scheme + temp_adjust_run + + + horizontal_loop_begin:horizontal_loop_end ccpp_constant_one:vertical_layer_dimension + scheme + temp_adjust_run + + + horizontal_loop_begin:horizontal_loop_end + scheme + temp_adjust_run + + + horizontal_loop_begin:horizontal_loop_end ccpp_constant_one:number_of_ccpp_constituents + scheme + temp_adjust_run + + + scheme + temp_adjust_run + + + scheme + temp_adjust_run + + + scheme + temp_adjust_run + + + + + + + + + + group + simple_suite_timestep_final + + + group + simple_suite_timestep_final + + + + + temp_adjust + + + + + + group + simple_suite_finalize + + + group + simple_suite_finalize + + + + + + + scheme + temp_adjust_finalize + + + scheme + temp_adjust_finalize + + + + + + + scheme + temp_adjust_finalize + + + scheme + temp_adjust_finalize + + + + + + diff --git a/test/unit/python/sample_files/phys_vars_init_check_constituent_dim.F90 b/test/unit/python/sample_files/phys_vars_init_check_constituent_dim.F90 new file mode 100644 index 000000000..5a5025baa --- /dev/null +++ b/test/unit/python/sample_files/phys_vars_init_check_constituent_dim.F90 @@ -0,0 +1,245 @@ +! +! This work (Common Community Physics Package Framework), identified by +! NOAA, NCAR, CU/CIRES, is free of known copyright restrictions and is +! placed in the public domain. +! +! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +! THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +! IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +! CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +!> +!! @brief Auto-generated Initialization-checking source file +!! +! +module phys_vars_init_check_constituent_dim + + + implicit none + private + +!! public interfaces + public :: mark_as_initialized + public :: mark_as_read_from_file + public :: is_initialized + public :: is_read_from_file + +!! Parameterized initialized_vars options - order matters + integer, public, parameter :: UNINITIALIZED = 0 + integer, public, parameter :: INITIALIZED = 1 + integer, public, parameter :: PARAM = 2 + integer, public, parameter :: READ_FROM_FILE = 3 + ! Total number of physics-related variables: + integer, public, parameter :: phys_var_num = 3 + integer, public, parameter :: phys_const_num = 16 + + !Max length of physics-related variable standard names: + integer, public, parameter :: std_name_len = 26 + + ! Max length of input (IC) file variable names: + integer, public, parameter :: ic_name_len = 13 + + ! Physics-related input variable standard names: + character(len=26), public, protected :: phys_var_stdnames(phys_var_num) = (/ & + 'potential_temperature ', & + 'air_pressure_at_sea_level ', & + 'super_cool_cat_every_const' /) + + character(len=36), public, protected :: phys_const_stdnames(phys_const_num) = (/ & + "ccpp_constituent_minimum_values ", & + "ccpp_constituent_properties ", & + "ccpp_constituent_tendencies ", & + "ccpp_constituents ", & + "ccpp_error_code ", & + "ccpp_error_message ", & + "do_log_output ", & + "log_output_unit ", & + "mpi_communicator ", & + "mpi_rank ", & + "mpi_root ", & + "number_of_ccpp_advected_constituents", & + "number_of_ccpp_constituents ", & + "number_of_mpi_tasks ", & + "suite_name ", & + "suite_part " /) + !Array storing all registered IC file input names for each variable: + character(len=13), public, protected :: input_var_names(2, phys_var_num) = reshape((/ & + 'theta ', 'pot_temp ', & + 'slp ', 'sea_lev_pres ', & + 'cool_cat_tend', ' ' /), (/2, phys_var_num/)) + + ! Array indicating whether or not variable is protected: + logical, public, protected :: protected_vars(phys_var_num)= (/ & + .false., & + .false., & + .false. /) + + ! Variable state (UNINITIALIZED, INTIIALIZED, PARAM or READ_FROM_FILE): + integer, public, protected :: initialized_vars(phys_var_num)= (/ & + UNINITIALIZED, & + UNINITIALIZED, & + UNINITIALIZED /) + + +CONTAINS + + subroutine mark_as_initialized(varname) + + ! This subroutine marks the variable, , as + ! INITIALIZED in the `initialized_vars` array, + ! which means any initialization check should + ! now return True. + + ! Dummy argument + character(len=*), intent(in) :: varname !Variable name being marked + + ! Local variable + integer :: stdnam_idx !Standard name array index + + ! Search for in the standard name array: + do stdnam_idx = 1, phys_var_num + if (trim(phys_var_stdnames(stdnam_idx)) == trim(varname)) then + ! Only set to INITIALIZED if state is UNINITIALIZED + if (initialized_vars(stdnam_idx) < PARAM) then + initialized_vars(stdnam_idx) = INITIALIZED + end if + exit ! Exit loop once variable has been found and initialized + end if + end do + + ! No match is not an error because only + ! contains variables required by a physics suite. + + end subroutine mark_as_initialized + + subroutine mark_as_read_from_file(varname) + + ! This subroutine marks the varible, , as READ_FROM_FILE in the + ! initialized_vars array + + use cam_abortutils, only: endrun + + ! Dummy argument + character(len=*), intent(in) :: varname ! Variable name being marked + + ! Local variables + integer :: stdnam_idx ! Standard name array index + logical :: found_var ! .true. if is in arr. + character(len=*), parameter :: subname = 'mark_as_read_from_file' + + found_var = .false. + ! Set variable to READ_FROM_FILE: + do stdnam_idx = 1, phys_var_num + if (trim(phys_var_stdnames(stdnam_idx)) == trim(varname)) then + ! It is an error if the variable has already been set to PARAM + if (initialized_vars(stdnam_idx) == PARAM) then + call endrun("Variable '"//trim(varname)// & + "' was read from file, but is a parameter") + end if + initialized_vars(stdnam_idx) = READ_FROM_FILE + + ! Indicate variable has been found: + found_var = .true. + exit ! Exit loop once variable has been found and marked + end if + end do + + if (.not. found_var) then + ! This condition is an internal error, it should not happen + call endrun(subname//": Variable '"//trim(varname)// & + "' is missing from phys_var_stdnames array.") + end if + + end subroutine mark_as_read_from_file + + logical function is_initialized(varname) + + ! This function checks if the variable, , is already + ! initialized according to the 'initialized_vars' array. + + use cam_abortutils, only: endrun + + ! Dummy argument + character(len=*), intent(in) :: varname ! Variable name being checked + + ! Local variables + integer :: stdnam_idx ! Standard name array index + logical :: found ! Check that was found + character(len=*), parameter :: subname = 'is_initialized: ' + + is_initialized = .false. + found = .false. + + ! Check if variable is initialized (PARAM, INITIALIZED, or READ_FROM_FILE) + do stdnam_idx = 1, phys_var_num + if (trim(phys_var_stdnames(stdnam_idx)) == trim(varname)) then + is_initialized = (initialized_vars(stdnam_idx) > UNINITIALIZED) + found = .true. + exit ! Exit loop once variable has been found and checked + end if + end do + + if (.not. found) then + ! This condition is an internal error, it should not happen + call endrun(subname//": Variable '"//trim(varname)// & + "' is missing from phys_var_stdnames array.") + end if + + end function is_initialized + + subroutine is_read_from_file(varname, is_read, stdnam_idx_out) + + ! This subroutine checks if the variable, , is read from + ! file according to the 'initialized_vars' array. + + use cam_abortutils, only: endrun + + ! Dummy arguments + character(len=*), intent(in) :: varname ! Variable name being checked + logical, intent(out) :: is_read ! Set to .true. if from file + integer, optional, intent(out) :: stdnam_idx_out + + ! Local variables + + integer :: stdnam_idx ! Standard name array index + logical :: found ! Check that was found + character(len=*), parameter :: subname = 'is_read_from_file: ' + + is_read = .false. + found = .false. + + ! Return .true. if the variable's status is READ_FROM_FILE: + do stdnam_idx = 1, phys_var_num + if (trim(phys_var_stdnames(stdnam_idx)) == trim(varname)) then + is_read = (initialized_vars(stdnam_idx) == READ_FROM_FILE) + ! Mark as found: + found = .true. + exit ! Exit loop once variable has been found and checked + end if + end do + + if (.not. found) then + ! Check to see if this is an internally-protected variable + do stdnam_idx = 1, phys_const_num + if (trim(phys_const_stdnames(stdnam_idx)) == trim(varname)) then + found = .true. + exit ! Exit loop once variable has been found + end if + end do + end if + + if (.not. found) then + ! This condition is an internal error, it should not happen + call endrun(subname//": Variable '"//trim(varname)// & + "' is missing from phys_var_stdnames array.") + end if + if (present(stdnam_idx_out)) then + stdnam_idx_out = stdnam_idx + end if + + end subroutine is_read_from_file + +end module phys_vars_init_check_constituent_dim diff --git a/test/unit/python/sample_files/physics_inputs_constituent_dim.F90 b/test/unit/python/sample_files/physics_inputs_constituent_dim.F90 new file mode 100644 index 000000000..80e44a8ef --- /dev/null +++ b/test/unit/python/sample_files/physics_inputs_constituent_dim.F90 @@ -0,0 +1,411 @@ +! +! This work (Common Community Physics Package Framework), identified by +! NOAA, NCAR, CU/CIRES, is free of known copyright restrictions and is +! placed in the public domain. +! +! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +! THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +! IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +! CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +!> +!! @brief Auto-generated Initial conditions source file, physics_inputs_constituent_dim.F90 +!! +! +module physics_inputs_constituent_dim + + + implicit none + private + + +!! public interfaces + public :: physics_read_data + public :: physics_check_data + +CONTAINS + + subroutine physics_read_data(file, suite_names, timestep, read_initialized_variables) + use pio, only: file_desc_t + use cam_abortutils, only: endrun + use spmd_utils, only: masterproc + use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX + use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use physics_data, only: read_constituent_dimensioned_field + use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties + use ccpp_kinds, only: kind_phys + use phys_vars_init_check_constituent_dim, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized + use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t + use cam_logfile, only: iulog + use physics_types_simple_constituent_dim, only: cool_cat_for_each_const, slp, theta + + ! Dummy arguments + type(file_desc_t), intent(inout) :: file + character(len=SHR_KIND_CS), intent(in) :: suite_names(:) !Names of CCPP suites + integer, intent(in) :: timestep + logical, optional, intent(in) :: read_initialized_variables + + ! Local variables: + + ! Character array containing all CCPP-required variable standard names: + character(len=std_name_len), allocatable :: ccpp_required_data(:) + + ! Strings which store names of any missing or non-initialized vars: + character(len=SHR_KIND_CL) :: missing_required_vars + character(len=SHR_KIND_CL) :: protected_non_init_vars + + character(len=SHR_KIND_CX) :: errmsg !CCPP framework error message + integer :: errflg !CCPP framework error flag + integer :: n !Loop control variable + integer :: name_idx !Input variable array index + integer :: constituent_idx !Constituent table index + integer :: const_input_idx !input_var_names index for a consituent + integer :: req_idx !Required variable array index + integer :: suite_idx !Suite array index + character(len=2) :: sep !String separator used to print err messages + character(len=2) :: sep2 !String separator used to print err messages + character(len=2) :: sep3 !String separator used to print err messages + real(kind=kind_phys), pointer :: field_data_ptr(:,:,:) + logical :: var_found !Bool to determine if consituent found in data files + character(len=std_name_len) :: std_name !Variable to hold constiutent standard name + + ! Fields needed for getting default data value for constituents + type(ccpp_constituent_prop_ptr_t), pointer :: const_props(:) + real(kind=kind_phys) :: constituent_default_value + real(kind=kind_phys) :: constituent_min_value + integer :: constituent_errflg + character(len=512) :: constituent_errmsg + logical :: constituent_has_default + + ! Logical to default optional argument to False: + logical :: use_init_variables + + ! Initalize missing and non-initialized variables strings: + missing_required_vars = ' ' + protected_non_init_vars = ' ' + sep = '' + sep2 = '' + sep3 = '' + + ! Initialize use_init_variables based on whether it was input to function: + if (present(read_initialized_variables)) then + use_init_variables = read_initialized_variables + else + use_init_variables = .false. + end if + + ! Loop over CCPP physics/chemistry suites: + do suite_idx = 1, size(suite_names, 1) + + ! Search for all needed CCPP input variables, so that they can be read from input file if need be: + call ccpp_physics_suite_variables(suite_names(suite_idx), ccpp_required_data, errmsg, errflg, input_vars=.true., output_vars=.false.) + + ! Loop over all required variables and read from file if uninitialized: + do req_idx = 1, size(ccpp_required_data, 1) + + ! Find IC file input name array index for required variable: + name_idx = find_input_name_idx(ccpp_required_data(req_idx), use_init_variables, constituent_idx) + + ! Check for special index values: + select case (name_idx) + + case (init_mark_idx) + + ! If variable is already initialized, then do nothing. + + case (no_exist_idx) + + ! If an index was never found, then save variable name and check the rest of the variables, after which the model simulation will + ! end: + missing_required_vars(len_trim(missing_required_vars)+1:) = trim(sep)//trim(ccpp_required_data(req_idx)) + + ! Update character separator to now include comma: + sep = ', ' + + case (prot_no_init_idx) + + ! If an index was found for a protected variable, but that variable was never marked as initialized, then save the variable name + ! and check the rest of the variables, after which the model simulation will end: + protected_non_init_vars(len_trim(protected_non_init_vars)+1:) = trim(sep2)//trim(ccpp_required_data(req_idx)) + + ! Update character separator to now include comma: + sep2 = ', ' + + case (const_idx) + + ! If an index was found in the constituent hash table, then do nothing, this will be handled later + + case default + + ! Read variable from IC file: + + select case (trim(phys_var_stdnames(name_idx))) + case ('potential_temperature') + call read_field(file, 'potential_temperature', input_var_names(:,name_idx), 'lev', timestep, theta) + + case ('air_pressure_at_sea_level') + call read_field(file, 'air_pressure_at_sea_level', input_var_names(:,name_idx), timestep, slp) + + end select !read variables + end select !special indices + + end do !Suite-required variables + + ! End simulation if there are missing input variables that are required: + if (len_trim(missing_required_vars) > 0) then + call endrun("Required variables missing from registered list of input variables: "//& + trim(missing_required_vars)) + end if + + ! End simulation if there are protected input variables that are not initialized: + if (len_trim(protected_non_init_vars) > 0) then + call endrun("Required, protected input variables are not initialized: "//& + trim(protected_non_init_vars)) + end if + + ! Deallocate required variables array for use in next suite: + deallocate(ccpp_required_data) + + end do !CCPP suites + + ! Read in constituent variables if not using init variables + field_data_ptr => cam_constituents_array() + const_props => cam_model_const_properties() + + ! Iterate over all registered constituents + do constituent_idx = 1, size(const_props) + var_found = .false. + ! Check if constituent standard name in registered SIMA standard names list: + call const_props(constituent_idx)%standard_name(std_name) + if(any(phys_var_stdnames == trim(std_name))) then + ! Don't read the variable in if it's already initialized + if (is_initialized(std_name)) then + cycle + end if + ! Find array index to extract correct input names: + do n=1, phys_var_num + if(trim(phys_var_stdnames(n)) == trim(std_name)) then + const_input_idx = n + exit + end if + end do + call read_field(file, std_name, input_var_names(:,const_input_idx), 'lev', timestep, field_data_ptr(:,:,constituent_idx), & + mark_as_read=.false., error_on_not_found=.false., var_found=var_found) + else + ! If not in standard names list, then just use constituent name as input file name: + call read_field(file, std_name, [std_name], 'lev', timestep, field_data_ptr(:,:,constituent_idx), mark_as_read=.false., & + error_on_not_found=.false., var_found=var_found) + end if + if(.not. var_found) then + constituent_has_default = .false. + call const_props(constituent_idx)%has_default(constituent_has_default, constituent_errflg, constituent_errmsg) + if (constituent_errflg /= 0) then + call endrun(constituent_errmsg, file=__FILE__, line=__LINE__) + end if + if (.not. constituent_has_default) then + ! Intialize to constituent's configured minimum value + call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) + field_data_ptr(:,:,constituent_idx) = constituent_min_value + if (masterproc) then + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + end if + end if + end if + end do + + ! Read in non-constituent variables with constituent dimensions + ! Handle variables like cam_in_cflx that are dimensioned by constituents + ! but are not constituents themselves + do req_idx = 1, size(ccpp_required_data, 1) + name_idx = find_input_name_idx(ccpp_required_data(req_idx), use_init_variables, constituent_idx) + if (name_idx > 0 .and. name_idx <= phys_var_num) then + select case (trim(phys_var_stdnames(name_idx))) + call read_constituent_dimensioned_field(file, 'super_cool_cat_every_const', + timestep, cool_cat_for_each_const, const_props) + + end select + end if + end do + end subroutine physics_read_data + + subroutine physics_check_data(file_name, suite_names, timestep, min_difference, min_relative_value, err_on_fail) + use pio, only: file_desc_t, pio_nowrite + use cam_abortutils, only: endrun + use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX + use physics_data, only: check_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_advected_constituents_array, cam_model_const_properties + use cam_constituents, only: const_get_index + use ccpp_kinds, only: kind_phys + use cam_logfile, only: iulog + use spmd_utils, only: masterproc + use phys_vars_init_check, only: is_read_from_file + use ioFileMod, only: cam_get_file + use cam_pio_utils, only: cam_pio_openfile, cam_pio_closefile + use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t + use phys_vars_init_check_constituent_dim, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len + use physics_types_simple_constituent_dim, only: cool_cat_for_each_const, slp, theta + + ! Dummy arguments + character(len=SHR_KIND_CL), intent(in) :: file_name + character(len=SHR_KIND_CS), intent(in) :: suite_names(:) !Names of CCPP suites + integer, intent(in) :: timestep + real(kind_phys), intent(in) :: min_difference + real(kind_phys), intent(in) :: min_relative_value + logical, intent(in) :: err_on_fail + + ! Local variables: + + ! Character array containing all CCPP-required variable standard names: + character(len=std_name_len), allocatable :: ccpp_required_data(:) + + ! Strings which store names of any missing or non-initialized vars: + character(len=SHR_KIND_CL) :: missing_required_vars + character(len=SHR_KIND_CL) :: protected_non_init_vars + character(len=SHR_KIND_CL) :: missing_input_names + + character(len=SHR_KIND_CX) :: errmsg !CCPP framework error message + integer :: errflg !CCPP framework error flag + integer :: n !Loop control variable + integer :: name_idx !Input variable array index + integer :: constituent_idx !Index of variable in constituent array + integer :: const_input_idx !input_var_names index for a consituent + integer :: req_idx !Required variable array index + integer :: suite_idx !Suite array index + character(len=SHR_KIND_CL) :: ncdata_check_loc + type(file_desc_t), pointer :: file + logical :: file_found + logical :: is_first + logical :: is_read + logical :: diff_found + logical :: overall_diff_found + character(len=std_name_len) :: std_name !Variable to hold constiutent standard name + real(kind=kind_phys), pointer :: field_data_ptr(:,:,:) + type(ccpp_constituent_prop_ptr_t), pointer :: const_props(:) + + ! Initalize missing and non-initialized variables strings: + missing_required_vars = ' ' + protected_non_init_vars = ' ' + missing_input_names = ' ' + nullify(file) + is_first = .true. + overall_diff_found = .false. + + if (masterproc) then + write(iulog,*) '' + write(iulog,*) '********** Physics Check Data Results **********' + write(iulog,*) '' + write(iulog,*) 'TIMESTEP: ', timestep + end if + if (file_name == 'UNSET') then + write(iulog,*) 'WARNING: Namelist variable ncdata_check is UNSET.', ' Model will run, but physics check data will not be printed' + return + end if + ! Open check file: + call cam_get_file(file_name, ncdata_check_loc, allow_fail=.true., lexist=file_found, log_info=.false.) + if (.not. file_found) then + write(iulog,*) 'WARNING: Check file ', trim(file_name), ' not found. Model will run, but physics check data will not be printed' + return + end if + allocate(file) + call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + ! Loop over CCPP physics/chemistry suites: + do suite_idx = 1, size(suite_names, 1) + + ! Search for all needed CCPP input variables, so that they can be read from input file if need be: + call ccpp_physics_suite_variables(suite_names(suite_idx), ccpp_required_data, errmsg, errflg, input_vars=.true., output_vars=.false.) + + ! Loop over all required variables as specified by CCPP suite: + do req_idx = 1, size(ccpp_required_data, 1) + + ! First check if the required variable is a constituent: + call const_get_index(ccpp_required_data(req_idx), constituent_idx, abort=.false., warning=.false.) + if (constituent_idx > -1) then + cycle + else + ! The required variable is not a constituent. Check if the variable was read from a file + ! Find IC file input name array index for required variable: + call is_read_from_file(ccpp_required_data(req_idx), is_read, stdnam_idx_out=name_idx) + if (.not. is_read) then + cycle + end if + ! Check variable vs input check file: + + select case (trim(phys_var_stdnames(name_idx))) + case ('potential_temperature') + call check_field(file, input_var_names(:,name_idx), 'lev', timestep, theta, 'potential_temperature', min_difference, & + min_relative_value, is_first, diff_found) + + case ('air_pressure_at_sea_level') + call check_field(file, input_var_names(:,name_idx), timestep, slp, 'air_pressure_at_sea_level', min_difference, min_relative_value, & + is_first, diff_found) + + case ('super_cool_cat_every_const') + call check_field(file, input_var_names(:,name_idx), timestep, cool_cat_for_each_const, 'super_cool_cat_every_const', & + min_difference, min_relative_value, is_first, diff_found) + + end select !check variables + if (diff_found) then + overall_diff_found = .true. + end if + end if !check if constituent + end do !Suite-required variables + + ! Deallocate required variables array for use in next suite: + deallocate(ccpp_required_data) + + end do !CCPP suites + + ! Check constituent variables + field_data_ptr => cam_advected_constituents_array() + const_props => cam_model_const_properties() + + do constituent_idx = 1, size(const_props) + ! Check if constituent standard name in registered SIMA standard names list: + call const_props(constituent_idx)%standard_name(std_name) + if(any(phys_var_stdnames == std_name)) then + ! Find array index to extract correct input names: + do n=1, phys_var_num + if(trim(phys_var_stdnames(n)) == trim(std_name)) then + const_input_idx = n + exit + end if + end do + call check_field(file, input_var_names(:,const_input_idx), 'lev', timestep, field_data_ptr(:,:,constituent_idx), std_name, & + min_difference, min_relative_value, is_first, diff_found) + if (diff_found) then + overall_diff_found = .true. + end if + else + ! If not in standard names list, then just use constituent name as input file name: + call check_field(file, [std_name], 'lev', timestep, field_data_ptr(:,:,constituent_idx), std_name, min_difference, min_relative_value, & + is_first, diff_found) + if (diff_found) then + overall_diff_found = .true. + end if + end if + end do + ! Close check file: + call cam_pio_closefile(file) + deallocate(file) + nullify(file) + if (is_first) then + if (masterproc) then + write(iulog,*) '' + write(iulog,*) 'No differences found!' + end if + end if + if (masterproc) then + write(iulog,*) '' + write(iulog,*) '********** End Physics Check Data Results **********' + write(iulog,*) '' + end if + ! Endrun if differences were found on this timestep and err_on_fail=TRUE + if (overall_diff_found .and. err_on_fail .and. masterproc) then + call endrun('ERROR: Difference(s) found during ncdata check', file=__FILE__, line=__LINE__) + end if + end subroutine physics_check_data + +end module physics_inputs_constituent_dim diff --git a/test/unit/python/sample_files/physics_types_simple_constituent_dim.F90 b/test/unit/python/sample_files/physics_types_simple_constituent_dim.F90 new file mode 100644 index 000000000..da9ce3e9a --- /dev/null +++ b/test/unit/python/sample_files/physics_types_simple_constituent_dim.F90 @@ -0,0 +1,120 @@ +! +! This work (Common Community Physics Package Framework), identified by +! NOAA, NCAR, CU/CIRES, is free of known copyright restrictions and is +! placed in the public domain. +! +! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +! THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +! IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +! CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +!> +!! @brief Auto-generated Variables for registry source file, physics_types_simple_constituent_dim +!! +! +module physics_types_simple_constituent_dim + + use ccpp_kinds, only: kind_phys + + + implicit none + private + +!> \section arg_table_physics_types_simple_constituent_dim Argument Table +!! \htmlinclude physics_types_simple_constituent_dim.html + ! theta: Potential temperature + real(kind_phys), public, pointer :: theta(:, :) => NULL() + ! slp: Air pressure at sea level + real(kind_phys), public, pointer :: slp(:) => NULL() + ! eddy_len: Eddy length scale + real(kind_phys), public, pointer :: eddy_len(:) => NULL() + ! cool_cat_for_each_const: The coolest imaginable tendency per constituent + real, public :: cool_cat_for_each_const(:, :) + +!! public interfaces + public :: allocate_physics_types_simple_constituent_dim_fields + public :: physics_types_simple_constituent_dim_tstep_init + +CONTAINS + + subroutine allocate_physics_types_simple_constituent_dim_fields(horizontal_dimension, & + vertical_layer_dimension, number_of_ccpp_constituents, set_init_val_in, reallocate_in) + use shr_infnan_mod, only: nan => shr_infnan_nan, assignment(=) + use cam_abortutils, only: endrun + !! Dummy arguments + integer, intent(in) :: horizontal_dimension + integer, intent(in) :: vertical_layer_dimension + integer, intent(in) :: number_of_ccpp_constituents + logical, optional, intent(in) :: set_init_val_in + logical, optional, intent(in) :: reallocate_in + + !! Local variables + logical :: set_init_val + logical :: reallocate + character(len=*), parameter :: subname = & + "allocate_physics_types_simple_constituent_dim_fields" + + ! Set optional argument values + if (present(set_init_val_in)) then + set_init_val = set_init_val_in + else + set_init_val = .true. + end if + if (present(reallocate_in)) then + reallocate = reallocate_in + else + reallocate = .false. + end if + + if (associated(theta)) then + if (reallocate) then + deallocate(theta) + nullify(theta) + else + call endrun(subname//": theta is already associated, cannot allocate") + end if + end if + allocate(theta(horizontal_dimension, vertical_layer_dimension)) + if (set_init_val) then + theta = nan + end if + if (associated(slp)) then + if (reallocate) then + deallocate(slp) + nullify(slp) + else + call endrun(subname//": slp is already associated, cannot allocate") + end if + end if + allocate(slp(horizontal_dimension)) + if (set_init_val) then + slp = nan + end if + if (associated(eddy_len)) then + if (reallocate) then + deallocate(eddy_len) + nullify(eddy_len) + else + call endrun(subname//": eddy_len is already associated, cannot allocate") + end if + end if + allocate(eddy_len(horizontal_dimension)) + if (set_init_val) then + eddy_len = nan + end if + if (set_init_val) then + cool_cat_for_each_const = nan + end if + end subroutine allocate_physics_types_simple_constituent_dim_fields + + subroutine physics_types_simple_constituent_dim_tstep_init() + + !! Local variables + character(len=*), parameter :: subname = "physics_types_simple_constituent_dim_tstep_init" + + end subroutine physics_types_simple_constituent_dim_tstep_init + +end module physics_types_simple_constituent_dim diff --git a/test/unit/python/sample_files/physics_types_simple_constituent_dim.meta b/test/unit/python/sample_files/physics_types_simple_constituent_dim.meta new file mode 100644 index 000000000..4f6268519 --- /dev/null +++ b/test/unit/python/sample_files/physics_types_simple_constituent_dim.meta @@ -0,0 +1,27 @@ +[ccpp-table-properties] + name = physics_types_simple_constituent_dim + type = module +[ccpp-arg-table] + name = physics_types_simple_constituent_dim + type = module +[ theta ] + standard_name = potential_temperature + units = K + type = real | kind = kind_phys + dimensions = (horizontal_dimension, vertical_layer_dimension) +[ slp ] + standard_name = air_pressure_at_sea_level + units = Pa + type = real | kind = kind_phys + dimensions = (horizontal_dimension) +[ eddy_len ] + standard_name = eddy_length_scale + units = m + type = real | kind = kind_phys + dimensions = (horizontal_dimension) +[ cool_cat_for_each_const ] + standard_name = super_cool_cat_every_const + long_name = The coolest imaginable tendency per constituent + units = kg kg-1 s-1 + type = real + dimensions = (horizontal_dimension, number_of_ccpp_constituents) diff --git a/test/unit/python/sample_files/write_init_files/phys_vars_init_check_constituent_dim.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_constituent_dim.F90 new file mode 100644 index 000000000..5a5025baa --- /dev/null +++ b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_constituent_dim.F90 @@ -0,0 +1,245 @@ +! +! This work (Common Community Physics Package Framework), identified by +! NOAA, NCAR, CU/CIRES, is free of known copyright restrictions and is +! placed in the public domain. +! +! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +! THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +! IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +! CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +!> +!! @brief Auto-generated Initialization-checking source file +!! +! +module phys_vars_init_check_constituent_dim + + + implicit none + private + +!! public interfaces + public :: mark_as_initialized + public :: mark_as_read_from_file + public :: is_initialized + public :: is_read_from_file + +!! Parameterized initialized_vars options - order matters + integer, public, parameter :: UNINITIALIZED = 0 + integer, public, parameter :: INITIALIZED = 1 + integer, public, parameter :: PARAM = 2 + integer, public, parameter :: READ_FROM_FILE = 3 + ! Total number of physics-related variables: + integer, public, parameter :: phys_var_num = 3 + integer, public, parameter :: phys_const_num = 16 + + !Max length of physics-related variable standard names: + integer, public, parameter :: std_name_len = 26 + + ! Max length of input (IC) file variable names: + integer, public, parameter :: ic_name_len = 13 + + ! Physics-related input variable standard names: + character(len=26), public, protected :: phys_var_stdnames(phys_var_num) = (/ & + 'potential_temperature ', & + 'air_pressure_at_sea_level ', & + 'super_cool_cat_every_const' /) + + character(len=36), public, protected :: phys_const_stdnames(phys_const_num) = (/ & + "ccpp_constituent_minimum_values ", & + "ccpp_constituent_properties ", & + "ccpp_constituent_tendencies ", & + "ccpp_constituents ", & + "ccpp_error_code ", & + "ccpp_error_message ", & + "do_log_output ", & + "log_output_unit ", & + "mpi_communicator ", & + "mpi_rank ", & + "mpi_root ", & + "number_of_ccpp_advected_constituents", & + "number_of_ccpp_constituents ", & + "number_of_mpi_tasks ", & + "suite_name ", & + "suite_part " /) + !Array storing all registered IC file input names for each variable: + character(len=13), public, protected :: input_var_names(2, phys_var_num) = reshape((/ & + 'theta ', 'pot_temp ', & + 'slp ', 'sea_lev_pres ', & + 'cool_cat_tend', ' ' /), (/2, phys_var_num/)) + + ! Array indicating whether or not variable is protected: + logical, public, protected :: protected_vars(phys_var_num)= (/ & + .false., & + .false., & + .false. /) + + ! Variable state (UNINITIALIZED, INTIIALIZED, PARAM or READ_FROM_FILE): + integer, public, protected :: initialized_vars(phys_var_num)= (/ & + UNINITIALIZED, & + UNINITIALIZED, & + UNINITIALIZED /) + + +CONTAINS + + subroutine mark_as_initialized(varname) + + ! This subroutine marks the variable, , as + ! INITIALIZED in the `initialized_vars` array, + ! which means any initialization check should + ! now return True. + + ! Dummy argument + character(len=*), intent(in) :: varname !Variable name being marked + + ! Local variable + integer :: stdnam_idx !Standard name array index + + ! Search for in the standard name array: + do stdnam_idx = 1, phys_var_num + if (trim(phys_var_stdnames(stdnam_idx)) == trim(varname)) then + ! Only set to INITIALIZED if state is UNINITIALIZED + if (initialized_vars(stdnam_idx) < PARAM) then + initialized_vars(stdnam_idx) = INITIALIZED + end if + exit ! Exit loop once variable has been found and initialized + end if + end do + + ! No match is not an error because only + ! contains variables required by a physics suite. + + end subroutine mark_as_initialized + + subroutine mark_as_read_from_file(varname) + + ! This subroutine marks the varible, , as READ_FROM_FILE in the + ! initialized_vars array + + use cam_abortutils, only: endrun + + ! Dummy argument + character(len=*), intent(in) :: varname ! Variable name being marked + + ! Local variables + integer :: stdnam_idx ! Standard name array index + logical :: found_var ! .true. if is in arr. + character(len=*), parameter :: subname = 'mark_as_read_from_file' + + found_var = .false. + ! Set variable to READ_FROM_FILE: + do stdnam_idx = 1, phys_var_num + if (trim(phys_var_stdnames(stdnam_idx)) == trim(varname)) then + ! It is an error if the variable has already been set to PARAM + if (initialized_vars(stdnam_idx) == PARAM) then + call endrun("Variable '"//trim(varname)// & + "' was read from file, but is a parameter") + end if + initialized_vars(stdnam_idx) = READ_FROM_FILE + + ! Indicate variable has been found: + found_var = .true. + exit ! Exit loop once variable has been found and marked + end if + end do + + if (.not. found_var) then + ! This condition is an internal error, it should not happen + call endrun(subname//": Variable '"//trim(varname)// & + "' is missing from phys_var_stdnames array.") + end if + + end subroutine mark_as_read_from_file + + logical function is_initialized(varname) + + ! This function checks if the variable, , is already + ! initialized according to the 'initialized_vars' array. + + use cam_abortutils, only: endrun + + ! Dummy argument + character(len=*), intent(in) :: varname ! Variable name being checked + + ! Local variables + integer :: stdnam_idx ! Standard name array index + logical :: found ! Check that was found + character(len=*), parameter :: subname = 'is_initialized: ' + + is_initialized = .false. + found = .false. + + ! Check if variable is initialized (PARAM, INITIALIZED, or READ_FROM_FILE) + do stdnam_idx = 1, phys_var_num + if (trim(phys_var_stdnames(stdnam_idx)) == trim(varname)) then + is_initialized = (initialized_vars(stdnam_idx) > UNINITIALIZED) + found = .true. + exit ! Exit loop once variable has been found and checked + end if + end do + + if (.not. found) then + ! This condition is an internal error, it should not happen + call endrun(subname//": Variable '"//trim(varname)// & + "' is missing from phys_var_stdnames array.") + end if + + end function is_initialized + + subroutine is_read_from_file(varname, is_read, stdnam_idx_out) + + ! This subroutine checks if the variable, , is read from + ! file according to the 'initialized_vars' array. + + use cam_abortutils, only: endrun + + ! Dummy arguments + character(len=*), intent(in) :: varname ! Variable name being checked + logical, intent(out) :: is_read ! Set to .true. if from file + integer, optional, intent(out) :: stdnam_idx_out + + ! Local variables + + integer :: stdnam_idx ! Standard name array index + logical :: found ! Check that was found + character(len=*), parameter :: subname = 'is_read_from_file: ' + + is_read = .false. + found = .false. + + ! Return .true. if the variable's status is READ_FROM_FILE: + do stdnam_idx = 1, phys_var_num + if (trim(phys_var_stdnames(stdnam_idx)) == trim(varname)) then + is_read = (initialized_vars(stdnam_idx) == READ_FROM_FILE) + ! Mark as found: + found = .true. + exit ! Exit loop once variable has been found and checked + end if + end do + + if (.not. found) then + ! Check to see if this is an internally-protected variable + do stdnam_idx = 1, phys_const_num + if (trim(phys_const_stdnames(stdnam_idx)) == trim(varname)) then + found = .true. + exit ! Exit loop once variable has been found + end if + end do + end if + + if (.not. found) then + ! This condition is an internal error, it should not happen + call endrun(subname//": Variable '"//trim(varname)// & + "' is missing from phys_var_stdnames array.") + end if + if (present(stdnam_idx_out)) then + stdnam_idx_out = stdnam_idx + end if + + end subroutine is_read_from_file + +end module phys_vars_init_check_constituent_dim diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_4D.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_4D.F90 index b88b2584f..0176da892 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_4D.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_4D.F90 @@ -34,6 +34,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia use spmd_utils, only: masterproc use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use physics_data, only: read_constituent_dimensioned_field use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use ccpp_kinds, only: kind_phys use phys_vars_init_check_4D, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized @@ -82,7 +83,10 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Logical to default optional argument to False: logical :: use_init_variables - ! Initalize missing and non-initialized variables strings: + ! Get constituent properties pointer: + const_props => cam_model_const_properties() + + ! Initialize missing and non-initialized variables strings: missing_required_vars = ' ' protected_non_init_vars = ' ' sep = '' @@ -172,7 +176,6 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Read in constituent variables if not using init variables field_data_ptr => cam_constituents_array() - const_props => cam_model_const_properties() ! Iterate over all registered constituents do constituent_idx = 1, size(const_props) diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_bvd.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_bvd.F90 index 363d26cc3..de1568909 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_bvd.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_bvd.F90 @@ -34,6 +34,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia use spmd_utils, only: masterproc use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use physics_data, only: read_constituent_dimensioned_field use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use ccpp_kinds, only: kind_phys use phys_vars_init_check_bvd, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized @@ -82,7 +83,10 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Logical to default optional argument to False: logical :: use_init_variables - ! Initalize missing and non-initialized variables strings: + ! Get constituent properties pointer: + const_props => cam_model_const_properties() + + ! Initialize missing and non-initialized variables strings: missing_required_vars = ' ' protected_non_init_vars = ' ' sep = '' @@ -172,7 +176,6 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Read in constituent variables if not using init variables field_data_ptr => cam_constituents_array() - const_props => cam_model_const_properties() ! Iterate over all registered constituents do constituent_idx = 1, size(const_props) diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_cnst.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_cnst.F90 index b76746a6f..649190bac 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_cnst.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_cnst.F90 @@ -34,6 +34,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia use spmd_utils, only: masterproc use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use physics_data, only: read_constituent_dimensioned_field use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use ccpp_kinds, only: kind_phys use phys_vars_init_check_cnst, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized @@ -82,7 +83,10 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Logical to default optional argument to False: logical :: use_init_variables - ! Initalize missing and non-initialized variables strings: + ! Get constituent properties pointer: + const_props => cam_model_const_properties() + + ! Initialize missing and non-initialized variables strings: missing_required_vars = ' ' protected_non_init_vars = ' ' sep = '' @@ -172,7 +176,6 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Read in constituent variables if not using init variables field_data_ptr => cam_constituents_array() - const_props => cam_model_const_properties() ! Iterate over all registered constituents do constituent_idx = 1, size(const_props) diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_constituent_dim.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_constituent_dim.F90 new file mode 100644 index 000000000..c856b3900 --- /dev/null +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_constituent_dim.F90 @@ -0,0 +1,404 @@ +! +! This work (Common Community Physics Package Framework), identified by +! NOAA, NCAR, CU/CIRES, is free of known copyright restrictions and is +! placed in the public domain. +! +! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +! THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +! IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +! CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +!> +!! @brief Auto-generated Initial conditions source file, physics_inputs_constituent_dim.F90 +!! +! +module physics_inputs_constituent_dim + + + implicit none + private + + +!! public interfaces + public :: physics_read_data + public :: physics_check_data + +CONTAINS + + subroutine physics_read_data(file, suite_names, timestep, read_initialized_variables) + use pio, only: file_desc_t + use cam_abortutils, only: endrun + use spmd_utils, only: masterproc + use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX + use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use physics_data, only: read_constituent_dimensioned_field + use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties + use ccpp_kinds, only: kind_phys + use phys_vars_init_check_constituent_dim, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized + use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t + use cam_logfile, only: iulog + use physics_types_simple_constituent_dim, only: cool_cat_for_each_const, slp, theta + + ! Dummy arguments + type(file_desc_t), intent(inout) :: file + character(len=SHR_KIND_CS), intent(in) :: suite_names(:) !Names of CCPP suites + integer, intent(in) :: timestep + logical, optional, intent(in) :: read_initialized_variables + + ! Local variables: + + ! Character array containing all CCPP-required variable standard names: + character(len=std_name_len), allocatable :: ccpp_required_data(:) + + ! Strings which store names of any missing or non-initialized vars: + character(len=SHR_KIND_CL) :: missing_required_vars + character(len=SHR_KIND_CL) :: protected_non_init_vars + + character(len=SHR_KIND_CX) :: errmsg !CCPP framework error message + integer :: errflg !CCPP framework error flag + integer :: n !Loop control variable + integer :: name_idx !Input variable array index + integer :: constituent_idx !Constituent table index + integer :: const_input_idx !input_var_names index for a consituent + integer :: req_idx !Required variable array index + integer :: suite_idx !Suite array index + character(len=2) :: sep !String separator used to print err messages + character(len=2) :: sep2 !String separator used to print err messages + character(len=2) :: sep3 !String separator used to print err messages + real(kind=kind_phys), pointer :: field_data_ptr(:,:,:) + logical :: var_found !Bool to determine if consituent found in data files + character(len=std_name_len) :: std_name !Variable to hold constiutent standard name + + ! Fields needed for getting default data value for constituents + type(ccpp_constituent_prop_ptr_t), pointer :: const_props(:) + real(kind=kind_phys) :: constituent_default_value + real(kind=kind_phys) :: constituent_min_value + integer :: constituent_errflg + character(len=512) :: constituent_errmsg + logical :: constituent_has_default + + ! Logical to default optional argument to False: + logical :: use_init_variables + + ! Get constituent properties pointer: + const_props => cam_model_const_properties() + + ! Initialize missing and non-initialized variables strings: + missing_required_vars = ' ' + protected_non_init_vars = ' ' + sep = '' + sep2 = '' + sep3 = '' + + ! Initialize use_init_variables based on whether it was input to function: + if (present(read_initialized_variables)) then + use_init_variables = read_initialized_variables + else + use_init_variables = .false. + end if + + ! Loop over CCPP physics/chemistry suites: + do suite_idx = 1, size(suite_names, 1) + + ! Search for all needed CCPP input variables, so that they can be read from input file if need be: + call ccpp_physics_suite_variables(suite_names(suite_idx), ccpp_required_data, errmsg, errflg, input_vars=.true., output_vars=.false.) + + ! Loop over all required variables and read from file if uninitialized: + do req_idx = 1, size(ccpp_required_data, 1) + + ! Find IC file input name array index for required variable: + name_idx = find_input_name_idx(ccpp_required_data(req_idx), use_init_variables, constituent_idx) + + ! Check for special index values: + select case (name_idx) + + case (init_mark_idx) + + ! If variable is already initialized, then do nothing. + + case (no_exist_idx) + + ! If an index was never found, then save variable name and check the rest of the variables, after which the model simulation will + ! end: + missing_required_vars(len_trim(missing_required_vars)+1:) = trim(sep)//trim(ccpp_required_data(req_idx)) + + ! Update character separator to now include comma: + sep = ', ' + + case (prot_no_init_idx) + + ! If an index was found for a protected variable, but that variable was never marked as initialized, then save the variable name + ! and check the rest of the variables, after which the model simulation will end: + protected_non_init_vars(len_trim(protected_non_init_vars)+1:) = trim(sep2)//trim(ccpp_required_data(req_idx)) + + ! Update character separator to now include comma: + sep2 = ', ' + + case (const_idx) + + ! If an index was found in the constituent hash table, then do nothing, this will be handled later + + case default + + ! Read variable from IC file: + + select case (trim(phys_var_stdnames(name_idx))) + case ('potential_temperature') + call read_field(file, 'potential_temperature', input_var_names(:,name_idx), 'lev', timestep, theta) + + case ('air_pressure_at_sea_level') + call read_field(file, 'air_pressure_at_sea_level', input_var_names(:,name_idx), timestep, slp) + + case ('super_cool_cat_every_const') + call read_constituent_dimensioned_field(const_props, file, 'super_cool_cat_every_const', input_var_names(:,name_idx), & + timestep, cool_cat_for_each_const) + + end select !read variables + end select !special indices + + end do !Suite-required variables + + ! End simulation if there are missing input variables that are required: + if (len_trim(missing_required_vars) > 0) then + call endrun("Required variables missing from registered list of input variables: "//& + trim(missing_required_vars)) + end if + + ! End simulation if there are protected input variables that are not initialized: + if (len_trim(protected_non_init_vars) > 0) then + call endrun("Required, protected input variables are not initialized: "//& + trim(protected_non_init_vars)) + end if + + ! Deallocate required variables array for use in next suite: + deallocate(ccpp_required_data) + + end do !CCPP suites + + ! Read in constituent variables if not using init variables + field_data_ptr => cam_constituents_array() + + ! Iterate over all registered constituents + do constituent_idx = 1, size(const_props) + var_found = .false. + ! Check if constituent standard name in registered SIMA standard names list: + call const_props(constituent_idx)%standard_name(std_name) + if(any(phys_var_stdnames == trim(std_name))) then + ! Don't read the variable in if it's already initialized + if (is_initialized(std_name)) then + cycle + end if + ! Find array index to extract correct input names: + do n=1, phys_var_num + if(trim(phys_var_stdnames(n)) == trim(std_name)) then + const_input_idx = n + exit + end if + end do + call read_field(file, std_name, input_var_names(:,const_input_idx), 'lev', timestep, field_data_ptr(:,:,constituent_idx), & + mark_as_read=.false., error_on_not_found=.false., var_found=var_found) + else + ! If not in standard names list, then just use constituent name as input file name: + call read_field(file, std_name, [std_name], 'lev', timestep, field_data_ptr(:,:,constituent_idx), mark_as_read=.false., & + error_on_not_found=.false., var_found=var_found) + end if + if(.not. var_found) then + constituent_has_default = .false. + call const_props(constituent_idx)%has_default(constituent_has_default, constituent_errflg, constituent_errmsg) + if (constituent_errflg /= 0) then + call endrun(constituent_errmsg, file=__FILE__, line=__LINE__) + end if + if (.not. constituent_has_default) then + ! Intialize to constituent's configured minimum value + call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) + field_data_ptr(:,:,constituent_idx) = constituent_min_value + if (masterproc) then + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + end if + end if + end if + end do + + end subroutine physics_read_data + + subroutine physics_check_data(file_name, suite_names, timestep, min_difference, min_relative_value, err_on_fail) + use pio, only: file_desc_t, pio_nowrite + use cam_abortutils, only: endrun + use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX + use physics_data, only: check_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_advected_constituents_array, cam_model_const_properties + use cam_constituents, only: const_get_index + use ccpp_kinds, only: kind_phys + use cam_logfile, only: iulog + use spmd_utils, only: masterproc + use phys_vars_init_check, only: is_read_from_file + use ioFileMod, only: cam_get_file + use cam_pio_utils, only: cam_pio_openfile, cam_pio_closefile + use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t + use phys_vars_init_check_constituent_dim, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len + use physics_types_simple_constituent_dim, only: cool_cat_for_each_const, slp, theta + + ! Dummy arguments + character(len=SHR_KIND_CL), intent(in) :: file_name + character(len=SHR_KIND_CS), intent(in) :: suite_names(:) !Names of CCPP suites + integer, intent(in) :: timestep + real(kind_phys), intent(in) :: min_difference + real(kind_phys), intent(in) :: min_relative_value + logical, intent(in) :: err_on_fail + + ! Local variables: + + ! Character array containing all CCPP-required variable standard names: + character(len=std_name_len), allocatable :: ccpp_required_data(:) + + ! Strings which store names of any missing or non-initialized vars: + character(len=SHR_KIND_CL) :: missing_required_vars + character(len=SHR_KIND_CL) :: protected_non_init_vars + character(len=SHR_KIND_CL) :: missing_input_names + + character(len=SHR_KIND_CX) :: errmsg !CCPP framework error message + integer :: errflg !CCPP framework error flag + integer :: n !Loop control variable + integer :: name_idx !Input variable array index + integer :: constituent_idx !Index of variable in constituent array + integer :: const_input_idx !input_var_names index for a consituent + integer :: req_idx !Required variable array index + integer :: suite_idx !Suite array index + character(len=SHR_KIND_CL) :: ncdata_check_loc + type(file_desc_t), pointer :: file + logical :: file_found + logical :: is_first + logical :: is_read + logical :: diff_found + logical :: overall_diff_found + character(len=std_name_len) :: std_name !Variable to hold constiutent standard name + real(kind=kind_phys), pointer :: field_data_ptr(:,:,:) + type(ccpp_constituent_prop_ptr_t), pointer :: const_props(:) + + ! Initalize missing and non-initialized variables strings: + missing_required_vars = ' ' + protected_non_init_vars = ' ' + missing_input_names = ' ' + nullify(file) + is_first = .true. + overall_diff_found = .false. + + if (masterproc) then + write(iulog,*) '' + write(iulog,*) '********** Physics Check Data Results **********' + write(iulog,*) '' + write(iulog,*) 'TIMESTEP: ', timestep + end if + if (file_name == 'UNSET') then + write(iulog,*) 'WARNING: Namelist variable ncdata_check is UNSET.', ' Model will run, but physics check data will not be printed' + return + end if + ! Open check file: + call cam_get_file(file_name, ncdata_check_loc, allow_fail=.true., lexist=file_found, log_info=.false.) + if (.not. file_found) then + write(iulog,*) 'WARNING: Check file ', trim(file_name), ' not found. Model will run, but physics check data will not be printed' + return + end if + allocate(file) + call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + ! Loop over CCPP physics/chemistry suites: + do suite_idx = 1, size(suite_names, 1) + + ! Search for all needed CCPP input variables, so that they can be read from input file if need be: + call ccpp_physics_suite_variables(suite_names(suite_idx), ccpp_required_data, errmsg, errflg, input_vars=.true., output_vars=.false.) + + ! Loop over all required variables as specified by CCPP suite: + do req_idx = 1, size(ccpp_required_data, 1) + + ! First check if the required variable is a constituent: + call const_get_index(ccpp_required_data(req_idx), constituent_idx, abort=.false., warning=.false.) + if (constituent_idx > -1) then + cycle + else + ! The required variable is not a constituent. Check if the variable was read from a file + ! Find IC file input name array index for required variable: + call is_read_from_file(ccpp_required_data(req_idx), is_read, stdnam_idx_out=name_idx) + if (.not. is_read) then + cycle + end if + ! Check variable vs input check file: + + select case (trim(phys_var_stdnames(name_idx))) + case ('potential_temperature') + call check_field(file, input_var_names(:,name_idx), 'lev', timestep, theta, 'potential_temperature', min_difference, & + min_relative_value, is_first, diff_found) + + case ('air_pressure_at_sea_level') + call check_field(file, input_var_names(:,name_idx), timestep, slp, 'air_pressure_at_sea_level', min_difference, min_relative_value, & + is_first, diff_found) + + case ('super_cool_cat_every_const') + call check_field(file, input_var_names(:,name_idx), timestep, cool_cat_for_each_const, 'super_cool_cat_every_const', & + min_difference, min_relative_value, is_first, diff_found) + + end select !check variables + if (diff_found) then + overall_diff_found = .true. + end if + end if !check if constituent + end do !Suite-required variables + + ! Deallocate required variables array for use in next suite: + deallocate(ccpp_required_data) + + end do !CCPP suites + + ! Check constituent variables + field_data_ptr => cam_advected_constituents_array() + const_props => cam_model_const_properties() + + do constituent_idx = 1, size(const_props) + ! Check if constituent standard name in registered SIMA standard names list: + call const_props(constituent_idx)%standard_name(std_name) + if(any(phys_var_stdnames == std_name)) then + ! Find array index to extract correct input names: + do n=1, phys_var_num + if(trim(phys_var_stdnames(n)) == trim(std_name)) then + const_input_idx = n + exit + end if + end do + call check_field(file, input_var_names(:,const_input_idx), 'lev', timestep, field_data_ptr(:,:,constituent_idx), std_name, & + min_difference, min_relative_value, is_first, diff_found) + if (diff_found) then + overall_diff_found = .true. + end if + else + ! If not in standard names list, then just use constituent name as input file name: + call check_field(file, [std_name], 'lev', timestep, field_data_ptr(:,:,constituent_idx), std_name, min_difference, min_relative_value, & + is_first, diff_found) + if (diff_found) then + overall_diff_found = .true. + end if + end if + end do + ! Close check file: + call cam_pio_closefile(file) + deallocate(file) + nullify(file) + if (is_first) then + if (masterproc) then + write(iulog,*) '' + write(iulog,*) 'No differences found!' + end if + end if + if (masterproc) then + write(iulog,*) '' + write(iulog,*) '********** End Physics Check Data Results **********' + write(iulog,*) '' + end if + ! Endrun if differences were found on this timestep and err_on_fail=TRUE + if (overall_diff_found .and. err_on_fail .and. masterproc) then + call endrun('ERROR: Difference(s) found during ncdata check', file=__FILE__, line=__LINE__) + end if + end subroutine physics_check_data + +end module physics_inputs_constituent_dim diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_ddt.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_ddt.F90 index b5854591b..15d5c24b0 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_ddt.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_ddt.F90 @@ -34,6 +34,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia use spmd_utils, only: masterproc use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use physics_data, only: read_constituent_dimensioned_field use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use ccpp_kinds, only: kind_phys use phys_vars_init_check_ddt, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized @@ -82,7 +83,10 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Logical to default optional argument to False: logical :: use_init_variables - ! Initalize missing and non-initialized variables strings: + ! Get constituent properties pointer: + const_props => cam_model_const_properties() + + ! Initialize missing and non-initialized variables strings: missing_required_vars = ' ' protected_non_init_vars = ' ' sep = '' @@ -172,7 +176,6 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Read in constituent variables if not using init variables field_data_ptr => cam_constituents_array() - const_props => cam_model_const_properties() ! Iterate over all registered constituents do constituent_idx = 1, size(const_props) diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_ddt2.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_ddt2.F90 index 490a022a5..83afed2f3 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_ddt2.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_ddt2.F90 @@ -34,6 +34,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia use spmd_utils, only: masterproc use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use physics_data, only: read_constituent_dimensioned_field use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use ccpp_kinds, only: kind_phys use phys_vars_init_check_ddt2, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized @@ -82,7 +83,10 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Logical to default optional argument to False: logical :: use_init_variables - ! Initalize missing and non-initialized variables strings: + ! Get constituent properties pointer: + const_props => cam_model_const_properties() + + ! Initialize missing and non-initialized variables strings: missing_required_vars = ' ' protected_non_init_vars = ' ' sep = '' @@ -172,7 +176,6 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Read in constituent variables if not using init variables field_data_ptr => cam_constituents_array() - const_props => cam_model_const_properties() ! Iterate over all registered constituents do constituent_idx = 1, size(const_props) diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_ddt_array.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_ddt_array.F90 index a23b6561f..0fe0822d1 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_ddt_array.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_ddt_array.F90 @@ -34,6 +34,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia use spmd_utils, only: masterproc use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use physics_data, only: read_constituent_dimensioned_field use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use ccpp_kinds, only: kind_phys use phys_vars_init_check_ddt_array, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized @@ -82,7 +83,10 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Logical to default optional argument to False: logical :: use_init_variables - ! Initalize missing and non-initialized variables strings: + ! Get constituent properties pointer: + const_props => cam_model_const_properties() + + ! Initialize missing and non-initialized variables strings: missing_required_vars = ' ' protected_non_init_vars = ' ' sep = '' @@ -172,7 +176,6 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Read in constituent variables if not using init variables field_data_ptr => cam_constituents_array() - const_props => cam_model_const_properties() ! Iterate over all registered constituents do constituent_idx = 1, size(const_props) diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_host_var.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_host_var.F90 index 7bae27488..a599b8f15 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_host_var.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_host_var.F90 @@ -34,6 +34,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia use spmd_utils, only: masterproc use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use physics_data, only: read_constituent_dimensioned_field use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use ccpp_kinds, only: kind_phys use phys_vars_init_check_host_var, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized @@ -82,7 +83,10 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Logical to default optional argument to False: logical :: use_init_variables - ! Initalize missing and non-initialized variables strings: + ! Get constituent properties pointer: + const_props => cam_model_const_properties() + + ! Initialize missing and non-initialized variables strings: missing_required_vars = ' ' protected_non_init_vars = ' ' sep = '' @@ -169,7 +173,6 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Read in constituent variables if not using init variables field_data_ptr => cam_constituents_array() - const_props => cam_model_const_properties() ! Iterate over all registered constituents do constituent_idx = 1, size(const_props) diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_initial_value.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_initial_value.F90 index cc367a4fc..266a582da 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_initial_value.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_initial_value.F90 @@ -34,6 +34,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia use spmd_utils, only: masterproc use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use physics_data, only: read_constituent_dimensioned_field use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use ccpp_kinds, only: kind_phys use phys_vars_init_check_initial_value, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized @@ -82,7 +83,10 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Logical to default optional argument to False: logical :: use_init_variables - ! Initalize missing and non-initialized variables strings: + ! Get constituent properties pointer: + const_props => cam_model_const_properties() + + ! Initialize missing and non-initialized variables strings: missing_required_vars = ' ' protected_non_init_vars = ' ' sep = '' @@ -172,7 +176,6 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Read in constituent variables if not using init variables field_data_ptr => cam_constituents_array() - const_props => cam_model_const_properties() ! Iterate over all registered constituents do constituent_idx = 1, size(const_props) diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_mf.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_mf.F90 index 48ec363f0..0b2088427 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_mf.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_mf.F90 @@ -34,6 +34,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia use spmd_utils, only: masterproc use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use physics_data, only: read_constituent_dimensioned_field use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use ccpp_kinds, only: kind_phys use phys_vars_init_check_mf, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized @@ -83,7 +84,10 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Logical to default optional argument to False: logical :: use_init_variables - ! Initalize missing and non-initialized variables strings: + ! Get constituent properties pointer: + const_props => cam_model_const_properties() + + ! Initialize missing and non-initialized variables strings: missing_required_vars = ' ' protected_non_init_vars = ' ' sep = '' @@ -173,7 +177,6 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Read in constituent variables if not using init variables field_data_ptr => cam_constituents_array() - const_props => cam_model_const_properties() ! Iterate over all registered constituents do constituent_idx = 1, size(const_props) diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_no_horiz.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_no_horiz.F90 index f72f84160..ec8e85458 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_no_horiz.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_no_horiz.F90 @@ -34,6 +34,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia use spmd_utils, only: masterproc use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use physics_data, only: read_constituent_dimensioned_field use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use ccpp_kinds, only: kind_phys use phys_vars_init_check_no_horiz, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized @@ -82,7 +83,10 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Logical to default optional argument to False: logical :: use_init_variables - ! Initalize missing and non-initialized variables strings: + ! Get constituent properties pointer: + const_props => cam_model_const_properties() + + ! Initialize missing and non-initialized variables strings: missing_required_vars = ' ' protected_non_init_vars = ' ' sep = '' @@ -172,7 +176,6 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Read in constituent variables if not using init variables field_data_ptr => cam_constituents_array() - const_props => cam_model_const_properties() ! Iterate over all registered constituents do constituent_idx = 1, size(const_props) diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_noreq.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_noreq.F90 index 93607a57b..b3d7f241c 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_noreq.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_noreq.F90 @@ -34,6 +34,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia use spmd_utils, only: masterproc use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use physics_data, only: read_constituent_dimensioned_field use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use ccpp_kinds, only: kind_phys use phys_vars_init_check_noreq, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized @@ -81,7 +82,10 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Logical to default optional argument to False: logical :: use_init_variables - ! Initalize missing and non-initialized variables strings: + ! Get constituent properties pointer: + const_props => cam_model_const_properties() + + ! Initialize missing and non-initialized variables strings: missing_required_vars = ' ' protected_non_init_vars = ' ' sep = '' @@ -165,7 +169,6 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Read in constituent variables if not using init variables field_data_ptr => cam_constituents_array() - const_props => cam_model_const_properties() ! Iterate over all registered constituents do constituent_idx = 1, size(const_props) diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_param.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_param.F90 index 0f8fdaa5a..fb3711050 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_param.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_param.F90 @@ -34,6 +34,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia use spmd_utils, only: masterproc use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use physics_data, only: read_constituent_dimensioned_field use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use ccpp_kinds, only: kind_phys use phys_vars_init_check_param, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized @@ -82,7 +83,10 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Logical to default optional argument to False: logical :: use_init_variables - ! Initalize missing and non-initialized variables strings: + ! Get constituent properties pointer: + const_props => cam_model_const_properties() + + ! Initialize missing and non-initialized variables strings: missing_required_vars = ' ' protected_non_init_vars = ' ' sep = '' @@ -175,7 +179,6 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Read in constituent variables if not using init variables field_data_ptr => cam_constituents_array() - const_props => cam_model_const_properties() ! Iterate over all registered constituents do constituent_idx = 1, size(const_props) diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_protect.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_protect.F90 index 80bba9b77..0c06875e0 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_protect.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_protect.F90 @@ -34,6 +34,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia use spmd_utils, only: masterproc use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use physics_data, only: read_constituent_dimensioned_field use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use ccpp_kinds, only: kind_phys use phys_vars_init_check_protect, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized @@ -82,7 +83,10 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Logical to default optional argument to False: logical :: use_init_variables - ! Initalize missing and non-initialized variables strings: + ! Get constituent properties pointer: + const_props => cam_model_const_properties() + + ! Initialize missing and non-initialized variables strings: missing_required_vars = ' ' protected_non_init_vars = ' ' sep = '' @@ -172,7 +176,6 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Read in constituent variables if not using init variables field_data_ptr => cam_constituents_array() - const_props => cam_model_const_properties() ! Iterate over all registered constituents do constituent_idx = 1, size(const_props) diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_scalar.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_scalar.F90 index 6f8d5330b..570a43242 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_scalar.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_scalar.F90 @@ -34,6 +34,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia use spmd_utils, only: masterproc use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use physics_data, only: read_constituent_dimensioned_field use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use ccpp_kinds, only: kind_phys use phys_vars_init_check_scalar, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized @@ -82,7 +83,10 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Logical to default optional argument to False: logical :: use_init_variables - ! Initalize missing and non-initialized variables strings: + ! Get constituent properties pointer: + const_props => cam_model_const_properties() + + ! Initialize missing and non-initialized variables strings: missing_required_vars = ' ' protected_non_init_vars = ' ' sep = '' @@ -172,7 +176,6 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Read in constituent variables if not using init variables field_data_ptr => cam_constituents_array() - const_props => cam_model_const_properties() ! Iterate over all registered constituents do constituent_idx = 1, size(const_props) diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_simple.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_simple.F90 index b5873948b..490e535cb 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_simple.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_simple.F90 @@ -34,6 +34,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia use spmd_utils, only: masterproc use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx + use physics_data, only: read_constituent_dimensioned_field use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use ccpp_kinds, only: kind_phys use phys_vars_init_check_simple, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized @@ -82,7 +83,10 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Logical to default optional argument to False: logical :: use_init_variables - ! Initalize missing and non-initialized variables strings: + ! Get constituent properties pointer: + const_props => cam_model_const_properties() + + ! Initialize missing and non-initialized variables strings: missing_required_vars = ' ' protected_non_init_vars = ' ' sep = '' @@ -172,7 +176,6 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia ! Read in constituent variables if not using init variables field_data_ptr => cam_constituents_array() - const_props => cam_model_const_properties() ! Iterate over all registered constituents do constituent_idx = 1, size(const_props) diff --git a/test/unit/python/sample_files/write_init_files/simple_reg.xml b/test/unit/python/sample_files/write_init_files/simple_reg.xml index 26f058cc3..d9b0892dc 100644 --- a/test/unit/python/sample_files/write_init_files/simple_reg.xml +++ b/test/unit/python/sample_files/write_init_files/simple_reg.xml @@ -18,7 +18,7 @@ horizontal_dimension eddy_len - + The coolest constituent imaginable diff --git a/test/unit/python/sample_files/write_init_files/simple_reg_constituent_dim.xml b/test/unit/python/sample_files/write_init_files/simple_reg_constituent_dim.xml new file mode 100644 index 000000000..8d306af58 --- /dev/null +++ b/test/unit/python/sample_files/write_init_files/simple_reg_constituent_dim.xml @@ -0,0 +1,29 @@ + + + + + + + horizontal_dimension vertical_layer_dimension + theta pot_temp + + + horizontal_dimension + slp sea_lev_pres + + + horizontal_dimension + eddy_len + + + + The coolest imaginable tendency per constituent + horizontal_dimension number_of_ccpp_constituents + cool_cat_tend + + + diff --git a/test/unit/python/sample_files/write_init_files/temp_adjust_constituent_dim.F90 b/test/unit/python/sample_files/write_init_files/temp_adjust_constituent_dim.F90 new file mode 100644 index 000000000..d9d8523a3 --- /dev/null +++ b/test/unit/python/sample_files/write_init_files/temp_adjust_constituent_dim.F90 @@ -0,0 +1,87 @@ +!simple demonstration parameterization +! + +MODULE temp_adjust + + USE ccpp_kinds, ONLY: kind_phys + + IMPLICIT NONE + PRIVATE + + PUBLIC :: temp_adjust_init + PUBLIC :: temp_adjust_run + PUBLIC :: temp_adjust_finalize + +CONTAINS + +!> \section arg_table_temp_adjust_run Argument Table +!! \htmlinclude arg_table_temp_adjust_run.html +!! + SUBROUTINE temp_adjust_run(nbox, lev, temp_layer, & + slp, cool_cat_for_each_const, timestep, errmsg, errflg) +!---------------------------------------------------------------- + IMPLICIT NONE +!---------------------------------------------------------------- + + integer, intent(in) :: nbox, lev + REAL(kind_phys), intent(inout) :: temp_layer(:, :) + real(kind_phys), intent(in) :: slp(:) + real(kind_phys), intent(inout) :: cool_cat_for_each_const(:,:) + real(kind_phys), intent(in) :: timestep + character(len=512), intent(out) :: errmsg + integer, intent(out) :: errflg +!---------------------------------------------------------------- + + integer :: box_index + integer :: lev_index + + errmsg = '' + errflg = 0 + + do box_index = 1, nbox + do lev_index = 1, lev + temp_layer(box_index, lev_index) = temp_layer(box_index, lev_index) & + + 1.0_kind_phys + + !Add a made-up term which uses slp: + temp_layer(box_index, lev_index) = temp_layer(box_index, lev_index) & + + 0._kind_phys*slp(box_index) + end do + end do + + ! every constituent should have a cool cat + cool_cat_for_each_const(:,:) = 0._kind_phys + + END SUBROUTINE temp_adjust_run + +!> \section arg_table_temp_adjust_init Argument Table +!! \htmlinclude arg_table_temp_adjust_init.html +!! + subroutine temp_adjust_init (errmsg, errflg) + + character(len=512), intent(out) :: errmsg + integer, intent(out) :: errflg + + ! This routine currently does nothing + + errmsg = '' + errflg = 0 + + end subroutine temp_adjust_init + +!> \section arg_table_temp_adjust_finalize Argument Table +!! \htmlinclude arg_table_temp_adjust_finalize.html +!! + subroutine temp_adjust_finalize (errmsg, errflg) + + character(len=512), intent(out) :: errmsg + integer, intent(out) :: errflg + + ! This routine currently does nothing + + errmsg = '' + errflg = 0 + + end subroutine temp_adjust_finalize + +END MODULE temp_adjust diff --git a/test/unit/python/sample_files/write_init_files/temp_adjust_constituent_dim.meta b/test/unit/python/sample_files/write_init_files/temp_adjust_constituent_dim.meta new file mode 100644 index 000000000..f9f07e3fc --- /dev/null +++ b/test/unit/python/sample_files/write_init_files/temp_adjust_constituent_dim.meta @@ -0,0 +1,98 @@ +[ccpp-table-properties] + name = temp_adjust + type = scheme +[ccpp-arg-table] + name = temp_adjust_run + type = scheme +[ nbox ] + standard_name = horizontal_loop_extent + type = integer + units = count + dimensions = () + intent = in +[ lev ] + standard_name = vertical_layer_dimension + type = integer + units = count + dimensions = () + intent = in +[ temp_layer ] + standard_name = potential_temperature + units = K + dimensions = (horizontal_loop_extent, vertical_layer_dimension) + type = real + kind = kind_phys + intent = inout +[ slp ] + standard_name = air_pressure_at_sea_level + units = Pa + dimensions = (horizontal_loop_extent) + type = real + kind = kind_phys + intent = in +[ cool_cat_for_each_const ] + standard_name = super_cool_cat_every_const + units = kg kg-1 s-1 + dimensions = (horizontal_loop_extent, number_of_ccpp_constituents) + type = real + kind = kind_phys + intent = inout +[ timestep ] + standard_name = timestep_for_physics + long_name = time step + units = s + dimensions = () + type = real + kind = kind_phys + intent = in +[ errmsg ] + standard_name = ccpp_error_message + long_name = Error message for error handling in CCPP + units = none + dimensions = () + type = character + kind = len=512 + intent = out +[ errflg ] + standard_name = ccpp_error_code + long_name = Error flag for error handling in CCPP + units = 1 + dimensions = () + type = integer + intent = out +[ccpp-arg-table] + name = temp_adjust_init + type = scheme +[ errmsg ] + standard_name = ccpp_error_message + long_name = Error message for error handling in CCPP + units = none + dimensions = () + type = character + kind = len=512 + intent = out +[ errflg ] + standard_name = ccpp_error_code + long_name = Error flag for error handling in CCPP + units = 1 + dimensions = () + type = integer + intent = out +[ccpp-arg-table] + name = temp_adjust_finalize + type = scheme +[ errmsg ] + standard_name = ccpp_error_message + long_name = Error message for error handling in CCPP + units = none + dimensions = () + type = character + kind = len=512 + intent = out +[ errflg ] + standard_name = ccpp_error_code + long_name = Error flag for error handling in CCPP + units = 1 + dimensions = () + type = integer + intent = out diff --git a/test/unit/python/test_write_init_files.py b/test/unit/python/test_write_init_files.py index e2c25d004..3683c3614 100644 --- a/test/unit/python/test_write_init_files.py +++ b/test/unit/python/test_write_init_files.py @@ -1253,7 +1253,8 @@ def test_bad_vertical_dimension(self): correctly determines that a variable that could be called from "read_field" has two dimensions but the second is not a vertical dimension (which read_field can't - handle) and exits with both the correct return + handle) that is not the number of constituents, + and exits with both the correct return message, and with no Fortran files generated. """ @@ -1325,6 +1326,90 @@ def test_bad_vertical_dimension(self): self.assertTrue(filecmp.cmp(phys_input_out, phys_input_in, shallow=False), msg=amsg) + def test_simple_constituent_dimensioned_var_write_init(self): + """ + Test that the 'write_init_files' function + generates the correct Fortran code given + a simple registry and CCPP physics suite with + a variable with a horizontal dimension and + the number of constituents as the second dimension. + """ + + # Setup registry inputs: + filename = os.path.join(_INIT_SAMPLES_DIR, "simple_reg_constituent_dim.xml") + out_source_name = "physics_types_simple_constituent_dim" + out_source = os.path.join(_TMP_DIR, out_source_name + '.F90') + out_meta = os.path.join(_TMP_DIR, out_source_name + '.meta') + + # Setup capgen inputs: + model_host = os.path.join(_INIT_SAMPLES_DIR,"simple_host.meta") + sdf = os.path.join(_INIT_SAMPLES_DIR,"suite_simple.xml") + scheme_files = os.path.join(_INIT_SAMPLES_DIR, "temp_adjust_constituent_dim.meta") + cap_datafile = os.path.join(_TMP_DIR, "datatable_constituent_dim.xml") + + host_files = [model_host, out_meta] + + # Setup write_init_files inputs: + vic_name = "phys_vars_init_check_constituent_dim.F90" + pi_name = "physics_inputs_constituent_dim.F90" + check_init_out = os.path.join(_TMP_DIR, vic_name) + phys_input_out = os.path.join(_TMP_DIR, pi_name) + # Setup comparison files + check_init_in = os.path.join(_INIT_SAMPLES_DIR, vic_name) + phys_input_in = os.path.join(_INIT_SAMPLES_DIR, pi_name) + + # Create local logger: + logger = logging.getLogger("write_init_files_constituent_dim") + + # Clear all temporary output files: + remove_files([out_source, out_meta, cap_datafile, + check_init_out, phys_input_out]) + + # Generate registry files: + _, _, ic_names, constituents, _ = gen_registry(filename, 'se', _TMP_DIR, 3, + _SRC_MOD_DIR, _CAM_ROOT, + loglevel=logging.ERROR, + error_on_no_validate=True) + + # Generate CCPP capgen files: + kind_types = ['kind_phys=REAL64'] + run_env = CCPPFrameworkEnv(logger, host_files=host_files, + scheme_files=scheme_files, suites=sdf, + preproc_directives='', + generate_docfiles=False, + host_name='cam', kind_types=kind_types, + use_error_obj=False, + force_overwrite=True, + output_root=_TMP_DIR, + ccpp_datafile=cap_datafile) + + cap_database = capgen(run_env, return_db=True) + + # Generate physics initialization files: + retmsg = write_init.write_init_files(cap_database, ic_names, constituents, [], _TMP_DIR, + find_file, _INC_SEARCH_DIRS, + 3, logger, + phys_check_filename=vic_name, + phys_input_filename=pi_name) + + # Check return message: + amsg = f"Test failure: retmsg={retmsg}" + self.assertEqual(retmsg, '', msg=amsg) + + # Make sure each output file was created: + amsg = f"{check_init_out} does not exist" + self.assertTrue(os.path.exists(check_init_out), msg=amsg) + amsg = f"{phys_input_out} does not exist" + self.assertTrue(os.path.exists(phys_input_out), msg=amsg) + + # For each output file, make sure it matches input file + amsg = f"{check_init_out} does not match {check_init_in}" + self.assertTrue(filecmp.cmp(check_init_in, check_init_out, + shallow=False), msg=amsg) + amsg = f"{phys_input_out} does not match {phys_input_in}" + self.assertTrue(filecmp.cmp(phys_input_in, phys_input_out, + shallow=False), msg=amsg) + ########## if __name__ == '__main__': From abcae47891cdc53c7141a55124afd2e5f334fb1f Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Thu, 26 Jun 2025 17:44:34 -0400 Subject: [PATCH 02/11] Fix unit tests. --- .../datatable_constituent_dim.xml | 393 ------------------ .../simple_build_cache_template.xml | 6 +- 2 files changed, 3 insertions(+), 396 deletions(-) delete mode 100644 test/unit/python/sample_files/datatable_constituent_dim.xml diff --git a/test/unit/python/sample_files/datatable_constituent_dim.xml b/test/unit/python/sample_files/datatable_constituent_dim.xml deleted file mode 100644 index 287922248..000000000 --- a/test/unit/python/sample_files/datatable_constituent_dim.xml +++ /dev/null @@ -1,393 +0,0 @@ - - - - /Users/hplin/devel/CAM-SIMA/test/unit/python/tmp/write_init_files/ccpp_kinds.F90 - /Users/hplin/devel/CAM-SIMA/ccpp_framework/src/ccpp_constituent_prop_mod.F90 - /Users/hplin/devel/CAM-SIMA/ccpp_framework/src/ccpp_scheme_utils.F90 - /Users/hplin/devel/CAM-SIMA/ccpp_framework/src/ccpp_hashable.F90 - /Users/hplin/devel/CAM-SIMA/ccpp_framework/src/ccpp_hash_table.F90 - - - /Users/hplin/devel/CAM-SIMA/test/unit/python/tmp/write_init_files/cam_ccpp_cap.F90 - - - /Users/hplin/devel/CAM-SIMA/test/unit/python/tmp/write_init_files/ccpp_simple_suite_cap.F90 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - cam_ddt_vars ccpp_physics_api cam_constituents - - - host - simple_sub - - - host - simple_sub - - - host - simple_sub - - - host - simple_sub - - - host - simple_sub - - - host - simple_sub - - - host - simple_sub - - - ccpp_constant_one:horizontal_dimension ccpp_constant_one:vertical_layer_dimension - module - physics_types_simple_constituent_dim - - - ccpp_constant_one:horizontal_dimension - module - physics_types_simple_constituent_dim - - - ccpp_constant_one:horizontal_dimension - module - physics_types_simple_constituent_dim - - - ccpp_constant_one:horizontal_dimension ccpp_constant_one:number_of_ccpp_constituents - module - physics_types_simple_constituent_dim - - - module - cam_ccpp_cap - - - - - suite_simple_constituents - - - - simple_suite_register simple_suite_initialize simple_suite_finalize simple_suite_timestep_initial simple_suite_timestep_final simple_suite_physics - - - - - - - - - group - simple_suite_register - - - group - simple_suite_register - - - - - temp_adjust - - - - - - group - simple_suite_initialize - - - group - simple_suite_initialize - - - - - - - scheme - temp_adjust_init - - - scheme - temp_adjust_init - - - - - - - scheme - temp_adjust_init - - - scheme - temp_adjust_init - - - - - - - - - - group - simple_suite_timestep_initial - - - group - simple_suite_timestep_initial - - - - - TimeSplit - - - local - ccpp_api - - - - - - - group - simple_suite_physics - - - group - simple_suite_physics - - - group - simple_suite_physics - - - group - simple_suite_physics - - - group - simple_suite_physics - - - horizontal_loop_begin:horizontal_loop_end ccpp_constant_one:vertical_layer_dimension - group - simple_suite_physics - - - horizontal_loop_begin:horizontal_loop_end - group - simple_suite_physics - - - horizontal_loop_begin:horizontal_loop_end ccpp_constant_one:number_of_ccpp_constituents - group - simple_suite_physics - - - group - simple_suite_physics - - - - - temp_adjust - - - - - - scheme - temp_adjust_run - - - scheme - temp_adjust_run - - - horizontal_loop_begin:horizontal_loop_end ccpp_constant_one:vertical_layer_dimension - scheme - temp_adjust_run - - - horizontal_loop_begin:horizontal_loop_end - scheme - temp_adjust_run - - - horizontal_loop_begin:horizontal_loop_end ccpp_constant_one:number_of_ccpp_constituents - scheme - temp_adjust_run - - - scheme - temp_adjust_run - - - scheme - temp_adjust_run - - - scheme - temp_adjust_run - - - - - - - scheme - temp_adjust_run - - - scheme - temp_adjust_run - - - horizontal_loop_begin:horizontal_loop_end ccpp_constant_one:vertical_layer_dimension - scheme - temp_adjust_run - - - horizontal_loop_begin:horizontal_loop_end - scheme - temp_adjust_run - - - horizontal_loop_begin:horizontal_loop_end ccpp_constant_one:number_of_ccpp_constituents - scheme - temp_adjust_run - - - scheme - temp_adjust_run - - - scheme - temp_adjust_run - - - scheme - temp_adjust_run - - - - - - - - - - group - simple_suite_timestep_final - - - group - simple_suite_timestep_final - - - - - temp_adjust - - - - - - group - simple_suite_finalize - - - group - simple_suite_finalize - - - - - - - scheme - temp_adjust_finalize - - - scheme - temp_adjust_finalize - - - - - - - scheme - temp_adjust_finalize - - - scheme - temp_adjust_finalize - - - - - - diff --git a/test/unit/python/sample_files/write_init_files/simple_build_cache_template.xml b/test/unit/python/sample_files/write_init_files/simple_build_cache_template.xml index 5f2d63516..6f0931696 100644 --- a/test/unit/python/sample_files/write_init_files/simple_build_cache_template.xml +++ b/test/unit/python/sample_files/write_init_files/simple_build_cache_template.xml @@ -1,10 +1,10 @@ TAG1 - + TAG2 - - + + none From 0b6031edfebef630a7d43e029ca47d0bc55b1e55 Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Thu, 26 Jun 2025 18:25:33 -0400 Subject: [PATCH 03/11] Fix unit tests; also fix missing files for old initial_value unit test --- src/data/write_init_files.py | 15 ++- src/physics/utils/physics_data.F90 | 28 ++--- .../physics_types_simple_constituent_dim.F90 | 35 +++++- .../physics_types_simple_constituent_dim.meta | 6 + .../physics_types_simple_initial_value.F90 | 113 ++++++++++++++++++ .../physics_types_simple_initial_value.meta | 21 ++++ .../phys_vars_init_check_constituent_dim.F90 | 26 ++-- .../physics_inputs_constituent_dim.F90 | 12 +- .../physics_inputs_initial_value.F90 | 4 +- .../simple_reg_constituent_dim.xml | 9 +- .../simple_reg_initial_value.xml | 4 +- .../temp_adjust_constituent_dim.F90 | 4 +- .../temp_adjust_constituent_dim.meta | 7 ++ test/unit/python/test_write_init_files.py | 2 +- 14 files changed, 237 insertions(+), 49 deletions(-) create mode 100644 test/unit/python/sample_files/physics_types_simple_initial_value.F90 create mode 100644 test/unit/python/sample_files/physics_types_simple_initial_value.meta diff --git a/src/data/write_init_files.py b/src/data/write_init_files.py index 6029a2a15..ed6846f2e 100644 --- a/src/data/write_init_files.py +++ b/src/data/write_init_files.py @@ -892,14 +892,12 @@ def write_phys_read_subroutine(outfile, host_dict, host_vars, host_imports, call_str += f"'{levnm}', " # end if - initial_value_string = "" + err_on_not_found_string = "" if var_stdname in vars_init_value: - # if initial value is available, pass it to the read routine - # so it can be used for this variable when data for some - # constituent cannot be found. - initial_value_string = f", initial_value={vars_init_value[var_stdname]}_kind_phys" + # if initial value is available, do not throw error when not found in initial condition file. + err_on_not_found_string = ", error_on_not_found=.false." # end if - call_str += f"timestep, {var_locname}{initial_value_string})" + call_str += f"timestep, {var_locname}{err_on_not_found_string})" else: # Replace vertical dimension with local name call_str = "call read_field(file, " + \ @@ -1238,6 +1236,11 @@ def write_phys_check_subroutine(outfile, host_dict, host_vars, host_imports, # Extract vertical level variable: levnm, call_check_field, reason, has_constituent_read = get_dimension_info(hvar) + # If this is a constituent-indexed field, do not check it for now. + if has_constituent_read: + continue + # end if + # Set "check_field" call string: if call_check_field: call_str = "call check_field(file, input_var_names(:,name_idx), " diff --git a/src/physics/utils/physics_data.F90 b/src/physics/utils/physics_data.F90 index 97a4f7e40..ab5829ad2 100644 --- a/src/physics/utils/physics_data.F90 +++ b/src/physics/utils/physics_data.F90 @@ -8,6 +8,7 @@ module physics_data public :: find_input_name_idx public :: read_field + public :: read_constituent_dimensioned_field public :: check_field !Non-standard variable indices: @@ -329,7 +330,7 @@ subroutine read_field_3d(file, std_name, var_names, vcoord_name, & end if end subroutine read_field_3d - subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, base_var_names, timestep, field_array, initial_value) + subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, base_var_names, timestep, field_array, error_on_not_found) use shr_assert_mod, only: shr_assert_in_domain use shr_sys_mod, only: shr_sys_flush use pio, only: file_desc_t, var_desc_t @@ -349,7 +350,7 @@ subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, ba character(len=*), intent(in) :: base_var_names(:) ! "Base" name(s) used to construct variable name (base_constname) integer, intent(in) :: timestep ! Timestep to read [count] real(kind_phys), intent(inout) :: field_array(:,:) ! Output field array (ncol, pcnst) - real(kind_phys), optional, intent(in) :: initial_value ! Default value if not found + logical, optional, intent(in) :: error_on_not_found ! Flag to error and exit if not found ! Local variables logical :: var_found @@ -361,8 +362,7 @@ subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, ba real(kind_phys), allocatable :: buffer(:) integer :: const_idx, base_idx integer :: ierr - real(kind_phys) :: default_value - logical :: has_initial_value + logical :: error_on_not_found_local logical :: any_missing ! For construction of constituent short name mapping @@ -373,8 +373,11 @@ subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, ba character(len=*), parameter :: subname = 'read_constituent_dimensioned_field: ' - ! Check if initial value was provided - has_initial_value = present(initial_value) + if (present(error_on_not_found)) then + error_on_not_found_local = error_on_not_found + else + error_on_not_found_local = .true. + end if ! Initialize tracking variables any_missing = .false. @@ -408,7 +411,7 @@ subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, ba const_input_idx = n exit phys_inputvar_loop end if - end do + end do phys_inputvar_loop if (const_input_idx > 0) then ! Use the first entry from the input_var_names -- assumed to be short name. @@ -420,11 +423,6 @@ subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, ba end do const_shortmap_loop !END REMOVECAM - ! Initialize field array to default value (only if initial_value provided) - if (has_initial_value) then - field_array(:,:) = initial_value - end if - ! Loop through all possible base names to find correct base name. ! Note this assumes that the same base name is used for all constituents. ! i.e., there cannot be something like cam_in_cflx_Q & cflx_CLDLIQ in one file. @@ -494,8 +492,8 @@ subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, ba missing_vars = trim(file_var_name) end if - if (has_initial_value) then - ! Use default value (already set above) + if (.not. error_on_not_found_local) then + ! Use default value (already set at initialization) if (masterproc) then write(iulog, *) 'Constituent-dimensioned field ', trim(file_var_name), & @@ -507,7 +505,7 @@ subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, ba end do const_read_loop ! Check if we should fail due to missing variables - if (any_missing .and. .not. has_initial_value) then + if (any_missing .and. error_on_not_found_local) then call endrun(subname//'Required constituent-dimensioned variables not found: ' // trim(missing_vars)) end if diff --git a/test/unit/python/sample_files/physics_types_simple_constituent_dim.F90 b/test/unit/python/sample_files/physics_types_simple_constituent_dim.F90 index da9ce3e9a..5da59b752 100644 --- a/test/unit/python/sample_files/physics_types_simple_constituent_dim.F90 +++ b/test/unit/python/sample_files/physics_types_simple_constituent_dim.F90 @@ -26,13 +26,16 @@ module physics_types_simple_constituent_dim !> \section arg_table_physics_types_simple_constituent_dim Argument Table !! \htmlinclude physics_types_simple_constituent_dim.html ! theta: Potential temperature - real(kind_phys), public, pointer :: theta(:, :) => NULL() + real(kind_phys), public, pointer :: theta(:, :) => NULL() ! slp: Air pressure at sea level - real(kind_phys), public, pointer :: slp(:) => NULL() + real(kind_phys), public, pointer :: slp(:) => NULL() ! eddy_len: Eddy length scale - real(kind_phys), public, pointer :: eddy_len(:) => NULL() + real(kind_phys), public, pointer :: eddy_len(:) => NULL() ! cool_cat_for_each_const: The coolest imaginable tendency per constituent - real, public :: cool_cat_for_each_const(:, :) + real, public, allocatable :: cool_cat_for_each_const(:, :) + ! cool_default_cat_for_each_const: The coolest imaginable tendency per constituent now with a + ! default + real, public, allocatable :: cool_default_cat_for_each_const(:, :) !! public interfaces public :: allocate_physics_types_simple_constituent_dim_fields @@ -105,9 +108,33 @@ subroutine allocate_physics_types_simple_constituent_dim_fields(horizontal_dimen if (set_init_val) then eddy_len = nan end if + if (allocated(cool_cat_for_each_const)) then + if (reallocate) then + deallocate(cool_cat_for_each_const) + else + call & + endrun(subname// & + ": cool_cat_for_each_const is already allocated, cannot allocate") + end if + end if + allocate(cool_cat_for_each_const(horizontal_dimension, number_of_ccpp_constituents)) if (set_init_val) then cool_cat_for_each_const = nan end if + if (allocated(cool_default_cat_for_each_const)) then + if (reallocate) then + deallocate(cool_default_cat_for_each_const) + else + call & + endrun(subname// & + ": cool_default_cat_for_each_const is already allocated, cannot allocate") + end if + end if + allocate(cool_default_cat_for_each_const(horizontal_dimension, & + number_of_ccpp_constituents)) + if (set_init_val) then + cool_default_cat_for_each_const = 2.33_kind_phys + end if end subroutine allocate_physics_types_simple_constituent_dim_fields subroutine physics_types_simple_constituent_dim_tstep_init() diff --git a/test/unit/python/sample_files/physics_types_simple_constituent_dim.meta b/test/unit/python/sample_files/physics_types_simple_constituent_dim.meta index 4f6268519..34cc9d7d2 100644 --- a/test/unit/python/sample_files/physics_types_simple_constituent_dim.meta +++ b/test/unit/python/sample_files/physics_types_simple_constituent_dim.meta @@ -25,3 +25,9 @@ units = kg kg-1 s-1 type = real dimensions = (horizontal_dimension, number_of_ccpp_constituents) +[ cool_default_cat_for_each_const ] + standard_name = super_cool_cat_with_default_every_const + long_name = The coolest imaginable tendency per constituent now with a default + units = kg kg-1 s-1 + type = real + dimensions = (horizontal_dimension, number_of_ccpp_constituents) diff --git a/test/unit/python/sample_files/physics_types_simple_initial_value.F90 b/test/unit/python/sample_files/physics_types_simple_initial_value.F90 new file mode 100644 index 000000000..adc1701c2 --- /dev/null +++ b/test/unit/python/sample_files/physics_types_simple_initial_value.F90 @@ -0,0 +1,113 @@ +! +! This work (Common Community Physics Package Framework), identified by +! NOAA, NCAR, CU/CIRES, is free of known copyright restrictions and is +! placed in the public domain. +! +! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +! THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +! IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +! CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +!> +!! @brief Auto-generated Variables for registry source file, physics_types_simple_initial_value +!! +! +module physics_types_simple_initial_value + + use ccpp_kinds, only: kind_phys + + + implicit none + private + +!> \section arg_table_physics_types_simple_initial_value Argument Table +!! \htmlinclude physics_types_simple_initial_value.html + ! theta: Potential temperature + real(kind_phys), public, pointer :: theta(:, :) => NULL() + ! slp: Air pressure at sea level + real(kind_phys), public, allocatable :: slp(:) + ! eddy_len: Eddy length scale + real(kind_phys), public, pointer :: eddy_len(:) => NULL() + +!! public interfaces + public :: allocate_physics_types_simple_initial_value_fields + public :: physics_types_simple_initial_value_tstep_init + +CONTAINS + + subroutine allocate_physics_types_simple_initial_value_fields(horizontal_dimension, & + vertical_layer_dimension, set_init_val_in, reallocate_in) + use shr_infnan_mod, only: nan => shr_infnan_nan, assignment(=) + use cam_abortutils, only: endrun + !! Dummy arguments + integer, intent(in) :: horizontal_dimension + integer, intent(in) :: vertical_layer_dimension + logical, optional, intent(in) :: set_init_val_in + logical, optional, intent(in) :: reallocate_in + + !! Local variables + logical :: set_init_val + logical :: reallocate + character(len=*), parameter :: subname = & + "allocate_physics_types_simple_initial_value_fields" + + ! Set optional argument values + if (present(set_init_val_in)) then + set_init_val = set_init_val_in + else + set_init_val = .true. + end if + if (present(reallocate_in)) then + reallocate = reallocate_in + else + reallocate = .false. + end if + + if (associated(theta)) then + if (reallocate) then + deallocate(theta) + nullify(theta) + else + call endrun(subname//": theta is already associated, cannot allocate") + end if + end if + allocate(theta(horizontal_dimension, vertical_layer_dimension)) + if (set_init_val) then + theta = nan + end if + if (allocated(slp)) then + if (reallocate) then + deallocate(slp) + else + call endrun(subname//": slp is already allocated, cannot allocate") + end if + end if + allocate(slp(horizontal_dimension)) + if (set_init_val) then + slp = 101325.0_kind_phys + end if + if (associated(eddy_len)) then + if (reallocate) then + deallocate(eddy_len) + nullify(eddy_len) + else + call endrun(subname//": eddy_len is already associated, cannot allocate") + end if + end if + allocate(eddy_len(horizontal_dimension)) + if (set_init_val) then + eddy_len = nan + end if + end subroutine allocate_physics_types_simple_initial_value_fields + + subroutine physics_types_simple_initial_value_tstep_init() + + !! Local variables + character(len=*), parameter :: subname = "physics_types_simple_initial_value_tstep_init" + + end subroutine physics_types_simple_initial_value_tstep_init + +end module physics_types_simple_initial_value diff --git a/test/unit/python/sample_files/physics_types_simple_initial_value.meta b/test/unit/python/sample_files/physics_types_simple_initial_value.meta new file mode 100644 index 000000000..f27cdfddd --- /dev/null +++ b/test/unit/python/sample_files/physics_types_simple_initial_value.meta @@ -0,0 +1,21 @@ +[ccpp-table-properties] + name = physics_types_simple_initial_value + type = module +[ccpp-arg-table] + name = physics_types_simple_initial_value + type = module +[ theta ] + standard_name = potential_temperature + units = K + type = real | kind = kind_phys + dimensions = (horizontal_dimension, vertical_layer_dimension) +[ slp ] + standard_name = air_pressure_at_sea_level + units = Pa + type = real | kind = kind_phys + dimensions = (horizontal_dimension) +[ eddy_len ] + standard_name = eddy_length_scale + units = m + type = real | kind = kind_phys + dimensions = (horizontal_dimension) diff --git a/test/unit/python/sample_files/write_init_files/phys_vars_init_check_constituent_dim.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_constituent_dim.F90 index 5a5025baa..4ba8b7b2d 100644 --- a/test/unit/python/sample_files/write_init_files/phys_vars_init_check_constituent_dim.F90 +++ b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_constituent_dim.F90 @@ -33,20 +33,21 @@ module phys_vars_init_check_constituent_dim integer, public, parameter :: PARAM = 2 integer, public, parameter :: READ_FROM_FILE = 3 ! Total number of physics-related variables: - integer, public, parameter :: phys_var_num = 3 + integer, public, parameter :: phys_var_num = 4 integer, public, parameter :: phys_const_num = 16 !Max length of physics-related variable standard names: - integer, public, parameter :: std_name_len = 26 + integer, public, parameter :: std_name_len = 39 ! Max length of input (IC) file variable names: - integer, public, parameter :: ic_name_len = 13 + integer, public, parameter :: ic_name_len = 21 ! Physics-related input variable standard names: - character(len=26), public, protected :: phys_var_stdnames(phys_var_num) = (/ & - 'potential_temperature ', & - 'air_pressure_at_sea_level ', & - 'super_cool_cat_every_const' /) + character(len=39), public, protected :: phys_var_stdnames(phys_var_num) = (/ & + 'potential_temperature ', & + 'air_pressure_at_sea_level ', & + 'super_cool_cat_every_const ', & + 'super_cool_cat_with_default_every_const' /) character(len=36), public, protected :: phys_const_stdnames(phys_const_num) = (/ & "ccpp_constituent_minimum_values ", & @@ -66,19 +67,22 @@ module phys_vars_init_check_constituent_dim "suite_name ", & "suite_part " /) !Array storing all registered IC file input names for each variable: - character(len=13), public, protected :: input_var_names(2, phys_var_num) = reshape((/ & - 'theta ', 'pot_temp ', & - 'slp ', 'sea_lev_pres ', & - 'cool_cat_tend', ' ' /), (/2, phys_var_num/)) + character(len=21), public, protected :: input_var_names(2, phys_var_num) = reshape((/ & + 'theta ', 'pot_temp ', & + 'slp ', 'sea_lev_pres ', & + 'cool_cat_tend ', ' ', & + 'cool_cat_default_tend', ' ' /), (/2, phys_var_num/)) ! Array indicating whether or not variable is protected: logical, public, protected :: protected_vars(phys_var_num)= (/ & + .false., & .false., & .false., & .false. /) ! Variable state (UNINITIALIZED, INTIIALIZED, PARAM or READ_FROM_FILE): integer, public, protected :: initialized_vars(phys_var_num)= (/ & + UNINITIALIZED, & UNINITIALIZED, & UNINITIALIZED, & UNINITIALIZED /) diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_constituent_dim.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_constituent_dim.F90 index c856b3900..e8a5ad51b 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_constituent_dim.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_constituent_dim.F90 @@ -40,7 +40,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia use phys_vars_init_check_constituent_dim, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t use cam_logfile, only: iulog - use physics_types_simple_constituent_dim, only: cool_cat_for_each_const, slp, theta + use physics_types_simple_constituent_dim, only: cool_cat_for_each_const, cool_default_cat_for_each_const, slp, theta ! Dummy arguments type(file_desc_t), intent(inout) :: file @@ -156,6 +156,10 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call read_constituent_dimensioned_field(const_props, file, 'super_cool_cat_every_const', input_var_names(:,name_idx), & timestep, cool_cat_for_each_const) + case ('super_cool_cat_with_default_every_const') + call read_constituent_dimensioned_field(const_props, file, 'super_cool_cat_with_default_every_const', & + input_var_names(:,name_idx), timestep, cool_default_cat_for_each_const) + end select !read variables end select !special indices @@ -239,7 +243,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, use cam_pio_utils, only: cam_pio_openfile, cam_pio_closefile use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t use phys_vars_init_check_constituent_dim, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len - use physics_types_simple_constituent_dim, only: cool_cat_for_each_const, slp, theta + use physics_types_simple_constituent_dim, only: cool_cat_for_each_const, cool_default_cat_for_each_const, slp, theta ! Dummy arguments character(len=SHR_KIND_CL), intent(in) :: file_name @@ -335,10 +339,6 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, call check_field(file, input_var_names(:,name_idx), timestep, slp, 'air_pressure_at_sea_level', min_difference, min_relative_value, & is_first, diff_found) - case ('super_cool_cat_every_const') - call check_field(file, input_var_names(:,name_idx), timestep, cool_cat_for_each_const, 'super_cool_cat_every_const', & - min_difference, min_relative_value, is_first, diff_found) - end select !check variables if (diff_found) then overall_diff_found = .true. diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_initial_value.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_initial_value.F90 index 266a582da..c21a8609c 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_initial_value.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_initial_value.F90 @@ -40,7 +40,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia use phys_vars_init_check_initial_value, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t use cam_logfile, only: iulog - use physics_types_simple, only: slp, theta + use physics_types_simple_initial_value, only: slp, theta ! Dummy arguments type(file_desc_t), intent(inout) :: file @@ -235,7 +235,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, use cam_pio_utils, only: cam_pio_openfile, cam_pio_closefile use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t use phys_vars_init_check_initial_value, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len - use physics_types_simple, only: slp, theta + use physics_types_simple_initial_value, only: slp, theta ! Dummy arguments character(len=SHR_KIND_CL), intent(in) :: file_name diff --git a/test/unit/python/sample_files/write_init_files/simple_reg_constituent_dim.xml b/test/unit/python/sample_files/write_init_files/simple_reg_constituent_dim.xml index 8d306af58..b2adabee3 100644 --- a/test/unit/python/sample_files/write_init_files/simple_reg_constituent_dim.xml +++ b/test/unit/python/sample_files/write_init_files/simple_reg_constituent_dim.xml @@ -20,10 +20,17 @@ + units="kg kg-1 s-1" type="real" kind="kind_phys" allocatable="allocatable"> The coolest imaginable tendency per constituent horizontal_dimension number_of_ccpp_constituents cool_cat_tend + + The coolest imaginable tendency per constituent now with a default + horizontal_dimension number_of_ccpp_constituents + 2.33_kind_phys + cool_cat_default_tend + diff --git a/test/unit/python/sample_files/write_init_files/simple_reg_initial_value.xml b/test/unit/python/sample_files/write_init_files/simple_reg_initial_value.xml index 4384c4ed9..019bda879 100644 --- a/test/unit/python/sample_files/write_init_files/simple_reg_initial_value.xml +++ b/test/unit/python/sample_files/write_init_files/simple_reg_initial_value.xml @@ -1,7 +1,7 @@ - + @@ -9,7 +9,7 @@ theta pot_temp + units="Pa" type="real" kind="kind_phys" allocatable="allocatable"> horizontal_dimension 101325.0_kind_phys diff --git a/test/unit/python/sample_files/write_init_files/temp_adjust_constituent_dim.F90 b/test/unit/python/sample_files/write_init_files/temp_adjust_constituent_dim.F90 index d9d8523a3..9dd3d099f 100644 --- a/test/unit/python/sample_files/write_init_files/temp_adjust_constituent_dim.F90 +++ b/test/unit/python/sample_files/write_init_files/temp_adjust_constituent_dim.F90 @@ -18,7 +18,8 @@ MODULE temp_adjust !! \htmlinclude arg_table_temp_adjust_run.html !! SUBROUTINE temp_adjust_run(nbox, lev, temp_layer, & - slp, cool_cat_for_each_const, timestep, errmsg, errflg) + slp, cool_cat_for_each_const, cool_cat_default_for_each_const, & + timestep, errmsg, errflg) !---------------------------------------------------------------- IMPLICIT NONE !---------------------------------------------------------------- @@ -27,6 +28,7 @@ SUBROUTINE temp_adjust_run(nbox, lev, temp_layer, & REAL(kind_phys), intent(inout) :: temp_layer(:, :) real(kind_phys), intent(in) :: slp(:) real(kind_phys), intent(inout) :: cool_cat_for_each_const(:,:) + real(kind_phys), intent(in) :: cool_cat_default_for_each_const(:,:) real(kind_phys), intent(in) :: timestep character(len=512), intent(out) :: errmsg integer, intent(out) :: errflg diff --git a/test/unit/python/sample_files/write_init_files/temp_adjust_constituent_dim.meta b/test/unit/python/sample_files/write_init_files/temp_adjust_constituent_dim.meta index f9f07e3fc..09f531380 100644 --- a/test/unit/python/sample_files/write_init_files/temp_adjust_constituent_dim.meta +++ b/test/unit/python/sample_files/write_init_files/temp_adjust_constituent_dim.meta @@ -37,6 +37,13 @@ type = real kind = kind_phys intent = inout +[ cool_cat_default_for_each_const ] + standard_name = super_cool_cat_with_default_every_const + units = kg kg-1 s-1 + dimensions = (horizontal_loop_extent, number_of_ccpp_constituents) + type = real + kind = kind_phys + intent = in [ timestep ] standard_name = timestep_for_physics long_name = time step diff --git a/test/unit/python/test_write_init_files.py b/test/unit/python/test_write_init_files.py index 3683c3614..b13035832 100644 --- a/test/unit/python/test_write_init_files.py +++ b/test/unit/python/test_write_init_files.py @@ -292,7 +292,7 @@ def test_simple_initial_value_write_init(self): # Setup registry inputs: filename = os.path.join(_INIT_SAMPLES_DIR, "simple_reg_initial_value.xml") - out_source_name = "physics_types_simple" + out_source_name = "physics_types_simple_initial_value" out_source = os.path.join(_TMP_DIR, out_source_name + '.F90') out_meta = os.path.join(_TMP_DIR, out_source_name + '.meta') From 1a0e42352ecec2f95336276a335502775f17c6e4 Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Thu, 26 Jun 2025 18:33:50 -0400 Subject: [PATCH 04/11] Fix edge case of no base name found --- src/physics/utils/physics_data.F90 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/physics/utils/physics_data.F90 b/src/physics/utils/physics_data.F90 index ab5829ad2..55808db5d 100644 --- a/src/physics/utils/physics_data.F90 +++ b/src/physics/utils/physics_data.F90 @@ -445,6 +445,10 @@ subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, ba end do const_idx_loop end do base_idx_loop + if(.not. var_found .and. error_on_not_found_local) then + call endrun(subname//'Required constituent-dimensioned variables not found: No match for ' // trim(std_name)) + end if + ! Once base_idx is identified, use it in the actual constituent loop: const_read_loop: do const_idx = 1, size(const_props) ! Get constituent short name From ded4f16550155c44bbbf4d47bc753c4e5b5f6dd5 Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Wed, 2 Jul 2025 11:30:35 -0400 Subject: [PATCH 05/11] Address review comments --- src/data/write_init_files.py | 34 +++++++------------ src/physics/utils/physics_data.F90 | 16 ++++----- .../phys_vars_init_check_constituent_dim.F90 | 2 +- .../physics_inputs_constituent_dim.F90 | 2 +- .../simple_reg_constituent_dim.xml | 2 +- test/unit/python/test_write_init_files.py | 4 +-- 6 files changed, 24 insertions(+), 36 deletions(-) diff --git a/src/data/write_init_files.py b/src/data/write_init_files.py index ed6846f2e..14226c64e 100644 --- a/src/data/write_init_files.py +++ b/src/data/write_init_files.py @@ -746,7 +746,6 @@ def get_dimension_info(hvar): vdim_name = None legal_dims = False fail_reason = "" - has_constituent_read = False dims = hvar.get_dimensions() levnm = hvar.has_vertical_dimension() @@ -800,7 +799,6 @@ def get_dimension_info(hvar): # # In this case, override legal_dims as this case will be handled separately if has_constituent_dim: - has_constituent_read = True legal_dims = True fail_reason = "" # end if @@ -829,7 +827,7 @@ def get_dimension_info(hvar): # end if # end if - return vdim_name, legal_dims, fail_reason, has_constituent_read + return vdim_name, legal_dims, fail_reason, has_constituent_dim def write_phys_read_subroutine(outfile, host_dict, host_vars, host_imports, phys_check_fname_str, constituent_set, @@ -888,30 +886,22 @@ def write_phys_read_subroutine(outfile, host_dict, host_vars, host_imports, if has_constituent_read: # Special case for constituent-dimension variables. call_str = f"call read_constituent_dimensioned_field(const_props, file, '{var_stdname}', input_var_names(:,name_idx), " - if levnm is not None: - call_str += f"'{levnm}', " - # end if - - err_on_not_found_string = "" - if var_stdname in vars_init_value: - # if initial value is available, do not throw error when not found in initial condition file. - err_on_not_found_string = ", error_on_not_found=.false." - # end if - call_str += f"timestep, {var_locname}{err_on_not_found_string})" else: # Replace vertical dimension with local name call_str = "call read_field(file, " + \ f"'{var_stdname}', input_var_names(:,name_idx), " - if levnm is not None: - call_str += f"'{levnm}', " - # end if - err_on_not_found_string = "" - if var_stdname in vars_init_value: - # if initial value is available, do not throw error when not found in initial condition file. - err_on_not_found_string = ", error_on_not_found=.false." - # end if - call_str += f"timestep, {var_locname}{err_on_not_found_string})" # end if + + if levnm is not None: + call_str += f"'{levnm}', " + # end if + + err_on_not_found_string = "" + if var_stdname in vars_init_value: + # if initial value is available, do not throw error when not found in initial condition file. + err_on_not_found_string = ", error_on_not_found=.false." + # end if + call_str += f"timestep, {var_locname}{err_on_not_found_string})" else: # if initial value is assigned, then it can be ignored if var_stdname in vars_init_value: diff --git a/src/physics/utils/physics_data.F90 b/src/physics/utils/physics_data.F90 index 55808db5d..b6e6d48d3 100644 --- a/src/physics/utils/physics_data.F90 +++ b/src/physics/utils/physics_data.F90 @@ -336,7 +336,7 @@ subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, ba use pio, only: file_desc_t, var_desc_t use spmd_utils, only: masterproc use cam_pio_utils, only: cam_pio_find_var - use cam_abortutils, only: endrun + use cam_abortutils, only: endrun, check_allocate use cam_logfile, only: iulog use cam_field_read, only: cam_read_field use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t @@ -385,9 +385,7 @@ subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, ba ! Allocate temporary buffer allocate(buffer(size(field_array, 1)), stat=ierr) - if (ierr /= 0) then - call endrun(subname//'Failed to allocate buffer') - end if + call check_allocate(ierr, subname, 'buffer') !REMOVECAM: ! Because the constituent properties pointer contains standard names, and not input constituent names @@ -395,16 +393,15 @@ subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, ba ! we have to construct a mapping of the standard names to the short input IC file names ! When CAM is retired and only standard names are used for constituents, this mapping can be removed. allocate(constituent_short_names(size(const_props)), stat=ierr) - if (ierr /= 0) then - call endrun(subname//'Failed to allocate constituent_short_names') - end if + call check_allocate(ierr, subname, 'constituent_short_names') const_shortmap_loop: do const_idx = 1, size(const_props) ! Get constituent standard name. call const_props(const_idx)%standard_name(constituent_std_name) ! Check if constituent standard name is in the registry to look up its IC name - ! n.b. this assumes that the first IC name is the short name + ! n.b. this assumes that the first IC name specified in the registry for this constituent + ! is the short name const_input_idx = -1 phys_inputvar_loop: do n = 1, phys_var_num if (trim(phys_var_stdnames(n)) == trim(constituent_std_name)) then @@ -510,7 +507,8 @@ subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, ba ! Check if we should fail due to missing variables if (any_missing .and. error_on_not_found_local) then - call endrun(subname//'Required constituent-dimensioned variables not found: ' // trim(missing_vars)) + call endrun(subname//'Required constituent-dimensioned variables not found: ' // trim(missing_vars) // & + 'Make sure the constituent short name is the first in the list in the registry.') end if ! Mark the base variable as read from file (only if no errors) diff --git a/test/unit/python/sample_files/write_init_files/phys_vars_init_check_constituent_dim.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_constituent_dim.F90 index 4ba8b7b2d..d656f2009 100644 --- a/test/unit/python/sample_files/write_init_files/phys_vars_init_check_constituent_dim.F90 +++ b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_constituent_dim.F90 @@ -70,7 +70,7 @@ module phys_vars_init_check_constituent_dim character(len=21), public, protected :: input_var_names(2, phys_var_num) = reshape((/ & 'theta ', 'pot_temp ', & 'slp ', 'sea_lev_pres ', & - 'cool_cat_tend ', ' ', & + 'cool_cat_tend ', 'pbuf_COOL_CAT_TEND ', & 'cool_cat_default_tend', ' ' /), (/2, phys_var_num/)) ! Array indicating whether or not variable is protected: diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_constituent_dim.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_constituent_dim.F90 index e8a5ad51b..ae4d58420 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_constituent_dim.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_constituent_dim.F90 @@ -158,7 +158,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia case ('super_cool_cat_with_default_every_const') call read_constituent_dimensioned_field(const_props, file, 'super_cool_cat_with_default_every_const', & - input_var_names(:,name_idx), timestep, cool_default_cat_for_each_const) + input_var_names(:,name_idx), timestep, cool_default_cat_for_each_const, error_on_not_found=.false.) end select !read variables end select !special indices diff --git a/test/unit/python/sample_files/write_init_files/simple_reg_constituent_dim.xml b/test/unit/python/sample_files/write_init_files/simple_reg_constituent_dim.xml index b2adabee3..2563d7f62 100644 --- a/test/unit/python/sample_files/write_init_files/simple_reg_constituent_dim.xml +++ b/test/unit/python/sample_files/write_init_files/simple_reg_constituent_dim.xml @@ -23,7 +23,7 @@ units="kg kg-1 s-1" type="real" kind="kind_phys" allocatable="allocatable"> The coolest imaginable tendency per constituent horizontal_dimension number_of_ccpp_constituents - cool_cat_tend + cool_cat_tend pbuf_COOL_CAT_TEND diff --git a/test/unit/python/test_write_init_files.py b/test/unit/python/test_write_init_files.py index b13035832..a1a2f7a97 100644 --- a/test/unit/python/test_write_init_files.py +++ b/test/unit/python/test_write_init_files.py @@ -1366,7 +1366,7 @@ def test_simple_constituent_dimensioned_var_write_init(self): check_init_out, phys_input_out]) # Generate registry files: - _, _, ic_names, constituents, _ = gen_registry(filename, 'se', _TMP_DIR, 3, + _, _, ic_names, constituents, vars_init_value = gen_registry(filename, 'se', _TMP_DIR, 3, _SRC_MOD_DIR, _CAM_ROOT, loglevel=logging.ERROR, error_on_no_validate=True) @@ -1386,7 +1386,7 @@ def test_simple_constituent_dimensioned_var_write_init(self): cap_database = capgen(run_env, return_db=True) # Generate physics initialization files: - retmsg = write_init.write_init_files(cap_database, ic_names, constituents, [], _TMP_DIR, + retmsg = write_init.write_init_files(cap_database, ic_names, constituents, vars_init_value, _TMP_DIR, find_file, _INC_SEARCH_DIRS, 3, logger, phys_check_filename=vic_name, From 6bb43b0928f4179143b479a81f4c014cdbf91932 Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Wed, 16 Jul 2025 12:25:04 -0400 Subject: [PATCH 06/11] Remove wrongly added temporary files (they are incorrect and in the wrong directory) --- .../phys_vars_init_check_constituent_dim.F90 | 245 ----------- .../physics_inputs_constituent_dim.F90 | 411 ------------------ 2 files changed, 656 deletions(-) delete mode 100644 test/unit/python/sample_files/phys_vars_init_check_constituent_dim.F90 delete mode 100644 test/unit/python/sample_files/physics_inputs_constituent_dim.F90 diff --git a/test/unit/python/sample_files/phys_vars_init_check_constituent_dim.F90 b/test/unit/python/sample_files/phys_vars_init_check_constituent_dim.F90 deleted file mode 100644 index 5a5025baa..000000000 --- a/test/unit/python/sample_files/phys_vars_init_check_constituent_dim.F90 +++ /dev/null @@ -1,245 +0,0 @@ -! -! This work (Common Community Physics Package Framework), identified by -! NOAA, NCAR, CU/CIRES, is free of known copyright restrictions and is -! placed in the public domain. -! -! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -! THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -! IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -! CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -!> -!! @brief Auto-generated Initialization-checking source file -!! -! -module phys_vars_init_check_constituent_dim - - - implicit none - private - -!! public interfaces - public :: mark_as_initialized - public :: mark_as_read_from_file - public :: is_initialized - public :: is_read_from_file - -!! Parameterized initialized_vars options - order matters - integer, public, parameter :: UNINITIALIZED = 0 - integer, public, parameter :: INITIALIZED = 1 - integer, public, parameter :: PARAM = 2 - integer, public, parameter :: READ_FROM_FILE = 3 - ! Total number of physics-related variables: - integer, public, parameter :: phys_var_num = 3 - integer, public, parameter :: phys_const_num = 16 - - !Max length of physics-related variable standard names: - integer, public, parameter :: std_name_len = 26 - - ! Max length of input (IC) file variable names: - integer, public, parameter :: ic_name_len = 13 - - ! Physics-related input variable standard names: - character(len=26), public, protected :: phys_var_stdnames(phys_var_num) = (/ & - 'potential_temperature ', & - 'air_pressure_at_sea_level ', & - 'super_cool_cat_every_const' /) - - character(len=36), public, protected :: phys_const_stdnames(phys_const_num) = (/ & - "ccpp_constituent_minimum_values ", & - "ccpp_constituent_properties ", & - "ccpp_constituent_tendencies ", & - "ccpp_constituents ", & - "ccpp_error_code ", & - "ccpp_error_message ", & - "do_log_output ", & - "log_output_unit ", & - "mpi_communicator ", & - "mpi_rank ", & - "mpi_root ", & - "number_of_ccpp_advected_constituents", & - "number_of_ccpp_constituents ", & - "number_of_mpi_tasks ", & - "suite_name ", & - "suite_part " /) - !Array storing all registered IC file input names for each variable: - character(len=13), public, protected :: input_var_names(2, phys_var_num) = reshape((/ & - 'theta ', 'pot_temp ', & - 'slp ', 'sea_lev_pres ', & - 'cool_cat_tend', ' ' /), (/2, phys_var_num/)) - - ! Array indicating whether or not variable is protected: - logical, public, protected :: protected_vars(phys_var_num)= (/ & - .false., & - .false., & - .false. /) - - ! Variable state (UNINITIALIZED, INTIIALIZED, PARAM or READ_FROM_FILE): - integer, public, protected :: initialized_vars(phys_var_num)= (/ & - UNINITIALIZED, & - UNINITIALIZED, & - UNINITIALIZED /) - - -CONTAINS - - subroutine mark_as_initialized(varname) - - ! This subroutine marks the variable, , as - ! INITIALIZED in the `initialized_vars` array, - ! which means any initialization check should - ! now return True. - - ! Dummy argument - character(len=*), intent(in) :: varname !Variable name being marked - - ! Local variable - integer :: stdnam_idx !Standard name array index - - ! Search for in the standard name array: - do stdnam_idx = 1, phys_var_num - if (trim(phys_var_stdnames(stdnam_idx)) == trim(varname)) then - ! Only set to INITIALIZED if state is UNINITIALIZED - if (initialized_vars(stdnam_idx) < PARAM) then - initialized_vars(stdnam_idx) = INITIALIZED - end if - exit ! Exit loop once variable has been found and initialized - end if - end do - - ! No match is not an error because only - ! contains variables required by a physics suite. - - end subroutine mark_as_initialized - - subroutine mark_as_read_from_file(varname) - - ! This subroutine marks the varible, , as READ_FROM_FILE in the - ! initialized_vars array - - use cam_abortutils, only: endrun - - ! Dummy argument - character(len=*), intent(in) :: varname ! Variable name being marked - - ! Local variables - integer :: stdnam_idx ! Standard name array index - logical :: found_var ! .true. if is in arr. - character(len=*), parameter :: subname = 'mark_as_read_from_file' - - found_var = .false. - ! Set variable to READ_FROM_FILE: - do stdnam_idx = 1, phys_var_num - if (trim(phys_var_stdnames(stdnam_idx)) == trim(varname)) then - ! It is an error if the variable has already been set to PARAM - if (initialized_vars(stdnam_idx) == PARAM) then - call endrun("Variable '"//trim(varname)// & - "' was read from file, but is a parameter") - end if - initialized_vars(stdnam_idx) = READ_FROM_FILE - - ! Indicate variable has been found: - found_var = .true. - exit ! Exit loop once variable has been found and marked - end if - end do - - if (.not. found_var) then - ! This condition is an internal error, it should not happen - call endrun(subname//": Variable '"//trim(varname)// & - "' is missing from phys_var_stdnames array.") - end if - - end subroutine mark_as_read_from_file - - logical function is_initialized(varname) - - ! This function checks if the variable, , is already - ! initialized according to the 'initialized_vars' array. - - use cam_abortutils, only: endrun - - ! Dummy argument - character(len=*), intent(in) :: varname ! Variable name being checked - - ! Local variables - integer :: stdnam_idx ! Standard name array index - logical :: found ! Check that was found - character(len=*), parameter :: subname = 'is_initialized: ' - - is_initialized = .false. - found = .false. - - ! Check if variable is initialized (PARAM, INITIALIZED, or READ_FROM_FILE) - do stdnam_idx = 1, phys_var_num - if (trim(phys_var_stdnames(stdnam_idx)) == trim(varname)) then - is_initialized = (initialized_vars(stdnam_idx) > UNINITIALIZED) - found = .true. - exit ! Exit loop once variable has been found and checked - end if - end do - - if (.not. found) then - ! This condition is an internal error, it should not happen - call endrun(subname//": Variable '"//trim(varname)// & - "' is missing from phys_var_stdnames array.") - end if - - end function is_initialized - - subroutine is_read_from_file(varname, is_read, stdnam_idx_out) - - ! This subroutine checks if the variable, , is read from - ! file according to the 'initialized_vars' array. - - use cam_abortutils, only: endrun - - ! Dummy arguments - character(len=*), intent(in) :: varname ! Variable name being checked - logical, intent(out) :: is_read ! Set to .true. if from file - integer, optional, intent(out) :: stdnam_idx_out - - ! Local variables - - integer :: stdnam_idx ! Standard name array index - logical :: found ! Check that was found - character(len=*), parameter :: subname = 'is_read_from_file: ' - - is_read = .false. - found = .false. - - ! Return .true. if the variable's status is READ_FROM_FILE: - do stdnam_idx = 1, phys_var_num - if (trim(phys_var_stdnames(stdnam_idx)) == trim(varname)) then - is_read = (initialized_vars(stdnam_idx) == READ_FROM_FILE) - ! Mark as found: - found = .true. - exit ! Exit loop once variable has been found and checked - end if - end do - - if (.not. found) then - ! Check to see if this is an internally-protected variable - do stdnam_idx = 1, phys_const_num - if (trim(phys_const_stdnames(stdnam_idx)) == trim(varname)) then - found = .true. - exit ! Exit loop once variable has been found - end if - end do - end if - - if (.not. found) then - ! This condition is an internal error, it should not happen - call endrun(subname//": Variable '"//trim(varname)// & - "' is missing from phys_var_stdnames array.") - end if - if (present(stdnam_idx_out)) then - stdnam_idx_out = stdnam_idx - end if - - end subroutine is_read_from_file - -end module phys_vars_init_check_constituent_dim diff --git a/test/unit/python/sample_files/physics_inputs_constituent_dim.F90 b/test/unit/python/sample_files/physics_inputs_constituent_dim.F90 deleted file mode 100644 index 80e44a8ef..000000000 --- a/test/unit/python/sample_files/physics_inputs_constituent_dim.F90 +++ /dev/null @@ -1,411 +0,0 @@ -! -! This work (Common Community Physics Package Framework), identified by -! NOAA, NCAR, CU/CIRES, is free of known copyright restrictions and is -! placed in the public domain. -! -! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -! THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -! IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -! CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -!> -!! @brief Auto-generated Initial conditions source file, physics_inputs_constituent_dim.F90 -!! -! -module physics_inputs_constituent_dim - - - implicit none - private - - -!! public interfaces - public :: physics_read_data - public :: physics_check_data - -CONTAINS - - subroutine physics_read_data(file, suite_names, timestep, read_initialized_variables) - use pio, only: file_desc_t - use cam_abortutils, only: endrun - use spmd_utils, only: masterproc - use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX - use physics_data, only: read_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx - use physics_data, only: read_constituent_dimensioned_field - use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties - use ccpp_kinds, only: kind_phys - use phys_vars_init_check_constituent_dim, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len, is_initialized - use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t - use cam_logfile, only: iulog - use physics_types_simple_constituent_dim, only: cool_cat_for_each_const, slp, theta - - ! Dummy arguments - type(file_desc_t), intent(inout) :: file - character(len=SHR_KIND_CS), intent(in) :: suite_names(:) !Names of CCPP suites - integer, intent(in) :: timestep - logical, optional, intent(in) :: read_initialized_variables - - ! Local variables: - - ! Character array containing all CCPP-required variable standard names: - character(len=std_name_len), allocatable :: ccpp_required_data(:) - - ! Strings which store names of any missing or non-initialized vars: - character(len=SHR_KIND_CL) :: missing_required_vars - character(len=SHR_KIND_CL) :: protected_non_init_vars - - character(len=SHR_KIND_CX) :: errmsg !CCPP framework error message - integer :: errflg !CCPP framework error flag - integer :: n !Loop control variable - integer :: name_idx !Input variable array index - integer :: constituent_idx !Constituent table index - integer :: const_input_idx !input_var_names index for a consituent - integer :: req_idx !Required variable array index - integer :: suite_idx !Suite array index - character(len=2) :: sep !String separator used to print err messages - character(len=2) :: sep2 !String separator used to print err messages - character(len=2) :: sep3 !String separator used to print err messages - real(kind=kind_phys), pointer :: field_data_ptr(:,:,:) - logical :: var_found !Bool to determine if consituent found in data files - character(len=std_name_len) :: std_name !Variable to hold constiutent standard name - - ! Fields needed for getting default data value for constituents - type(ccpp_constituent_prop_ptr_t), pointer :: const_props(:) - real(kind=kind_phys) :: constituent_default_value - real(kind=kind_phys) :: constituent_min_value - integer :: constituent_errflg - character(len=512) :: constituent_errmsg - logical :: constituent_has_default - - ! Logical to default optional argument to False: - logical :: use_init_variables - - ! Initalize missing and non-initialized variables strings: - missing_required_vars = ' ' - protected_non_init_vars = ' ' - sep = '' - sep2 = '' - sep3 = '' - - ! Initialize use_init_variables based on whether it was input to function: - if (present(read_initialized_variables)) then - use_init_variables = read_initialized_variables - else - use_init_variables = .false. - end if - - ! Loop over CCPP physics/chemistry suites: - do suite_idx = 1, size(suite_names, 1) - - ! Search for all needed CCPP input variables, so that they can be read from input file if need be: - call ccpp_physics_suite_variables(suite_names(suite_idx), ccpp_required_data, errmsg, errflg, input_vars=.true., output_vars=.false.) - - ! Loop over all required variables and read from file if uninitialized: - do req_idx = 1, size(ccpp_required_data, 1) - - ! Find IC file input name array index for required variable: - name_idx = find_input_name_idx(ccpp_required_data(req_idx), use_init_variables, constituent_idx) - - ! Check for special index values: - select case (name_idx) - - case (init_mark_idx) - - ! If variable is already initialized, then do nothing. - - case (no_exist_idx) - - ! If an index was never found, then save variable name and check the rest of the variables, after which the model simulation will - ! end: - missing_required_vars(len_trim(missing_required_vars)+1:) = trim(sep)//trim(ccpp_required_data(req_idx)) - - ! Update character separator to now include comma: - sep = ', ' - - case (prot_no_init_idx) - - ! If an index was found for a protected variable, but that variable was never marked as initialized, then save the variable name - ! and check the rest of the variables, after which the model simulation will end: - protected_non_init_vars(len_trim(protected_non_init_vars)+1:) = trim(sep2)//trim(ccpp_required_data(req_idx)) - - ! Update character separator to now include comma: - sep2 = ', ' - - case (const_idx) - - ! If an index was found in the constituent hash table, then do nothing, this will be handled later - - case default - - ! Read variable from IC file: - - select case (trim(phys_var_stdnames(name_idx))) - case ('potential_temperature') - call read_field(file, 'potential_temperature', input_var_names(:,name_idx), 'lev', timestep, theta) - - case ('air_pressure_at_sea_level') - call read_field(file, 'air_pressure_at_sea_level', input_var_names(:,name_idx), timestep, slp) - - end select !read variables - end select !special indices - - end do !Suite-required variables - - ! End simulation if there are missing input variables that are required: - if (len_trim(missing_required_vars) > 0) then - call endrun("Required variables missing from registered list of input variables: "//& - trim(missing_required_vars)) - end if - - ! End simulation if there are protected input variables that are not initialized: - if (len_trim(protected_non_init_vars) > 0) then - call endrun("Required, protected input variables are not initialized: "//& - trim(protected_non_init_vars)) - end if - - ! Deallocate required variables array for use in next suite: - deallocate(ccpp_required_data) - - end do !CCPP suites - - ! Read in constituent variables if not using init variables - field_data_ptr => cam_constituents_array() - const_props => cam_model_const_properties() - - ! Iterate over all registered constituents - do constituent_idx = 1, size(const_props) - var_found = .false. - ! Check if constituent standard name in registered SIMA standard names list: - call const_props(constituent_idx)%standard_name(std_name) - if(any(phys_var_stdnames == trim(std_name))) then - ! Don't read the variable in if it's already initialized - if (is_initialized(std_name)) then - cycle - end if - ! Find array index to extract correct input names: - do n=1, phys_var_num - if(trim(phys_var_stdnames(n)) == trim(std_name)) then - const_input_idx = n - exit - end if - end do - call read_field(file, std_name, input_var_names(:,const_input_idx), 'lev', timestep, field_data_ptr(:,:,constituent_idx), & - mark_as_read=.false., error_on_not_found=.false., var_found=var_found) - else - ! If not in standard names list, then just use constituent name as input file name: - call read_field(file, std_name, [std_name], 'lev', timestep, field_data_ptr(:,:,constituent_idx), mark_as_read=.false., & - error_on_not_found=.false., var_found=var_found) - end if - if(.not. var_found) then - constituent_has_default = .false. - call const_props(constituent_idx)%has_default(constituent_has_default, constituent_errflg, constituent_errmsg) - if (constituent_errflg /= 0) then - call endrun(constituent_errmsg, file=__FILE__, line=__LINE__) - end if - if (.not. constituent_has_default) then - ! Intialize to constituent's configured minimum value - call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) - field_data_ptr(:,:,constituent_idx) = constituent_min_value - if (masterproc) then - write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' - end if - end if - end if - end do - - ! Read in non-constituent variables with constituent dimensions - ! Handle variables like cam_in_cflx that are dimensioned by constituents - ! but are not constituents themselves - do req_idx = 1, size(ccpp_required_data, 1) - name_idx = find_input_name_idx(ccpp_required_data(req_idx), use_init_variables, constituent_idx) - if (name_idx > 0 .and. name_idx <= phys_var_num) then - select case (trim(phys_var_stdnames(name_idx))) - call read_constituent_dimensioned_field(file, 'super_cool_cat_every_const', - timestep, cool_cat_for_each_const, const_props) - - end select - end if - end do - end subroutine physics_read_data - - subroutine physics_check_data(file_name, suite_names, timestep, min_difference, min_relative_value, err_on_fail) - use pio, only: file_desc_t, pio_nowrite - use cam_abortutils, only: endrun - use shr_kind_mod, only: SHR_KIND_CS, SHR_KIND_CL, SHR_KIND_CX - use physics_data, only: check_field, find_input_name_idx, no_exist_idx, init_mark_idx, prot_no_init_idx, const_idx - use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_advected_constituents_array, cam_model_const_properties - use cam_constituents, only: const_get_index - use ccpp_kinds, only: kind_phys - use cam_logfile, only: iulog - use spmd_utils, only: masterproc - use phys_vars_init_check, only: is_read_from_file - use ioFileMod, only: cam_get_file - use cam_pio_utils, only: cam_pio_openfile, cam_pio_closefile - use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t - use phys_vars_init_check_constituent_dim, only: phys_var_num, phys_var_stdnames, input_var_names, std_name_len - use physics_types_simple_constituent_dim, only: cool_cat_for_each_const, slp, theta - - ! Dummy arguments - character(len=SHR_KIND_CL), intent(in) :: file_name - character(len=SHR_KIND_CS), intent(in) :: suite_names(:) !Names of CCPP suites - integer, intent(in) :: timestep - real(kind_phys), intent(in) :: min_difference - real(kind_phys), intent(in) :: min_relative_value - logical, intent(in) :: err_on_fail - - ! Local variables: - - ! Character array containing all CCPP-required variable standard names: - character(len=std_name_len), allocatable :: ccpp_required_data(:) - - ! Strings which store names of any missing or non-initialized vars: - character(len=SHR_KIND_CL) :: missing_required_vars - character(len=SHR_KIND_CL) :: protected_non_init_vars - character(len=SHR_KIND_CL) :: missing_input_names - - character(len=SHR_KIND_CX) :: errmsg !CCPP framework error message - integer :: errflg !CCPP framework error flag - integer :: n !Loop control variable - integer :: name_idx !Input variable array index - integer :: constituent_idx !Index of variable in constituent array - integer :: const_input_idx !input_var_names index for a consituent - integer :: req_idx !Required variable array index - integer :: suite_idx !Suite array index - character(len=SHR_KIND_CL) :: ncdata_check_loc - type(file_desc_t), pointer :: file - logical :: file_found - logical :: is_first - logical :: is_read - logical :: diff_found - logical :: overall_diff_found - character(len=std_name_len) :: std_name !Variable to hold constiutent standard name - real(kind=kind_phys), pointer :: field_data_ptr(:,:,:) - type(ccpp_constituent_prop_ptr_t), pointer :: const_props(:) - - ! Initalize missing and non-initialized variables strings: - missing_required_vars = ' ' - protected_non_init_vars = ' ' - missing_input_names = ' ' - nullify(file) - is_first = .true. - overall_diff_found = .false. - - if (masterproc) then - write(iulog,*) '' - write(iulog,*) '********** Physics Check Data Results **********' - write(iulog,*) '' - write(iulog,*) 'TIMESTEP: ', timestep - end if - if (file_name == 'UNSET') then - write(iulog,*) 'WARNING: Namelist variable ncdata_check is UNSET.', ' Model will run, but physics check data will not be printed' - return - end if - ! Open check file: - call cam_get_file(file_name, ncdata_check_loc, allow_fail=.true., lexist=file_found, log_info=.false.) - if (.not. file_found) then - write(iulog,*) 'WARNING: Check file ', trim(file_name), ' not found. Model will run, but physics check data will not be printed' - return - end if - allocate(file) - call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) - ! Loop over CCPP physics/chemistry suites: - do suite_idx = 1, size(suite_names, 1) - - ! Search for all needed CCPP input variables, so that they can be read from input file if need be: - call ccpp_physics_suite_variables(suite_names(suite_idx), ccpp_required_data, errmsg, errflg, input_vars=.true., output_vars=.false.) - - ! Loop over all required variables as specified by CCPP suite: - do req_idx = 1, size(ccpp_required_data, 1) - - ! First check if the required variable is a constituent: - call const_get_index(ccpp_required_data(req_idx), constituent_idx, abort=.false., warning=.false.) - if (constituent_idx > -1) then - cycle - else - ! The required variable is not a constituent. Check if the variable was read from a file - ! Find IC file input name array index for required variable: - call is_read_from_file(ccpp_required_data(req_idx), is_read, stdnam_idx_out=name_idx) - if (.not. is_read) then - cycle - end if - ! Check variable vs input check file: - - select case (trim(phys_var_stdnames(name_idx))) - case ('potential_temperature') - call check_field(file, input_var_names(:,name_idx), 'lev', timestep, theta, 'potential_temperature', min_difference, & - min_relative_value, is_first, diff_found) - - case ('air_pressure_at_sea_level') - call check_field(file, input_var_names(:,name_idx), timestep, slp, 'air_pressure_at_sea_level', min_difference, min_relative_value, & - is_first, diff_found) - - case ('super_cool_cat_every_const') - call check_field(file, input_var_names(:,name_idx), timestep, cool_cat_for_each_const, 'super_cool_cat_every_const', & - min_difference, min_relative_value, is_first, diff_found) - - end select !check variables - if (diff_found) then - overall_diff_found = .true. - end if - end if !check if constituent - end do !Suite-required variables - - ! Deallocate required variables array for use in next suite: - deallocate(ccpp_required_data) - - end do !CCPP suites - - ! Check constituent variables - field_data_ptr => cam_advected_constituents_array() - const_props => cam_model_const_properties() - - do constituent_idx = 1, size(const_props) - ! Check if constituent standard name in registered SIMA standard names list: - call const_props(constituent_idx)%standard_name(std_name) - if(any(phys_var_stdnames == std_name)) then - ! Find array index to extract correct input names: - do n=1, phys_var_num - if(trim(phys_var_stdnames(n)) == trim(std_name)) then - const_input_idx = n - exit - end if - end do - call check_field(file, input_var_names(:,const_input_idx), 'lev', timestep, field_data_ptr(:,:,constituent_idx), std_name, & - min_difference, min_relative_value, is_first, diff_found) - if (diff_found) then - overall_diff_found = .true. - end if - else - ! If not in standard names list, then just use constituent name as input file name: - call check_field(file, [std_name], 'lev', timestep, field_data_ptr(:,:,constituent_idx), std_name, min_difference, min_relative_value, & - is_first, diff_found) - if (diff_found) then - overall_diff_found = .true. - end if - end if - end do - ! Close check file: - call cam_pio_closefile(file) - deallocate(file) - nullify(file) - if (is_first) then - if (masterproc) then - write(iulog,*) '' - write(iulog,*) 'No differences found!' - end if - end if - if (masterproc) then - write(iulog,*) '' - write(iulog,*) '********** End Physics Check Data Results **********' - write(iulog,*) '' - end if - ! Endrun if differences were found on this timestep and err_on_fail=TRUE - if (overall_diff_found .and. err_on_fail .and. masterproc) then - call endrun('ERROR: Difference(s) found during ncdata check', file=__FILE__, line=__LINE__) - end if - end subroutine physics_check_data - -end module physics_inputs_constituent_dim From a578c921c4da831142e1c2814f0ee8ad889634e4 Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Wed, 16 Jul 2025 14:09:07 -0400 Subject: [PATCH 07/11] Update src/data/write_init_files.py Co-authored-by: Jesse Nusbaumer --- src/data/write_init_files.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/write_init_files.py b/src/data/write_init_files.py index 14226c64e..afea54e93 100644 --- a/src/data/write_init_files.py +++ b/src/data/write_init_files.py @@ -793,7 +793,7 @@ def get_dimension_info(hvar): # end if # A special case where any dimensions include number_of_ccpp_constituents, - # in this case the variable needs to be suffixed by _ + constituent name + # in this case the variable needs to be suffixed by an underscore plus the constituent name # and read and reassembled separately into host model constituent indices # based on constituent name. # From 7b01e0ef54d670770d2a5d4c952bf746657b0e99 Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Wed, 16 Jul 2025 14:09:15 -0400 Subject: [PATCH 08/11] Update src/physics/utils/physics_data.F90 Co-authored-by: Jesse Nusbaumer --- src/physics/utils/physics_data.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/physics/utils/physics_data.F90 b/src/physics/utils/physics_data.F90 index b6e6d48d3..3592b7dc9 100644 --- a/src/physics/utils/physics_data.F90 +++ b/src/physics/utils/physics_data.F90 @@ -345,7 +345,7 @@ subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, ba ! Dummy arguments type(ccpp_constituent_prop_ptr_t), intent(in) :: const_props(:) ! Constituent properties - type(file_desc_t), intent(inout) :: file + type(file_desc_t), intent(inout) :: file !Parallel I/O (PIO) file type. character(len=*), intent(in) :: std_name ! Standard name of base variable. character(len=*), intent(in) :: base_var_names(:) ! "Base" name(s) used to construct variable name (base_constname) integer, intent(in) :: timestep ! Timestep to read [count] From b4263b606538da53199a2c4702617148046e58a0 Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Wed, 16 Jul 2025 14:23:42 -0400 Subject: [PATCH 09/11] Address review comments --- src/data/registry.xml | 2 ++ src/data/write_init_files.py | 26 +++++++++++++------------- src/physics/utils/physics_data.F90 | 10 ++++++---- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/data/registry.xml b/src/data/registry.xml index f174ea059..e9368f27e 100644 --- a/src/data/registry.xml +++ b/src/data/registry.xml @@ -530,8 +530,10 @@ horizontal_dimension vertical_layer_dimension cp_or_cv_dycore + + diff --git a/src/data/write_init_files.py b/src/data/write_init_files.py index afea54e93..b62a5b124 100644 --- a/src/data/write_init_files.py +++ b/src/data/write_init_files.py @@ -752,7 +752,8 @@ def get_dimension_info(hvar): has_constituent_dim = any('number_of_ccpp_constituents' in dim for dim in dims) # is only 'legal' for 2 or 3 dimensional fields (i.e., 1 or 2 - # dimensional variables). The second dimension must be vertical. + # dimensional variables). + # The second dimension must either be vertical or number of constituents. # XXgoldyXX: If we ever need to read scalars, it would have to be # done using global attributes, not 'infld'. ldims = len(dims) @@ -764,7 +765,17 @@ def get_dimension_info(hvar): fail_reason += f"{suff}{lname} has no horizontal dimension" suff = "; " # end if - if (ldims > 2) or ((ldims > 1) and (not levnm)): + + if has_constituent_dim: + # A special case where any dimensions include number_of_ccpp_constituents, + # in this case the variable needs to be suffixed by an underscore plus the constituent name + # and read and reassembled separately into host model constituent indices + # based on constituent name. + # This case will be handled separately. + legal_dims = True + elif (ldims > 2) or ((ldims > 1) and (not levnm)): + # The regular case where the second dimension must be vertical, + # and higher dimensions are unsupported. legal_dims = False unsupp = [] for dim in dims: @@ -792,17 +803,6 @@ def get_dimension_info(hvar): suff = "; " # end if - # A special case where any dimensions include number_of_ccpp_constituents, - # in this case the variable needs to be suffixed by an underscore plus the constituent name - # and read and reassembled separately into host model constituent indices - # based on constituent name. - # - # In this case, override legal_dims as this case will be handled separately - if has_constituent_dim: - legal_dims = True - fail_reason = "" - # end if - if legal_dims and levnm: # should be legal, find the correct local name for the # vertical dimension diff --git a/src/physics/utils/physics_data.F90 b/src/physics/utils/physics_data.F90 index 3592b7dc9..7cf1f8ef5 100644 --- a/src/physics/utils/physics_data.F90 +++ b/src/physics/utils/physics_data.F90 @@ -371,6 +371,8 @@ subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, ba integer :: n integer :: const_input_idx + character(len=256) :: errmsg + character(len=*), parameter :: subname = 'read_constituent_dimensioned_field: ' if (present(error_on_not_found)) then @@ -384,16 +386,16 @@ subroutine read_constituent_dimensioned_field_2d(const_props, file, std_name, ba missing_vars = '' ! Allocate temporary buffer - allocate(buffer(size(field_array, 1)), stat=ierr) - call check_allocate(ierr, subname, 'buffer') + allocate(buffer(size(field_array, 1)), stat=ierr, errmsg=errmsg) + call check_allocate(ierr, subname, 'buffer', errmsg=errmsg) !REMOVECAM: ! Because the constituent properties pointer contains standard names, and not input constituent names ! (e.g., Q, CLDLIQ, ...) which are used in the input file names, ! we have to construct a mapping of the standard names to the short input IC file names ! When CAM is retired and only standard names are used for constituents, this mapping can be removed. - allocate(constituent_short_names(size(const_props)), stat=ierr) - call check_allocate(ierr, subname, 'constituent_short_names') + allocate(constituent_short_names(size(const_props)), stat=ierr, errmsg=errmsg) + call check_allocate(ierr, subname, 'constituent_short_names', errmsg=errmsg) const_shortmap_loop: do const_idx = 1, size(const_props) ! Get constituent standard name. From c413cc10a1bd45cf333616030d017829038787aa Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Wed, 16 Jul 2025 14:23:56 -0400 Subject: [PATCH 10/11] Update test/unit/python/test_write_init_files.py Co-authored-by: Jesse Nusbaumer --- test/unit/python/test_write_init_files.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/python/test_write_init_files.py b/test/unit/python/test_write_init_files.py index a1a2f7a97..f8834326a 100644 --- a/test/unit/python/test_write_init_files.py +++ b/test/unit/python/test_write_init_files.py @@ -1253,7 +1253,7 @@ def test_bad_vertical_dimension(self): correctly determines that a variable that could be called from "read_field" has two dimensions but the second is not a vertical dimension (which read_field can't - handle) that is not the number of constituents, + handle) and is not the number of constituents, and exits with both the correct return message, and with no Fortran files generated. """ From 273cefb60ea9ff4c52a02ecdf8d90e6ccc3fbb67 Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Wed, 16 Jul 2025 14:29:00 -0400 Subject: [PATCH 11/11] Add writeout for constituent_min_value --- src/data/write_init_files.py | 2 +- .../python/sample_files/write_init_files/physics_inputs_4D.F90 | 2 +- .../python/sample_files/write_init_files/physics_inputs_bvd.F90 | 2 +- .../sample_files/write_init_files/physics_inputs_cnst.F90 | 2 +- .../write_init_files/physics_inputs_constituent_dim.F90 | 2 +- .../python/sample_files/write_init_files/physics_inputs_ddt.F90 | 2 +- .../sample_files/write_init_files/physics_inputs_ddt2.F90 | 2 +- .../sample_files/write_init_files/physics_inputs_ddt_array.F90 | 2 +- .../sample_files/write_init_files/physics_inputs_host_var.F90 | 2 +- .../write_init_files/physics_inputs_initial_value.F90 | 2 +- .../python/sample_files/write_init_files/physics_inputs_mf.F90 | 2 +- .../sample_files/write_init_files/physics_inputs_no_horiz.F90 | 2 +- .../sample_files/write_init_files/physics_inputs_noreq.F90 | 2 +- .../sample_files/write_init_files/physics_inputs_param.F90 | 2 +- .../sample_files/write_init_files/physics_inputs_protect.F90 | 2 +- .../sample_files/write_init_files/physics_inputs_scalar.F90 | 2 +- .../sample_files/write_init_files/physics_inputs_simple.F90 | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/data/write_init_files.py b/src/data/write_init_files.py index b62a5b124..54286690d 100644 --- a/src/data/write_init_files.py +++ b/src/data/write_init_files.py @@ -1171,7 +1171,7 @@ def write_phys_read_subroutine(outfile, host_dict, host_vars, host_imports, outfile.write("call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg)", 5) outfile.write("field_data_ptr(:,:,constituent_idx) = constituent_min_value", 5) outfile.write("if (masterproc) then", 5) - outfile.write("write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.'", 6) + outfile.write("write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to min value of ', constituent_min_value", 6) outfile.write("end if", 5) outfile.write("end if", 4) outfile.write("end if", 3) diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_4D.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_4D.F90 index 0176da892..40a1e332b 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_4D.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_4D.F90 @@ -212,7 +212,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) field_data_ptr(:,:,constituent_idx) = constituent_min_value if (masterproc) then - write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to min value of ', constituent_min_value end if end if end if diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_bvd.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_bvd.F90 index de1568909..9bf56d36e 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_bvd.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_bvd.F90 @@ -212,7 +212,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) field_data_ptr(:,:,constituent_idx) = constituent_min_value if (masterproc) then - write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to min value of ', constituent_min_value end if end if end if diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_cnst.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_cnst.F90 index 649190bac..7de9cb056 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_cnst.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_cnst.F90 @@ -212,7 +212,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) field_data_ptr(:,:,constituent_idx) = constituent_min_value if (masterproc) then - write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to min value of ', constituent_min_value end if end if end if diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_constituent_dim.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_constituent_dim.F90 index ae4d58420..9598eb585 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_constituent_dim.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_constituent_dim.F90 @@ -220,7 +220,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) field_data_ptr(:,:,constituent_idx) = constituent_min_value if (masterproc) then - write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to min value of ', constituent_min_value end if end if end if diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_ddt.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_ddt.F90 index 15d5c24b0..9e74f73e3 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_ddt.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_ddt.F90 @@ -212,7 +212,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) field_data_ptr(:,:,constituent_idx) = constituent_min_value if (masterproc) then - write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to min value of ', constituent_min_value end if end if end if diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_ddt2.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_ddt2.F90 index 83afed2f3..f8814a301 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_ddt2.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_ddt2.F90 @@ -212,7 +212,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) field_data_ptr(:,:,constituent_idx) = constituent_min_value if (masterproc) then - write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to min value of ', constituent_min_value end if end if end if diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_ddt_array.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_ddt_array.F90 index 0fe0822d1..c8171a6d1 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_ddt_array.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_ddt_array.F90 @@ -212,7 +212,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) field_data_ptr(:,:,constituent_idx) = constituent_min_value if (masterproc) then - write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to min value of ', constituent_min_value end if end if end if diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_host_var.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_host_var.F90 index a599b8f15..6e4e750c9 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_host_var.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_host_var.F90 @@ -209,7 +209,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) field_data_ptr(:,:,constituent_idx) = constituent_min_value if (masterproc) then - write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to min value of ', constituent_min_value end if end if end if diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_initial_value.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_initial_value.F90 index c21a8609c..b5c19c57a 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_initial_value.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_initial_value.F90 @@ -212,7 +212,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) field_data_ptr(:,:,constituent_idx) = constituent_min_value if (masterproc) then - write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to min value of ', constituent_min_value end if end if end if diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_mf.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_mf.F90 index 0b2088427..5b1fbf680 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_mf.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_mf.F90 @@ -213,7 +213,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) field_data_ptr(:,:,constituent_idx) = constituent_min_value if (masterproc) then - write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to min value of ', constituent_min_value end if end if end if diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_no_horiz.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_no_horiz.F90 index ec8e85458..ce6db2e5c 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_no_horiz.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_no_horiz.F90 @@ -212,7 +212,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) field_data_ptr(:,:,constituent_idx) = constituent_min_value if (masterproc) then - write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to min value of ', constituent_min_value end if end if end if diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_noreq.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_noreq.F90 index b3d7f241c..3d655e72e 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_noreq.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_noreq.F90 @@ -205,7 +205,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) field_data_ptr(:,:,constituent_idx) = constituent_min_value if (masterproc) then - write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to min value of ', constituent_min_value end if end if end if diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_param.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_param.F90 index fb3711050..cedc2e657 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_param.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_param.F90 @@ -215,7 +215,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) field_data_ptr(:,:,constituent_idx) = constituent_min_value if (masterproc) then - write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to min value of ', constituent_min_value end if end if end if diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_protect.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_protect.F90 index 0c06875e0..c904aad1e 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_protect.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_protect.F90 @@ -212,7 +212,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) field_data_ptr(:,:,constituent_idx) = constituent_min_value if (masterproc) then - write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to min value of ', constituent_min_value end if end if end if diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_scalar.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_scalar.F90 index 570a43242..60214bc58 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_scalar.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_scalar.F90 @@ -212,7 +212,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) field_data_ptr(:,:,constituent_idx) = constituent_min_value if (masterproc) then - write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to min value of ', constituent_min_value end if end if end if diff --git a/test/unit/python/sample_files/write_init_files/physics_inputs_simple.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_simple.F90 index 490e535cb..ae1222ccd 100644 --- a/test/unit/python/sample_files/write_init_files/physics_inputs_simple.F90 +++ b/test/unit/python/sample_files/write_init_files/physics_inputs_simple.F90 @@ -212,7 +212,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call const_props(constituent_idx)%minimum(constituent_min_value, constituent_errflg, constituent_errmsg) field_data_ptr(:,:,constituent_idx) = constituent_min_value if (masterproc) then - write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to 0.' + write(iulog,*) 'Constituent ', trim(std_name), ' default value not configured. Setting to min value of ', constituent_min_value end if end if end if