From a5e24e49c6a0507527fc093683b40a1ce3eaed16 Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Mon, 13 Jul 2026 12:17:29 -0400 Subject: [PATCH 1/5] physics_data: read vertically-resolved constituent-dimensioned fields. write_init_files already emits the generic read_constituent_dimensioned_field call with a vertical coordinate for (horizontal, vertical, constituent) registry variables, but the interface only carried the (horizontal, constituent) specific, so such a variable generated a call with no matching specific and could not compile. Add read_constituent_dimensioned_field_3d as a sibling of the 2-D specific. Each constituent row is read from its own _ file variable (e.g. aerochem_vmr_bc_a1); rows with no matching file variable are left at their initial value, since a capture only writes the species the source model carries. Standard names are case-insensitive, so the registry lookup compares them with to_lower: a constituent declared with authored case (e.g. 'CO2') is normalized to lowercase by capgen on the scheme side, and a case-sensitive compare would miss the registry row and silently read nothing. --- src/physics/utils/physics_data.F90 | 238 ++++++++++++++++++++++++++++- 1 file changed, 237 insertions(+), 1 deletion(-) diff --git a/src/physics/utils/physics_data.F90 b/src/physics/utils/physics_data.F90 index 2ae08c51..a9b2b98e 100644 --- a/src/physics/utils/physics_data.F90 +++ b/src/physics/utils/physics_data.F90 @@ -28,7 +28,8 @@ module physics_data end interface check_field interface read_constituent_dimensioned_field - module procedure read_constituent_dimensioned_field_2d + module procedure read_constituent_dimensioned_field_2d !horizontal columns only (1-D buffer per constituent) + module procedure read_constituent_dimensioned_field_3d !horizontal columns + vertical levels (2-D buffer per constituent) end interface read_constituent_dimensioned_field ! Module-level storage for verbose check_field entries. @@ -695,6 +696,241 @@ function constituent_dim_file_var_names(base_name, cname) result(var_names) end function constituent_dim_file_var_names + ! Vertically-resolved analog of read_constituent_dimensioned_field_2d: reads a + ! (horizontal, vertical, constituent) field where each constituent is stored on + ! file as a separate _ variable (so the CAM + ! <-> CAM-SIMA constituent-index reshuffle is handled by name). vcoord_name is + ! the vertical coordinate ('lev' or 'ilev'), emitted by write_init_files.py for + ! constituent-dimensioned variables that also carry a vertical dimension. + subroutine read_constituent_dimensioned_field_3d(const_props, file, std_name, base_var_names, vcoord_name, timestep, field_array, error_on_not_found) + use ccpp_kinds, only: kind_phys + 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, check_allocate + use cam_logfile, only: iulog + use cam_field_read, only: cam_read_field + use vert_coord, only: pver, pverp + 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 + use string_utils, only: to_lower + + ! Dummy arguments + type(ccpp_constituent_prop_ptr_t), intent(in) :: const_props(:) ! Constituent properties + 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) + character(len=*), intent(in) :: vcoord_name ! Vertical coordinate name ('lev' or 'ilev') + integer, intent(in) :: timestep ! Timestep to read [count] + real(kind_phys), intent(inout) :: field_array(:,:,:) ! Output field array (ncol, nlev, pcnst) + logical, optional, intent(in) :: error_on_not_found ! Flag to error and exit 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 :: num_levs + integer :: ierr + logical :: error_on_not_found_local + 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=256) :: errmsg + + character(len=*), parameter :: subname = 'read_constituent_dimensioned_field: ' + + 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 + + ! Resolve the vertical extent from the coordinate name. + if (trim(vcoord_name) == 'lev') then + num_levs = pver + else if (trim(vcoord_name) == 'ilev') then + num_levs = pverp + else + call endrun(subname//'Unknown vcoord_name, '//trim(vcoord_name)) + end if + + ! Initialize tracking variables + any_missing = .false. + missing_vars = '' + + ! Allocate temporary buffer + allocate(buffer(size(field_array, 1), num_levs), 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, 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. + 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 specified in the registry for this constituent + ! is the short name + const_input_idx = -1 + phys_inputvar_loop: do n = 1, phys_var_num + ! case-insensitive: see find_input_name_idx + if (to_lower(trim(phys_var_stdnames(n))) == to_lower(trim(constituent_std_name))) then + const_input_idx = n + exit phys_inputvar_loop + end if + 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. + 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 + + ! 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 + + if(.not. var_found) then + if(error_on_not_found_local) then + !End model run with appropriate error message: + call endrun(subname // 'Required constituent-dimensioned variables not found: No match for ' // trim(std_name)) + else + !Write message to log file, then exit subroutine: + if (masterproc) then + write(iulog, *) subname // 'Required constituent-dimensioned variables not found: No match for ' // trim(std_name) + call shr_sys_flush(iulog) + end if + return !Nothing more to do here + end if + 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 + 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) + + ! Some constituents whose names are not specified in the registry + ! will have cnst_ prepended to them (e.g., cnst_dst_a1); also try reading + ! from file by removing this prefix: + if (.not. var_found) then + if (constituent_name(1:5) == 'cnst_') then + file_var_name = trim(base_var_names(base_idx)) // '_' // trim(constituent_name(6:)) + call cam_pio_find_var(file, [file_var_name], found_name, vardesc, var_found) + end if + end if + + 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, dim3name=trim(vcoord_name), & + dim3_bnds=[1, num_levs]) + + 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 (.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), & + ' 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. error_on_not_found_local) then + 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) + call mark_as_read_from_file(std_name) + + ! Clean up + deallocate(constituent_short_names) + deallocate(buffer) + + end subroutine read_constituent_dimensioned_field_3d + subroutine check_field_2d(file, var_names, timestep, current_value, & stdname, min_difference, min_relative_value, is_first, diff_found) use ccpp_kinds, only: kind_phys From eda7e0496e74dcbefec1b510c39ce622fc597b39 Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Mon, 13 Jul 2026 12:17:52 -0400 Subject: [PATCH 2/5] physics_check_data: check vertically-resolved constituent-dimensioned fields. Constituent-dimensioned registry variables were skipped entirely by physics_check_data, so they were silently absent from the check output. Add check_constituent_dimensioned_field (the (horizontal, vertical, constituent) specific) and emit the call for such variables, mirroring the read_constituent_dimensioned_field path; the registry lookup is case-insensitive for the same reason. Each constituent row is checked against its own _ file variable; rows with no matching file variable are skipped. The (horizontal, constituent) case is not implemented yet and is still skipped. const_props is hoisted above the suite loop, since the check calls inside the loop now need it. The test registry gains a vertically-resolved constituent-dimensioned variable (cool_cat_3d_for_each_const) exercising both the read and the check path. --- src/data/write_init_files.py | 25 +++- src/physics/utils/physics_data.F90 | 134 ++++++++++++++++++ .../phys_vars_init_check_constituent_dim.F90 | 10 +- .../write_init_files/physics_inputs_4D.F90 | 4 +- .../write_init_files/physics_inputs_bvd.F90 | 4 +- .../write_init_files/physics_inputs_cnst.F90 | 4 +- .../physics_inputs_constituent_dim.F90 | 16 ++- .../write_init_files/physics_inputs_ddt.F90 | 4 +- .../write_init_files/physics_inputs_ddt2.F90 | 4 +- .../physics_inputs_ddt_array.F90 | 4 +- .../physics_inputs_host_var.F90 | 4 +- .../physics_inputs_initial_value.F90 | 4 +- .../write_init_files/physics_inputs_mf.F90 | 4 +- .../physics_inputs_no_horiz.F90 | 4 +- .../write_init_files/physics_inputs_noreq.F90 | 4 +- .../write_init_files/physics_inputs_param.F90 | 4 +- .../physics_inputs_protect.F90 | 4 +- .../physics_inputs_scalar.F90 | 4 +- .../physics_inputs_simple.F90 | 4 +- .../simple_reg_constituent_dim.xml | 9 ++ .../temp_adjust_constituent_dim.F90 | 3 +- .../temp_adjust_constituent_dim.meta | 7 + 22 files changed, 221 insertions(+), 43 deletions(-) diff --git a/src/data/write_init_files.py b/src/data/write_init_files.py index 55728d4c..08f7b103 100644 --- a/src/data/write_init_files.py +++ b/src/data/write_init_files.py @@ -1270,18 +1270,29 @@ 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: + # Constituent-indexed fields are checked per constituent against + # _ file variables (mirroring the + # read_constituent_dimensioned_field call in physics_read_data). + # Only the vertically-resolved case is implemented; skip otherwise. + if has_constituent_read and levnm is None: continue # end if # Set "check_field" call string: if call_check_field: - call_str = "call check_field(file, input_var_names(:,name_idx), " + if has_constituent_read: + # Special case for constituent-dimension variables. + call_str = f"call check_constituent_dimensioned_field(const_props, file, '{var_stdname}', input_var_names(:,name_idx), " + else: + call_str = "call check_field(file, input_var_names(:,name_idx), " + # end if if levnm is not None: call_str += f"'{levnm}', " # end if - call_str += f"timestep, {var_locname}, '{var_stdname}', " + call_str += f"timestep, {var_locname}, " + if not has_constituent_read: + call_str += f"'{var_stdname}', " + # end if call_str += "min_difference, min_relative_value, is_first, diff_found)" else: # For check field, don't endrun @@ -1305,6 +1316,7 @@ def write_phys_check_subroutine(outfile, host_dict, host_vars, host_imports, ["physics_data", ["check_field", "find_input_name_idx", "no_exist_idx", "init_mark_idx", "prot_no_init_idx", "const_idx", + "check_constituent_dimensioned_field", "flush_check_field_verbose"]], ["cam_ccpp_cap", ["ccpp_physics_suite_variables", "cam_constituents_array", @@ -1412,6 +1424,10 @@ def write_phys_check_subroutine(outfile, host_dict, host_vars, host_imports, outfile.write("call cam_pio_openfile(file, ncdata_check_loc, " + \ "pio_nowrite, log_info=.false.)", 2) + # Constituent properties are needed inside the suite loop by + # check_constituent_dimensioned_field, and below for the constituent checks: + outfile.write("const_props => cam_model_const_properties()", 2) + # Loop over physics suites: outfile.comment("Loop over CCPP physics/chemistry suites:", 2) outfile.write("do suite_idx = 1, size(suite_names, 1)", 2) @@ -1493,7 +1509,6 @@ def write_phys_check_subroutine(outfile, host_dict, host_vars, host_imports, outfile.blank_line() outfile.comment("Check constituent variables", 2) outfile.write("field_data_ptr => cam_constituents_array()", 2) - outfile.write("const_props => cam_model_const_properties()", 2) outfile.blank_line() outfile.write("do constituent_idx = 1, size(const_props)", 2) outfile.comment("Check if constituent standard name in registered SIMA standard names list:", 3) diff --git a/src/physics/utils/physics_data.F90 b/src/physics/utils/physics_data.F90 index a9b2b98e..35db4a2c 100644 --- a/src/physics/utils/physics_data.F90 +++ b/src/physics/utils/physics_data.F90 @@ -7,6 +7,7 @@ module physics_data public :: read_field public :: read_constituent_dimensioned_field public :: check_field + public :: check_constituent_dimensioned_field public :: flush_check_field_verbose ! Non-standard variable indices: @@ -32,6 +33,10 @@ module physics_data module procedure read_constituent_dimensioned_field_3d !horizontal columns + vertical levels (2-D buffer per constituent) end interface read_constituent_dimensioned_field + interface check_constituent_dimensioned_field + module procedure check_constituent_dimensioned_field_3d !horizontal columns + vertical levels (2-D buffer per constituent) + end interface check_constituent_dimensioned_field + ! Module-level storage for verbose check_field entries. ! These are accumulated during check_field calls and flushed ! at the end via flush_check_field_verbose, so that the verbose @@ -931,6 +936,135 @@ subroutine read_constituent_dimensioned_field_3d(const_props, file, std_name, ba end subroutine read_constituent_dimensioned_field_3d + ! Check analog of read_constituent_dimensioned_field_3d: compares each slice of + ! a (horizontal, vertical, constituent) field against the per-constituent + ! _ variables on the check file, via the same + ! constituent-name resolution as the read. Constituents whose variable is not on + ! the check file are skipped silently (the file only carries the species the + ! capture wrote), so slices that were never read/compared -- including any + ! poisoned non-cluster slots -- are not reported as differences. Each compared + ! slice is labeled by its file variable name in the check output. + subroutine check_constituent_dimensioned_field_3d(const_props, file, std_name, base_var_names, & + vcoord_name, timestep, field_array, min_difference, min_relative_value, is_first, diff_found) + use ccpp_kinds, only: kind_phys + use pio, only: file_desc_t, var_desc_t + use cam_pio_utils, only: cam_pio_find_var + use cam_abortutils, only: check_allocate + use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t + use phys_vars_init_check, only: phys_var_stdnames, input_var_names, phys_var_num + use string_utils, only: to_lower + + ! Dummy arguments + type(ccpp_constituent_prop_ptr_t), intent(in) :: const_props(:) ! Constituent properties + 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) + character(len=*), intent(in) :: vcoord_name ! Vertical coordinate name ('lev' or 'ilev') + integer, intent(in) :: timestep ! Timestep to check [count] + real(kind_phys), intent(in) :: field_array(:,:,:) ! Field array to check (ncol, nlev, pcnst) + real(kind_phys), intent(in) :: min_difference + real(kind_phys), intent(in) :: min_relative_value + logical, intent(inout) :: is_first + logical, intent(out) :: diff_found + + ! Local variables + logical :: var_found + logical :: slice_diff_found + character(len=128) :: constituent_name + character(len=256) :: file_var_name + character(len=256) :: found_name + type(var_desc_t) :: vardesc + integer :: const_idx + integer :: ierr + + ! 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=256) :: errmsg + + character(len=*), parameter :: subname = 'check_constituent_dimensioned_field: ' + + diff_found = .false. + + !REMOVECAM: + ! Same standard-name -> short-input-name mapping as + ! read_constituent_dimensioned_field, so the check resolves exactly the file + ! variable names the read used. 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, 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. + 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 specified in the registry for this constituent + ! is the short name + const_input_idx = -1 + phys_inputvar_loop: do n = 1, phys_var_num + ! case-insensitive: see find_input_name_idx + if (to_lower(trim(phys_var_stdnames(n))) == to_lower(trim(constituent_std_name))) then + const_input_idx = n + exit phys_inputvar_loop + end if + 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. + 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 + + const_check_loop: do const_idx = 1, size(const_props) + ! Get constituent short name + constituent_name = constituent_short_names(const_idx) + + ! Create file variable name: _, trying all + ! base names (the read assumes one base name serves all constituents, so the + ! first hit is used). + var_found = .false. + base_idx_loop: do n = 1, size(base_var_names) + file_var_name = trim(base_var_names(n)) // '_' // trim(constituent_name) + call cam_pio_find_var(file, [file_var_name], found_name, vardesc, var_found) + if (var_found) exit base_idx_loop + + ! Some constituents whose names are not specified in the registry + ! will have cnst_ prepended to them (e.g., cnst_dst_a1); also try + ! matching on the file by removing this prefix: + if (constituent_name(1:5) == 'cnst_') then + file_var_name = trim(base_var_names(n)) // '_' // trim(constituent_name(6:)) + call cam_pio_find_var(file, [file_var_name], found_name, vardesc, var_found) + if (var_found) exit base_idx_loop + end if + end do base_idx_loop + + ! Constituent slices without a matching variable on the check file are + ! skipped: the capture only writes the species the source model carries. + if (.not. var_found) cycle const_check_loop + + ! Pass the resolved per-constituent name as both the search name and the + ! reporting label, so check_field's standard-name fallback search cannot + ! match an unrelated variable and diffs are reported per constituent. + call check_field(file, [found_name], vcoord_name, timestep, & + field_array(:, :, const_idx), trim(found_name), & + min_difference, min_relative_value, is_first, slice_diff_found) + if (slice_diff_found) then + diff_found = .true. + end if + end do const_check_loop + + deallocate(constituent_short_names) + + end subroutine check_constituent_dimensioned_field_3d + subroutine check_field_2d(file, var_names, timestep, current_value, & stdname, min_difference, min_relative_value, is_first, diff_found) use ccpp_kinds, only: kind_phys 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 a1235d79..e9ee6919 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 @@ -32,7 +32,7 @@ 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 = 4 + integer, public, parameter :: phys_var_num = 5 integer, public, parameter :: phys_const_num = 16 !Max length of physics-related variable standard names: @@ -46,7 +46,8 @@ module phys_vars_init_check_constituent_dim 'potential_temperature ', & 'air_pressure_at_sea_level ', & 'super_cool_cat_every_const ', & - 'super_cool_cat_with_default_every_const' /) + 'super_cool_cat_with_default_every_const', & + 'super_cool_cat_3d_every_const ' /) character(len=36), public, protected :: phys_const_stdnames(phys_const_num) = (/ & "ccpp_constituent_minimum_values ", & @@ -70,13 +71,15 @@ module phys_vars_init_check_constituent_dim 'theta ', 'pot_temp ', & 'slp ', 'sea_lev_pres ', & 'cool_cat_tend ', 'pbuf_COOL_CAT_TEND ', & - 'cool_cat_default_tend', ' ' /), (/2, phys_var_num/)) + 'cool_cat_default_tend', ' ', & + 'cool_cat_3d ', ' ' /), (/2, phys_var_num/)) ! Array indicating whether or not variable is protected: logical, public, protected :: protected_vars(phys_var_num)= (/ & .false., & .false., & .false., & + .false., & .false. /) ! Variable state (UNINITIALIZED, INTIIALIZED, PARAM or READ_FROM_FILE): @@ -84,6 +87,7 @@ module phys_vars_init_check_constituent_dim UNINITIALIZED, & UNINITIALIZED, & UNINITIALIZED, & + UNINITIALIZED, & UNINITIALIZED /) 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 d92ff84b..a2ee1f90 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 @@ -235,7 +235,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, 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 physics_data, only: flush_check_field_verbose + use physics_data, only: check_constituent_dimensioned_field, flush_check_field_verbose use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use cam_constituents, only: const_get_index use ccpp_kinds, only: kind_phys @@ -312,6 +312,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, end if allocate(file) call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + const_props => cam_model_const_properties() ! Loop over CCPP physics/chemistry suites: do suite_idx = 1, size(suite_names, 1) @@ -364,7 +365,6 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, ! Check constituent variables field_data_ptr => cam_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: 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 3137ab8a..c0a2e7dc 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 @@ -235,7 +235,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, 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 physics_data, only: flush_check_field_verbose + use physics_data, only: check_constituent_dimensioned_field, flush_check_field_verbose use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use cam_constituents, only: const_get_index use ccpp_kinds, only: kind_phys @@ -312,6 +312,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, end if allocate(file) call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + const_props => cam_model_const_properties() ! Loop over CCPP physics/chemistry suites: do suite_idx = 1, size(suite_names, 1) @@ -364,7 +365,6 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, ! Check constituent variables field_data_ptr => cam_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: 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 c9d8c9c5..3d748b88 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 @@ -232,7 +232,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, 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 physics_data, only: flush_check_field_verbose + use physics_data, only: check_constituent_dimensioned_field, flush_check_field_verbose use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use cam_constituents, only: const_get_index use ccpp_kinds, only: kind_phys @@ -309,6 +309,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, end if allocate(file) call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + const_props => cam_model_const_properties() ! Loop over CCPP physics/chemistry suites: do suite_idx = 1, size(suite_names, 1) @@ -361,7 +362,6 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, ! Check constituent variables field_data_ptr => cam_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: 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 8e0c4375..cf26069a 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, cool_default_cat_for_each_const, slp, theta + use physics_types_simple_constituent_dim, only: cool_cat_3d_for_each_const, cool_cat_for_each_const, cool_default_cat_for_each_const, slp, theta ! Dummy arguments type(file_desc_t), intent(inout) :: file @@ -160,6 +160,10 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia 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, error_on_not_found=.false.) + case ('super_cool_cat_3d_every_const') + call read_constituent_dimensioned_field(const_props, file, 'super_cool_cat_3d_every_const', input_var_names(:,name_idx), & + 'lev', timestep, cool_cat_3d_for_each_const, error_on_not_found=.false.) + end select !read variables end select !special indices @@ -240,7 +244,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, 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 physics_data, only: flush_check_field_verbose + use physics_data, only: check_constituent_dimensioned_field, flush_check_field_verbose use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use cam_constituents, only: const_get_index use ccpp_kinds, only: kind_phys @@ -252,7 +256,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, theta + use physics_types_simple_constituent_dim, only: cool_cat_3d_for_each_const, cool_cat_for_each_const, theta ! Dummy arguments character(len=SHR_KIND_CL), intent(in) :: file_name @@ -317,6 +321,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, end if allocate(file) call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + const_props => cam_model_const_properties() ! Loop over CCPP physics/chemistry suites: do suite_idx = 1, size(suite_names, 1) @@ -354,6 +359,10 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, call check_field(file, input_var_names(:,name_idx), 'lev', timestep, theta, 'potential_temperature', min_difference, & min_relative_value, is_first, diff_found) + case ('super_cool_cat_3d_every_const') + call check_constituent_dimensioned_field(const_props, file, 'super_cool_cat_3d_every_const', input_var_names(:,name_idx), 'lev', & + timestep, cool_cat_3d_for_each_const, min_difference, min_relative_value, is_first, diff_found) + end select !check variables if (diff_found) then overall_diff_found = .true. @@ -369,7 +378,6 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, ! Check constituent variables field_data_ptr => cam_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: 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 b3d0ca07..20982d90 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 @@ -232,7 +232,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, 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 physics_data, only: flush_check_field_verbose + use physics_data, only: check_constituent_dimensioned_field, flush_check_field_verbose use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use cam_constituents, only: const_get_index use ccpp_kinds, only: kind_phys @@ -309,6 +309,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, end if allocate(file) call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + const_props => cam_model_const_properties() ! Loop over CCPP physics/chemistry suites: do suite_idx = 1, size(suite_names, 1) @@ -361,7 +362,6 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, ! Check constituent variables field_data_ptr => cam_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: 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 00aa313f..67922f9f 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 @@ -232,7 +232,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, 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 physics_data, only: flush_check_field_verbose + use physics_data, only: check_constituent_dimensioned_field, flush_check_field_verbose use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use cam_constituents, only: const_get_index use ccpp_kinds, only: kind_phys @@ -309,6 +309,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, end if allocate(file) call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + const_props => cam_model_const_properties() ! Loop over CCPP physics/chemistry suites: do suite_idx = 1, size(suite_names, 1) @@ -361,7 +362,6 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, ! Check constituent variables field_data_ptr => cam_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: 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 d22a8f18..069a73e2 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 @@ -232,7 +232,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, 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 physics_data, only: flush_check_field_verbose + use physics_data, only: check_constituent_dimensioned_field, flush_check_field_verbose use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use cam_constituents, only: const_get_index use ccpp_kinds, only: kind_phys @@ -309,6 +309,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, end if allocate(file) call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + const_props => cam_model_const_properties() ! Loop over CCPP physics/chemistry suites: do suite_idx = 1, size(suite_names, 1) @@ -361,7 +362,6 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, ! Check constituent variables field_data_ptr => cam_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: 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 05cc3104..842b0c39 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 @@ -229,7 +229,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, 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 physics_data, only: flush_check_field_verbose + use physics_data, only: check_constituent_dimensioned_field, flush_check_field_verbose use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use cam_constituents, only: const_get_index use ccpp_kinds, only: kind_phys @@ -305,6 +305,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, end if allocate(file) call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + const_props => cam_model_const_properties() ! Loop over CCPP physics/chemistry suites: do suite_idx = 1, size(suite_names, 1) @@ -353,7 +354,6 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, ! Check constituent variables field_data_ptr => cam_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: 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 8b7cf12f..30e6c79c 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 @@ -232,7 +232,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, 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 physics_data, only: flush_check_field_verbose + use physics_data, only: check_constituent_dimensioned_field, flush_check_field_verbose use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use cam_constituents, only: const_get_index use ccpp_kinds, only: kind_phys @@ -309,6 +309,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, end if allocate(file) call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + const_props => cam_model_const_properties() ! Loop over CCPP physics/chemistry suites: do suite_idx = 1, size(suite_names, 1) @@ -361,7 +362,6 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, ! Check constituent variables field_data_ptr => cam_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: 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 0b0af8bd..440722c3 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 @@ -233,7 +233,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, 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 physics_data, only: flush_check_field_verbose + use physics_data, only: check_constituent_dimensioned_field, flush_check_field_verbose use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use cam_constituents, only: const_get_index use ccpp_kinds, only: kind_phys @@ -310,6 +310,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, end if allocate(file) call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + const_props => cam_model_const_properties() ! Loop over CCPP physics/chemistry suites: do suite_idx = 1, size(suite_names, 1) @@ -362,7 +363,6 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, ! Check constituent variables field_data_ptr => cam_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: 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 b993b3db..267ec148 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 @@ -232,7 +232,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, 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 physics_data, only: flush_check_field_verbose + use physics_data, only: check_constituent_dimensioned_field, flush_check_field_verbose use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use cam_constituents, only: const_get_index use ccpp_kinds, only: kind_phys @@ -309,6 +309,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, end if allocate(file) call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + const_props => cam_model_const_properties() ! Loop over CCPP physics/chemistry suites: do suite_idx = 1, size(suite_names, 1) @@ -361,7 +362,6 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, ! Check constituent variables field_data_ptr => cam_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: 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 382c3129..5bbdb006 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 @@ -225,7 +225,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, 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 physics_data, only: flush_check_field_verbose + use physics_data, only: check_constituent_dimensioned_field, flush_check_field_verbose use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use cam_constituents, only: const_get_index use ccpp_kinds, only: kind_phys @@ -301,6 +301,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, end if allocate(file) call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + const_props => cam_model_const_properties() ! Loop over CCPP physics/chemistry suites: do suite_idx = 1, size(suite_names, 1) @@ -349,7 +350,6 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, ! Check constituent variables field_data_ptr => cam_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: 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 f9a7a1ab..48444036 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 @@ -235,7 +235,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, 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 physics_data, only: flush_check_field_verbose + use physics_data, only: check_constituent_dimensioned_field, flush_check_field_verbose use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use cam_constituents, only: const_get_index use ccpp_kinds, only: kind_phys @@ -312,6 +312,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, end if allocate(file) call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + const_props => cam_model_const_properties() ! Loop over CCPP physics/chemistry suites: do suite_idx = 1, size(suite_names, 1) @@ -364,7 +365,6 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, ! Check constituent variables field_data_ptr => cam_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: 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 37c85010..ecd878dd 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 @@ -232,7 +232,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, 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 physics_data, only: flush_check_field_verbose + use physics_data, only: check_constituent_dimensioned_field, flush_check_field_verbose use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use cam_constituents, only: const_get_index use ccpp_kinds, only: kind_phys @@ -309,6 +309,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, end if allocate(file) call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + const_props => cam_model_const_properties() ! Loop over CCPP physics/chemistry suites: do suite_idx = 1, size(suite_names, 1) @@ -361,7 +362,6 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, ! Check constituent variables field_data_ptr => cam_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: 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 1b338728..2ca6475d 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 @@ -232,7 +232,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, 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 physics_data, only: flush_check_field_verbose + use physics_data, only: check_constituent_dimensioned_field, flush_check_field_verbose use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use cam_constituents, only: const_get_index use ccpp_kinds, only: kind_phys @@ -309,6 +309,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, end if allocate(file) call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + const_props => cam_model_const_properties() ! Loop over CCPP physics/chemistry suites: do suite_idx = 1, size(suite_names, 1) @@ -361,7 +362,6 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, ! Check constituent variables field_data_ptr => cam_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: 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 2f31c548..a2b6dc55 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 @@ -232,7 +232,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, 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 physics_data, only: flush_check_field_verbose + use physics_data, only: check_constituent_dimensioned_field, flush_check_field_verbose use cam_ccpp_cap, only: ccpp_physics_suite_variables, cam_constituents_array, cam_model_const_properties use cam_constituents, only: const_get_index use ccpp_kinds, only: kind_phys @@ -309,6 +309,7 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, end if allocate(file) call cam_pio_openfile(file, ncdata_check_loc, pio_nowrite, log_info=.false.) + const_props => cam_model_const_properties() ! Loop over CCPP physics/chemistry suites: do suite_idx = 1, size(suite_names, 1) @@ -368,7 +369,6 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, ! Check constituent variables field_data_ptr => cam_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: 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 2563d7f6..302d84c4 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 @@ -32,5 +32,14 @@ 2.33_kind_phys cool_cat_default_tend + + + The coolest imaginable vertically-resolved value per constituent + horizontal_dimension vertical_layer_dimension number_of_ccpp_constituents + 0.0_kind_phys + cool_cat_3d + 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 9dd3d099..9090a49c 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 @@ -19,7 +19,7 @@ MODULE temp_adjust !! SUBROUTINE temp_adjust_run(nbox, lev, temp_layer, & slp, cool_cat_for_each_const, cool_cat_default_for_each_const, & - timestep, errmsg, errflg) + cool_cat_3d_for_each_const, timestep, errmsg, errflg) !---------------------------------------------------------------- IMPLICIT NONE !---------------------------------------------------------------- @@ -29,6 +29,7 @@ SUBROUTINE temp_adjust_run(nbox, lev, 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(inout) :: cool_cat_3d_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 09f53138..9b0f72e2 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 @@ -44,6 +44,13 @@ type = real kind = kind_phys intent = in +[ cool_cat_3d_for_each_const ] + standard_name = super_cool_cat_3d_every_const + units = kg kg-1 + dimensions = (horizontal_loop_extent, vertical_layer_dimension, number_of_ccpp_constituents) + type = real + kind = kind_phys + intent = inout [ timestep ] standard_name = timestep_for_physics long_name = time step From 276a5eba83f0d7cb17cfc923d8280c00f75cca78 Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Mon, 13 Jul 2026 12:17:56 -0400 Subject: [PATCH 3/5] physics_data: read fixed-dimensioned fields from numbered file variables. A (horizontal, fixed-size extra dimension) registry variable cannot be captured as a single snapshot/history variable, so such fields are written out as numbered per-slice variables (e.g. 1 ... N). Add read_indexed_dimensioned_field, a sibling of read_constituent_dimensioned_field keyed by slice index instead of constituent name, and teach write_init_files to emit it for 2-D variables whose second dimension is neither vertical nor the constituent dimension. The ic_file_input_names of such variables are therefore name PREFIXES; the slice index is appended by the reader. physics_check_data skips these variables (read-only inputs), mirroring the 2-D constituent skip. The (horizontal, band_number) variable in the bad-vertical-dimension test is now a supported shape and generates a read call. --- src/data/write_init_files.py | 36 +++- src/physics/utils/physics_data.F90 | 161 ++++++++++++++++++ .../write_init_files/physics_inputs_4D.F90 | 2 +- .../write_init_files/physics_inputs_bvd.F90 | 4 +- .../write_init_files/physics_inputs_cnst.F90 | 2 +- .../physics_inputs_constituent_dim.F90 | 2 +- .../write_init_files/physics_inputs_ddt.F90 | 2 +- .../write_init_files/physics_inputs_ddt2.F90 | 2 +- .../physics_inputs_ddt_array.F90 | 2 +- .../physics_inputs_host_var.F90 | 2 +- .../physics_inputs_initial_value.F90 | 2 +- .../write_init_files/physics_inputs_mf.F90 | 2 +- .../physics_inputs_no_horiz.F90 | 2 +- .../write_init_files/physics_inputs_noreq.F90 | 2 +- .../write_init_files/physics_inputs_param.F90 | 2 +- .../physics_inputs_protect.F90 | 2 +- .../physics_inputs_scalar.F90 | 2 +- .../physics_inputs_simple.F90 | 2 +- test/unit/python/test_write_init_files.py | 4 +- 19 files changed, 212 insertions(+), 23 deletions(-) diff --git a/src/data/write_init_files.py b/src/data/write_init_files.py index 08f7b103..8a09bcbc 100644 --- a/src/data/write_init_files.py +++ b/src/data/write_init_files.py @@ -766,6 +766,9 @@ def get_dimension_info(hvar): - Flag if any dimensions are number_of_ccpp_constituents and needs reading separate variables by constituent and reassembled into host model indices. + - Flag if the variable is (horizontal, fixed-size extra dimension) and + needs reading separate numbered per-slice variables + (). """ vdim_name = None legal_dims = False @@ -774,6 +777,13 @@ def get_dimension_info(hvar): dims = hvar.get_dimensions() levnm = hvar.has_vertical_dimension() has_constituent_dim = any('number_of_ccpp_constituents' in dim for dim in dims) + # A (horizontal, extra) variable whose only extra dimension is a fixed-size, + # non-vertical dimension (e.g. dust_size_bin_dimension) is read from numbered + # per-slice file variables (a fixed extra dimension cannot be captured as a + # single history/snapshot variable on the CAM side): + has_indexed_dim = ((len(dims) == 2) and (not levnm) and + (not has_constituent_dim) and + is_horizontal_dimension(dims[0])) # is only 'legal' for 2 or 3 dimensional fields (i.e., 1 or 2 # dimensional variables). @@ -797,6 +807,12 @@ def get_dimension_info(hvar): # based on constituent name. # This case will be handled separately. legal_dims = True + elif has_indexed_dim: + # A special case where the only extra dimension is a fixed-size, + # non-vertical dimension: the variable is read from numbered + # per-slice file variables, . + # This case will be handled separately. + legal_dims = True elif (ldims > 3) or ((ldims > 1) and (not levnm)): # The regular case where the second dimension must be vertical, # and dimensions greater than three are unsupported. @@ -865,7 +881,7 @@ def get_dimension_info(hvar): # end if # end if - return vdim_name, legal_dims, fail_reason, has_constituent_dim + return vdim_name, legal_dims, fail_reason, has_constituent_dim, has_indexed_dim def write_phys_read_subroutine(outfile, host_dict, host_vars, host_imports, phys_check_fname_str, constituent_set, @@ -907,7 +923,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, has_constituent_read = get_dimension_info(hvar) + levnm, call_read_field, reason, has_constituent_read, has_indexed_read = get_dimension_info(hvar) if hvar.get_prop_value('protected'): call_read_field = False if reason: @@ -924,6 +940,10 @@ 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), " + elif has_indexed_read: + # Special case for fixed-size indexed-dimension variables + # (read from numbered per-slice file variables). + call_str = f"call read_indexed_dimensioned_field(file, '{var_stdname}', input_var_names(:,name_idx), " else: # Replace vertical dimension with local name call_str = "call read_field(file, " + \ @@ -970,7 +990,8 @@ def write_phys_read_subroutine(outfile, host_dict, host_vars, host_imports, ["physics_data", ["read_field", "find_input_name_idx", "no_exist_idx", "init_mark_idx", "prot_no_init_idx", "const_idx", - "read_constituent_dimensioned_field"]], + "read_constituent_dimensioned_field", + "read_indexed_dimensioned_field"]], ["cam_ccpp_cap", ["ccpp_physics_suite_variables", "cam_constituents_array", "cam_model_const_properties"]], @@ -1268,7 +1289,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, has_constituent_read = get_dimension_info(hvar) + levnm, call_check_field, reason, has_constituent_read, has_indexed_read = get_dimension_info(hvar) # Constituent-indexed fields are checked per constituent against # _ file variables (mirroring the @@ -1278,6 +1299,13 @@ def write_phys_check_subroutine(outfile, host_dict, host_vars, host_imports, continue # end if + # Indexed-dimensioned (fixed-size extra dimension) fields are read-only + # inputs assembled from numbered per-slice file variables; checking is + # not implemented for them (mirroring the 2-D constituent skip above). + if has_indexed_read: + continue + # end if + # Set "check_field" call string: if call_check_field: if has_constituent_read: diff --git a/src/physics/utils/physics_data.F90 b/src/physics/utils/physics_data.F90 index 35db4a2c..25dbf220 100644 --- a/src/physics/utils/physics_data.F90 +++ b/src/physics/utils/physics_data.F90 @@ -6,6 +6,7 @@ module physics_data public :: find_input_name_idx public :: read_field public :: read_constituent_dimensioned_field + public :: read_indexed_dimensioned_field public :: check_field public :: check_constituent_dimensioned_field public :: flush_check_field_verbose @@ -936,6 +937,166 @@ subroutine read_constituent_dimensioned_field_3d(const_props, file, std_name, ba end subroutine read_constituent_dimensioned_field_3d + ! Reads a (horizontal, N) field whose trailing dimension is a fixed-size, + ! non-vertical dimension (e.g., dust emission size bins): each slice is stored + ! on file as a separate numbered variable (e.g., + ! cam_in_dstflx_bin1 ... cam_in_dstflx_bin4), because a fixed extra dimension + ! cannot be captured as a single history/snapshot variable. The slice index is + ! appended directly to the base name, so the base names given in the registry + ! must carry any separator text (e.g., 'cam_in_dstflx_bin'). + ! The number of slices is taken from the trailing dimension of field_array. + subroutine read_indexed_dimensioned_field(file, std_name, base_var_names, timestep, field_array, error_on_not_found) + use ccpp_kinds, only: kind_phys + 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, check_allocate + use cam_logfile, only: iulog + use cam_field_read, only: cam_read_field + use phys_vars_init_check, only: mark_as_read_from_file + + ! Dummy arguments + 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_varname) + integer, intent(in) :: timestep ! Timestep to read [count] + real(kind_phys), intent(inout) :: field_array(:,:) ! Output field array (ncol, indexed dimension) + logical, optional, intent(in) :: error_on_not_found ! Flag to error and exit if not found + + ! Local variables + logical :: var_found + 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 :: slice_idx, base_idx + integer :: ierr + logical :: error_on_not_found_local + logical :: any_missing + + character(len=256) :: errmsg + + character(len=*), parameter :: subname = 'read_indexed_dimensioned_field: ' + + 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. + missing_vars = '' + + ! Allocate temporary buffer + allocate(buffer(size(field_array, 1)), stat=ierr, errmsg=errmsg) + call check_allocate(ierr, subname, 'buffer', errmsg=errmsg) + + ! Loop through all possible base names to find correct base name. + ! Note this assumes that the same base name is used for all slices. + base_idx_loop: do base_idx = 1, size(base_var_names) + ! Loop through all slices of the indexed dimension + slice_idx_loop: do slice_idx = 1, size(field_array, 2) + ! Create file variable name: + write(file_var_name, '(a,i0)') trim(base_var_names(base_idx)), slice_idx + + ! 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 slice_idx_loop + end do base_idx_loop + + if(.not. var_found) then + if(error_on_not_found_local) then + !End model run with appropriate error message: + call endrun(subname // 'Required indexed-dimensioned variables not found: No match for ' // trim(std_name)) + else + !Write message to log file, then exit subroutine: + if (masterproc) then + write(iulog, *) subname // 'Required indexed-dimensioned variables not found: No match for ' // trim(std_name) + call shr_sys_flush(iulog) + end if + return !Nothing more to do here + end if + end if + + ! Once base_idx is identified, use it in the actual slice loop: + slice_read_loop: do slice_idx = 1, size(field_array, 2) + ! Create file variable name: + write(file_var_name, '(a,i0)') trim(base_var_names(base_idx)), slice_idx + + ! 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 indexed-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 slice index in field array + field_array(:, slice_idx) = buffer(:) + + ! Check for NaN values + call shr_assert_in_domain(field_array(:, slice_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 (.not. error_on_not_found_local) then + ! Use default value (already set at initialization) + + if (masterproc) then + write(iulog, *) 'Indexed-dimensioned field ', trim(file_var_name), & + ' not found, using default value for this slice' + call shr_sys_flush(iulog) + end if + end if + end if + end do slice_read_loop + + ! Check if we should fail due to missing variables + if (any_missing .and. error_on_not_found_local) then + call endrun(subname//'Required indexed-dimensioned variables not found: ' // trim(missing_vars) // & + '. Make sure the in the registry hold the numbered-variable prefix.') + 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(buffer) + + end subroutine read_indexed_dimensioned_field + ! Check analog of read_constituent_dimensioned_field_3d: compares each slice of ! a (horizontal, vertical, constituent) field against the per-constituent ! _ variables on the check file, via the same 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 a2ee1f90..919aab86 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 @@ -33,7 +33,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 physics_data, only: read_constituent_dimensioned_field, read_indexed_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 string_utils, only: to_lower, to_upper 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 c0a2e7dc..3b26e5d9 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 @@ -33,7 +33,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 physics_data, only: read_constituent_dimensioned_field, read_indexed_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 string_utils, only: to_lower, to_upper @@ -150,7 +150,7 @@ subroutine physics_read_data(file, suite_names, timestep, read_initialized_varia call read_field(file, 'potential_temperature', input_var_names(:,name_idx), 'lev', timestep, theta) case ('air_pressure_at_sea_level') - call endrun('Cannot read slp from file'//', slp has unsupported dimension, band_number (dimension 2).') + call read_indexed_dimensioned_field(file, 'air_pressure_at_sea_level', input_var_names(:,name_idx), timestep, slp) case ('band_number') call endrun('Cannot read band_no from file'//', band_no has no horizontal dimension; band_no is a protected variable') 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 3d748b88..5ed77ebe 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 @@ -33,7 +33,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 physics_data, only: read_constituent_dimensioned_field, read_indexed_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 string_utils, only: to_lower, to_upper 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 cf26069a..4fdc5530 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 @@ -33,7 +33,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 physics_data, only: read_constituent_dimensioned_field, read_indexed_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 string_utils, only: to_lower, to_upper 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 20982d90..e8df9a24 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 @@ -33,7 +33,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 physics_data, only: read_constituent_dimensioned_field, read_indexed_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 string_utils, only: to_lower, to_upper 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 67922f9f..7b86f12b 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 @@ -33,7 +33,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 physics_data, only: read_constituent_dimensioned_field, read_indexed_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 string_utils, only: to_lower, to_upper 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 069a73e2..e52f262d 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 @@ -33,7 +33,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 physics_data, only: read_constituent_dimensioned_field, read_indexed_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 string_utils, only: to_lower, to_upper 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 842b0c39..dc909251 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 @@ -33,7 +33,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 physics_data, only: read_constituent_dimensioned_field, read_indexed_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 string_utils, only: to_lower, to_upper 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 30e6c79c..042e32ae 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 @@ -33,7 +33,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 physics_data, only: read_constituent_dimensioned_field, read_indexed_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 string_utils, only: to_lower, to_upper 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 440722c3..e75434b0 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 @@ -33,7 +33,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 physics_data, only: read_constituent_dimensioned_field, read_indexed_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 string_utils, only: to_lower, to_upper 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 267ec148..3bf1f15e 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 @@ -33,7 +33,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 physics_data, only: read_constituent_dimensioned_field, read_indexed_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 string_utils, only: to_lower, to_upper 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 5bbdb006..99b0d3f1 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 @@ -33,7 +33,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 physics_data, only: read_constituent_dimensioned_field, read_indexed_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 string_utils, only: to_lower, to_upper 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 48444036..4e66af18 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 @@ -33,7 +33,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 physics_data, only: read_constituent_dimensioned_field, read_indexed_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 string_utils, only: to_lower, to_upper 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 ecd878dd..32c1dc37 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 @@ -33,7 +33,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 physics_data, only: read_constituent_dimensioned_field, read_indexed_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 string_utils, only: to_lower, to_upper 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 2ca6475d..9257ca98 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 @@ -33,7 +33,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 physics_data, only: read_constituent_dimensioned_field, read_indexed_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 string_utils, only: to_lower, to_upper 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 a2b6dc55..6865977b 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 @@ -33,7 +33,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 physics_data, only: read_constituent_dimensioned_field, read_indexed_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 string_utils, only: to_lower, to_upper diff --git a/test/unit/python/test_write_init_files.py b/test/unit/python/test_write_init_files.py index cc563747..a80332d2 100644 --- a/test/unit/python/test_write_init_files.py +++ b/test/unit/python/test_write_init_files.py @@ -1256,8 +1256,8 @@ def test_bad_vertical_dimension(self): could be called from "read_field" has two dimensions but the second is not a vertical dimension (which read_field can't handle) and is not the number of constituents, - and exits with both the correct return - message, and with no Fortran files generated. + and generates a "read_indexed_dimensioned_field" call for it + (numbered per-slice file variables) instead of a "read_field" call. """ # Setup registry inputs: From 2b59f4da61e85e0e50412b7ec95125a0db17c492 Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Mon, 13 Jul 2026 12:17:59 -0400 Subject: [PATCH 4/5] physics_check_data: check constituent-dimensioned fields with no vertical dimension. physics_check_data skipped registry variables with a constituent dimension but no vertical dimension, because check_constituent_dimensioned_field only implemented the vertically-resolved case; constituent surface fluxes (cam_in%cflx) were therefore silently absent from the check output. Add the (horizontal, constituent) specific, mirroring the vertically-resolved one the same way the read_constituent_dimensioned_field pair does, and emit the check call for such variables. Each constituent row is checked against _ file variables (e.g. cam_in_cflx_dst_a1); rows without a matching file variable are skipped, since a capture only writes the species the source model carries. --- src/data/write_init_files.py | 7 +- src/physics/utils/physics_data.F90 | 121 ++++++++++++++++++ .../physics_inputs_constituent_dim.F90 | 4 + 3 files changed, 127 insertions(+), 5 deletions(-) diff --git a/src/data/write_init_files.py b/src/data/write_init_files.py index 8a09bcbc..74b8d877 100644 --- a/src/data/write_init_files.py +++ b/src/data/write_init_files.py @@ -1293,11 +1293,8 @@ def write_phys_check_subroutine(outfile, host_dict, host_vars, host_imports, # Constituent-indexed fields are checked per constituent against # _ file variables (mirroring the - # read_constituent_dimensioned_field call in physics_read_data). - # Only the vertically-resolved case is implemented; skip otherwise. - if has_constituent_read and levnm is None: - continue - # end if + # read_constituent_dimensioned_field call in physics_read_data), + # with and without a vertical dimension. # Indexed-dimensioned (fixed-size extra dimension) fields are read-only # inputs assembled from numbered per-slice file variables; checking is diff --git a/src/physics/utils/physics_data.F90 b/src/physics/utils/physics_data.F90 index 25dbf220..94f1d799 100644 --- a/src/physics/utils/physics_data.F90 +++ b/src/physics/utils/physics_data.F90 @@ -35,6 +35,7 @@ module physics_data end interface read_constituent_dimensioned_field interface check_constituent_dimensioned_field + module procedure check_constituent_dimensioned_field_2d !horizontal columns only (1-D buffer per constituent) module procedure check_constituent_dimensioned_field_3d !horizontal columns + vertical levels (2-D buffer per constituent) end interface check_constituent_dimensioned_field @@ -1105,6 +1106,126 @@ end subroutine read_indexed_dimensioned_field ! capture wrote), so slices that were never read/compared -- including any ! poisoned non-cluster slots -- are not reported as differences. Each compared ! slice is labeled by its file variable name in the check output. + subroutine check_constituent_dimensioned_field_2d(const_props, file, std_name, base_var_names, & + timestep, field_array, min_difference, min_relative_value, is_first, diff_found) + use ccpp_kinds, only: kind_phys + use pio, only: file_desc_t, var_desc_t + use cam_pio_utils, only: cam_pio_find_var + use cam_abortutils, only: check_allocate + use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t + use phys_vars_init_check, only: phys_var_stdnames, input_var_names, phys_var_num + use string_utils, only: to_lower + + ! Dummy arguments + type(ccpp_constituent_prop_ptr_t), intent(in) :: const_props(:) ! Constituent properties + 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 check [count] + real(kind_phys), intent(in) :: field_array(:,:) ! Field array to check (ncol, pcnst) + real(kind_phys), intent(in) :: min_difference + real(kind_phys), intent(in) :: min_relative_value + logical, intent(inout) :: is_first + logical, intent(out) :: diff_found + + ! Local variables + logical :: var_found + logical :: slice_diff_found + character(len=128) :: constituent_name + character(len=256) :: file_var_name + character(len=256) :: found_name + type(var_desc_t) :: vardesc + integer :: const_idx + integer :: ierr + + ! 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=256) :: errmsg + + character(len=*), parameter :: subname = 'check_constituent_dimensioned_field: ' + + diff_found = .false. + + !REMOVECAM: + ! Same standard-name -> short-input-name mapping as + ! read_constituent_dimensioned_field, so the check resolves exactly the file + ! variable names the read used. 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, 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. + 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 specified in the registry for this constituent + ! is the short name + const_input_idx = -1 + phys_inputvar_loop: do n = 1, phys_var_num + ! case-insensitive: see find_input_name_idx + if (to_lower(trim(phys_var_stdnames(n))) == to_lower(trim(constituent_std_name))) then + const_input_idx = n + exit phys_inputvar_loop + end if + 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. + 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 + + const_check_loop: do const_idx = 1, size(const_props) + ! Get constituent short name + constituent_name = constituent_short_names(const_idx) + + ! Create file variable name: _, trying all + ! base names (the read assumes one base name serves all constituents, so the + ! first hit is used). + var_found = .false. + base_idx_loop: do n = 1, size(base_var_names) + file_var_name = trim(base_var_names(n)) // '_' // trim(constituent_name) + call cam_pio_find_var(file, [file_var_name], found_name, vardesc, var_found) + if (var_found) exit base_idx_loop + + ! Some constituents whose names are not specified in the registry + ! will have cnst_ prepended to them (e.g., cnst_dst_a1); also try + ! matching on the file by removing this prefix: + if (constituent_name(1:5) == 'cnst_') then + file_var_name = trim(base_var_names(n)) // '_' // trim(constituent_name(6:)) + call cam_pio_find_var(file, [file_var_name], found_name, vardesc, var_found) + if (var_found) exit base_idx_loop + end if + end do base_idx_loop + + ! Constituent slices without a matching variable on the check file are + ! skipped: the capture only writes the species the source model carries. + if (.not. var_found) cycle const_check_loop + + ! Pass the resolved per-constituent name as both the search name and the + ! reporting label, so check_field's standard-name fallback search cannot + ! match an unrelated variable and diffs are reported per constituent. + call check_field(file, [found_name], timestep, & + field_array(:, const_idx), trim(found_name), & + min_difference, min_relative_value, is_first, slice_diff_found) + if (slice_diff_found) then + diff_found = .true. + end if + end do const_check_loop + + deallocate(constituent_short_names) + + end subroutine check_constituent_dimensioned_field_2d + subroutine check_constituent_dimensioned_field_3d(const_props, file, std_name, base_var_names, & vcoord_name, timestep, field_array, min_difference, min_relative_value, is_first, diff_found) use ccpp_kinds, only: kind_phys 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 4fdc5530..b647f271 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 @@ -359,6 +359,10 @@ subroutine physics_check_data(file_name, suite_names, timestep, min_difference, call check_field(file, input_var_names(:,name_idx), 'lev', timestep, theta, 'potential_temperature', min_difference, & min_relative_value, is_first, diff_found) + case ('super_cool_cat_every_const') + call check_constituent_dimensioned_field(const_props, file, 'super_cool_cat_every_const', input_var_names(:,name_idx), timestep, & + cool_cat_for_each_const, min_difference, min_relative_value, is_first, diff_found) + case ('super_cool_cat_3d_every_const') call check_constituent_dimensioned_field(const_props, file, 'super_cool_cat_3d_every_const', input_var_names(:,name_idx), 'lev', & timestep, cool_cat_3d_for_each_const, min_difference, min_relative_value, is_first, diff_found) From 6012e9e7408ff6372fb49e1cb99f8b43e0bfda14 Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Wed, 22 Jul 2026 11:32:34 -0400 Subject: [PATCH 5/5] Use case-variant lookup for constituent-dimensioned file variables. The routines added by this branch were written before sima0_16_001 and built a single '_' candidate per lookup. netCDF variable names are case-sensitive, while a constituent not enumerated in the registry derives its name from its standard name and arrives lowercased from capgen, so an uppercase snapshot field (e.g. aerochem_vmr_CO2) was never matched: - read_constituent_dimensioned_field_3d: the field fell back to its default silently, the failure mode sima0_16_001 fixed for the 2-D analog. - check_constituent_dimensioned_field_2d/3d: worse - the row is skipped as 'not on the check file', so a suite can report b4b without ever having compared those constituents. All lookups now go through constituent_dim_file_var_names. file_var_name is dropped from the two check routines, which report under found_name and so had no remaining use for it; the read routines still build it for reporting. read_indexed_dimensioned_field is unaffected: it names slices by integer index. --- src/physics/utils/physics_data.F90 | 68 ++++++++++++++++++------------ 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/src/physics/utils/physics_data.F90 b/src/physics/utils/physics_data.F90 index 94f1d799..218dc2cf 100644 --- a/src/physics/utils/physics_data.F90 +++ b/src/physics/utils/physics_data.F90 @@ -825,12 +825,14 @@ subroutine read_constituent_dimensioned_field_3d(const_props, file, std_name, ba ! 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 + ! Try to find variable in file: _, + ! trying case variants of the constituent name (see + ! constituent_dim_file_var_names). var_found = .false. - call cam_pio_find_var(file, [file_var_name], found_name, vardesc, var_found) + call cam_pio_find_var(file, & + constituent_dim_file_var_names(base_var_names(base_idx), & + constituent_name), & + found_name, vardesc, var_found) if(var_found) then exit base_idx_loop @@ -857,20 +859,26 @@ subroutine read_constituent_dimensioned_field_3d(const_props, file, std_name, ba ! Get constituent short name constituent_name = constituent_short_names(const_idx) - ! Create file variable name: _ + ! Create file variable name (used for reporting): _ file_var_name = trim(base_var_names(base_idx)) // '_' // trim(constituent_name) - ! Try to find variable in file + ! Try to find variable in file, trying case variants of the + ! constituent name (see constituent_dim_file_var_names). var_found = .false. - call cam_pio_find_var(file, [file_var_name], found_name, vardesc, var_found) + call cam_pio_find_var(file, & + constituent_dim_file_var_names(base_var_names(base_idx), & + constituent_name), & + found_name, vardesc, var_found) ! Some constituents whose names are not specified in the registry ! will have cnst_ prepended to them (e.g., cnst_dst_a1); also try reading ! from file by removing this prefix: if (.not. var_found) then if (constituent_name(1:5) == 'cnst_') then - file_var_name = trim(base_var_names(base_idx)) // '_' // trim(constituent_name(6:)) - call cam_pio_find_var(file, [file_var_name], found_name, vardesc, var_found) + call cam_pio_find_var(file, & + constituent_dim_file_var_names(base_var_names(base_idx), & + constituent_name(6:)), & + found_name, vardesc, var_found) end if end if @@ -1132,7 +1140,6 @@ subroutine check_constituent_dimensioned_field_2d(const_props, file, std_name, b logical :: var_found logical :: slice_diff_found character(len=128) :: constituent_name - character(len=256) :: file_var_name character(len=256) :: found_name type(var_desc_t) :: vardesc integer :: const_idx @@ -1188,21 +1195,26 @@ subroutine check_constituent_dimensioned_field_2d(const_props, file, std_name, b ! Get constituent short name constituent_name = constituent_short_names(const_idx) - ! Create file variable name: _, trying all - ! base names (the read assumes one base name serves all constituents, so the - ! first hit is used). + ! Search for _, trying all base names + ! (the read assumes one base name serves all constituents, so the first + ! hit is used) and the case variants of the constituent name (see + ! constituent_dim_file_var_names). var_found = .false. base_idx_loop: do n = 1, size(base_var_names) - file_var_name = trim(base_var_names(n)) // '_' // trim(constituent_name) - call cam_pio_find_var(file, [file_var_name], found_name, vardesc, var_found) + call cam_pio_find_var(file, & + constituent_dim_file_var_names(base_var_names(n), & + constituent_name), & + found_name, vardesc, var_found) if (var_found) exit base_idx_loop ! Some constituents whose names are not specified in the registry ! will have cnst_ prepended to them (e.g., cnst_dst_a1); also try ! matching on the file by removing this prefix: if (constituent_name(1:5) == 'cnst_') then - file_var_name = trim(base_var_names(n)) // '_' // trim(constituent_name(6:)) - call cam_pio_find_var(file, [file_var_name], found_name, vardesc, var_found) + call cam_pio_find_var(file, & + constituent_dim_file_var_names(base_var_names(n), & + constituent_name(6:)), & + found_name, vardesc, var_found) if (var_found) exit base_idx_loop end if end do base_idx_loop @@ -1253,7 +1265,6 @@ subroutine check_constituent_dimensioned_field_3d(const_props, file, std_name, b logical :: var_found logical :: slice_diff_found character(len=128) :: constituent_name - character(len=256) :: file_var_name character(len=256) :: found_name type(var_desc_t) :: vardesc integer :: const_idx @@ -1309,21 +1320,26 @@ subroutine check_constituent_dimensioned_field_3d(const_props, file, std_name, b ! Get constituent short name constituent_name = constituent_short_names(const_idx) - ! Create file variable name: _, trying all - ! base names (the read assumes one base name serves all constituents, so the - ! first hit is used). + ! Search for _, trying all base names + ! (the read assumes one base name serves all constituents, so the first + ! hit is used) and the case variants of the constituent name (see + ! constituent_dim_file_var_names). var_found = .false. base_idx_loop: do n = 1, size(base_var_names) - file_var_name = trim(base_var_names(n)) // '_' // trim(constituent_name) - call cam_pio_find_var(file, [file_var_name], found_name, vardesc, var_found) + call cam_pio_find_var(file, & + constituent_dim_file_var_names(base_var_names(n), & + constituent_name), & + found_name, vardesc, var_found) if (var_found) exit base_idx_loop ! Some constituents whose names are not specified in the registry ! will have cnst_ prepended to them (e.g., cnst_dst_a1); also try ! matching on the file by removing this prefix: if (constituent_name(1:5) == 'cnst_') then - file_var_name = trim(base_var_names(n)) // '_' // trim(constituent_name(6:)) - call cam_pio_find_var(file, [file_var_name], found_name, vardesc, var_found) + call cam_pio_find_var(file, & + constituent_dim_file_var_names(base_var_names(n), & + constituent_name(6:)), & + found_name, vardesc, var_found) if (var_found) exit base_idx_loop end if end do base_idx_loop