From 17f26ab5b1d704d161ce47398f8a01dda35b9e4f Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Tue, 12 Nov 2024 15:30:50 -0700 Subject: [PATCH 01/45] Making a generic subroutine to read model_nml:model_state_variables into a table and verify its contents; putting it in the default_model_mod --- models/utilities/default_model_mod.f90 | 80 +++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/models/utilities/default_model_mod.f90 b/models/utilities/default_model_mod.f90 index d4046eb88e..fe8d09ef9f 100644 --- a/models/utilities/default_model_mod.f90 +++ b/models/utilities/default_model_mod.f90 @@ -19,14 +19,16 @@ module default_model_mod use utilities_mod, only : error_handler, E_ERR, E_MSG, nmlfileunit, & do_output, find_namelist_in_file, check_namelist_read, & - do_nml_file, do_nml_term + do_nml_file, do_nml_term, to_upper -use netcdf_utilities_mod, only : nc_check +use netcdf_utilities_mod, only : nc_check, NF90_MAX_NAME use ensemble_manager_mod, only : ensemble_type use dart_time_io_mod, only : read_model_time, write_model_time +use obs_kind_mod, only : get_index_for_quantity + implicit none private @@ -49,7 +51,8 @@ module default_model_mod convert_vertical_obs, & convert_vertical_state, & read_model_time, & ! from the dart_time_io module - write_model_time + write_model_time, & + verify_state_variables character(len=*), parameter :: source = 'utilities/default_model_mod.f90' @@ -283,6 +286,77 @@ subroutine pert_model_copies(state_ens_handle, ens_size, pert_amp, interf_provid end subroutine pert_model_copies +!-------------------------------------------------------------------- + +!> Reads in model_nml:model_state_variables and puts it in a table. +! +!> Verifies that the namelist was filled in correctly, and check +!> that there are valid entries for the dart_kind. +!> Returns a table with columns: +! +!> netcdf_variable_name ; dart_qty_string ; update_string + +subroutine verify_state_variables(state_variables, ngood, table, qty_list, update_var) + +character(len=*), intent(inout) :: state_variables(:) +integer, intent(out) :: ngood +character(len=*), intent(out) :: table(:,:) +integer, intent(out) :: qty_list(:) ! kind number +logical, intent(out) :: update_var(:) ! logical update + +integer :: nrows, i +character(len=NF90_MAX_NAME) :: netcdf_var_name, dart_qty_str, update +character(len=256) :: string1, string2 + +nrows = size(table,1) +ngood = 0 + +RowsLoop : do i = 1, nrows ! each row contains netcdf variable name, dart qty, update + + netcdf_var_name = trim(state_variables(3*i -2)) + dart_qty_str = trim(state_variables(3*i -1)) + update = trim(state_variables(3*i)) + + call to_upper(dart_qty_str) + call to_upper(update) + + table(i,1) = trim(netcdf_var_name) + table(i,2) = trim(dart_qty_str) + table(i,3) = trim(update) + + if ( table(i,1) == ' ' .and. table(i,2) == ' ' .and. table(i,3) == ' ') exit RowsLoop ! Found end of list. + + if ( table(i,1) == ' ' .or. table(i,2) == ' ' .or. table(i,3) == ' ' ) then + string1 = 'model_nml:model_state_variables not fully specified' + call error_handler(E_ERR, 'verify_state_variables', string1) + endif + + ! Make sure DART qty is valid + + qty_list(i) = get_index_for_quantity(dart_qty_str) + if( qty_list(i) < 0 ) then + write(string1,*) 'The quantity specified in the &model_nml "', dart_qty_str, '", is not present in obs_kind_mod.f90' + call error_handler(E_ERR,'verify_state_variables',string1) + endif + + ! Make sure the update variable has a valid name + + select case (update) + case ('UPDATE') + update_var(i) = .true. + case ('NO_COPY_BACK') + update_var(i) = .false. + case default + write(string1,'(A)') 'Invalid update variable in &model_nml:model_state_variable - only UPDATE or NO_COPY_BACK are supported' + write(string2,'(6A)') 'Issue: ', trim(netcdf_var_name), ', ', trim(dart_qty_str), ', ', trim(update) + call error_handler(E_ERR,'verify_state_variables',string1, text2=string2) + end select + + ngood = ngood + 1 +enddo RowsLoop + +end subroutine verify_state_variables + !=================================================================== ! End of model_mod !=================================================================== From 179d34f6a94e85de6abbbd52eb374b73cab4b4d5 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Wed, 13 Nov 2024 13:35:33 -0700 Subject: [PATCH 02/45] Adding a unit test for the state var table read --- .../namelist/test_read_state_variables.f90 | 42 +++++++++++ developer_tests/namelist/work/input.nml | 74 +++++++++++++++++++ developer_tests/namelist/work/quickbuild.sh | 43 +++++++++++ 3 files changed, 159 insertions(+) create mode 100644 developer_tests/namelist/test_read_state_variables.f90 create mode 100644 developer_tests/namelist/work/input.nml create mode 100755 developer_tests/namelist/work/quickbuild.sh diff --git a/developer_tests/namelist/test_read_state_variables.f90 b/developer_tests/namelist/test_read_state_variables.f90 new file mode 100644 index 0000000000..f7dff57072 --- /dev/null +++ b/developer_tests/namelist/test_read_state_variables.f90 @@ -0,0 +1,42 @@ +! DART software - Copyright UCAR. This open source software is provided +! by UCAR, "as is", without charge, subject to all terms of use at +! http://www.image.ucar.edu/DAReS/DART/DART_download + +program test_read_variable_namelist + +use utilities_mod, only : find_namelist_in_file, check_namelist_read +use mpi_utilities_mod, only : initialize_mpi_utilities, & + finalize_mpi_utilities +use types_mod, only : vtablenamelength +use default_model_mod, only : verify_state_variables + +!use test + +implicit none + +integer :: iunit, io + +! DART state vector contents are specified in the input.nml:&model_nml namelist. +integer, parameter :: MAX_STATE_VARIABLES = 20 +integer, parameter :: NUM_STATE_TABLE_COLUMNS = 3 + +character(len=vtablenamelength) :: state_variables(MAX_STATE_VARIABLES * NUM_STATE_TABLE_COLUMNS ) = ' ' +integer :: nfields ! number of fields in the state vector +character(len=vtablenamelength) :: variable_table(MAX_STATE_VARIABLES, NUM_STATE_TABLE_COLUMNS) +integer :: state_qty_list(MAX_STATE_VARIABLES) +logical :: update_var_list(MAX_STATE_VARIABLES) + +namelist /model_nml/ & + state_variables + +call initialize_mpi_utilities('test_read_variable_namelist') + +call find_namelist_in_file('input.nml', 'model_nml', iunit) +read(iunit, nml = model_nml, iostat = io) +call check_namelist_read(iunit, io, 'model_nml') + +call verify_state_variables(state_variables, nfields, variable_table, state_qty_list, update_var_list) + +call finalize_mpi_utilities() + +end program test_read_variable_namelist diff --git a/developer_tests/namelist/work/input.nml b/developer_tests/namelist/work/input.nml new file mode 100644 index 0000000000..4fe4f64e55 --- /dev/null +++ b/developer_tests/namelist/work/input.nml @@ -0,0 +1,74 @@ +&model_nml + state_variables = 'SALT_CUR ', 'QTY_SALINITY ', 'UPDATE', + 'TEMP_CUR ', 'QTY_POTENTIAL_TEMPERATURE', 'UPDATE', + 'UVEL_CUR ', 'QTY_U_CURRENT_COMPONENT ', 'UPDATE', + 'VVEL_CUR ', 'QTY_V_CURRENT_COMPONENT ', 'UPDATE', + 'PSURF_CUR', 'QTY_SEA_SURFACE_PRESSURE ', 'UPDATE' +! +! state_variables = 'theta', 'QTY_POTENTIAL_TEMPERATURE', +! 'rho', 'QTY_DENSITY', +! 'uReconstructZonal', 'QTY_U_WIND_COMPONENT', +! 'uReconstructMeridional','QTY_V_WIND_COMPONENT', +! 'w', 'QTY_VERTICAL_VELOCITY', +! 'qv', 'QTY_VAPOR_MIXING_RATIO', +! 'surface_pressure', 'QTY_SURFACE_PRESSURE' +! +! state_variables = 'SH2O', 'QTY_SOIL_LIQUID_WATER', '0.0', 'NA', 'NOUPDATE', +! 'SUBSURFACE_FLUX', 'QTY_SUBSURFACE', '0.0', 'NA', 'NOUPDATE', +! 'OVERLAND_FLUX', 'QTY_OVERLAND_FLOW', '0.0', 'NA', 'NOUPDATE' + / + +&utilities_nml + module_details = .false. + write_nml = 'none' + / + +&preprocess_nml + input_obs_kind_mod_file = '../../../assimilation_code/modules/observations/DEFAULT_obs_kind_mod.F90' + quantity_files = '../../../assimilation_code/modules/observations/land_quantities_mod.f90', + '../../../assimilation_code/modules/observations/default_quantities_mod.f90' + '../../../assimilation_code/modules/observations/atmosphere_quantities_mod.f90' + output_obs_kind_mod_file = '../../../assimilation_code/modules/observations/obs_kind_mod.f90' + input_obs_def_mod_file = '../../../observations/forward_operators/DEFAULT_obs_def_mod.F90' + output_obs_def_mod_file = '../../../observations/forward_operators/obs_def_mod.f90' + input_files = '../../../observations/forward_operators/obs_def_AIRS_mod.f90', + '../../../observations/forward_operators/obs_def_AOD_mod.f90', + '../../../observations/forward_operators/obs_def_AURA_mod.f90', + '../../../observations/forward_operators/obs_def_COSMOS_mod.f90', + '../../../observations/forward_operators/obs_def_CO_Nadir_mod.f90', + '../../../observations/forward_operators/obs_def_GWD_mod.f90', + '../../../observations/forward_operators/obs_def_QuikSCAT_mod.f90', + '../../../observations/forward_operators/obs_def_SABER_mod.f90', + '../../../observations/forward_operators/obs_def_altimeter_mod.f90', + '../../../observations/forward_operators/obs_def_cloud_mod.f90', + '../../../observations/forward_operators/obs_def_dew_point_mod.f90', + '../../../observations/forward_operators/obs_def_dwl_mod.f90', + '../../../observations/forward_operators/obs_def_eval_mod.f90', + '../../../observations/forward_operators/obs_def_gps_mod.f90', + '../../../observations/forward_operators/obs_def_gts_mod.f90', + '../../../observations/forward_operators/obs_def_land_mod.f90', + '../../../observations/forward_operators/obs_def_metar_mod.f90', + '../../../observations/forward_operators/obs_def_ocean_mod.f90', + '../../../observations/forward_operators/obs_def_pe2lyr_mod.f90', + '../../../observations/forward_operators/obs_def_radar_mod.f90', + '../../../observations/forward_operators/obs_def_reanalysis_bufr_mod.f90', + '../../../observations/forward_operators/obs_def_rel_humidity_mod.f90', + '../../../observations/forward_operators/obs_def_sqg_mod.f90', + '../../../observations/forward_operators/obs_def_tower_mod.f90', + '../../../observations/forward_operators/obs_def_tpw_mod.f90', + '../../../observations/forward_operators/obs_def_upper_atm_mod.f90', + '../../../observations/forward_operators/obs_def_vortex_mod.f90', + '../../../observations/forward_operators/obs_def_wind_speed_mod.f90' + / + +&mpi_utilities_nml + / + +&obs_kind_nml + / + +&ensemble_manager_nml + / + +&state_vector_io_nml + / diff --git a/developer_tests/namelist/work/quickbuild.sh b/developer_tests/namelist/work/quickbuild.sh new file mode 100755 index 0000000000..ae8d9443b3 --- /dev/null +++ b/developer_tests/namelist/work/quickbuild.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +# DART software - Copyright UCAR. This open source software is provided +# by UCAR, "as is", without charge, subject to all terms of use at +# http://www.image.ucar.edu/DAReS/DART/DART_download + +main() { + +export DART=$(git rev-parse --show-toplevel) +source "$DART"/build_templates/buildfunctions.sh + +MODEL="none" +EXTRA="$DART"/models/template/threed_model_mod.f90 +dev_test=1 +TEST="namelist" +LOCATION="threed_sphere" + +programs=( +test_read_state_variables +) + + +# quickbuild arguments +arguments "$@" + +# clean the directory +\rm -f -- *.o *.mod Makefile .cppdefs + +# build any NetCDF files from .cdl files +cdl_to_netcdf + +# build and run preprocess before making any other DART executables +buildpreprocess + +# build +buildit + +# clean up +\rm -f -- *.o *.mod + +} + +main "$@" From f18942eb3c606f010f309b82b8f7eb2dd57d7a77 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Wed, 13 Nov 2024 14:06:27 -0700 Subject: [PATCH 03/45] Fixing file name for dev test --- ...read_state_variables.f90 => test_read_variable_namelist.f90} | 0 developer_tests/namelist/work/quickbuild.sh | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename developer_tests/namelist/{test_read_state_variables.f90 => test_read_variable_namelist.f90} (100%) diff --git a/developer_tests/namelist/test_read_state_variables.f90 b/developer_tests/namelist/test_read_variable_namelist.f90 similarity index 100% rename from developer_tests/namelist/test_read_state_variables.f90 rename to developer_tests/namelist/test_read_variable_namelist.f90 diff --git a/developer_tests/namelist/work/quickbuild.sh b/developer_tests/namelist/work/quickbuild.sh index ae8d9443b3..3ea44e166d 100755 --- a/developer_tests/namelist/work/quickbuild.sh +++ b/developer_tests/namelist/work/quickbuild.sh @@ -16,7 +16,7 @@ TEST="namelist" LOCATION="threed_sphere" programs=( -test_read_state_variables +test_read_variable_namelist ) From c0bb758cebcf1c8313264a70dc6077e49787145e Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Mon, 9 Dec 2024 19:46:33 -0700 Subject: [PATCH 04/45] Changing the name of the subroutine to get_state_variables Updating the generic routine by including the type state_vars_type and storing the contents of the nml entry here Including a conditional use_clamping that tells whether or not the model's nml entry includes clamping values; executing a separate do loop over the rows if true to allow for reading in these values --- models/utilities/default_model_mod.f90 | 197 +++++++++++++++++-------- 1 file changed, 135 insertions(+), 62 deletions(-) diff --git a/models/utilities/default_model_mod.f90 b/models/utilities/default_model_mod.f90 index fe8d09ef9f..82d59355ac 100644 --- a/models/utilities/default_model_mod.f90 +++ b/models/utilities/default_model_mod.f90 @@ -52,7 +52,16 @@ module default_model_mod convert_vertical_state, & read_model_time, & ! from the dart_time_io module write_model_time, & - verify_state_variables + get_state_variables, & + state_var_type + +type :: state_var_type + integer :: nvars + character(len=64), allocatable :: netcdf_var_names(:) + integer, allocatable :: qtys(:) + real(r8), allocatable :: clamp_values(:, :) + logical, allocatable :: updates(:) +end type state_var_type character(len=*), parameter :: source = 'utilities/default_model_mod.f90' @@ -288,74 +297,138 @@ end subroutine pert_model_copies !-------------------------------------------------------------------- -!> Reads in model_nml:model_state_variables and puts it in a table. +!> Reads in model_nml:model_state_variables and returns a +!> state_var_type state_vars with nvars ; netcdf variable names ; +!> qtys (kinds) ; clamp values ; updates ! -!> Verifies that the namelist was filled in correctly, and check +!> Verifies that the namelist was filled in correctly, and checks !> that there are valid entries for the dart_kind. -!> Returns a table with columns: -! -!> netcdf_variable_name ; dart_qty_string ; update_string -subroutine verify_state_variables(state_variables, ngood, table, qty_list, update_var) +subroutine get_state_variables(nml_state_vars, MAX_STATE_VARIABLES, use_clamping, state_vars) -character(len=*), intent(inout) :: state_variables(:) -integer, intent(out) :: ngood -character(len=*), intent(out) :: table(:,:) -integer, intent(out) :: qty_list(:) ! kind number -logical, intent(out) :: update_var(:) ! logical update +character(len=*), intent(inout) :: nml_state_vars(:) +logical, intent(in) :: use_clamping +integer, intent(in) :: MAX_STATE_VARIABLES +type(state_var_type), intent(out) :: state_vars -integer :: nrows, i character(len=NF90_MAX_NAME) :: netcdf_var_name, dart_qty_str, update character(len=256) :: string1, string2 - -nrows = size(table,1) -ngood = 0 - -RowsLoop : do i = 1, nrows ! each row contains netcdf variable name, dart qty, update - - netcdf_var_name = trim(state_variables(3*i -2)) - dart_qty_str = trim(state_variables(3*i -1)) - update = trim(state_variables(3*i)) - - call to_upper(dart_qty_str) - call to_upper(update) - - table(i,1) = trim(netcdf_var_name) - table(i,2) = trim(dart_qty_str) - table(i,3) = trim(update) - - if ( table(i,1) == ' ' .and. table(i,2) == ' ' .and. table(i,3) == ' ') exit RowsLoop ! Found end of list. - - if ( table(i,1) == ' ' .or. table(i,2) == ' ' .or. table(i,3) == ' ' ) then - string1 = 'model_nml:model_state_variables not fully specified' - call error_handler(E_ERR, 'verify_state_variables', string1) - endif - - ! Make sure DART qty is valid - - qty_list(i) = get_index_for_quantity(dart_qty_str) - if( qty_list(i) < 0 ) then - write(string1,*) 'The quantity specified in the &model_nml "', dart_qty_str, '", is not present in obs_kind_mod.f90' - call error_handler(E_ERR,'verify_state_variables',string1) - endif - - ! Make sure the update variable has a valid name - - select case (update) - case ('UPDATE') - update_var(i) = .true. - case ('NO_COPY_BACK') - update_var(i) = .false. - case default - write(string1,'(A)') 'Invalid update variable in &model_nml:model_state_variable - only UPDATE or NO_COPY_BACK are supported' - write(string2,'(6A)') 'Issue: ', trim(netcdf_var_name), ', ', trim(dart_qty_str), ', ', trim(update) - call error_handler(E_ERR,'verify_state_variables',string1, text2=string2) - end select - - ngood = ngood + 1 -enddo RowsLoop - -end subroutine verify_state_variables +integer :: i, ivar + +if(use_clamping) then + ! Loop through the variables array to get the actual count of the number of variables + do ivar = 1, MAX_STATE_VARIABLES + ! If the first element in the row is an empty string, the loop has exceeded the extent of the variables + if (nml_state_vars(5*ivar-4) == '') then + state_vars%nvars = ivar-1 + exit + endif + enddo +else + ! Loop through the variables array to get the actual count of the number of variables + do ivar = 1, MAX_STATE_VARIABLES + ! If the first element in the row is an empty string, the loop has exceeded the extent of the variables + if (nml_state_vars(3*ivar-2) == '') then + state_vars%nvars = ivar-1 + exit + endif + enddo +endif + +! Allocate the arrays in the var derived type +allocate(state_vars%netcdf_var_names(state_vars%nvars), state_vars%qtys(state_vars%nvars), state_vars%clamp_values(state_vars%nvars, 2), state_vars%updates(state_vars%nvars)) + +if(use_clamping) then + + RowsLoop : do i = 1, state_vars%nvars + + netcdf_var_name = trim(nml_state_vars(5*i-4)) + state_vars%netcdf_var_names(i) = trim(netcdf_var_name) + + dart_qty_str = trim(nml_state_vars(5*i-3)) + call to_upper(dart_qty_str) + ! Make sure DART qty is valid + state_vars%qtys(i) = get_index_for_quantity(dart_qty_str) + if( state_vars%qtys(i) < 0 ) then + write(string1,*) 'The quantity specified in the &model_nml "', dart_qty_str, '", is not present in obs_kind_mod.f90' + call error_handler(E_ERR,'verify_state_variables',string1) + endif + + if (nml_state_vars(5*i-2) /= 'NA') then + read(nml_state_vars(5*i-2), '(d16.8)') state_vars%clamp_values(i,1) + else + state_vars%clamp_values(i,1) = MISSING_R8 + endif + + if (nml_state_vars(5*i-1) /= 'NA') then + read(nml_state_vars(5*i-1), '(d16.8)') state_vars%clamp_values(i,2) + else + state_vars%clamp_values(i,2) = MISSING_R8 + endif + + update = trim(nml_state_vars(5*i)) + call to_upper(update) + select case (update) + case ('UPDATE') + state_vars%updates(i) = .true. + case ('NO_COPY_BACK') + state_vars%updates(i) = .false. + case default + write(string1,'(A)') 'Invalid update variable in &model_nml:model_state_variable - only UPDATE or NO_COPY_BACK are supported' + write(string2,'(6A)') 'Issue: ', trim(netcdf_var_name), ', ', trim(dart_qty_str), ', ', trim(update) + call error_handler(E_ERR,'verify_state_variables',string1, text2=string2) + end select + + ! Checking that the rows in the nml entry are all complete + if ( dart_qty_str == '' .or. nml_state_vars(5*i-2) == '' .or. nml_state_vars(5*i-1) == '' .or. update == '' ) then + string1 = 'model_nml:model_state_variables not fully specified' + call error_handler(E_ERR, 'verify_state_variables', string1) + endif + + enddo RowsLoop + +else + + RowsLoopNoClamp : do i = 1, state_vars%nvars + + state_vars%clamp_values(:,:) = MISSING_R8 + + netcdf_var_name = trim(nml_state_vars(3*i-2)) + state_vars%netcdf_var_names(i) = trim(netcdf_var_name) + + dart_qty_str = trim(nml_state_vars(3*i-1)) + call to_upper(dart_qty_str) + ! Make sure DART qty is valid + state_vars%qtys(i) = get_index_for_quantity(dart_qty_str) + if( state_vars%qtys(i) < 0 ) then + write(string1,*) 'The quantity specified in the &model_nml "', dart_qty_str, '", is not present in obs_kind_mod.f90' + call error_handler(E_ERR,'verify_state_variables',string1) + endif + + update = trim(nml_state_vars(3*i)) + call to_upper(update) + select case (update) + case ('UPDATE') + state_vars%updates(i) = .true. + case ('NO_COPY_BACK') + state_vars%updates(i) = .false. + case default + write(string1,'(A)') 'Invalid update variable in &model_nml:model_state_variable - only UPDATE or NO_COPY_BACK are supported' + write(string2,'(6A)') 'Issue: ', trim(netcdf_var_name), ', ', trim(dart_qty_str), ', ', trim(update) + call error_handler(E_ERR,'verify_state_variables',string1, text2=string2) + end select + + ! Checking that the rows in the nml entry are all complete + if ( dart_qty_str == '' .or. update == '' ) then + string1 = 'model_nml:model_state_variables not fully specified' + call error_handler(E_ERR, 'verify_state_variables', string1) + endif + + enddo RowsLoopNoClamp + +endif + +end subroutine get_state_variables !=================================================================== ! End of model_mod From 1914a420ce6423f189e4cbd174df225f19c08e07 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Mon, 9 Dec 2024 19:53:48 -0700 Subject: [PATCH 05/45] Changing the name of the dev test to better fit the subroutine name and its functionality. Adding checks against known values with fortran-test-anything --- .../namelist/test_get_state_variables.f90 | 79 +++++++++++++++++++ .../namelist/test_read_variable_namelist.f90 | 42 ---------- developer_tests/namelist/work/quickbuild.sh | 2 +- 3 files changed, 80 insertions(+), 43 deletions(-) create mode 100644 developer_tests/namelist/test_get_state_variables.f90 delete mode 100644 developer_tests/namelist/test_read_variable_namelist.f90 diff --git a/developer_tests/namelist/test_get_state_variables.f90 b/developer_tests/namelist/test_get_state_variables.f90 new file mode 100644 index 0000000000..7b32a0664a --- /dev/null +++ b/developer_tests/namelist/test_get_state_variables.f90 @@ -0,0 +1,79 @@ +! DART software - Copyright UCAR. This open source software is provided +! by UCAR, "as is", without charge, subject to all terms of use at +! http://www.image.ucar.edu/DAReS/DART/DART_download + +program test_get_state_variables + +use utilities_mod, only : find_namelist_in_file, check_namelist_read +use mpi_utilities_mod, only : initialize_mpi_utilities, task_count, my_task_id, finalize_mpi_utilities +use types_mod, only : vtablenamelength, MISSING_R8 +use default_model_mod, only : get_state_variables, state_var_type + +use test ! fortran-testanything + +implicit none + +integer :: iunit, io +logical :: use_clamping +type(state_var_type) :: state_vars + +! DART state vector contents are specified in the input.nml:&model_nml namelist. +integer, parameter :: MAX_STATE_VARIABLES = 20 +integer, parameter :: NUM_STATE_TABLE_COLUMNS = 3 +character(len=vtablenamelength) :: state_variables(MAX_STATE_VARIABLES * NUM_STATE_TABLE_COLUMNS ) = ' ' + +namelist /model_nml/ & + state_variables + +call initialize_mpi_utilities('test_get_state_variables') + +if (my_task_id() == 0 ) then + call plan(12*task_count()) +endif + +! Using namelist file WITHOUT clamping values +use_clamping = .false. + +call find_namelist_in_file('input.nml', 'model_nml', iunit) +read(iunit, nml = model_nml, iostat = io) +call check_namelist_read(iunit, io, 'model_nml') + +call get_state_variables(state_variables, MAX_STATE_VARIABLES, use_clamping, state_vars) + +write(*,*) 'state_vars%nvars: ', state_vars%nvars +write(*,*) 'state_vars%netcdf_var_names: ', state_vars%netcdf_var_names +write(*,*) 'state_vars%qtys: ', state_vars%qtys +write(*,*) 'state_vars%updates: ', state_vars%updates + +call ok(state_vars%nvars == 5) !OK +call ok(state_vars%netcdf_var_names(2) == '') !NOT OK +call ok(state_vars%qtys(3) == 20) !NOT OK +call ok(state_vars%clamp_values(1,1) == MISSING_R8) !OK +call ok(state_vars%clamp_values(5,2) == 0.4) !NOT OK +call ok(state_vars%updates(4) == .true.) !OK + +! Using namelist file WITH clamping values +use_clamping = .true. + +call find_namelist_in_file('input.nml.clamp', 'model_nml', iunit) +read(iunit, nml = model_nml, iostat = io) +call check_namelist_read(iunit, io, 'model_nml') + +call get_state_variables(state_variables, MAX_STATE_VARIABLES, use_clamping, state_vars) +write(*,*) 'state_vars%nvars: ', state_vars%nvars +write(*,*) 'state_vars%netcdf_var_names: ', state_vars%netcdf_var_names +write(*,*) 'state_vars%qtys: ', state_vars%qtys +write(*,*) 'state_vars%clamp_values(:,1): ', state_vars%clamp_values(:, 1) +write(*,*) 'state_vars%clamp_values(:,2): ', state_vars%clamp_values(:, 2) +write(*,*) 'state_vars%updates: ', state_vars%updates + +call ok(state_vars%nvars == 7) !NOT OK +call ok(state_vars%netcdf_var_names(2) == 'TEMP_CUR') !OK +call ok(state_vars%qtys(3) == 356) !OK +call ok(state_vars%clamp_values(1,1) == 99) !NOT OK +call ok(state_vars%clamp_values(5,2) == 0.0) !OK +call ok(state_vars%updates(4) == .false.) !NOT OK + +call finalize_mpi_utilities() + +end program test_get_state_variables diff --git a/developer_tests/namelist/test_read_variable_namelist.f90 b/developer_tests/namelist/test_read_variable_namelist.f90 deleted file mode 100644 index f7dff57072..0000000000 --- a/developer_tests/namelist/test_read_variable_namelist.f90 +++ /dev/null @@ -1,42 +0,0 @@ -! DART software - Copyright UCAR. This open source software is provided -! by UCAR, "as is", without charge, subject to all terms of use at -! http://www.image.ucar.edu/DAReS/DART/DART_download - -program test_read_variable_namelist - -use utilities_mod, only : find_namelist_in_file, check_namelist_read -use mpi_utilities_mod, only : initialize_mpi_utilities, & - finalize_mpi_utilities -use types_mod, only : vtablenamelength -use default_model_mod, only : verify_state_variables - -!use test - -implicit none - -integer :: iunit, io - -! DART state vector contents are specified in the input.nml:&model_nml namelist. -integer, parameter :: MAX_STATE_VARIABLES = 20 -integer, parameter :: NUM_STATE_TABLE_COLUMNS = 3 - -character(len=vtablenamelength) :: state_variables(MAX_STATE_VARIABLES * NUM_STATE_TABLE_COLUMNS ) = ' ' -integer :: nfields ! number of fields in the state vector -character(len=vtablenamelength) :: variable_table(MAX_STATE_VARIABLES, NUM_STATE_TABLE_COLUMNS) -integer :: state_qty_list(MAX_STATE_VARIABLES) -logical :: update_var_list(MAX_STATE_VARIABLES) - -namelist /model_nml/ & - state_variables - -call initialize_mpi_utilities('test_read_variable_namelist') - -call find_namelist_in_file('input.nml', 'model_nml', iunit) -read(iunit, nml = model_nml, iostat = io) -call check_namelist_read(iunit, io, 'model_nml') - -call verify_state_variables(state_variables, nfields, variable_table, state_qty_list, update_var_list) - -call finalize_mpi_utilities() - -end program test_read_variable_namelist diff --git a/developer_tests/namelist/work/quickbuild.sh b/developer_tests/namelist/work/quickbuild.sh index 3ea44e166d..07bef04b65 100755 --- a/developer_tests/namelist/work/quickbuild.sh +++ b/developer_tests/namelist/work/quickbuild.sh @@ -16,7 +16,7 @@ TEST="namelist" LOCATION="threed_sphere" programs=( -test_read_variable_namelist +test_get_state_variables ) From c168bbe81b5a9e758103be2ba6e4b9790561573a Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Mon, 9 Dec 2024 19:57:18 -0700 Subject: [PATCH 06/45] Removing MOM6 version of subroutine and making necessary adjustments --- models/MOM6/model_mod.f90 | 107 ++++++-------------------------------- 1 file changed, 16 insertions(+), 91 deletions(-) diff --git a/models/MOM6/model_mod.f90 b/models/MOM6/model_mod.f90 index e6ebcd03f3..2ff74b47be 100644 --- a/models/MOM6/model_mod.f90 +++ b/models/MOM6/model_mod.f90 @@ -21,11 +21,9 @@ module model_mod get_location, query_location, VERTISLEVEL, & VERTISHEIGHT, set_vertical -use utilities_mod, only : error_handler, & - E_ERR, E_MSG, & +use utilities_mod, only : error_handler, E_ERR, E_MSG, & nmlfileunit, do_output, do_nml_file, do_nml_term, & - find_namelist_in_file, check_namelist_read, & - to_upper + find_namelist_in_file, check_namelist_read use netcdf_utilities_mod, only : nc_add_global_attribute, nc_synchronize_file, & nc_add_global_creation_time, & @@ -47,9 +45,8 @@ module model_mod use distributed_state_mod, only : get_state, get_state_array -use obs_kind_mod, only : get_index_for_quantity, QTY_U_CURRENT_COMPONENT, & - QTY_V_CURRENT_COMPONENT, QTY_LAYER_THICKNESS, & - QTY_DRY_LAND, QTY_SALINITY +use obs_kind_mod, only : QTY_U_CURRENT_COMPONENT, QTY_V_CURRENT_COMPONENT, & + QTY_LAYER_THICKNESS, QTY_DRY_LAND, QTY_SALINITY use ensemble_manager_mod, only : ensemble_type @@ -60,7 +57,8 @@ module model_mod use default_model_mod, only : pert_model_copies, write_model_time, & init_time => fail_init_time, & init_conditions => fail_init_conditions, & - convert_vertical_obs, adv_1step + convert_vertical_obs, adv_1step, & + get_state_variables, state_var_type implicit none private @@ -108,6 +106,7 @@ module model_mod ! DART state vector contents are specified in the input.nml:&model_nml namelist. integer, parameter :: MAX_STATE_VARIABLES = 10 integer, parameter :: NUM_STATE_TABLE_COLUMNS = 3 +logical, parameter :: use_clamping = .false. ! model_interpolate failure codes integer, parameter :: NOT_IN_STATE = 12 @@ -150,10 +149,7 @@ module model_mod subroutine static_init_model() integer :: iunit, io -character(len=vtablenamelength) :: variable_table(MAX_STATE_VARIABLES, NUM_STATE_TABLE_COLUMNS) - -integer :: state_qty_list(MAX_STATE_VARIABLES) -logical :: update_var_list(MAX_STATE_VARIABLES) +type(state_var_type) :: state_vars ! identifiers for variable_table integer, parameter :: VAR_NAME_INDEX = 1 @@ -180,15 +176,16 @@ subroutine static_init_model() assimilation_time_step = set_time(assimilation_period_seconds, & assimilation_period_days) -! verify that the model_state_variables namelist was filled in correctly. -! returns variable_table which has variable names, kinds and update strings. -call verify_state_variables(model_state_variables, nfields, variable_table, state_qty_list, update_var_list) +! Reads the model_state_variables namelist entry into a table and returns +! state_var_type state_vars with nvars, netcdf variable names, kinds/qtys, +! clamp values (optional), and update strings. +call get_state_variables(model_state_variables, MAX_STATE_VARIABLES, use_clamping, state_vars) ! Define which variables are in the model state -dom_id = add_domain(template_file, nfields, & - var_names = variable_table(1:nfields, VAR_NAME_INDEX), & - kind_list = state_qty_list(1:nfields), & - update_list = update_var_list(1:nfields)) +dom_id = add_domain(template_file, state_vars%nvars, & + var_names = state_vars%netcdf_var_names(1:state_vars%nvars), & + kind_list = state_vars%qtys(1:state_vars%nvars), & + update_list = state_vars%updates(1:state_vars%nvars)) model_size = get_domain_size(dom_id) @@ -897,78 +894,6 @@ function get_interp_handle(qty) end function - -!------------------------------------------------------------------ -! Verify that the namelist was filled in correctly, and check -! that there are valid entries for the dart_kind. -! Returns a table with columns: -! -! netcdf_variable_name ; dart_qty_string ; update_string - -subroutine verify_state_variables(state_variables, ngood, table, qty_list, update_var) - -character(len=*), intent(inout) :: state_variables(:) -integer, intent(out) :: ngood -character(len=*), intent(out) :: table(:,:) -integer, intent(out) :: qty_list(:) ! kind number -logical, intent(out) :: update_var(:) ! logical update - -integer :: nrows, i -character(len=NF90_MAX_NAME) :: varname, dartstr, update -character(len=256) :: string1, string2 - -if ( .not. module_initialized ) call static_init_model - -nrows = size(table,1) - -ngood = 0 - -MyLoop : do i = 1, nrows - - varname = trim(state_variables(3*i -2)) - dartstr = trim(state_variables(3*i -1)) - update = trim(state_variables(3*i )) - - call to_upper(update) - - table(i,1) = trim(varname) - table(i,2) = trim(dartstr) - table(i,3) = trim(update) - - if ( table(i,1) == ' ' .and. table(i,2) == ' ' .and. table(i,3) == ' ') exit MyLoop ! Found end of list. - - if ( table(i,1) == ' ' .or. table(i,2) == ' ' .or. table(i,3) == ' ' ) then - string1 = 'model_nml:model_state_variables not fully specified' - call error_handler(E_ERR,'verify_state_variables',string1) - endif - - ! Make sure DART qty is valid - - qty_list(i) = get_index_for_quantity(dartstr) - if( qty_list(i) < 0 ) then - write(string1,'(''there is no obs_kind <'',a,''> in obs_kind_mod.f90'')') trim(dartstr) - call error_handler(E_ERR,'verify_state_variables',string1) - endif - - ! Make sure the update variable has a valid name - - select case (update) - case ('UPDATE') - update_var(i) = .true. - case ('NO_COPY_BACK') - update_var(i) = .false. - case default - write(string1,'(A)') 'only UPDATE or NO_COPY_BACK supported in model_state_variable namelist' - write(string2,'(6A)') 'you provided : ', trim(varname), ', ', trim(dartstr), ', ', trim(update) - call error_handler(E_ERR,'verify_state_variables',string1, text2=string2) - end select - - ngood = ngood + 1 -enddo MyLoop - - -end subroutine verify_state_variables - !-------------------------------------------------------------------- function read_model_time(filename) From e82df2e8c57276376d3b99a6223fa0e5cf88a499 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Mon, 9 Dec 2024 19:59:57 -0700 Subject: [PATCH 07/45] including other needed input.nml file for test --- developer_tests/namelist/work/input.nml.clamp | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 developer_tests/namelist/work/input.nml.clamp diff --git a/developer_tests/namelist/work/input.nml.clamp b/developer_tests/namelist/work/input.nml.clamp new file mode 100644 index 0000000000..6a4d315fa8 --- /dev/null +++ b/developer_tests/namelist/work/input.nml.clamp @@ -0,0 +1,62 @@ +&model_nml + state_variables = 'SALT_CUR ', 'QTY_SALINITY', '0.0', '0.0', 'UPDATE', + 'TEMP_CUR ', 'QTY_POTENTIAL_TEMPERATURE', '0.0', '0.0', 'UPDATE', + 'UVEL_CUR ', 'QTY_U_CURRENT_COMPONENT ', '0.0', '0.0', 'UPDATE', + 'VVEL_CUR ', 'QTY_V_CURRENT_COMPONENT ', '0.0', '0.0', 'UPDATE', + 'PSURF_CUR', 'QTY_SEA_SURFACE_PRESSURE ', '0.0', '0.0', 'UPDATE' + / + +&utilities_nml + module_details = .false. + write_nml = 'none' + / + +&preprocess_nml + input_obs_kind_mod_file = '../../../assimilation_code/modules/observations/DEFAULT_obs_kind_mod.F90' + quantity_files = '../../../assimilation_code/modules/observations/land_quantities_mod.f90', + '../../../assimilation_code/modules/observations/default_quantities_mod.f90' + '../../../assimilation_code/modules/observations/atmosphere_quantities_mod.f90' + output_obs_kind_mod_file = '../../../assimilation_code/modules/observations/obs_kind_mod.f90' + input_obs_def_mod_file = '../../../observations/forward_operators/DEFAULT_obs_def_mod.F90' + output_obs_def_mod_file = '../../../observations/forward_operators/obs_def_mod.f90' + input_files = '../../../observations/forward_operators/obs_def_AIRS_mod.f90', + '../../../observations/forward_operators/obs_def_AOD_mod.f90', + '../../../observations/forward_operators/obs_def_AURA_mod.f90', + '../../../observations/forward_operators/obs_def_COSMOS_mod.f90', + '../../../observations/forward_operators/obs_def_CO_Nadir_mod.f90', + '../../../observations/forward_operators/obs_def_GWD_mod.f90', + '../../../observations/forward_operators/obs_def_QuikSCAT_mod.f90', + '../../../observations/forward_operators/obs_def_SABER_mod.f90', + '../../../observations/forward_operators/obs_def_altimeter_mod.f90', + '../../../observations/forward_operators/obs_def_cloud_mod.f90', + '../../../observations/forward_operators/obs_def_dew_point_mod.f90', + '../../../observations/forward_operators/obs_def_dwl_mod.f90', + '../../../observations/forward_operators/obs_def_eval_mod.f90', + '../../../observations/forward_operators/obs_def_gps_mod.f90', + '../../../observations/forward_operators/obs_def_gts_mod.f90', + '../../../observations/forward_operators/obs_def_land_mod.f90', + '../../../observations/forward_operators/obs_def_metar_mod.f90', + '../../../observations/forward_operators/obs_def_ocean_mod.f90', + '../../../observations/forward_operators/obs_def_pe2lyr_mod.f90', + '../../../observations/forward_operators/obs_def_radar_mod.f90', + '../../../observations/forward_operators/obs_def_reanalysis_bufr_mod.f90', + '../../../observations/forward_operators/obs_def_rel_humidity_mod.f90', + '../../../observations/forward_operators/obs_def_sqg_mod.f90', + '../../../observations/forward_operators/obs_def_tower_mod.f90', + '../../../observations/forward_operators/obs_def_tpw_mod.f90', + '../../../observations/forward_operators/obs_def_upper_atm_mod.f90', + '../../../observations/forward_operators/obs_def_vortex_mod.f90', + '../../../observations/forward_operators/obs_def_wind_speed_mod.f90' + / + +&mpi_utilities_nml + / + +&obs_kind_nml + / + +&ensemble_manager_nml + / + +&state_vector_io_nml + / From f177c69a0784bbcbfa0a53c86ef1c6b4e342e941 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Thu, 12 Dec 2024 10:28:36 -0700 Subject: [PATCH 08/45] Removing MPI from dev test --- developer_tests/namelist/test_get_state_variables.f90 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/developer_tests/namelist/test_get_state_variables.f90 b/developer_tests/namelist/test_get_state_variables.f90 index 7b32a0664a..e3bb7bc7e5 100644 --- a/developer_tests/namelist/test_get_state_variables.f90 +++ b/developer_tests/namelist/test_get_state_variables.f90 @@ -5,7 +5,7 @@ program test_get_state_variables use utilities_mod, only : find_namelist_in_file, check_namelist_read -use mpi_utilities_mod, only : initialize_mpi_utilities, task_count, my_task_id, finalize_mpi_utilities +use mpi_utilities_mod, only : initialize_mpi_utilities, finalize_mpi_utilities use types_mod, only : vtablenamelength, MISSING_R8 use default_model_mod, only : get_state_variables, state_var_type @@ -27,9 +27,7 @@ program test_get_state_variables call initialize_mpi_utilities('test_get_state_variables') -if (my_task_id() == 0 ) then - call plan(12*task_count()) -endif +call plan(12) ! Using namelist file WITHOUT clamping values use_clamping = .false. @@ -60,6 +58,7 @@ program test_get_state_variables call check_namelist_read(iunit, io, 'model_nml') call get_state_variables(state_variables, MAX_STATE_VARIABLES, use_clamping, state_vars) + write(*,*) 'state_vars%nvars: ', state_vars%nvars write(*,*) 'state_vars%netcdf_var_names: ', state_vars%netcdf_var_names write(*,*) 'state_vars%qtys: ', state_vars%qtys From ccf37db4752f14906f4c9ebefdc926eb71fc6ea4 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Thu, 12 Dec 2024 11:21:26 -0700 Subject: [PATCH 09/45] Adding an interface to create 2 distinct subroutines for get_state_variables: clamp and noclamp --- models/utilities/default_model_mod.f90 | 244 ++++++++++++++----------- 1 file changed, 135 insertions(+), 109 deletions(-) diff --git a/models/utilities/default_model_mod.f90 b/models/utilities/default_model_mod.f90 index 82d59355ac..e25cdbd51b 100644 --- a/models/utilities/default_model_mod.f90 +++ b/models/utilities/default_model_mod.f90 @@ -55,6 +55,11 @@ module default_model_mod get_state_variables, & state_var_type +interface get_state_variables + module procedure get_state_variables_clamp + module procedure get_state_variables_noclamp +end interface + type :: state_var_type integer :: nvars character(len=64), allocatable :: netcdf_var_names(:) @@ -304,131 +309,152 @@ end subroutine pert_model_copies !> Verifies that the namelist was filled in correctly, and checks !> that there are valid entries for the dart_kind. -subroutine get_state_variables(nml_state_vars, MAX_STATE_VARIABLES, use_clamping, state_vars) +subroutine get_state_variables_clamp(nml_state_vars, MAX_STATE_VARIABLES, use_clamping, state_vars) character(len=*), intent(inout) :: nml_state_vars(:) -logical, intent(in) :: use_clamping integer, intent(in) :: MAX_STATE_VARIABLES +logical, intent(in) :: use_clamping type(state_var_type), intent(out) :: state_vars character(len=NF90_MAX_NAME) :: netcdf_var_name, dart_qty_str, update character(len=256) :: string1, string2 integer :: i, ivar -if(use_clamping) then - ! Loop through the variables array to get the actual count of the number of variables - do ivar = 1, MAX_STATE_VARIABLES - ! If the first element in the row is an empty string, the loop has exceeded the extent of the variables - if (nml_state_vars(5*ivar-4) == '') then - state_vars%nvars = ivar-1 - exit - endif - enddo -else - ! Loop through the variables array to get the actual count of the number of variables - do ivar = 1, MAX_STATE_VARIABLES - ! If the first element in the row is an empty string, the loop has exceeded the extent of the variables - if (nml_state_vars(3*ivar-2) == '') then - state_vars%nvars = ivar-1 - exit - endif - enddo +if(.not. use_clamping) then + string1 = 'logical use_clamping set to false when values for clamping are required in this model nml' + call error_handler(E_ERR, 'get_state_variables_clamp', string1) endif +! Loop through the variables array to get the actual count of the number of variables +do ivar = 1, MAX_STATE_VARIABLES + ! If the first element in the row is an empty string, the loop has exceeded the extent of the variables + if (nml_state_vars(5*ivar-4) == '') then + state_vars%nvars = ivar-1 + exit + endif +enddo + ! Allocate the arrays in the var derived type allocate(state_vars%netcdf_var_names(state_vars%nvars), state_vars%qtys(state_vars%nvars), state_vars%clamp_values(state_vars%nvars, 2), state_vars%updates(state_vars%nvars)) -if(use_clamping) then - - RowsLoop : do i = 1, state_vars%nvars - - netcdf_var_name = trim(nml_state_vars(5*i-4)) - state_vars%netcdf_var_names(i) = trim(netcdf_var_name) - - dart_qty_str = trim(nml_state_vars(5*i-3)) - call to_upper(dart_qty_str) - ! Make sure DART qty is valid - state_vars%qtys(i) = get_index_for_quantity(dart_qty_str) - if( state_vars%qtys(i) < 0 ) then - write(string1,*) 'The quantity specified in the &model_nml "', dart_qty_str, '", is not present in obs_kind_mod.f90' - call error_handler(E_ERR,'verify_state_variables',string1) - endif - - if (nml_state_vars(5*i-2) /= 'NA') then - read(nml_state_vars(5*i-2), '(d16.8)') state_vars%clamp_values(i,1) - else - state_vars%clamp_values(i,1) = MISSING_R8 - endif - - if (nml_state_vars(5*i-1) /= 'NA') then - read(nml_state_vars(5*i-1), '(d16.8)') state_vars%clamp_values(i,2) - else - state_vars%clamp_values(i,2) = MISSING_R8 - endif - - update = trim(nml_state_vars(5*i)) - call to_upper(update) - select case (update) - case ('UPDATE') - state_vars%updates(i) = .true. - case ('NO_COPY_BACK') - state_vars%updates(i) = .false. - case default - write(string1,'(A)') 'Invalid update variable in &model_nml:model_state_variable - only UPDATE or NO_COPY_BACK are supported' - write(string2,'(6A)') 'Issue: ', trim(netcdf_var_name), ', ', trim(dart_qty_str), ', ', trim(update) - call error_handler(E_ERR,'verify_state_variables',string1, text2=string2) - end select - - ! Checking that the rows in the nml entry are all complete - if ( dart_qty_str == '' .or. nml_state_vars(5*i-2) == '' .or. nml_state_vars(5*i-1) == '' .or. update == '' ) then - string1 = 'model_nml:model_state_variables not fully specified' - call error_handler(E_ERR, 'verify_state_variables', string1) - endif - - enddo RowsLoop - -else - - RowsLoopNoClamp : do i = 1, state_vars%nvars - - state_vars%clamp_values(:,:) = MISSING_R8 - - netcdf_var_name = trim(nml_state_vars(3*i-2)) - state_vars%netcdf_var_names(i) = trim(netcdf_var_name) - - dart_qty_str = trim(nml_state_vars(3*i-1)) - call to_upper(dart_qty_str) - ! Make sure DART qty is valid - state_vars%qtys(i) = get_index_for_quantity(dart_qty_str) - if( state_vars%qtys(i) < 0 ) then - write(string1,*) 'The quantity specified in the &model_nml "', dart_qty_str, '", is not present in obs_kind_mod.f90' - call error_handler(E_ERR,'verify_state_variables',string1) - endif - - update = trim(nml_state_vars(3*i)) - call to_upper(update) - select case (update) - case ('UPDATE') - state_vars%updates(i) = .true. - case ('NO_COPY_BACK') - state_vars%updates(i) = .false. - case default - write(string1,'(A)') 'Invalid update variable in &model_nml:model_state_variable - only UPDATE or NO_COPY_BACK are supported' - write(string2,'(6A)') 'Issue: ', trim(netcdf_var_name), ', ', trim(dart_qty_str), ', ', trim(update) - call error_handler(E_ERR,'verify_state_variables',string1, text2=string2) - end select - - ! Checking that the rows in the nml entry are all complete - if ( dart_qty_str == '' .or. update == '' ) then - string1 = 'model_nml:model_state_variables not fully specified' - call error_handler(E_ERR, 'verify_state_variables', string1) - endif - - enddo RowsLoopNoClamp +RowsLoop : do i = 1, state_vars%nvars + + netcdf_var_name = trim(nml_state_vars(5*i-4)) + state_vars%netcdf_var_names(i) = trim(netcdf_var_name) + + dart_qty_str = trim(nml_state_vars(5*i-3)) + call to_upper(dart_qty_str) + ! Make sure DART qty is valid + state_vars%qtys(i) = get_index_for_quantity(dart_qty_str) + if( state_vars%qtys(i) < 0 ) then + write(string1,*) 'The quantity specified in the &model_nml "', dart_qty_str, '", is not present in obs_kind_mod.f90' + call error_handler(E_ERR,'get_state_variables_clamp',string1) + endif + + if (nml_state_vars(5*i-2) /= 'NA') then + read(nml_state_vars(5*i-2), '(d16.8)') state_vars%clamp_values(i,1) + else + state_vars%clamp_values(i,1) = MISSING_R8 + endif + + if (nml_state_vars(5*i-1) /= 'NA') then + read(nml_state_vars(5*i-1), '(d16.8)') state_vars%clamp_values(i,2) + else + state_vars%clamp_values(i,2) = MISSING_R8 + endif + + update = trim(nml_state_vars(5*i)) + call to_upper(update) + select case (update) + case ('UPDATE') + state_vars%updates(i) = .true. + case ('NO_COPY_BACK') + state_vars%updates(i) = .false. + case default + write(string1,'(A)') 'Invalid update variable in &model_nml:model_state_variable - only UPDATE or NO_COPY_BACK are supported' + write(string2,'(6A)') 'Issue: ', trim(netcdf_var_name), ', ', trim(dart_qty_str), ', ', trim(update) + call error_handler(E_ERR,'get_state_variables_clamp',string1, text2=string2) + end select + + ! Checking that the rows in the nml entry are all complete + if ( dart_qty_str == '' .or. nml_state_vars(5*i-2) == '' .or. nml_state_vars(5*i-1) == '' .or. update == '' ) then + string1 = 'model_nml:model_state_variables not fully specified' + call error_handler(E_ERR, 'get_state_variables_clamp', string1) + endif + +enddo RowsLoop + +end subroutine get_state_variables_clamp -endif +!-------------------------------------------------------------------- + +!> Reads in model_nml:model_state_variables and returns a +!> state_var_type state_vars with nvars ; netcdf variable names ; +!> qtys (kinds) ; updates +! +!> Verifies that the namelist was filled in correctly, and checks +!> that there are valid entries for the dart_kind. -end subroutine get_state_variables +subroutine get_state_variables_noclamp(nml_state_vars, MAX_STATE_VARIABLES, state_vars) + +character(len=*), intent(inout) :: nml_state_vars(:) +integer, intent(in) :: MAX_STATE_VARIABLES +type(state_var_type), intent(out) :: state_vars + +character(len=NF90_MAX_NAME) :: netcdf_var_name, dart_qty_str, update +character(len=256) :: string1, string2 +integer :: i, ivar + +! Loop through the variables array to get the actual count of the number of variables +do ivar = 1, MAX_STATE_VARIABLES + ! If the first element in the row is an empty string, the loop has exceeded the extent of the variables + if (nml_state_vars(3*ivar-2) == '') then + state_vars%nvars = ivar-1 + exit + endif +enddo + +! Allocate the arrays in the var derived type +allocate(state_vars%netcdf_var_names(state_vars%nvars), state_vars%qtys(state_vars%nvars), state_vars%updates(state_vars%nvars)) + +RowsLoopNoClamp : do i = 1, state_vars%nvars + + state_vars%clamp_values(:,:) = MISSING_R8 + + netcdf_var_name = trim(nml_state_vars(3*i-2)) + state_vars%netcdf_var_names(i) = trim(netcdf_var_name) + + dart_qty_str = trim(nml_state_vars(3*i-1)) + call to_upper(dart_qty_str) + ! Make sure DART qty is valid + state_vars%qtys(i) = get_index_for_quantity(dart_qty_str) + if( state_vars%qtys(i) < 0 ) then + write(string1,*) 'The quantity specified in the &model_nml "', dart_qty_str, '", is not present in obs_kind_mod.f90' + call error_handler(E_ERR,'verify_state_variables',string1) + endif + + update = trim(nml_state_vars(3*i)) + call to_upper(update) + select case (update) + case ('UPDATE') + state_vars%updates(i) = .true. + case ('NO_COPY_BACK') + state_vars%updates(i) = .false. + case default + write(string1,'(A)') 'Invalid update variable in &model_nml:model_state_variable - only UPDATE or NO_COPY_BACK are supported' + write(string2,'(6A)') 'Issue: ', trim(netcdf_var_name), ', ', trim(dart_qty_str), ', ', trim(update) + call error_handler(E_ERR,'verify_state_variables',string1, text2=string2) + end select + + ! Checking that the rows in the nml entry are all complete + if ( dart_qty_str == '' .or. update == '' ) then + string1 = 'model_nml:model_state_variables not fully specified' + call error_handler(E_ERR, 'verify_state_variables', string1) + endif + +enddo RowsLoopNoClamp + +end subroutine get_state_variables_noclamp !=================================================================== ! End of model_mod From 7381c2bc13fca72942ca9add5c0a79d7515be048 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Thu, 12 Dec 2024 11:22:29 -0700 Subject: [PATCH 10/45] Updating MOM6 model_mod to use new interface --- models/MOM6/model_mod.f90 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/models/MOM6/model_mod.f90 b/models/MOM6/model_mod.f90 index 2ff74b47be..5c09f15115 100644 --- a/models/MOM6/model_mod.f90 +++ b/models/MOM6/model_mod.f90 @@ -106,7 +106,6 @@ module model_mod ! DART state vector contents are specified in the input.nml:&model_nml namelist. integer, parameter :: MAX_STATE_VARIABLES = 10 integer, parameter :: NUM_STATE_TABLE_COLUMNS = 3 -logical, parameter :: use_clamping = .false. ! model_interpolate failure codes integer, parameter :: NOT_IN_STATE = 12 @@ -178,8 +177,8 @@ subroutine static_init_model() ! Reads the model_state_variables namelist entry into a table and returns ! state_var_type state_vars with nvars, netcdf variable names, kinds/qtys, -! clamp values (optional), and update strings. -call get_state_variables(model_state_variables, MAX_STATE_VARIABLES, use_clamping, state_vars) +! and update logicals. +call get_state_variables(model_state_variables, MAX_STATE_VARIABLES, state_vars) ! Define which variables are in the model state dom_id = add_domain(template_file, state_vars%nvars, & From 914468f6c7acaf48d9f3433a9c6c6633690a18f0 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Thu, 12 Dec 2024 13:01:56 -0700 Subject: [PATCH 11/45] Replacing CAM-FV version of the routine with generic in default_model_mod --- .../cam-common-code/cam_common_code_mod.f90 | 90 +------------------ models/cam-fv/model_mod.f90 | 20 ++++- 2 files changed, 18 insertions(+), 92 deletions(-) diff --git a/models/cam-common-code/cam_common_code_mod.f90 b/models/cam-common-code/cam_common_code_mod.f90 index 4970e16474..e0e1faa20b 100644 --- a/models/cam-common-code/cam_common_code_mod.f90 +++ b/models/cam-common-code/cam_common_code_mod.f90 @@ -62,7 +62,7 @@ module cam_common_code_mod read_model_time, ref_model_top_pressure, ref_nlevels, scale_height, & set_vert_localization, vert_interp, vertical_localization_type, write_model_time -public :: nc_write_model_atts, grid_data, read_grid_info, set_cam_variable_info, & +public :: nc_write_model_atts, grid_data, read_grid_info, & MAX_STATE_VARIABLES, num_state_table_columns, common_initialized, & MAX_PERT, shortest_time_between_assimilations, domain_id, & ccustom_routine_to_generate_ensemble, & @@ -172,94 +172,6 @@ module cam_common_code_mod contains - -!----------------------------------------------------------------------- -!> -!> Fill the array of requested variables, dart kinds, possible min/max -!> values and whether or not to update the field in the output file. -!> Then calls 'add_domain()' to tell the DART code which variables to -!> read into the state vector after this code returns. -!> -!>@param variable_array the list of variables and kinds from model_mod_nml -!>@param nfields the number of variable/Quantity pairs specified - -subroutine set_cam_variable_info(cam_template_filename, variable_array) - -character(len=*), intent(in) :: cam_template_filename -character(len=*), intent(in) :: variable_array(:) - -character(len=*), parameter :: routine = 'set_cam_variable_info:' - -integer :: i, nfields -integer, parameter :: MAX_STRING_LEN = 128 - -character(len=MAX_STRING_LEN) :: varname ! column 1, NetCDF variable name -character(len=MAX_STRING_LEN) :: dartstr ! column 2, DART Quantity -character(len=MAX_STRING_LEN) :: minvalstr ! column 3, Clamp min val -character(len=MAX_STRING_LEN) :: maxvalstr ! column 4, Clamp max val -character(len=MAX_STRING_LEN) :: updatestr ! column 5, Update output or not - -character(len=vtablenamelength) :: var_names(MAX_STATE_VARIABLES) = ' ' -logical :: update_list(MAX_STATE_VARIABLES) = .FALSE. -integer :: kind_list(MAX_STATE_VARIABLES) = MISSING_I -real(r8) :: clamp_vals(MAX_STATE_VARIABLES,2) = MISSING_R8 - -nfields = 0 -ParseVariables : do i = 1, MAX_STATE_VARIABLES - - varname = variable_array(num_state_table_columns*i-4) - dartstr = variable_array(num_state_table_columns*i-3) - minvalstr = variable_array(num_state_table_columns*i-2) - maxvalstr = variable_array(num_state_table_columns*i-1) - updatestr = variable_array(num_state_table_columns*i ) - - if ( varname == ' ' .and. dartstr == ' ' ) exit ParseVariables ! Found end of list. - - if ( varname == ' ' .or. dartstr == ' ' ) then - string1 = 'model_nml:model "state_variables" not fully specified' - call error_handler(E_ERR,routine,string1,source,revision,revdate) - endif - - ! Make sure DART kind is valid - - if( get_index_for_quantity(dartstr) < 0 ) then - write(string1,'(3A)') 'there is no obs_kind "', trim(dartstr), '" in obs_kind_mod.f90' - call error_handler(E_ERR,routine,string1,source,revision,revdate) - endif - - call to_upper(minvalstr) - call to_upper(maxvalstr) - call to_upper(updatestr) - - var_names( i) = varname - kind_list( i) = get_index_for_quantity(dartstr) - clamp_vals(i,1) = string_to_real(minvalstr) - clamp_vals(i,2) = string_to_real(maxvalstr) - update_list( i) = string_to_logical(updatestr, 'UPDATE') - - nfields = nfields + 1 - -enddo ParseVariables - -if (nfields == MAX_STATE_VARIABLES) then - write(string1,'(2A)') 'WARNING: There is a possibility you need to increase ', & - 'MAX_STATE_VARIABLES in the global variables in model_mod.f90' - - write(string2,'(A,i4,A)') 'WARNING: you have specified at least ', nfields, & - ' perhaps more' - - call error_handler(E_MSG,routine,string1,source,revision,revdate,text2=string2) -endif - -! CAM only has a single domain (only a single grid, no nests or multiple grids) - -domain_id = add_domain(cam_template_filename, nfields, var_names, kind_list, & - clamp_vals, update_list) - -end subroutine set_cam_variable_info - - - !----------------------------------------------------------------------- !> Read the data from the various cam grid arrays !> diff --git a/models/cam-fv/model_mod.f90 b/models/cam-fv/model_mod.f90 index 6402eaaeb4..3b279fa53b 100644 --- a/models/cam-fv/model_mod.f90 +++ b/models/cam-fv/model_mod.f90 @@ -81,7 +81,8 @@ module model_mod QUAD_LOCATED_CELL_CENTERS use default_model_mod, only : adv_1step, nc_write_model_vars, & init_time => fail_init_time, & - init_conditions => fail_init_conditions + init_conditions => fail_init_conditions, & + get_state_variables, state_var_type use cam_common_code_mod, only : above_ramp_start, are_damping, build_cam_pressure_columns, build_heights, & cam_grid, cdebug_level, check_good_levels, cno_normalization_of_scale_heights, & @@ -94,7 +95,7 @@ module model_mod set_vert_localization, vert_interp, vertical_localization_type, write_model_time use cam_common_code_mod, only : nc_write_model_atts, grid_data, read_grid_info, & - set_cam_variable_info, MAX_STATE_VARIABLES, & + MAX_STATE_VARIABLES, & num_state_table_columns, MAX_PERT, & shortest_time_between_assimilations, domain_id, & cuse_log_vertical_scale, & @@ -203,6 +204,7 @@ module model_mod debug_level ! global variables +logical, parameter :: use_clamping = .true. character(len=512) :: string1, string2, string3 logical, save :: module_initialized = .false. @@ -257,6 +259,8 @@ subroutine static_init_model() character(len=*), parameter :: routine = 'static_init_model' +type(state_var_type) :: state_vars + if ( module_initialized ) return ! Record version info @@ -298,7 +302,17 @@ subroutine static_init_model() ! read the namelist &model_nml :: state_variables ! to set up what will be read into the cam state vector -call set_cam_variable_info(cam_template_filename, state_variables) +call get_state_variables(state_variables, MAX_STATE_VARIABLES, use_clamping, state_vars) + +write(*,*) 'state_vars%nvars: ', state_vars%nvars +write(*,*) 'state_vars%netcdf_var_names: ', state_vars%netcdf_var_names +write(*,*) 'state_vars%qtys: ', state_vars%qtys +write(*,*) 'state_vars%clamp_values(:,1): ', state_vars%clamp_values(:, 1) +write(*,*) 'state_vars%clamp_values(:,2): ', state_vars%clamp_values(:, 2) +write(*,*) 'state_vars%updates: ', state_vars%updates + +domain_id = add_domain(cam_template_filename, state_vars%nvars, state_vars%netcdf_var_names, state_vars%qtys, & + state_vars%clamp_values, state_vars%updates) call fill_cam_stagger_info(grid_stagger) From 15e984fa572e9c41c3df4b99d4028d5f0f9c4185 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Thu, 12 Dec 2024 13:04:57 -0700 Subject: [PATCH 12/45] Replacing CAM-SE version of the routine with generic in default_model_mod --- models/cam-se/model_mod.f90 | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/models/cam-se/model_mod.f90 b/models/cam-se/model_mod.f90 index 25912640f3..1fca1d31d4 100644 --- a/models/cam-se/model_mod.f90 +++ b/models/cam-se/model_mod.f90 @@ -82,7 +82,8 @@ module model_mod get_molar_mass, get_volume_mixing_ratio use default_model_mod, only : adv_1step, nc_write_model_vars, & init_time => fail_init_time, & - init_conditions => fail_init_conditions + init_conditions => fail_init_conditions, & + get_state_variables, state_var_type use cam_common_code_mod, only : above_ramp_start, are_damping, build_cam_pressure_columns, build_heights, & cam_grid, cdebug_level, check_good_levels, cno_normalization_of_scale_heights, & @@ -96,7 +97,7 @@ module model_mod use cam_common_code_mod, only : nc_write_model_atts, grid_data, read_grid_info, & - set_cam_variable_info, MAX_STATE_VARIABLES, & + MAX_STATE_VARIABLES, & num_state_table_columns, MAX_PERT, & shortest_time_between_assimilations, domain_id, & cuse_log_vertical_scale, & @@ -216,6 +217,8 @@ module model_mod character(len=512) :: string1, string2, string3 logical, save :: module_initialized = .false. +logical, parameter :: use_clamping = .true. + ! Surface potential; used for calculation of geometric heights. ! SENote: right now every process has their own complete copy of this real(r8), allocatable :: phis(:) @@ -299,6 +302,8 @@ module model_mod subroutine static_init_model() +type(state_var_type) :: state_vars + integer :: iunit, io, i integer :: nc_file_ID integer :: ncol_temp(1) @@ -343,7 +348,11 @@ subroutine static_init_model() ! read the namelist &model_nml :: state_variables ! to set up what will be read into the cam state vector -call set_cam_variable_info(cam_template_filename, state_variables) +call get_state_variables(state_variables, MAX_STATE_VARIABLES, use_clamping, state_vars) + +domain_id = add_domain(cam_template_filename, state_vars%nvars, state_vars%netcdf_var_names, state_vars%qtys, & + state_vars%clamp_values, state_vars%updates) + ! The size of the only surface pressure dimension is the number of columns ncol_temp = get_dim_lengths(domain_id, get_varid_from_kind(domain_id, QTY_SURFACE_PRESSURE)) From 6ed2022ad924222ce65b2a1c0f7e782c78235e80 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Thu, 12 Dec 2024 13:14:33 -0700 Subject: [PATCH 13/45] Removing forgotten print statements from ca-fv model_mod --- models/cam-fv/model_mod.f90 | 7 ------- 1 file changed, 7 deletions(-) diff --git a/models/cam-fv/model_mod.f90 b/models/cam-fv/model_mod.f90 index 3b279fa53b..19629488e5 100644 --- a/models/cam-fv/model_mod.f90 +++ b/models/cam-fv/model_mod.f90 @@ -304,13 +304,6 @@ subroutine static_init_model() ! to set up what will be read into the cam state vector call get_state_variables(state_variables, MAX_STATE_VARIABLES, use_clamping, state_vars) -write(*,*) 'state_vars%nvars: ', state_vars%nvars -write(*,*) 'state_vars%netcdf_var_names: ', state_vars%netcdf_var_names -write(*,*) 'state_vars%qtys: ', state_vars%qtys -write(*,*) 'state_vars%clamp_values(:,1): ', state_vars%clamp_values(:, 1) -write(*,*) 'state_vars%clamp_values(:,2): ', state_vars%clamp_values(:, 2) -write(*,*) 'state_vars%updates: ', state_vars%updates - domain_id = add_domain(cam_template_filename, state_vars%nvars, state_vars%netcdf_var_names, state_vars%qtys, & state_vars%clamp_values, state_vars%updates) From 7336795700d3d3d9eae313e3fc1480c700c942f5 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Thu, 12 Dec 2024 14:31:00 -0700 Subject: [PATCH 14/45] Using separate namelist groups in a single input.nml as opposed to using 2 distinct input.nml files --- .../namelist/test_get_state_variables.f90 | 60 +++++++++--------- developer_tests/namelist/work/input.nml | 25 ++++---- developer_tests/namelist/work/input.nml.clamp | 62 ------------------- 3 files changed, 41 insertions(+), 106 deletions(-) delete mode 100644 developer_tests/namelist/work/input.nml.clamp diff --git a/developer_tests/namelist/test_get_state_variables.f90 b/developer_tests/namelist/test_get_state_variables.f90 index e3bb7bc7e5..721fb272e1 100644 --- a/developer_tests/namelist/test_get_state_variables.f90 +++ b/developer_tests/namelist/test_get_state_variables.f90 @@ -15,28 +15,30 @@ program test_get_state_variables integer :: iunit, io logical :: use_clamping -type(state_var_type) :: state_vars +type(state_var_type) :: state_vars, state_vars_clamp ! DART state vector contents are specified in the input.nml:&model_nml namelist. integer, parameter :: MAX_STATE_VARIABLES = 20 integer, parameter :: NUM_STATE_TABLE_COLUMNS = 3 character(len=vtablenamelength) :: state_variables(MAX_STATE_VARIABLES * NUM_STATE_TABLE_COLUMNS ) = ' ' -namelist /model_nml/ & +namelist /model_nml_noclamp/ & + state_variables + +namelist /model_nml_clamp/ & state_variables call initialize_mpi_utilities('test_get_state_variables') -call plan(12) +call plan(10) -! Using namelist file WITHOUT clamping values -use_clamping = .false. +! Using namelist WITHOUT clamping values -call find_namelist_in_file('input.nml', 'model_nml', iunit) -read(iunit, nml = model_nml, iostat = io) -call check_namelist_read(iunit, io, 'model_nml') +call find_namelist_in_file('input.nml', 'model_nml_noclamp', iunit) +read(iunit, nml = model_nml_noclamp, iostat = io) +call check_namelist_read(iunit, io, 'model_nml_noclamp') -call get_state_variables(state_variables, MAX_STATE_VARIABLES, use_clamping, state_vars) +call get_state_variables(state_variables, MAX_STATE_VARIABLES, state_vars) write(*,*) 'state_vars%nvars: ', state_vars%nvars write(*,*) 'state_vars%netcdf_var_names: ', state_vars%netcdf_var_names @@ -46,32 +48,30 @@ program test_get_state_variables call ok(state_vars%nvars == 5) !OK call ok(state_vars%netcdf_var_names(2) == '') !NOT OK call ok(state_vars%qtys(3) == 20) !NOT OK -call ok(state_vars%clamp_values(1,1) == MISSING_R8) !OK -call ok(state_vars%clamp_values(5,2) == 0.4) !NOT OK call ok(state_vars%updates(4) == .true.) !OK ! Using namelist file WITH clamping values use_clamping = .true. -call find_namelist_in_file('input.nml.clamp', 'model_nml', iunit) -read(iunit, nml = model_nml, iostat = io) -call check_namelist_read(iunit, io, 'model_nml') - -call get_state_variables(state_variables, MAX_STATE_VARIABLES, use_clamping, state_vars) - -write(*,*) 'state_vars%nvars: ', state_vars%nvars -write(*,*) 'state_vars%netcdf_var_names: ', state_vars%netcdf_var_names -write(*,*) 'state_vars%qtys: ', state_vars%qtys -write(*,*) 'state_vars%clamp_values(:,1): ', state_vars%clamp_values(:, 1) -write(*,*) 'state_vars%clamp_values(:,2): ', state_vars%clamp_values(:, 2) -write(*,*) 'state_vars%updates: ', state_vars%updates - -call ok(state_vars%nvars == 7) !NOT OK -call ok(state_vars%netcdf_var_names(2) == 'TEMP_CUR') !OK -call ok(state_vars%qtys(3) == 356) !OK -call ok(state_vars%clamp_values(1,1) == 99) !NOT OK -call ok(state_vars%clamp_values(5,2) == 0.0) !OK -call ok(state_vars%updates(4) == .false.) !NOT OK +call find_namelist_in_file('input.nml', 'model_nml_clamp', iunit) +read(iunit, nml = model_nml_clamp, iostat = io) +call check_namelist_read(iunit, io, 'model_nml_clamp') + +call get_state_variables(state_variables, MAX_STATE_VARIABLES, use_clamping, state_vars_clamp) + +write(*,*) 'state_vars%nvars: ', state_vars_clamp%nvars +write(*,*) 'state_vars%netcdf_var_names: ', state_vars_clamp%netcdf_var_names +write(*,*) 'state_vars%qtys: ', state_vars_clamp%qtys +write(*,*) 'state_vars%clamp_values(:,1): ', state_vars_clamp%clamp_values(:, 1) +write(*,*) 'state_vars%clamp_values(:,2): ', state_vars_clamp%clamp_values(:, 2) +write(*,*) 'state_vars%updates: ', state_vars_clamp%updates + +call ok(state_vars_clamp%nvars == 7) !NOT OK +call ok(state_vars_clamp%netcdf_var_names(2) == 'TEMP_CUR') !OK +call ok(state_vars_clamp%qtys(3) == 356) !OK +call ok(state_vars_clamp%clamp_values(1,1) == 99) !NOT OK +call ok(state_vars_clamp%clamp_values(5,2) == 0.0) !OK +call ok(state_vars_clamp%updates(4) == .false.) !NOT OK call finalize_mpi_utilities() diff --git a/developer_tests/namelist/work/input.nml b/developer_tests/namelist/work/input.nml index 4fe4f64e55..621b507e24 100644 --- a/developer_tests/namelist/work/input.nml +++ b/developer_tests/namelist/work/input.nml @@ -1,21 +1,17 @@ -&model_nml - state_variables = 'SALT_CUR ', 'QTY_SALINITY ', 'UPDATE', +&model_nml_clamp + state_variables = 'SALT_CUR ', 'QTY_SALINITY', '0.0', '0.0', 'UPDATE', + 'TEMP_CUR ', 'QTY_POTENTIAL_TEMPERATURE', '0.0', '0.0', 'UPDATE', + 'UVEL_CUR ', 'QTY_U_CURRENT_COMPONENT ', '0.0', '0.0', 'UPDATE', + 'VVEL_CUR ', 'QTY_V_CURRENT_COMPONENT ', '0.0', '0.0', 'UPDATE', + 'PSURF_CUR', 'QTY_SEA_SURFACE_PRESSURE ', '0.0', '0.0', 'UPDATE' + / + +&model_nml_noclamp + state_variables = 'SALT_CUR ', 'QTY_SALINITY', 'UPDATE', 'TEMP_CUR ', 'QTY_POTENTIAL_TEMPERATURE', 'UPDATE', 'UVEL_CUR ', 'QTY_U_CURRENT_COMPONENT ', 'UPDATE', 'VVEL_CUR ', 'QTY_V_CURRENT_COMPONENT ', 'UPDATE', 'PSURF_CUR', 'QTY_SEA_SURFACE_PRESSURE ', 'UPDATE' -! -! state_variables = 'theta', 'QTY_POTENTIAL_TEMPERATURE', -! 'rho', 'QTY_DENSITY', -! 'uReconstructZonal', 'QTY_U_WIND_COMPONENT', -! 'uReconstructMeridional','QTY_V_WIND_COMPONENT', -! 'w', 'QTY_VERTICAL_VELOCITY', -! 'qv', 'QTY_VAPOR_MIXING_RATIO', -! 'surface_pressure', 'QTY_SURFACE_PRESSURE' -! -! state_variables = 'SH2O', 'QTY_SOIL_LIQUID_WATER', '0.0', 'NA', 'NOUPDATE', -! 'SUBSURFACE_FLUX', 'QTY_SUBSURFACE', '0.0', 'NA', 'NOUPDATE', -! 'OVERLAND_FLUX', 'QTY_OVERLAND_FLOW', '0.0', 'NA', 'NOUPDATE' / &utilities_nml @@ -28,6 +24,7 @@ quantity_files = '../../../assimilation_code/modules/observations/land_quantities_mod.f90', '../../../assimilation_code/modules/observations/default_quantities_mod.f90' '../../../assimilation_code/modules/observations/atmosphere_quantities_mod.f90' + '../../../assimilation_code/modules/observations/ocean_quantities_mod.f90' output_obs_kind_mod_file = '../../../assimilation_code/modules/observations/obs_kind_mod.f90' input_obs_def_mod_file = '../../../observations/forward_operators/DEFAULT_obs_def_mod.F90' output_obs_def_mod_file = '../../../observations/forward_operators/obs_def_mod.f90' diff --git a/developer_tests/namelist/work/input.nml.clamp b/developer_tests/namelist/work/input.nml.clamp deleted file mode 100644 index 6a4d315fa8..0000000000 --- a/developer_tests/namelist/work/input.nml.clamp +++ /dev/null @@ -1,62 +0,0 @@ -&model_nml - state_variables = 'SALT_CUR ', 'QTY_SALINITY', '0.0', '0.0', 'UPDATE', - 'TEMP_CUR ', 'QTY_POTENTIAL_TEMPERATURE', '0.0', '0.0', 'UPDATE', - 'UVEL_CUR ', 'QTY_U_CURRENT_COMPONENT ', '0.0', '0.0', 'UPDATE', - 'VVEL_CUR ', 'QTY_V_CURRENT_COMPONENT ', '0.0', '0.0', 'UPDATE', - 'PSURF_CUR', 'QTY_SEA_SURFACE_PRESSURE ', '0.0', '0.0', 'UPDATE' - / - -&utilities_nml - module_details = .false. - write_nml = 'none' - / - -&preprocess_nml - input_obs_kind_mod_file = '../../../assimilation_code/modules/observations/DEFAULT_obs_kind_mod.F90' - quantity_files = '../../../assimilation_code/modules/observations/land_quantities_mod.f90', - '../../../assimilation_code/modules/observations/default_quantities_mod.f90' - '../../../assimilation_code/modules/observations/atmosphere_quantities_mod.f90' - output_obs_kind_mod_file = '../../../assimilation_code/modules/observations/obs_kind_mod.f90' - input_obs_def_mod_file = '../../../observations/forward_operators/DEFAULT_obs_def_mod.F90' - output_obs_def_mod_file = '../../../observations/forward_operators/obs_def_mod.f90' - input_files = '../../../observations/forward_operators/obs_def_AIRS_mod.f90', - '../../../observations/forward_operators/obs_def_AOD_mod.f90', - '../../../observations/forward_operators/obs_def_AURA_mod.f90', - '../../../observations/forward_operators/obs_def_COSMOS_mod.f90', - '../../../observations/forward_operators/obs_def_CO_Nadir_mod.f90', - '../../../observations/forward_operators/obs_def_GWD_mod.f90', - '../../../observations/forward_operators/obs_def_QuikSCAT_mod.f90', - '../../../observations/forward_operators/obs_def_SABER_mod.f90', - '../../../observations/forward_operators/obs_def_altimeter_mod.f90', - '../../../observations/forward_operators/obs_def_cloud_mod.f90', - '../../../observations/forward_operators/obs_def_dew_point_mod.f90', - '../../../observations/forward_operators/obs_def_dwl_mod.f90', - '../../../observations/forward_operators/obs_def_eval_mod.f90', - '../../../observations/forward_operators/obs_def_gps_mod.f90', - '../../../observations/forward_operators/obs_def_gts_mod.f90', - '../../../observations/forward_operators/obs_def_land_mod.f90', - '../../../observations/forward_operators/obs_def_metar_mod.f90', - '../../../observations/forward_operators/obs_def_ocean_mod.f90', - '../../../observations/forward_operators/obs_def_pe2lyr_mod.f90', - '../../../observations/forward_operators/obs_def_radar_mod.f90', - '../../../observations/forward_operators/obs_def_reanalysis_bufr_mod.f90', - '../../../observations/forward_operators/obs_def_rel_humidity_mod.f90', - '../../../observations/forward_operators/obs_def_sqg_mod.f90', - '../../../observations/forward_operators/obs_def_tower_mod.f90', - '../../../observations/forward_operators/obs_def_tpw_mod.f90', - '../../../observations/forward_operators/obs_def_upper_atm_mod.f90', - '../../../observations/forward_operators/obs_def_vortex_mod.f90', - '../../../observations/forward_operators/obs_def_wind_speed_mod.f90' - / - -&mpi_utilities_nml - / - -&obs_kind_nml - / - -&ensemble_manager_nml - / - -&state_vector_io_nml - / From 14352e1fd8919bb96010b6dd1a7562e71f0d2e5e Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Thu, 12 Dec 2024 15:12:21 -0700 Subject: [PATCH 15/45] Replacing WRF_HYDRO version of the routine with generic in default_model_mod This model is the perfect example of how we can have separate calls to get_state_variables for each file domain instead of having the domain included in the state_variables nml item. --- models/wrf_hydro/model_mod.f90 | 137 +++++++-------------------------- 1 file changed, 29 insertions(+), 108 deletions(-) diff --git a/models/wrf_hydro/model_mod.f90 b/models/wrf_hydro/model_mod.f90 index 8f943989ea..2301d6ac56 100644 --- a/models/wrf_hydro/model_mod.f90 +++ b/models/wrf_hydro/model_mod.f90 @@ -44,7 +44,8 @@ module model_mod use dart_time_io_mod, only : write_model_time -use default_model_mod, only : adv_1step, nc_write_model_vars +use default_model_mod, only : adv_1step, nc_write_model_vars, get_state_variables, & + state_var_type use noah_hydro_mod, only : configure_lsm, configure_hydro, & n_link, linkLong, linkLat, linkAlt, get_link_tree, & @@ -110,6 +111,8 @@ module model_mod logical, save :: module_initialized = .false. +logical, parameter :: use_clamping = .true. + character(len=512) :: string1, string2, string3 integer(i8) :: model_size @@ -140,9 +143,9 @@ module model_mod character(len=256) :: perturb_distribution = 'lognormal' character(len=obstypelength) :: & - lsm_variables( NUM_STATE_TABLE_COLUMNS,MAX_STATE_VARIABLES) = '', & - hydro_variables(NUM_STATE_TABLE_COLUMNS,MAX_STATE_VARIABLES) = '', & - parameters( NUM_STATE_TABLE_COLUMNS,MAX_STATE_VARIABLES) = '' + lsm_variables( NUM_STATE_TABLE_COLUMNS*MAX_STATE_VARIABLES) = '', & + hydro_variables(NUM_STATE_TABLE_COLUMNS*MAX_STATE_VARIABLES) = '', & + parameters( NUM_STATE_TABLE_COLUMNS*MAX_STATE_VARIABLES) = '' namelist /model_nml/ assimilation_period_days, & assimilation_period_seconds, & @@ -176,6 +179,8 @@ subroutine static_init_model() character(len=*), parameter :: routine = 'static_init_model' +type(state_var_type) :: state_vars_hydro, state_vars_parameters, state_vars_lsm + integer :: iunit, io, domainID integer :: n_lsm_fields integer :: n_hydro_fields @@ -250,13 +255,13 @@ subroutine static_init_model() call configure_hydro() call read_hydro_global_atts(domain_shapefiles(domainID)) - call verify_variables(hydro_variables, domain_shapefiles(domainID), & - n_hydro_fields, var_names, var_qtys, var_ranges, var_update) + + call get_state_variables(hydro_variables, MAX_STATE_VARIABLES, use_clamping, state_vars_hydro) idom_hydro = add_domain(domain_shapefiles(domainID), & - n_hydro_fields, var_names, & - kind_list=var_qtys, & - clamp_vals=var_ranges(1:n_hydro_fields,:), & - update_list=var_update) + state_vars_hydro%nvars, state_vars_hydro%netcdf_var_names, & + kind_list=state_vars_hydro%qtys, & + clamp_vals=state_vars_hydro%clamp_values, & + update_list=state_vars_hydro%updates) if (debug > 99) call state_structure_info(idom_hydro) @@ -273,13 +278,13 @@ subroutine static_init_model() elseif (index(domain_name,'PARAMETER') > 0) then - call verify_variables(parameters, domain_shapefiles(domainID), n_parameters, & - var_names, var_qtys, var_ranges, var_update) + call get_state_variables(parameters, MAX_STATE_VARIABLES, use_clamping, state_vars_parameters) idom_parameters = add_domain(domain_shapefiles(domainID), & - n_parameters, var_names, & - kind_list=var_qtys, & - clamp_vals=var_ranges(1:n_parameters,:), & - update_list=var_update ) + state_vars_parameters%nvars, state_vars_parameters%netcdf_var_names, & + kind_list=state_vars_parameters%qtys, & + clamp_vals=state_vars_parameters%clamp_values, & + update_list=state_vars_parameters%updates) + if (debug > 99) call state_structure_info(idom_parameters) !>@todo check the size of the parameter variables against nlinks @@ -288,14 +293,14 @@ subroutine static_init_model() call configure_lsm(lsm_model_choice) call read_noah_global_atts(domain_shapefiles(domainID)) - call verify_variables(lsm_variables, domain_shapefiles(domainID), n_lsm_fields, & - var_names, var_qtys, var_ranges, var_update) - idom_lsm = add_domain(domain_shapefiles(domainID), & - n_lsm_fields, var_names, & - kind_list=var_qtys, & - clamp_vals=var_ranges(1:n_lsm_fields,:), & - update_list=var_update) - if (debug > 99) call state_structure_info(idom_lsm) + call get_state_variables(lsm_variables, MAX_STATE_VARIABLES, use_clamping, state_vars_lsm) + idom_hydro = add_domain(domain_shapefiles(domainID), & + state_vars_lsm%nvars, state_vars_lsm%netcdf_var_names, & + kind_list=state_vars_lsm%qtys, & + clamp_vals=state_vars_lsm%clamp_values, & + update_list=state_vars_lsm%updates) + + if (debug > 99) call state_structure_info(idom_lsm) else @@ -1252,90 +1257,6 @@ end subroutine end_model ! End of the required interfaces !======================================================================= -!----------------------------------------------------------------------- -!> given the list of variables and a filename, check user input -!> return the handle to the open netCDF file and the number of variables -!> in this 'domain' - -subroutine verify_variables( variable_table, filename, ngood, & - var_names, var_qtys, var_ranges, var_update) - -character(len=*), intent(in) :: variable_table(:,:) -character(len=*), intent(in) :: filename -integer, intent(out) :: ngood -character(len=*), intent(out) :: var_names(:) -real(r8), intent(out) :: var_ranges(:,:) -logical, intent(out) :: var_update(:) -integer , intent(out) :: var_qtys(:) - -character(len=*), parameter :: routine = 'verify_variables' - -integer :: io, i, quantity -real(r8) :: minvalue, maxvalue - -character(len=NF90_MAX_NAME) :: varname -character(len=NF90_MAX_NAME) :: dartstr -character(len=NF90_MAX_NAME) :: minvalstring -character(len=NF90_MAX_NAME) :: maxvalstring -character(len=NF90_MAX_NAME) :: state_or_aux - -ngood = 0 -MyLoop : do i = 1, size(variable_table,2) - - varname = variable_table(VT_VARNAMEINDX,i) - dartstr = variable_table(VT_KINDINDX ,i) - minvalstring = variable_table(VT_MINVALINDX ,i) - maxvalstring = variable_table(VT_MAXVALINDX ,i) - state_or_aux = variable_table(VT_STATEINDX ,i) - - if ( varname == ' ' .and. dartstr == ' ' ) exit MyLoop ! Found end of list. - - if ( varname == ' ' .or. dartstr == ' ' ) then - string1 = 'model_nml: variable list not fully specified' - string2 = 'reading from "'//trim(filename)//'"' - call error_handler(E_ERR,routine, string1, & - source, revision, revdate, text2=string2) - endif - - ! The internal DART routines check if the variable name is valid. - - ! Make sure DART kind is valid - quantity = get_index_for_quantity(dartstr) - if( quantity < 0 ) then - write(string1,'(''there is no obs_kind "'',a,''" in obs_kind_mod.f90'')') & - trim(dartstr) - call error_handler(E_ERR,routine,string1,source,revision,revdate) - endif - - ! All good to here - fill the output variables - - ngood = ngood + 1 - var_names( ngood) = varname - var_qtys( ngood) = quantity - var_ranges(ngood,:) = (/ MISSING_R8, MISSING_R8 /) - var_update(ngood) = .false. ! at least initially - - ! convert the [min,max]valstrings to numeric values if possible - read(minvalstring,*,iostat=io) minvalue - if (io == 0) var_ranges(ngood,1) = minvalue - - read(maxvalstring,*,iostat=io) maxvalue - if (io == 0) var_ranges(ngood,2) = maxvalue - - call to_upper(state_or_aux) - if (state_or_aux == 'UPDATE') var_update(ngood) = .true. - -enddo MyLoop - -if (ngood == MAX_STATE_VARIABLES) then - string1 = 'WARNING: you may need to increase "MAX_STATE_VARIABLES"' - write(string2,'(''you have specified at least '',i4,'' perhaps more.'')') ngood - call error_handler(E_MSG,routine,string1,source,revision,revdate,text2=string2) -endif - -end subroutine verify_variables - - !----------------------------------------------------------------------- !> Sets the location information arrays for each domain !> Each location array is declared to be 3D to make it easy to use From 4d996bd3685251b4aa829b5742f116be41d860fd Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Mon, 16 Dec 2024 13:15:06 -0700 Subject: [PATCH 16/45] WRF_HYDRO: Removing varibles that are no longer used due to changes --- models/wrf_hydro/model_mod.f90 | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/models/wrf_hydro/model_mod.f90 b/models/wrf_hydro/model_mod.f90 index 2301d6ac56..8e64514f34 100644 --- a/models/wrf_hydro/model_mod.f90 +++ b/models/wrf_hydro/model_mod.f90 @@ -118,12 +118,7 @@ module model_mod integer(i8) :: model_size type(time_type) :: time_step -! Codes for interpreting the columns of the variable_table -integer, parameter :: VT_VARNAMEINDX = 1 ! ... variable name -integer, parameter :: VT_KINDINDX = 2 ! ... DART kind -integer, parameter :: VT_MINVALINDX = 3 ! ... minimum value if any -integer, parameter :: VT_MAXVALINDX = 4 ! ... maximum value if any -integer, parameter :: VT_STATEINDX = 5 ! ... update (state) or not +! For interpreting the columns of the variable_table integer, parameter :: MAX_STATE_VARIABLES = 40 integer, parameter :: NUM_STATE_TABLE_COLUMNS = 5 @@ -182,16 +177,8 @@ subroutine static_init_model() type(state_var_type) :: state_vars_hydro, state_vars_parameters, state_vars_lsm integer :: iunit, io, domainID -integer :: n_lsm_fields -integer :: n_hydro_fields -integer :: n_parameters integer :: vsize -character(len=obstypelength) :: var_names(MAX_STATE_VARIABLES) -real(r8) :: var_ranges(MAX_STATE_VARIABLES,2) -logical :: var_update(MAX_STATE_VARIABLES) -integer :: var_qtys( MAX_STATE_VARIABLES) - character(len=256) :: domain_name if ( module_initialized ) return ! only need to do this once From b1f0878da7dc813535e4d39c03f5e9f4d47827aa Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Mon, 16 Dec 2024 13:24:46 -0700 Subject: [PATCH 17/45] MOM6: Removing varibles that are no longer used due to changes --- models/MOM6/model_mod.f90 | 5 ----- 1 file changed, 5 deletions(-) diff --git a/models/MOM6/model_mod.f90 b/models/MOM6/model_mod.f90 index 5c09f15115..945548ceae 100644 --- a/models/MOM6/model_mod.f90 +++ b/models/MOM6/model_mod.f90 @@ -150,11 +150,6 @@ subroutine static_init_model() integer :: iunit, io type(state_var_type) :: state_vars -! identifiers for variable_table -integer, parameter :: VAR_NAME_INDEX = 1 -integer, parameter :: VAR_QTY_INDEX = 2 -integer, parameter :: VAR_UPDATE_INDEX = 3 - module_initialized = .true. call find_namelist_in_file("input.nml", "model_nml", iunit) From 80a63e1a4d5a6a6f11a9c78fe8b7c4f36d59c531 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Mon, 16 Dec 2024 13:43:29 -0700 Subject: [PATCH 18/45] Replacing POP version of the routine with generic in default_model_mod --- models/POP/model_mod.f90 | 130 ++++----------------------------------- 1 file changed, 12 insertions(+), 118 deletions(-) diff --git a/models/POP/model_mod.f90 b/models/POP/model_mod.f90 index 23ddfb9a7c..c18bec77e2 100644 --- a/models/POP/model_mod.f90 +++ b/models/POP/model_mod.f90 @@ -50,7 +50,9 @@ module model_mod get_num_variables, get_index_start, & get_num_dims, get_domain_size, & get_dart_vector_index -use default_model_mod, only : adv_1step, init_time, init_conditions, nc_write_model_vars +use default_model_mod, only : adv_1step, init_time, init_conditions, & + nc_write_model_vars, get_state_variables, & + state_var_type use typesizes use netcdf @@ -109,14 +111,7 @@ module model_mod ! DART state vector contents are specified in the input.nml:&model_nml namelist. integer, parameter :: max_state_variables = 10 integer, parameter :: num_state_table_columns = 3 -character(len=vtablenamelength) :: variable_table( max_state_variables, num_state_table_columns ) -integer :: state_kinds_list( max_state_variables ) -logical :: update_var_list( max_state_variables ) - -! identifiers for variable_table -integer, parameter :: VAR_NAME_INDEX = 1 -integer, parameter :: VAR_QTY_INDEX = 2 -integer, parameter :: VAR_UPDATE_INDEX = 3 +type(state_var_type) :: state_vars ! things which can/should be in the model_nml integer :: assimilation_period_days = -1 @@ -157,9 +152,6 @@ module model_mod ! is not being used. !------------------------------------------------------------------ -! Number of fields in the state vector -integer :: nfields - ! Grid parameters - the values will be read from a ! standard POP namelist and filled in here. @@ -190,8 +182,6 @@ module model_mod integer(i8) :: model_size ! the state vector length - - !------------------------------------------------ ! NOTE (dipole/tripole grids): since both of the dipole and tripole @@ -336,8 +326,8 @@ subroutine static_init_model() if (debug > 2) call write_grid_interptest() ! DEBUG only ! verify that the model_state_variables namelist was filled in correctly. -! returns variable_table which has variable names, kinds and update strings. -call verify_state_variables(model_state_variables, nfields, variable_table, state_kinds_list, update_var_list) +! returns state_vars which has netcdf variable names, kinds and update strings. +call get_state_variables(model_state_variables, MAX_STATE_VARIABLES, state_vars) ! in spite of the staggering, all grids are the same size ! and offset by half a grid cell. 4 are 3D and 1 is 2D. @@ -356,9 +346,10 @@ subroutine static_init_model() call init_interp() !> @todo 'pop.r.nc' is hardcoded in dart_pop_mod.f90 -domain_id = add_domain('pop.r.nc', nfields, & - var_names = variable_table(1:nfields, VAR_NAME_INDEX), & - update_list = update_var_list(1:nfields)) +domain_id = add_domain('pop.r.nc', state_vars%nvars, & + var_names = state_vars%netcdf_var_names, & + kind_list = state_vars%qtys, & + update_list = state_vars%updates) model_size = get_domain_size(domain_id) if (do_output()) write(*,*) 'model_size = ', model_size @@ -1796,7 +1787,7 @@ function get_varid_from_kind(dart_kind) integer :: i do i = 1, get_num_variables(domain_id) - if (dart_kind == state_kinds_list(i)) then + if (dart_kind == state_vars%qtys(i)) then get_varid_from_kind = i return endif @@ -1825,7 +1816,7 @@ subroutine get_state_kind(var_ind, var_type) if ( .not. module_initialized ) call static_init_model -var_type = state_kinds_list(var_ind) +var_type = state_vars%qtys(var_ind) end subroutine get_state_kind @@ -2746,103 +2737,6 @@ subroutine vert_convert(state_handle, location, obs_kind, istatus) end subroutine vert_convert -!------------------------------------------------------------------ -!> Verify that the namelist was filled in correctly, and check -!> that there are valid entries for the dart_kind. -!> Returns a table with columns: -!> -!> netcdf_variable_name ; dart_kind_string ; update_string - - -subroutine verify_state_variables( state_variables, ngood, table, kind_list, update_var ) - -character(len=*), intent(inout) :: state_variables(:) -integer, intent(out) :: ngood -character(len=*), intent(out) :: table(:,:) -integer, intent(out) :: kind_list(:) ! kind number -logical, optional, intent(out) :: update_var(:) ! logical update - -integer :: nrows, i -character(len=NF90_MAX_NAME) :: varname, dartstr, update - -if ( .not. module_initialized ) call static_init_model - -nrows = size(table,1) - -ngood = 0 - -if ( state_variables(1) == ' ' ) then ! no model_state_variables namelist provided - call use_default_state_variables( state_variables ) - string1 = 'model_nml:model_state_variables not specified using default variables' - call error_handler(E_MSG,'verify_state_variables',string1,source,revision,revdate) -endif - -MyLoop : do i = 1, nrows - - varname = trim(state_variables(3*i -2)) - dartstr = trim(state_variables(3*i -1)) - update = trim(state_variables(3*i )) - - call to_upper(update) - - table(i,1) = trim(varname) - table(i,2) = trim(dartstr) - table(i,3) = trim(update) - - if ( table(i,1) == ' ' .and. table(i,2) == ' ' .and. table(i,3) == ' ') exit MyLoop ! Found end of list. - - if ( table(i,1) == ' ' .or. table(i,2) == ' ' .or. table(i,3) == ' ' ) then - string1 = 'model_nml:model_state_variables not fully specified' - call error_handler(E_ERR,'verify_state_variables',string1,source,revision,revdate) - endif - - ! Make sure DART kind is valid - - kind_list(i) = get_index_for_quantity(dartstr) - if( kind_list(i) < 0 ) then - write(string1,'(''there is no obs_kind <'',a,''> in obs_kind_mod.f90'')') trim(dartstr) - call error_handler(E_ERR,'verify_state_variables',string1,source,revision,revdate) - endif - - ! Make sure the update variable has a valid name - - if ( present(update_var) )then - SELECT CASE (update) - CASE ('UPDATE') - update_var(i) = .true. - CASE ('NO_COPY_BACK') - update_var(i) = .false. - CASE DEFAULT - write(string1,'(A)') 'only UPDATE or NO_COPY_BACK supported in model_state_variable namelist' - write(string2,'(6A)') 'you provided : ', trim(varname), ', ', trim(dartstr), ', ', trim(update) - call error_handler(E_ERR,'verify_state_variables',string1,source,revision,revdate, text2=string2) - END SELECT - endif - - ! Record the contents of the DART state vector - - if (do_output()) then - write(string1,'(A,I2,6A)') 'variable ',i,' is ',trim(varname), ', ', trim(dartstr), ', ', trim(update) - call error_handler(E_MSG,'verify_state_variables',string1,source,revision,revdate) - endif - - ngood = ngood + 1 -enddo MyLoop - -! check to see if temp and salinity are both in the state otherwise you will not -! be able to interpolate in XXX subroutine -if ( any(kind_list == QTY_SALINITY) ) then - ! check to see that temperature is also in the variable list - if ( .not. any(kind_list == QTY_POTENTIAL_TEMPERATURE) ) then - write(string1,'(A)') 'in order to compute temperature you need to have both ' - write(string2,'(A)') 'QTY_SALINITY and QTY_POTENTIAL_TEMPERATURE in the model state' - call error_handler(E_ERR,'verify_state_variables',string1,source,revision,revdate, text2=string2) - endif -endif - -end subroutine verify_state_variables - - !------------------------------------------------------------------ !> Default state_variables from the original pop model_mod. Must !> keep in the same order to be consistent with previous versions. From d76f1cb4a70e832e5a27b35ecac746c2a8616a85 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Mon, 16 Dec 2024 13:45:11 -0700 Subject: [PATCH 19/45] Replacing aether_lat-lon version of the routine with generic in default_model_mod --- models/aether_lat-lon/model_mod.f90 | 88 +++-------------------------- 1 file changed, 8 insertions(+), 80 deletions(-) diff --git a/models/aether_lat-lon/model_mod.f90 b/models/aether_lat-lon/model_mod.f90 index 87ac6c0e9c..3b47750b47 100644 --- a/models/aether_lat-lon/model_mod.f90 +++ b/models/aether_lat-lon/model_mod.f90 @@ -61,7 +61,8 @@ module model_mod pert_model_copies, read_model_time, write_model_time, & init_time => fail_init_time, & init_conditions => fail_init_conditions, & - convert_vertical_obs, convert_vertical_state, adv_1step + convert_vertical_obs, convert_vertical_state, adv_1step, & + get_state_variables, state_var_type implicit none private @@ -88,6 +89,7 @@ module model_mod character(len=256), parameter :: source = 'aether_lat-lon/model_mod.f90' logical :: module_initialized = .false. +logical, parameter :: use_clamping = .true. integer :: dom_id ! used to access the state structure type(time_type) :: assimilation_time_step @@ -99,15 +101,7 @@ module model_mod integer, parameter :: MAX_STATE_VARIABLES = 100 integer, parameter :: NUM_STATE_TABLE_COLUMNS = 5 -character(len=vtablenamelength) :: variables(NUM_STATE_TABLE_COLUMNS,MAX_STATE_VARIABLES) = '' - -type :: var_type - integer :: count - character(len=64), allocatable :: names(:) - integer, allocatable :: qtys(:) - real(r8), allocatable :: clamp_values(:, :) - logical, allocatable :: updates(:) -end type var_type +character(len=vtablenamelength) :: variables(NUM_STATE_TABLE_COLUMNS*MAX_STATE_VARIABLES) = '' namelist /model_nml/ template_file, time_step_days, time_step_seconds, variables @@ -157,7 +151,7 @@ module model_mod subroutine static_init_model() integer :: iunit, io -type(var_type) :: var +type(state_var_type) :: state_vars module_initialized = .true. @@ -179,7 +173,7 @@ subroutine static_init_model() lat_start = lats(1) lat_delta = lats(2) - lats(1) -var = assign_var(variables, MAX_STATE_VARIABLES) +call get_state_variables(variables, MAX_STATE_VARIABLES, use_clamping, state_vars) ! This time is both the minimum time you can ask the model to advance ! (for models that can be advanced by filter) and it sets the assimilation @@ -190,8 +184,8 @@ subroutine static_init_model() ! Define which variables are in the model state ! This is using add_domain_from_file (arg list matches) -dom_id = add_domain(template_file, var%count, var%names, var%qtys, & - var%clamp_values, var%updates) +dom_id = add_domain(template_file, state_vars%nvars, state_vars%netcdf_var_names, state_vars%qtys, & + state_vars%clamp_values, state_vars%updates) call state_structure_info(dom_id) @@ -409,72 +403,6 @@ subroutine assign_dimensions() end subroutine assign_dimensions -!----------------------------------------------------------------------- -! Parse the table of variables characteristics into arrays for easier access. - -function assign_var(variables, MAX_STATE_VARIABLES) result(var) - -character(len=vtablenamelength), intent(in) :: variables(:, :) -integer, intent(in) :: MAX_STATE_VARIABLES - -type(var_type) :: var -integer :: ivar -character(len=vtablenamelength) :: table_entry - -!----------------------------------------------------------------------- -! Codes for interpreting the NUM_STATE_TABLE_COLUMNS of the variables table -integer, parameter :: NAME_INDEX = 1 ! ... variable name -integer, parameter :: QTY_INDEX = 2 ! ... DART qty -integer, parameter :: MIN_VAL_INDEX = 3 ! ... minimum value if any -integer, parameter :: MAX_VAL_INDEX = 4 ! ... maximum value if any -integer, parameter :: UPDATE_INDEX = 5 ! ... update (state) or not - -! Loop through the variables array to get the actual count of the number of variables -do ivar = 1, MAX_STATE_VARIABLES - ! If the element is an empty string, the loop has exceeded the extent of the variables - if (variables(1, ivar) == '') then - var%count = ivar-1 - exit - endif -enddo - -! Allocate the arrays in the var derived type -allocate(var%names(var%count), var%qtys(var%count), var%clamp_values(var%count, 2), var%updates(var%count)) - -do ivar = 1, var%count - - var%names(ivar) = trim(variables(NAME_INDEX, ivar)) - - table_entry = variables(QTY_INDEX, ivar) - call to_upper(table_entry) - - var%qtys(ivar) = get_index_for_quantity(table_entry) - - if (variables(MIN_VAL_INDEX, ivar) /= 'NA') then - read(variables(MIN_VAL_INDEX, ivar), '(d16.8)') var%clamp_values(ivar,1) - else - var%clamp_values(ivar,1) = MISSING_R8 - endif - - if (variables(MAX_VAL_INDEX, ivar) /= 'NA') then - read(variables(MAX_VAL_INDEX, ivar), '(d16.8)') var%clamp_values(ivar,2) - else - var%clamp_values(ivar,2) = MISSING_R8 - endif - - table_entry = variables(UPDATE_INDEX, ivar) - call to_upper(table_entry) - - if (table_entry == 'UPDATE') then - var%updates(ivar) = .true. - else - var%updates(ivar) = .false. - endif - -enddo - -end function assign_var - !----------------------------------------------------------------------- ! Extract state values needed by the interpolation from all ensemble members. From 979a9cf4f94850429a26d02aedf3408f1371182c Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Mon, 16 Dec 2024 13:53:24 -0700 Subject: [PATCH 20/45] Replacing NOAH version of the routine with generic in default_model_mod --- models/noah/model_mod.f90 | 108 ++++---------------------------------- 1 file changed, 11 insertions(+), 97 deletions(-) diff --git a/models/noah/model_mod.f90 b/models/noah/model_mod.f90 index 3be41d10c1..ac2a5bbd8a 100644 --- a/models/noah/model_mod.f90 +++ b/models/noah/model_mod.f90 @@ -46,7 +46,8 @@ module model_mod use distributed_state_mod, only : get_state -use default_model_mod, only : adv_1step, nc_write_model_vars +use default_model_mod, only : adv_1step, nc_write_model_vars, & + get_state_variables, state_var_type use noah_hydro_mod, only : configure_lsm, get_noah_timestepping, & num_soil_layers, lsm_namelist_filename, & @@ -161,13 +162,15 @@ module model_mod real(r8) :: model_perturbation_amplitude = 0.002 character(len=256) :: perturb_distribution = 'lognormal' integer :: debug = 0 ! turn up for more and more debug messages -character(len=obstypelength) :: lsm_variables(NUM_STATE_TABLE_COLUMNS,MAX_STATE_VARIABLES) = ' ' +character(len=obstypelength) :: lsm_variables(NUM_STATE_TABLE_COLUMNS*MAX_STATE_VARIABLES) = ' ' !nc -- we are adding these to the model.nml until they appear in the NetCDF files logical :: polar = .false. ! wrap over the poles logical :: periodic_x = .false. ! wrap in longitude or x logical :: periodic_y = .false. ! used for single column model, wrap in y +logical, parameter :: use_clamping = .true. + namelist /model_nml/ domain_shapefiles, & lsm_model_choice, & assimilation_period_days, & @@ -226,10 +229,7 @@ subroutine static_init_model() integer :: n_lsm_fields integer :: i -character(len=obstypelength) :: var_names(MAX_STATE_VARIABLES) -real(r8) :: var_ranges(MAX_STATE_VARIABLES,2) -logical :: var_update(MAX_STATE_VARIABLES) -integer :: var_qtys( MAX_STATE_VARIABLES) +type(state_var_type) :: state_vars character(len=256) :: filename @@ -274,12 +274,11 @@ subroutine static_init_model() else call configure_lsm(lsm_model_choice,domain_shapefiles(domainID)) call read_noah_global_atts(domain_shapefiles(domainID)) - call verify_variables(lsm_variables, domain_shapefiles(domainID), n_lsm_fields, & - var_names, var_qtys, var_ranges, var_update) - idom_lsm = add_domain(domain_shapefiles(domainID), n_lsm_fields, var_names, & - kind_list=var_qtys, & - clamp_vals=var_ranges(1:n_lsm_fields,:), & - update_list=var_update) + call get_state_variables(lsm_variables, MAX_STATE_VARIABLES, use_clamping, state_vars) + idom_lsm = add_domain(domain_shapefiles(domainID), state_vars%nvars, state_vars%netcdf_var_names, & + kind_list=state_vars%qtys, & + clamp_vals=state_vars%clamp_values, & + update_list=state_vars%updates) if (debug > 99) call state_structure_info(idom_lsm) call check_vertical_dimension(idom_lsm) @@ -1220,91 +1219,6 @@ end subroutine pert_model_copies ! with predefined file formats and control structures.) !================================================================== - -!------------------------------------------------------------------ -!> given the list of variables and a filename, check user input -!> return the handle to the open netCDF file and the number of variables -!> in this 'domain' - -subroutine verify_variables( variable_table, filename, ngood, & - var_names, var_qtys, var_ranges, var_update) - -character(len=*), intent(in) :: variable_table(:,:) -character(len=*), intent(in) :: filename -integer, intent(out) :: ngood -character(len=*), intent(out) :: var_names(:) -real(r8), intent(out) :: var_ranges(:,:) -logical, intent(out) :: var_update(:) -integer , intent(out) :: var_qtys(:) - -character(len=*), parameter :: routine = 'verify_variables' - -integer :: io, i, quantity -real(r8) :: minvalue, maxvalue - -character(len=NF90_MAX_NAME) :: varname -character(len=NF90_MAX_NAME) :: dartstr -character(len=NF90_MAX_NAME) :: minvalstring -character(len=NF90_MAX_NAME) :: maxvalstring -character(len=NF90_MAX_NAME) :: state_or_aux - -ngood = 0 -MyLoop : do i = 1, size(variable_table,2) - - varname = variable_table(VT_VARNAMEINDX,i) - dartstr = variable_table(VT_KINDINDX ,i) - minvalstring = variable_table(VT_MINVALINDX ,i) - maxvalstring = variable_table(VT_MAXVALINDX ,i) - state_or_aux = variable_table(VT_STATEINDX ,i) - - if ( varname == ' ' .and. dartstr == ' ' ) exit MyLoop ! Found end of list. - - if ( varname == ' ' .or. dartstr == ' ' ) then - string1 = 'model_nml: variable list not fully specified' - string2 = 'reading from "'//trim(filename)//'"' - call error_handler(E_ERR,routine, string1, source, text2=string2) - endif - - ! The internal DART routines check if the variable name is valid. - - ! Make sure DART kind is valid - quantity = get_index_for_quantity(dartstr) - if( quantity < 0 ) then - write(string1,'(''there is no obs_kind "'',a,''" in obs_kind_mod.f90'')') & - trim(dartstr) - call error_handler(E_ERR,routine,string1,source) - endif - - ! All good to here - fill the output variables - - ngood = ngood + 1 - var_names( ngood) = varname - var_qtys( ngood) = quantity - var_ranges(ngood,:) = (/ MISSING_R8, MISSING_R8 /) - var_update(ngood) = .false. ! at least initially - - ! convert the [min,max]valstrings to numeric values if possible - read(minvalstring,*,iostat=io) minvalue - if (io == 0) var_ranges(ngood,1) = minvalue - - read(maxvalstring,*,iostat=io) maxvalue - if (io == 0) var_ranges(ngood,2) = maxvalue - - call to_upper(state_or_aux) - if (state_or_aux == 'UPDATE') var_update(ngood) = .true. - -enddo MyLoop - -if (ngood == MAX_STATE_VARIABLES) then - string1 = 'WARNING: you may need to increase "MAX_STATE_VARIABLES"' - write(string2,'(''you have specified at least '',i4,'' perhaps more.'')') ngood - call error_handler(E_MSG,routine,string1,source,text2=string2) -endif - - -end subroutine verify_variables - - !----------------------------------------------------------------------- !> Sets the location information arrays for each domain !> Each location array is declared to be 3D to make it easy to use From 3e629cf3a224f847bbee5b04dd21f721ba360762 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Mon, 16 Dec 2024 13:55:36 -0700 Subject: [PATCH 21/45] Replacing MITgcm_ocean version of the routine with generic in default_model_mod --- models/MITgcm_ocean/model_mod.f90 | 106 +++--------------------------- 1 file changed, 10 insertions(+), 96 deletions(-) diff --git a/models/MITgcm_ocean/model_mod.f90 b/models/MITgcm_ocean/model_mod.f90 index 85d635e520..e3966c72cf 100644 --- a/models/MITgcm_ocean/model_mod.f90 +++ b/models/MITgcm_ocean/model_mod.f90 @@ -43,7 +43,8 @@ module model_mod use random_seq_mod, only : random_seq_type, init_random_seq, random_gaussian use default_model_mod, only : nc_write_model_vars, adv_1step, & - init_conditions => fail_init_conditions + init_conditions => fail_init_conditions, & + get_state_variables, state_var_type use dart_time_io_mod, only : write_model_time @@ -127,6 +128,8 @@ module model_mod logical :: calendarDumps = .false. logical :: pickupStrictlyMatch = .false. +logical, parameter :: use_clamping = .true. + NAMELIST /CAL_NML/ TheCalendar, startDate_1, startDate_2, calendarDumps ! FIXME: these namelists should probably be in a separate file, and only @@ -285,7 +288,7 @@ module model_mod integer, parameter :: VT_STATEINDX = 5 ! ... update (state) or not integer, parameter :: MAX_STATE_VARIABLES = 20 integer, parameter :: NUM_STATE_TABLE_COLUMNS = 5 -character(len=vtablenamelength) :: mitgcm_variables(NUM_STATE_TABLE_COLUMNS, MAX_STATE_VARIABLES ) = ' ' +character(len=vtablenamelength) :: mitgcm_variables(NUM_STATE_TABLE_COLUMNS*MAX_STATE_VARIABLES ) = ' ' character(len=256) :: model_shape_file = ' ' integer :: assimilation_period_days = 7 @@ -317,7 +320,6 @@ module model_mod end type MIT_meta_type integer :: domain_id -integer :: nvars contains @@ -330,10 +332,7 @@ module model_mod subroutine static_init_model() -character(len=vtablenamelength) :: var_names(MAX_STATE_VARIABLES) = ' ' -integer :: quantity_list(MAX_STATE_VARIABLES) = MISSING_I -real(r8) :: clamp_vals(MAX_STATE_VARIABLES,2) = MISSING_R8 -logical :: update_list(MAX_STATE_VARIABLES) = .FALSE. +type(state_var_type) :: state_vars integer :: i, iunit, io integer :: ss, dd @@ -533,11 +532,11 @@ subroutine static_init_model() if (do_output()) write( * , *) 'Using grid size : ' if (do_output()) write( * , *) ' Nx, Ny, Nz = ', Nx, Ny, Nz -call parse_variable_input(mitgcm_variables, model_shape_file, nvars, & - var_names, quantity_list, clamp_vals, update_list) +call get_state_variables(mitgcm_variables, MAX_STATE_VARIABLES, use_clamping, state_vars) -domain_id = add_domain(model_shape_file, nvars, & - var_names, quantity_list, clamp_vals, update_list ) +domain_id = add_domain(model_shape_file, state_vars%nvars, & + state_vars%netcdf_var_names, state_vars%qtys, & + state_vars%clamp_values, state_vars%updates) if (compress) then ! read in compressed coordinates @@ -2050,91 +2049,6 @@ subroutine write_data_namelistfile end subroutine write_data_namelistfile -!----------------------------------------------------------------------- -!> -!> Fill the array of requested variables, dart kinds, possible min/max -!> values and whether or not to update the field in the output file. -!> -!>@param state_variables the list of variables and kinds from model_mod_nml -!>@param ngood the number of variable/KIND pairs specified - -subroutine parse_variable_input(state_variables, filename, ngood, & - var_names, quantity_list, clamp_vals, update_list) - -character(len=*), intent(in) :: state_variables(:,:) -character(len=*), intent(in) :: filename -integer, intent(out) :: ngood -character(len=*), intent(out) :: var_names(:) -integer, intent(out) :: quantity_list(:) -real(r8), intent(out) :: clamp_vals(:,:) -logical, intent(out) :: update_list(:) - -integer :: i -character(len=NF90_MAX_NAME) :: varname -character(len=NF90_MAX_NAME) :: dartstr -character(len=NF90_MAX_NAME) :: minvalstring -character(len=NF90_MAX_NAME) :: maxvalstring -character(len=NF90_MAX_NAME) :: updateable - -ngood = 0 -MyLoop : do i = 1, MAX_STATE_VARIABLES - - varname = trim(state_variables(VT_VARNAMEINDX,i)) - dartstr = trim(state_variables(VT_KINDINDX ,i)) - minvalstring = trim(state_variables(VT_MINVALINDX ,i)) - maxvalstring = trim(state_variables(VT_MAXVALINDX ,i)) - updateable = trim(state_variables(VT_STATEINDX ,i)) - - if ( varname == ' ' .and. dartstr == ' ' ) exit MyLoop ! Found end of list. - - if ( varname == ' ' .or. dartstr == ' ' ) then - string1 = 'model_nml:model "variables" not fully specified' - string2 = 'reading from "'//trim(filename)//'"' - call error_handler(E_ERR,'parse_variable_input:',string1,source,text2=string2) - endif - - ! Make sure DART quantity is valid - - if( get_index_for_quantity(dartstr) < 0 ) then - write(string1,'(''there is no quantity <'',a,''> in obs_kind_mod.f90'')') trim(dartstr) - call error_handler(E_ERR,'parse_variable_input:',string1,source) - endif - - call to_upper(minvalstring) - call to_upper(maxvalstring) - call to_upper(updateable) - - var_names(i) = varname - quantity_list(i) = get_index_for_quantity(dartstr) - clamp_vals(i, 1) = string_to_real(minvalstring) - clamp_vals(i, 2) = string_to_real(maxvalstring) - update_list(i) = string_to_logical(updateable, 'UPDATE') - - ! Adjust clamping in case of log-transform - if (quantity_list(i) == QTY_NITRATE_CONCENTRATION ) call adjust_clamp(clamp_vals(i, 1)) - if (quantity_list(i) == QTY_PHOSPHATE_CONCENTRATION ) call adjust_clamp(clamp_vals(i, 1)) - if (quantity_list(i) == QTY_DISSOLVED_OXYGEN ) call adjust_clamp(clamp_vals(i, 1)) - if (quantity_list(i) == QTY_PHYTOPLANKTON_BIOMASS ) call adjust_clamp(clamp_vals(i, 1)) - if (quantity_list(i) == QTY_ALKALINITY ) call adjust_clamp(clamp_vals(i, 1)) - if (quantity_list(i) == QTY_DISSOLVED_INORGANIC_CARBON) call adjust_clamp(clamp_vals(i, 1)) - if (quantity_list(i) == QTY_DISSOLVED_ORGANIC_P ) call adjust_clamp(clamp_vals(i, 1)) - if (quantity_list(i) == QTY_DISSOLVED_ORGANIC_NITROGEN) call adjust_clamp(clamp_vals(i, 1)) - if (quantity_list(i) == QTY_DISSOLVED_INORGANIC_IRON ) call adjust_clamp(clamp_vals(i, 1)) - if (quantity_list(i) == QTY_SURFACE_CHLOROPHYLL ) call adjust_clamp(clamp_vals(i, 1)) - - ngood = ngood + 1 - -enddo MyLoop - -if (ngood == MAX_STATE_VARIABLES) then - string1 = 'WARNING: There is a possibility you need to increase ''MAX_STATE_VARIABLES''' - write(string2,'(''WARNING: you have specified at least '',i4,'' perhaps more.'')')ngood - call error_handler(E_MSG,'parse_variable_input:',string1,source,text2=string2) -endif - -end subroutine parse_variable_input - - !----------------------------------------------------------------------- !> !> We may choose to do a log transformation for the bgc tracers From 0d759f34790a607dff0ad27177e8a3f816b3cb5e Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Mon, 16 Dec 2024 14:01:43 -0700 Subject: [PATCH 22/45] Replacing CICE version of the routine with generic in default_model_mod --- models/cice/model_mod.f90 | 121 ++++---------------------------------- 1 file changed, 10 insertions(+), 111 deletions(-) diff --git a/models/cice/model_mod.f90 b/models/cice/model_mod.f90 index 4346d584aa..1a41047031 100644 --- a/models/cice/model_mod.f90 +++ b/models/cice/model_mod.f90 @@ -1,8 +1,6 @@ ! DART software - Copyright UCAR. This open source software is provided ! by UCAR, "as is", without charge, subject to all terms of use at ! http://www.image.ucar.edu/DAReS/DART/DART_download -! -! $Id$ module model_mod @@ -37,7 +35,7 @@ module model_mod nc_write_location use default_model_mod, only : init_time, init_conditions, adv_1step, & - nc_write_model_vars + nc_write_model_vars, get_state_variables, state_var_type use utilities_mod, only : register_module, error_handler, & E_ERR, E_MSG, nmlfileunit, get_unit, & @@ -170,14 +168,7 @@ module model_mod ! DART state vector contents are specified in the input.nml:&model_nml namelist. integer, parameter :: max_state_variables = 10 integer, parameter :: num_state_table_columns = 3 -character(len=NF90_MAX_NAME) :: variable_table( max_state_variables, num_state_table_columns ) -integer :: state_kinds_list( max_state_variables ) -logical :: update_var_list( max_state_variables ) - -! identifiers for variable_table -integer, parameter :: VAR_NAME_INDEX = 1 -integer, parameter :: VAR_QTY_INDEX = 2 -integer, parameter :: VAR_UPDATE_INDEX = 3 +type(state_var_type) :: state_vars ! things which can/should be in the model_nml integer :: assimilation_period_days = 1 @@ -241,9 +232,6 @@ module model_mod ! is not being used. !------------------------------------------------------------------ -! Number of fields in the state vector -integer :: nfields - ! Grid parameters - the values will be read from a ! standard cice namelist and filled in here. @@ -411,9 +399,8 @@ subroutine static_init_model() if (debug > 2) call write_grid_interptest() ! DEBUG only ! verify that the model_state_variables namelist was filled in correctly. -! returns variable_table which has variable names, kinds and update strings. -call verify_state_variables(model_state_variables, nfields, variable_table, & - state_kinds_list, update_var_list) +! returns state_vars which has netcdf variable names, kinds and update strings. +call get_state_variables(model_state_variables, MAX_STATE_VARIABLES, state_vars) ! in spite of the staggering, all grids are the same size ! and offset by half a grid cell. 4 are 3D and 2 are 2D. @@ -431,10 +418,10 @@ subroutine static_init_model() ! Determine the shape of the variables from "cice.r.nc" ! The assimilate.csh, perfect_model.csh must ensure the cice restart file ! is linked to this filename. -domain_id = add_domain('cice.r.nc', nfields, & - var_names = variable_table(1:nfields, VAR_NAME_INDEX), & - kind_list = state_kinds_list(1:nfields), & - update_list = update_var_list(1:nfields)) +domain_id = add_domain('cice.r.nc', state_vars%nvars, & + var_names = state_vars%netcdf_var_names, & + kind_list = state_vars%qtys, & + update_list = state_vars%updates) if (debug > 2) call state_structure_info(domain_id) @@ -1867,7 +1854,7 @@ function get_varid_from_kind(dart_kind) integer :: i do i = 1, get_num_variables(domain_id) - if (dart_kind == state_kinds_list(i)) then + if (dart_kind == state_vars%qtys(i)) then get_varid_from_kind = i return endif @@ -1897,7 +1884,7 @@ subroutine get_state_kind(var_ind, var_type) if ( .not. module_initialized ) call static_init_model -var_type = state_kinds_list(var_ind) +var_type = state_vars%qtys(var_ind) end subroutine get_state_kind @@ -2587,94 +2574,6 @@ function read_model_time(filename) end function read_model_time -!------------------------------------------------------------------ -!> Verify that the namelist was filled in correctly, and check -!> that there are valid entries for the dart_kind. -!> Returns a table with columns: -!> -!> netcdf_variable_name ; dart_kind_string ; update_string -!> - -subroutine verify_state_variables( state_variables, ngood, table, kind_list, update_var ) - -character(len=*), intent(inout) :: state_variables(:) -integer, intent(out) :: ngood -character(len=*), intent(out) :: table(:,:) -integer, intent(out) :: kind_list(:) ! kind number -logical, optional, intent(out) :: update_var(:) ! logical update - -integer :: nrows, i -character(len=NF90_MAX_NAME) :: varname, dartstr, update - -if ( .not. module_initialized ) call static_init_model - -nrows = size(table,1) - -ngood = 0 - -!>@todo deprecate. Remove a hidden 'default' set of variables. -!>@ The default is provided in the input namelist. - -if ( state_variables(1) == ' ' ) then ! no model_state_variables namelist provided - call use_default_state_variables( state_variables ) - string1 = 'model_nml:model_state_variables not specified using default variables' - call error_handler(E_MSG,'verify_state_variables',string1,source,revision,revdate) -endif - -MyLoop : do i = 1, nrows - - varname = trim(state_variables(3*i -2)) - dartstr = trim(state_variables(3*i -1)) - update = trim(state_variables(3*i )) - - call to_upper(update) - - table(i,1) = trim(varname) - table(i,2) = trim(dartstr) - table(i,3) = trim(update) - - if ( table(i,1) == ' ' .and. table(i,2) == ' ' .and. table(i,3) == ' ') exit MyLoop - - if ( table(i,1) == ' ' .or. table(i,2) == ' ' .or. table(i,3) == ' ' ) then - string1 = 'model_nml:model_state_variables not fully specified' - call error_handler(E_ERR,'verify_state_variables',string1,source,revision,revdate) - endif - - ! Make sure DART kind is valid - - kind_list(i) = get_index_for_quantity(dartstr) - if( kind_list(i) < 0 ) then - write(string1,'(''there is no obs_kind <'',a,''> in obs_kind_mod.f90'')') trim(dartstr) - call error_handler(E_ERR,'verify_state_variables',string1,source,revision,revdate) - endif - - ! Make sure the update variable has a valid name - - if ( present(update_var) )then - SELECT CASE (update) - CASE ('UPDATE') - update_var(i) = .true. - CASE ('NO_COPY_BACK') - update_var(i) = .false. - CASE DEFAULT - write(string1,'(A)') 'only UPDATE or NO_COPY_BACK supported in model_state_variable namelist' - write(string2,'(6A)') 'you provided : ', trim(varname), ', ', trim(dartstr), ', ', trim(update) - call error_handler(E_ERR,'verify_state_variables',string1,source,revision,revdate, text2=string2) - END SELECT - endif - - ! Record the contents of the DART state vector - - if (do_output()) then - write(string1,'(A,I2,6A)') 'variable ',i,' is ',trim(varname), ', ', trim(dartstr), ', ', trim(update) - call error_handler(E_MSG,'verify_state_variables',string1,source,revision,revdate) - endif - - ngood = ngood + 1 -enddo MyLoop - -end subroutine verify_state_variables - !------------------------------------------------------------------ !> Default state_variables from model_mod. !>@todo DEPRECATE From 4d790b549bd564af8351ac016de9b5e0cd036b94 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Tue, 17 Dec 2024 15:30:19 -0700 Subject: [PATCH 23/45] Fixing print statement in default_model_mod --- models/utilities/default_model_mod.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/utilities/default_model_mod.f90 b/models/utilities/default_model_mod.f90 index e25cdbd51b..ed8f911901 100644 --- a/models/utilities/default_model_mod.f90 +++ b/models/utilities/default_model_mod.f90 @@ -429,7 +429,7 @@ subroutine get_state_variables_noclamp(nml_state_vars, MAX_STATE_VARIABLES, stat ! Make sure DART qty is valid state_vars%qtys(i) = get_index_for_quantity(dart_qty_str) if( state_vars%qtys(i) < 0 ) then - write(string1,*) 'The quantity specified in the &model_nml "', dart_qty_str, '", is not present in obs_kind_mod.f90' + write(string1,'(3A)') 'The quantity specified in the &model_nml "', trim(dart_qty_str), '", is not present in obs_kind_mod.f90' call error_handler(E_ERR,'verify_state_variables',string1) endif From 83ad490812780462b919d44c568590c2b61d6004 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Thu, 9 Jan 2025 12:59:51 -0700 Subject: [PATCH 24/45] - Make function instead of subroutine - Make the name of the function parse_variables - Remove interface to create two distinct functions: parse_variables and parse_variables_clamp - Remove use_clamping and max_state_vars arguments - Update the calls to the function in all edited model_mods --- .../modules/io/state_structure_mod.f90 | 57 ++++++++- models/MITgcm_ocean/model_mod.f90 | 22 +--- models/MOM6/model_mod.f90 | 27 ++--- models/POP/model_mod.f90 | 88 +++----------- models/aether_lat-lon/model_mod.f90 | 19 ++- .../cam-common-code/cam_common_code_mod.f90 | 6 +- models/cam-fv/model_mod.f90 | 22 ++-- models/cam-se/model_mod.f90 | 23 ++-- models/cice/model_mod.f90 | 105 +++++------------ models/noah/model_mod.f90 | 24 +--- models/utilities/default_model_mod.f90 | 109 +++++++++--------- models/wrf_hydro/model_mod.f90 | 36 ++---- 12 files changed, 201 insertions(+), 337 deletions(-) diff --git a/assimilation_code/modules/io/state_structure_mod.f90 b/assimilation_code/modules/io/state_structure_mod.f90 index 97de1ea97e..132f63239a 100644 --- a/assimilation_code/modules/io/state_structure_mod.f90 +++ b/assimilation_code/modules/io/state_structure_mod.f90 @@ -68,6 +68,8 @@ module state_structure_mod use sort_mod, only : index_sort +use default_model_mod, only : state_var_type + use netcdf implicit none @@ -281,6 +283,7 @@ module state_structure_mod module procedure add_domain_blank module procedure add_domain_from_file module procedure add_domain_from_spec + module procedure add_domain_from_state_type end interface interface get_index_start @@ -313,8 +316,6 @@ module state_structure_mod !> into the state_strucutre. !> !> Returns a dom_id that can be used to harvest information of a particular domain -!> -!> Does this need to be a function or a subroutine? function add_domain_from_file(info_file, num_vars, var_names, kind_list, clamp_vals, update_list) result(dom_id) @@ -365,6 +366,58 @@ function add_domain_from_file(info_file, num_vars, var_names, kind_list, clamp_v end function add_domain_from_file +!------------------------------------------------------------------------------- +!> Given an info_file, reads in a state_var_type including nvars, netcdf +!> variable names, qtys (kinds), clamp values (optional), and updates into the +!> state_structure +!> +!> Returns a dom_id that can be used to harvest information of a particular domain + + +function add_domain_from_state_type(info_file, vars) result(dom_id) + +character(len=*), intent(in) :: info_file +type(state_var_type), intent(in) :: vars +integer :: dom_id + +integer :: ivar + +! add to domains +call assert_below_max_num_domains('add_domain_from_file') +state%num_domains = state%num_domains + 1 +!>@todo dom_id should be a handle. +dom_id = state%num_domains + +! save information about the information file +state%domain(dom_id)%info_file = info_file +state%domain(dom_id)%method = 'file' + +! set number of variables in this domain +state%domain(dom_id)%num_variables = vars%nvars + +! load up the variable names +allocate(state%domain(dom_id)%variable(vars%nvars)) + +do ivar = 1, vars%nvars + state%domain(dom_id)%variable(ivar)%varname = vars%netcdf_var_names(ivar) +enddo + +! load up variable id's and sizes +call load_state_variable_info(state%domain(dom_id),dom_id) + +! load up the domain unique dimension info +call load_unique_dim_info(dom_id) + +! load up any cf-conventions if they exist +call load_common_cf_conventions(state%domain(dom_id)) + +call set_dart_kinds(dom_id, vars%nvars, vars%qtys) +if (allocated(vars%clamp_values)) call set_clamping(dom_id, vars%nvars, vars%clamp_values) +call set_update_list(dom_id, vars%nvars, vars%updates) + +end function add_domain_from_state_type + + !------------------------------------------------------------------------------- !> Defines a skeleton structure for the state structure. Dimension can be !> added to variables with add_dimension_to_variable. diff --git a/models/MITgcm_ocean/model_mod.f90 b/models/MITgcm_ocean/model_mod.f90 index e3966c72cf..461a7cd25b 100644 --- a/models/MITgcm_ocean/model_mod.f90 +++ b/models/MITgcm_ocean/model_mod.f90 @@ -44,7 +44,8 @@ module model_mod use default_model_mod, only : nc_write_model_vars, adv_1step, & init_conditions => fail_init_conditions, & - get_state_variables, state_var_type + parse_variables_clamp, & + MAX_STATE_VARIABLE_FIELDS_CLAMP use dart_time_io_mod, only : write_model_time @@ -128,8 +129,6 @@ module model_mod logical :: calendarDumps = .false. logical :: pickupStrictlyMatch = .false. -logical, parameter :: use_clamping = .true. - NAMELIST /CAL_NML/ TheCalendar, startDate_1, startDate_2, calendarDumps ! FIXME: these namelists should probably be in a separate file, and only @@ -281,14 +280,7 @@ module model_mod ! Model namelist declarations with defaults ! Codes for interpreting the columns of the variable_table -integer, parameter :: VT_VARNAMEINDX = 1 ! ... variable name -integer, parameter :: VT_KINDINDX = 2 ! ... DART QUANTITY -integer, parameter :: VT_MINVALINDX = 3 ! ... minimum value if any -integer, parameter :: VT_MAXVALINDX = 4 ! ... maximum value if any -integer, parameter :: VT_STATEINDX = 5 ! ... update (state) or not -integer, parameter :: MAX_STATE_VARIABLES = 20 -integer, parameter :: NUM_STATE_TABLE_COLUMNS = 5 -character(len=vtablenamelength) :: mitgcm_variables(NUM_STATE_TABLE_COLUMNS*MAX_STATE_VARIABLES ) = ' ' +character(len=vtablenamelength) :: mitgcm_variables(MAX_STATE_VARIABLE_FIELDS_CLAMP) = ' ' character(len=256) :: model_shape_file = ' ' integer :: assimilation_period_days = 7 @@ -332,8 +324,6 @@ module model_mod subroutine static_init_model() -type(state_var_type) :: state_vars - integer :: i, iunit, io integer :: ss, dd integer :: ncid ! for reading compressed coordinates @@ -532,11 +522,7 @@ subroutine static_init_model() if (do_output()) write( * , *) 'Using grid size : ' if (do_output()) write( * , *) ' Nx, Ny, Nz = ', Nx, Ny, Nz -call get_state_variables(mitgcm_variables, MAX_STATE_VARIABLES, use_clamping, state_vars) - -domain_id = add_domain(model_shape_file, state_vars%nvars, & - state_vars%netcdf_var_names, state_vars%qtys, & - state_vars%clamp_values, state_vars%updates) +domain_id = add_domain(model_shape_file, parse_variables_clamp(mitgcm_variables)) if (compress) then ! read in compressed coordinates diff --git a/models/MOM6/model_mod.f90 b/models/MOM6/model_mod.f90 index 945548ceae..743dac1b27 100644 --- a/models/MOM6/model_mod.f90 +++ b/models/MOM6/model_mod.f90 @@ -58,7 +58,8 @@ module model_mod init_time => fail_init_time, & init_conditions => fail_init_conditions, & convert_vertical_obs, adv_1step, & - get_state_variables, state_var_type + parse_variables, & + MAX_STATE_VARIABLE_FIELDS implicit none private @@ -103,10 +104,6 @@ module model_mod ! Ocean vs land real(r8), allocatable :: wet(:,:), basin_depth(:,:) -! DART state vector contents are specified in the input.nml:&model_nml namelist. -integer, parameter :: MAX_STATE_VARIABLES = 10 -integer, parameter :: NUM_STATE_TABLE_COLUMNS = 3 - ! model_interpolate failure codes integer, parameter :: NOT_IN_STATE = 12 integer, parameter :: THICKNESS_NOT_IN_STATE = 13 @@ -125,7 +122,7 @@ module model_mod character(len=256) :: ocean_geometry = 'ocean_geometry.nc' integer :: assimilation_period_days = -1 integer :: assimilation_period_seconds = -1 -character(len=vtablenamelength) :: model_state_variables(MAX_STATE_VARIABLES * NUM_STATE_TABLE_COLUMNS ) = ' ' +character(len=vtablenamelength) :: model_state_variables(MAX_STATE_VARIABLE_FIELDS) = ' ' namelist /model_nml/ template_file, static_file, ocean_geometry, assimilation_period_days, & assimilation_period_seconds, model_state_variables @@ -148,7 +145,6 @@ module model_mod subroutine static_init_model() integer :: iunit, io -type(state_var_type) :: state_vars module_initialized = .true. @@ -156,7 +152,7 @@ subroutine static_init_model() read(iunit, nml = model_nml, iostat = io) call check_namelist_read(iunit, io, "model_nml") -! Record the namelist values used for the run +! Record the namelist values used for the run if (do_nml_file()) write(nmlfileunit, nml=model_nml) if (do_nml_term()) write( * , nml=model_nml) @@ -170,16 +166,11 @@ subroutine static_init_model() assimilation_time_step = set_time(assimilation_period_seconds, & assimilation_period_days) -! Reads the model_state_variables namelist entry into a table and returns -! state_var_type state_vars with nvars, netcdf variable names, kinds/qtys, -! and update logicals. -call get_state_variables(model_state_variables, MAX_STATE_VARIABLES, state_vars) - -! Define which variables are in the model state -dom_id = add_domain(template_file, state_vars%nvars, & - var_names = state_vars%netcdf_var_names(1:state_vars%nvars), & - kind_list = state_vars%qtys(1:state_vars%nvars), & - update_list = state_vars%updates(1:state_vars%nvars)) +! Define which variables are in the model state; +! parse_variables converts the character table that was read in from +! model_nml:model_state_variables to a state_var_type that can be passed +! to add_domain +dom_id = add_domain(template_file, parse_variables(model_state_variables)) model_size = get_domain_size(dom_id) diff --git a/models/POP/model_mod.f90 b/models/POP/model_mod.f90 index c18bec77e2..6661a5a324 100644 --- a/models/POP/model_mod.f90 +++ b/models/POP/model_mod.f90 @@ -49,10 +49,11 @@ module model_mod use state_structure_mod, only : add_domain, get_model_variable_indices, & get_num_variables, get_index_start, & get_num_dims, get_domain_size, & - get_dart_vector_index + get_dart_vector_index, get_varid_from_kind, & + get_kind_index use default_model_mod, only : adv_1step, init_time, init_conditions, & - nc_write_model_vars, get_state_variables, & - state_var_type + nc_write_model_vars, parse_variables, & + MAX_STATE_VARIABLE_FIELDS use typesizes use netcdf @@ -108,17 +109,12 @@ module model_mod ! Storage for a random sequence for perturbing a single initial state type(random_seq_type) :: random_seq -! DART state vector contents are specified in the input.nml:&model_nml namelist. -integer, parameter :: max_state_variables = 10 -integer, parameter :: num_state_table_columns = 3 -type(state_var_type) :: state_vars - ! things which can/should be in the model_nml integer :: assimilation_period_days = -1 integer :: assimilation_period_seconds = -1 real(r8) :: model_perturbation_amplitude = 0.2 logical :: update_dry_cell_walls = .false. -character(len=vtablenamelength) :: model_state_variables(max_state_variables * num_state_table_columns ) = ' ' +character(len=vtablenamelength) :: model_state_variables(MAX_STATE_VARIABLE_FIELDS) = ' ' integer :: debug = 0 ! turn up for more and more debug messages ! only valid values: native, big_endian, little_endian @@ -325,10 +321,6 @@ subroutine static_init_model() if (debug > 2) call write_grid_netcdf() ! DEBUG only if (debug > 2) call write_grid_interptest() ! DEBUG only -! verify that the model_state_variables namelist was filled in correctly. -! returns state_vars which has netcdf variable names, kinds and update strings. -call get_state_variables(model_state_variables, MAX_STATE_VARIABLES, state_vars) - ! in spite of the staggering, all grids are the same size ! and offset by half a grid cell. 4 are 3D and 1 is 2D. ! e.g. S,T,U,V = 256 x 225 x 70 @@ -346,10 +338,10 @@ subroutine static_init_model() call init_interp() !> @todo 'pop.r.nc' is hardcoded in dart_pop_mod.f90 -domain_id = add_domain('pop.r.nc', state_vars%nvars, & - var_names = state_vars%netcdf_var_names, & - kind_list = state_vars%qtys, & - update_list = state_vars%updates) +! parse_variables converts the character table that was read in from +! model_nml:model_state_variables and returns a state_var_type that +! can be passed to add_domain +domain_id = add_domain('pop.r.nc', parse_variables(model_state_variables)) model_size = get_domain_size(domain_id) if (do_output()) write(*,*) 'model_size = ', model_size @@ -832,7 +824,7 @@ subroutine model_interpolate(state_handle, ens_size, location, obs_type, expecte source, revision, revdate, text2=string2, text3=string3) endif - base_offset = get_index_start(domain_id, get_varid_from_kind(QTY_SEA_SURFACE_PRESSURE)) + base_offset = get_index_start(domain_id, get_varid_from_kind(domain_id, QTY_SEA_SURFACE_PRESSURE)) call lon_lat_interpolate(state_handle, ens_size, base_offset, llon, llat, & QTY_SEA_SURFACE_HEIGHT, 1, expected_obs, istatus, expected_mdt) @@ -853,10 +845,10 @@ subroutine model_interpolate(state_handle, ens_size, location, obs_type, expecte QTY_U_CURRENT_COMPONENT, & QTY_V_CURRENT_COMPONENT, & QTY_SEA_SURFACE_PRESSURE) - base_offset = get_index_start(domain_id, get_varid_from_kind(obs_type)) + base_offset = get_index_start(domain_id, get_varid_from_kind(domain_id, obs_type)) CASE (QTY_SEA_SURFACE_HEIGHT) - base_offset = get_index_start(domain_id, get_varid_from_kind(QTY_SEA_SURFACE_PRESSURE)) + base_offset = get_index_start(domain_id, get_varid_from_kind(domain_id, QTY_SEA_SURFACE_PRESSURE)) convert_to_ssh = .TRUE. ! simple linear transform of PSURF CASE DEFAULT @@ -1745,7 +1737,7 @@ subroutine get_state_meta_data(index_in, location, var_type) if ( .not. module_initialized ) call static_init_model call get_model_variable_indices(index_in, lon_index, lat_index, depth_index, var_id=var_id) -call get_state_kind(var_id, local_var) +local_var = get_kind_index(domain_id, var_id) if (is_on_ugrid(local_var)) then lon = ULON(lon_index, lat_index) @@ -1775,52 +1767,6 @@ subroutine get_state_meta_data(index_in, location, var_type) end subroutine get_state_meta_data -!-------------------------------------------------------------------- -!> given a DART kind, return the variable number (position in the list) - - -function get_varid_from_kind(dart_kind) - -integer, intent(in) :: dart_kind -integer :: get_varid_from_kind - -integer :: i - -do i = 1, get_num_variables(domain_id) - if (dart_kind == state_vars%qtys(i)) then - get_varid_from_kind = i - return - endif -end do - -write(string1, *) 'Kind ', dart_kind, ' not found in state vector' -write(string2, *) 'AKA ', get_name_for_quantity(dart_kind), ' not found in state vector' -call error_handler(E_MSG,'get_varid_from_kind', string1, & - source, revision, revdate, text2=string2) - -get_varid_from_kind = -1 - -end function get_varid_from_kind - - -!------------------------------------------------------------------ -!> Given an integer index into the state vector structure, returns the kind, -!> and both the starting offset for this kind, as well as the offset into -!> the block of this kind. - - -subroutine get_state_kind(var_ind, var_type) - -integer, intent(in) :: var_ind -integer, intent(out) :: var_type - -if ( .not. module_initialized ) call static_init_model - -var_type = state_vars%qtys(var_ind) - -end subroutine get_state_kind - - !------------------------------------------------------------------ !> Given an integer index into the state vector structure, returns the !> type, taking into account the ocean bottom and dry land. @@ -1836,7 +1782,7 @@ subroutine get_state_kind_inc_dry(index_in, var_type) if ( .not. module_initialized ) call static_init_model call get_model_variable_indices(index_in, lon_index, lat_index, depth_index, var_id=var_id) -call get_state_kind(var_id, var_type) +var_type = get_kind_index(domain_id, var_id) ! if on land or below ocean floor, replace type with dry land. if(is_dry_land(var_type, lon_index, lat_index, depth_index)) then @@ -2352,8 +2298,8 @@ subroutine compute_temperature(state_handle, ens_size, llon, llat, lheight, expe return endif -offset_salt = get_index_start(domain_id, get_varid_from_kind(QTY_SALINITY)) -offset_temp = get_index_start(domain_id, get_varid_from_kind(QTY_POTENTIAL_TEMPERATURE)) +offset_salt = get_index_start(domain_id, get_varid_from_kind(domain_id, QTY_SALINITY)) +offset_temp = get_index_start(domain_id, get_varid_from_kind(domain_id, QTY_POTENTIAL_TEMPERATURE)) ! salinity - in msu (kg/kg). converter will want psu (g/kg). call do_interp(state_handle, ens_size, offset_salt, hgt_bot, hgt_top, hgt_fract, llon, llat, & @@ -2747,7 +2693,7 @@ subroutine use_default_state_variables( state_variables ) character(len=*), intent(inout) :: state_variables(:) ! strings must all be the same length for the gnu compiler -state_variables( 1:5*num_state_table_columns ) = & +state_variables( 1:5*3 ) = & (/ 'SALT_CUR ', 'QTY_SALINITY ', 'UPDATE ', & 'TEMP_CUR ', 'QTY_POTENTIAL_TEMPERATURE ', 'UPDATE ', & 'UVEL_CUR ', 'QTY_U_CURRENT_COMPONENT ', 'UPDATE ', & diff --git a/models/aether_lat-lon/model_mod.f90 b/models/aether_lat-lon/model_mod.f90 index 3b47750b47..39b76ed67e 100644 --- a/models/aether_lat-lon/model_mod.f90 +++ b/models/aether_lat-lon/model_mod.f90 @@ -62,7 +62,7 @@ module model_mod init_time => fail_init_time, & init_conditions => fail_init_conditions, & convert_vertical_obs, convert_vertical_state, adv_1step, & - get_state_variables, state_var_type + parse_variables_clamp, MAX_STATE_VARIABLE_FIELDS_CLAMP implicit none private @@ -89,7 +89,6 @@ module model_mod character(len=256), parameter :: source = 'aether_lat-lon/model_mod.f90' logical :: module_initialized = .false. -logical, parameter :: use_clamping = .true. integer :: dom_id ! used to access the state structure type(time_type) :: assimilation_time_step @@ -99,9 +98,7 @@ module model_mod integer :: time_step_days = 0 integer :: time_step_seconds = 3600 -integer, parameter :: MAX_STATE_VARIABLES = 100 -integer, parameter :: NUM_STATE_TABLE_COLUMNS = 5 -character(len=vtablenamelength) :: variables(NUM_STATE_TABLE_COLUMNS*MAX_STATE_VARIABLES) = '' +character(len=vtablenamelength) :: variables(MAX_STATE_VARIABLE_FIELDS_CLAMP) = '' namelist /model_nml/ template_file, time_step_days, time_step_seconds, variables @@ -151,7 +148,6 @@ module model_mod subroutine static_init_model() integer :: iunit, io -type(state_var_type) :: state_vars module_initialized = .true. @@ -173,8 +169,6 @@ subroutine static_init_model() lat_start = lats(1) lat_delta = lats(2) - lats(1) -call get_state_variables(variables, MAX_STATE_VARIABLES, use_clamping, state_vars) - ! This time is both the minimum time you can ask the model to advance ! (for models that can be advanced by filter) and it sets the assimilation ! window. All observations within +/- 1/2 this interval from the current @@ -182,10 +176,11 @@ subroutine static_init_model() ! feel free to hardcode it and remove from the namelist. assimilation_time_step = set_time(time_step_seconds, time_step_days) -! Define which variables are in the model state -! This is using add_domain_from_file (arg list matches) -dom_id = add_domain(template_file, state_vars%nvars, state_vars%netcdf_var_names, state_vars%qtys, & - state_vars%clamp_values, state_vars%updates) +! Define which variables are in the model state; +! parse_variables converts the character table that was read in from +! model_nml:model_state_variables and returns a state_var_type that +! can be passed to add_domain +dom_id = add_domain(template_file, parse_variables_clamp(variables)) call state_structure_info(dom_id) diff --git a/models/cam-common-code/cam_common_code_mod.f90 b/models/cam-common-code/cam_common_code_mod.f90 index e0e1faa20b..dc635c268b 100644 --- a/models/cam-common-code/cam_common_code_mod.f90 +++ b/models/cam-common-code/cam_common_code_mod.f90 @@ -63,8 +63,8 @@ module cam_common_code_mod set_vert_localization, vert_interp, vertical_localization_type, write_model_time public :: nc_write_model_atts, grid_data, read_grid_info, & - MAX_STATE_VARIABLES, num_state_table_columns, common_initialized, & - MAX_PERT, shortest_time_between_assimilations, domain_id, & + common_initialized, MAX_PERT, & + shortest_time_between_assimilations, domain_id, & ccustom_routine_to_generate_ensemble, & cfields_to_perturb, & cperturbation_amplitude, & @@ -107,8 +107,6 @@ module cam_common_code_mod ! info and is required for getting state variables. integer :: domain_id = -1 -integer, parameter :: MAX_STATE_VARIABLES = 100 -integer, parameter :: num_state_table_columns = 5 ! maximum number of fields you can list to be perturbed ! to generate an ensemble if starting from a single state. integer, parameter :: MAX_PERT = 100 diff --git a/models/cam-fv/model_mod.f90 b/models/cam-fv/model_mod.f90 index 19629488e5..a9c7abb3fe 100644 --- a/models/cam-fv/model_mod.f90 +++ b/models/cam-fv/model_mod.f90 @@ -82,7 +82,8 @@ module model_mod use default_model_mod, only : adv_1step, nc_write_model_vars, & init_time => fail_init_time, & init_conditions => fail_init_conditions, & - get_state_variables, state_var_type + parse_variables_clamp, & + MAX_STATE_VARIABLE_FIELDS_CLAMP use cam_common_code_mod, only : above_ramp_start, are_damping, build_cam_pressure_columns, build_heights, & cam_grid, cdebug_level, check_good_levels, cno_normalization_of_scale_heights, & @@ -95,8 +96,7 @@ module model_mod set_vert_localization, vert_interp, vertical_localization_type, write_model_time use cam_common_code_mod, only : nc_write_model_atts, grid_data, read_grid_info, & - MAX_STATE_VARIABLES, & - num_state_table_columns, MAX_PERT, & + MAX_PERT, & shortest_time_between_assimilations, domain_id, & cuse_log_vertical_scale, & cno_normalization_of_scale_heights, & @@ -181,8 +181,7 @@ module model_mod ! for no clamping, use the string 'NA' ! to have the assimilation change the variable use 'UPDATE', else 'NO_UPDATE' -character(len=vtablenamelength) :: state_variables(MAX_STATE_VARIABLES * & - num_state_table_columns ) = ' ' +character(len=vtablenamelength) :: state_variables(MAX_STATE_VARIABLE_FIELDS_CLAMP) = ' ' namelist /model_nml/ & cam_template_filename, & @@ -204,7 +203,6 @@ module model_mod debug_level ! global variables -logical, parameter :: use_clamping = .true. character(len=512) :: string1, string2, string3 logical, save :: module_initialized = .false. @@ -259,8 +257,6 @@ subroutine static_init_model() character(len=*), parameter :: routine = 'static_init_model' -type(state_var_type) :: state_vars - if ( module_initialized ) return ! Record version info @@ -300,12 +296,10 @@ subroutine static_init_model() ! initialize global values that are used frequently call init_globals() -! read the namelist &model_nml :: state_variables -! to set up what will be read into the cam state vector -call get_state_variables(state_variables, MAX_STATE_VARIABLES, use_clamping, state_vars) - -domain_id = add_domain(cam_template_filename, state_vars%nvars, state_vars%netcdf_var_names, state_vars%qtys, & - state_vars%clamp_values, state_vars%updates) +! parse_variables converts the character table that was read in from +! model_nml:model_state_variables into a state_var_type that can be +! passed to add_domain +domain_id = add_domain(cam_template_filename, parse_variables_clamp(state_variables)) call fill_cam_stagger_info(grid_stagger) diff --git a/models/cam-se/model_mod.f90 b/models/cam-se/model_mod.f90 index 1fca1d31d4..06d7c03b86 100644 --- a/models/cam-se/model_mod.f90 +++ b/models/cam-se/model_mod.f90 @@ -83,7 +83,8 @@ module model_mod use default_model_mod, only : adv_1step, nc_write_model_vars, & init_time => fail_init_time, & init_conditions => fail_init_conditions, & - get_state_variables, state_var_type + parse_variables_clamp, & + MAX_STATE_VARIABLE_FIELDS_CLAMP use cam_common_code_mod, only : above_ramp_start, are_damping, build_cam_pressure_columns, build_heights, & cam_grid, cdebug_level, check_good_levels, cno_normalization_of_scale_heights, & @@ -97,8 +98,7 @@ module model_mod use cam_common_code_mod, only : nc_write_model_atts, grid_data, read_grid_info, & - MAX_STATE_VARIABLES, & - num_state_table_columns, MAX_PERT, & + MAX_PERT, & shortest_time_between_assimilations, domain_id, & cuse_log_vertical_scale, & cno_normalization_of_scale_heights, & @@ -187,8 +187,7 @@ module model_mod ! for no clamping, use the string 'NA' ! to have the assimilation change the variable use 'UPDATE', else 'NO_UPDATE' -character(len=vtablenamelength) :: state_variables(MAX_STATE_VARIABLES * & - num_state_table_columns ) = ' ' +character(len=vtablenamelength) :: state_variables(MAX_STATE_VARIABLE_FIELDS_CLAMP) = ' ' namelist /model_nml/ & dry_mass_vertical_coordinate, & @@ -217,8 +216,6 @@ module model_mod character(len=512) :: string1, string2, string3 logical, save :: module_initialized = .false. -logical, parameter :: use_clamping = .true. - ! Surface potential; used for calculation of geometric heights. ! SENote: right now every process has their own complete copy of this real(r8), allocatable :: phis(:) @@ -302,8 +299,6 @@ module model_mod subroutine static_init_model() -type(state_var_type) :: state_vars - integer :: iunit, io, i integer :: nc_file_ID integer :: ncol_temp(1) @@ -346,12 +341,10 @@ subroutine static_init_model() ! initialize global values that are used frequently call init_globals() -! read the namelist &model_nml :: state_variables -! to set up what will be read into the cam state vector -call get_state_variables(state_variables, MAX_STATE_VARIABLES, use_clamping, state_vars) - -domain_id = add_domain(cam_template_filename, state_vars%nvars, state_vars%netcdf_var_names, state_vars%qtys, & - state_vars%clamp_values, state_vars%updates) +! parse_variables converts the character table that was read in from +! model_nml:model_state_variables and returns a state_var_type +! that can be passed to add_domain +domain_id = add_domain(cam_template_filename, parse_variables_clamp(state_variables)) ! The size of the only surface pressure dimension is the number of columns diff --git a/models/cice/model_mod.f90 b/models/cice/model_mod.f90 index 1a41047031..ca88c110f3 100644 --- a/models/cice/model_mod.f90 +++ b/models/cice/model_mod.f90 @@ -35,7 +35,8 @@ module model_mod nc_write_location use default_model_mod, only : init_time, init_conditions, adv_1step, & - nc_write_model_vars, get_state_variables, state_var_type + nc_write_model_vars, parse_variables, & + MAX_STATE_VARIABLE_FIELDS use utilities_mod, only : register_module, error_handler, & E_ERR, E_MSG, nmlfileunit, get_unit, & @@ -113,7 +114,8 @@ module model_mod use state_structure_mod, only : add_domain, get_model_variable_indices, & get_num_variables, get_index_start, & - get_num_dims, get_domain_size, state_structure_info + get_num_dims, get_domain_size, state_structure_info, & + get_varid_from_kind, get_kind_index use typesizes use netcdf @@ -165,17 +167,12 @@ module model_mod ! Storage for a random sequence for perturbing a single initial state type(random_seq_type) :: random_seq -! DART state vector contents are specified in the input.nml:&model_nml namelist. -integer, parameter :: max_state_variables = 10 -integer, parameter :: num_state_table_columns = 3 -type(state_var_type) :: state_vars - ! things which can/should be in the model_nml integer :: assimilation_period_days = 1 integer :: assimilation_period_seconds = 0 real(r8) :: model_perturbation_amplitude = 0.2 logical :: update_dry_cell_walls = .false. -character(len=metadatalength) :: model_state_variables(max_state_variables * num_state_table_columns ) = ' ' +character(len=metadatalength) :: model_state_variables(MAX_STATE_VARIABLE_FIELDS) = ' ' integer :: debug = 0 ! turn up for more and more debug messages ! valid values: native, big_endian, little_endian @@ -398,10 +395,6 @@ subroutine static_init_model() if (debug > 2) call write_grid_netcdf() ! DEBUG only if (debug > 2) call write_grid_interptest() ! DEBUG only -! verify that the model_state_variables namelist was filled in correctly. -! returns state_vars which has netcdf variable names, kinds and update strings. -call get_state_variables(model_state_variables, MAX_STATE_VARIABLES, state_vars) - ! in spite of the staggering, all grids are the same size ! and offset by half a grid cell. 4 are 3D and 2 are 2D. ! e.g. aicen,vicen,vsnon = 256 x 225 x 5 @@ -418,10 +411,10 @@ subroutine static_init_model() ! Determine the shape of the variables from "cice.r.nc" ! The assimilate.csh, perfect_model.csh must ensure the cice restart file ! is linked to this filename. -domain_id = add_domain('cice.r.nc', state_vars%nvars, & - var_names = state_vars%netcdf_var_names, & - kind_list = state_vars%qtys, & - update_list = state_vars%updates) +! parse_variables converts the character table that was read in from +! model_nml:model_state_variables and returns a state_var_type that can be +! passed to add_domain +domain_id = add_domain('cice.r.nc', parse_variables(model_state_variables)) if (debug > 2) call state_structure_info(domain_id) @@ -872,25 +865,25 @@ subroutine model_interpolate(state_handle, ens_size, location, obs_type, expecte SELECT CASE (obs_type) CASE (QTY_SEAICE_AGREG_THICKNESS ) ! these kinds require aggregating 3D vars to make a 2D var cat_signal = -1 ! for extra special procedure to aggregate - base_offset = get_index_start(domain_id, get_varid_from_kind(QTY_SEAICE_VOLUME)) + base_offset = get_index_start(domain_id, get_varid_from_kind(domain_id, QTY_SEAICE_VOLUME)) CASE (QTY_SEAICE_AGREG_SNOWDEPTH ) ! these kinds require aggregating 3D vars to make a 2D var cat_signal = -1 ! for extra special procedure to aggregate - base_offset = get_index_start(domain_id, get_varid_from_kind(QTY_SEAICE_SNOWVOLUME)) + base_offset = get_index_start(domain_id, get_varid_from_kind(domain_id, QTY_SEAICE_SNOWVOLUME)) CASE (QTY_SEAICE_AGREG_CONCENTR ) ! these kinds require aggregating a 3D var to make a 2D var cat_signal = 0 ! for aggregate variable, send signal to lon_lat_interp - base_offset = get_index_start(domain_id, get_varid_from_kind(QTY_SEAICE_CONCENTR)) + base_offset = get_index_start(domain_id, get_varid_from_kind(domain_id, QTY_SEAICE_CONCENTR)) CASE (QTY_SEAICE_AGREG_VOLUME ) ! these kinds require aggregating a 3D var to make a 2D var cat_signal = 0 ! for aggregate variable, send signal to lon_lat_interp - base_offset = get_index_start(domain_id, get_varid_from_kind(QTY_SEAICE_VOLUME)) + base_offset = get_index_start(domain_id, get_varid_from_kind(domain_id, QTY_SEAICE_VOLUME)) CASE (QTY_SEAICE_AGREG_SNOWVOLUME ) ! these kinds require aggregating a 3D var to make a 2D var cat_signal = 0 ! for aggregate variable, send signal to lon_lat_interp - base_offset = get_index_start(domain_id, get_varid_from_kind(QTY_SEAICE_SNOWVOLUME)) + base_offset = get_index_start(domain_id, get_varid_from_kind(domain_id, QTY_SEAICE_SNOWVOLUME)) CASE (QTY_SEAICE_AGREG_SURFACETEMP) ! FEI need aicen to average the temp, have not considered open water temp yet cat_signal = -3 - base_offset = get_index_start(domain_id, get_varid_from_kind(QTY_SEAICE_SURFACETEMP)) + base_offset = get_index_start(domain_id, get_varid_from_kind(domain_id, QTY_SEAICE_SURFACETEMP)) CASE (QTY_SOM_TEMPERATURE) ! these kinds are 1d variables cat_signal = 3 - base_offset = get_index_start(domain_id,get_varid_from_kind(QTY_SOM_TEMPERATURE)) + base_offset = get_index_start(domain_id,get_varid_from_kind(domain_id, QTY_SOM_TEMPERATURE)) CASE (QTY_SEAICE_CONCENTR , & ! these kinds have an additional dim for category QTY_SEAICE_FY , & QTY_SEAICE_VOLUME , & @@ -925,7 +918,7 @@ subroutine model_interpolate(state_handle, ens_size, location, obs_type, expecte QTY_SEAICE_SNOWENTHALPY003 ) ! move pointer to the particular category ! then treat as 2d field in lon_lat_interp - base_offset = get_index_start(domain_id, get_varid_from_kind(obs_type)) + base_offset = get_index_start(domain_id, get_varid_from_kind(domain_id, obs_type)) base_offset = base_offset + (cat_index-1) * Nx * Ny cat_signal = 1 ! now same as boring 2d field CASE ( QTY_U_SEAICE_COMPONENT , & ! these kinds are just 2D vars @@ -934,7 +927,7 @@ subroutine model_interpolate(state_handle, ens_size, location, obs_type, expecte QTY_SEAICE_ALBEDODIRNIR , & QTY_SEAICE_ALBEDOINDVIZ , & QTY_SEAICE_ALBEDOINDNIR ) - base_offset = get_index_start(domain_id, get_varid_from_kind(obs_type)) + base_offset = get_index_start(domain_id, get_varid_from_kind(domain_id, obs_type)) cat_signal = 2 ! also boring 2d field (treat same as cat_signal 1) CASE DEFAULT ! Not a legal type for interpolation, return istatus error @@ -952,12 +945,12 @@ subroutine model_interpolate(state_handle, ens_size, location, obs_type, expecte do icat = 1,Ncat !reads in aicen cat_signal_interm = 1 - base_offset = get_index_start(domain_id,get_varid_from_kind(QTY_SEAICE_CONCENTR)) + base_offset = get_index_start(domain_id,get_varid_from_kind(domain_id, QTY_SEAICE_CONCENTR)) base_offset = base_offset + (icat-1) * Nx * Ny call lon_lat_interpolate(state_handle, ens_size, base_offset, llon, llat, obs_type, cat_signal_interm, expected_conc, istatus) !reads in fyn cat_signal_interm = 1 - base_offset = get_index_start(domain_id,get_varid_from_kind(QTY_SEAICE_FY)) + base_offset = get_index_start(domain_id,get_varid_from_kind(domain_id, QTY_SEAICE_FY)) base_offset = base_offset + (icat-1) * Nx * Ny call lon_lat_interpolate(state_handle, ens_size, base_offset, llon, llat, obs_type, cat_signal_interm, expected_fy, istatus) temp = temp + expected_conc * expected_fy !sum(aicen*fyn) = FY % over ice @@ -980,12 +973,12 @@ subroutine model_interpolate(state_handle, ens_size, location, obs_type, expecte do icat = 1,Ncat !reads in aicen cat_signal_interm = 1 - base_offset = get_index_start(domain_id,get_varid_from_kind(QTY_SEAICE_CONCENTR)) + base_offset = get_index_start(domain_id,get_varid_from_kind(domain_id, QTY_SEAICE_CONCENTR)) base_offset = base_offset + (icat-1) * Nx * Ny call lon_lat_interpolate(state_handle, ens_size, base_offset, llon, llat, obs_type, cat_signal_interm, expected_conc, istatus) !reads in Tsfcn cat_signal_interm = 1 - base_offset = get_index_start(domain_id,get_varid_from_kind(QTY_SEAICE_SURFACETEMP)) + base_offset = get_index_start(domain_id,get_varid_from_kind(domain_id, QTY_SEAICE_SURFACETEMP)) base_offset = base_offset + (icat-1) * Nx * Ny call lon_lat_interpolate(state_handle, ens_size, base_offset, llon, llat, obs_type, cat_signal_interm, expected_tsfc, istatus) if (any(expected_conc<0.0) .or. any(expected_conc>1.0))then @@ -1023,7 +1016,7 @@ subroutine model_interpolate(state_handle, ens_size, location, obs_type, expecte if (cat_signal == -1) then ! we need to know the aggregate sea ice concentration for these special cases - base_offset = get_index_start(domain_id, get_varid_from_kind(QTY_SEAICE_CONCENTR)) + base_offset = get_index_start(domain_id, get_varid_from_kind(domain_id, QTY_SEAICE_CONCENTR)) call lon_lat_interpolate(state_handle, ens_size, base_offset, llon, llat, obs_type, cat_signal, expected_aggr_conc, istatus) expected_obs = expected_obs/max(expected_aggr_conc,1.0e-8) ! hope this is allowed so we never divide by zero @@ -1819,7 +1812,7 @@ subroutine get_state_meta_data(index_in, location, var_type) if ( .not. module_initialized ) call static_init_model call get_model_variable_indices(index_in, lon_index, lat_index, cat_index, var_id=var_id) -call get_state_kind(var_id, local_var) +local_var = get_kind_index(domain_id, var_id) if (is_on_ugrid(local_var)) then lon = ULON(lon_index, lat_index) @@ -1844,52 +1837,6 @@ end subroutine get_state_meta_data !-------------------------------------------------------------------- -function get_varid_from_kind(dart_kind) - -integer, intent(in) :: dart_kind -integer :: get_varid_from_kind - -! given a kind, return what variable number it is - -integer :: i - -do i = 1, get_num_variables(domain_id) - if (dart_kind == state_vars%qtys(i)) then - get_varid_from_kind = i - return - endif -end do - -if (debug > 1) then - write(string1, *) 'Kind ', dart_kind, ' not found in state vector' - write(string2, *) 'AKA ', get_name_for_quantity(dart_kind), ' not found in state vector' - call error_handler(E_MSG,'get_varid_from_kind', string1, & - source, revision, revdate, text2=string2) -endif - -get_varid_from_kind = -1 - -end function get_varid_from_kind - - -!------------------------------------------------------------------ - -subroutine get_state_kind(var_ind, var_type) - integer, intent(in) :: var_ind - integer, intent(out) :: var_type - -! Given an integer index into the state vector structure, returns the kind, -! and both the starting offset for this kind, as well as the offset into -! the block of this kind. - -if ( .not. module_initialized ) call static_init_model - -var_type = state_vars%qtys(var_ind) - -end subroutine get_state_kind - -!------------------------------------------------------------------ - subroutine get_state_kind_inc_dry(index_in, var_type) integer(i8), intent(in) :: index_in integer, intent(out) :: var_type @@ -1902,7 +1849,7 @@ subroutine get_state_kind_inc_dry(index_in, var_type) if ( .not. module_initialized ) call static_init_model call get_model_variable_indices(index_in, lon_index, lat_index, depth_index, var_id=var_id) -call get_state_kind(var_id, var_type) +var_type = get_kind_index(domain_id, var_id) ! if on land, replace type with dry land. if(is_dry_land(var_type, lon_index, lat_index)) then @@ -2583,7 +2530,7 @@ subroutine use_default_state_variables( state_variables ) character(len=*), intent(inout) :: state_variables(:) ! strings must all be the same length for the gnu compiler -state_variables( 1:5*num_state_table_columns ) = & +state_variables( 1:5*3 ) = & (/ 'CONCENTRATION ', 'QTY_SEAICE_CONCENTR ', 'UPDATE ', & 'ICEVOLUME ', 'QTY_SEAICE_VOLUME ', 'UPDATE ', & 'SNOWVOLUME ', 'QTY_SEAICE_SNOWVOLUME ', 'UPDATE ', & diff --git a/models/noah/model_mod.f90 b/models/noah/model_mod.f90 index ac2a5bbd8a..913f09c2ca 100644 --- a/models/noah/model_mod.f90 +++ b/models/noah/model_mod.f90 @@ -47,7 +47,8 @@ module model_mod use distributed_state_mod, only : get_state use default_model_mod, only : adv_1step, nc_write_model_vars, & - get_state_variables, state_var_type + parse_variables_clamp, & + MAX_STATE_VARIABLE_FIELDS_CLAMP use noah_hydro_mod, only : configure_lsm, get_noah_timestepping, & num_soil_layers, lsm_namelist_filename, & @@ -124,15 +125,6 @@ module model_mod integer, parameter :: NSOLDX = 100 character(len=512) :: string1, string2, string3 -! Codes for interpreting the columns of the variable_table -integer, parameter :: VT_VARNAMEINDX = 1 ! ... variable name -integer, parameter :: VT_KINDINDX = 2 ! ... DART kind -integer, parameter :: VT_MINVALINDX = 3 ! ... minimum value if any -integer, parameter :: VT_MAXVALINDX = 4 ! ... maximum value if any -integer, parameter :: VT_STATEINDX = 5 ! ... update (state) or not -integer, parameter :: MAX_STATE_VARIABLES = 40 -integer, parameter :: NUM_STATE_TABLE_COLUMNS = 5 - integer :: domain_count integer :: idom, idom_lsm = -1 @@ -162,15 +154,13 @@ module model_mod real(r8) :: model_perturbation_amplitude = 0.002 character(len=256) :: perturb_distribution = 'lognormal' integer :: debug = 0 ! turn up for more and more debug messages -character(len=obstypelength) :: lsm_variables(NUM_STATE_TABLE_COLUMNS*MAX_STATE_VARIABLES) = ' ' +character(len=obstypelength) :: lsm_variables(MAX_STATE_VARIABLE_FIELDS_CLAMP) = ' ' !nc -- we are adding these to the model.nml until they appear in the NetCDF files logical :: polar = .false. ! wrap over the poles logical :: periodic_x = .false. ! wrap in longitude or x logical :: periodic_y = .false. ! used for single column model, wrap in y -logical, parameter :: use_clamping = .true. - namelist /model_nml/ domain_shapefiles, & lsm_model_choice, & assimilation_period_days, & @@ -229,8 +219,6 @@ subroutine static_init_model() integer :: n_lsm_fields integer :: i -type(state_var_type) :: state_vars - character(len=256) :: filename if ( module_initialized ) return ! only need to do this once. @@ -274,11 +262,7 @@ subroutine static_init_model() else call configure_lsm(lsm_model_choice,domain_shapefiles(domainID)) call read_noah_global_atts(domain_shapefiles(domainID)) - call get_state_variables(lsm_variables, MAX_STATE_VARIABLES, use_clamping, state_vars) - idom_lsm = add_domain(domain_shapefiles(domainID), state_vars%nvars, state_vars%netcdf_var_names, & - kind_list=state_vars%qtys, & - clamp_vals=state_vars%clamp_values, & - update_list=state_vars%updates) + idom_lsm = add_domain(domain_shapefiles(domainID), parse_variables_clamp(lsm_variables)) if (debug > 99) call state_structure_info(idom_lsm) call check_vertical_dimension(idom_lsm) diff --git a/models/utilities/default_model_mod.f90 b/models/utilities/default_model_mod.f90 index ed8f911901..a5dd6378fc 100644 --- a/models/utilities/default_model_mod.f90 +++ b/models/utilities/default_model_mod.f90 @@ -52,22 +52,23 @@ module default_model_mod convert_vertical_state, & read_model_time, & ! from the dart_time_io module write_model_time, & - get_state_variables, & - state_var_type - -interface get_state_variables - module procedure get_state_variables_clamp - module procedure get_state_variables_noclamp -end interface + parse_variables, & + parse_variables_clamp, & + state_var_type, & + MAX_STATE_VARIABLE_FIELDS, & + MAX_STATE_VARIABLE_FIELDS_CLAMP type :: state_var_type integer :: nvars character(len=64), allocatable :: netcdf_var_names(:) - integer, allocatable :: qtys(:) - real(r8), allocatable :: clamp_values(:, :) - logical, allocatable :: updates(:) + integer, allocatable :: qtys(:) + real(r8), allocatable :: clamp_values(:, :) + logical, allocatable :: updates(:) end type state_var_type +integer, parameter :: MAX_STATE_VARIABLES = 100 +integer, parameter :: MAX_STATE_VARIABLE_FIELDS = 300 +integer, parameter :: MAX_STATE_VARIABLE_FIELDS_CLAMP = 500 character(len=*), parameter :: source = 'utilities/default_model_mod.f90' contains @@ -99,6 +100,7 @@ subroutine init_conditions(x) end subroutine init_conditions +!------------------------------------------------------------------ !------------------------------------------------------------------ subroutine fail_init_conditions(x) @@ -302,33 +304,28 @@ end subroutine pert_model_copies !-------------------------------------------------------------------- -!> Reads in model_nml:model_state_variables and returns a +!> Parses the character table that was read in from +!> model_nml:model_state_variables and returns a !> state_var_type state_vars with nvars ; netcdf variable names ; !> qtys (kinds) ; clamp values ; updates +!> that there are valid entries for the dart_kind. ! -!> Verifies that the namelist was filled in correctly, and checks +!> Verifies that the namelist entry was filled in correctly, and checks !> that there are valid entries for the dart_kind. -subroutine get_state_variables_clamp(nml_state_vars, MAX_STATE_VARIABLES, use_clamping, state_vars) +function parse_variables_clamp(vars_table) result(state_vars) -character(len=*), intent(inout) :: nml_state_vars(:) -integer, intent(in) :: MAX_STATE_VARIABLES -logical, intent(in) :: use_clamping -type(state_var_type), intent(out) :: state_vars +character(len=*), intent(in) :: vars_table(MAX_STATE_VARIABLE_FIELDS_CLAMP) +type(state_var_type) :: state_vars character(len=NF90_MAX_NAME) :: netcdf_var_name, dart_qty_str, update character(len=256) :: string1, string2 integer :: i, ivar -if(.not. use_clamping) then - string1 = 'logical use_clamping set to false when values for clamping are required in this model nml' - call error_handler(E_ERR, 'get_state_variables_clamp', string1) -endif - ! Loop through the variables array to get the actual count of the number of variables do ivar = 1, MAX_STATE_VARIABLES ! If the first element in the row is an empty string, the loop has exceeded the extent of the variables - if (nml_state_vars(5*ivar-4) == '') then + if (vars_table(5*ivar-4) == '') then state_vars%nvars = ivar-1 exit endif @@ -339,10 +336,10 @@ subroutine get_state_variables_clamp(nml_state_vars, MAX_STATE_VARIABLES, use_cl RowsLoop : do i = 1, state_vars%nvars - netcdf_var_name = trim(nml_state_vars(5*i-4)) + netcdf_var_name = trim(vars_table(5*i-4)) state_vars%netcdf_var_names(i) = trim(netcdf_var_name) - dart_qty_str = trim(nml_state_vars(5*i-3)) + dart_qty_str = trim(vars_table(5*i-3)) call to_upper(dart_qty_str) ! Make sure DART qty is valid state_vars%qtys(i) = get_index_for_quantity(dart_qty_str) @@ -351,19 +348,19 @@ subroutine get_state_variables_clamp(nml_state_vars, MAX_STATE_VARIABLES, use_cl call error_handler(E_ERR,'get_state_variables_clamp',string1) endif - if (nml_state_vars(5*i-2) /= 'NA') then - read(nml_state_vars(5*i-2), '(d16.8)') state_vars%clamp_values(i,1) + if (vars_table(5*i-2) /= 'NA') then + read(vars_table(5*i-2), '(d16.8)') state_vars%clamp_values(i,1) else state_vars%clamp_values(i,1) = MISSING_R8 endif - if (nml_state_vars(5*i-1) /= 'NA') then - read(nml_state_vars(5*i-1), '(d16.8)') state_vars%clamp_values(i,2) + if (vars_table(5*i-1) /= 'NA') then + read(vars_table(5*i-1), '(d16.8)') state_vars%clamp_values(i,2) else state_vars%clamp_values(i,2) = MISSING_R8 endif - update = trim(nml_state_vars(5*i)) + update = trim(vars_table(5*i)) call to_upper(update) select case (update) case ('UPDATE') @@ -377,29 +374,29 @@ subroutine get_state_variables_clamp(nml_state_vars, MAX_STATE_VARIABLES, use_cl end select ! Checking that the rows in the nml entry are all complete - if ( dart_qty_str == '' .or. nml_state_vars(5*i-2) == '' .or. nml_state_vars(5*i-1) == '' .or. update == '' ) then + if ( dart_qty_str == '' .or. vars_table(5*i-2) == '' .or. vars_table(5*i-1) == '' .or. update == '' ) then string1 = 'model_nml:model_state_variables not fully specified' call error_handler(E_ERR, 'get_state_variables_clamp', string1) endif enddo RowsLoop -end subroutine get_state_variables_clamp +end function parse_variables_clamp !-------------------------------------------------------------------- -!> Reads in model_nml:model_state_variables and returns a +!> Parses the character table that was read in from +!> model_nml:model_state_variables and returns a !> state_var_type state_vars with nvars ; netcdf variable names ; !> qtys (kinds) ; updates ! -!> Verifies that the namelist was filled in correctly, and checks +!> Verifies that the namelist entry was filled in correctly, and checks !> that there are valid entries for the dart_kind. -subroutine get_state_variables_noclamp(nml_state_vars, MAX_STATE_VARIABLES, state_vars) +function parse_variables(vars_table) result(state_vars) -character(len=*), intent(inout) :: nml_state_vars(:) -integer, intent(in) :: MAX_STATE_VARIABLES -type(state_var_type), intent(out) :: state_vars +character(len=*), intent(in) :: vars_table(MAX_STATE_VARIABLE_FIELDS) +type(state_var_type) :: state_vars character(len=NF90_MAX_NAME) :: netcdf_var_name, dart_qty_str, update character(len=256) :: string1, string2 @@ -408,7 +405,7 @@ subroutine get_state_variables_noclamp(nml_state_vars, MAX_STATE_VARIABLES, stat ! Loop through the variables array to get the actual count of the number of variables do ivar = 1, MAX_STATE_VARIABLES ! If the first element in the row is an empty string, the loop has exceeded the extent of the variables - if (nml_state_vars(3*ivar-2) == '') then + if (vars_table(3*ivar-2) == '') then state_vars%nvars = ivar-1 exit endif @@ -417,24 +414,30 @@ subroutine get_state_variables_noclamp(nml_state_vars, MAX_STATE_VARIABLES, stat ! Allocate the arrays in the var derived type allocate(state_vars%netcdf_var_names(state_vars%nvars), state_vars%qtys(state_vars%nvars), state_vars%updates(state_vars%nvars)) -RowsLoopNoClamp : do i = 1, state_vars%nvars - - state_vars%clamp_values(:,:) = MISSING_R8 +RowsLoop : do i = 1, state_vars%nvars - netcdf_var_name = trim(nml_state_vars(3*i-2)) + netcdf_var_name = trim(vars_table(3*i-2)) state_vars%netcdf_var_names(i) = trim(netcdf_var_name) - dart_qty_str = trim(nml_state_vars(3*i-1)) + update = trim(vars_table(3*i)) + call to_upper(update) + + dart_qty_str = trim(vars_table(3*i-1)) call to_upper(dart_qty_str) + + ! Checking that the rows in the nml entry are all complete + if ( dart_qty_str == '' .or. update == '' ) then + string1 = 'model_nml:model_state_variables not fully specified' + call error_handler(E_ERR, 'parse_variables', string1) + endif + ! Make sure DART qty is valid state_vars%qtys(i) = get_index_for_quantity(dart_qty_str) if( state_vars%qtys(i) < 0 ) then write(string1,'(3A)') 'The quantity specified in the &model_nml "', trim(dart_qty_str), '", is not present in obs_kind_mod.f90' - call error_handler(E_ERR,'verify_state_variables',string1) + call error_handler(E_ERR,'parse_variables',string1) endif - update = trim(nml_state_vars(3*i)) - call to_upper(update) select case (update) case ('UPDATE') state_vars%updates(i) = .true. @@ -443,18 +446,12 @@ subroutine get_state_variables_noclamp(nml_state_vars, MAX_STATE_VARIABLES, stat case default write(string1,'(A)') 'Invalid update variable in &model_nml:model_state_variable - only UPDATE or NO_COPY_BACK are supported' write(string2,'(6A)') 'Issue: ', trim(netcdf_var_name), ', ', trim(dart_qty_str), ', ', trim(update) - call error_handler(E_ERR,'verify_state_variables',string1, text2=string2) + call error_handler(E_ERR,'parse_variables',string1, text2=string2) end select - ! Checking that the rows in the nml entry are all complete - if ( dart_qty_str == '' .or. update == '' ) then - string1 = 'model_nml:model_state_variables not fully specified' - call error_handler(E_ERR, 'verify_state_variables', string1) - endif - -enddo RowsLoopNoClamp +enddo RowsLoop -end subroutine get_state_variables_noclamp +end function parse_variables !=================================================================== ! End of model_mod diff --git a/models/wrf_hydro/model_mod.f90 b/models/wrf_hydro/model_mod.f90 index 8e64514f34..8dc7b54ba3 100644 --- a/models/wrf_hydro/model_mod.f90 +++ b/models/wrf_hydro/model_mod.f90 @@ -44,8 +44,8 @@ module model_mod use dart_time_io_mod, only : write_model_time -use default_model_mod, only : adv_1step, nc_write_model_vars, get_state_variables, & - state_var_type +use default_model_mod, only : adv_1step, nc_write_model_vars, parse_variables_clamp, & + MAX_STATE_VARIABLE_FIELDS_CLAMP use noah_hydro_mod, only : configure_lsm, configure_hydro, & n_link, linkLong, linkLat, linkAlt, get_link_tree, & @@ -111,17 +111,11 @@ module model_mod logical, save :: module_initialized = .false. -logical, parameter :: use_clamping = .true. - character(len=512) :: string1, string2, string3 integer(i8) :: model_size type(time_type) :: time_step -! For interpreting the columns of the variable_table -integer, parameter :: MAX_STATE_VARIABLES = 40 -integer, parameter :: NUM_STATE_TABLE_COLUMNS = 5 - integer :: domain_count integer :: idom, idom_hydro = -1, idom_parameters = -1, idom_lsm = -1 @@ -138,9 +132,9 @@ module model_mod character(len=256) :: perturb_distribution = 'lognormal' character(len=obstypelength) :: & - lsm_variables( NUM_STATE_TABLE_COLUMNS*MAX_STATE_VARIABLES) = '', & - hydro_variables(NUM_STATE_TABLE_COLUMNS*MAX_STATE_VARIABLES) = '', & - parameters( NUM_STATE_TABLE_COLUMNS*MAX_STATE_VARIABLES) = '' + lsm_variables(MAX_STATE_VARIABLE_FIELDS_CLAMP) = '', & + hydro_variables(MAX_STATE_VARIABLE_FIELDS_CLAMP) = '', & + parameters(MAX_STATE_VARIABLE_FIELDS_CLAMP) = '' namelist /model_nml/ assimilation_period_days, & assimilation_period_seconds, & @@ -174,8 +168,6 @@ subroutine static_init_model() character(len=*), parameter :: routine = 'static_init_model' -type(state_var_type) :: state_vars_hydro, state_vars_parameters, state_vars_lsm - integer :: iunit, io, domainID integer :: vsize @@ -243,12 +235,8 @@ subroutine static_init_model() call configure_hydro() call read_hydro_global_atts(domain_shapefiles(domainID)) - call get_state_variables(hydro_variables, MAX_STATE_VARIABLES, use_clamping, state_vars_hydro) idom_hydro = add_domain(domain_shapefiles(domainID), & - state_vars_hydro%nvars, state_vars_hydro%netcdf_var_names, & - kind_list=state_vars_hydro%qtys, & - clamp_vals=state_vars_hydro%clamp_values, & - update_list=state_vars_hydro%updates) + parse_variables_clamp(hydro_variables)) if (debug > 99) call state_structure_info(idom_hydro) @@ -265,12 +253,8 @@ subroutine static_init_model() elseif (index(domain_name,'PARAMETER') > 0) then - call get_state_variables(parameters, MAX_STATE_VARIABLES, use_clamping, state_vars_parameters) idom_parameters = add_domain(domain_shapefiles(domainID), & - state_vars_parameters%nvars, state_vars_parameters%netcdf_var_names, & - kind_list=state_vars_parameters%qtys, & - clamp_vals=state_vars_parameters%clamp_values, & - update_list=state_vars_parameters%updates) + parse_variables_clamp(parameters)) if (debug > 99) call state_structure_info(idom_parameters) @@ -280,12 +264,8 @@ subroutine static_init_model() call configure_lsm(lsm_model_choice) call read_noah_global_atts(domain_shapefiles(domainID)) - call get_state_variables(lsm_variables, MAX_STATE_VARIABLES, use_clamping, state_vars_lsm) idom_hydro = add_domain(domain_shapefiles(domainID), & - state_vars_lsm%nvars, state_vars_lsm%netcdf_var_names, & - kind_list=state_vars_lsm%qtys, & - clamp_vals=state_vars_lsm%clamp_values, & - update_list=state_vars_lsm%updates) + parse_variables_clamp(lsm_variables)) if (debug > 99) call state_structure_info(idom_lsm) From 4da59864f0a6331d568890dd6a8a99c5a0579113 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Thu, 9 Jan 2025 13:03:02 -0700 Subject: [PATCH 25/45] Updating dev test; remove prints and test all components of the state_var_type --- .../namelist/test_get_state_variables.f90 | 78 ------------------ .../namelist/test_parse_variables.f90 | 81 +++++++++++++++++++ developer_tests/namelist/work/input.nml | 16 ++-- developer_tests/namelist/work/quickbuild.sh | 2 +- 4 files changed, 88 insertions(+), 89 deletions(-) delete mode 100644 developer_tests/namelist/test_get_state_variables.f90 create mode 100644 developer_tests/namelist/test_parse_variables.f90 diff --git a/developer_tests/namelist/test_get_state_variables.f90 b/developer_tests/namelist/test_get_state_variables.f90 deleted file mode 100644 index 721fb272e1..0000000000 --- a/developer_tests/namelist/test_get_state_variables.f90 +++ /dev/null @@ -1,78 +0,0 @@ -! DART software - Copyright UCAR. This open source software is provided -! by UCAR, "as is", without charge, subject to all terms of use at -! http://www.image.ucar.edu/DAReS/DART/DART_download - -program test_get_state_variables - -use utilities_mod, only : find_namelist_in_file, check_namelist_read -use mpi_utilities_mod, only : initialize_mpi_utilities, finalize_mpi_utilities -use types_mod, only : vtablenamelength, MISSING_R8 -use default_model_mod, only : get_state_variables, state_var_type - -use test ! fortran-testanything - -implicit none - -integer :: iunit, io -logical :: use_clamping -type(state_var_type) :: state_vars, state_vars_clamp - -! DART state vector contents are specified in the input.nml:&model_nml namelist. -integer, parameter :: MAX_STATE_VARIABLES = 20 -integer, parameter :: NUM_STATE_TABLE_COLUMNS = 3 -character(len=vtablenamelength) :: state_variables(MAX_STATE_VARIABLES * NUM_STATE_TABLE_COLUMNS ) = ' ' - -namelist /model_nml_noclamp/ & - state_variables - -namelist /model_nml_clamp/ & - state_variables - -call initialize_mpi_utilities('test_get_state_variables') - -call plan(10) - -! Using namelist WITHOUT clamping values - -call find_namelist_in_file('input.nml', 'model_nml_noclamp', iunit) -read(iunit, nml = model_nml_noclamp, iostat = io) -call check_namelist_read(iunit, io, 'model_nml_noclamp') - -call get_state_variables(state_variables, MAX_STATE_VARIABLES, state_vars) - -write(*,*) 'state_vars%nvars: ', state_vars%nvars -write(*,*) 'state_vars%netcdf_var_names: ', state_vars%netcdf_var_names -write(*,*) 'state_vars%qtys: ', state_vars%qtys -write(*,*) 'state_vars%updates: ', state_vars%updates - -call ok(state_vars%nvars == 5) !OK -call ok(state_vars%netcdf_var_names(2) == '') !NOT OK -call ok(state_vars%qtys(3) == 20) !NOT OK -call ok(state_vars%updates(4) == .true.) !OK - -! Using namelist file WITH clamping values -use_clamping = .true. - -call find_namelist_in_file('input.nml', 'model_nml_clamp', iunit) -read(iunit, nml = model_nml_clamp, iostat = io) -call check_namelist_read(iunit, io, 'model_nml_clamp') - -call get_state_variables(state_variables, MAX_STATE_VARIABLES, use_clamping, state_vars_clamp) - -write(*,*) 'state_vars%nvars: ', state_vars_clamp%nvars -write(*,*) 'state_vars%netcdf_var_names: ', state_vars_clamp%netcdf_var_names -write(*,*) 'state_vars%qtys: ', state_vars_clamp%qtys -write(*,*) 'state_vars%clamp_values(:,1): ', state_vars_clamp%clamp_values(:, 1) -write(*,*) 'state_vars%clamp_values(:,2): ', state_vars_clamp%clamp_values(:, 2) -write(*,*) 'state_vars%updates: ', state_vars_clamp%updates - -call ok(state_vars_clamp%nvars == 7) !NOT OK -call ok(state_vars_clamp%netcdf_var_names(2) == 'TEMP_CUR') !OK -call ok(state_vars_clamp%qtys(3) == 356) !OK -call ok(state_vars_clamp%clamp_values(1,1) == 99) !NOT OK -call ok(state_vars_clamp%clamp_values(5,2) == 0.0) !OK -call ok(state_vars_clamp%updates(4) == .false.) !NOT OK - -call finalize_mpi_utilities() - -end program test_get_state_variables diff --git a/developer_tests/namelist/test_parse_variables.f90 b/developer_tests/namelist/test_parse_variables.f90 new file mode 100644 index 0000000000..aa6c2a15f3 --- /dev/null +++ b/developer_tests/namelist/test_parse_variables.f90 @@ -0,0 +1,81 @@ +! DART software - Copyright UCAR. This open source software is provided +! by UCAR, "as is", without charge, subject to all terms of use at +! http://www.image.ucar.edu/DAReS/DART/DART_download + +program test_parse_variables + +use utilities_mod, only : find_namelist_in_file, check_namelist_read +use mpi_utilities_mod, only : initialize_mpi_utilities, finalize_mpi_utilities +use types_mod, only : vtablenamelength, MISSING_R8 +use default_model_mod, only : parse_variables_clamp, parse_variables, & + state_var_type, & + MAX_STATE_VARIABLE_FIELDS_CLAMP, & + MAX_STATE_VARIABLE_FIELDS + +use test ! fortran-testanything + +implicit none + +integer :: iunit, io +type(state_var_type) :: state_vars, state_vars_clamp + +character(len=vtablenamelength) :: state_variables(MAX_STATE_VARIABLE_FIELDS) = ' ' +character(len=vtablenamelength) :: state_variables_clamp(MAX_STATE_VARIABLE_FIELDS_CLAMP) = ' ' + +namelist /model_nml/ & + state_variables + +namelist /model_nml_clamp/ & + state_variables_clamp + +call initialize_mpi_utilities('test_parse_variables') + +call plan(26) + +! Using namelist entry WITHOUT clamping values + +call find_namelist_in_file('input.nml', 'model_nml', iunit) +read(iunit, nml = model_nml, iostat = io) +call check_namelist_read(iunit, io, 'model_nml') + +state_vars = parse_variables(state_variables) + +call ok(state_vars%nvars == 3) +call ok(state_vars%netcdf_var_names(1) == 'SALT_CUR') +call ok(state_vars%netcdf_var_names(2) == 'TEMP_CUR') +call ok(state_vars%netcdf_var_names(3) == 'UVEL_CUR') +call ok(state_vars%qtys(1) == 263) +call ok(state_vars%qtys(2) == 254) +call ok(state_vars%qtys(3) == 356) +call ok(state_vars%updates(1) == .true.) +call ok(state_vars%updates(2) == .true.) +call ok(state_vars%updates(3) == .true.) + +! Using namelist entry WITH clamping values + +call find_namelist_in_file('input.nml', 'model_nml_clamp', iunit) +read(iunit, nml = model_nml_clamp, iostat = io) +call check_namelist_read(iunit, io, 'model_nml_clamp') + +state_vars_clamp = parse_variables_clamp(state_variables_clamp) + +call ok(state_vars_clamp%nvars == 3) +call ok(state_vars_clamp%netcdf_var_names(1) == 'SALT_CUR') +call ok(state_vars_clamp%netcdf_var_names(2) == 'TEMP_CUR') +call ok(state_vars_clamp%netcdf_var_names(3) == 'UVEL_CUR') +call ok(state_vars_clamp%qtys(1) == 263) +call ok(state_vars_clamp%qtys(2) == 254) +call ok(state_vars_clamp%qtys(3) == 356) +call ok(state_vars_clamp%clamp_values(1,1) == 0.0) +call ok(state_vars_clamp%clamp_values(1,2) == 0.0) +call ok(state_vars_clamp%clamp_values(2,1) == 0.0) +call ok(state_vars_clamp%clamp_values(2,2) == 0.0) +call ok(state_vars_clamp%clamp_values(3,1) == 0.0) +call ok(state_vars_clamp%clamp_values(3,2) == 0.0) +call ok(state_vars_clamp%updates(1) == .true.) +call ok(state_vars_clamp%updates(2) == .true.) +call ok(state_vars_clamp%updates(3) == .true.) + +call finalize_mpi_utilities() + +end program test_parse_variables diff --git a/developer_tests/namelist/work/input.nml b/developer_tests/namelist/work/input.nml index 621b507e24..6cdc2a9abf 100644 --- a/developer_tests/namelist/work/input.nml +++ b/developer_tests/namelist/work/input.nml @@ -1,17 +1,13 @@ &model_nml_clamp - state_variables = 'SALT_CUR ', 'QTY_SALINITY', '0.0', '0.0', 'UPDATE', + state_variables_clamp = 'SALT_CUR ', 'QTY_SALINITY', '0.0', '0.0', 'UPDATE', 'TEMP_CUR ', 'QTY_POTENTIAL_TEMPERATURE', '0.0', '0.0', 'UPDATE', - 'UVEL_CUR ', 'QTY_U_CURRENT_COMPONENT ', '0.0', '0.0', 'UPDATE', - 'VVEL_CUR ', 'QTY_V_CURRENT_COMPONENT ', '0.0', '0.0', 'UPDATE', - 'PSURF_CUR', 'QTY_SEA_SURFACE_PRESSURE ', '0.0', '0.0', 'UPDATE' + 'UVEL_CUR ', 'QTY_U_CURRENT_COMPONENT ', '0', '0.0', 'UPDATE', / -&model_nml_noclamp - state_variables = 'SALT_CUR ', 'QTY_SALINITY', 'UPDATE', - 'TEMP_CUR ', 'QTY_POTENTIAL_TEMPERATURE', 'UPDATE', - 'UVEL_CUR ', 'QTY_U_CURRENT_COMPONENT ', 'UPDATE', - 'VVEL_CUR ', 'QTY_V_CURRENT_COMPONENT ', 'UPDATE', - 'PSURF_CUR', 'QTY_SEA_SURFACE_PRESSURE ', 'UPDATE' +&model_nml + state_variables = 'SALT_CUR ', 'QTY_SALINITY', 'UPDATE', + 'TEMP_CUR ', 'QTY_POTENTIAL_TEMPERATURE', 'UPDATE', + 'UVEL_CUR ', 'QTY_U_CURRENT_COMPONENT ', 'update' / &utilities_nml diff --git a/developer_tests/namelist/work/quickbuild.sh b/developer_tests/namelist/work/quickbuild.sh index 07bef04b65..9a4d4ef828 100755 --- a/developer_tests/namelist/work/quickbuild.sh +++ b/developer_tests/namelist/work/quickbuild.sh @@ -16,7 +16,7 @@ TEST="namelist" LOCATION="threed_sphere" programs=( -test_get_state_variables +test_parse_variables ) From 022e6baad81c37360ea31a9e3e9d572d43f0d995 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Thu, 9 Jan 2025 13:05:40 -0700 Subject: [PATCH 26/45] Use parse_variables in the 3d template model_mod --- models/template/threed_model_mod.f90 | 16 ++++++++++------ models/template/work/threed_input.nml | 3 +++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/models/template/threed_model_mod.f90 b/models/template/threed_model_mod.f90 index 07976db5de..335e1d582a 100644 --- a/models/template/threed_model_mod.f90 +++ b/models/template/threed_model_mod.f90 @@ -9,7 +9,7 @@ module model_mod ! with the DART data assimilation infrastructure. Do not change the arguments ! for the public routines. -use types_mod, only : r8, i8, MISSING_R8 +use types_mod, only : r8, i8, MISSING_R8, vtablenamelength use time_manager_mod, only : time_type, set_time @@ -38,7 +38,8 @@ module model_mod use default_model_mod, only : pert_model_copies, read_model_time, write_model_time, & init_time => fail_init_time, & init_conditions => fail_init_conditions, & - convert_vertical_obs, convert_vertical_state, adv_1step + convert_vertical_obs, convert_vertical_state, adv_1step, & + parse_variables, MAX_STATE_VARIABLE_FIELDS implicit none private @@ -74,8 +75,9 @@ module model_mod character(len=256) :: template_file = 'model_restart.nc' integer :: time_step_days = 0 integer :: time_step_seconds = 3600 +character(len=vtablenamelength) :: state_variables(MAX_STATE_VARIABLE_FIELDS) = ' ' -namelist /model_nml/ template_file, time_step_days, time_step_seconds +namelist /model_nml/ template_file, time_step_days, time_step_seconds, state_variables contains @@ -108,9 +110,11 @@ subroutine static_init_model() assimilation_time_step = set_time(time_step_seconds, & time_step_days) - -! Define which variables are in the model state -dom_id = add_domain(template_file, num_vars=2, var_names=(/'Temp', 'Wind'/)) +! Define which variables are in the model state; +! parse_variables converts the character table that was read in from +! model_nml:model_state_variables to a state_var_type that can be passed +! to add_domain +dom_id = add_domain(template_file, parse_variables(state_variables)) end subroutine static_init_model diff --git a/models/template/work/threed_input.nml b/models/template/work/threed_input.nml index ca07cf2034..8c61116ac5 100644 --- a/models/template/work/threed_input.nml +++ b/models/template/work/threed_input.nml @@ -127,6 +127,9 @@ &model_nml time_step_days = 0, time_step_seconds = 3600 + state_variables = 'SALT_CUR ', 'QTY_SALINITY', 'UPDATE', + 'TEMP_CUR ', 'QTY_POTENTIAL_TEMPERATURE', 'UPDATE', + 'UVEL_CUR ', 'QTY_U_CURRENT_COMPONENT ', 'UPDATE', / &utilities_nml From 23c8d847dfcf10ffa7db1dd32f2d452620fe42f4 Mon Sep 17 00:00:00 2001 From: Marlena Smith <44214771+mjs2369@users.noreply.github.com> Date: Thu, 9 Jan 2025 13:34:03 -0700 Subject: [PATCH 27/45] Fix typo in state_structure_mod.f90 Co-authored-by: Helen Kershaw <20047007+hkershaw-brown@users.noreply.github.com> --- assimilation_code/modules/io/state_structure_mod.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assimilation_code/modules/io/state_structure_mod.f90 b/assimilation_code/modules/io/state_structure_mod.f90 index 132f63239a..a3955e9ecf 100644 --- a/assimilation_code/modules/io/state_structure_mod.f90 +++ b/assimilation_code/modules/io/state_structure_mod.f90 @@ -383,7 +383,7 @@ function add_domain_from_state_type(info_file, vars) result(dom_id) integer :: ivar ! add to domains -call assert_below_max_num_domains('add_domain_from_file') +call assert_below_max_num_domains('add_domain_from_state_type') state%num_domains = state%num_domains + 1 !>@todo dom_id should be a handle. dom_id = state%num_domains From ceeb76d0cb209b41a0a1d9be1dc3a924287cec0d Mon Sep 17 00:00:00 2001 From: Helen Kershaw Date: Mon, 13 Jan 2025 12:45:08 -0500 Subject: [PATCH 28/45] standard: .eqv. for logicals --- developer_tests/namelist/test_parse_variables.f90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/developer_tests/namelist/test_parse_variables.f90 b/developer_tests/namelist/test_parse_variables.f90 index aa6c2a15f3..a3cf9111f9 100644 --- a/developer_tests/namelist/test_parse_variables.f90 +++ b/developer_tests/namelist/test_parse_variables.f90 @@ -47,9 +47,9 @@ program test_parse_variables call ok(state_vars%qtys(1) == 263) call ok(state_vars%qtys(2) == 254) call ok(state_vars%qtys(3) == 356) -call ok(state_vars%updates(1) == .true.) -call ok(state_vars%updates(2) == .true.) -call ok(state_vars%updates(3) == .true.) +call ok(state_vars%updates(1) .eqv. .true.) +call ok(state_vars%updates(2) .eqv. .true.) +call ok(state_vars%updates(3) .eqv. .true.) ! Using namelist entry WITH clamping values @@ -72,9 +72,9 @@ program test_parse_variables call ok(state_vars_clamp%clamp_values(2,2) == 0.0) call ok(state_vars_clamp%clamp_values(3,1) == 0.0) call ok(state_vars_clamp%clamp_values(3,2) == 0.0) -call ok(state_vars_clamp%updates(1) == .true.) -call ok(state_vars_clamp%updates(2) == .true.) -call ok(state_vars_clamp%updates(3) == .true.) +call ok(state_vars_clamp%updates(1) .eqv. .true.) +call ok(state_vars_clamp%updates(2) .eqv. .true.) +call ok(state_vars_clamp%updates(3) .eqv. .true.) call finalize_mpi_utilities() From e5057dadd609099e7405c9093fcb6baa39cc24f0 Mon Sep 17 00:00:00 2001 From: Helen Kershaw Date: Mon, 13 Jan 2025 13:06:16 -0500 Subject: [PATCH 29/45] fix: use QTY_NAME rather than hardcoded number --- developer_tests/namelist/test_parse_variables.f90 | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/developer_tests/namelist/test_parse_variables.f90 b/developer_tests/namelist/test_parse_variables.f90 index a3cf9111f9..11852cfce7 100644 --- a/developer_tests/namelist/test_parse_variables.f90 +++ b/developer_tests/namelist/test_parse_variables.f90 @@ -11,6 +11,8 @@ program test_parse_variables state_var_type, & MAX_STATE_VARIABLE_FIELDS_CLAMP, & MAX_STATE_VARIABLE_FIELDS +use obs_kind_mod, only : QTY_SALINITY, QTY_POTENTIAL_TEMPERATURE, & + QTY_U_CURRENT_COMPONENT use test ! fortran-testanything @@ -44,9 +46,9 @@ program test_parse_variables call ok(state_vars%netcdf_var_names(1) == 'SALT_CUR') call ok(state_vars%netcdf_var_names(2) == 'TEMP_CUR') call ok(state_vars%netcdf_var_names(3) == 'UVEL_CUR') -call ok(state_vars%qtys(1) == 263) -call ok(state_vars%qtys(2) == 254) -call ok(state_vars%qtys(3) == 356) +call ok(state_vars%qtys(1) == QTY_SALINITY) +call ok(state_vars%qtys(2) == QTY_POTENTIAL_TEMPERATURE) +call ok(state_vars%qtys(3) == QTY_U_CURRENT_COMPONENT) call ok(state_vars%updates(1) .eqv. .true.) call ok(state_vars%updates(2) .eqv. .true.) call ok(state_vars%updates(3) .eqv. .true.) @@ -63,9 +65,9 @@ program test_parse_variables call ok(state_vars_clamp%netcdf_var_names(1) == 'SALT_CUR') call ok(state_vars_clamp%netcdf_var_names(2) == 'TEMP_CUR') call ok(state_vars_clamp%netcdf_var_names(3) == 'UVEL_CUR') -call ok(state_vars_clamp%qtys(1) == 263) -call ok(state_vars_clamp%qtys(2) == 254) -call ok(state_vars_clamp%qtys(3) == 356) +call ok(state_vars_clamp%qtys(1) == QTY_SALINITY) +call ok(state_vars_clamp%qtys(2) == QTY_POTENTIAL_TEMPERATURE) +call ok(state_vars_clamp%qtys(3) == QTY_U_CURRENT_COMPONENT) call ok(state_vars_clamp%clamp_values(1,1) == 0.0) call ok(state_vars_clamp%clamp_values(1,2) == 0.0) call ok(state_vars_clamp%clamp_values(2,1) == 0.0) From b0c2a00c788c859a448f320f38ce9b22c32fe791 Mon Sep 17 00:00:00 2001 From: Helen Kershaw Date: Mon, 13 Jan 2025 13:07:14 -0500 Subject: [PATCH 30/45] chore: add test_parse_variables program to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c0823ccc76..0485a91280 100644 --- a/.gitignore +++ b/.gitignore @@ -207,6 +207,7 @@ test_normal_dist test_beta_dist test_kde_dist test_window +test_parse_variables # Directories to NOT IGNORE ... same as executable names # as far as I know, these must be listed after the executables From a4b10d6d44fa02a9c17acb969ded021feec77042 Mon Sep 17 00:00:00 2001 From: Marlena Smith <44214771+mjs2369@users.noreply.github.com> Date: Mon, 13 Jan 2025 11:28:54 -0700 Subject: [PATCH 31/45] Fix state_var_type%netcdf_var_names character length in default_model_mod.f90 Co-authored-by: Helen Kershaw <20047007+hkershaw-brown@users.noreply.github.com> --- models/utilities/default_model_mod.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/utilities/default_model_mod.f90 b/models/utilities/default_model_mod.f90 index a5dd6378fc..e45f8b351b 100644 --- a/models/utilities/default_model_mod.f90 +++ b/models/utilities/default_model_mod.f90 @@ -60,7 +60,7 @@ module default_model_mod type :: state_var_type integer :: nvars - character(len=64), allocatable :: netcdf_var_names(:) + character(len=NF90_MAX_NAME), allocatable :: netcdf_var_names(:) integer, allocatable :: qtys(:) real(r8), allocatable :: clamp_values(:, :) logical, allocatable :: updates(:) From 79cd8ea027b913a5625ec43c507f178dc77d07f3 Mon Sep 17 00:00:00 2001 From: Marlena Smith <44214771+mjs2369@users.noreply.github.com> Date: Mon, 13 Jan 2025 11:47:19 -0700 Subject: [PATCH 32/45] Add needed trim to default_model_mod.f90 Co-authored-by: Helen Kershaw <20047007+hkershaw-brown@users.noreply.github.com> --- models/utilities/default_model_mod.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/utilities/default_model_mod.f90 b/models/utilities/default_model_mod.f90 index e45f8b351b..3098c750ca 100644 --- a/models/utilities/default_model_mod.f90 +++ b/models/utilities/default_model_mod.f90 @@ -344,7 +344,7 @@ function parse_variables_clamp(vars_table) result(state_vars) ! Make sure DART qty is valid state_vars%qtys(i) = get_index_for_quantity(dart_qty_str) if( state_vars%qtys(i) < 0 ) then - write(string1,*) 'The quantity specified in the &model_nml "', dart_qty_str, '", is not present in obs_kind_mod.f90' + write(string1,*) 'The quantity specified in the &model_nml "', trim(dart_qty_str), '", is not present in obs_kind_mod.f90' call error_handler(E_ERR,'get_state_variables_clamp',string1) endif From b66ee828b265e9e91a6a92b77ab13e4c38c3fcbb Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Mon, 13 Jan 2025 13:34:06 -0700 Subject: [PATCH 33/45] Making final adjustments and fixes to the dev test --- .../namelist/test_parse_variables.f90 | 22 ++++++++++--------- developer_tests/namelist/work/input.nml | 4 ++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/developer_tests/namelist/test_parse_variables.f90 b/developer_tests/namelist/test_parse_variables.f90 index 11852cfce7..e2411d50b3 100644 --- a/developer_tests/namelist/test_parse_variables.f90 +++ b/developer_tests/namelist/test_parse_variables.f90 @@ -6,7 +6,7 @@ program test_parse_variables use utilities_mod, only : find_namelist_in_file, check_namelist_read use mpi_utilities_mod, only : initialize_mpi_utilities, finalize_mpi_utilities -use types_mod, only : vtablenamelength, MISSING_R8 +use types_mod, only : vtablenamelength, MISSING_R8, r8 use default_model_mod, only : parse_variables_clamp, parse_variables, & state_var_type, & MAX_STATE_VARIABLE_FIELDS_CLAMP, & @@ -32,7 +32,7 @@ program test_parse_variables call initialize_mpi_utilities('test_parse_variables') -call plan(26) +call plan(28) ! Using namelist entry WITHOUT clamping values @@ -49,8 +49,9 @@ program test_parse_variables call ok(state_vars%qtys(1) == QTY_SALINITY) call ok(state_vars%qtys(2) == QTY_POTENTIAL_TEMPERATURE) call ok(state_vars%qtys(3) == QTY_U_CURRENT_COMPONENT) +call ok(allocated(state_vars_clamp%clamp_values) .eqv. .false.) call ok(state_vars%updates(1) .eqv. .true.) -call ok(state_vars%updates(2) .eqv. .true.) +call ok(state_vars%updates(2) .eqv. .false.) call ok(state_vars%updates(3) .eqv. .true.) ! Using namelist entry WITH clamping values @@ -68,14 +69,15 @@ program test_parse_variables call ok(state_vars_clamp%qtys(1) == QTY_SALINITY) call ok(state_vars_clamp%qtys(2) == QTY_POTENTIAL_TEMPERATURE) call ok(state_vars_clamp%qtys(3) == QTY_U_CURRENT_COMPONENT) -call ok(state_vars_clamp%clamp_values(1,1) == 0.0) -call ok(state_vars_clamp%clamp_values(1,2) == 0.0) -call ok(state_vars_clamp%clamp_values(2,1) == 0.0) -call ok(state_vars_clamp%clamp_values(2,2) == 0.0) -call ok(state_vars_clamp%clamp_values(3,1) == 0.0) -call ok(state_vars_clamp%clamp_values(3,2) == 0.0) +call ok(allocated(state_vars_clamp%clamp_values) .eqv. .true.) +call ok(state_vars_clamp%clamp_values(1,1) == 0.0_r8) +call ok(state_vars_clamp%clamp_values(1,2) == 0.0_r8) +call ok(state_vars_clamp%clamp_values(2,1) == 0.0_r8) +call ok(state_vars_clamp%clamp_values(2,2) == 0.0_r8) +call ok(state_vars_clamp%clamp_values(3,1) == 0.0_r8) +call ok(state_vars_clamp%clamp_values(3,2) == 0.0_r8) call ok(state_vars_clamp%updates(1) .eqv. .true.) -call ok(state_vars_clamp%updates(2) .eqv. .true.) +call ok(state_vars_clamp%updates(2) .eqv. .false.) call ok(state_vars_clamp%updates(3) .eqv. .true.) call finalize_mpi_utilities() diff --git a/developer_tests/namelist/work/input.nml b/developer_tests/namelist/work/input.nml index 6cdc2a9abf..5cb8222ada 100644 --- a/developer_tests/namelist/work/input.nml +++ b/developer_tests/namelist/work/input.nml @@ -1,12 +1,12 @@ &model_nml_clamp state_variables_clamp = 'SALT_CUR ', 'QTY_SALINITY', '0.0', '0.0', 'UPDATE', - 'TEMP_CUR ', 'QTY_POTENTIAL_TEMPERATURE', '0.0', '0.0', 'UPDATE', + 'TEMP_CUR ', 'QTY_POTENTIAL_TEMPERATURE', '0.0', '0.0', 'NO_COPY_BACK', 'UVEL_CUR ', 'QTY_U_CURRENT_COMPONENT ', '0', '0.0', 'UPDATE', / &model_nml state_variables = 'SALT_CUR ', 'QTY_SALINITY', 'UPDATE', - 'TEMP_CUR ', 'QTY_POTENTIAL_TEMPERATURE', 'UPDATE', + 'TEMP_CUR ', 'QTY_POTENTIAL_TEMPERATURE', 'NO_COPY_BACK', 'UVEL_CUR ', 'QTY_U_CURRENT_COMPONENT ', 'update' / From d6bebae7999ec7c682c83264790288a730e1ea9b Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Mon, 13 Jan 2025 13:51:22 -0700 Subject: [PATCH 34/45] Reverting the MITgcm_ocean model_mod to the original (cannot use the new routine) --- models/MITgcm_ocean/model_mod.f90 | 110 ++++++++++++++++++++++++++++-- 1 file changed, 105 insertions(+), 5 deletions(-) diff --git a/models/MITgcm_ocean/model_mod.f90 b/models/MITgcm_ocean/model_mod.f90 index 461a7cd25b..85d635e520 100644 --- a/models/MITgcm_ocean/model_mod.f90 +++ b/models/MITgcm_ocean/model_mod.f90 @@ -43,9 +43,7 @@ module model_mod use random_seq_mod, only : random_seq_type, init_random_seq, random_gaussian use default_model_mod, only : nc_write_model_vars, adv_1step, & - init_conditions => fail_init_conditions, & - parse_variables_clamp, & - MAX_STATE_VARIABLE_FIELDS_CLAMP + init_conditions => fail_init_conditions use dart_time_io_mod, only : write_model_time @@ -280,7 +278,14 @@ module model_mod ! Model namelist declarations with defaults ! Codes for interpreting the columns of the variable_table -character(len=vtablenamelength) :: mitgcm_variables(MAX_STATE_VARIABLE_FIELDS_CLAMP) = ' ' +integer, parameter :: VT_VARNAMEINDX = 1 ! ... variable name +integer, parameter :: VT_KINDINDX = 2 ! ... DART QUANTITY +integer, parameter :: VT_MINVALINDX = 3 ! ... minimum value if any +integer, parameter :: VT_MAXVALINDX = 4 ! ... maximum value if any +integer, parameter :: VT_STATEINDX = 5 ! ... update (state) or not +integer, parameter :: MAX_STATE_VARIABLES = 20 +integer, parameter :: NUM_STATE_TABLE_COLUMNS = 5 +character(len=vtablenamelength) :: mitgcm_variables(NUM_STATE_TABLE_COLUMNS, MAX_STATE_VARIABLES ) = ' ' character(len=256) :: model_shape_file = ' ' integer :: assimilation_period_days = 7 @@ -312,6 +317,7 @@ module model_mod end type MIT_meta_type integer :: domain_id +integer :: nvars contains @@ -324,6 +330,11 @@ module model_mod subroutine static_init_model() +character(len=vtablenamelength) :: var_names(MAX_STATE_VARIABLES) = ' ' +integer :: quantity_list(MAX_STATE_VARIABLES) = MISSING_I +real(r8) :: clamp_vals(MAX_STATE_VARIABLES,2) = MISSING_R8 +logical :: update_list(MAX_STATE_VARIABLES) = .FALSE. + integer :: i, iunit, io integer :: ss, dd integer :: ncid ! for reading compressed coordinates @@ -522,7 +533,11 @@ subroutine static_init_model() if (do_output()) write( * , *) 'Using grid size : ' if (do_output()) write( * , *) ' Nx, Ny, Nz = ', Nx, Ny, Nz -domain_id = add_domain(model_shape_file, parse_variables_clamp(mitgcm_variables)) +call parse_variable_input(mitgcm_variables, model_shape_file, nvars, & + var_names, quantity_list, clamp_vals, update_list) + +domain_id = add_domain(model_shape_file, nvars, & + var_names, quantity_list, clamp_vals, update_list ) if (compress) then ! read in compressed coordinates @@ -2035,6 +2050,91 @@ subroutine write_data_namelistfile end subroutine write_data_namelistfile +!----------------------------------------------------------------------- +!> +!> Fill the array of requested variables, dart kinds, possible min/max +!> values and whether or not to update the field in the output file. +!> +!>@param state_variables the list of variables and kinds from model_mod_nml +!>@param ngood the number of variable/KIND pairs specified + +subroutine parse_variable_input(state_variables, filename, ngood, & + var_names, quantity_list, clamp_vals, update_list) + +character(len=*), intent(in) :: state_variables(:,:) +character(len=*), intent(in) :: filename +integer, intent(out) :: ngood +character(len=*), intent(out) :: var_names(:) +integer, intent(out) :: quantity_list(:) +real(r8), intent(out) :: clamp_vals(:,:) +logical, intent(out) :: update_list(:) + +integer :: i +character(len=NF90_MAX_NAME) :: varname +character(len=NF90_MAX_NAME) :: dartstr +character(len=NF90_MAX_NAME) :: minvalstring +character(len=NF90_MAX_NAME) :: maxvalstring +character(len=NF90_MAX_NAME) :: updateable + +ngood = 0 +MyLoop : do i = 1, MAX_STATE_VARIABLES + + varname = trim(state_variables(VT_VARNAMEINDX,i)) + dartstr = trim(state_variables(VT_KINDINDX ,i)) + minvalstring = trim(state_variables(VT_MINVALINDX ,i)) + maxvalstring = trim(state_variables(VT_MAXVALINDX ,i)) + updateable = trim(state_variables(VT_STATEINDX ,i)) + + if ( varname == ' ' .and. dartstr == ' ' ) exit MyLoop ! Found end of list. + + if ( varname == ' ' .or. dartstr == ' ' ) then + string1 = 'model_nml:model "variables" not fully specified' + string2 = 'reading from "'//trim(filename)//'"' + call error_handler(E_ERR,'parse_variable_input:',string1,source,text2=string2) + endif + + ! Make sure DART quantity is valid + + if( get_index_for_quantity(dartstr) < 0 ) then + write(string1,'(''there is no quantity <'',a,''> in obs_kind_mod.f90'')') trim(dartstr) + call error_handler(E_ERR,'parse_variable_input:',string1,source) + endif + + call to_upper(minvalstring) + call to_upper(maxvalstring) + call to_upper(updateable) + + var_names(i) = varname + quantity_list(i) = get_index_for_quantity(dartstr) + clamp_vals(i, 1) = string_to_real(minvalstring) + clamp_vals(i, 2) = string_to_real(maxvalstring) + update_list(i) = string_to_logical(updateable, 'UPDATE') + + ! Adjust clamping in case of log-transform + if (quantity_list(i) == QTY_NITRATE_CONCENTRATION ) call adjust_clamp(clamp_vals(i, 1)) + if (quantity_list(i) == QTY_PHOSPHATE_CONCENTRATION ) call adjust_clamp(clamp_vals(i, 1)) + if (quantity_list(i) == QTY_DISSOLVED_OXYGEN ) call adjust_clamp(clamp_vals(i, 1)) + if (quantity_list(i) == QTY_PHYTOPLANKTON_BIOMASS ) call adjust_clamp(clamp_vals(i, 1)) + if (quantity_list(i) == QTY_ALKALINITY ) call adjust_clamp(clamp_vals(i, 1)) + if (quantity_list(i) == QTY_DISSOLVED_INORGANIC_CARBON) call adjust_clamp(clamp_vals(i, 1)) + if (quantity_list(i) == QTY_DISSOLVED_ORGANIC_P ) call adjust_clamp(clamp_vals(i, 1)) + if (quantity_list(i) == QTY_DISSOLVED_ORGANIC_NITROGEN) call adjust_clamp(clamp_vals(i, 1)) + if (quantity_list(i) == QTY_DISSOLVED_INORGANIC_IRON ) call adjust_clamp(clamp_vals(i, 1)) + if (quantity_list(i) == QTY_SURFACE_CHLOROPHYLL ) call adjust_clamp(clamp_vals(i, 1)) + + ngood = ngood + 1 + +enddo MyLoop + +if (ngood == MAX_STATE_VARIABLES) then + string1 = 'WARNING: There is a possibility you need to increase ''MAX_STATE_VARIABLES''' + write(string2,'(''WARNING: you have specified at least '',i4,'' perhaps more.'')')ngood + call error_handler(E_MSG,'parse_variable_input:',string1,source,text2=string2) +endif + +end subroutine parse_variable_input + + !----------------------------------------------------------------------- !> !> We may choose to do a log transformation for the bgc tracers From e78fbdaacb91a4f0e8e66ea69e80f18b668d6cf0 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Mon, 13 Jan 2025 13:53:17 -0700 Subject: [PATCH 35/45] Adding the logic to use the default state variables if model_state_variables is empty before calling add_domain (POP and CICE) --- models/POP/model_mod.f90 | 8 +++++++- models/cice/model_mod.f90 | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/models/POP/model_mod.f90 b/models/POP/model_mod.f90 index 6661a5a324..d99eeba52d 100644 --- a/models/POP/model_mod.f90 +++ b/models/POP/model_mod.f90 @@ -337,6 +337,12 @@ subroutine static_init_model() ! Initialize the interpolation routines call init_interp() +if ( model_state_variables(1) == ' ' ) then ! no model_state_variables namelist provided + call use_default_state_variables( model_state_variables ) + string1 = 'model_nml:model_state_variables not specified - using default variables' + call error_handler(E_MSG,'static_init_model',string1,source,revision,revdate) +endif + !> @todo 'pop.r.nc' is hardcoded in dart_pop_mod.f90 ! parse_variables converts the character table that was read in from ! model_nml:model_state_variables and returns a state_var_type that @@ -2693,7 +2699,7 @@ subroutine use_default_state_variables( state_variables ) character(len=*), intent(inout) :: state_variables(:) ! strings must all be the same length for the gnu compiler -state_variables( 1:5*3 ) = & +state_variables( 1:15 ) = & (/ 'SALT_CUR ', 'QTY_SALINITY ', 'UPDATE ', & 'TEMP_CUR ', 'QTY_POTENTIAL_TEMPERATURE ', 'UPDATE ', & 'UVEL_CUR ', 'QTY_U_CURRENT_COMPONENT ', 'UPDATE ', & diff --git a/models/cice/model_mod.f90 b/models/cice/model_mod.f90 index ca88c110f3..08e838ff43 100644 --- a/models/cice/model_mod.f90 +++ b/models/cice/model_mod.f90 @@ -408,6 +408,12 @@ subroutine static_init_model() ! Initialize the interpolation routines call init_interp() +if ( model_state_variables(1) == ' ' ) then ! no model_state_variables namelist provided + call use_default_state_variables( model_state_variables ) + string1 = 'model_nml:model_state_variables not specified - using default variables' + call error_handler(E_MSG,'static_init_model',string1,source,revision,revdate) +endif + ! Determine the shape of the variables from "cice.r.nc" ! The assimilate.csh, perfect_model.csh must ensure the cice restart file ! is linked to this filename. @@ -2530,7 +2536,7 @@ subroutine use_default_state_variables( state_variables ) character(len=*), intent(inout) :: state_variables(:) ! strings must all be the same length for the gnu compiler -state_variables( 1:5*3 ) = & +state_variables( 1:15 ) = & (/ 'CONCENTRATION ', 'QTY_SEAICE_CONCENTR ', 'UPDATE ', & 'ICEVOLUME ', 'QTY_SEAICE_VOLUME ', 'UPDATE ', & 'SNOWVOLUME ', 'QTY_SEAICE_SNOWVOLUME ', 'UPDATE ', & From 807cfe5bad9ea6df66b834f8dd51b51f9ea0b0f2 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Wed, 29 Jan 2025 15:20:24 -0700 Subject: [PATCH 36/45] Updating the domain_type%method field to include state_type and fixing comments in state_structure_mod --- assimilation_code/modules/io/state_structure_mod.f90 | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/assimilation_code/modules/io/state_structure_mod.f90 b/assimilation_code/modules/io/state_structure_mod.f90 index a3955e9ecf..fb3240ac49 100644 --- a/assimilation_code/modules/io/state_structure_mod.f90 +++ b/assimilation_code/modules/io/state_structure_mod.f90 @@ -18,11 +18,15 @@ module state_structure_mod !> The add_domain() call adds a 'domain' to the state. This may be a component in !> the case of XCESM or another coupled model. !> -!> There are three ways to add a domain (these are overloaded as add_domain): +!> There are four ways to add a domain (these are overloaded as add_domain): !> * add_domain_blank. This takes model size as an argument. !> !> * add_domain_from_file. This takes a netcdf file and a list of variables !> +!> * add_domain_from_state_type. This takes a netcdf file and a state_var_type, +!> which includes nvars, netcdf variable names, qtys (kinds), +!> clamp_values (optional), and updates +!> !> * add_domain_from_spec. This makes a skeleton structure for a domain. Dimensions !> for each variable must be added using add_dimension_to_variable(). This is intended !> to be used to create netcdf output for models like bgrid_solo that are spun up. @@ -242,8 +246,8 @@ module state_structure_mod character(len=256) :: info_file = 'NULL' ! string identifying the manner in which the domain was created - ! 'blank', 'file', or 'spec' - character(len=6) :: method = 'none' + ! 'blank', 'file', 'state_type', or 'spec' + character(len=11) :: method = 'none' end type domain_type @@ -390,7 +394,7 @@ function add_domain_from_state_type(info_file, vars) result(dom_id) ! save information about the information file state%domain(dom_id)%info_file = info_file -state%domain(dom_id)%method = 'file' +state%domain(dom_id)%method = 'state_type' ! set number of variables in this domain state%domain(dom_id)%num_variables = vars%nvars From 548190e876568f031144776b3140764148c90755 Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Wed, 29 Jan 2025 16:31:51 -0700 Subject: [PATCH 37/45] Reverting changes to noah model_mod back to main (original) --- models/noah/model_mod.f90 | 112 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 107 insertions(+), 5 deletions(-) diff --git a/models/noah/model_mod.f90 b/models/noah/model_mod.f90 index 913f09c2ca..3be41d10c1 100644 --- a/models/noah/model_mod.f90 +++ b/models/noah/model_mod.f90 @@ -46,9 +46,7 @@ module model_mod use distributed_state_mod, only : get_state -use default_model_mod, only : adv_1step, nc_write_model_vars, & - parse_variables_clamp, & - MAX_STATE_VARIABLE_FIELDS_CLAMP +use default_model_mod, only : adv_1step, nc_write_model_vars use noah_hydro_mod, only : configure_lsm, get_noah_timestepping, & num_soil_layers, lsm_namelist_filename, & @@ -125,6 +123,15 @@ module model_mod integer, parameter :: NSOLDX = 100 character(len=512) :: string1, string2, string3 +! Codes for interpreting the columns of the variable_table +integer, parameter :: VT_VARNAMEINDX = 1 ! ... variable name +integer, parameter :: VT_KINDINDX = 2 ! ... DART kind +integer, parameter :: VT_MINVALINDX = 3 ! ... minimum value if any +integer, parameter :: VT_MAXVALINDX = 4 ! ... maximum value if any +integer, parameter :: VT_STATEINDX = 5 ! ... update (state) or not +integer, parameter :: MAX_STATE_VARIABLES = 40 +integer, parameter :: NUM_STATE_TABLE_COLUMNS = 5 + integer :: domain_count integer :: idom, idom_lsm = -1 @@ -154,7 +161,7 @@ module model_mod real(r8) :: model_perturbation_amplitude = 0.002 character(len=256) :: perturb_distribution = 'lognormal' integer :: debug = 0 ! turn up for more and more debug messages -character(len=obstypelength) :: lsm_variables(MAX_STATE_VARIABLE_FIELDS_CLAMP) = ' ' +character(len=obstypelength) :: lsm_variables(NUM_STATE_TABLE_COLUMNS,MAX_STATE_VARIABLES) = ' ' !nc -- we are adding these to the model.nml until they appear in the NetCDF files logical :: polar = .false. ! wrap over the poles @@ -219,6 +226,11 @@ subroutine static_init_model() integer :: n_lsm_fields integer :: i +character(len=obstypelength) :: var_names(MAX_STATE_VARIABLES) +real(r8) :: var_ranges(MAX_STATE_VARIABLES,2) +logical :: var_update(MAX_STATE_VARIABLES) +integer :: var_qtys( MAX_STATE_VARIABLES) + character(len=256) :: filename if ( module_initialized ) return ! only need to do this once. @@ -262,7 +274,12 @@ subroutine static_init_model() else call configure_lsm(lsm_model_choice,domain_shapefiles(domainID)) call read_noah_global_atts(domain_shapefiles(domainID)) - idom_lsm = add_domain(domain_shapefiles(domainID), parse_variables_clamp(lsm_variables)) + call verify_variables(lsm_variables, domain_shapefiles(domainID), n_lsm_fields, & + var_names, var_qtys, var_ranges, var_update) + idom_lsm = add_domain(domain_shapefiles(domainID), n_lsm_fields, var_names, & + kind_list=var_qtys, & + clamp_vals=var_ranges(1:n_lsm_fields,:), & + update_list=var_update) if (debug > 99) call state_structure_info(idom_lsm) call check_vertical_dimension(idom_lsm) @@ -1203,6 +1220,91 @@ end subroutine pert_model_copies ! with predefined file formats and control structures.) !================================================================== + +!------------------------------------------------------------------ +!> given the list of variables and a filename, check user input +!> return the handle to the open netCDF file and the number of variables +!> in this 'domain' + +subroutine verify_variables( variable_table, filename, ngood, & + var_names, var_qtys, var_ranges, var_update) + +character(len=*), intent(in) :: variable_table(:,:) +character(len=*), intent(in) :: filename +integer, intent(out) :: ngood +character(len=*), intent(out) :: var_names(:) +real(r8), intent(out) :: var_ranges(:,:) +logical, intent(out) :: var_update(:) +integer , intent(out) :: var_qtys(:) + +character(len=*), parameter :: routine = 'verify_variables' + +integer :: io, i, quantity +real(r8) :: minvalue, maxvalue + +character(len=NF90_MAX_NAME) :: varname +character(len=NF90_MAX_NAME) :: dartstr +character(len=NF90_MAX_NAME) :: minvalstring +character(len=NF90_MAX_NAME) :: maxvalstring +character(len=NF90_MAX_NAME) :: state_or_aux + +ngood = 0 +MyLoop : do i = 1, size(variable_table,2) + + varname = variable_table(VT_VARNAMEINDX,i) + dartstr = variable_table(VT_KINDINDX ,i) + minvalstring = variable_table(VT_MINVALINDX ,i) + maxvalstring = variable_table(VT_MAXVALINDX ,i) + state_or_aux = variable_table(VT_STATEINDX ,i) + + if ( varname == ' ' .and. dartstr == ' ' ) exit MyLoop ! Found end of list. + + if ( varname == ' ' .or. dartstr == ' ' ) then + string1 = 'model_nml: variable list not fully specified' + string2 = 'reading from "'//trim(filename)//'"' + call error_handler(E_ERR,routine, string1, source, text2=string2) + endif + + ! The internal DART routines check if the variable name is valid. + + ! Make sure DART kind is valid + quantity = get_index_for_quantity(dartstr) + if( quantity < 0 ) then + write(string1,'(''there is no obs_kind "'',a,''" in obs_kind_mod.f90'')') & + trim(dartstr) + call error_handler(E_ERR,routine,string1,source) + endif + + ! All good to here - fill the output variables + + ngood = ngood + 1 + var_names( ngood) = varname + var_qtys( ngood) = quantity + var_ranges(ngood,:) = (/ MISSING_R8, MISSING_R8 /) + var_update(ngood) = .false. ! at least initially + + ! convert the [min,max]valstrings to numeric values if possible + read(minvalstring,*,iostat=io) minvalue + if (io == 0) var_ranges(ngood,1) = minvalue + + read(maxvalstring,*,iostat=io) maxvalue + if (io == 0) var_ranges(ngood,2) = maxvalue + + call to_upper(state_or_aux) + if (state_or_aux == 'UPDATE') var_update(ngood) = .true. + +enddo MyLoop + +if (ngood == MAX_STATE_VARIABLES) then + string1 = 'WARNING: you may need to increase "MAX_STATE_VARIABLES"' + write(string2,'(''you have specified at least '',i4,'' perhaps more.'')') ngood + call error_handler(E_MSG,routine,string1,source,text2=string2) +endif + + +end subroutine verify_variables + + !----------------------------------------------------------------------- !> Sets the location information arrays for each domain !> Each location array is declared to be 3D to make it easy to use From a95013b1d8098c104c8d2fd3f6b1ff167cdc3975 Mon Sep 17 00:00:00 2001 From: Helen Kershaw Date: Mon, 3 Feb 2025 11:49:27 -0700 Subject: [PATCH 38/45] test_parse_variables is a serial program --- developer_tests/namelist/test_parse_variables.f90 | 8 ++++---- developer_tests/namelist/work/quickbuild.sh | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/developer_tests/namelist/test_parse_variables.f90 b/developer_tests/namelist/test_parse_variables.f90 index e2411d50b3..16e75ceb42 100644 --- a/developer_tests/namelist/test_parse_variables.f90 +++ b/developer_tests/namelist/test_parse_variables.f90 @@ -4,8 +4,8 @@ program test_parse_variables -use utilities_mod, only : find_namelist_in_file, check_namelist_read -use mpi_utilities_mod, only : initialize_mpi_utilities, finalize_mpi_utilities +use utilities_mod, only : find_namelist_in_file, check_namelist_read, & + initialize_utilities, finalize_utilities use types_mod, only : vtablenamelength, MISSING_R8, r8 use default_model_mod, only : parse_variables_clamp, parse_variables, & state_var_type, & @@ -30,7 +30,7 @@ program test_parse_variables namelist /model_nml_clamp/ & state_variables_clamp -call initialize_mpi_utilities('test_parse_variables') +call initialize_utilities('test_parse_variables') call plan(28) @@ -80,6 +80,6 @@ program test_parse_variables call ok(state_vars_clamp%updates(2) .eqv. .false.) call ok(state_vars_clamp%updates(3) .eqv. .true.) -call finalize_mpi_utilities() +call finalize_utilities() end program test_parse_variables diff --git a/developer_tests/namelist/work/quickbuild.sh b/developer_tests/namelist/work/quickbuild.sh index 9a4d4ef828..61d4f99ebf 100755 --- a/developer_tests/namelist/work/quickbuild.sh +++ b/developer_tests/namelist/work/quickbuild.sh @@ -15,7 +15,7 @@ dev_test=1 TEST="namelist" LOCATION="threed_sphere" -programs=( +serial_programs=( test_parse_variables ) From 57734f1106f4f5aa01a7b997ee48d9907b752481 Mon Sep 17 00:00:00 2001 From: Helen Kershaw Date: Mon, 3 Feb 2025 12:45:57 -0700 Subject: [PATCH 39/45] comment typo, namelist item is state_variables --- models/cam-fv/model_mod.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/cam-fv/model_mod.f90 b/models/cam-fv/model_mod.f90 index 6314ee6be5..a2fd4adf8b 100644 --- a/models/cam-fv/model_mod.f90 +++ b/models/cam-fv/model_mod.f90 @@ -297,7 +297,7 @@ subroutine static_init_model() call init_globals() ! parse_variables converts the character table that was read in from -! model_nml:model_state_variables into a state_var_type that can be +! model_nml:state_variables into a state_var_type that can be ! passed to add_domain domain_id = add_domain(cam_template_filename, parse_variables_clamp(state_variables)) From e7bb723c02b5d12176b8ffcc8afc36b0ac8841b1 Mon Sep 17 00:00:00 2001 From: Helen Kershaw Date: Mon, 3 Feb 2025 12:46:58 -0700 Subject: [PATCH 40/45] chore: remove old svn info --- models/POP/model_mod.f90 | 6 ------ 1 file changed, 6 deletions(-) diff --git a/models/POP/model_mod.f90 b/models/POP/model_mod.f90 index d99eeba52d..24a66017f3 100644 --- a/models/POP/model_mod.f90 +++ b/models/POP/model_mod.f90 @@ -2,7 +2,6 @@ ! by UCAR, "as is", without charge, subject to all terms of use at ! http://www.image.ucar.edu/DAReS/DART/DART_download ! -! $Id$ module model_mod @@ -2714,8 +2713,3 @@ end subroutine use_default_state_variables end module model_mod -! -! $URL$ -! $Id$ -! $Revision$ -! $Date$ From d43f7ee13b583300340b0d86de863063d139c292 Mon Sep 17 00:00:00 2001 From: Helen Kershaw Date: Mon, 3 Feb 2025 13:12:47 -0700 Subject: [PATCH 41/45] comment on relative size MAX_STATE_VARIABLE_FIELDS, MAX_STATE_VARIABLE_FIELDS_CLAMP https://github.com/NCAR/DART/pull/783/files#r1939952202 --- models/utilities/default_model_mod.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/models/utilities/default_model_mod.f90 b/models/utilities/default_model_mod.f90 index 3098c750ca..c8ae1cb7a9 100644 --- a/models/utilities/default_model_mod.f90 +++ b/models/utilities/default_model_mod.f90 @@ -66,6 +66,7 @@ module default_model_mod logical, allocatable :: updates(:) end type state_var_type +! note _fields is 3* and _fields_clamp is 5*MAX_STATE_VARIABLES integer, parameter :: MAX_STATE_VARIABLES = 100 integer, parameter :: MAX_STATE_VARIABLE_FIELDS = 300 integer, parameter :: MAX_STATE_VARIABLE_FIELDS_CLAMP = 500 From 9e367a60c74cc42b65022f82559291febd5b3e90 Mon Sep 17 00:00:00 2001 From: Helen Kershaw Date: Mon, 3 Feb 2025 13:27:19 -0700 Subject: [PATCH 42/45] comment typo, the namelist item is called state_variables --- models/cam-se/model_mod.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/cam-se/model_mod.f90 b/models/cam-se/model_mod.f90 index 324fe53346..c2a228916b 100644 --- a/models/cam-se/model_mod.f90 +++ b/models/cam-se/model_mod.f90 @@ -342,7 +342,7 @@ subroutine static_init_model() call init_globals() ! parse_variables converts the character table that was read in from -! model_nml:model_state_variables and returns a state_var_type +! model_nml:state_variables and returns a state_var_type ! that can be passed to add_domain domain_id = add_domain(cam_template_filename, parse_variables_clamp(state_variables)) From 120d83a833211753ef75cc820bec677b5280d0a3 Mon Sep 17 00:00:00 2001 From: Helen Kershaw Date: Tue, 4 Feb 2025 11:44:44 -0700 Subject: [PATCH 43/45] max number of state variables 1000 "big" for models currently in the repo --- models/utilities/default_model_mod.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/models/utilities/default_model_mod.f90 b/models/utilities/default_model_mod.f90 index c8ae1cb7a9..8fdb131494 100644 --- a/models/utilities/default_model_mod.f90 +++ b/models/utilities/default_model_mod.f90 @@ -67,9 +67,9 @@ module default_model_mod end type state_var_type ! note _fields is 3* and _fields_clamp is 5*MAX_STATE_VARIABLES -integer, parameter :: MAX_STATE_VARIABLES = 100 -integer, parameter :: MAX_STATE_VARIABLE_FIELDS = 300 -integer, parameter :: MAX_STATE_VARIABLE_FIELDS_CLAMP = 500 +integer, parameter :: MAX_STATE_VARIABLES = 1000 +integer, parameter :: MAX_STATE_VARIABLE_FIELDS = 3000 +integer, parameter :: MAX_STATE_VARIABLE_FIELDS_CLAMP = 5000 character(len=*), parameter :: source = 'utilities/default_model_mod.f90' contains From a7bf0bf472dacb7e59acfb20cd40369276f06f3a Mon Sep 17 00:00:00 2001 From: Helen Kershaw Date: Tue, 4 Feb 2025 12:03:05 -0700 Subject: [PATCH 44/45] catch if number of state variables gets too big Default value for nvars = -1 (rather than unset) in that state_var_type --- models/utilities/default_model_mod.f90 | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/models/utilities/default_model_mod.f90 b/models/utilities/default_model_mod.f90 index 8fdb131494..49f9a0458c 100644 --- a/models/utilities/default_model_mod.f90 +++ b/models/utilities/default_model_mod.f90 @@ -59,7 +59,7 @@ module default_model_mod MAX_STATE_VARIABLE_FIELDS_CLAMP type :: state_var_type - integer :: nvars + integer :: nvars = -1 character(len=NF90_MAX_NAME), allocatable :: netcdf_var_names(:) integer, allocatable :: qtys(:) real(r8), allocatable :: clamp_values(:, :) @@ -332,6 +332,11 @@ function parse_variables_clamp(vars_table) result(state_vars) endif enddo +if (state_vars%nvars >= MAX_STATE_VARIABLES -1) then + write(string1,*) 'nvars ', state_vars%nvars, ' >= MAX_STATE_VARIABLES-1', MAX_STATE_VARIABLES-1, 'increase MAX_STATE_VARIABLES' + call error_handler(E_ERR, string1, source) +endif + ! Allocate the arrays in the var derived type allocate(state_vars%netcdf_var_names(state_vars%nvars), state_vars%qtys(state_vars%nvars), state_vars%clamp_values(state_vars%nvars, 2), state_vars%updates(state_vars%nvars)) @@ -412,6 +417,11 @@ function parse_variables(vars_table) result(state_vars) endif enddo +if (state_vars%nvars >= MAX_STATE_VARIABLES -1) then + write(string1,*) 'nvars ', state_vars%nvars, ' >= MAX_STATE_VARIABLES-1', MAX_STATE_VARIABLES-1, 'increase MAX_STATE_VARIABLES' + call error_handler(E_ERR, string1, source) +endif + ! Allocate the arrays in the var derived type allocate(state_vars%netcdf_var_names(state_vars%nvars), state_vars%qtys(state_vars%nvars), state_vars%updates(state_vars%nvars)) From 9d0a17ae6821ceb3a5c785ed0bdfa0ccd5c01c2d Mon Sep 17 00:00:00 2001 From: Marlee Smith Date: Tue, 4 Feb 2025 14:10:04 -0700 Subject: [PATCH 45/45] bump conf.py and CHANGELOG --- CHANGELOG.rst | 8 ++++++++ conf.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a29103d032..7b8761d0f8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -22,6 +22,14 @@ individual files. The changes are now listed with the most recent at the top. +**February 4 2025 :: Generic model_mod subroutine parse_variables. Tag v11.10.3** + +- Creates two generalized subroutines that convert the table of state variables that is + read in from the &model_nml to a state_var_type: parse_variables and parse_variables_clamp +- Alternate versions for this subroutine were replaced with parse_variables in the + models MOM6, wrf_hydro, aether_lat-lon, cam-fv, cam-se, POP, and cice +- New dev test test_parse_variables added + **February 3 2025 :: Inflation documentation. Tag v11.10.2** - Improved inflation documentation diff --git a/conf.py b/conf.py index f7ce20b498..67e3a861ce 100644 --- a/conf.py +++ b/conf.py @@ -21,7 +21,7 @@ author = 'Data Assimilation Research Section' # The full version, including alpha/beta/rc tags -release = '11.10.2' +release = '11.10.3' root_doc = 'index' # -- General configuration ---------------------------------------------------