Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions src/core_init_atmosphere/Registry.xml
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,11 @@
description="Whether to use specific-humidity as the first-guess moisture variable. If this option is False, relative humidity will be used."
possible_values="true or false"/>

<nml_option name="config_active_hydrometeors" type="character" default_value="detect_automatically"
units="-"
description="The set of hydrometeor species that are to be processed from input intermediate files when generating ICs and LBCs for real-data simulations."
possible_values="`detect_automatically', or a semicolon-separated list of hydrometeor names (e.g., `qc;qr'"/>

</nml_record>

<nml_record name="vertical_grid" in_defaults="true">
Expand Down Expand Up @@ -387,6 +392,8 @@
<package name="first_guess_field" description="3-d atmospheric or land-surface fields on first-guess levels"/>
<package name="microphysics_aerosols" description="Initialization of GOCART-based water- and ice-friendly aerosols for Thompson and TEMPO microphysics"/>
<package name="noahmp" description="Whether to process, read, and write static fields only required by Noah-MP"/>
<package name="qc" description="Whether QC is available for inclusion in ICs and LBCs"/>
<package name="qr" description="Whether QR is available for inclusion in ICs and LBCs"/>
</packages>


Expand Down Expand Up @@ -1159,10 +1166,12 @@
description="Water vapor mixing ratio"/>

<var name="qc" array_group="moist" units="kg kg^{-1}"
description="Cloud water mixing ratio"/>
description="Cloud water mixing ratio"
packages="qc"/>

<var name="qr" array_group="moist" units="kg kg^{-1}"
description="Rain water mixing ratio"/>
description="Rain water mixing ratio"
packages="qr"/>

<var name="nifa" array_group="number" units="nb kg^{-1}"
description="Gocart ice-friendly aerosol number concentration"
Expand Down Expand Up @@ -1204,10 +1213,12 @@
description="Water vapor mixing ratio on lateral boundary cells"/>

<var name="lbc_qc" array_group="moist" units="kg kg^{-1}"
description="Cloud water mixing ratio on lateral boundary cells"/>
description="Cloud water mixing ratio on lateral boundary cells"
packages="qc"/>

<var name="lbc_qr" array_group="moist" units="kg kg^{-1}"
description="Rain water mixing ratio on lateral boundary cells"/>
description="Rain water mixing ratio on lateral boundary cells"
packages="qr"/>

<var name="lbc_nifa" array_group="number" units="nb kg^{-1}"
description="Gocart ice-friendly aerosol number concentration on lateral boundary cells"
Expand Down
262 changes: 262 additions & 0 deletions src/core_init_atmosphere/mpas_init_atm_core_interface.F
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ module init_atm_core_interface
use mpas_io_units
use mpas_log, only : mpas_log_write

private :: setup_hydrometeor_packages, &
mpas_split_string_new

contains


Expand Down Expand Up @@ -316,9 +319,192 @@ function init_atm_setup_packages(configs, streamInfo, packages, iocontext, dminf
return
end if

if (config_init_case == 7 .or. config_init_case == 9) then
if (met_stage_out) then
call setup_hydrometeor_packages(packages, configs, dminfo, ierr)
end if
end if

end function init_atm_setup_packages


!***********************************************************************
!
! function setup_hydrometeor_packages
!
!> \brief Set up packages for hydrometeors based on contents of intermediate file
!> \author Michael Duda
!> \date 27 May 2026
!> \details
!> This routine is responsible for setting up packages for hydrometeor species (qc,
!> qr, etc.) based on the contents of the initial intermediate file (i.e., the
!> intermediate file valid at the start time of a simmulation; the assumption is
!> that all intermediate files contain the same set of fields. A hydrometeor
!> packages is set to true if and only if the intermediate file contains the
!> corresponding hydrometeor.
!>
!> If the setup of all hydrometeor packages is successful, a value of 0 is returned
!> in the ierr output argument; otherwise, a non-zero value is returned.
!
!-----------------------------------------------------------------------
subroutine setup_hydrometeor_packages(packages, configs, dminfo, ierr)

use init_atm_read_met, only : read_met_init, read_next_met_field, read_met_close, met_data
use mpas_timer, only : mpas_timer_start, mpas_timer_stop

implicit none

type (mpas_pool_type), intent(inout) :: packages
type (mpas_pool_type), intent(in) :: configs
type (dm_info), intent(in) :: dminfo
integer, intent(out) :: ierr

character(len=StrKIND), pointer :: config_active_hydrometeors
character(len=StrKIND), pointer :: config_met_prefix, config_start_time
integer :: istatus
type (met_data) :: field

integer :: i
character(len=:), dimension(:), allocatable :: hydrometeors

logical, pointer :: qc, qr


call mpas_timer_start('setup_hydrometeor_packages')

ierr = 0

call mpas_pool_get_config(configs, 'config_active_hydrometeors', config_active_hydrometeors)

if (.not. associated(config_active_hydrometeors)) then
call mpas_log_write( &
"The namelist option 'config_active_hydrometeors' could not be found when setting up hydrometeor packages.", &
messageType=MPAS_LOG_ERR)

ierr = 1
call mpas_timer_stop('setup_hydrometeor_packages')
return
end if

call mpas_pool_get_package(packages, 'qcActive', qc)
call mpas_pool_get_package(packages, 'qrActive', qr)

if (.not. associated(qc) &
.or. .not. associated(qr) &
) then

call mpas_log_write('One or more packages could not be found when setting up hydrometeor packages.', &
messageType=MPAS_LOG_ERR)

ierr = 1
call mpas_timer_stop('setup_hydrometeor_packages')
return
end if

qc = .false.
qr = .false.

if (trim(config_active_hydrometeors) == 'detect_automatically') then

call mpas_pool_get_config(configs, 'config_met_prefix', config_met_prefix)
call mpas_pool_get_config(configs, 'config_start_time', config_start_time)

if (.not. associated(config_met_prefix) &
.or. .not. associated(config_start_time) &
) then

call mpas_log_write( &
'One or more namelist options could not be found when setting up hydrometeor packages.', &
messageType=MPAS_LOG_ERR)

ierr = 1
call mpas_timer_stop('setup_hydrometeor_packages')
return
end if

call mpas_log_write('Setting up hydrometeor packages from '//trim(config_met_prefix)//':'//config_start_time(1:13))

if (dminfo % my_proc_id == IO_NODE) then
call read_met_init(trim(config_met_prefix), .false., config_start_time(1:13), ierr)

if (ierr == 0) then
call read_next_met_field(field, istatus)
else
call mpas_log_write('Could not open intermediate file ' &
//trim(config_met_prefix)//':'//config_start_time(1:13) &
//' to set up hydrometeor packages.', &
messageType=MPAS_LOG_ERR)
istatus = 1
end if

do while (istatus == 0)
if (trim(field % field) == 'QC') then
qc = .true.
else if (trim(field % field) == 'QR') then
qr = .true.
end if

deallocate(field % slab)

! If all packages are true, there is no point in scanning through the rest
! of the intermediate file, since this loop over fields can only switch packages
! from false to true.
if (qc .and. qr) exit

call read_next_met_field(field, istatus)
end do

call read_met_close()

call mpas_dmpar_bcast_int(dminfo, ierr)
call mpas_dmpar_bcast_logical(dminfo, qc)
call mpas_dmpar_bcast_logical(dminfo, qr)
else
call mpas_dmpar_bcast_int(dminfo, ierr)
call mpas_dmpar_bcast_logical(dminfo, qc)
call mpas_dmpar_bcast_logical(dminfo, qr)
end if

else

call mpas_log_write('Setting up hydrometeor packages from config_active_hydrometeors list: ' &
//trim(config_active_hydrometeors))

call mpas_split_string_new(trim(config_active_hydrometeors), ';', hydrometeors)

do i = 1, size(hydrometeors)
select case (trim(hydrometeors(i)))
case ('qc')
qc = .true.
case ('qr')
qr = .true.
case default
call mpas_log_write('Unrecognized hydrometeor '//trim(hydrometeors(i)) &
//' found in config_active_hydrometeors', &
messageType=MPAS_LOG_WARN)
end select
end do

deallocate(hydrometeors)

end if

if (ierr /= 0) then
call mpas_log_write('Failed to set up hydrometeor packages.', messageType=MPAS_LOG_ERR)
call mpas_timer_stop('setup_hydrometeor_packages')
return
end if

call mpas_log_write(' QC = $l', logicArgs=[qc])
call mpas_log_write(' QR = $l', logicArgs=[qr])
call mpas_log_write('----- done setting up hydrometeor packages -----')
call mpas_log_write('')

call mpas_timer_stop('setup_hydrometeor_packages')

end subroutine setup_hydrometeor_packages


!***********************************************************************
!
! function init_atm_setup_clock
Expand Down Expand Up @@ -523,6 +709,82 @@ function init_atm_setup_block(block) result(ierr)
end function init_atm_setup_block


!-----------------------------------------------------------------------
! routine mpas_split_string_new
!
!> \brief Splits a string at a specified delimiter, returning an array of strings
!> \author Michael Duda
!> \date 28 May 2026
!> \details
!> This routine takes as input a string and a delimiter character, and returns an
!> array of sub-strings from the input string that are separated by one or more
!> delimiter characters.
!>
!> If more than one delimiter character appears consecutively, no empty string
!> in the output subStrings array is generated.
!>
!> The length of the strings in the output subStrings argument is equal to the
!> length of the longest substring in the input string.
!
!-----------------------------------------------------------------------
subroutine mpas_split_string_new(string, delimiter, subStrings)

implicit none

! Arguments
character(len=*), intent(in) :: string
character, intent(in) :: delimiter
character(len=:), dimension(:), allocatable, intent(inout) :: subStrings

! Local variables
integer :: i, j, n_strs, strlen, max_strlen


i = 1
n_strs = 0
strlen = 0
max_strlen = 1
PARSE_LOOP: do while (i <= len(string))
do while (string(i:i) == delimiter)
i = i + 1
if (i > len(string)) exit PARSE_LOOP
end do

n_strs = n_strs + 1
strlen = 0
do while (string(i:i) /= delimiter)
i = i + 1
strlen = strlen + 1
if (i > len(string)) exit
end do
max_strlen = max(strlen, max_strlen)
end do PARSE_LOOP

if (allocated(subStrings)) deallocate(subStrings)
allocate(character(len=max_strlen) :: subStrings(n_strs))

i = 1
n_strs = 0
COPY_LOOP: do while (i <= len(string))
do while (string(i:i) == delimiter)
i = i + 1
if (i > len(string)) exit COPY_LOOP
end do

n_strs = n_strs + 1
j = 1
do while (string(i:i) /= delimiter)
subStrings(n_strs)(j:j) = string(i:i)
i = i + 1
j = j + 1
if (i > len(string)) exit
end do
subStrings(n_strs)(j:max_strlen) = ''
end do COPY_LOOP

end subroutine mpas_split_string_new


#include "setup_immutable_streams.inc"

#include "block_dimension_routines.inc"
Expand Down