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)
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(:,:) |
Finalize variables that are shared and repeatedly used by the set_mpas_physics_tendency_*
internal subroutines.
(KCW, 2024-09-13)
Initialize variables that are shared and repeatedly used by the set_mpas_physics_tendency_*
internal subroutines.
(KCW, 2024-09-13)
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)
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)
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)
module subroutine physics_to_dynamics_coupling() ! Module(s) from CAM-SIMA. use cam_logfile, only: debugout_debug use dyn_comp, only: dyn_debug_print, kind_dyn_mpas ! Module(s) from CESM Share. use shr_kind_mod, only: kind_r8 => shr_kind_r8 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 use dyn_grid, only: 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: mpas_dynamical_core use dyn_grid, only: ncells_solve use dyn_procedure, only: reverse 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 use dyn_grid, only: 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: mpas_dynamical_core use dyn_grid, only: ncells_solve use dyn_procedure, only: t_of_theta_rhod_qv, t_of_tm_qv, theta_of_t_rhod_qv, tm_of_t_qv, & reverse use dynconst, only: constant_cpd => cpair, constant_p0 => pref, & 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(:) = t_of_tm_qv(constant_rd, constant_rv, thetam_col_prev, qv_col_prev) t_col_prev(:) = t_of_theta_rhod_qv( & constant_cpd, constant_p0, constant_rd, constant_rv, 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( & constant_cpd, constant_p0, constant_rd, constant_rv, t_col_curr, rhod_col, qv_col_curr) thetam_col_curr(:) = tm_of_t_qv(constant_rd, constant_rv, theta_col_curr, 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 end subroutine physics_to_dynamics_coupling