From 50748d0287b9382bc4505aefc721628b37e2fa61 Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Mon, 8 Jun 2026 01:22:48 -0400 Subject: [PATCH 01/19] Add share/shr_generic_mod --- share/shr_generic_mod.F90 | 115 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 share/shr_generic_mod.F90 diff --git a/share/shr_generic_mod.F90 b/share/shr_generic_mod.F90 new file mode 100644 index 000000000..e9b6d80bd --- /dev/null +++ b/share/shr_generic_mod.F90 @@ -0,0 +1,115 @@ +module shr_generic_mod + + use ESMF + use NUOPC + use shr_strdata_mod, only : shr_strdata_type, shr_strdata_perstream, & + shr_strdata_get_stream_count, shr_strdata_get_stream, & + shr_strdata_advance, shr_strdata_get_stream_pointer + use dshr_state_mod, only : dshr_state_getfldptr + + implicit none + private + + public :: datamode_generic_advertise + public :: datamode_generic_advance + +contains + + ! ======================================================================= + ! ROUTINE: datamode_generic_advertise + ! PURPOSE: Dynamically loop through the stream configuration (XML or ESMF) + ! and advertise exactly the fields the user explicitly requested. + ! ======================================================================= + subroutine datamode_generic_advertise(exportState, sdat, rc) + type(ESMF_State), intent(inout) :: exportState + type(shr_strdata_type), intent(inout) :: sdat + integer, intent(out), optional :: rc + + integer :: n, i, num_streams + character(len=ESMF_MAXSTR) :: fieldName + type(shr_strdata_perstream), pointer :: stream_ptr + + if (present(rc)) rc = ESMF_SUCCESS + + ! Retrieve the total number of streams defined in the user's config + num_streams = shr_strdata_get_stream_count(sdat) + + do n = 1, num_streams + ! Get the pointer to the specific stream object + stream_ptr => shr_strdata_get_stream(sdat, n) + + ! Loop over the array of model field names parsed from the stream config + if (associated(stream_ptr%fldlist_model)) then + do i = 1, size(stream_ptr%fldlist_model) + fieldName = trim(stream_ptr%fldlist_model(i)) + + call NUOPC_StateAdvertise(exportState, StandardName=trim(fieldName), rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU)) return + end do + endif + end do + + end subroutine datamode_generic_advertise + + ! ======================================================================= + ! ROUTINE: datamode_generic_advance + ! PURPOSE: Advances the stream reader, interpolates data in time, and + ! dynamically copies data from the internal stream buffers + ! directly to the NUOPC export state with ZERO math or physics. + ! ======================================================================= + subroutine datamode_generic_advance(exportState, sdat, year, month, day, sec, rc) + type(ESMF_State), intent(inout) :: exportState + type(shr_strdata_type), intent(inout) :: sdat + integer, intent(in) :: year, month, day, sec + integer, intent(out), optional :: rc + + integer :: n, i, num_streams + character(len=ESMF_MAXSTR) :: fieldName + type(shr_strdata_perstream), pointer :: stream_ptr + + ! Pointers for the explicit pass-through copy + real(ESMF_KIND_R8), pointer :: strm_ptr(:) + real(ESMF_KIND_R8), pointer :: state_ptr(:) + + if (present(rc)) rc = ESMF_SUCCESS + + ! 1. Advance the stream (Handles NetCDF I/O and time interpolation) + call shr_strdata_advance(sdat, year, month, day, sec, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU)) return + + ! 2. Dynamically fetch pointers and copy data to the NUOPC Export State + num_streams = shr_strdata_get_stream_count(sdat) + + do n = 1, num_streams + stream_ptr => shr_strdata_get_stream(sdat, n) + + if (associated(stream_ptr%fldlist_model)) then + do i = 1, size(stream_ptr%fldlist_model) + fieldName = trim(stream_ptr%fldlist_model(i)) + + ! Nullify pointers to ensure clean association + strm_ptr => null() + state_ptr => null() + + ! Fetch pointer to the internal shr_strdata memory buffer + call shr_strdata_get_stream_pointer(sdat, fieldName, strm_ptr, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU)) return + + ! Fetch pointer to the NUOPC Export State ESMF_Field memory + call dshr_state_getfldptr(exportState, fieldName, fldptr1=state_ptr, & + allowNullReturn=.true., rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU)) return + + ! 3. The Core Pass-Through Logic: Direct Copy + ! If CMEPS accepted the field, the state pointer will be associated. + if (associated(strm_ptr) .and. associated(state_ptr)) then + state_ptr(:) = strm_ptr(:) + endif + + end do + endif + end do + + end subroutine datamode_generic_advance + +end module shr_generic_mod From 955704f10f88c80e8524cb9b33130f7f982701b0 Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Mon, 8 Jun 2026 01:24:05 -0400 Subject: [PATCH 02/19] Add shr_generic_mod.F90 to share/CMakeLists.txt --- share/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/share/CMakeLists.txt b/share/CMakeLists.txt index fcf3c4e74..bf7e17a9a 100644 --- a/share/CMakeLists.txt +++ b/share/CMakeLists.txt @@ -21,6 +21,7 @@ add_library(cdeps_share ${GenF90_SRCS} shr_precip_mod.F90 shr_string_mod.F90 shr_is_restart_fh_mod.F90 + shr_generic_mod.F90 nuopc_shr_methods.F90) target_include_directories (cdeps_share PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${ESMF_F90COMPILEPATHS} ${PIO_Fortran_INCLUDE_DIRS}) From e3aa2cea2cd259be8ba7698c6db240fb3e3b3951 Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Mon, 8 Jun 2026 02:21:25 -0400 Subject: [PATCH 03/19] Need to rethink datamode_generic_advertise as haven't read stream configuration yet in InitializeAdvertise --- datm/atm_comp_nuopc.F90 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/datm/atm_comp_nuopc.F90 b/datm/atm_comp_nuopc.F90 index 5758916a6..ff9295573 100644 --- a/datm/atm_comp_nuopc.F90 +++ b/datm/atm_comp_nuopc.F90 @@ -83,6 +83,9 @@ module cdeps_datm_comp use datm_pres_co2_mod , only : datm_pres_co2_init_pointers use datm_pres_co2_mod , only : datm_pres_co2_advance + use shr_generic_mod , only : datamode_generic_advertise + use shr_generic_mod , only : datamode_generic_advance + implicit none private @@ -411,6 +414,9 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) call datm_datamode_simple_advertise(exportState, fldsExport, flds_scalar_name, & nlfilename, my_task, vm, rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return + case ('GENERIC') + call datamode_generic_advertise(exportState, sdat, rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return end select end subroutine InitializeAdvertise @@ -755,6 +761,9 @@ subroutine datm_comp_run(gcomp, importState, exportState, target_ymd, target_tod case('SIMPLE') call datm_datamode_simple_advance(target_ymd, target_tod, target_mon, sdat%model_calendar, rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return + case('GENERIC') + call datamode_generic_advance(exportState, sdat, rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return end select ! Write restarts if needed From c8521be6c075a1b5e8f61c016ddebeece97aaea5 Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Mon, 8 Jun 2026 12:58:39 -0400 Subject: [PATCH 04/19] refactor shr_strdata_init_from_config into advertise and realize --- streams/dshr_strdata_mod.F90 | 43 +++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/streams/dshr_strdata_mod.F90 b/streams/dshr_strdata_mod.F90 index 1b1529e3b..2c3dc8a96 100644 --- a/streams/dshr_strdata_mod.F90 +++ b/streams/dshr_strdata_mod.F90 @@ -188,13 +188,10 @@ type(ESMF_FieldBundle) function shr_strdata_get_stream_fieldbundle(sdat, ns, nam end function shr_strdata_get_stream_fieldbundle !=============================================================================== - subroutine shr_strdata_init_from_config(sdat, streamfilename, model_mesh, clock, compname, logunit, rc) - + subroutine shr_strdata_init_advertise(sdat, streamfilename, compname, logunit, rc) ! input/output variables type(shr_strdata_type) , intent(inout) :: sdat character(len=*) , intent(in) :: streamfilename - type(ESMF_Mesh) , intent(in) :: model_mesh - type(ESMF_Clock) , intent(in) :: clock character(len=*) , intent(in) :: compname integer , intent(in) :: logunit integer , intent(out) :: rc @@ -204,8 +201,8 @@ subroutine shr_strdata_init_from_config(sdat, streamfilename, model_mesh, clock, type(ESMF_VM) :: vm integer :: stream_count integer :: istat - character(len=*), parameter :: subname='(shr_strdata_init_from_config)' - ! ---------------------------------------------- + character(len=*), parameter :: subname='(shr_strdata_init_advertise)' + rc = ESMF_SUCCESS #ifdef CESMCOUPLED @@ -244,6 +241,16 @@ subroutine shr_strdata_init_from_config(sdat, streamfilename, model_mesh, clock, ': allocation error for sdat%pstrm with stream_count '//toString(stream_count), rc=rc) return end if + end subroutine shr_strdata_init_advertise + + !=============================================================================== + subroutine shr_strdata_init_realize(sdat, model_mesh, clock, rc) + + ! input/output variables + type(shr_strdata_type) , intent(inout) :: sdat + type(ESMF_Mesh) , intent(in) :: model_mesh + type(ESMF_Clock) , intent(in) :: clock + integer , intent(out) :: rc ! Initialize sdat model domain sdat%model_mesh = model_mesh @@ -254,6 +261,30 @@ subroutine shr_strdata_init_from_config(sdat, streamfilename, model_mesh, clock, call shr_strdata_init(sdat, clock, rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return + end subroutine shr_strdata_init_realize + + !=============================================================================== + subroutine shr_strdata_init_from_config(sdat, streamfilename, model_mesh, clock, compname, logunit, rc) + + ! input/output variables + type(shr_strdata_type) , intent(inout) :: sdat + character(len=*) , intent(in) :: streamfilename + type(ESMF_Mesh) , intent(in) :: model_mesh + type(ESMF_Clock) , intent(in) :: clock + character(len=*) , intent(in) :: compname + integer , intent(in) :: logunit + integer , intent(out) :: rc + + ! local variables + character(len=*), parameter :: subname='(shr_strdata_init_from_config)' + ! ---------------------------------------------- + + call shr_strdata_init_advertise(sdat, streamfilename, compname, logunit, rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + call shr_strdata_init_realize(sdat, model_mesh, clock, rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + end subroutine shr_strdata_init_from_config !=============================================================================== From fdde2b3da996c56614d5a9752ebf455785217a99 Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Mon, 8 Jun 2026 13:17:13 -0400 Subject: [PATCH 05/19] Make them public --- streams/dshr_strdata_mod.F90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/streams/dshr_strdata_mod.F90 b/streams/dshr_strdata_mod.F90 index 2c3dc8a96..ae20a4ac2 100644 --- a/streams/dshr_strdata_mod.F90 +++ b/streams/dshr_strdata_mod.F90 @@ -63,6 +63,8 @@ module dshr_strdata_mod ! Public routines public :: shr_strdata_init_from_config + public :: shr_strdata_init_advertise + public :: shr_strdata_init_realize public :: shr_strdata_init_from_inline public :: shr_strdata_setOrbs public :: shr_strdata_advance From a01026a0e81e82ffe80f90c8bcc4d434f5f07c44 Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Mon, 8 Jun 2026 13:26:55 -0400 Subject: [PATCH 06/19] call shr_strdata_init_{advertise,realize} in datm/atm_comp_nuopc.F90 --- datm/atm_comp_nuopc.F90 | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/datm/atm_comp_nuopc.F90 b/datm/atm_comp_nuopc.F90 index ff9295573..5053de7c9 100644 --- a/datm/atm_comp_nuopc.F90 +++ b/datm/atm_comp_nuopc.F90 @@ -376,6 +376,14 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) return end select + ! Initialize stream data type + streamfilename = 'datm.streams'//trim(inst_suffix) +#ifndef DISABLE_FoX + streamfilename = trim(streamfilename)//'.xml' +#endif + call shr_strdata_init_advertise(sdat, streamfilename, 'ATM', logunit, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + ! Advertise fields that ARE NOT datamode specific if (flds_co2) then call datm_pres_co2_advertise(fldsExport, datamode) @@ -460,12 +468,7 @@ subroutine InitializeRealize(gcomp, importState, exportState, clock, rc) model_meshfile, model_maskfile, model_mesh, model_mask, model_frac, restart_read, rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return - ! Initialize stream data type - streamfilename = 'datm.streams'//trim(inst_suffix) -#ifndef DISABLE_FoX - streamfilename = trim(streamfilename)//'.xml' -#endif - call shr_strdata_init_from_config(sdat, streamfilename, model_mesh, clock, 'ATM', logunit, rc=rc) + call shr_strdata_init_realize(sdat, model_mesh, clock, rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return call ESMF_TraceRegionExit('datm_strdata_init') From 62850a6f2148ab60aa52d4578bee45b76cbe8053 Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Mon, 8 Jun 2026 13:32:14 -0400 Subject: [PATCH 07/19] Add use calls for subroutines in datm/atm_comp_nuopc --- datm/atm_comp_nuopc.F90 | 1 + 1 file changed, 1 insertion(+) diff --git a/datm/atm_comp_nuopc.F90 b/datm/atm_comp_nuopc.F90 index 5053de7c9..38a7f7ae3 100644 --- a/datm/atm_comp_nuopc.F90 +++ b/datm/atm_comp_nuopc.F90 @@ -32,6 +32,7 @@ module cdeps_datm_comp use shr_log_mod , only : shr_log_setLogUnit, shr_log_error use dshr_methods_mod , only : dshr_state_diagnose, chkerr, memcheck use dshr_strdata_mod , only : shr_strdata_type, shr_strdata_init_from_config, shr_strdata_advance + use dshr_strdata_mod , only : shr_strdata_init_advertise, shr_strdata_init_realize use dshr_strdata_mod , only : shr_strdata_get_stream_pointer, shr_strdata_setOrbs use dshr_mod , only : dshr_model_initphase, dshr_init, dshr_restart_write use dshr_mod , only : dshr_state_setscalar, dshr_set_runclock, dshr_log_clock_advance From 79a362a327d6a1d1f4701c5ffb832ee047bfc6e7 Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Mon, 8 Jun 2026 14:18:35 -0400 Subject: [PATCH 08/19] Update share/shr_generic_mod.F90 --- share/shr_generic_mod.F90 | 187 ++++++++++++++++++++++---------------- 1 file changed, 108 insertions(+), 79 deletions(-) diff --git a/share/shr_generic_mod.F90 b/share/shr_generic_mod.F90 index e9b6d80bd..c5637d07f 100644 --- a/share/shr_generic_mod.F90 +++ b/share/shr_generic_mod.F90 @@ -2,113 +2,142 @@ module shr_generic_mod use ESMF use NUOPC - use shr_strdata_mod, only : shr_strdata_type, shr_strdata_perstream, & - shr_strdata_get_stream_count, shr_strdata_get_stream, & - shr_strdata_advance, shr_strdata_get_stream_pointer - use dshr_state_mod, only : dshr_state_getfldptr + use dshr_fldlist_mod, only : fldlist_type, dshr_fldlist_add + use dshr_strdata_mod, only : shr_strdata_type, shr_strdata_get_stream_pointer + use dshr_state_mod, only : dshr_state_getfldptr implicit none private public :: datamode_generic_advertise + public :: datamode_generic_init_pointers public :: datamode_generic_advance + ! ----------------------------------------------------------------------- + ! Derived type to cache the pointer pairs dynamically for the Advance loop + ! ----------------------------------------------------------------------- + type :: ptr_map_type + real(ESMF_KIND_R8), pointer :: strm_ptr(:) => null() + real(ESMF_KIND_R8), pointer :: state_ptr(:) => null() + end type ptr_map_type + + ! Module-level cache array + type(ptr_map_type), allocatable :: ptr_cache(:) + contains ! ======================================================================= - ! ROUTINE: datamode_generic_advertise - ! PURPOSE: Dynamically loop through the stream configuration (XML or ESMF) - ! and advertise exactly the fields the user explicitly requested. - ! ======================================================================= - subroutine datamode_generic_advertise(exportState, sdat, rc) - type(ESMF_State), intent(inout) :: exportState - type(shr_strdata_type), intent(inout) :: sdat - integer, intent(out), optional :: rc + subroutine datamode_generic_advertise(fldsExport, sdat, rc) + type(fldList_type), pointer :: fldsExport + type(shr_strdata_type), intent(in) :: sdat + integer, intent(out), optional :: rc - integer :: n, i, num_streams + integer :: i, n character(len=ESMF_MAXSTR) :: fieldName - type(shr_strdata_perstream), pointer :: stream_ptr if (present(rc)) rc = ESMF_SUCCESS - ! Retrieve the total number of streams defined in the user's config - num_streams = shr_strdata_get_stream_count(sdat) - - do n = 1, num_streams - ! Get the pointer to the specific stream object - stream_ptr => shr_strdata_get_stream(sdat, n) - - ! Loop over the array of model field names parsed from the stream config - if (associated(stream_ptr%fldlist_model)) then - do i = 1, size(stream_ptr%fldlist_model) - fieldName = trim(stream_ptr%fldlist_model(i)) + ! Natively access the array of stream objects inside sdat + if (allocated(sdat%stream)) then + do i = 1, size(sdat%stream) + if (sdat%stream(i)%nvars > 0 .and. allocated(sdat%stream(i)%varlist)) then - call NUOPC_StateAdvertise(exportState, StandardName=trim(fieldName), rc=rc) - if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU)) return - end do - endif - end do + ! Extract the 'nameinmodel' string directly from the varlist + do n = 1, sdat%stream(i)%nvars + fieldName = trim(sdat%stream(i)%varlist(n)%nameinmodel) + + ! Add to the CDEPS list (which handles the NUOPC advertise) + call dshr_fldlist_add(fldsExport, trim(fieldName), 'scalar', rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU)) return + end do + + endif + end do + endif end subroutine datamode_generic_advertise + ! ======================================================================= - ! ROUTINE: datamode_generic_advance - ! PURPOSE: Advances the stream reader, interpolates data in time, and - ! dynamically copies data from the internal stream buffers - ! directly to the NUOPC export state with ZERO math or physics. - ! ======================================================================= - subroutine datamode_generic_advance(exportState, sdat, year, month, day, sec, rc) + subroutine datamode_generic_init_pointers(exportState, sdat, rc) type(ESMF_State), intent(inout) :: exportState - type(shr_strdata_type), intent(inout) :: sdat - integer, intent(in) :: year, month, day, sec - integer, intent(out), optional :: rc + type(shr_strdata_type), intent(in) :: sdat + integer, intent(out), optional :: rc - integer :: n, i, num_streams + integer :: i, n, total_vars, cache_idx character(len=ESMF_MAXSTR) :: fieldName - type(shr_strdata_perstream), pointer :: stream_ptr - - ! Pointers for the explicit pass-through copy - real(ESMF_KIND_R8), pointer :: strm_ptr(:) - real(ESMF_KIND_R8), pointer :: state_ptr(:) + character(len=ESMF_MAXSTR) :: logMsg if (present(rc)) rc = ESMF_SUCCESS - ! 1. Advance the stream (Handles NetCDF I/O and time interpolation) - call shr_strdata_advance(sdat, year, month, day, sec, rc=rc) - if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU)) return + ! 1. Count the total number of fields to allocate the cache + total_vars = 0 + if (allocated(sdat%stream)) then + do i = 1, size(sdat%stream) + if (allocated(sdat%stream(i)%varlist)) then + total_vars = total_vars + sdat%stream(i)%nvars + endif + end do + endif + + ! Allocate the module-level pointer cache + if (allocated(ptr_cache)) deallocate(ptr_cache) + if (total_vars > 0) allocate(ptr_cache(total_vars)) + + ! Populate the cache and log fieldName diagnostics + cache_idx = 1 + if (allocated(sdat%stream)) then + do i = 1, size(sdat%stream) + if (allocated(sdat%stream(i)%varlist)) then + do n = 1, sdat%stream(i)%nvars + fieldName = trim(sdat%stream(i)%varlist(n)%nameinmodel) + + ! Look up stream array pointer + call shr_strdata_get_stream_pointer(sdat, fieldName, & + ptr_cache(cache_idx)%strm_ptr, rc=rc) + + ! Look up NUOPC export array pointer (allow null if CMEPS didn't connect it) + call dshr_state_getfldptr(exportState, fieldName, & + fldptr1=ptr_cache(cache_idx)%state_ptr, & + allowNullReturn=.true., rc=rc) + + ! Diagnostic Logging + if (.not. associated(ptr_cache(cache_idx)%state_ptr)) then + write(logMsg, '(A,A,A)') "GENERIC Datamode INFO: field '", trim(fieldName), & + "' ignored. Not requested by CMEPS mediator." + call ESMF_LogWrite(trim(logMsg), ESMF_LOGMSG_INFO) + endif + + if (.not. associated(ptr_cache(cache_idx)%strm_ptr)) then + write(logMsg, '(A,A,A)') "GENERIC Datamode WARNING: field '", trim(fieldName), & + "' missing from internal stream buffer." + call ESMF_LogWrite(trim(logMsg), ESMF_LOGMSG_WARNING) + endif + + cache_idx = cache_idx + 1 + end do + endif + end do + endif + + end subroutine datamode_generic_init_pointers - ! 2. Dynamically fetch pointers and copy data to the NUOPC Export State - num_streams = shr_strdata_get_stream_count(sdat) - do n = 1, num_streams - stream_ptr => shr_strdata_get_stream(sdat, n) - - if (associated(stream_ptr%fldlist_model)) then - do i = 1, size(stream_ptr%fldlist_model) - fieldName = trim(stream_ptr%fldlist_model(i)) - - ! Nullify pointers to ensure clean association - strm_ptr => null() - state_ptr => null() - - ! Fetch pointer to the internal shr_strdata memory buffer - call shr_strdata_get_stream_pointer(sdat, fieldName, strm_ptr, rc=rc) - if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU)) return - - ! Fetch pointer to the NUOPC Export State ESMF_Field memory - call dshr_state_getfldptr(exportState, fieldName, fldptr1=state_ptr, & - allowNullReturn=.true., rc=rc) - if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU)) return - - ! 3. The Core Pass-Through Logic: Direct Copy - ! If CMEPS accepted the field, the state pointer will be associated. - if (associated(strm_ptr) .and. associated(state_ptr)) then - state_ptr(:) = strm_ptr(:) - endif - - end do - endif - end do + ! ======================================================================= + subroutine datamode_generic_advance(rc) + integer, intent(out), optional :: rc + + integer :: i + + if (present(rc)) rc = ESMF_SUCCESS + + if (allocated(ptr_cache)) then + do i = 1, size(ptr_cache) + if (associated(ptr_cache(i)%strm_ptr) .and. associated(ptr_cache(i)%state_ptr)) then + ptr_cache(i)%state_ptr(:) = ptr_cache(i)%strm_ptr(:) + endif + end do + endif end subroutine datamode_generic_advance From bd59aa92800a89bb5787eba0cb8a17729cf1776f Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Mon, 8 Jun 2026 14:24:58 -0400 Subject: [PATCH 09/19] Update GENERIC datamode calls in datm/atm_comp_nuopc.F90 --- datm/atm_comp_nuopc.F90 | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/datm/atm_comp_nuopc.F90 b/datm/atm_comp_nuopc.F90 index 38a7f7ae3..ed3cd455e 100644 --- a/datm/atm_comp_nuopc.F90 +++ b/datm/atm_comp_nuopc.F90 @@ -85,6 +85,7 @@ module cdeps_datm_comp use datm_pres_co2_mod , only : datm_pres_co2_advance use shr_generic_mod , only : datamode_generic_advertise + use shr_generic_mod , only : datamode_generic_init_pointers use shr_generic_mod , only : datamode_generic_advance implicit none @@ -370,7 +371,7 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) select case (trim(datamode)) case ('CORE2_NYF','CORE2_IAF','CORE_IAF_JRA', & 'CORE_RYF6162_JRA','CORE_RYF8485_JRA','CORE_RYF9091_JRA','CORE_RYF0304_JRA', & - 'CLMNCEP','CPLHIST','GEFS','ERA5','SIMPLE') + 'CLMNCEP','CPLHIST','GEFS','ERA5','SIMPLE','GENERIC') if (mainproc) write(logunit,'(3a)') subname,'datm datamode = ',trim(datamode) case default call shr_log_error(' ERROR illegal datm datamode = '//trim(datamode), rc=rc) @@ -424,7 +425,7 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) nlfilename, my_task, vm, rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return case ('GENERIC') - call datamode_generic_advertise(exportState, sdat, rc) + call datamode_generic_advertise(fldsExport, sdat, rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return end select @@ -687,6 +688,9 @@ subroutine datm_comp_run(gcomp, importState, exportState, target_ymd, target_tod case('SIMPLE') call datm_datamode_simple_init_pointers(exportState, sdat, rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return + case('GENERIC') + call datamode_generic_init_pointers(exportState, sdat, rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return end select ! Read restart if needed @@ -697,7 +701,7 @@ subroutine datm_comp_run(gcomp, importState, exportState, target_ymd, target_tod case('CORE2_NYF','CORE2_IAF','CORE_IAF_JRA',& 'CORE_RYF6162_JRA','CORE_RYF8485_JRA' ,& 'CORE_RYF9091_JRA','CORE_RYF0304_JRA' ,& - 'CLMNCEP','CPLHIST','ERA5','GEFS','SIMPLE') + 'CLMNCEP','CPLHIST','ERA5','GEFS','SIMPLE','GENERIC') call dshr_restart_read(restfilm, rpfile, logunit, my_task, mpicom, sdat, rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return case default @@ -766,7 +770,7 @@ subroutine datm_comp_run(gcomp, importState, exportState, target_ymd, target_tod call datm_datamode_simple_advance(target_ymd, target_tod, target_mon, sdat%model_calendar, rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return case('GENERIC') - call datamode_generic_advance(exportState, sdat, rc) + call datamode_generic_advance(rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return end select From 2a7732603e6c18a91a868512bcc6826c466b3eda Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:11:22 -0400 Subject: [PATCH 10/19] Write restart for generic too in datm/atm_comp_nuopc.F90 --- datm/atm_comp_nuopc.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datm/atm_comp_nuopc.F90 b/datm/atm_comp_nuopc.F90 index ed3cd455e..283b58d8a 100644 --- a/datm/atm_comp_nuopc.F90 +++ b/datm/atm_comp_nuopc.F90 @@ -782,7 +782,7 @@ subroutine datm_comp_run(gcomp, importState, exportState, target_ymd, target_tod case('CORE2_NYF','CORE2_IAF','CORE_IAF_JRA',& 'CORE_RYF6162_JRA','CORE_RYF8485_JRA' ,& 'CORE_RYF9091_JRA','CORE_RYF0304_JRA' ,& - 'CLMNCEP','CPLHIST','ERA5','GEFS','SIMPLE') + 'CLMNCEP','CPLHIST','ERA5','GEFS','SIMPLE','GENERIC') call dshr_restart_write(rpfile, case_name, 'datm', inst_suffix, & target_ymd, target_tod, logunit, my_task, sdat, rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return From d25db510e4b60b96ac6908055b71e202cf2e01d5 Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:12:54 -0400 Subject: [PATCH 11/19] Fix dshr_fldlist_add call in share/shr_generic_mod.F90 --- share/shr_generic_mod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/shr_generic_mod.F90 b/share/shr_generic_mod.F90 index c5637d07f..ac48deee5 100644 --- a/share/shr_generic_mod.F90 +++ b/share/shr_generic_mod.F90 @@ -47,7 +47,7 @@ subroutine datamode_generic_advertise(fldsExport, sdat, rc) fieldName = trim(sdat%stream(i)%varlist(n)%nameinmodel) ! Add to the CDEPS list (which handles the NUOPC advertise) - call dshr_fldlist_add(fldsExport, trim(fieldName), 'scalar', rc=rc) + call dshr_fldlist_add(fldsExport, trim(fieldName)) if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU)) return end do From 9af744a326da11fbbf17f31987f9de6d03b446aa Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:42:36 -0400 Subject: [PATCH 12/19] use esmf, only ... in share/shr_generic_mod.F90 --- share/shr_generic_mod.F90 | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/share/shr_generic_mod.F90 b/share/shr_generic_mod.F90 index ac48deee5..1b2a13cb1 100644 --- a/share/shr_generic_mod.F90 +++ b/share/shr_generic_mod.F90 @@ -1,7 +1,8 @@ module shr_generic_mod - use ESMF - use NUOPC + use ESMF , only : ESMF_SUCCESS, ESMF_State, & + ESMF_LogWrite, ESMF_LOGMSG_INFO, ESMF_LOGMSG_WARNING + use shr_kind_mod , only : r8=>shr_kind_r8, cl=>shr_kind_cl use dshr_fldlist_mod, only : fldlist_type, dshr_fldlist_add use dshr_strdata_mod, only : shr_strdata_type, shr_strdata_get_stream_pointer use dshr_state_mod, only : dshr_state_getfldptr @@ -17,8 +18,8 @@ module shr_generic_mod ! Derived type to cache the pointer pairs dynamically for the Advance loop ! ----------------------------------------------------------------------- type :: ptr_map_type - real(ESMF_KIND_R8), pointer :: strm_ptr(:) => null() - real(ESMF_KIND_R8), pointer :: state_ptr(:) => null() + real(r8), pointer :: strm_ptr(:) => null() + real(r8), pointer :: state_ptr(:) => null() end type ptr_map_type ! Module-level cache array @@ -30,10 +31,10 @@ module shr_generic_mod subroutine datamode_generic_advertise(fldsExport, sdat, rc) type(fldList_type), pointer :: fldsExport type(shr_strdata_type), intent(in) :: sdat - integer, intent(out), optional :: rc + integer, intent(out), optional :: rc integer :: i, n - character(len=ESMF_MAXSTR) :: fieldName + character(len=CL) :: fieldName if (present(rc)) rc = ESMF_SUCCESS @@ -48,7 +49,6 @@ subroutine datamode_generic_advertise(fldsExport, sdat, rc) ! Add to the CDEPS list (which handles the NUOPC advertise) call dshr_fldlist_add(fldsExport, trim(fieldName)) - if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU)) return end do endif @@ -57,7 +57,6 @@ subroutine datamode_generic_advertise(fldsExport, sdat, rc) end subroutine datamode_generic_advertise - ! ======================================================================= subroutine datamode_generic_init_pointers(exportState, sdat, rc) type(ESMF_State), intent(inout) :: exportState @@ -65,8 +64,8 @@ subroutine datamode_generic_init_pointers(exportState, sdat, rc) integer, intent(out), optional :: rc integer :: i, n, total_vars, cache_idx - character(len=ESMF_MAXSTR) :: fieldName - character(len=ESMF_MAXSTR) :: logMsg + character(len=CL) :: fieldName + character(len=CL) :: logMsg if (present(rc)) rc = ESMF_SUCCESS From 30927397977ccf6c2a31f35d27ec70338566dd3f Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Tue, 9 Jun 2026 14:12:04 -0400 Subject: [PATCH 13/19] add datamode_generic_clean to share/shr_generic_mod.F90 --- share/shr_generic_mod.F90 | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/share/shr_generic_mod.F90 b/share/shr_generic_mod.F90 index 1b2a13cb1..bf8566a13 100644 --- a/share/shr_generic_mod.F90 +++ b/share/shr_generic_mod.F90 @@ -13,6 +13,7 @@ module shr_generic_mod public :: datamode_generic_advertise public :: datamode_generic_init_pointers public :: datamode_generic_advance + public :: datamode_generic_clean ! ----------------------------------------------------------------------- ! Derived type to cache the pointer pairs dynamically for the Advance loop @@ -140,4 +141,16 @@ subroutine datamode_generic_advance(rc) end subroutine datamode_generic_advance + ! ======================================================================= + subroutine datamode_generic_clean(rc) + integer, intent(out), optional :: rc + + if (present(rc)) rc = ESMF_SUCCESS + + if (allocated(ptr_cache)) then + deallocate(ptr_cache) + endif + + end subroutine datamode_generic_clean + end module shr_generic_mod From f4ce084d9b8bbaa13662c56b194a6301fe157678 Mon Sep 17 00:00:00 2001 From: Nick Szapiro Date: Tue, 9 Jun 2026 15:36:58 -0400 Subject: [PATCH 14/19] Try in dshr --- datm/atm_comp_nuopc.F90 | 6 +++--- dshr/CMakeLists.txt | 3 ++- share/shr_generic_mod.F90 => dshr/dshr_generic_mod.F90 | 4 ++-- share/CMakeLists.txt | 1 - 4 files changed, 7 insertions(+), 7 deletions(-) rename share/shr_generic_mod.F90 => dshr/dshr_generic_mod.F90 (99%) diff --git a/datm/atm_comp_nuopc.F90 b/datm/atm_comp_nuopc.F90 index 283b58d8a..723c1d865 100644 --- a/datm/atm_comp_nuopc.F90 +++ b/datm/atm_comp_nuopc.F90 @@ -84,9 +84,9 @@ module cdeps_datm_comp use datm_pres_co2_mod , only : datm_pres_co2_init_pointers use datm_pres_co2_mod , only : datm_pres_co2_advance - use shr_generic_mod , only : datamode_generic_advertise - use shr_generic_mod , only : datamode_generic_init_pointers - use shr_generic_mod , only : datamode_generic_advance + use dshr_generic_mod , only : datamode_generic_advertise + use dshr_generic_mod , only : datamode_generic_init_pointers + use dshr_generic_mod , only : datamode_generic_advance implicit none private diff --git a/dshr/CMakeLists.txt b/dshr/CMakeLists.txt index 5e29eb600..597aa6d83 100644 --- a/dshr/CMakeLists.txt +++ b/dshr/CMakeLists.txt @@ -2,7 +2,8 @@ project(dshr Fortran) set(SRCFILES dshr_dfield_mod.F90 dshr_fldlist_mod.F90 - dshr_mod.F90) + dshr_mod.F90 + dshr_generic_mod.F90) foreach(FILE ${SRCFILES}) if(EXISTS "${CASEROOT}/SourceMods/src.cdeps/${FILE}") diff --git a/share/shr_generic_mod.F90 b/dshr/dshr_generic_mod.F90 similarity index 99% rename from share/shr_generic_mod.F90 rename to dshr/dshr_generic_mod.F90 index bf8566a13..542039a2b 100644 --- a/share/shr_generic_mod.F90 +++ b/dshr/dshr_generic_mod.F90 @@ -1,4 +1,4 @@ -module shr_generic_mod +module dshr_generic_mod use ESMF , only : ESMF_SUCCESS, ESMF_State, & ESMF_LogWrite, ESMF_LOGMSG_INFO, ESMF_LOGMSG_WARNING @@ -153,4 +153,4 @@ subroutine datamode_generic_clean(rc) end subroutine datamode_generic_clean -end module shr_generic_mod +end module dshr_generic_mod diff --git a/share/CMakeLists.txt b/share/CMakeLists.txt index bf7e17a9a..fcf3c4e74 100644 --- a/share/CMakeLists.txt +++ b/share/CMakeLists.txt @@ -21,7 +21,6 @@ add_library(cdeps_share ${GenF90_SRCS} shr_precip_mod.F90 shr_string_mod.F90 shr_is_restart_fh_mod.F90 - shr_generic_mod.F90 nuopc_shr_methods.F90) target_include_directories (cdeps_share PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${ESMF_F90COMPILEPATHS} ${PIO_Fortran_INCLUDE_DIRS}) From 7c171f54ed0fc185ce04ff3a0d23e86bde56d9af Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Tue, 9 Jun 2026 15:50:46 -0400 Subject: [PATCH 15/19] Fix typo use dshr_methods_mod --- dshr/dshr_generic_mod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dshr/dshr_generic_mod.F90 b/dshr/dshr_generic_mod.F90 index 542039a2b..9f903bbee 100644 --- a/dshr/dshr_generic_mod.F90 +++ b/dshr/dshr_generic_mod.F90 @@ -5,7 +5,7 @@ module dshr_generic_mod use shr_kind_mod , only : r8=>shr_kind_r8, cl=>shr_kind_cl use dshr_fldlist_mod, only : fldlist_type, dshr_fldlist_add use dshr_strdata_mod, only : shr_strdata_type, shr_strdata_get_stream_pointer - use dshr_state_mod, only : dshr_state_getfldptr + use dshr_methods_mod, only : dshr_state_getfldptr implicit none private From d64b4b2a858aabc448c38a261c234300a689e080 Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Tue, 9 Jun 2026 18:09:21 -0400 Subject: [PATCH 16/19] Fix associated(sdat%stream) (not allocated) in dshr/dshr_generic_mod.F90 --- dshr/dshr_generic_mod.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dshr/dshr_generic_mod.F90 b/dshr/dshr_generic_mod.F90 index 9f903bbee..690a51410 100644 --- a/dshr/dshr_generic_mod.F90 +++ b/dshr/dshr_generic_mod.F90 @@ -40,7 +40,7 @@ subroutine datamode_generic_advertise(fldsExport, sdat, rc) if (present(rc)) rc = ESMF_SUCCESS ! Natively access the array of stream objects inside sdat - if (allocated(sdat%stream)) then + if (associated(sdat%stream)) then do i = 1, size(sdat%stream) if (sdat%stream(i)%nvars > 0 .and. allocated(sdat%stream(i)%varlist)) then @@ -72,7 +72,7 @@ subroutine datamode_generic_init_pointers(exportState, sdat, rc) ! 1. Count the total number of fields to allocate the cache total_vars = 0 - if (allocated(sdat%stream)) then + if (associated(sdat%stream)) then do i = 1, size(sdat%stream) if (allocated(sdat%stream(i)%varlist)) then total_vars = total_vars + sdat%stream(i)%nvars @@ -86,7 +86,7 @@ subroutine datamode_generic_init_pointers(exportState, sdat, rc) ! Populate the cache and log fieldName diagnostics cache_idx = 1 - if (allocated(sdat%stream)) then + if (associated(sdat%stream)) then do i = 1, size(sdat%stream) if (allocated(sdat%stream(i)%varlist)) then do n = 1, sdat%stream(i)%nvars From 06965cfebac03b83ece74e2b428160723503ca8a Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Wed, 10 Jun 2026 12:50:01 -0400 Subject: [PATCH 17/19] Add chkerr in dshr/dshr_generic_mod.F90 --- dshr/dshr_generic_mod.F90 | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/dshr/dshr_generic_mod.F90 b/dshr/dshr_generic_mod.F90 index 690a51410..e6d729580 100644 --- a/dshr/dshr_generic_mod.F90 +++ b/dshr/dshr_generic_mod.F90 @@ -5,7 +5,7 @@ module dshr_generic_mod use shr_kind_mod , only : r8=>shr_kind_r8, cl=>shr_kind_cl use dshr_fldlist_mod, only : fldlist_type, dshr_fldlist_add use dshr_strdata_mod, only : shr_strdata_type, shr_strdata_get_stream_pointer - use dshr_methods_mod, only : dshr_state_getfldptr + use dshr_methods_mod, only : dshr_state_getfldptr, chkerr implicit none private @@ -26,6 +26,9 @@ module dshr_generic_mod ! Module-level cache array type(ptr_map_type), allocatable :: ptr_cache(:) + character(len=*) , parameter :: u_FILE_u = & + __FILE__ + contains ! ======================================================================= @@ -95,12 +98,14 @@ subroutine datamode_generic_init_pointers(exportState, sdat, rc) ! Look up stream array pointer call shr_strdata_get_stream_pointer(sdat, fieldName, & ptr_cache(cache_idx)%strm_ptr, rc=rc) - + if (ChkErr(rc,__LINE__,u_FILE_u)) return + ! Look up NUOPC export array pointer (allow null if CMEPS didn't connect it) call dshr_state_getfldptr(exportState, fieldName, & fldptr1=ptr_cache(cache_idx)%state_ptr, & allowNullReturn=.true., rc=rc) - + if (ChkErr(rc,__LINE__,u_FILE_u)) return + ! Diagnostic Logging if (.not. associated(ptr_cache(cache_idx)%state_ptr)) then write(logMsg, '(A,A,A)') "GENERIC Datamode INFO: field '", trim(fieldName), & From 40b98c8bfd2363b662940ee6e2cf4575a4e52aa9 Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Wed, 10 Jun 2026 12:57:28 -0400 Subject: [PATCH 18/19] rc not optional in datamode_generic_init_pointers --- dshr/dshr_generic_mod.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dshr/dshr_generic_mod.F90 b/dshr/dshr_generic_mod.F90 index e6d729580..888d4688e 100644 --- a/dshr/dshr_generic_mod.F90 +++ b/dshr/dshr_generic_mod.F90 @@ -65,13 +65,13 @@ end subroutine datamode_generic_advertise subroutine datamode_generic_init_pointers(exportState, sdat, rc) type(ESMF_State), intent(inout) :: exportState type(shr_strdata_type), intent(in) :: sdat - integer, intent(out), optional :: rc + integer, intent(out), :: rc integer :: i, n, total_vars, cache_idx character(len=CL) :: fieldName character(len=CL) :: logMsg - if (present(rc)) rc = ESMF_SUCCESS + rc = ESMF_SUCCESS ! 1. Count the total number of fields to allocate the cache total_vars = 0 From 4a2ad209add54fbac4d5fe5a51ce2924a7916b7c Mon Sep 17 00:00:00 2001 From: Nicholas Szapiro <149816583+NickSzapiro-NOAA@users.noreply.github.com> Date: Wed, 10 Jun 2026 12:59:38 -0400 Subject: [PATCH 19/19] typo --- dshr/dshr_generic_mod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dshr/dshr_generic_mod.F90 b/dshr/dshr_generic_mod.F90 index 888d4688e..6cf832ff7 100644 --- a/dshr/dshr_generic_mod.F90 +++ b/dshr/dshr_generic_mod.F90 @@ -65,7 +65,7 @@ end subroutine datamode_generic_advertise subroutine datamode_generic_init_pointers(exportState, sdat, rc) type(ESMF_State), intent(inout) :: exportState type(shr_strdata_type), intent(in) :: sdat - integer, intent(out), :: rc + integer, intent(out) :: rc integer :: i, n, total_vars, cache_idx character(len=CL) :: fieldName