From d5fb2dddba3a787c08aa4167643608ffa69e4849 Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Mon, 20 Apr 2026 14:47:07 -0400 Subject: [PATCH 1/2] Allow for dimensions set to be used in variables in registry.xml Assisted-by: claude-opus:4.6[1m] --- src/data/generate_registry_data.py | 16 +++ .../sample_files/physics_types_parameter.F90 | 4 + .../physics_types_self_ref_dim.F90 | 105 ++++++++++++++++++ .../physics_types_self_ref_dim.meta | 31 ++++++ .../sample_files/physics_types_simple.F90 | 4 + .../sample_files/reg_good_self_ref_dim.xml | 27 +++++ test/unit/python/test_registry.py | 35 ++++++ 7 files changed, 222 insertions(+) create mode 100644 test/unit/python/sample_files/physics_types_self_ref_dim.F90 create mode 100644 test/unit/python/sample_files/physics_types_self_ref_dim.meta create mode 100644 test/unit/python/sample_files/reg_good_self_ref_dim.xml diff --git a/src/data/generate_registry_data.py b/src/data/generate_registry_data.py index b8e022528..8a1c64917 100755 --- a/src/data/generate_registry_data.py +++ b/src/data/generate_registry_data.py @@ -1405,6 +1405,15 @@ def write_allocate_routine(self, outfile, physconst_vars, var_module_dict): #then this section of code will likely need to be modified: if ('number_of_ccpp_constituents' in self.__var_dict.known_dimensions): outfile.write("use cam_constituents, only: number_of_ccpp_constituents=>num_constituents", 2) + + # Collect self-referential dimensions (defined in this module) + self_ref_dims = [] + for dim in sorted(self.__var_dict.known_dimensions): + if dim not in var_module_dict and dim != 'number_of_ccpp_constituents': + var = self.__var_dict.find_variable_by_standard_name(dim) + if var and var.local_name != dim: + self_ref_dims.append((dim, var.local_name)) + outfile.blank_line() # Dummy arguments @@ -1425,6 +1434,8 @@ def write_allocate_routine(self, outfile, physconst_vars, var_module_dict): outfile.write(f'logical :: {reall_var}', 2) subn_str = f'character(len=*), parameter :: subname = "{subname}"' outfile.write(subn_str, 2) + for dim_std, _ in self_ref_dims: + outfile.write(f'integer :: {dim_std}', 2) outfile.write('', 0) outfile.write('! Set optional argument values', 2) outfile.write(f'if (present({init_var}_in)) then', 2) @@ -1437,6 +1448,11 @@ def write_allocate_routine(self, outfile, physconst_vars, var_module_dict): outfile.write('else', 2) outfile.write(f'{reall_var} = .false.', 3) outfile.write('end if', 2) + if self_ref_dims: + outfile.blank_line() + outfile.write('! Set self-referential dimension variables', 2) + for dim_std, dim_loc in self_ref_dims: + outfile.write(f'{dim_std} = {dim_loc}', 2) outfile.write('', 0) for var in self.__var_dict.variable_list(): var.write_allocate_routine(outfile, 2, init_var, reall_var, '', physconst_vars) diff --git a/test/unit/python/sample_files/physics_types_parameter.F90 b/test/unit/python/sample_files/physics_types_parameter.F90 index 35b767ded..189b05190 100644 --- a/test/unit/python/sample_files/physics_types_parameter.F90 +++ b/test/unit/python/sample_files/physics_types_parameter.F90 @@ -52,6 +52,7 @@ subroutine allocate_physics_types_parameter_fields(set_init_val_in, reallocate_i logical :: set_init_val logical :: reallocate character(len=*), parameter :: subname = "allocate_physics_types_parameter_fields" + integer :: horizontal_dimension ! Set optional argument values if (present(set_init_val_in)) then @@ -65,6 +66,9 @@ subroutine allocate_physics_types_parameter_fields(set_init_val_in, reallocate_i reallocate = .false. end if + ! Set self-referential dimension variables + horizontal_dimension = ncol + if (set_init_val) then ncol = 0 end if diff --git a/test/unit/python/sample_files/physics_types_self_ref_dim.F90 b/test/unit/python/sample_files/physics_types_self_ref_dim.F90 new file mode 100644 index 000000000..0405c065c --- /dev/null +++ b/test/unit/python/sample_files/physics_types_self_ref_dim.F90 @@ -0,0 +1,105 @@ +! +! This work (Common Community Physics Package Framework), identified by +! NOAA, NCAR, CU/CIRES, is free of known copyright restrictions and is +! placed in the public domain. +! +! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +! THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +! IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +! CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +!> +!! @brief Auto-generated Variables for registry source file, physics_types_self_ref_dim +!! +! +module physics_types_self_ref_dim + + use ccpp_kinds, only: kind_phys + + + implicit none + private + +!> \section arg_table_physics_types_self_ref_dim Argument Table +!! \htmlinclude physics_types_self_ref_dim.html + ! ncol: Number of horizontal columns + integer, public, protected :: ncol = 0 + ! pver: Number of vertical layers + integer, public, protected :: pver = 0 + ! ndust: Number of dust size bins + integer, public :: ndust = 4 + ! rndst: Dust radii by size bin + real(kind_phys), public, allocatable :: rndst(:, :, :) + +!! public interfaces + public :: allocate_physics_types_self_ref_dim_fields + public :: physics_types_self_ref_dim_tstep_init + +contains + + subroutine allocate_physics_types_self_ref_dim_fields(set_init_val_in, reallocate_in) + use shr_infnan_mod, only: nan => shr_infnan_nan, assignment(=) + use cam_abortutils, only: endrun + + + !! Dummy arguments + logical, optional, intent(in) :: set_init_val_in + logical, optional, intent(in) :: reallocate_in + + !! Local variables + logical :: set_init_val + logical :: reallocate + character(len=*), parameter :: subname = "allocate_physics_types_self_ref_dim_fields" + integer :: dust_size_bin_dimension + integer :: horizontal_dimension + integer :: vertical_layer_dimension + + ! Set optional argument values + if (present(set_init_val_in)) then + set_init_val = set_init_val_in + else + set_init_val = .true. + end if + if (present(reallocate_in)) then + reallocate = reallocate_in + else + reallocate = .false. + end if + + ! Set self-referential dimension variables + dust_size_bin_dimension = ndust + horizontal_dimension = ncol + vertical_layer_dimension = pver + + if (set_init_val) then + ncol = 0 + end if + if (set_init_val) then + pver = 0 + end if + if (set_init_val) then + ndust = 4 + end if + if (allocated(rndst)) then + if (reallocate) then + deallocate(rndst) + else + call endrun(subname//": rndst is already allocated, cannot allocate") + end if + end if + allocate(rndst(horizontal_dimension, vertical_layer_dimension, dust_size_bin_dimension)) + if (set_init_val) then + rndst = 0.0_kind_phys + end if + end subroutine allocate_physics_types_self_ref_dim_fields + + subroutine physics_types_self_ref_dim_tstep_init() + + !! Local variables + character(len=*), parameter :: subname = "physics_types_self_ref_dim_tstep_init" + + end subroutine physics_types_self_ref_dim_tstep_init + +end module physics_types_self_ref_dim diff --git a/test/unit/python/sample_files/physics_types_self_ref_dim.meta b/test/unit/python/sample_files/physics_types_self_ref_dim.meta new file mode 100644 index 000000000..af49f1f82 --- /dev/null +++ b/test/unit/python/sample_files/physics_types_self_ref_dim.meta @@ -0,0 +1,31 @@ +[ccpp-table-properties] + name = physics_types_self_ref_dim + type = module +[ccpp-arg-table] + name = physics_types_self_ref_dim + type = module +[ ncol ] + standard_name = horizontal_dimension + long_name = Number of horizontal columns + units = count + type = integer + dimensions = () + protected = True +[ pver ] + standard_name = vertical_layer_dimension + long_name = Number of vertical layers + units = count + type = integer + dimensions = () + protected = True +[ ndust ] + standard_name = dust_size_bin_dimension + long_name = Number of dust size bins + units = count + type = integer + dimensions = () +[ rndst ] + standard_name = dust_radii_by_size_bin + units = m + type = real | kind = kind_phys + dimensions = (horizontal_dimension, vertical_layer_dimension, dust_size_bin_dimension) diff --git a/test/unit/python/sample_files/physics_types_simple.F90 b/test/unit/python/sample_files/physics_types_simple.F90 index 6aa5cf80c..38219b3fc 100644 --- a/test/unit/python/sample_files/physics_types_simple.F90 +++ b/test/unit/python/sample_files/physics_types_simple.F90 @@ -50,6 +50,7 @@ subroutine allocate_physics_types_simple_fields(set_init_val_in, reallocate_in) logical :: set_init_val logical :: reallocate character(len=*), parameter :: subname = "allocate_physics_types_simple_fields" + integer :: horizontal_dimension ! Set optional argument values if (present(set_init_val_in)) then @@ -63,6 +64,9 @@ subroutine allocate_physics_types_simple_fields(set_init_val_in, reallocate_in) reallocate = .false. end if + ! Set self-referential dimension variables + horizontal_dimension = ncol + if (set_init_val) then ncol = 0 end if diff --git a/test/unit/python/sample_files/reg_good_self_ref_dim.xml b/test/unit/python/sample_files/reg_good_self_ref_dim.xml new file mode 100644 index 000000000..cc9299860 --- /dev/null +++ b/test/unit/python/sample_files/reg_good_self_ref_dim.xml @@ -0,0 +1,27 @@ + + + + + + + Number of horizontal columns + 0 + + + Number of vertical layers + 0 + + + Number of dust size bins + 4 + + + horizontal_dimension vertical_layer_dimension dust_size_bin_dimension + 0.0_kind_phys + + + diff --git a/test/unit/python/test_registry.py b/test/unit/python/test_registry.py index d166c3670..12bef3c75 100644 --- a/test/unit/python/test_registry.py +++ b/test/unit/python/test_registry.py @@ -477,6 +477,41 @@ def test_good_complete_registry(self): amsg = f"Expected 14 metadata variables, found {num_vars}" self.assertEqual(num_vars, 14, msg=amsg) + def test_good_self_ref_dim_registry(self): + """Test that a registry with self-referential dimensions + (dimensions defined as variables in the same module) generates + correct local aliases in the allocate subroutine.""" + # Setup test + filename = os.path.join(_SAMPLE_FILES_DIR, "reg_good_self_ref_dim.xml") + out_source_name = "physics_types_self_ref_dim" + in_source = os.path.join(_SAMPLE_FILES_DIR, out_source_name + '.F90') + in_meta = os.path.join(_SAMPLE_FILES_DIR, out_source_name + '.meta') + out_source = os.path.join(_TMP_DIR, out_source_name + '.F90') + out_meta = os.path.join(_TMP_DIR, out_source_name + '.meta') + remove_files([out_source, out_meta]) + # Run test + retcode, files, _, _, _ = gen_registry(filename, 'fv', _TMP_DIR, 2, + _SRC_MOD_DIR, _CAM_ROOT, + loglevel=logging.ERROR, + error_on_no_validate=True) + # Check return code + amsg = f"Test failure: retcode={retcode}" + self.assertEqual(retcode, 0, msg=amsg) + flen = len(files) + amsg = f"Test failure: Found {flen} files, expected 1" + self.assertEqual(flen, 1, msg=amsg) + # Make sure each output file was created + amsg = f"{out_meta} does not exist" + self.assertTrue(os.path.exists(out_meta), msg=amsg) + amsg = f"{out_source} does not exist" + self.assertTrue(os.path.exists(out_source), msg=amsg) + # For each output file, make sure it matches input file + amsg = f"{out_meta} does not match {in_meta}" + self.assertTrue(filecmp.cmp(out_meta, in_meta, shallow=False), msg=amsg) + amsg = f"{out_source} does not match {in_source}" + self.assertTrue(filecmp.cmp(out_source, in_source, shallow=False), + msg=amsg) + def test_no_metadata_file_registry(self): """Test code and metadata generation from a good registry with a non-existent metadata file. From 1054bb1ac5e8b90a945f54ae9c5ef21660b40768 Mon Sep 17 00:00:00 2001 From: Haipeng Lin Date: Thu, 23 Apr 2026 13:53:12 -0400 Subject: [PATCH 2/2] Registry updates to introduce nacon, rndst with dust dimension --- src/data/registry.xml | 54 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/data/registry.xml b/src/data/registry.xml index 83ba849e0..119c8aae2 100644 --- a/src/data/registry.xml +++ b/src/data/registry.xml @@ -1491,6 +1491,18 @@ pbuf_VPWP_CLUBB_GW + + + number of dust size bins used in aerosol model + 4 + + + dust4 pbuf_dust4 cnst_dust4 + + + horizontal_dimension vertical_layer_dimension + 0.0_kind_phys + pbuf_NAAI + + + horizontal_dimension vertical_layer_dimension + 0.0_kind_phys + pbuf_NAAI_HOM + + + horizontal_dimension vertical_layer_dimension + 0.0_kind_phys + pbuf_NPCCN + + + horizontal_dimension vertical_layer_dimension dust_size_bin_dimension + 0.0_kind_phys + pbuf_NACON + + + horizontal_dimension vertical_layer_dimension dust_size_bin_dimension + 0.0_kind_phys + pbuf_RNDST + +