physics_to_dynamics_coupling Subroutine

public subroutine physics_to_dynamics_coupling()

Uses

  • proc~~physics_to_dynamics_coupling~~UsesGraph proc~physics_to_dynamics_coupling physics_to_dynamics_coupling cam_logfile cam_logfile proc~physics_to_dynamics_coupling->cam_logfile module~dyn_comp dyn_comp proc~physics_to_dynamics_coupling->module~dyn_comp module~dyn_mpas_subdriver dyn_mpas_subdriver proc~physics_to_dynamics_coupling->module~dyn_mpas_subdriver shr_kind_mod shr_kind_mod proc~physics_to_dynamics_coupling->shr_kind_mod module~dyn_comp->module~dyn_mpas_subdriver iso_fortran_env iso_fortran_env module~dyn_mpas_subdriver->iso_fortran_env mpas_derived_types mpas_derived_types module~dyn_mpas_subdriver->mpas_derived_types mpas_kind_types mpas_kind_types module~dyn_mpas_subdriver->mpas_kind_types mpi mpi module~dyn_mpas_subdriver->mpi

Perform one-way coupling from the physics output states to the dynamics input states. The other coupling direction is implemented by its counterpart, dynamics_to_physics_coupling. (KCW, 2024-09-20)

Arguments

None

Calls

proc~~physics_to_dynamics_coupling~~CallsGraph proc~physics_to_dynamics_coupling physics_to_dynamics_coupling none~final_shared_variables~3 final_shared_variables proc~physics_to_dynamics_coupling->none~final_shared_variables~3 none~init_shared_variables~3 init_shared_variables proc~physics_to_dynamics_coupling->none~init_shared_variables~3 none~set_mpas_physics_tendency_rho set_mpas_physics_tendency_rho proc~physics_to_dynamics_coupling->none~set_mpas_physics_tendency_rho none~set_mpas_physics_tendency_rtheta set_mpas_physics_tendency_rtheta proc~physics_to_dynamics_coupling->none~set_mpas_physics_tendency_rtheta none~set_mpas_physics_tendency_ru set_mpas_physics_tendency_ru proc~physics_to_dynamics_coupling->none~set_mpas_physics_tendency_ru proc~dyn_debug_print dyn_debug_print proc~physics_to_dynamics_coupling->proc~dyn_debug_print proc~dyn_exchange_constituent_states dyn_exchange_constituent_states proc~physics_to_dynamics_coupling->proc~dyn_exchange_constituent_states none~init_shared_variables~3->proc~dyn_debug_print check_allocate check_allocate none~init_shared_variables~3->check_allocate none~get_variable_pointer mpas_dynamical_core_type%get_variable_pointer none~init_shared_variables~3->none~get_variable_pointer none~set_mpas_physics_tendency_rho->proc~dyn_debug_print none~set_mpas_physics_tendency_rho->none~get_variable_pointer proc~dyn_mpas_exchange_halo mpas_dynamical_core_type%dyn_mpas_exchange_halo none~set_mpas_physics_tendency_rho->proc~dyn_mpas_exchange_halo none~set_mpas_physics_tendency_rtheta->proc~dyn_debug_print none~set_mpas_physics_tendency_rtheta->check_allocate dtdt_total dtdt_total none~set_mpas_physics_tendency_rtheta->dtdt_total none~set_mpas_physics_tendency_rtheta->none~get_variable_pointer none~t_of_theta_rhod_qv t_of_theta_rhod_qv none~set_mpas_physics_tendency_rtheta->none~t_of_theta_rhod_qv none~theta_of_t_rhod_qv theta_of_t_rhod_qv none~set_mpas_physics_tendency_rtheta->none~theta_of_t_rhod_qv none~set_mpas_physics_tendency_rtheta->proc~dyn_mpas_exchange_halo proc~reverse reverse none~set_mpas_physics_tendency_rtheta->proc~reverse none~set_mpas_physics_tendency_ru->proc~dyn_debug_print dudt_total dudt_total none~set_mpas_physics_tendency_ru->dudt_total dvdt_total dvdt_total none~set_mpas_physics_tendency_ru->dvdt_total none~set_mpas_physics_tendency_ru->none~get_variable_pointer proc~dyn_mpas_compute_edge_wind mpas_dynamical_core_type%dyn_mpas_compute_edge_wind none~set_mpas_physics_tendency_ru->proc~dyn_mpas_compute_edge_wind none~set_mpas_physics_tendency_ru->proc~reverse stringify stringify proc~dyn_debug_print->stringify proc~dyn_exchange_constituent_states->proc~dyn_debug_print cam_constituents_array cam_constituents_array proc~dyn_exchange_constituent_states->cam_constituents_array proc~dyn_exchange_constituent_states->check_allocate const_is_dry const_is_dry proc~dyn_exchange_constituent_states->const_is_dry const_is_water_species const_is_water_species proc~dyn_exchange_constituent_states->const_is_water_species endrun endrun proc~dyn_exchange_constituent_states->endrun proc~dyn_exchange_constituent_states->none~get_variable_pointer pdel pdel proc~dyn_exchange_constituent_states->pdel pdeldry pdeldry proc~dyn_exchange_constituent_states->pdeldry proc~dyn_exchange_constituent_states->proc~dyn_mpas_exchange_halo proc~dyn_mpas_map_constituent_index mpas_dynamical_core_type%dyn_mpas_map_constituent_index proc~dyn_exchange_constituent_states->proc~dyn_mpas_map_constituent_index proc~dyn_mpas_map_mpas_scalar_index mpas_dynamical_core_type%dyn_mpas_map_mpas_scalar_index proc~dyn_exchange_constituent_states->proc~dyn_mpas_map_mpas_scalar_index proc~dyn_exchange_constituent_states->proc~reverse proc~dyn_mpas_get_variable_pointer_c0 mpas_dynamical_core_type%dyn_mpas_get_variable_pointer_c0 none~get_variable_pointer->proc~dyn_mpas_get_variable_pointer_c0 proc~dyn_mpas_get_variable_pointer_c1 mpas_dynamical_core_type%dyn_mpas_get_variable_pointer_c1 none~get_variable_pointer->proc~dyn_mpas_get_variable_pointer_c1 proc~dyn_mpas_get_variable_pointer_i0 mpas_dynamical_core_type%dyn_mpas_get_variable_pointer_i0 none~get_variable_pointer->proc~dyn_mpas_get_variable_pointer_i0 proc~dyn_mpas_get_variable_pointer_i1 mpas_dynamical_core_type%dyn_mpas_get_variable_pointer_i1 none~get_variable_pointer->proc~dyn_mpas_get_variable_pointer_i1 proc~dyn_mpas_get_variable_pointer_i2 mpas_dynamical_core_type%dyn_mpas_get_variable_pointer_i2 none~get_variable_pointer->proc~dyn_mpas_get_variable_pointer_i2 proc~dyn_mpas_get_variable_pointer_i3 mpas_dynamical_core_type%dyn_mpas_get_variable_pointer_i3 none~get_variable_pointer->proc~dyn_mpas_get_variable_pointer_i3 proc~dyn_mpas_get_variable_pointer_l0 mpas_dynamical_core_type%dyn_mpas_get_variable_pointer_l0 none~get_variable_pointer->proc~dyn_mpas_get_variable_pointer_l0 proc~dyn_mpas_get_variable_pointer_r0 mpas_dynamical_core_type%dyn_mpas_get_variable_pointer_r0 none~get_variable_pointer->proc~dyn_mpas_get_variable_pointer_r0 proc~dyn_mpas_get_variable_pointer_r1 mpas_dynamical_core_type%dyn_mpas_get_variable_pointer_r1 none~get_variable_pointer->proc~dyn_mpas_get_variable_pointer_r1 proc~dyn_mpas_get_variable_pointer_r2 mpas_dynamical_core_type%dyn_mpas_get_variable_pointer_r2 none~get_variable_pointer->proc~dyn_mpas_get_variable_pointer_r2 proc~dyn_mpas_get_variable_pointer_r3 mpas_dynamical_core_type%dyn_mpas_get_variable_pointer_r3 none~get_variable_pointer->proc~dyn_mpas_get_variable_pointer_r3 proc~dyn_mpas_get_variable_pointer_r4 mpas_dynamical_core_type%dyn_mpas_get_variable_pointer_r4 none~get_variable_pointer->proc~dyn_mpas_get_variable_pointer_r4 proc~dyn_mpas_get_variable_pointer_r5 mpas_dynamical_core_type%dyn_mpas_get_variable_pointer_r5 none~get_variable_pointer->proc~dyn_mpas_get_variable_pointer_r5 proc~dyn_mpas_compute_edge_wind->none~get_variable_pointer proc~dyn_mpas_compute_edge_wind->proc~dyn_mpas_exchange_halo proc~dyn_mpas_debug_print mpas_dynamical_core_type%dyn_mpas_debug_print proc~dyn_mpas_compute_edge_wind->proc~dyn_mpas_debug_print mpas_dmpar_exch_halo_field mpas_dmpar_exch_halo_field proc~dyn_mpas_exchange_halo->mpas_dmpar_exch_halo_field mpas_pool_get_field mpas_pool_get_field proc~dyn_mpas_exchange_halo->mpas_pool_get_field mpas_pool_get_field_info mpas_pool_get_field_info proc~dyn_mpas_exchange_halo->mpas_pool_get_field_info proc~dyn_mpas_exchange_halo->proc~dyn_mpas_debug_print proc~stringify stringify proc~dyn_mpas_exchange_halo->proc~stringify proc~dyn_mpas_debug_print->proc~stringify mpas_pool_get_array mpas_pool_get_array proc~dyn_mpas_get_variable_pointer_c0->mpas_pool_get_array mpas_pool_get_config mpas_pool_get_config proc~dyn_mpas_get_variable_pointer_c0->mpas_pool_get_config proc~dyn_mpas_get_pool_pointer mpas_dynamical_core_type%dyn_mpas_get_pool_pointer proc~dyn_mpas_get_variable_pointer_c0->proc~dyn_mpas_get_pool_pointer proc~dyn_mpas_get_variable_pointer_c1->mpas_pool_get_array proc~dyn_mpas_get_variable_pointer_c1->proc~dyn_mpas_get_pool_pointer proc~dyn_mpas_get_variable_pointer_i0->mpas_pool_get_array proc~dyn_mpas_get_variable_pointer_i0->mpas_pool_get_config mpas_pool_get_dimension mpas_pool_get_dimension proc~dyn_mpas_get_variable_pointer_i0->mpas_pool_get_dimension proc~dyn_mpas_get_variable_pointer_i0->proc~dyn_mpas_get_pool_pointer proc~dyn_mpas_get_variable_pointer_i1->mpas_pool_get_array proc~dyn_mpas_get_variable_pointer_i1->mpas_pool_get_dimension proc~dyn_mpas_get_variable_pointer_i1->proc~dyn_mpas_get_pool_pointer proc~dyn_mpas_get_variable_pointer_i2->mpas_pool_get_array proc~dyn_mpas_get_variable_pointer_i2->proc~dyn_mpas_get_pool_pointer proc~dyn_mpas_get_variable_pointer_i3->mpas_pool_get_array proc~dyn_mpas_get_variable_pointer_i3->proc~dyn_mpas_get_pool_pointer proc~dyn_mpas_get_variable_pointer_l0->mpas_pool_get_config proc~dyn_mpas_get_variable_pointer_l0->proc~dyn_mpas_get_pool_pointer proc~dyn_mpas_get_variable_pointer_r0->mpas_pool_get_array proc~dyn_mpas_get_variable_pointer_r0->mpas_pool_get_config proc~dyn_mpas_get_variable_pointer_r0->proc~dyn_mpas_get_pool_pointer proc~dyn_mpas_get_variable_pointer_r1->mpas_pool_get_array proc~dyn_mpas_get_variable_pointer_r1->proc~dyn_mpas_get_pool_pointer proc~dyn_mpas_get_variable_pointer_r2->mpas_pool_get_array proc~dyn_mpas_get_variable_pointer_r2->proc~dyn_mpas_get_pool_pointer proc~dyn_mpas_get_variable_pointer_r3->mpas_pool_get_array proc~dyn_mpas_get_variable_pointer_r3->proc~dyn_mpas_get_pool_pointer proc~dyn_mpas_get_variable_pointer_r4->mpas_pool_get_array proc~dyn_mpas_get_variable_pointer_r4->proc~dyn_mpas_get_pool_pointer proc~dyn_mpas_get_variable_pointer_r5->mpas_pool_get_array proc~dyn_mpas_get_variable_pointer_r5->proc~dyn_mpas_get_pool_pointer mpas_pool_get_subpool mpas_pool_get_subpool proc~dyn_mpas_get_pool_pointer->mpas_pool_get_subpool

Called by

proc~~physics_to_dynamics_coupling~~CalledByGraph proc~physics_to_dynamics_coupling physics_to_dynamics_coupling proc~stepon_run2 stepon_run2 proc~stepon_run2->proc~physics_to_dynamics_coupling

Variables

Type Visibility Attributes Name Initial
integer, private, pointer :: index_qv
real(kind=kind_r8), private, allocatable :: qv_prev(:,:)
real(kind=kind_dyn_mpas), private, pointer :: rho_zz(:,:)
real(kind=kind_dyn_mpas), private, pointer :: scalars(:,:,:)
character(len=*), private, parameter :: subname = 'dyn_coupling::physics_to_dynamics_coupling'
real(kind=kind_dyn_mpas), private, pointer :: zz(:,:)

Functions

pure elemental function t_of_theta_rhod_qv(theta, rhod, qv) result(t)

Compute temperature t as a function of potential temperature theta, dry air density rhod and water vapor mixing ratio qv. Essentially, . The formulation comes from Poisson equation with equation of state plugged in and arranging for temperature. This function is the exact inverse of theta_of_t_rhod_qv, which means that: t == t_of_theta_rhod_qv(theta_of_t_rhod_qv(t, rhod, qv), rhod, qv). (KCW, 2024-09-13)

Arguments

Type IntentOptional Attributes Name
real(kind=kind_r8), intent(in) :: theta
real(kind=kind_r8), intent(in) :: rhod
real(kind=kind_r8), intent(in) :: qv

Return Value real(kind=kind_r8)

pure elemental function theta_of_t_rhod_qv(t, rhod, qv) result(theta)

Compute potential temperature theta as a function of temperature t, dry air density rhod and water vapor mixing ratio qv. Essentially, . The formulation comes from Poisson equation with equation of state plugged in and arranging for potential temperature. This function is the exact inverse of t_of_theta_rhod_qv, which means that: theta == theta_of_t_rhod_qv(t_of_theta_rhod_qv(theta, rhod, qv), rhod, qv). (KCW, 2024-09-13)

Arguments

Type IntentOptional Attributes Name
real(kind=kind_r8), intent(in) :: t
real(kind=kind_r8), intent(in) :: rhod
real(kind=kind_r8), intent(in) :: qv

Return Value real(kind=kind_r8)


Subroutines

subroutine final_shared_variables()

Finalize variables that are shared and repeatedly used by the set_mpas_physics_tendency_* internal subroutines. (KCW, 2024-09-13)

Arguments

None

subroutine init_shared_variables()

Initialize variables that are shared and repeatedly used by the set_mpas_physics_tendency_* internal subroutines. (KCW, 2024-09-13)

Arguments

None

Set MPAS physics tendency tend_rho_physics (i.e., "coupled" tendency of dry air density due to physics). In MPAS, a "coupled" variable means that it is multiplied by a vertical metric term, rho_zz. (KCW, 2024-09-11)

Arguments

None

Set MPAS physics tendency tend_rtheta_physics (i.e., "coupled" tendency of modified "moist" potential temperature due to physics). In MPAS, a "coupled" variable means that it is multiplied by a vertical metric term, rho_zz. (KCW, 2024-09-19)

Arguments

None

Set MPAS physics tendency tend_ru_physics (i.e., "coupled" tendency of horizontal velocity at edge interfaces due to physics). In MPAS, a "coupled" variable means that it is multiplied by a vertical metric term, rho_zz. (KCW, 2024-09-11)

Arguments

None

Source Code

    subroutine physics_to_dynamics_coupling()
        ! Module(s) from CAM-SIMA.
        use cam_logfile, only: debugout_debug
        use dyn_comp, only: dyn_debug_print, dyn_exchange_constituent_states
        ! Module(s) from CESM Share.
        use shr_kind_mod, only: kind_r8 => shr_kind_r8
        ! Module(s) from MPAS.
        use dyn_mpas_subdriver, only: kind_dyn_mpas => mpas_dynamical_core_real_kind

        character(*), parameter :: subname = 'dyn_coupling::physics_to_dynamics_coupling'
        integer, pointer :: index_qv
        real(kind_r8), allocatable :: qv_prev(:, :) ! Water vapor mixing ratio (kg kg-1)
                                                    ! before being updated by physics.
        real(kind_dyn_mpas), pointer :: rho_zz(:, :)
        real(kind_dyn_mpas), pointer :: scalars(:, :, :)
        real(kind_dyn_mpas), pointer :: zz(:, :)

        call dyn_debug_print(debugout_debug, subname // ' entered')

        call init_shared_variables()

        call dyn_exchange_constituent_states(direction='e', exchange=.true., conversion=.true.)

        call set_mpas_physics_tendency_ru()
        call set_mpas_physics_tendency_rho()
        call set_mpas_physics_tendency_rtheta()

        call final_shared_variables()

        call dyn_debug_print(debugout_debug, subname // ' completed')
    contains
        !> Initialize variables that are shared and repeatedly used by the `set_mpas_physics_tendency_*` internal subroutines.
        !> (KCW, 2024-09-13)
        subroutine init_shared_variables()
            ! Module(s) from CAM-SIMA.
            use cam_abortutils, only: check_allocate
            use cam_logfile, only: debugout_info
            use dyn_comp, only: mpas_dynamical_core, ncells_solve
            use vert_coord, only: pver

            character(*), parameter :: subname = 'dyn_coupling::physics_to_dynamics_coupling::init_shared_variables'
            integer :: ierr

            call dyn_debug_print(debugout_info, 'Preparing for physics-dynamics coupling')

            nullify(index_qv)
            nullify(rho_zz)
            nullify(scalars)
            nullify(zz)

            call mpas_dynamical_core % get_variable_pointer(index_qv, 'dim', 'index_qv')
            call mpas_dynamical_core % get_variable_pointer(rho_zz, 'state', 'rho_zz', time_level=1)
            call mpas_dynamical_core % get_variable_pointer(scalars, 'state', 'scalars', time_level=1)
            call mpas_dynamical_core % get_variable_pointer(zz, 'mesh', 'zz')

            allocate(qv_prev(pver, ncells_solve), stat=ierr)
            call check_allocate(ierr, subname, &
                'qv_prev(pver, ncells_solve)', &
                'dyn_coupling', __LINE__)

            ! Save water vapor mixing ratio before being updated by physics because `set_mpas_physics_tendency_rtheta`
            ! needs it. This must be done before calling `dyn_exchange_constituent_states`.
            qv_prev(:, :) = real(scalars(index_qv, :, 1:ncells_solve), kind_r8)
        end subroutine init_shared_variables

        !> Finalize variables that are shared and repeatedly used by the `set_mpas_physics_tendency_*` internal subroutines.
        !> (KCW, 2024-09-13)
        subroutine final_shared_variables()
            character(*), parameter :: subname = 'dyn_coupling::physics_to_dynamics_coupling::final_shared_variables'

            deallocate(qv_prev)

            nullify(index_qv)
            nullify(rho_zz)
            nullify(scalars)
            nullify(zz)
        end subroutine final_shared_variables

        !> Set MPAS physics tendency `tend_ru_physics` (i.e., "coupled" tendency of horizontal velocity at edge interfaces
        !> due to physics). In MPAS, a "coupled" variable means that it is multiplied by a vertical metric term, `rho_zz`.
        !> (KCW, 2024-09-11)
        subroutine set_mpas_physics_tendency_ru()
            ! Module(s) from CAM-SIMA.
            use cam_logfile, only: debugout_info
            use dyn_comp, only: reverse, mpas_dynamical_core, ncells_solve
            use physics_types, only: phys_tend

            character(*), parameter :: subname = 'dyn_coupling::physics_to_dynamics_coupling::set_mpas_physics_tendency_ru'
            integer :: i
            real(kind_dyn_mpas), pointer :: u_tendency(:, :), v_tendency(:, :)

            call dyn_debug_print(debugout_info, 'Setting MPAS physics tendency "tend_ru_physics"')

            nullify(u_tendency, v_tendency)

            call mpas_dynamical_core % get_variable_pointer(u_tendency, 'tend_physics', 'tend_uzonal')
            call mpas_dynamical_core % get_variable_pointer(v_tendency, 'tend_physics', 'tend_umerid')

            ! Vertical index order is reversed between CAM-SIMA and MPAS.
            ! Always call `reverse` when assigning anything to/from the `physics_tend` derived type.
            do i = 1, ncells_solve
                u_tendency(:, i) = real(reverse(phys_tend % dudt_total(i, :)) * real(rho_zz(:, i), kind_r8), kind_dyn_mpas)
                v_tendency(:, i) = real(reverse(phys_tend % dvdt_total(i, :)) * real(rho_zz(:, i), kind_r8), kind_dyn_mpas)
            end do

            nullify(u_tendency, v_tendency)

            call mpas_dynamical_core % compute_edge_wind(wind_tendency=.true.)
        end subroutine set_mpas_physics_tendency_ru

        !> Set MPAS physics tendency `tend_rho_physics` (i.e., "coupled" tendency of dry air density due to physics).
        !> In MPAS, a "coupled" variable means that it is multiplied by a vertical metric term, `rho_zz`.
        !> (KCW, 2024-09-11)
        subroutine set_mpas_physics_tendency_rho()
            ! Module(s) from CAM-SIMA.
            use cam_logfile, only: debugout_info
            use dyn_comp, only: mpas_dynamical_core, ncells_solve

            character(*), parameter :: subname = 'dyn_coupling::physics_to_dynamics_coupling::set_mpas_physics_tendency_rho'
            real(kind_dyn_mpas), pointer :: rho_tendency(:, :)

            call dyn_debug_print(debugout_info, 'Setting MPAS physics tendency "tend_rho_physics"')

            nullify(rho_tendency)

            call mpas_dynamical_core % get_variable_pointer(rho_tendency, 'tend_physics', 'tend_rho_physics')

            ! The material derivative of `rho` (i.e., dry air density) is zero for incompressible fluid.
            rho_tendency(:, 1:ncells_solve) = 0.0_kind_dyn_mpas

            nullify(rho_tendency)

            ! Because we are injecting data directly into MPAS memory, halo layers need to be updated manually.
            call mpas_dynamical_core % exchange_halo('tend_rho_physics')
        end subroutine set_mpas_physics_tendency_rho

        !> Set MPAS physics tendency `tend_rtheta_physics` (i.e., "coupled" tendency of modified "moist" potential temperature
        !> due to physics). In MPAS, a "coupled" variable means that it is multiplied by a vertical metric term, `rho_zz`.
        !> (KCW, 2024-09-19)
        subroutine set_mpas_physics_tendency_rtheta()
            ! Module(s) from CAM-SIMA.
            use cam_abortutils, only: check_allocate
            use cam_logfile, only: debugout_info
            use dyn_comp, only: reverse, mpas_dynamical_core, ncells_solve
            use dynconst, only: constant_rd => rair, constant_rv => rh2o
            use physics_types, only: dtime_phys, phys_tend
            use vert_coord, only: pver

            character(*), parameter :: subname = 'dyn_coupling::physics_to_dynamics_coupling::set_mpas_physics_tendency_rtheta'
            integer :: i
            integer :: ierr
            ! Variable name suffixes have the following meanings:
            ! `*_col`: Variable is of each column.
            ! `*_prev`: Variable is before being updated by physics.
            ! `*_curr`: Variable is after being updated by physics.
            real(kind_r8), allocatable :: qv_col_prev(:), qv_col_curr(:)         ! Water vapor mixing ratio (kg kg-1).
            real(kind_r8), allocatable :: rhod_col(:)                            ! Dry air density (kg m-3).
            real(kind_r8), allocatable :: t_col_prev(:), t_col_curr(:)           ! Temperature (K).
            real(kind_r8), allocatable :: theta_col_prev(:), theta_col_curr(:)   ! Potential temperature (K).
            real(kind_r8), allocatable :: thetam_col_prev(:), thetam_col_curr(:) ! Modified "moist" potential temperature (K).
            real(kind_dyn_mpas), pointer :: theta_m(:, :)
            real(kind_dyn_mpas), pointer :: theta_m_tendency(:, :)

            call dyn_debug_print(debugout_info, 'Setting MPAS physics tendency "tend_rtheta_physics"')

            nullify(theta_m)
            nullify(theta_m_tendency)

            allocate(qv_col_prev(pver), qv_col_curr(pver), stat=ierr)
            call check_allocate(ierr, subname, &
                'qv_col_prev(pver), qv_col_curr(pver)', &
                'dyn_coupling', __LINE__)

            allocate(rhod_col(pver), stat=ierr)
            call check_allocate(ierr, subname, &
                'rhod_col(pver)', &
                'dyn_coupling', __LINE__)

            allocate(t_col_prev(pver), t_col_curr(pver), stat=ierr)
            call check_allocate(ierr, subname, &
                't_col_prev(pver), t_col_curr(pver)', &
                'dyn_coupling', __LINE__)

            allocate(theta_col_prev(pver), theta_col_curr(pver), stat=ierr)
            call check_allocate(ierr, subname, &
                'theta_col_prev(pver), theta_col_curr(pver)', &
                'dyn_coupling', __LINE__)

            allocate(thetam_col_prev(pver), thetam_col_curr(pver), stat=ierr)
            call check_allocate(ierr, subname, &
                'thetam_col_prev(pver), thetam_col_curr(pver)', &
                'dyn_coupling', __LINE__)

            call mpas_dynamical_core % get_variable_pointer(theta_m, 'state', 'theta_m', time_level=1)
            call mpas_dynamical_core % get_variable_pointer(theta_m_tendency, 'tend_physics', 'tend_rtheta_physics')

            ! Set `theta_m_tendency` column by column. This way, peak memory usage can be reduced.
            do i = 1, ncells_solve
                qv_col_curr(:) = real(scalars(index_qv, :, i), kind_r8)
                qv_col_prev(:) = qv_prev(:, i)
                rhod_col(:) = real(rho_zz(:, i) * zz(:, i), kind_r8)

                thetam_col_prev(:) = real(theta_m(:, i), kind_r8)
                theta_col_prev(:) = thetam_col_prev(:) / (1.0_kind_r8 + constant_rv / constant_rd * qv_col_prev(:))
                t_col_prev(:) = t_of_theta_rhod_qv(theta_col_prev, rhod_col, qv_col_prev)

                ! Vertical index order is reversed between CAM-SIMA and MPAS.
                ! Always call `reverse` when assigning anything to/from the `physics_tend` derived type.
                t_col_curr(:) = t_col_prev(:) + reverse(phys_tend % dtdt_total(i, :)) * dtime_phys
                theta_col_curr(:) = theta_of_t_rhod_qv(t_col_curr, rhod_col, qv_col_curr)
                thetam_col_curr(:) = theta_col_curr(:) * (1.0_kind_r8 + constant_rv / constant_rd * qv_col_curr(:))

                theta_m_tendency(:, i) = &
                    real((thetam_col_curr(:) - thetam_col_prev(:)) * real(rho_zz(:, i), kind_r8) / dtime_phys, kind_dyn_mpas)
            end do

            deallocate(qv_col_prev, qv_col_curr)
            deallocate(rhod_col)
            deallocate(t_col_prev, t_col_curr)
            deallocate(theta_col_prev, theta_col_curr)
            deallocate(thetam_col_prev, thetam_col_curr)

            nullify(theta_m)
            nullify(theta_m_tendency)

            ! Because we are injecting data directly into MPAS memory, halo layers need to be updated manually.
            call mpas_dynamical_core % exchange_halo('tend_rtheta_physics')
        end subroutine set_mpas_physics_tendency_rtheta

        !> Compute temperature `t` as a function of potential temperature `theta`, dry air density `rhod` and water vapor
        !> mixing ratio `qv`. Essentially,
        !> \( T = \theta^{\frac{C_p}{C_v}} [\frac{\rho_d R_d (1 + \frac{R_v}{R_d} q_v)}{P_0}]^{\frac{R_d}{C_v}} \).
        !> The formulation comes from Poisson equation with equation of state plugged in and arranging
        !> for temperature. This function is the exact inverse of `theta_of_t_rhod_qv`, which means that:
        !> `t == t_of_theta_rhod_qv(theta_of_t_rhod_qv(t, rhod, qv), rhod, qv)`.
        !> (KCW, 2024-09-13)
        pure elemental function t_of_theta_rhod_qv(theta, rhod, qv) result(t)
            ! Module(s) from CAM-SIMA.
            use dynconst, only: constant_cpd => cpair, constant_p0 => pref, &
                                constant_rd => rair, constant_rv => rh2o

            real(kind_r8), intent(in) :: theta, rhod, qv
            real(kind_r8) :: t

            real(kind_r8) :: constant_cvd ! Specific heat of dry air at constant volume.

            ! Mayer's relation.
            constant_cvd = constant_cpd - constant_rd

            ! Poisson equation with equation of state plugged in and arranging for temperature. For equation of state,
            ! it can be shown that the effect of water vapor can be passed on to the temperature term entirely such that
            ! dry air density and dry air gas constant can be used at all times. This modified "moist" temperature is
            ! described herein:
            ! The paragraph below equation 2.7 in doi:10.5065/1DFH-6P97.
            ! The paragraph below equation 2 in doi:10.1175/MWR-D-11-00215.1.
            !
            ! In all, solve the below equation set for $T$ in terms of $\theta$, $\rho_d$ and $q_v$:
            ! \begin{equation*}
            !     \begin{cases}
            !         \theta &= T (\frac{P_0}{P})^{\frac{R_d}{C_p}} \\
            !         P &= \rho_d R_d T_m \\
            !         T_m &= T (1 + \frac{R_v}{R_d} q_v)
            !     \end{cases}
            ! \end{equation*}
            t = (theta ** (constant_cpd / constant_cvd)) * &
                (((rhod * constant_rd * (1.0_kind_r8 + constant_rv / constant_rd * qv)) / constant_p0) ** &
                (constant_rd / constant_cvd))
        end function t_of_theta_rhod_qv

        !> Compute potential temperature `theta` as a function of temperature `t`, dry air density `rhod` and water vapor
        !> mixing ratio `qv`. Essentially,
        !> \( \theta = T^{\frac{C_v}{C_p}} [\frac{P_0}{\rho_d R_d (1 + \frac{R_v}{R_d} q_v)}]^{\frac{R_d}{C_p}} \).
        !> The formulation comes from Poisson equation with equation of state plugged in and arranging
        !> for potential temperature. This function is the exact inverse of `t_of_theta_rhod_qv`, which means that:
        !> `theta == theta_of_t_rhod_qv(t_of_theta_rhod_qv(theta, rhod, qv), rhod, qv)`.
        !> (KCW, 2024-09-13)
        pure elemental function theta_of_t_rhod_qv(t, rhod, qv) result(theta)
            ! Module(s) from CAM-SIMA.
            use dynconst, only: constant_cpd => cpair, constant_p0 => pref, &
                                constant_rd => rair, constant_rv => rh2o

            real(kind_r8), intent(in) :: t, rhod, qv
            real(kind_r8) :: theta

            real(kind_r8) :: constant_cvd ! Specific heat of dry air at constant volume.

            ! Mayer's relation.
            constant_cvd = constant_cpd - constant_rd

            ! Poisson equation with equation of state plugged in and arranging for potential temperature. For equation of state,
            ! it can be shown that the effect of water vapor can be passed on to the temperature term entirely such that
            ! dry air density and dry air gas constant can be used at all times. This modified "moist" temperature is
            ! described herein:
            ! The paragraph below equation 2.7 in doi:10.5065/1DFH-6P97.
            ! The paragraph below equation 2 in doi:10.1175/MWR-D-11-00215.1.
            !
            ! In all, solve the below equation set for $\theta$ in terms of $T$, $\rho_d$ and $q_v$:
            ! \begin{equation*}
            !     \begin{cases}
            !         \theta &= T (\frac{P_0}{P})^{\frac{R_d}{C_p}} \\
            !         P &= \rho_d R_d T_m \\
            !         T_m &= T (1 + \frac{R_v}{R_d} q_v)
            !     \end{cases}
            ! \end{equation*}
            theta = (t ** (constant_cvd / constant_cpd)) * &
                ((constant_p0 / (rhod * constant_rd * (1.0_kind_r8 + constant_rv / constant_rd * qv))) ** &
                (constant_rd / constant_cpd))
        end function theta_of_t_rhod_qv
    end subroutine physics_to_dynamics_coupling