From 0d8389e51a45313d2a5cafedc50cf6d6eb25e6c0 Mon Sep 17 00:00:00 2001 From: Walter Hannah Date: Wed, 1 Jul 2026 17:24:25 -0400 Subject: [PATCH 01/15] add new MCSP namelist variables --- components/eam/bld/build-namelist | 4 ++-- .../eam/bld/namelist_files/namelist_defaults_eam.xml | 4 ++-- .../eam/bld/namelist_files/namelist_definition.xml | 10 ++++++---- components/eam/docs/user-guide/namelist_parameters.md | 4 ++-- .../eamxx/cime_config/namelist_defaults_eamxx.xml | 10 +++++----- components/eamxx/src/physics/zm/impl/zm_opts_impl.hpp | 4 ++-- 6 files changed, 19 insertions(+), 17 deletions(-) diff --git a/components/eam/bld/build-namelist b/components/eam/bld/build-namelist index 65dcb193626c..823531c565bf 100755 --- a/components/eam/bld/build-namelist +++ b/components/eam/bld/build-namelist @@ -4049,8 +4049,8 @@ add_default($nl, 'zmconv_accr_fac'); add_default($nl, 'zmconv_micro_dcs'); add_default($nl, 'zmconv_MCSP_heat_coeff'); add_default($nl, 'zmconv_MCSP_moisture_coeff'); -add_default($nl, 'zmconv_MCSP_uwind_coeff'); -add_default($nl, 'zmconv_MCSP_vwind_coeff'); +add_default($nl, 'zmconv_MCSP_mom_coeff'); +add_default($nl, 'zmconv_MCSP_use_full_shear'); # # moist convection rainwater coefficients # These no more needed for EAM, including uwshcu_rpen. No need to reset for 'cam5' diff --git a/components/eam/bld/namelist_files/namelist_defaults_eam.xml b/components/eam/bld/namelist_files/namelist_defaults_eam.xml index b42f03b8564f..a56f8a4c7a7c 100755 --- a/components/eam/bld/namelist_files/namelist_defaults_eam.xml +++ b/components/eam/bld/namelist_files/namelist_defaults_eam.xml @@ -1270,8 +1270,8 @@ 0.0D0 0.0D0 -0.0D0 -0.0D0 +0.0D0 +.false. diff --git a/components/eam/bld/namelist_files/namelist_definition.xml b/components/eam/bld/namelist_files/namelist_definition.xml index 258c3ef00e27..2f3a91816be7 100644 --- a/components/eam/bld/namelist_files/namelist_definition.xml +++ b/components/eam/bld/namelist_files/namelist_definition.xml @@ -3075,15 +3075,17 @@ Multiscale Coherent System Parameterization (MCSP), moisture coefficient Default: set by build-namelist - -Multiscale Coherent System Parameterization (MCSP), zonal wind coefficient +Multiscale Coherent System Parameterization (MCSP), momentum coefficient +(dimensionless fraction of the storm-relative shear) Default: set by build-namelist - -Multiscale Coherent System Parameterization (MCSP), meridional wind coefficient +Multiscale Coherent System Parameterization (MCSP), use the full (u,v) shear +vector instead of zonal-only shear. Default (.false.) preserves E3SMv3 behavior. Default: set by build-namelist diff --git a/components/eam/docs/user-guide/namelist_parameters.md b/components/eam/docs/user-guide/namelist_parameters.md index 5864fcad6383..b61532962e04 100644 --- a/components/eam/docs/user-guide/namelist_parameters.md +++ b/components/eam/docs/user-guide/namelist_parameters.md @@ -147,8 +147,8 @@ | ---------------------------- | ----------------------------------------------------------------- | ---------------------- | | `zmconv_mcsp_heat_coeff` | MCSP heating coefficient | `0.3` | | `zmconv_mcsp_moisture_coeff` | MCSP moisture coefficient | `0.0` | -| `zmconv_mcsp_uwind_coeff` | MCSP zonal wind coefficient | `0.0` | -| `zmconv_mcsp_vwind_coeff` | MCSP meridional wind coefficient | `0.0` | +| `zmconv_mcsp_mom_coeff` | MCSP momentum coefficient (fraction of storm-relative shear) | `0.0` | +| `zmconv_mcsp_use_full_shear` | MCSP use full (u,v) shear vector instead of zonal-only shear | `.false.` | ## Cloud Feedback Model Intercomparison Project (CFMIP) Observation Simulator Package diff --git a/components/eamxx/cime_config/namelist_defaults_eamxx.xml b/components/eamxx/cime_config/namelist_defaults_eamxx.xml index 29c61a6260fe..adb0eb3bdb91 100644 --- a/components/eamxx/cime_config/namelist_defaults_eamxx.xml +++ b/components/eamxx/cime_config/namelist_defaults_eamxx.xml @@ -289,11 +289,11 @@ be lost if SCREAM_HACK_XML is not enabled. 0.002 1 - true - 0.3 - 0.0 - 0.0 - 0.0 + true + 0.3 + 0.0 + 0.0 + true diff --git a/components/eamxx/src/physics/zm/impl/zm_opts_impl.hpp b/components/eamxx/src/physics/zm/impl/zm_opts_impl.hpp index e940a4caed54..1a11bd62c596 100644 --- a/components/eamxx/src/physics/zm/impl/zm_opts_impl.hpp +++ b/components/eamxx/src/physics/zm/impl/zm_opts_impl.hpp @@ -41,8 +41,8 @@ void Functions::zm_opts_init() s_zm_opts.mcsp_enabled = true; s_zm_opts.mcsp_t_coeff = ZMC::MCSP_t_coeff_default; s_zm_opts.mcsp_q_coeff = ZMC::MCSP_q_coeff_default; - s_zm_opts.mcsp_u_coeff = ZMC::MCSP_u_coeff_default; - s_zm_opts.mcsp_v_coeff = ZMC::MCSP_v_coeff_default; + s_zm_opts.mcsp_mom_coeff = ZMC::MCSP_mom_coeff_default; + s_zm_opts.mcsp_use_full_shear = true; // // set up table values of saturation vapor pressure From 7f838819c26f0b5637382f2c28782cc545f014e2 Mon Sep 17 00:00:00 2001 From: Walter Hannah Date: Wed, 1 Jul 2026 17:25:31 -0400 Subject: [PATCH 02/15] add new MCSP namelist variables to zm_functions.hpp and modify MCSP function interface --- .../eamxx/src/physics/zm/zm_functions.hpp | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/components/eamxx/src/physics/zm/zm_functions.hpp b/components/eamxx/src/physics/zm/zm_functions.hpp index c4a3d748243c..35c8a5bfbd1a 100644 --- a/components/eamxx/src/physics/zm/zm_functions.hpp +++ b/components/eamxx/src/physics/zm/zm_functions.hpp @@ -122,8 +122,7 @@ struct Functions { static inline constexpr Real MCSP_shear_max = 200.0; // max shear value for MCSP to be active static inline constexpr Real MCSP_t_coeff_default = 0.3; // default MCSP temperature coefficient static inline constexpr Real MCSP_q_coeff_default = 0.0; // default MCSP sp. humidity coefficient - static inline constexpr Real MCSP_u_coeff_default = 0.0; // default MCSP U-wind coefficient - static inline constexpr Real MCSP_v_coeff_default = 0.0; // default MCSP V-wind coefficient + static inline constexpr Real MCSP_mom_coeff_default = 0.0; // default MCSP momentum coefficient (fraction of shear) // Default values for ZmRuntimeOpt fields static inline constexpr Real alfa = 0.14; // default downdraft proportionality factor static inline constexpr Real ke = 2.5E-6; // default evaporation efficiency @@ -175,8 +174,10 @@ struct Functions { mcsp_enabled = params.get("mcsp_enabled", true); mcsp_t_coeff = params.get("mcsp_t_coeff", ZMC::MCSP_t_coeff_default); mcsp_q_coeff = params.get("mcsp_q_coeff", ZMC::MCSP_q_coeff_default); - mcsp_u_coeff = params.get("mcsp_u_coeff", ZMC::MCSP_u_coeff_default); - mcsp_v_coeff = params.get("mcsp_v_coeff", ZMC::MCSP_v_coeff_default); + mcsp_mom_coeff = params.get("mcsp_mom_coeff", ZMC::MCSP_mom_coeff_default); + // new-feature toggle: eamxx defaults ON (vector shear); Fortran/EAM defaults + // OFF to preserve bit-for-bit E3SMv3 (zonal-only shear that gates T/q) + mcsp_use_full_shear = params.get("mcsp_use_full_shear", true); // determine SVP table size (add two to make the table slightly too big, just in case) plenest = static_cast(ZMC::tmax-ZMC::tmin) + 3; @@ -251,8 +252,8 @@ struct Functions { os << indent << "mcsp_enabled : " << mcsp_enabled << "\n"; os << indent << "mcsp_t_coeff : " << mcsp_t_coeff << "\n"; os << indent << "mcsp_q_coeff : " << mcsp_q_coeff << "\n"; - os << indent << "mcsp_u_coeff : " << mcsp_u_coeff << "\n"; - os << indent << "mcsp_v_coeff : " << mcsp_v_coeff << "\n"; + os << indent << "mcsp_mom_coeff : " << mcsp_mom_coeff << "\n"; + os << indent << "mcsp_use_full_shear : " << mcsp_use_full_shear << "\n"; os << std::endl; os.flags(saved_flags); } @@ -283,8 +284,8 @@ struct Functions { bool mcsp_enabled; // flag for mesoscale coherent structure parameterization (MSCP) Real mcsp_t_coeff; // MCSP coefficient for temperature tendencies Real mcsp_q_coeff; // MCSP coefficient for specific humidity tendencies - Real mcsp_u_coeff; // MCSP coefficient for zonal momentum tendencies - Real mcsp_v_coeff; // MCSP coefficient for meridional momentum tendencies + Real mcsp_mom_coeff; // MCSP momentum coefficient (dimensionless fraction of the shear) + bool mcsp_use_full_shear; // use full (u,v) shear vector instead of zonal-only shear // saturation vapor pressure (svp) table Int plenest; // saturation vapor pressure table size view_1d estbl; // saturation vapor pressure table values @@ -751,8 +752,10 @@ struct Functions { const Int& pver, // number of mid-point vertical levels const uview_1d& state_pmid, // physics state mid-point pressure const uview_1d& state_u, // physics state u momentum + const uview_1d& state_v, // physics state v momentum // Outputs - Real& mcsp_shear); + Real& shear_u, // zonal component of storm-relative shear + Real& shear_v); // meridional component of storm-relative shear KOKKOS_FUNCTION static void zm_conv_mcsp_tend( From 314532e9e130296248605e6398772d7243c58bd4 Mon Sep 17 00:00:00 2001 From: Walter Hannah Date: Wed, 1 Jul 2026 17:26:36 -0400 Subject: [PATCH 03/15] update fortran MSCP to support new options --- .../eam/src/physics/cam/zm/zm_conv_intr.F90 | 26 +++---- .../eam/src/physics/cam/zm/zm_conv_mcsp.F90 | 75 +++++++++++++------ .../eam/src/physics/cam/zm/zm_conv_types.F90 | 28 +++---- 3 files changed, 79 insertions(+), 50 deletions(-) diff --git a/components/eam/src/physics/cam/zm/zm_conv_intr.F90 b/components/eam/src/physics/cam/zm/zm_conv_intr.F90 index aa1be347e52d..1fbdb5e3cb6f 100644 --- a/components/eam/src/physics/cam/zm/zm_conv_intr.F90 +++ b/components/eam/src/physics/cam/zm/zm_conv_intr.F90 @@ -109,17 +109,17 @@ subroutine zm_conv_readnl(nlfile) real(r8) :: zmconv_micro_dcs = unset_r8 real(r8) :: zmconv_MCSP_heat_coeff = 0._r8 real(r8) :: zmconv_MCSP_moisture_coeff = 0._r8 - real(r8) :: zmconv_MCSP_uwind_coeff = 0._r8 - real(r8) :: zmconv_MCSP_vwind_coeff = 0._r8 + real(r8) :: zmconv_MCSP_mom_coeff = 0._r8 + logical :: zmconv_MCSP_use_full_shear = .false. !---------------------------------------------------------------------------- namelist /zmconv_nl/ zmconv_tau, zmconv_alfa, zmconv_ke, zmconv_dmpdz, & zmconv_tpert_fix, zmconv_tp_fac, zmconv_tiedke_add, & - zmconv_c0_lnd, zmconv_c0_ocn, & + zmconv_c0_lnd, zmconv_c0_ocn, & zmconv_cape_cin, zmconv_mx_bot_lyr_adj, & zmconv_trig_dcape, zmconv_trig_ull, zmconv_clos_dyn_adj, & zmconv_microp, zmconv_auto_fac, zmconv_accr_fac, zmconv_micro_dcs, & zmconv_MCSP_heat_coeff, zmconv_MCSP_moisture_coeff, & - zmconv_MCSP_uwind_coeff, zmconv_MCSP_vwind_coeff + zmconv_MCSP_mom_coeff, zmconv_MCSP_use_full_shear !---------------------------------------------------------------------------- if (masterproc) then @@ -157,12 +157,12 @@ subroutine zm_conv_readnl(nlfile) zm_param%micro_dcs = zmconv_micro_dcs ! mesoscale coherent structure parameterization (MCSP) parameters - zm_param%mcsp_t_coeff = zmconv_MCSP_heat_coeff - zm_param%mcsp_q_coeff = zmconv_MCSP_moisture_coeff - zm_param%mcsp_u_coeff = zmconv_MCSP_uwind_coeff - zm_param%mcsp_v_coeff = zmconv_MCSP_vwind_coeff + zm_param%mcsp_t_coeff = zmconv_MCSP_heat_coeff + zm_param%mcsp_q_coeff = zmconv_MCSP_moisture_coeff + zm_param%mcsp_mom_coeff = zmconv_MCSP_mom_coeff + zm_param%mcsp_use_full_shear = zmconv_MCSP_use_full_shear if ( abs(zm_param%mcsp_t_coeff)>0._r8 .or. abs(zm_param%mcsp_q_coeff)>0._r8 .or. & - abs(zm_param%mcsp_u_coeff)>0._r8 .or. abs(zm_param%mcsp_v_coeff)>0._r8 ) then + abs(zm_param%mcsp_mom_coeff)>0._r8 ) then zm_param%mcsp_enabled = .true. else zm_param%mcsp_enabled = .false. @@ -483,8 +483,7 @@ subroutine zm_conv_tend(pblh, mcon, cme, tpert, dlftot, pflx, zdu, & ! flags for MCSP tendencies logical :: do_mcsp_t = .false. logical :: do_mcsp_q(pcnst) = .false. - logical :: do_mcsp_u = .false. - logical :: do_mcsp_v = .false. + logical :: do_mcsp_mom = .false. ! MCSP history output variables real(r8), dimension(pcols,pver) :: mcsp_dt_out ! MCSP tendency for DSE @@ -674,11 +673,10 @@ subroutine zm_conv_tend(pblh, mcon, cme, tpert, dlftot, pflx, zdu, & ! a problem with bridging to this routine from C++ for porting ZM to EAMxx do_mcsp_t = .true. do_mcsp_q(1) = .true. - do_mcsp_u = .true. - do_mcsp_v = .true. + do_mcsp_mom = .true. call physics_ptend_init( ptend_mcsp, state%psetcols, 'zm_conv_mcsp_tend', & - ls=do_mcsp_t, lq=do_mcsp_q, lu=do_mcsp_u, lv=do_mcsp_v) + ls=do_mcsp_t, lq=do_mcsp_q, lu=do_mcsp_mom, lv=do_mcsp_mom) call zm_conv_mcsp_tend( pcols, ncol, pver, pverp, & ztodt, jctop, zm_const, zm_param, & diff --git a/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 b/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 index d7f38a7cb047..2286cd86ed8e 100644 --- a/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 +++ b/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 @@ -80,9 +80,11 @@ end subroutine zm_conv_mcsp_init !=================================================================================================== -subroutine zm_conv_mcsp_calculate_shear( pcols, ncol, pver, state_pmid, state_u, state_v, mcsp_shear) +subroutine zm_conv_mcsp_calculate_shear( pcols, ncol, pver, state_pmid, state_u, state_v, shear_u, shear_v) !---------------------------------------------------------------------------- - ! Purpose: calculate shear for MCSP + ! Purpose: calculate storm-relative shear vector for MCSP. The zonal and + ! meridional components are returned separately; the caller decides whether to + ! gate/scale on the zonal component alone (legacy) or the full shear magnitude. !---------------------------------------------------------------------------- #ifdef SCREAM_CONFIG_IS_CMAKE use zm_eamxx_bridge_methods, only: vertinterp @@ -97,23 +99,29 @@ subroutine zm_conv_mcsp_calculate_shear( pcols, ncol, pver, state_pmid, state_u, real(r8), dimension(pcols,pver), intent(in ) :: state_pmid ! physics state mid-point pressure real(r8), dimension(pcols,pver), intent(in ) :: state_u ! physics state u momentum real(r8), dimension(pcols,pver), intent(in ) :: state_v ! physics state v momentum - real(r8), dimension(pcols), intent( out) :: mcsp_shear + real(r8), dimension(pcols), intent( out) :: shear_u ! zonal component of storm-relative shear + real(r8), dimension(pcols), intent( out) :: shear_v ! meridional component of storm-relative shear !---------------------------------------------------------------------------- ! Local variables integer :: i real(r8), dimension(pcols) :: storm_u ! u wind at storm reference level set by MCSP_storm_speed_pref + real(r8), dimension(pcols) :: storm_v ! v wind at storm reference level set by MCSP_storm_speed_pref !---------------------------------------------------------------------------- - ! Interpolate wind to pressure level specified by MCSP_storm_speed_pref + ! Interpolate winds to pressure level specified by MCSP_storm_speed_pref call vertinterp( ncol, pcols, pver, state_pmid, MCSP_storm_speed_pref, state_u, storm_u ) + call vertinterp( ncol, pcols, pver, state_pmid, MCSP_storm_speed_pref, state_v, storm_v ) !---------------------------------------------------------------------------- - ! calculate low-level shear + ! calculate low-level shear components. The -999 sentinel (when the surface is + ! above the storm reference level) fails the caller's shear-magnitude gate. do i = 1,ncol if (state_pmid(i,pver).gt.MCSP_storm_speed_pref) then - mcsp_shear(i) = storm_u(i)-state_u(i,pver) + shear_u(i) = storm_u(i)-state_u(i,pver) + shear_v(i) = storm_v(i)-state_v(i,pver) else - mcsp_shear(i) = -999 + shear_u(i) = -999 + shear_v(i) = 0 end if end do @@ -185,10 +193,13 @@ subroutine zm_conv_mcsp_tend( pcols, ncol, pver, pverp, & real(r8), dimension(pcols) :: mcsp_avg_tend_q ! mass weighted column average MCSP tendency of qv real(r8), dimension(pcols) :: mcsp_avg_tend_k ! mass weighted column average MCSP tendency of kinetic energy + real(r8), dimension(pcols) :: shear_u ! zonal component of storm-relative shear + real(r8), dimension(pcols) :: shear_v ! meridional component of storm-relative shear + real(r8) :: cos_struct ! vertical structure factor for momentum tendencies + logical :: do_mcsp_t = .false. ! internal flag to enable tendency calculations logical :: do_mcsp_q = .false. ! internal flag to enable tendency calculations - logical :: do_mcsp_u = .false. ! internal flag to enable tendency calculations - logical :: do_mcsp_v = .false. ! internal flag to enable tendency calculations + logical :: do_mcsp_mom = .false. ! internal flag to enable momentum tendency calculations !---------------------------------------------------------------------------- @@ -197,10 +208,9 @@ subroutine zm_conv_mcsp_tend( pcols, ncol, pver, pverp, & !---------------------------------------------------------------------------- ! initialize variables - if (zm_param%mcsp_t_coeff>0) do_mcsp_t = .true. - if (zm_param%mcsp_q_coeff>0) do_mcsp_q = .true. - if (zm_param%mcsp_u_coeff>0) do_mcsp_u = .true. - if (zm_param%mcsp_v_coeff>0) do_mcsp_v = .true. + if (zm_param%mcsp_t_coeff>0) do_mcsp_t = .true. + if (zm_param%mcsp_q_coeff>0) do_mcsp_q = .true. + if (zm_param%mcsp_mom_coeff>0) do_mcsp_mom = .true. zm_avg_tend_s(1:ncol) = 0 zm_avg_tend_q(1:ncol) = 0 @@ -219,7 +229,7 @@ subroutine zm_conv_mcsp_tend( pcols, ncol, pver, pverp, & !---------------------------------------------------------------------------- ! calculate shear - call zm_conv_mcsp_calculate_shear( pcols, ncol, pver, state_pmid, state_u, state_v, mcsp_shear ) + call zm_conv_mcsp_calculate_shear( pcols, ncol, pver, state_pmid, state_u, state_v, shear_u, shear_v ) !---------------------------------------------------------------------------- ! calculate mass weighted column average tendencies from ZM @@ -259,6 +269,17 @@ subroutine zm_conv_mcsp_tend( pcols, ncol, pver, pverp, & do i = 1,ncol + ! The momentum forcing always follows the (u,v) shear vector (du~shear_u, + ! dv~shear_v). The mcsp_use_full_shear option only controls the scalar shear + ! used for the activation threshold and diagnostic: the full shear magnitude, + ! or the legacy signed zonal shear (which reproduces E3SMv3, since the same + ! shear also gates the T/q tendencies). + if (zm_param%mcsp_use_full_shear) then + mcsp_shear(i) = sqrt( shear_u(i)*shear_u(i) + shear_v(i)*shear_v(i) ) + else + mcsp_shear(i) = shear_u(i) ! legacy signed zonal shear diagnostic + end if + ! check that ZM produced tendencies over a depth that exceeds the threshold if ( zm_depth(i) >= MCSP_conv_depth_min ) then ! check that ZM provided a non-zero column total heating @@ -275,8 +296,18 @@ subroutine zm_conv_mcsp_tend( pcols, ncol, pver, pverp, & ! specify the assumed vertical structure if (do_mcsp_t) mcsp_tend_s(i,k) = -1*zm_param%mcsp_t_coeff * bfb_sin(2.0_r8*zm_const%pi*(pdepth_mid_k/pdepth_total)) if (do_mcsp_q) mcsp_tend_q(i,k) = -1*zm_param%mcsp_q_coeff * bfb_sin(2.0_r8*zm_const%pi*(pdepth_mid_k/pdepth_total)) - if (do_mcsp_u) mcsp_tend_u(i,k) = zm_param%mcsp_u_coeff * (bfb_cos(zm_const%pi*(pdepth_mid_k/pdepth_total))) - if (do_mcsp_v) mcsp_tend_v(i,k) = zm_param%mcsp_v_coeff * (bfb_cos(zm_const%pi*(pdepth_mid_k/pdepth_total))) + ! Momentum tendencies are scaled by the storm-relative shear vector so + ! mcsp_mom_coeff is a dimensionless O(0.01-0.1) fraction rather than a + ! raw acceleration. The implied wind increment over the step is + ! du = -mcsp_mom_coeff * shear_u * cos(...) -> tend_u = du / ztodt + ! The leading minus sign makes the forcing up-gradient (amplifying the + ! shear), consistent with organized-convection momentum transport + ! (Moncrieff), and bounds the KE correction. + if (do_mcsp_mom) then + cos_struct = bfb_cos(zm_const%pi*(pdepth_mid_k/pdepth_total)) + mcsp_tend_u(i,k) = -1*zm_param%mcsp_mom_coeff * shear_u(i) * cos_struct / ztodt + mcsp_tend_v(i,k) = -1*zm_param%mcsp_mom_coeff * shear_v(i) * cos_struct / ztodt + end if ! scale the vertical structure by the ZM heating/drying tendencies if (do_mcsp_t) mcsp_tend_s(i,k) = zm_avg_tend_s(i) * mcsp_tend_s(i,k) @@ -287,7 +318,7 @@ subroutine zm_conv_mcsp_tend( pcols, ncol, pver, pverp, & if (do_mcsp_q) mcsp_avg_tend_q(i) = mcsp_avg_tend_q(i) + mcsp_tend_q(i,k) * state_pdel(i,k) / pdel_sum(i) ! integrate the change in kinetic energy (KE) for energy fixer - if (do_mcsp_u.or.do_mcsp_v) then + if (do_mcsp_mom) then tend_k = ( 2.0_r8*mcsp_tend_u(i,k)*ztodt*state_u(i,k) + mcsp_tend_u(i,k)*mcsp_tend_u(i,k)*ztodt*ztodt & +2.0_r8*mcsp_tend_v(i,k)*ztodt*state_v(i,k) + mcsp_tend_v(i,k)*mcsp_tend_v(i,k)*ztodt*ztodt )/2.0_r8/ztodt mcsp_avg_tend_k(i) = mcsp_avg_tend_k(i) + tend_k*state_pdel(i,k) / pdel_sum(i) @@ -326,15 +357,15 @@ subroutine zm_conv_mcsp_tend( pcols, ncol, pver, pverp, & ! make sure kinetic energy correction is added to DSE tendency ! to conserve total energy whenever momentum tendencies are calculated - if (do_mcsp_u.or.do_mcsp_v) then + if (do_mcsp_mom) then mcsp_dt_out(i,k) = mcsp_dt_out(i,k) - mcsp_avg_tend_k(i) end if ! update output tendencies - if (do_mcsp_t) ptend_s(i,k) = ptend_s(i,k) + mcsp_dt_out(i,k) - if (do_mcsp_q) ptend_q(i,k) = ptend_q(i,k) + mcsp_dq_out(i,k) - if (do_mcsp_u) ptend_u(i,k) = ptend_u(i,k) + mcsp_du_out(i,k) - if (do_mcsp_v) ptend_v(i,k) = ptend_v(i,k) + mcsp_dv_out(i,k) + if (do_mcsp_t) ptend_s(i,k) = ptend_s(i,k) + mcsp_dt_out(i,k) + if (do_mcsp_q) ptend_q(i,k) = ptend_q(i,k) + mcsp_dq_out(i,k) + if (do_mcsp_mom) ptend_u(i,k) = ptend_u(i,k) + mcsp_du_out(i,k) + if (do_mcsp_mom) ptend_v(i,k) = ptend_v(i,k) + mcsp_dv_out(i,k) ! adjust units for diagnostic outputs if (do_mcsp_t) mcsp_dt_out(i,k) = mcsp_dt_out(i,k)/zm_const%cpair diff --git a/components/eam/src/physics/cam/zm/zm_conv_types.F90 b/components/eam/src/physics/cam/zm/zm_conv_types.F90 index 9c3c53a1dea8..d0134478aab5 100644 --- a/components/eam/src/physics/cam/zm/zm_conv_types.F90 +++ b/components/eam/src/physics/cam/zm/zm_conv_types.F90 @@ -77,11 +77,11 @@ module zm_conv_types real(r8) :: accr_fac = unset_r8 ! ZM microphysics enhancement factor for droplet-rain accretion real(r8) :: micro_dcs = unset_r8 ! ZM microphysics size threshold for cloud ice to snow autoconversion [m] ! MCSP parameters - logical :: mcsp_enabled = .false. ! flag for mesoscale coherent structure parameterization (MSCP) - real(r8) :: mcsp_t_coeff = 0 ! MCSP coefficient for temperature tendencies - real(r8) :: mcsp_q_coeff = 0 ! MCSP coefficient for specific humidity tendencies - real(r8) :: mcsp_u_coeff = 0 ! MCSP coefficient for zonal momentum tendencies - real(r8) :: mcsp_v_coeff = 0 ! MCSP coefficient for meridional momentum tendencies + logical :: mcsp_enabled = .false. ! flag for mesoscale coherent structure parameterization (MSCP) + real(r8) :: mcsp_t_coeff = 0 ! MCSP coefficient for temperature tendencies + real(r8) :: mcsp_q_coeff = 0 ! MCSP coefficient for specific humidity tendencies + real(r8) :: mcsp_mom_coeff = 0 ! MCSP momentum coefficient (dimensionless fraction of the shear) + logical :: mcsp_use_full_shear = .false. ! use full (u,v) shear vector instead of zonal-only shear (default preserves E3SMv3) end type zm_param_t !=================================================================================================== @@ -213,11 +213,11 @@ subroutine zm_param_set_for_testing(zm_param) zm_param%accr_fac = 1.5D0 zm_param%micro_dcs = 150.E-6 ! MCSP parameters - zm_param%mcsp_enabled = .true. - zm_param%mcsp_t_coeff = 0.3 - zm_param%mcsp_q_coeff = 0 - zm_param%mcsp_u_coeff = 0 - zm_param%mcsp_v_coeff = 0 + zm_param%mcsp_enabled = .true. + zm_param%mcsp_t_coeff = 0.3 + zm_param%mcsp_q_coeff = 0 + zm_param%mcsp_mom_coeff = 0 + zm_param%mcsp_use_full_shear = .false. end subroutine zm_param_set_for_testing !=================================================================================================== @@ -256,8 +256,8 @@ subroutine zm_param_mpi_broadcast(zm_param) call mpibcast(zm_param%mcsp_enabled, 1, mpilog, 0, mpicom) ! MCSP parameters call mpibcast(zm_param%mcsp_t_coeff, 1, mpir8, 0, mpicom) call mpibcast(zm_param%mcsp_q_coeff, 1, mpir8, 0, mpicom) - call mpibcast(zm_param%mcsp_u_coeff, 1, mpir8, 0, mpicom) - call mpibcast(zm_param%mcsp_v_coeff, 1, mpir8, 0, mpicom) + call mpibcast(zm_param%mcsp_mom_coeff, 1, mpir8, 0, mpicom) + call mpibcast(zm_param%mcsp_use_full_shear, 1, mpilog, 0, mpicom) #endif end subroutine zm_param_mpi_broadcast @@ -300,8 +300,8 @@ subroutine zm_param_print(zm_param) write(iulog,*) indent,'mcsp_enabled : ',zm_param%mcsp_enabled write(iulog,*) indent,'mcsp_t_coeff : ',zm_param%mcsp_t_coeff write(iulog,*) indent,'mcsp_q_coeff : ',zm_param%mcsp_q_coeff - write(iulog,*) indent,'mcsp_u_coeff : ',zm_param%mcsp_u_coeff - write(iulog,*) indent,'mcsp_v_coeff : ',zm_param%mcsp_v_coeff + write(iulog,*) indent,'mcsp_mom_coeff : ',zm_param%mcsp_mom_coeff + write(iulog,*) indent,'mcsp_use_full_shear : ',zm_param%mcsp_use_full_shear write(iulog,*) '' call shr_sys_flush(iulog) end if ! masterproc From a73d849ed45bae0aa9523bd23dd3d83c0b62286c Mon Sep 17 00:00:00 2001 From: Walter Hannah Date: Wed, 1 Jul 2026 17:29:42 -0400 Subject: [PATCH 04/15] add fortran bridge support for new MCSP options --- .../physics/zm/fortran_bridge/zm_eamxx_bridge.cpp | 12 +++++++++--- .../physics/zm/fortran_bridge/zm_eamxx_bridge.hpp | 4 +++- .../zm/fortran_bridge/zm_eamxx_bridge_main.F90 | 12 +++++++++++- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/components/eamxx/src/physics/zm/fortran_bridge/zm_eamxx_bridge.cpp b/components/eamxx/src/physics/zm/fortran_bridge/zm_eamxx_bridge.cpp index 8312423be48c..eb104d6ec04e 100644 --- a/components/eamxx/src/physics/zm/fortran_bridge/zm_eamxx_bridge.cpp +++ b/components/eamxx/src/physics/zm/fortran_bridge/zm_eamxx_bridge.cpp @@ -9,7 +9,9 @@ extern "C" { void zm_eamxx_bridge_init_c( Int pver_in, Int limcnv_in, bool trig_dcape_in, bool trig_ull_in, - bool clos_dyn_adj_in, bool mcsp_enabled_in ); + bool clos_dyn_adj_in, bool mcsp_enabled_in, + Real mcsp_t_coeff_in, Real mcsp_q_coeff_in, + Real mcsp_mom_coeff_in, bool mcsp_use_full_shear_in ); void zm_eamxx_bridge_run_c( Int ncol, // 01 Real dtime, // 02 @@ -63,10 +65,14 @@ namespace zm { void zm_eamxx_bridge_init( Int pver, Int limcnv_in, bool trig_dcape_in, bool trig_ull_in, - bool clos_dyn_adj_in, bool mcsp_enabled_in ){ + bool clos_dyn_adj_in, bool mcsp_enabled_in, + Real mcsp_t_coeff_in, Real mcsp_q_coeff_in, + Real mcsp_mom_coeff_in, bool mcsp_use_full_shear_in ){ zm_eamxx_bridge_init_c( pver, limcnv_in, trig_dcape_in, trig_ull_in, - clos_dyn_adj_in, mcsp_enabled_in ); + clos_dyn_adj_in, mcsp_enabled_in, + mcsp_t_coeff_in, mcsp_q_coeff_in, + mcsp_mom_coeff_in, mcsp_use_full_shear_in ); } void zm_eamxx_bridge_run( Int ncol, Int pver, Real dtime, bool is_first_step, diff --git a/components/eamxx/src/physics/zm/fortran_bridge/zm_eamxx_bridge.hpp b/components/eamxx/src/physics/zm/fortran_bridge/zm_eamxx_bridge.hpp index c5270fae6a10..782506fbc110 100644 --- a/components/eamxx/src/physics/zm/fortran_bridge/zm_eamxx_bridge.hpp +++ b/components/eamxx/src/physics/zm/fortran_bridge/zm_eamxx_bridge.hpp @@ -16,7 +16,9 @@ using ZMF = zm::Functions; // Glue functions to call fortran from from C++ with the Data struct void zm_eamxx_bridge_init( Int pver, Int limcnv_in, bool trig_dcape_in, bool trig_ull_in, - bool clos_dyn_adj_in, bool mcsp_enabled_in ); + bool clos_dyn_adj_in, bool mcsp_enabled_in, + Real mcsp_t_coeff_in, Real mcsp_q_coeff_in, + Real mcsp_mom_coeff_in, bool mcsp_use_full_shear_in ); void zm_eamxx_bridge_run( Int ncol, Int pver, Real dtime, bool is_first_step, ZMF::ZmInputState& zm_input, ZMF::ZmOutputTend& zm_output, diff --git a/components/eamxx/src/physics/zm/fortran_bridge/zm_eamxx_bridge_main.F90 b/components/eamxx/src/physics/zm/fortran_bridge/zm_eamxx_bridge_main.F90 index a1f90149cbec..2889387f2459 100644 --- a/components/eamxx/src/physics/zm/fortran_bridge/zm_eamxx_bridge_main.F90 +++ b/components/eamxx/src/physics/zm/fortran_bridge/zm_eamxx_bridge_main.F90 @@ -28,7 +28,9 @@ module zm_eamxx_bridge_main subroutine zm_eamxx_bridge_init_c( pver_in, limcnv_in, & trig_dcape_in, trig_ull_in, & - clos_dyn_adj_in, mcsp_enabled_in ) bind(C) + clos_dyn_adj_in, mcsp_enabled_in, & + mcsp_t_coeff_in, mcsp_q_coeff_in, & + mcsp_mom_coeff_in, mcsp_use_full_shear_in ) bind(C) use mpi use zm_conv, only: zm_const, zm_param use zm_conv_types, only: zm_const_set_for_testing, zm_param_set_for_testing @@ -42,6 +44,10 @@ subroutine zm_eamxx_bridge_init_c( pver_in, limcnv_in, & logical(kind=c_bool),value, intent(in) :: trig_ull_in logical(kind=c_bool),value, intent(in) :: clos_dyn_adj_in logical(kind=c_bool),value, intent(in) :: mcsp_enabled_in + real(kind=c_real), value, intent(in) :: mcsp_t_coeff_in + real(kind=c_real), value, intent(in) :: mcsp_q_coeff_in + real(kind=c_real), value, intent(in) :: mcsp_mom_coeff_in + logical(kind=c_bool),value, intent(in) :: mcsp_use_full_shear_in !----------------------------------------------------------------------------- ! Local variables integer :: mpi_rank, ierror @@ -67,6 +73,10 @@ subroutine zm_eamxx_bridge_init_c( pver_in, limcnv_in, & zm_param%trig_ull = trig_ull_in zm_param%clos_dyn_adj = clos_dyn_adj_in zm_param%mcsp_enabled = mcsp_enabled_in + zm_param%mcsp_t_coeff = mcsp_t_coeff_in + zm_param%mcsp_q_coeff = mcsp_q_coeff_in + zm_param%mcsp_mom_coeff = mcsp_mom_coeff_in + zm_param%mcsp_use_full_shear = mcsp_use_full_shear_in call zm_param_print(zm_param) !----------------------------------------------------------------------------- call wv_sat_init() From e4f533d65f603a0c73981c179ee73b971e0563f4 Mon Sep 17 00:00:00 2001 From: Walter Hannah Date: Wed, 1 Jul 2026 17:31:30 -0400 Subject: [PATCH 05/15] update EAMXX ZM interface for new MCSP options --- .../eamxx/src/physics/zm/eamxx_zm_process_interface.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/eamxx/src/physics/zm/eamxx_zm_process_interface.cpp b/components/eamxx/src/physics/zm/eamxx_zm_process_interface.cpp index 9cf88d10f326..b5ddc420ec06 100644 --- a/components/eamxx/src/physics/zm/eamxx_zm_process_interface.cpp +++ b/components/eamxx/src/physics/zm/eamxx_zm_process_interface.cpp @@ -138,10 +138,10 @@ void ZMDeepConvection::initialize_impl (const RunType) zm_output.init_host_mirrors(m_ncol, m_nlev); // initialize variables on the fortran side zm::zm_eamxx_bridge_init( m_nlev, ZMF::s_zm_opts.limcnv+1, - ZMF::s_zm_opts.trig_dcape, - ZMF::s_zm_opts.trig_ull, - ZMF::s_zm_opts.clos_dyn_adj, - ZMF::s_zm_opts.mcsp_enabled); + ZMF::s_zm_opts.trig_dcape, ZMF::s_zm_opts.trig_ull, + ZMF::s_zm_opts.clos_dyn_adj, ZMF::s_zm_opts.mcsp_enabled, + ZMF::s_zm_opts.mcsp_t_coeff, ZMF::s_zm_opts.mcsp_q_coeff, + ZMF::s_zm_opts.mcsp_mom_coeff, ZMF::s_zm_opts.mcsp_use_full_shear ); } // if use_fortran_bridge //---------------------------------------------------------------------------- } // ZMDeepConvection::initialize_impl From 9991cae6c76e1c691d810b0edf4065603dc6329f Mon Sep 17 00:00:00 2001 From: Walter Hannah Date: Wed, 1 Jul 2026 17:31:43 -0400 Subject: [PATCH 06/15] update C++ MCSP --- .../zm_conv_mcsp_calculate_shear_impl.hpp | 25 ++++++--- .../zm/impl/zm_conv_mcsp_tend_impl.hpp | 54 +++++++++++++------ 2 files changed, 56 insertions(+), 23 deletions(-) diff --git a/components/eamxx/src/physics/zm/impl/zm_conv_mcsp_calculate_shear_impl.hpp b/components/eamxx/src/physics/zm/impl/zm_conv_mcsp_calculate_shear_impl.hpp index d296b4f6d22e..79da5f7ce859 100644 --- a/components/eamxx/src/physics/zm/impl/zm_conv_mcsp_calculate_shear_impl.hpp +++ b/components/eamxx/src/physics/zm/impl/zm_conv_mcsp_calculate_shear_impl.hpp @@ -19,15 +19,20 @@ void Functions::zm_conv_mcsp_calculate_shear( const Int& pver, // number of mid-point vertical levels const uview_1d& state_pmid, // physics state mid-point pressure const uview_1d& state_u, // physics state u momentum + const uview_1d& state_v, // physics state v momentum // Outputs - Real& mcsp_shear) + Real& shear_u, // zonal component of storm-relative shear + Real& shear_v) // meridional component of storm-relative shear { //---------------------------------------------------------------------------- - // Purpose: calculate shear for MCSP + // Purpose: calculate storm-relative shear vector for MCSP. The zonal and + // meridional components are returned separately; the caller decides whether to + // gate/scale on the zonal component alone (legacy) or the full shear magnitude. //---------------------------------------------------------------------------- // Local variables Real storm_u = 0; // u wind at storm reference level set by MCSP_storm_speed_pref + Real storm_v = 0; // v wind at storm reference level set by MCSP_storm_speed_pref //---------------------------------------------------------------------------- // Interpolate wind to pressure level specified by MCSP_storm_speed_pref @@ -47,29 +52,35 @@ void Functions::zm_conv_mcsp_calculate_shear( // Linear interpolation - computed redundantly on all team threads so the // result is consistent across the team (no single/broadcast needed). Using - // Kokkos::single here would update mcsp_shear on only one thread, leaving the + // Kokkos::single here would update the outputs on only one thread, leaving the // other threads with stale values and causing a divergent branch (and GPU - // deadlock) where mcsp_shear later gates a team-collective. + // deadlock) where the shear later gates a team-collective. // (removing the Kokkos::single fixed a hang issue) if (state_pmid(0) >= ZMC::MCSP_storm_speed_pref) { storm_u = state_u(0); + storm_v = state_v(0); } else if (state_pmid(pver - 1) < ZMC::MCSP_storm_speed_pref) { storm_u = state_u(pver - 1); + storm_v = state_v(pver - 1); } else { EKAT_KERNEL_ASSERT(k_below < pver-1); const Real dpu = ZMC::MCSP_storm_speed_pref - state_pmid(k_below); const Real dpl = state_pmid(k_below+1) - ZMC::MCSP_storm_speed_pref; storm_u = (state_u(k_below)*dpl + state_u(k_below+1)*dpu) / (dpl + dpu); + storm_v = (state_v(k_below)*dpl + state_v(k_below+1)*dpu) / (dpl + dpu); } //---------------------------------------------------------------------------- - // calculate low-level shear + // calculate low-level shear components. The -999 sentinel (when the surface is + // above the storm reference level) fails the caller's shear-magnitude gate. if (state_pmid(pver - 1) > ZMC::MCSP_storm_speed_pref) { - mcsp_shear = storm_u - state_u(pver - 1); + shear_u = storm_u - state_u(pver - 1); + shear_v = storm_v - state_v(pver - 1); } else { - mcsp_shear = -999; + shear_u = -999; + shear_v = 0; } } diff --git a/components/eamxx/src/physics/zm/impl/zm_conv_mcsp_tend_impl.hpp b/components/eamxx/src/physics/zm/impl/zm_conv_mcsp_tend_impl.hpp index 4ff8c0b35bb9..886de6de8426 100644 --- a/components/eamxx/src/physics/zm/impl/zm_conv_mcsp_tend_impl.hpp +++ b/components/eamxx/src/physics/zm/impl/zm_conv_mcsp_tend_impl.hpp @@ -54,10 +54,9 @@ void Functions::zm_conv_mcsp_tend( //---------------------------------------------------------------------------- // initialize variables - const bool do_mcsp_t = (runtime_opt.mcsp_t_coeff > 0); - const bool do_mcsp_q = (runtime_opt.mcsp_q_coeff > 0); - const bool do_mcsp_u = (runtime_opt.mcsp_u_coeff > 0); - const bool do_mcsp_v = (runtime_opt.mcsp_v_coeff > 0); + const bool do_mcsp_t = (runtime_opt.mcsp_t_coeff > 0); + const bool do_mcsp_q = (runtime_opt.mcsp_q_coeff > 0); + const bool do_mcsp_mom = (runtime_opt.mcsp_mom_coeff > 0); // Allocate temporary arrays uview_1d mcsp_tend_s, mcsp_tend_q, mcsp_tend_u, mcsp_tend_v; @@ -86,9 +85,21 @@ void Functions::zm_conv_mcsp_tend( team.team_barrier(); //---------------------------------------------------------------------------- - // calculate shear - - zm_conv_mcsp_calculate_shear(team, pver, state_pmid, state_u, mcsp_shear); + // calculate shear vector and derive the scalar metric used for gating/scaling + + Real shear_u = 0; // zonal component of storm-relative shear + Real shear_v = 0; // meridional component of storm-relative shear + zm_conv_mcsp_calculate_shear(team, pver, state_pmid, state_u, state_v, shear_u, shear_v); + + // The momentum forcing always follows the (u,v) shear vector (du~shear_u, + // dv~shear_v). The mcsp_use_full_shear option only controls the scalar shear + // used for the activation threshold and diagnostic: the full shear magnitude, + // or the legacy signed zonal shear (which reproduces E3SMv3, since the same + // shear also gates the T/q tendencies). The gate below uses abs(mcsp_shear), + // which equals the magnitude either way. + mcsp_shear = runtime_opt.mcsp_use_full_shear + ? std::sqrt(shear_u * shear_u + shear_v * shear_v) + : shear_u; //---------------------------------------------------------------------------- // calculate mass weighted column average tendencies from ZM @@ -143,8 +154,19 @@ void Functions::zm_conv_mcsp_tend( // specify the assumed vertical structure if (do_mcsp_t) mcsp_tend_s(k) = -1 * runtime_opt.mcsp_t_coeff * std::sin(2 * PC::Pi * (pdepth_mid_k / pdepth_total)); if (do_mcsp_q) mcsp_tend_q(k) = -1 * runtime_opt.mcsp_q_coeff * std::sin(2 * PC::Pi * (pdepth_mid_k / pdepth_total)); - if (do_mcsp_u) mcsp_tend_u(k) = runtime_opt.mcsp_u_coeff * (std::cos(PC::Pi * (pdepth_mid_k / pdepth_total))); - if (do_mcsp_v) mcsp_tend_v(k) = runtime_opt.mcsp_v_coeff * (std::cos(PC::Pi * (pdepth_mid_k / pdepth_total))); + // Momentum tendencies are scaled by the storm-relative shear vector so + // mcsp_mom_coeff is a dimensionless O(0.01-0.1) fraction rather than a + // raw acceleration. The implied wind increment over the step is + // du = -mcsp_mom_coeff * shear_u * cos(...) -> tend_u = du / ztodt + // The leading minus sign makes the forcing up-gradient (amplifying the + // shear), consistent with organized-convection momentum transport + // (Moncrieff). Using the shear components keeps the tendency aligned + // with the shear vector and bounds the KE correction. + if (do_mcsp_mom) { + const Real cos_struct = std::cos(PC::Pi * (pdepth_mid_k / pdepth_total)); + mcsp_tend_u(k) = -1 * runtime_opt.mcsp_mom_coeff * shear_u * cos_struct / ztodt; + mcsp_tend_v(k) = -1 * runtime_opt.mcsp_mom_coeff * shear_v * cos_struct / ztodt; + } // scale the vertical structure by the ZM heating/drying tendencies if (do_mcsp_t) mcsp_tend_s(k) = zm_avg_tend_s * mcsp_tend_s(k); @@ -155,9 +177,9 @@ void Functions::zm_conv_mcsp_tend( if (do_mcsp_q) avg_q += mcsp_tend_q(k) * state_pdel(k) / pdel_sum; // integrate the change in kinetic energy (KE) for energy fixer - if (do_mcsp_u || do_mcsp_v) { + if (do_mcsp_mom) { const Real tend_k = (2 * mcsp_tend_u(k) * ztodt * state_u(k) + mcsp_tend_u(k) * mcsp_tend_u(k) * ztodt * ztodt - + 2 * mcsp_tend_v(k) * ztodt * state_v(k) + mcsp_tend_v(k) * mcsp_tend_v(k) * ztodt * ztodt) / 2 / ztodt; + +2 * mcsp_tend_v(k) * ztodt * state_v(k) + mcsp_tend_v(k) * mcsp_tend_v(k) * ztodt * ztodt) / 2 / ztodt; avg_k += tend_k * state_pdel(k) / pdel_sum; } }, @@ -184,15 +206,15 @@ void Functions::zm_conv_mcsp_tend( // make sure kinetic energy correction is added to DSE tendency // to conserve total energy whenever momentum tendencies are calculated - if (do_mcsp_u || do_mcsp_v) { + if (do_mcsp_mom) { mcsp_ds_out(k) = mcsp_ds_out(k) - mcsp_avg_tend_k; } // update output tendencies - if (do_mcsp_t) ptend_s(k) = ptend_s(k) + mcsp_ds_out(k); - if (do_mcsp_q) ptend_q(k) = ptend_q(k) + mcsp_dq_out(k); - if (do_mcsp_u) ptend_u(k) = ptend_u(k) + mcsp_du_out(k); - if (do_mcsp_v) ptend_v(k) = ptend_v(k) + mcsp_dv_out(k); + if (do_mcsp_t) ptend_s(k) = ptend_s(k) + mcsp_ds_out(k); + if (do_mcsp_q) ptend_q(k) = ptend_q(k) + mcsp_dq_out(k); + if (do_mcsp_mom) ptend_u(k) = ptend_u(k) + mcsp_du_out(k); + if (do_mcsp_mom) ptend_v(k) = ptend_v(k) + mcsp_dv_out(k); // adjust units for diagnostic outputs if (do_mcsp_t) mcsp_ds_out(k) = mcsp_ds_out(k) / PC::Cpair.value; From a8a3a4666e21aa0ec2a7c57717b3d02fd60ce623 Mon Sep 17 00:00:00 2001 From: Walter Hannah Date: Thu, 2 Jul 2026 17:52:49 -0400 Subject: [PATCH 07/15] update MCSP momentum to allow negative coefficients --- components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 | 5 ++++- components/eam/src/physics/cam/zm/zm_conv_types.F90 | 7 +++++-- .../src/physics/zm/impl/zm_conv_mcsp_tend_impl.hpp | 5 ++++- components/eamxx/src/physics/zm/impl/zm_opts_impl.hpp | 7 ++++++- .../src/physics/zm/tests/zm_conv_mcsp_tend_tests.cpp | 11 +++++++---- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 b/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 index 2286cd86ed8e..0871651364fc 100644 --- a/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 +++ b/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 @@ -210,7 +210,10 @@ subroutine zm_conv_mcsp_tend( pcols, ncol, pver, pverp, & if (zm_param%mcsp_t_coeff>0) do_mcsp_t = .true. if (zm_param%mcsp_q_coeff>0) do_mcsp_q = .true. - if (zm_param%mcsp_mom_coeff>0) do_mcsp_mom = .true. + ! momentum is active for any non-zero coefficient; a negative coefficient + ! flips the leading sign in the tendency, switching up-gradient forcing + ! (default) to down-gradient forcing. + if (abs(zm_param%mcsp_mom_coeff)>0) do_mcsp_mom = .true. zm_avg_tend_s(1:ncol) = 0 zm_avg_tend_q(1:ncol) = 0 diff --git a/components/eam/src/physics/cam/zm/zm_conv_types.F90 b/components/eam/src/physics/cam/zm/zm_conv_types.F90 index d0134478aab5..ddae1f303f76 100644 --- a/components/eam/src/physics/cam/zm/zm_conv_types.F90 +++ b/components/eam/src/physics/cam/zm/zm_conv_types.F90 @@ -216,8 +216,11 @@ subroutine zm_param_set_for_testing(zm_param) zm_param%mcsp_enabled = .true. zm_param%mcsp_t_coeff = 0.3 zm_param%mcsp_q_coeff = 0 - zm_param%mcsp_mom_coeff = 0 - zm_param%mcsp_use_full_shear = .false. + ! Use a negative momentum coefficient so the unit tests exercise the + ! do_mcsp_mom path AND confirm the guards admit a negative coefficient + ! (down-gradient forcing). Must match the C++ zm_opts_init test setup. + zm_param%mcsp_mom_coeff = -0.05 + zm_param%mcsp_use_full_shear = .true. end subroutine zm_param_set_for_testing !=================================================================================================== diff --git a/components/eamxx/src/physics/zm/impl/zm_conv_mcsp_tend_impl.hpp b/components/eamxx/src/physics/zm/impl/zm_conv_mcsp_tend_impl.hpp index 886de6de8426..96e005651f8e 100644 --- a/components/eamxx/src/physics/zm/impl/zm_conv_mcsp_tend_impl.hpp +++ b/components/eamxx/src/physics/zm/impl/zm_conv_mcsp_tend_impl.hpp @@ -56,7 +56,10 @@ void Functions::zm_conv_mcsp_tend( const bool do_mcsp_t = (runtime_opt.mcsp_t_coeff > 0); const bool do_mcsp_q = (runtime_opt.mcsp_q_coeff > 0); - const bool do_mcsp_mom = (runtime_opt.mcsp_mom_coeff > 0); + // momentum is active for any non-zero coefficient; a negative coefficient + // flips the leading sign in the tendency, switching up-gradient forcing + // (default) to down-gradient forcing. + const bool do_mcsp_mom = (runtime_opt.mcsp_mom_coeff != 0); // Allocate temporary arrays uview_1d mcsp_tend_s, mcsp_tend_q, mcsp_tend_u, mcsp_tend_v; diff --git a/components/eamxx/src/physics/zm/impl/zm_opts_impl.hpp b/components/eamxx/src/physics/zm/impl/zm_opts_impl.hpp index 1a11bd62c596..ad8c3f2d9d83 100644 --- a/components/eamxx/src/physics/zm/impl/zm_opts_impl.hpp +++ b/components/eamxx/src/physics/zm/impl/zm_opts_impl.hpp @@ -41,7 +41,12 @@ void Functions::zm_opts_init() s_zm_opts.mcsp_enabled = true; s_zm_opts.mcsp_t_coeff = ZMC::MCSP_t_coeff_default; s_zm_opts.mcsp_q_coeff = ZMC::MCSP_q_coeff_default; - s_zm_opts.mcsp_mom_coeff = ZMC::MCSP_mom_coeff_default; + // Use a negative momentum coefficient so the unit tests exercise the + // do_mcsp_mom path AND confirm the guards admit a negative coefficient + // (down-gradient forcing). Must match the Fortran zm_param_set_for_testing + // setup. (This routine is only used by the standalone test harness; the real + // eamxx run overwrites s_zm_opts via load_runtime_options from XML.) + s_zm_opts.mcsp_mom_coeff = -0.05; s_zm_opts.mcsp_use_full_shear = true; // diff --git a/components/eamxx/src/physics/zm/tests/zm_conv_mcsp_tend_tests.cpp b/components/eamxx/src/physics/zm/tests/zm_conv_mcsp_tend_tests.cpp index 6c370eb29033..80f0d4644880 100644 --- a/components/eamxx/src/physics/zm/tests/zm_conv_mcsp_tend_tests.cpp +++ b/components/eamxx/src/physics/zm/tests/zm_conv_mcsp_tend_tests.cpp @@ -79,14 +79,17 @@ struct UnitWrap::UnitTest::TestZmConvMcspTend : public UnitWrap::UnitTest: REQUIRE(d_baseline.total(d_baseline.ptend_s) == d_test.total(d_test.mcsp_du_out)); REQUIRE(d_baseline.total(d_baseline.ptend_s) == d_test.total(d_test.mcsp_dv_out)); for (Int k = 0; k < d_baseline.total(d_baseline.ptend_s); ++k) { + // Momentum (u/v) fields use Approx() because the momentum tendency is + // built from cos(), which is std::cos in C++ but bfb_cos in Fortran; + // the two are not bit-for-bit. DSE (s) fields likewise use Approx(). REQUIRE(d_baseline.ptend_s[k] == Approx(d_test.ptend_s[k]).margin(margin)); REQUIRE(d_baseline.ptend_q[k] == d_test.ptend_q[k]); - REQUIRE(d_baseline.ptend_u[k] == d_test.ptend_u[k]); - REQUIRE(d_baseline.ptend_v[k] == d_test.ptend_v[k]); + REQUIRE(d_baseline.ptend_u[k] == Approx(d_test.ptend_u[k]).margin(margin)); + REQUIRE(d_baseline.ptend_v[k] == Approx(d_test.ptend_v[k]).margin(margin)); REQUIRE(d_baseline.mcsp_ds_out[k] == Approx(d_test.mcsp_ds_out[k]).margin(margin)); REQUIRE(d_baseline.mcsp_dq_out[k] == d_test.mcsp_dq_out[k]); - REQUIRE(d_baseline.mcsp_du_out[k] == d_test.mcsp_du_out[k]); - REQUIRE(d_baseline.mcsp_dv_out[k] == d_test.mcsp_dv_out[k]); + REQUIRE(d_baseline.mcsp_du_out[k] == Approx(d_test.mcsp_du_out[k]).margin(margin)); + REQUIRE(d_baseline.mcsp_dv_out[k] == Approx(d_test.mcsp_dv_out[k]).margin(margin)); } REQUIRE(d_baseline.total(d_baseline.mcsp_freq) == d_test.total(d_test.mcsp_freq)); REQUIRE(d_baseline.total(d_baseline.mcsp_freq) == d_test.total(d_test.mcsp_shear)); From 3b331dcc19892bf558843566d68cb6fcd7a5abf5 Mon Sep 17 00:00:00 2001 From: Walter Hannah Date: Thu, 13 Aug 2026 12:21:59 -0600 Subject: [PATCH 08/15] update zm_conv_mcsp.F90 to suppress 'unused arg' warnings --- components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 b/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 index 0871651364fc..e1a097d12ec8 100644 --- a/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 +++ b/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 @@ -44,7 +44,9 @@ module zm_conv_mcsp public :: zm_conv_mcsp_init ! Initialize MCSP output fields public :: zm_conv_mcsp_tend ! Perform MCSP tendency calculations +#endif public :: zm_conv_mcsp_hist ! Write diagnostic quantities to history files +#endif #ifdef SCREAM_CONFIG_IS_CMAKE public :: zm_conv_mcsp_calculate_shear ! just for testing #endif @@ -383,15 +385,14 @@ end subroutine zm_conv_mcsp_tend !=================================================================================================== +#ifndef SCREAM_CONFIG_IS_CMAKE subroutine zm_conv_mcsp_hist( lchnk, pcols, pver, & mcsp_dt_out, mcsp_dq_out, mcsp_du_out, mcsp_dv_out, & mcsp_freq, mcsp_shear, zm_depth ) !---------------------------------------------------------------------------- ! Purpose: write diagnostic quantities to history files !---------------------------------------------------------------------------- -#ifndef SCREAM_CONFIG_IS_CMAKE use cam_history, only: outfld -#endif !---------------------------------------------------------------------------- ! Arguments integer, intent(in) :: lchnk ! chunk identifier @@ -405,7 +406,6 @@ subroutine zm_conv_mcsp_hist( lchnk, pcols, pver, & real(r8), dimension(pcols), intent(in) :: mcsp_shear ! shear used to check against threshold real(r8), dimension(pcols), intent(in) :: zm_depth ! pressure depth of ZM heating !---------------------------------------------------------------------------- -#ifndef SCREAM_CONFIG_IS_CMAKE ! write out MCSP diagnostic history fields call outfld('MCSP_DT', mcsp_dt_out, pcols, lchnk ) call outfld('MCSP_DQ', mcsp_dq_out, pcols, lchnk ) @@ -414,11 +414,11 @@ subroutine zm_conv_mcsp_hist( lchnk, pcols, pver, & call outfld('MCSP_freq', mcsp_freq, pcols, lchnk ) call outfld('MCSP_shear', mcsp_shear, pcols, lchnk ) call outfld('MCSP_zm_depth', zm_depth, pcols, lchnk ) -#endif !---------------------------------------------------------------------------- return end subroutine zm_conv_mcsp_hist +#endif !=================================================================================================== From 2d9125dd9cf512d1e1577e34257b4caf3d222d3e Mon Sep 17 00:00:00 2001 From: Walter Hannah Date: Thu, 13 Aug 2026 12:28:40 -0600 Subject: [PATCH 09/15] minor updates to reduce build eamxx warnings --- components/eam/src/physics/cam/zm/zm_conv.F90 | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/components/eam/src/physics/cam/zm/zm_conv.F90 b/components/eam/src/physics/cam/zm/zm_conv.F90 index 11a5de6d0ac4..e41554ed7571 100644 --- a/components/eam/src/physics/cam/zm/zm_conv.F90 +++ b/components/eam/src/physics/cam/zm/zm_conv.F90 @@ -50,7 +50,8 @@ module zm_conv public zm_conv_main_init ! ZM scheme initialization public zm_conv_main ! ZM scheme calculations public zm_conv_evap ! ZM scheme evaporation of precip -#ifdef SCREAM_CONFIG_IS_CMAKE ! Public for testing only +#ifdef SCREAM_CONFIG_IS_CMAKE + ! Public for testing only public zm_calc_fractional_entrainment public zm_downdraft_properties public zm_cloud_properties @@ -128,7 +129,7 @@ subroutine zm_get_gather_index(pcols, ncol, pver, pverp, is_first_step, cape, dc integer, intent( out) :: lengath ! number of columns for gathered arrays !---------------------------------------------------------------------------- ! Local variables - integer :: i, ii + integer :: i !---------------------------------------------------------------------------- ! set local threshold to be used for zm_closure() if ( zm_param%trig_dcape .and. (.not.is_first_step) ) then @@ -239,7 +240,6 @@ subroutine zm_conv_main(pcols, ncol, pver, pverp, is_first_step, time_step, & real(r8), dimension(pcols) :: t_pcl_lcl ! parcel temperature at lcl [K] integer, dimension(pcols) :: lcl ! base level index of deep cumulus convection integer, dimension(pcols) :: lel ! index of highest theoretical convective plume - integer, dimension(pcols) :: lon ! index of onset level for deep convection integer, dimension(pcols) :: msemax_klev ! index of level with largest moist static energy real(r8), dimension(pcols,pver) :: t_pcl_m1 ! time n-1 parcel temperatures @@ -247,7 +247,6 @@ subroutine zm_conv_main(pcols, ncol, pver, pverp, is_first_step, time_step, & real(r8), dimension(pcols) :: t_pcl_lcl_m1 ! time n-1 parcel Temperature at LCL integer, dimension(pcols) :: lcl_m1 ! time n-1 base level index of deep cumulus convection integer, dimension(pcols) :: lel_m1 ! time n-1 index of highest theoretical convective plume - integer, dimension(pcols) :: lon_m1 ! time n-1 index of onset level for deep convection integer, dimension(pcols) :: msemax_klev_m1 ! time n-1 index of level with largest moist static energy real(r8), dimension(pcols) :: cape_m1 ! time n-1 CAPE @@ -295,7 +294,7 @@ subroutine zm_conv_main(pcols, ncol, pver, pverp, is_first_step, time_step, & type(zm_microp_st) :: loc_microp_st ! local (gathered) convective microphysics state and tendencies - integer i, ii, k, kk ! loop iterators + integer i, k ! loop iterators integer msg ! number of missing moisture levels at the top of model real(r8) qdifr From f6f5ea51694bba60139905ccdf5a749ca7c7c941 Mon Sep 17 00:00:00 2001 From: Walter Hannah Date: Thu, 13 Aug 2026 12:31:37 -0600 Subject: [PATCH 10/15] remove unused arguments --- components/eam/src/physics/cam/zm/zm_conv.F90 | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/components/eam/src/physics/cam/zm/zm_conv.F90 b/components/eam/src/physics/cam/zm/zm_conv.F90 index e41554ed7571..4002e593c572 100644 --- a/components/eam/src/physics/cam/zm/zm_conv.F90 +++ b/components/eam/src/physics/cam/zm/zm_conv.F90 @@ -111,7 +111,7 @@ end subroutine zm_conv_main_init !=================================================================================================== -subroutine zm_get_gather_index(pcols, ncol, pver, pverp, is_first_step, cape, dcape, & +subroutine zm_get_gather_index(pcols, ncol, is_first_step, cape, dcape, & cape_threshold_loc, gather_index, lengath) !---------------------------------------------------------------------------- ! Purpose: determine length of gathered arrays @@ -119,8 +119,6 @@ subroutine zm_get_gather_index(pcols, ncol, pver, pverp, is_first_step, cape, dc ! Arguments integer, intent(in ) :: pcols ! maximum number of columns integer, intent(in ) :: ncol ! actual number of columns - integer, intent(in ) :: pver ! number of mid-point vertical levels - integer, intent(in ) :: pverp ! number of interface vertical levels logical(btype), intent(in ) :: is_first_step ! flag for first step of run real(r8), dimension(pcols), intent(in ) :: cape ! conv. avail. potential energy [J] real(r8), dimension(pcols), intent(in ) :: dcape ! CAPE generated by dycor (dCAPE) [J] @@ -439,7 +437,7 @@ subroutine zm_conv_main(pcols, ncol, pver, pverp, is_first_step, time_step, & !---------------------------------------------------------------------------- ! determine whether active columns for gathering - call zm_get_gather_index(pcols, ncol, pver, pverp, is_first_step, cape, dcape, & + call zm_get_gather_index(pcols, ncol, is_first_step, cape, dcape, & cape_threshold_loc, gather_index, lengath) if (lengath.eq.0) then ! Deallocate local microphysics arrays before returning From 5f6682912d3e578f33c21d501a80c1c85af16dca Mon Sep 17 00:00:00 2001 From: Walter Hannah Date: Thu, 13 Aug 2026 12:44:02 -0600 Subject: [PATCH 11/15] reset MCSP shear back to default --- components/eamxx/cime_config/namelist_defaults_eamxx.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/eamxx/cime_config/namelist_defaults_eamxx.xml b/components/eamxx/cime_config/namelist_defaults_eamxx.xml index adb0eb3bdb91..054afdf29b73 100644 --- a/components/eamxx/cime_config/namelist_defaults_eamxx.xml +++ b/components/eamxx/cime_config/namelist_defaults_eamxx.xml @@ -293,7 +293,7 @@ be lost if SCREAM_HACK_XML is not enabled. 0.3 0.0 0.0 - true + false From cc80f03280ad0bfdad60b2bd16a124ed2265aabe Mon Sep 17 00:00:00 2001 From: Walter Hannah Date: Thu, 13 Aug 2026 13:29:16 -0600 Subject: [PATCH 12/15] add missing #ifndef --- components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 b/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 index e1a097d12ec8..3319b45d64a0 100644 --- a/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 +++ b/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 @@ -44,7 +44,7 @@ module zm_conv_mcsp public :: zm_conv_mcsp_init ! Initialize MCSP output fields public :: zm_conv_mcsp_tend ! Perform MCSP tendency calculations -#endif +#ifndef SCREAM_CONFIG_IS_CMAKE public :: zm_conv_mcsp_hist ! Write diagnostic quantities to history files #endif #ifdef SCREAM_CONFIG_IS_CMAKE From f5bf0b34f5c9eebe25dbf74deb751a5038e66586 Mon Sep 17 00:00:00 2001 From: Walter Hannah Date: Thu, 13 Aug 2026 13:31:46 -0600 Subject: [PATCH 13/15] fix default mcsp_use_full_shear in zm_functions --- components/eamxx/src/physics/zm/zm_functions.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/eamxx/src/physics/zm/zm_functions.hpp b/components/eamxx/src/physics/zm/zm_functions.hpp index 35c8a5bfbd1a..74a791a57dbe 100644 --- a/components/eamxx/src/physics/zm/zm_functions.hpp +++ b/components/eamxx/src/physics/zm/zm_functions.hpp @@ -175,9 +175,7 @@ struct Functions { mcsp_t_coeff = params.get("mcsp_t_coeff", ZMC::MCSP_t_coeff_default); mcsp_q_coeff = params.get("mcsp_q_coeff", ZMC::MCSP_q_coeff_default); mcsp_mom_coeff = params.get("mcsp_mom_coeff", ZMC::MCSP_mom_coeff_default); - // new-feature toggle: eamxx defaults ON (vector shear); Fortran/EAM defaults - // OFF to preserve bit-for-bit E3SMv3 (zonal-only shear that gates T/q) - mcsp_use_full_shear = params.get("mcsp_use_full_shear", true); + mcsp_use_full_shear = params.get("mcsp_use_full_shear", false); // determine SVP table size (add two to make the table slightly too big, just in case) plenest = static_cast(ZMC::tmax-ZMC::tmin) + 3; From d8a8ea92d61c73f62dbc863fcdde7c7f837dbf86 Mon Sep 17 00:00:00 2001 From: Walter Hannah Date: Thu, 13 Aug 2026 13:44:15 -0600 Subject: [PATCH 14/15] fix some linter errors in namelist_parameters.md --- .../docs/user-guide/namelist_parameters.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/components/eam/docs/user-guide/namelist_parameters.md b/components/eam/docs/user-guide/namelist_parameters.md index b61532962e04..78a50e23c774 100644 --- a/components/eam/docs/user-guide/namelist_parameters.md +++ b/components/eam/docs/user-guide/namelist_parameters.md @@ -135,20 +135,20 @@ | Conv. micro. Parameters | Description | Default value | | ------------------------- | ----------------------------------------------------------------- | ---------------------- | | `zmconv_microp` | Convective microphysics option in ZM convection scheme | `true` | -| `zmconv_auto_fac` | Cloud droplet-rain autoconversion enhancement factor in the convective microphysics scheme | `7.0` | -| `zmconv_accr_fac` | Cloud droplet-rain accretion enhancement factor in the convective microphysics scheme | `1.5` | -| `zmconv_micro_dcs` | Autoconversion size threshold for cloud ice to snow (m) | `150.E-6` | +| `zmconv_auto_fac` | Cloud droplet-rain autoconversion factor in the ZM microphysics | `7.0` | +| `zmconv_accr_fac` | Cloud droplet-rain accretion factor in the ZM microphysics | `1.5` | +| `zmconv_micro_dcs` | Autoconversion size threshold for cloud ice to snow (m) | `150.E-6` | | Mass flux adj. Parameters | Description | Default value | | ------------------------- | ----------------------------------------------------------------- | ---------------------- | -| `zmconv_clos_dyn_adj` | Apply mass flux adjustment to ZM convection scheme | `true` | - -| MCSP Parameters | Description | Default value | -| ---------------------------- | ----------------------------------------------------------------- | ---------------------- | -| `zmconv_mcsp_heat_coeff` | MCSP heating coefficient | `0.3` | -| `zmconv_mcsp_moisture_coeff` | MCSP moisture coefficient | `0.0` | -| `zmconv_mcsp_mom_coeff` | MCSP momentum coefficient (fraction of storm-relative shear) | `0.0` | -| `zmconv_mcsp_use_full_shear` | MCSP use full (u,v) shear vector instead of zonal-only shear | `.false.` | +| `zmconv_clos_dyn_adj` | Apply mass flux adjustment to ZM convection scheme | `true` | + +| MCSP Parameters | Description | Default value | +| ---------------------------- | ---------------------------------------------------------------| ---------------------- | +| `zmconv_mcsp_heat_coeff` | MCSP heating coefficient | `0.3` | +| `zmconv_mcsp_moisture_coeff` | MCSP moisture coefficient | `0.0` | +| `zmconv_mcsp_mom_coeff` | MCSP momentum coefficient (fraction of storm-relative shear) | `0.0` | +| `zmconv_mcsp_use_full_shear` | MCSP use full (u,v) shear vector instead of zonal-only shear | `.false.` | ## Cloud Feedback Model Intercomparison Project (CFMIP) Observation Simulator Package From 33cedd2ec6b052017fc73cb84ffab8d99df88ef3 Mon Sep 17 00:00:00 2001 From: Walter Hannah Date: Thu, 13 Aug 2026 13:45:17 -0600 Subject: [PATCH 15/15] update ZM tests --- .../physics/zm/tests/infra/zm_c2f_bridge.f90 | 6 +++--- .../physics/zm/tests/infra/zm_test_data.cpp | 20 +++++++++++-------- .../physics/zm/tests/infra/zm_test_data.hpp | 4 ++-- .../zm_conv_mcsp_calculate_shear_tests.cpp | 10 +++++++--- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/components/eamxx/src/physics/zm/tests/infra/zm_c2f_bridge.f90 b/components/eamxx/src/physics/zm/tests/infra/zm_c2f_bridge.f90 index fede6c75eed0..1c28b4d1c2f6 100644 --- a/components/eamxx/src/physics/zm/tests/infra/zm_c2f_bridge.f90 +++ b/components/eamxx/src/physics/zm/tests/infra/zm_c2f_bridge.f90 @@ -171,14 +171,14 @@ subroutine compute_cape_from_parcel_bridge_f(pcols, ncol, pver, pverp, num_cin, call compute_cape_from_parcel(pcols, ncol, pver, pverp, num_cin, num_msg, temperature, tv, sp_humidity, pint, msemax_klev, lcl_pmid, lcl_klev, zm_const, zm_param, parcel_qsat, parcel_temp, parcel_vtemp, eql_klev, cape) end subroutine compute_cape_from_parcel_bridge_f -subroutine zm_conv_mcsp_calculate_shear_bridge_f(pcols, ncol, pver, state_pmid, state_u, state_v, mcsp_shear) bind(C) +subroutine zm_conv_mcsp_calculate_shear_bridge_f(pcols, ncol, pver, state_pmid, state_u, state_v, shear_u, shear_v) bind(C) use zm_conv_mcsp, only : zm_conv_mcsp_calculate_shear integer(kind=c_int) , value, intent(in) :: pcols, ncol, pver real(kind=c_real) , intent(in), dimension(pcols, pver) :: state_pmid, state_u, state_v - real(kind=c_real) , intent(out), dimension(pcols) :: mcsp_shear + real(kind=c_real) , intent(out), dimension(pcols) :: shear_u, shear_v - call zm_conv_mcsp_calculate_shear(pcols, ncol, pver, state_pmid, state_u, state_v, mcsp_shear) + call zm_conv_mcsp_calculate_shear(pcols, ncol, pver, state_pmid, state_u, state_v, shear_u, shear_v) end subroutine zm_conv_mcsp_calculate_shear_bridge_f subroutine zm_conv_mcsp_tend_bridge_f(pcols, ncol, pver, pverp, ztodt, jctop, state_pmid, state_pint, state_pdel, state_s, state_q, state_u, state_v, ptend_zm_s, ptend_zm_q, ptend_s, ptend_q, ptend_u, ptend_v, mcsp_ds_out, mcsp_dq_out, mcsp_du_out, mcsp_dv_out, mcsp_freq, mcsp_shear, zm_depth) bind(C) diff --git a/components/eamxx/src/physics/zm/tests/infra/zm_test_data.cpp b/components/eamxx/src/physics/zm/tests/infra/zm_test_data.cpp index e064e0d410df..1f813bd51832 100644 --- a/components/eamxx/src/physics/zm/tests/infra/zm_test_data.cpp +++ b/components/eamxx/src/physics/zm/tests/infra/zm_test_data.cpp @@ -54,7 +54,7 @@ void compute_dilute_parcel_bridge_f(Int pcols, Int ncol, Int pver, Int num_msg, void compute_cape_from_parcel_bridge_f(Int pcols, Int ncol, Int pver, Int pverp, Int num_cin, Int num_msg, Real* temperature, Real* tv, Real* sp_humidity, Real* pint, Int* msemax_klev, Real* lcl_pmid, Int* lcl_klev, Real* parcel_qsat, Real* parcel_temp, Real* parcel_vtemp, Int* eql_klev, Real* cape); -void zm_conv_mcsp_calculate_shear_bridge_f(Int pcols, Int ncol, Int pver, Real* state_pmid, Real* state_u, Real* state_v, Real* mcsp_shear); +void zm_conv_mcsp_calculate_shear_bridge_f(Int pcols, Int ncol, Int pver, Real* state_pmid, Real* state_u, Real* state_v, Real* shear_u, Real* shear_v); void zm_conv_mcsp_tend_bridge_f(Int pcols, Int ncol, Int pver, Int pverp, Real ztodt, Int* jctop, Real* state_pmid, Real* state_pint, Real* state_pdel, Real* state_s, Real* state_q, Real* state_u, Real* state_v, Real* ptend_zm_s, Real* ptend_zm_q, Real* ptend_s, Real* ptend_q, Real* ptend_u, Real* ptend_v, Real* mcsp_ds_out, Real* mcsp_dq_out, Real* mcsp_du_out, Real* mcsp_dv_out, Real* mcsp_freq, Real* mcsp_shear, Real* zm_depth); @@ -864,7 +864,7 @@ void zm_conv_mcsp_calculate_shear_f(ZmConvMcspCalculateShearData& d) { d.transition(); zm_opts_init_f(); - zm_conv_mcsp_calculate_shear_bridge_f(d.pcols, d.ncol, d.pver, d.state_pmid, d.state_u, d.state_v, d.mcsp_shear); + zm_conv_mcsp_calculate_shear_bridge_f(d.pcols, d.ncol, d.pver, d.state_pmid, d.state_u, d.state_v, d.shear_u, d.shear_v); zm_opts_finalize_f(); d.transition(); } @@ -874,14 +874,15 @@ void zm_conv_mcsp_calculate_shear(ZmConvMcspCalculateShearData& d) zm_opts_init(); // create device views and copy - std::vector vec1dr_in(1); - ekat::host_to_device({d.mcsp_shear}, d.pcols, vec1dr_in); + std::vector vec1dr_in(2); + ekat::host_to_device({d.shear_u, d.shear_v}, d.pcols, vec1dr_in); std::vector vec2dr_in(3); ekat::host_to_device({d.state_pmid, d.state_u, d.state_v}, d.pcols, d.pver, vec2dr_in); view1dr_d - mcsp_shear_d(vec1dr_in[0]); + shear_u_d(vec1dr_in[0]), + shear_v_d(vec1dr_in[1]); view2dr_d state_pmid_d(vec2dr_in[0]), @@ -900,18 +901,21 @@ void zm_conv_mcsp_calculate_shear(ZmConvMcspCalculateShearData& d) // after this. const auto state_pmid_c = ekat::subview(state_pmid_d, i); const auto state_u_c = ekat::subview(state_u_d, i); + const auto state_v_c = ekat::subview(state_v_d, i); ZMF::zm_conv_mcsp_calculate_shear( team, pver, state_pmid_c, state_u_c, - mcsp_shear_d(i)); + state_v_c, + shear_u_d(i), + shear_v_d(i)); }); // Now get arrays - std::vector vec1dr_out = {mcsp_shear_d}; - ekat::device_to_host({d.mcsp_shear}, d.pcols, vec1dr_out); + std::vector vec1dr_out = {shear_u_d, shear_v_d}; + ekat::device_to_host({d.shear_u, d.shear_v}, d.pcols, vec1dr_out); zm_finalize_cxx(); } diff --git a/components/eamxx/src/physics/zm/tests/infra/zm_test_data.hpp b/components/eamxx/src/physics/zm/tests/infra/zm_test_data.hpp index 73ec02123945..36e2562833d0 100644 --- a/components/eamxx/src/physics/zm/tests/infra/zm_test_data.hpp +++ b/components/eamxx/src/physics/zm/tests/infra/zm_test_data.hpp @@ -345,7 +345,7 @@ struct ZmConvMcspCalculateShearData : public PhysicsTestData { Real *state_pmid, *state_u, *state_v; // Outputs - Real *mcsp_shear; + Real *shear_u, *shear_v; ZmConvMcspCalculateShearData(Int pcols_, Int ncol_, Int pver_) : PhysicsTestData({ @@ -354,7 +354,7 @@ struct ZmConvMcspCalculateShearData : public PhysicsTestData { }, { {&state_pmid, &state_u, &state_v}, - {&mcsp_shear} + {&shear_u, &shear_v} }), pcols(pcols_), ncol(ncol_), pver(pver_) {} diff --git a/components/eamxx/src/physics/zm/tests/zm_conv_mcsp_calculate_shear_tests.cpp b/components/eamxx/src/physics/zm/tests/zm_conv_mcsp_calculate_shear_tests.cpp index 5bc3adbb9412..14ac2d5ca783 100644 --- a/components/eamxx/src/physics/zm/tests/zm_conv_mcsp_calculate_shear_tests.cpp +++ b/components/eamxx/src/physics/zm/tests/zm_conv_mcsp_calculate_shear_tests.cpp @@ -65,9 +65,13 @@ struct UnitWrap::UnitTest::TestZmConvMcspCalculateShear : public UnitWrap::Un for (Int i = 0; i < num_runs; ++i) { ZmConvMcspCalculateShearData& d_baseline = baseline_data[i]; ZmConvMcspCalculateShearData& d_test = test_data[i]; - REQUIRE(d_baseline.total(d_baseline.mcsp_shear) == d_test.total(d_test.mcsp_shear)); - for (Int k = 0; k < d_baseline.total(d_baseline.mcsp_shear); ++k) { - REQUIRE(d_baseline.mcsp_shear[k] == d_test.mcsp_shear[k]); + REQUIRE(d_baseline.total(d_baseline.shear_u) == d_test.total(d_test.shear_u)); + for (Int k = 0; k < d_baseline.total(d_baseline.shear_u); ++k) { + REQUIRE(d_baseline.shear_u[k] == d_test.shear_u[k]); + } + REQUIRE(d_baseline.total(d_baseline.shear_v) == d_test.total(d_test.shear_v)); + for (Int k = 0; k < d_baseline.total(d_baseline.shear_v); ++k) { + REQUIRE(d_baseline.shear_v[k] == d_test.shear_v[k]); } } }