From 266cc6212e1f8bbdd303575b32bfffd4da5bff46 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 22 Aug 2022 12:06:43 -0600 Subject: [PATCH 001/406] Apply dust branch based off of release-clm5.0.34 to dev branch Do a diff of the dmleung DUSTEMIS branch based off of release-clm5.0.34 to ctsm5.1.dev106 and apply the diff after updating the paths for namelist_definition and lnd_import_export.F90 in the diff. git apply -3 dust.diff Diffs with conflicts: modified: src/biogeochem/DUSTMod.F90 modified: src/biogeophys/SoilStateInitTimeConstMod.F90 modified: src/main/controlMod.F90 modified: src/cpl/mct/lnd_import_export.F90 The changes in lnd_import_export were to change a check for negative LW that seems to be removed in the latest version. --- .../namelist_definition_ctsm.xml | 9 + src/biogeochem/DUSTMod.F90 | 466 ++++++++++++++++-- src/biogeophys/SoilStateInitTimeConstMod.F90 | 24 +- src/biogeophys/SoilStateType.F90 | 8 +- src/main/clm_varctl.F90 | 1 + src/main/controlMod.F90 | 13 + src/main/lnd2atmMod.F90 | 8 + 7 files changed, 496 insertions(+), 33 deletions(-) diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index a08795dd1f..ddc5d8372c 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -2796,4 +2796,13 @@ use case.) + + + +Full pathname of time-invariant roughness factor dataset for calculating drag partition of dust emission. + + + + diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index a86531ba62..7f7c92722c 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -15,7 +15,8 @@ module DUSTMod use shr_kind_mod , only : r8 => shr_kind_r8 use shr_log_mod , only : errMsg => shr_log_errMsg use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=) - use clm_varpar , only : dst_src_nbr, ndst, sz_nbr + use clm_varpar , only : dst_src_nbr, ndst, sz_nbr, & + natpft_lb, natpft_ub, natpft_size ! -dmleung added 24 Jul 2022 use clm_varcon , only : grav, spval use landunit_varcon , only : istcrop, istsoil use clm_varctl , only : iulog @@ -30,6 +31,9 @@ module DUSTMod use LandunitType , only : lun use ColumnType , only : col use PatchType , only : patch + use clm_instur , only : wt_lunit, wt_nat_patch ! dmleung added 24 Jul 2022 + use landunit_varcon , only : istsoil, istcrop ! dmleung added 24 Jul 2022 (refering to main/landunit_varcon.F90, for wt_lunit, istsoil=1 is nat veg, istcrop=2 is crop) + use pftconMod , only : noveg ! dmleung added 24 Jul 2022 ! ! !PUBLIC TYPES implicit none @@ -60,7 +64,32 @@ module DUSTMod real(r8), pointer, private :: vlc_trb_3_patch (:) ! turbulent deposition velocity 3(m/s) real(r8), pointer, private :: vlc_trb_4_patch (:) ! turbulent deposition velocity 4(m/s) real(r8), pointer, private :: mbl_bsn_fct_col (:) ! basin factor - + !########### added by dmleung 27 Nov 2021 ######################################################################## + real(r8), pointer, private :: dst_emiss_coeff_patch (:) ! dust emission coefficient (unitless) + real(r8), pointer, private :: wnd_frc_thr_patch (:) ! wet fluid threshold (m/s) + real(r8), pointer, private :: wnd_frc_thr_dry_patch (:) ! dry fluid threshold (m/s) + real(r8), pointer, private :: lnd_frc_mble_patch (:) ! land mobile fraction -dmleung + real(r8), pointer, private :: liq_frac_patch (:) ! liquid fraction of total water + real(r8), pointer, private :: wnd_frc_soil_patch (:) ! soil wind friction velocity (m/s) + real(r8), pointer, private :: gwc_patch (:) ! gravimetric water content (kg/kg) + !########### added by dmleung 2 Dec 2021 ######################################################################### + real(r8), pointer, private :: intrmtncy_fct_patch (:) ! intermittency factor, accounting for turbulence shutting down dust emissions (unitless) + real(r8), pointer, private :: stblty_patch (:) ! stability parameter for checking stability condition (stblty < 0 is unstable atmosphere) + real(r8), pointer, private :: u_mean_slt_patch (:) ! wind speed 0.1 m level of dust saltation (m/s) + real(r8), pointer, private :: u_sd_slt_patch (:) ! sd of wind speed 0.1 m level of dust saltation (m/s) + real(r8), pointer, private :: u_fld_thr_patch (:) ! fluid threshold wind speed 0.1 m level of dust saltation (m/s) + real(r8), pointer, private :: u_impct_thr_patch (:) ! impact threshold wind speed at 0.1 m level of dust saltation (m/s) + real(r8), pointer, private :: thr_crs_rate_patch (:) ! threshold crossing rate (unitless) + real(r8), pointer, private :: prb_crs_fld_thr_patch (:) ! probability of wind speed crossing fluid threshold + real(r8), pointer, private :: prb_crs_impct_thr_patch (:) ! probability of wind speed crossing impact threshold + !########### added by dmleung 17 Dec 2021 ######################################################################## + real(r8), pointer, private :: ustar_patch (:) ! output friction velocity for SP mode (m/s) + !########### added by dmleung 20 Dec 2021 ######################################################################## + real(r8), pointer, private :: ssr_patch (:) ! [dimless] integrated shear stress ratiio, defined by Okin (2008) and then integrated by Caroline Pierre et al. (2014) + real(r8), pointer, private :: lai_patch (:) ! [m2 leaf /m2 land] LAI+SAI for calculating Okin's drag partition, averaged to landunit level + real(r8), pointer, private :: frc_thr_rghn_fct_patch (:) ! [dimless] hybrid drag partition (or called roughness) factor + !########### added by dmleung 28 Jul 2022 ######################################################################## + real(r8), pointer, private :: wnd_frc_thr_std_patch (:) ! standardized fluid threshold friction velocity (m/s) contains procedure , public :: Init @@ -113,7 +142,32 @@ subroutine InitAllocate(this, bounds) allocate(this%vlc_trb_3_patch (begp:endp)) ; this%vlc_trb_3_patch (:) = nan allocate(this%vlc_trb_4_patch (begp:endp)) ; this%vlc_trb_4_patch (:) = nan allocate(this%mbl_bsn_fct_col (begc:endc)) ; this%mbl_bsn_fct_col (:) = nan - + !#### added by dmleung 27 Nov 2021 ##################################### + allocate(this%dst_emiss_coeff_patch (begp:endp)) ; this%dst_emiss_coeff_patch (:) = nan + allocate(this%wnd_frc_thr_patch (begp:endp)) ; this%wnd_frc_thr_patch (:) = nan + allocate(this%wnd_frc_thr_dry_patch (begp:endp)) ; this%wnd_frc_thr_dry_patch (:) = nan + allocate(this%lnd_frc_mble_patch (begp:endp)) ; this%lnd_frc_mble_patch (:) = nan + allocate(this%wnd_frc_soil_patch (begp:endp)) ; this%wnd_frc_soil_patch (:) = nan + allocate(this%gwc_patch (begp:endp)) ; this%gwc_patch (:) = nan + allocate(this%liq_frac_patch (begp:endp)) ; this%liq_frac_patch (:) = nan + !#### added by dmleung 2 Dec 2021 ###################################### + allocate(this%intrmtncy_fct_patch (begp:endp)) ; this%intrmtncy_fct_patch (:) = nan + allocate(this%stblty_patch (begp:endp)) ; this%stblty_patch (:) = nan + allocate(this%u_mean_slt_patch (begp:endp)) ; this%u_mean_slt_patch (:) = nan + allocate(this%u_sd_slt_patch (begp:endp)) ; this%u_sd_slt_patch (:) = nan + allocate(this%u_fld_thr_patch (begp:endp)) ; this%u_fld_thr_patch (:) = nan + allocate(this%u_impct_thr_patch (begp:endp)) ; this%u_impct_thr_patch (:) = nan + allocate(this%thr_crs_rate_patch (begp:endp)) ; this%thr_crs_rate_patch (:) = nan + allocate(this%prb_crs_fld_thr_patch (begp:endp)) ; this%prb_crs_fld_thr_patch (:) = nan + allocate(this%prb_crs_impct_thr_patch (begp:endp)) ; this%prb_crs_impct_thr_patch (:) = nan + !#### added by dmleung 17 Dec 2021 ###################################### + allocate(this%ustar_patch (begp:endp)) ; this%ustar_patch (:) = nan + !#### added by dmleung 17 Dec 2021 ###################################### + allocate(this%ssr_patch (begp:endp)) ; this%ssr_patch (:) = nan + allocate(this%lai_patch (begp:endp)) ; this%lai_patch (:) = nan + allocate(this%frc_thr_rghn_fct_patch (begp:endp)) ; this%frc_thr_rghn_fct_patch (:) = nan + !#### added by dmleung 28 Jul 2022 ###################################### + allocate(this%wnd_frc_thr_std_patch (begp:endp)) ; this%wnd_frc_thr_std_patch (:) = nan end subroutine InitAllocate !------------------------------------------------------------------------ @@ -158,6 +212,97 @@ subroutine InitHistory(this, bounds) avgflag='A', long_name='turbulent deposition velocity 4', & ptr_patch=this%vlc_trb_4_patch, default='inactive') + !#####added by dmleung 27 Nov 2021######################################### + this%dst_emiss_coeff_patch(begp:endp) = spval + call hist_addfld1d (fname='C_d', units='dimensionless', & + avgflag='A', long_name='dust emission coefficient', & + ptr_patch=this%dst_emiss_coeff_patch, set_lake=0._r8, set_urb=0._r8) + this%wnd_frc_thr_patch(begp:endp) = spval + call hist_addfld1d (fname='WND_FRC_FT', units='m/s', & + avgflag='A', long_name='fluid threshold friction velocity', & + ptr_patch=this%wnd_frc_thr_patch, set_lake=0._r8, set_urb=0._r8) + this%wnd_frc_thr_dry_patch(begp:endp) = spval + call hist_addfld1d (fname='WND_FRC_FT_DRY', units='m/s', & + avgflag='A', long_name='dry fluid threshold friction velocity', & + ptr_patch=this%wnd_frc_thr_dry_patch, set_lake=0._r8, set_urb=0._r8) + this%wnd_frc_soil_patch(begp:endp) = spval + call hist_addfld1d (fname='WND_FRC_SOIL', units='m/s', & + avgflag='A', long_name='soil surface wind friction velocity', & + ptr_patch=this%wnd_frc_soil_patch, set_lake=0._r8, set_urb=0._r8) + this%lnd_frc_mble_patch(begp:endp) = spval + call hist_addfld1d (fname='LND_FRC_MBLE', units='dimensionless', & + avgflag='A', long_name='land mobile fraction', & + ptr_patch=this%lnd_frc_mble_patch, set_lake=0._r8, set_urb=0._r8) + this%gwc_patch(begp:endp) = spval + call hist_addfld1d (fname='GWC', units='kg/kg', & + avgflag='A', long_name='gravimetric water content', & + ptr_patch=this%gwc_patch, set_lake=0._r8, set_urb=0._r8) + this%liq_frac_patch(begp:endp) = spval + call hist_addfld1d (fname='LIQ_FRAC', units='dimensionless', & + avgflag='A', long_name='fraction of total water that is liquid', & + ptr_patch=this%liq_frac_patch, set_lake=0._r8, set_urb=0._r8) + !#####added by dmleung 2 Dec 2021 ######################################### + this%u_mean_slt_patch(begp:endp) = spval + call hist_addfld1d (fname='U_S_MEAN', units='m/s', & + avgflag='A', long_name='mean wind velocity at saltation level', & + ptr_patch=this%u_mean_slt_patch, set_lake=0._r8, set_urb=0._r8) + this%u_sd_slt_patch(begp:endp) = spval + call hist_addfld1d (fname='U_S_SIGMA', units='m/s', & + avgflag='A', long_name='sd of wind velocity at saltation level', & + ptr_patch=this%u_sd_slt_patch, set_lake=0._r8, set_urb=0._r8) + this%stblty_patch(begp:endp) = spval + call hist_addfld1d (fname='ZETA', units='', & + avgflag='A', long_name='stability parameter', & + ptr_patch=this%stblty_patch, set_lake=0._r8, set_urb=0._r8) + this%u_fld_thr_patch(begp:endp) = spval + call hist_addfld1d (fname='U_FT', units='m/s', & + avgflag='A', long_name='fluid threshold velocity at saltation level', & + ptr_patch=this%u_fld_thr_patch, set_lake=0._r8, set_urb=0._r8) + this%u_impct_thr_patch(begp:endp) = spval + call hist_addfld1d (fname='U_IT', units='m/s', & + avgflag='A', long_name='impact threshold velocity at saltation level', & + ptr_patch=this%u_impct_thr_patch, set_lake=0._r8, set_urb=0._r8) + this%thr_crs_rate_patch(begp:endp) = spval + call hist_addfld1d (fname='ALPHA', units='', & + avgflag='A', long_name='threshold crossing rate', & + ptr_patch=this%thr_crs_rate_patch, set_lake=0._r8, set_urb=0._r8) + this%prb_crs_fld_thr_patch(begp:endp) = spval + call hist_addfld1d (fname='P_FT', units='', & + avgflag='A', long_name='probability of crossing fluid threshold', & + ptr_patch=this%prb_crs_fld_thr_patch, set_lake=0._r8, set_urb=0._r8) + this%prb_crs_impct_thr_patch(begp:endp) = spval + call hist_addfld1d (fname='P_IT', units='', & + avgflag='A', long_name='probability of crossing impact threshold', & + ptr_patch=this%prb_crs_impct_thr_patch, set_lake=0._r8, set_urb=0._r8) + this%intrmtncy_fct_patch(begp:endp) = spval + call hist_addfld1d (fname='ETA', units='', & + avgflag='A', long_name='intermittency factor', & + ptr_patch=this%intrmtncy_fct_patch, set_lake=0._r8, set_urb=0._r8) + !#####added by dmleung 2 Dec 2021 ######################################### + this%ustar_patch(begp:endp) = spval + call hist_addfld1d (fname='USTAR', units='m/s', & + avgflag='A', long_name='friction velocity', & + ptr_patch=this%ustar_patch, set_lake=0._r8, set_urb=0._r8) + !#####added by dmleung 20 Dec 2021 ######################################## + this%ssr_patch(begp:endp) = spval + call hist_addfld1d (fname='SSR', units='m/s', & + avgflag='A', long_name='Okin-Pierre shear stress ratio', & + ptr_patch=this%ssr_patch, set_lake=0._r8, set_urb=0._r8) + this%lai_patch(begp:endp) = spval + call hist_addfld1d (fname='LAI', units='m/s', & + avgflag='A', long_name='landunit-mean LAI for Okin-Pierre scheme', & + ptr_patch=this%lai_patch, set_lake=0._r8, set_urb=0._r8) + this%frc_thr_rghn_fct_patch(begp:endp) = spval + call hist_addfld1d (fname='FRC_THR_RGHN_FCT', units='dimensionless', & + avgflag='A', long_name='hybrid drag partition (or roughness) factor', & + ptr_patch=this%frc_thr_rghn_fct_patch, set_lake=0._r8, set_urb=0._r8) + !#####added by dmleung 28 Jul 2022 ######################################## + this%wnd_frc_thr_std_patch(begp:endp) = spval + call hist_addfld1d (fname='WND_FRC_FT_STD', units='m/s', & + avgflag='A', long_name='standardized fluid threshold friction velocity', & + ptr_patch=this%wnd_frc_thr_std_patch, set_lake=0._r8, set_urb=0._r8) + !########################################################################## + end subroutine InitHistory !----------------------------------------------------------------------- @@ -224,7 +369,7 @@ subroutine DustEmission (bounds, & real(r8) :: flx_mss_vrt_dst_ttl(bounds%begp:bounds%endp) real(r8) :: frc_thr_wet_fct real(r8) :: frc_thr_rgh_fct - real(r8) :: wnd_frc_thr_slt + !real(r8) :: wnd_frc_thr_slt ! dmleung commented and put below 2 Dec 2021 real(r8) :: wnd_rfr_thr_slt real(r8) :: wnd_frc_slt real(r8) :: lnd_frc_mbl(bounds%begp:bounds%endp) @@ -235,13 +380,43 @@ subroutine DustEmission (bounds, & real(r8) :: sumwt(bounds%begl:bounds%endl) ! sum of weights logical :: found ! temporary for error check integer :: index + !########### added by dmleung 27 Nov 2021 ######################################### + real(r8) :: tmp2 ! calculates the dry fluid threshold using Shao and Lu (2000) scheme; replace the tmp1 (Iversen and White, 1982) that was passed from Dustini to DustEmission; tmp2 will be calculated here 23 May 2020 -dmleung + real(r8) :: wnd_frc_thr_slt_std ! [m/s] The soil threshold friction speed at standard air density (1.2250 kg/m3) -jfk + real(r8) :: frag_expt ! fragmentation exponent, -dmleung 22 Jun 2021 + !########### added by dmleung 2 Dec 2021 for intermittency scheme ################# + real(r8) :: wnd_frc_thr_slt_it ! [m/s] created for impact threshold friction velocity, dmleung 9 Jun 2021 + real(r8) :: wnd_frc_thr_slt ! [m/s] used for wet fluid threshold friction velocity, dmleung 9 Jun 2021 + !########### added by dmleung 20 Dec 2021 for drag partition effect ################# + real(r8) :: K_length ! [dimless] normalized mean interobstacle distance, or called gap length (Okin, 2008) + !########### added by dmleung 22 Jul 2022 for LUH2 land cover #################### + real(r8) :: bare_frc ! LUH2 bare soil land cover fraction + real(r8) :: veg_frc ! LUH2 natural vegetation + crop land cover fraction ! ! constants ! real(r8), parameter :: cst_slt = 2.61_r8 ! [frc] Saltation constant real(r8), parameter :: flx_mss_fdg_fct = 5.0e-4_r8 ! [frc] Empir. mass flx tuning eflx_lh_vegt - real(r8), parameter :: vai_mbl_thr = 0.3_r8 ! [m2 m-2] VAI threshold quenching dust mobilization + !real(r8), parameter :: vai_mbl_thr = 0.3_r8 ! [m2 m-2] VAI threshold quenching dust mobilization + !real(r8), parameter :: vai_mbl_thr = 0.3_r8 ! [m2 m-2] VAI threshold quenching dust mobilization + !####### added by dmleung 27 Nov 2021 ########################################################################### character(len=*),parameter :: subname = 'DUSTEmission' + real(r8), parameter :: vai_mbl_thr = 1.0_r8 ! [m2 m-2] new VAI threshold; dmleung suggests 1 or 0.5, and the default 0.3 seems a bit too small -dmleung 27 Nov 2021 + real(r8), parameter :: Cd0 = 4.4e-5_r8 ! [dimless] proportionality constant in calculation of dust emission coefficient -jfk + real(r8), parameter :: Ca = 2.7_r8 ! [dimless] proportionality constant in scaling of dust emission exponent -jfk + real(r8), parameter :: Ce = 2.0_r8 ! [dimless] proportionality constant scaling exponential dependence of dust emission coefficient on standardized soil threshold friction speed -jfk + real(r8), parameter :: C_tune = 0.05_r8 ! [dimless] global tuning constant for vertical dust flux; set to produce ~same global dust flux in control sim (I_2000) as old parameterization -jfk + real(r8), parameter :: wnd_frc_thr_slt_std_min = 0.16_r8 ! [m/s] minimum standardized soil threshold friction speed -jfk + real(r8), parameter :: forc_rho_std = 1.2250_r8 ! [kg/m3] density of air at standard pressure (101325) and temperature (293 K) -jfk + real(r8), parameter :: dns_slt = 2650.0_r8 ! [kg m-3] Density of optimal saltation particles, dml 23 May 2020 + !####### added by dmleung 2 Dec 2021 for intermittency ########################################################## + real(r8), parameter :: B_it = 0.82_r8 ! [dimless] ratio = u_star_it / u_star_ft0 (may need to change into a fn of moisture later on) -dml + real(r8), parameter :: k = 0.4_r8 ! [dimless] von Karman constant -dml + !####### added by dmleung 2 Dec 2021 for Okin (2008) drag partition for plants ########################################################## + real(r8), parameter :: f_0 = 0.32_r8 ! [dimless] SSR in the immediate lee of a plant, dimensionless + real(r8), parameter :: c_e = 4.8_r8 ! [dimless] e-folding distance velocity recovery, dimensionless + !################################################################################################################ + !################################################################################################################ !------------------------------------------------------------------------ associate( & @@ -264,7 +439,34 @@ subroutine DustEmission (bounds, & mbl_bsn_fct => dust_inst%mbl_bsn_fct_col , & ! Input: [real(r8) (:) ] basin factor flx_mss_vrt_dst => dust_inst%flx_mss_vrt_dst_patch , & ! Output: [real(r8) (:,:) ] surface dust emission (kg/m**2/s) - flx_mss_vrt_dst_tot => dust_inst%flx_mss_vrt_dst_tot_patch & ! Output: [real(r8) (:) ] total dust flux back to atmosphere (pft) + flx_mss_vrt_dst_tot => dust_inst%flx_mss_vrt_dst_tot_patch , & ! Output: [real(r8) (:) ] total dust flux back to atmosphere (pft) + ! the following are added by dmleung 27 Nov 2021 + dst_emiss_coeff => dust_inst%dst_emiss_coeff_patch , & ! Output dust emission coefficient + wnd_frc_thr => dust_inst%wnd_frc_thr_patch , & ! output impact threshold -dmleung + wnd_frc_thr_dry => dust_inst%wnd_frc_thr_dry_patch , & ! output dry threshold + lnd_frc_mble => dust_inst%lnd_frc_mble_patch , & ! -dmleung, 3 Feb 2020 + wnd_frc_soil => dust_inst%wnd_frc_soil_patch , & ! soil friction velocity u_*s = (u_*)(f_eff) + gwc => dust_inst%gwc_patch , & ! output gravimetric water content + liq_frac => dust_inst%liq_frac_patch , & + ! added by dmleung 8 Jul 2019, recoded 2 Dec 2021 + intrmtncy_fct => dust_inst%intrmtncy_fct_patch , & + stblty => dust_inst%stblty_patch , & + u_mean_slt => dust_inst%u_mean_slt_patch , & + u_sd_slt => dust_inst%u_sd_slt_patch , & + u_fld_thr => dust_inst%u_fld_thr_patch , & + u_impct_thr => dust_inst%u_impct_thr_patch , & + thr_crs_rate => dust_inst%thr_crs_rate_patch , & + prb_crs_fld_thr => dust_inst%prb_crs_fld_thr_patch , & + prb_crs_impct_thr => dust_inst%prb_crs_impct_thr_patch , & + ! added by dmleung 17 Dec 2021 + roughfct => soilstate_inst%roughfct_patch , & + ustar => dust_inst%ustar_patch , & ! Output friction velocity for SP mode + ! added by dmleung 20 Dec 2021 + ssr => dust_inst%ssr_patch , & + lai => dust_inst%lai_patch , & + frc_thr_rghn_fct => dust_inst%frc_thr_rghn_fct_patch , & + ! added by dmleung 28 Jul 2022 + wnd_frc_thr_std => dust_inst%wnd_frc_thr_std_patch & ) ttlai(bounds%begp : bounds%endp) = 0._r8 @@ -342,6 +544,32 @@ subroutine DustEmission (bounds, & do fp = 1,num_nolakep p = filter_nolakep(fp) flx_mss_vrt_dst_tot(p) = 0.0_r8 + ! the following are added by dmleung 27 Nov 2021 + dst_emiss_coeff(p) = 0.0_r8 + wnd_frc_thr(p) = 0.0_r8 + wnd_frc_thr_dry(p) = 0.0_r8 + lnd_frc_mble(p) = 0.0_r8 + wnd_frc_soil(p) = 0.0_r8 + gwc(p) = 0.0_r8 + liq_frac(p) = 0.0_r8 + ! dmleung's edit, 8 Jul 2019; added by dmleung 2 Dec 2021 + u_mean_slt(p) = 0.0_r8 + u_sd_slt(p) = 0.0_r8 + stblty(p) = 0.0_r8 + u_fld_thr(p) = 0.0_r8 + u_impct_thr(p) = 0.0_r8 + thr_crs_rate(p) = 0.0_r8 + prb_crs_fld_thr(p) = 0.0_r8 + prb_crs_impct_thr(p) = 0.0_r8 + intrmtncy_fct(p) = 0.0_r8 + ! dmleung's edit for including friction velcoity for SP mode output, 17 Dec 2021 + ustar(p) = 0.0_r8 + ! dmleung's edit, 20 Dec 2021 + ssr(p) = 0.0_r8 + lai(p) = 0.0_r8 + frc_thr_rghn_fct(p) = 0.0_r8 + ! dmleung added 28 Jul 2022 + wnd_frc_thr_std(p) = 0.0_r8 end do do n = 1, ndst do fp = 1,num_nolakep @@ -356,6 +584,109 @@ subroutine DustEmission (bounds, & l = patch%landunit(p) g = patch%gridcell(p) + !################################################################################################ + ! put dust emission calculation here to output threshold friction velocity for the whole globe, + ! not just when lnd_frc_mbl = 0. Edited by dmleung 27 Nov 2021 + bd = (1._r8-watsat(c,1))*2.7e3_r8 ![kg m-3] Bulk density of dry surface soil + gwc_sfc = h2osoi_vol(c,1)*SHR_CONST_RHOFW/bd ![kg kg-1] Gravimetric H2O cont + if (gwc_sfc > gwc_thr(c)) then + frc_thr_wet_fct = sqrt(1.0_r8 + 1.21_r8 * (100.0_r8*(gwc_sfc - gwc_thr(c)))**0.68_r8) + else + frc_thr_wet_fct = 1.0_r8 + end if + + ! output moisture variables -dmleung, coded Jul 2020, recoded 18 Mar 2021, added to CLM5 27 Nov 2021 + gwc(p) = gwc_sfc ! output surface gravimetric water content + + ! slevis: adding liqfrac here, because related to effects from soil water + liqfrac = max( 0.0_r8, min( 1.0_r8, h2osoi_liq(c,1) / (h2osoi_ice(c,1)+h2osoi_liq(c,1)+1.0e-6_r8) ) ) !-dmleung 27 Nov 2021 + ! output liquid fraction -dmleung 27 Nov 2021 + liq_frac(p) = liqfrac + + !####################################################################################################### + ! calculate Shao & Lu (2000) dust emission threshold scheme here + ! use tmp1 from DUSTini for Iversen and White I&W (1982) (75 um is optimal); use tmp2 for S&L (2000) (107 um is optimal) + ! recoded to CLM5 27 Nov 2021 + !####################################################################################################### + + tmp2 = 1.0_r8*sqrt(0.0123_r8 * (dns_slt*grav*130.0e-6_r8 + 1.65e-4_r8/130.0e-6_r8)) ! calculate S&L (2000) scheme here for threshold; gamma = 1.65e-4 following S&L00, D_p = 127 um ~ 130 um following dmleung's dust paper. As this is a global constant, this line can be put outside the loop to save computational power. + wnd_frc_thr_dry(p) = tmp2 / sqrt(forc_rho(c)) ! output dry fluid threshold + wnd_frc_thr_slt = tmp2 / sqrt(forc_rho(c)) * frc_thr_wet_fct !* frc_thr_rgh_fct ! use as threshold in this module + wnd_frc_thr_slt_it = B_it * tmp2 / sqrt(forc_rho(c)) ! define impact threshold -dml 9 Jun 2021, recoded to CLM5 27 Nov 2021 + + !wnd_frc_thr_dry(p) = tmp1 / sqrt(forc_rho(c)) ! output dry fluid threshold + !wnd_frc_thr_slt = tmp1 / sqrt(forc_rho(c)) * frc_thr_wet_fct !* frc_thr_rgh_fct ! use as threshold in this module + !wnd_frc_thr_slt_it = B_it * tmp1 / sqrt(forc_rho(c)) ! define impact threshold -dmleung 9 Jun 2021, recoded to CLM5 27 Nov 2021 + ! the above formula is true for Iversen and White (1982) and Shao and Lu (2000) scheme -dmleung, 23 Feb 2020, added to CLM5 27 Nov 2021 + wnd_frc_thr(p) = wnd_frc_thr_slt ! output fluid threshold -dmleung + + ! use emission threshold to calculate standardized threshold and dust emission coefficient dmleung 27 Nov 2021 + wnd_frc_thr_slt_std = wnd_frc_thr_slt * sqrt(forc_rho(c) / forc_rho_std) ! standardized soil threshold friction speed -jfk (defined using fluid threshold + wnd_frc_thr_std(p) = wnd_frc_thr_slt_std ! output standardized fluid threshold -dmleung added 28 Jul 2022 + dst_emiss_coeff(p) = Cd0 * exp(-Ce * (wnd_frc_thr_slt_std - wnd_frc_thr_slt_std_min) / wnd_frc_thr_slt_std_min) ! save dust emission coefficient here for all grids, -dml, 1 Mar 2021 + + ! framentation exponent dmleung 27 Nov 2021; moved to this block 23 Dec 2021 + frag_expt = (Ca * (wnd_frc_thr_slt_std - wnd_frc_thr_slt_std_min) / wnd_frc_thr_slt_std_min) ! fragmentation exponent, defined in Kok et al. (2014a) -dmleung 27 Nov 2021 + if (frag_expt > 3_r8) then ! set fragmentation exponent to be 3 or 5 at maximum, to avoid local AOD blowup + frag_expt = 3_r8 + end if + + !################ drag partition effect, and soil friction velocity############################ + ! subsection on computing vegetation drag partition and hybrid drag partition factors + ! in our scheme, drag partition effect is applied on the wind instead of the threshold + ! -dmleung, 7 Jul 2021 , coded to CLM5 27 Nov 2021 + !############################################################################################## + ! the following comes from subr. frc_thr_rgh_fct_get + ! purpose: compute factor by which surface roughness increases threshold + ! friction velocity (currently a constant) + + if (lnd_frc_mbl(p) > 0.0_r8 .AND. tlai_lu(l)<=1_r8) then + ! vegetation drag partition equation following Gregory Okin (2008) + Caroline Pierre et al. (2014), dmleung 20 Dec 2021 + lai(p) = tlai_lu(l) ! LAI+SAI averaged to landunit level; saved for output + if (lai(p) < 0.1_r8) then + lai(p) = 0.1_r8 ! setting LAI ~ 0.1 to be a threshold value as computing K involves 1 / LAI + end if + ! calculate Okin's shear stress ratio (which is drag partition factor) using Pierre's equation + K_length = 2_r8 * (1_r8/lai(p) - 1_r8) ! Here LAI has to be non-zero to avoid blowup + ssr(p) = (K_length+f_0*c_e)/(K_length+c_e) + + !frc_thr_rgh_fct = (rockfrc(p)*(roughfct(p))**3_r8 + (vegefrc(p)+sparfrc(p))*(ssr(p))**3_r8 )**(0.3333_r8) ! land cover weighted mean using static GLCNMo bare land fraction LC0, dmleung 20 Dec 2021i; dmleung commented 24 Jul 2022 + + ! dmleung added calculation of LUH2 bare vs veg fraction within a grid 24 Jul 2022 + bare_frc = wt_lunit(g,istsoil) * wt_nat_patch(g,noveg) + veg_frc = wt_lunit(g,istsoil) * sum(wt_nat_patch(g,(noveg+1):natpft_ub)) + wt_lunit(g,istcrop) + + frc_thr_rgh_fct = (bare_frc*(roughfct(p))**3_r8 + veg_frc*(ssr(p))**3_r8 )**(0.3333_r8) ! land cover weighted mean using LUH2 land cover, dmleung 24 Jul 2022 + + + wnd_frc_slt = fv(p) * frc_thr_rgh_fct ! wnd_frc_slt will be used in the dust emission equation -dmleung + + frc_thr_rghn_fct(p) = frc_thr_rgh_fct ! save hybrid drag partition factor, dmleung 20 Dec 2021 + else + wnd_frc_slt = fv(p) ! The value here is not important since once lnd_frc_mbl(p) <= 0.0_r8 there will be no emission. + frc_thr_rghn_fct(p) = 0.0_r8 ! save hybrid drag partition factor, dmleung 20 Dec 2021 + end if + + !##########end of drag partition effect ####################################################### + + !############ Add Owen effect; if not, comment out this block !-dmleung, 27 Nov 2021 ########### + ! the following if-block comes from subr. wnd_frc_slt_get + ! purpose: compute the saltating friction velocity + ! theory: saltation roughens the boundary layer, AKA "Owen's effect" + + !if (u10(p) >= wnd_rfr_thr_slt) then + ! wnd_rfr_dlt = u10(p) - wnd_rfr_thr_slt + ! wnd_frc_slt_dlt = 0.003_r8 * wnd_rfr_dlt * wnd_rfr_dlt + ! wnd_frc_slt = wnd_frc_slt + wnd_frc_slt_dlt ! careful that RHS is now wnd_frc_slt instead of fv(p) + ! ! because wnd_frc_slt takes drag partition effect into account, but fv(p) doesn't. dmleung 27 Nov 2021 + !end if + !########## end of Owen effect ################################################################ + + ! save soil friction velocity and roughness effect before the if-statement, -dml, 1 Mar 2021, coded to CLM5 27 Nov 2021 + wnd_frc_soil(p) = wnd_frc_slt ! save soil friction velocity for CLM output, which has drag partition and Owen effect -dml + ustar(p) = fv(p) ! save friction velocity for SP mode (use_cn=0) since only CN/BGC mode (use_cn=1) has FV output -dmleung 17 Dec 2021 + ! save land mobile fraction + lnd_frc_mble(p) = lnd_frc_mbl(p) ! save land mobile fraction first, before the if-statement, -dml, 1 Mar 2021 ! only perform the following calculations if lnd_frc_mbl is non-zero if (lnd_frc_mbl(p) > 0.0_r8) then @@ -364,7 +695,7 @@ subroutine DustEmission (bounds, & ! purpose: compute factor by which surface roughness increases threshold ! friction velocity (currently a constant) - frc_thr_rgh_fct = 1.0_r8 + !frc_thr_rgh_fct = 1.0_r8 ! the following comes from subr. frc_thr_wet_fct_get ! purpose: compute factor by which soil moisture increases threshold friction velocity @@ -372,17 +703,17 @@ subroutine DustEmission (bounds, & ! modified 4/5/2002 (slevis) to use gravimetric instead of volumetric ! water content - bd = (1._r8-watsat(c,1))*2.7e3_r8 ![kg m-3] Bulk density of dry surface soil - gwc_sfc = h2osoi_vol(c,1)*SHR_CONST_RHOFW/bd ![kg kg-1] Gravimetric H2O cont - if (gwc_sfc > gwc_thr(c)) then - frc_thr_wet_fct = sqrt(1.0_r8 + 1.21_r8 * (100.0_r8*(gwc_sfc - gwc_thr(c)))**0.68_r8) - else - frc_thr_wet_fct = 1.0_r8 - end if + !bd = (1._r8-watsat(c,1))*2.7e3_r8 ![kg m-3] Bulk density of dry surface soil + !gwc_sfc = h2osoi_vol(c,1)*SHR_CONST_RHOFW/bd ![kg kg-1] Gravimetric H2O cont + !if (gwc_sfc > gwc_thr(c)) then + ! frc_thr_wet_fct = sqrt(1.0_r8 + 1.21_r8 * (100.0_r8*(gwc_sfc - gwc_thr(c)))**0.68_r8) + !else + ! frc_thr_wet_fct = 1.0_r8 + !end if ! slevis: adding liqfrac here, because related to effects from soil water - liqfrac = max( 0.0_r8, min( 1.0_r8, h2osoi_liq(c,1) / (h2osoi_ice(c,1)+h2osoi_liq(c,1)+1.0e-6_r8) ) ) + !liqfrac = max( 0.0_r8, min( 1.0_r8, h2osoi_liq(c,1) / (h2osoi_ice(c,1)+h2osoi_liq(c,1)+1.0e-6_r8) ) ) ! the following lines come from subr. dst_mbl ! purpose: adjust threshold friction velocity to acct for moisture and @@ -390,52 +721,125 @@ subroutine DustEmission (bounds, & ! subr. wnd_frc_thr_slt_get which computes dry threshold ! friction velocity for saltation - wnd_frc_thr_slt = tmp1 / sqrt(forc_rho(c)) * frc_thr_wet_fct * frc_thr_rgh_fct + !wnd_frc_thr_slt = tmp1 / sqrt(forc_rho(c)) * frc_thr_wet_fct * frc_thr_rgh_fct ! reset these variables which will be updated in the following if-block - wnd_frc_slt = fv(p) + !wnd_frc_slt = fv(p) flx_mss_hrz_slt_ttl = 0.0_r8 flx_mss_vrt_dst_ttl(p) = 0.0_r8 ! the following line comes from subr. dst_mbl ! purpose: threshold saltation wind speed - wnd_rfr_thr_slt = u10(p) * wnd_frc_thr_slt / fv(p) + wnd_rfr_thr_slt = u10(p) * wnd_frc_thr_slt / fv(p) ! keep and use if I want Z03 scheme -dmleung ! the following if-block comes from subr. wnd_frc_slt_get ! purpose: compute the saltating friction velocity ! theory: saltation roughens the boundary layer, AKA "Owen's effect" - if (u10(p) >= wnd_rfr_thr_slt) then - wnd_rfr_dlt = u10(p) - wnd_rfr_thr_slt - wnd_frc_slt_dlt = 0.003_r8 * wnd_rfr_dlt * wnd_rfr_dlt - wnd_frc_slt = fv(p) + wnd_frc_slt_dlt - end if + !if (u10(p) >= wnd_rfr_thr_slt) then + ! wnd_rfr_dlt = u10(p) - wnd_rfr_thr_slt + ! wnd_frc_slt_dlt = 0.003_r8 * wnd_rfr_dlt * wnd_rfr_dlt + ! wnd_frc_slt = fv(p) + wnd_frc_slt_dlt + !end if ! the following comes from subr. flx_mss_hrz_slt_ttl_Whi79_get ! purpose: compute vertically integrated streamwise mass flux of particles - if (wnd_frc_slt > wnd_frc_thr_slt) then - wnd_frc_rat = wnd_frc_thr_slt / wnd_frc_slt - flx_mss_hrz_slt_ttl = cst_slt * forc_rho(c) * (wnd_frc_slt**3.0_r8) * & - (1.0_r8 - wnd_frc_rat) * (1.0_r8 + wnd_frc_rat) * (1.0_r8 + wnd_frc_rat) / grav + !if (wnd_frc_slt > wnd_frc_thr_slt) then! if want to use fluid threshold for dust emission, uncomment this one, -dmleung 2 Dec 2021 + if (wnd_frc_slt > wnd_frc_thr_slt_it) then! if want to use impact threshold for dust emission, uncomment this one, -dmleung 2 Dec 2021 + + !################### for Zender et al. (2003) scheme -dmleung ########################### + !################ uncomment the below block if want to use Z03 scheme ################### + !wnd_frc_rat = wnd_frc_thr_slt / wnd_frc_slt + !flx_mss_hrz_slt_ttl = cst_slt * forc_rho(c) * (wnd_frc_slt**3.0_r8) * & + ! (1.0_r8 - wnd_frc_rat) * (1.0_r8 + wnd_frc_rat) * (1.0_r8 + wnd_frc_rat) / grav ! the following loop originates from subr. dst_mbl ! purpose: apply land sfc and veg limitations and global tuning factor ! slevis: multiply flx_mss_hrz_slt_ttl by liqfrac to incude the effect ! of frozen soil - flx_mss_hrz_slt_ttl = flx_mss_hrz_slt_ttl * lnd_frc_mbl(p) * mbl_bsn_fct(c) * & - flx_mss_fdg_fct * liqfrac + !flx_mss_hrz_slt_ttl = flx_mss_hrz_slt_ttl * lnd_frc_mbl(p) * mbl_bsn_fct(c) * & + ! flx_mss_fdg_fct * liqfrac + + ! dmleung moved to this block + !dst_slt_flx_rat_ttl = 100.0_r8 * exp( log(10.0_r8) * (13.4_r8 * mss_frc_cly_vld(c) - 6.0_r8) ) + !flx_mss_vrt_dst_ttl(p) = flx_mss_hrz_slt_ttl * dst_slt_flx_rat_ttl + !######################################################################################## + + !################### for Kok et al. (2014) scheme -dmleung ############################## + !################ uncomment the below block if want to use K14 scheme ################### + + ! if want to use fluid threshold for dust emission, uncomment this one, -dmleung 27 Nov 2021 + !flx_mss_vrt_dst_ttl(p) = dst_emiss_coeff(p) * mss_frc_cly_vld(c) * forc_rho(c) * ((wnd_frc_slt**2.0_r8 - wnd_frc_thr_slt**2.0_r8) / wnd_frc_thr_slt_std) * (wnd_frc_slt / wnd_frc_thr_slt)**frag_expt ! change forc_rho(g) to forc_rho(c) to avoid passing Nan values to the coupler -Longlei ! if want to use fluid threshold for dust emission, uncomment this one, -dml 27 Nov 2021 + + ! if want to use impact threshold for dust emission, uncomment this one, -dmleung 2 Dec 2021 + flx_mss_vrt_dst_ttl(p) = dst_emiss_coeff(p) * mss_frc_cly_vld(c) * forc_rho(c) * ((wnd_frc_slt**2.0_r8 - wnd_frc_thr_slt_it**2.0_r8) / wnd_frc_thr_slt_std) * (wnd_frc_slt / wnd_frc_thr_slt_it)**frag_expt ! if want to use impact threshold for dust emission, uncomment this one, -dml 2 Dec 2021 + + ! account for bare soil fraction, frozen soil fraction, and apply global tuning parameter (Kok et al. 2014) + flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) * lnd_frc_mbl(p) * C_tune * liqfrac + !######################################################################################## end if ! the following comes from subr. flx_mss_vrt_dst_ttl_MaB95_get ! purpose: diagnose total vertical mass flux of dust from vertically ! integrated streamwise mass flux - dst_slt_flx_rat_ttl = 100.0_r8 * exp( log(10.0_r8) * (13.4_r8 * mss_frc_cly_vld(c) - 6.0_r8) ) - flx_mss_vrt_dst_ttl(p) = flx_mss_hrz_slt_ttl * dst_slt_flx_rat_ttl + !dst_slt_flx_rat_ttl = 100.0_r8 * exp( log(10.0_r8) * (13.4_r8 * mss_frc_cly_vld(c) - 6.0_r8) ) ! dmleung commented and moved to the previous block + !flx_mss_vrt_dst_ttl(p) = flx_mss_hrz_slt_ttl * dst_slt_flx_rat_ttl + + !############## added by dmleung 2 Dec 2021 ############################################# + ! subsection for intermittency factor calculation + ! need to use with impact threshold and cannot be used with fluid threshold + ! Danny M. Leung, 24 Jun 2019, readded into CLM5 by dmleung 2 Dec 2021 + ! 2 Dec 2021 note: assume no buoyancy contribution to the wind fluctuation (u_sd_slt), so no obul(p) is needed. It is shown to be important for the wind fluctuations contribute little to the intermittency factor. We might add this back in the future revisions. + + ! mean lowpass-filtered wind speed at 0.1 m saltation height (assuming aerodynamic roughness length = 1e-4 m globally for ease; also assuming neutral condition) + u_mean_slt(p) = (wnd_frc_slt/k) * log(0.1_r8 / 1e-4_r8) + + ! sd of lowpass-filtered wind speed + !if (obul(p)==0) then + ! zetaobu = 0 + !else + !zetaobu = zii(p) / obul(p) ! For now zii is a constant of 1000 m in CLM -dml, 24 Aug 2021 + ! zetaobu = 1000_r8 / obul(p) ! For now zii is a constant of 1000 m in CLM -dml, 24 Aug 2021 + !end if + !stblty(p) = zetaobu ! zetaobu get outputted as the Obukhov stability parameter + stblty(p) = 0 ! -dmleung 2 Dec 2021: use 0 for now, assuming no buoyancy contribution. Might uncomment the above lines in future revisions. + if ((12_r8 - 0.5_r8 * stblty(p)) .GE. 0.001_r8) then + u_sd_slt(p) = wnd_frc_slt * (12_r8 - 0.5_r8 * stblty(p))**0.333_r8 + else + u_sd_slt(p) = 0.001_r8 ! should have used 0 theoretically; used 0.001 here to avoid undefined values + end if + + ! threshold velocities + ! Here wnd_frc_thr_slt is the fluid threshold; wnd_frc_thr_dry(p) is the dry fluid threshold; B_it*wnd_frc_thr_dry(p) is the impact threshold, -dml, 1 Mar 2021 + ! fluid threshold wind at 0.1 m saltation height + u_fld_thr(p) = (wnd_frc_thr_slt/k) * log(0.1_r8 / 1e-4_r8) + ! impact threshold wind at 0.1 m saltation height + u_impct_thr(p) = (wnd_frc_thr_slt_it/k) * log(0.1_r8 / 1e-4_r8) ! to avoid model error + + ! threshold crossing rate + thr_crs_rate(p) = (exp((u_fld_thr(p)**2_r8 - u_impct_thr(p)**2_r8 - 2_r8 * u_mean_slt(p) * (u_fld_thr(p) - u_impct_thr(p))) / (2_r8 * u_sd_slt(p)**2_r8)) + 1_r8)**(-1_r8) + + ! probability that lowpass-filtered wind speed does not exceed u_ft + prb_crs_fld_thr(p) = 0.5_r8 * (1_r8 + erf((u_fld_thr(p) - u_mean_slt(p)) / (1.414_r8 * u_sd_slt(p)))) + ! probability that lowpass-filtered wind speed does not exceed u_it + prb_crs_impct_thr(p) = 0.5_r8 * (1_r8 + erf((u_impct_thr(p) - u_mean_slt(p)) / (1.414_r8 * u_sd_slt(p)))) + + ! intermittency factor (from 0 to 1) + intrmtncy_fct(p) = 1_r8 - prb_crs_fld_thr(p) + thr_crs_rate(p) * (prb_crs_fld_thr(p) - prb_crs_impct_thr(p)) + + ! multiply dust emission flux by intermittency factor + if (intrmtncy_fct(p) /= intrmtncy_fct(p)) then ! if intrmtncy_fct(p) is not NaN then multiply by intermittency factor; this statement is needed because dust emission flx_mss_vrt_dst_ttl(p) has to be non NaN (at least zero) to be outputted, dmleung 9 Jun 2021 + flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) ! -dmleung + else + flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) * intrmtncy_fct(p) ! multiply dust flux by intermittency -dmleung + end if + + !############### end my subsection here -dmleung ######################################## end if ! lnd_frc_mbl > 0.0 diff --git a/src/biogeophys/SoilStateInitTimeConstMod.F90 b/src/biogeophys/SoilStateInitTimeConstMod.F90 index 9122b56890..2c1b00a8f6 100644 --- a/src/biogeophys/SoilStateInitTimeConstMod.F90 +++ b/src/biogeophys/SoilStateInitTimeConstMod.F90 @@ -172,6 +172,7 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) use clm_varcon , only : secspday, denh2o, denice, grlnd use clm_varctl , only : use_cn, use_lch4, use_fates use clm_varctl , only : iulog, fsurdat, paramfile, soil_layerstruct_predefined + use clm_varctl , only : rough_fct ! -dmleung added to CESM2/CLM5 17 Dec 2021 use landunit_varcon , only : istdlak, istwet, istsoil, istcrop, istice use column_varcon , only : icol_roof, icol_sunwall, icol_shadewall, icol_road_perv, icol_road_imperv use fileutils , only : getfil @@ -220,6 +221,7 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) real(r8) ,pointer :: sand3d (:,:) ! read in - soil texture: percent sand (needs to be a pointer for use in ncdio) real(r8) ,pointer :: clay3d (:,:) ! read in - soil texture: percent clay (needs to be a pointer for use in ncdio) real(r8) ,pointer :: organic3d (:,:) ! read in - organic matter: kg/m3 (needs to be a pointer for use in ncdio) + real(r8) ,pointer :: roughfct2d(:) ! read in - time-invariant roughness factor, dmleung added 17 Dec 2021 character(len=256) :: locfn ! local filename integer :: ipedof integer :: begp, endp @@ -369,6 +371,25 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) call ncd_pio_closefile(ncid) + !############ subsection for input data for new dust emission scheme ############## + + ! dmleung added to CESM2/CLM5 17 Dec 2021 + allocate(roughfct2d(begg:endg)) ! dmleung, 16 Jul 2020 + ! here to read roughness factor file, 16 Jul 2020 + !write(iulog,*) 'Attempting to read roughness factor data, by dmleung .....' + call getfil (rough_fct, locfn, 0) + call ncd_pio_openfile (ncid, locfn, 0) + call ncd_io(ncid=ncid, varname='F_eff', flag='read', data=roughfct2d, dim1name=grlnd, readvar=readvar) + !write(iulog,*) 'initialize pft level roughness factor from roughfct2d(g) to roughfct(p)' + + do p = begp,endp + g = patch%gridcell(p) + soilstate_inst%roughfct_patch(p) = roughfct2d(g) + end do + + call ncd_pio_closefile(ncid) + !################################################################################## + ! -------------------------------------------------------------------- ! get original soil depths to be used in interpolation of sand and clay ! -------------------------------------------------------------------- @@ -702,7 +723,8 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) do c = begc,endc g = col%gridcell(c) - soilstate_inst%gwc_thr_col(c) = 0.17_r8 + 0.14_r8 * clay3d(g,1) * 0.01_r8 + soilstate_inst%gwc_thr_col(c) = 0.01_r8*(0.17_r8*clay3d(g,1) + 0.0014_r8*clay3d(g,1)*clay3d(g,1)) !Fecan et al. (1999) -jfk, dmleung coded 27 Nov 2021 for CLM clay fraction + !soilstate_inst%gwc_thr_col(c) = 0.17_r8 + 0.14_r8 * clay3d(g,1) * 0.01_r8 -dmleung commented 27 Nov 2021 soilstate_inst%mss_frc_cly_vld_col(c) = min(clay3d(g,1) * 0.01_r8, 0.20_r8) end do diff --git a/src/biogeophys/SoilStateType.F90 b/src/biogeophys/SoilStateType.F90 index e301cc27b9..fa3d5a6aaf 100644 --- a/src/biogeophys/SoilStateType.F90 +++ b/src/biogeophys/SoilStateType.F90 @@ -24,12 +24,15 @@ module SoilStateType ! sand/ clay/ organic matter real(r8), pointer :: sandfrac_patch (:) ! patch sand fraction - real(r8), pointer :: clayfrac_patch (:) ! patch clay fraction + real(r8), pointer :: clayfrac_patch (:) ! patch clay fraction real(r8), pointer :: mss_frc_cly_vld_col (:) ! col mass fraction clay limited to 0.20 real(r8), pointer :: cellorg_col (:,:) ! col organic matter for gridcell containing column (1:nlevsoi) real(r8), pointer :: cellsand_col (:,:) ! sand value for gridcell containing column (1:nlevsoi) real(r8), pointer :: cellclay_col (:,:) ! clay value for gridcell containing column (1:nlevsoi) real(r8), pointer :: bd_col (:,:) ! col bulk density of dry soil material [kg/m^3] (CN) + !####################### for new dust emission scheme -dmleung ############################ + real(r8), pointer :: roughfct_patch (:) ! roughness factor, 17 dec 2021 + !########################################################################################### ! hydraulic properties real(r8), pointer :: hksat_col (:,:) ! col hydraulic conductivity at saturation (mm H2O /s) @@ -131,6 +134,9 @@ subroutine InitAllocate(this, bounds) allocate(this%cellclay_col (begc:endc,nlevsoi)) ; this%cellclay_col (:,:) = nan allocate(this%bd_col (begc:endc,nlevgrnd)) ; this%bd_col (:,:) = nan + !################ dmleung added 14 Dec 2021 ######################## + allocate(this%roughfct_patch (begp:endp)) ; this%roughfct_patch (:) = nan + !################################################################### allocate(this%hksat_col (begc:endc,nlevgrnd)) ; this%hksat_col (:,:) = spval allocate(this%hksat_min_col (begc:endc,nlevgrnd)) ; this%hksat_min_col (:,:) = spval allocate(this%hk_l_col (begc:endc,nlevgrnd)) ; this%hk_l_col (:,:) = nan diff --git a/src/main/clm_varctl.F90 b/src/main/clm_varctl.F90 index 9be9af2f73..3b7b1321ea 100644 --- a/src/main/clm_varctl.F90 +++ b/src/main/clm_varctl.F90 @@ -113,6 +113,7 @@ module clm_varctl character(len=fname_len), public :: nrevsn = ' ' ! restart data file name for branch run character(len=fname_len), public :: fsnowoptics = ' ' ! snow optical properties file name character(len=fname_len), public :: fsnowaging = ' ' ! snow aging parameters file name + character(len=fname_len), public :: rough_fct = ' ' ! roughness factor, dmleung, added 17 Dec 2021 character(len=fname_len), public :: fatmlndfrc = ' ' ! lnd frac file on atm grid ! only needed for LILAC and MCT drivers diff --git a/src/main/controlMod.F90 b/src/main/controlMod.F90 index a7bca1aa87..5645c8a0ca 100644 --- a/src/main/controlMod.F90 +++ b/src/main/controlMod.F90 @@ -282,6 +282,9 @@ subroutine control_init(dtime) use_grainproduct, use_snicar_frc, use_vancouver, use_mexicocity, use_noio, & use_nguardrail + ! dust emission, dmleung 14 Dec 2021 + namelist /clm_inparm/ & + rough_fct !-dmleung 17 Dec 2021 ! ---------------------------------------------------------------------- ! Default values @@ -653,6 +656,9 @@ subroutine control_spmd() call mpi_bcast (fsnowoptics, len(fsnowoptics), MPI_CHARACTER, 0, mpicom, ier) call mpi_bcast (fsnowaging, len(fsnowaging), MPI_CHARACTER, 0, mpicom, ier) + ! initialize input data for new dust emission module dmleung 14 Dec 2021 + call mpi_bcast (rough_fct, len(rough_fct), MPI_CHARACTER, 0, mpicom, ier)! added by dmleung, 17 Dec 2021 + ! Irrigation call mpi_bcast(irrigate, 1, MPI_LOGICAL, 0, mpicom, ier) @@ -904,6 +910,13 @@ subroutine control_print () write(iulog,*) ' Threshold above which the model keeps the lake landunit =', toosmall_lake write(iulog,*) ' Threshold above which the model keeps the wetland landunit =', toosmall_wetland write(iulog,*) ' Threshold above which the model keeps the urban landunits =', toosmall_urban + !##### for dmleung's input data for new dust emission module ##### + if (rough_fct == ' ') then ! -dmleung, 17 Dec 2021 + write(iulog,*) ' rough_fct surface dataset not set' + else + write(iulog,*) ' surface data = ',trim(rough_fct) + end if + !################################################################# if (use_cn) then if (suplnitro /= suplnNon)then write(iulog,*) ' Supplemental Nitrogen mode is set to run over Patches: ', & diff --git a/src/main/lnd2atmMod.F90 b/src/main/lnd2atmMod.F90 index ff7324e428..2638aa5f46 100644 --- a/src/main/lnd2atmMod.F90 +++ b/src/main/lnd2atmMod.F90 @@ -140,6 +140,14 @@ subroutine lnd2atm_minimal(bounds, & p2c_scale_type='unity', c2l_scale_type= 'urbanf', l2g_scale_type='unity') do g = bounds%begg,bounds%endg + !#####added by dmleung to avoid negative eflx_lwrad_out_grc(g) 11 Dec 2021 ################### + if (lnd2atm_inst%eflx_lwrad_out_grc(g) < 0) then + write(iulog,*) 'FIRE = ', lnd2atm_inst%eflx_lwrad_out_grc(g) + write(iulog,*) 'gridcell index = ', g + !write(iulog,*) 'sb constant = ', sb + lnd2atm_inst%eflx_lwrad_out_grc(g) = 100_r8 + end if + !##### should not be here but I am not sure why sometimes LW radiation is negative yet ####### lnd2atm_inst%t_rad_grc(g) = sqrt(sqrt(lnd2atm_inst%eflx_lwrad_out_grc(g)/sb)) end do From 33dcd9feed81a07177bc57b48d0ab2723614c374 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 22 Aug 2022 18:04:20 -0600 Subject: [PATCH 002/406] Set roughfct2d to 1.0 if rough_fct dataset is not being used --- src/biogeophys/SoilStateInitTimeConstMod.F90 | 33 ++++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/biogeophys/SoilStateInitTimeConstMod.F90 b/src/biogeophys/SoilStateInitTimeConstMod.F90 index 2c1b00a8f6..c5e2a72977 100644 --- a/src/biogeophys/SoilStateInitTimeConstMod.F90 +++ b/src/biogeophys/SoilStateInitTimeConstMod.F90 @@ -374,20 +374,27 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) !############ subsection for input data for new dust emission scheme ############## ! dmleung added to CESM2/CLM5 17 Dec 2021 - allocate(roughfct2d(begg:endg)) ! dmleung, 16 Jul 2020 - ! here to read roughness factor file, 16 Jul 2020 - !write(iulog,*) 'Attempting to read roughness factor data, by dmleung .....' - call getfil (rough_fct, locfn, 0) - call ncd_pio_openfile (ncid, locfn, 0) - call ncd_io(ncid=ncid, varname='F_eff', flag='read', data=roughfct2d, dim1name=grlnd, readvar=readvar) - !write(iulog,*) 'initialize pft level roughness factor from roughfct2d(g) to roughfct(p)' + if (rough_fct /= ' ') then + allocate(roughfct2d(begg:endg)) ! dmleung, 16 Jul 2020 + ! here to read roughness factor file, 16 Jul 2020 + !write(iulog,*) 'Attempting to read roughness factor data, by dmleung .....' + call getfil (rough_fct, locfn, 0) + call ncd_pio_openfile (ncid, locfn, 0) + call ncd_io(ncid=ncid, varname='F_eff', flag='read', data=roughfct2d, dim1name=grlnd, readvar=readvar) + !write(iulog,*) 'initialize pft level roughness factor from roughfct2d(g) to roughfct(p)' + + do p = begp,endp + g = patch%gridcell(p) + soilstate_inst%roughfct_patch(p) = roughfct2d(g) + end do + + call ncd_pio_closefile(ncid) + else + do p = begp,endp + soilstate_inst%roughfct_patch(p) = 1.0_r8 + end do - do p = begp,endp - g = patch%gridcell(p) - soilstate_inst%roughfct_patch(p) = roughfct2d(g) - end do - - call ncd_pio_closefile(ncid) + end if !################################################################################## ! -------------------------------------------------------------------- From c8c885ae4e6a1f06296ac9803b183f08234174c4 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 22 Aug 2022 18:05:01 -0600 Subject: [PATCH 003/406] Distinguish between normal fsurdat surface dataset and the rough_fct dataset --- src/main/controlMod.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/controlMod.F90 b/src/main/controlMod.F90 index 5645c8a0ca..49788b351c 100644 --- a/src/main/controlMod.F90 +++ b/src/main/controlMod.F90 @@ -912,9 +912,9 @@ subroutine control_print () write(iulog,*) ' Threshold above which the model keeps the urban landunits =', toosmall_urban !##### for dmleung's input data for new dust emission module ##### if (rough_fct == ' ') then ! -dmleung, 17 Dec 2021 - write(iulog,*) ' rough_fct surface dataset not set' + write(iulog,*) ' rough_fct surface roughness dataset not set' else - write(iulog,*) ' surface data = ',trim(rough_fct) + write(iulog,*) ' surface roughness data = ',trim(rough_fct) end if !################################################################# if (use_cn) then From 564875ed626419bd3867669786eb9083c212a473 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 22 Aug 2022 18:05:23 -0600 Subject: [PATCH 004/406] Always output FV --- src/biogeophys/FrictionVelocityMod.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/biogeophys/FrictionVelocityMod.F90 b/src/biogeophys/FrictionVelocityMod.F90 index 21a09fbc13..43f82495b7 100644 --- a/src/biogeophys/FrictionVelocityMod.F90 +++ b/src/biogeophys/FrictionVelocityMod.F90 @@ -231,12 +231,12 @@ subroutine InitHistory(this, bounds) ptr_patch=this%ram1_patch, default='inactive') end if - if (use_cn) then + !if (use_cn) then this%fv_patch(begp:endp) = spval call hist_addfld1d (fname='FV', units='m/s', & avgflag='A', long_name='friction velocity', & ptr_patch=this%fv_patch) - end if + !end if call hist_addfld1d (fname='RAH1', units='s/m', & avgflag='A', long_name='aerodynamical resistance ', & From f71bb054eaaaa0fbdb7bb760604edb55583cee3b Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 22 Aug 2022 18:06:27 -0600 Subject: [PATCH 005/406] Make sure to distinquish history variable names as it will otherwise abort, don't output USTAR as FV is already output in FrictionVelocity --- src/biogeochem/DUSTMod.F90 | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 7f7c92722c..b51468a62f 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -251,7 +251,7 @@ subroutine InitHistory(this, bounds) avgflag='A', long_name='sd of wind velocity at saltation level', & ptr_patch=this%u_sd_slt_patch, set_lake=0._r8, set_urb=0._r8) this%stblty_patch(begp:endp) = spval - call hist_addfld1d (fname='ZETA', units='', & + call hist_addfld1d (fname='ZETAOBU', units='', & avgflag='A', long_name='stability parameter', & ptr_patch=this%stblty_patch, set_lake=0._r8, set_urb=0._r8) this%u_fld_thr_patch(begp:endp) = spval @@ -263,7 +263,7 @@ subroutine InitHistory(this, bounds) avgflag='A', long_name='impact threshold velocity at saltation level', & ptr_patch=this%u_impct_thr_patch, set_lake=0._r8, set_urb=0._r8) this%thr_crs_rate_patch(begp:endp) = spval - call hist_addfld1d (fname='ALPHA', units='', & + call hist_addfld1d (fname='ALPHA_TC_RATE', units='', & avgflag='A', long_name='threshold crossing rate', & ptr_patch=this%thr_crs_rate_patch, set_lake=0._r8, set_urb=0._r8) this%prb_crs_fld_thr_patch(begp:endp) = spval @@ -279,10 +279,11 @@ subroutine InitHistory(this, bounds) avgflag='A', long_name='intermittency factor', & ptr_patch=this%intrmtncy_fct_patch, set_lake=0._r8, set_urb=0._r8) !#####added by dmleung 2 Dec 2021 ######################################### - this%ustar_patch(begp:endp) = spval - call hist_addfld1d (fname='USTAR', units='m/s', & - avgflag='A', long_name='friction velocity', & - ptr_patch=this%ustar_patch, set_lake=0._r8, set_urb=0._r8) + ! This is already output as FV in FrictionVelocityMod + !this%ustar_patch(begp:endp) = spval + !call hist_addfld1d (fname='USTAR', units='m/s', & + !avgflag='A', long_name='friction velocity', & + !ptr_patch=this%ustar_patch, set_lake=0._r8, set_urb=0._r8) !#####added by dmleung 20 Dec 2021 ######################################## this%ssr_patch(begp:endp) = spval call hist_addfld1d (fname='SSR', units='m/s', & From fd7e5e0e12f326de981fe75761e25d9b46aa77ce Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Fri, 14 Oct 2022 12:46:03 -0600 Subject: [PATCH 006/406] 14 Oct 2022: dmleung edited DUSTMod to properly use LUH2 to compute hybrid drag partitioning. (previous use of wt_lunit in DUSTMod was not working because wt_lunit was deallocated after CTSM initialization.) --- src/biogeochem/DUSTMod.F90 | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index b51468a62f..56721e2536 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -31,8 +31,8 @@ module DUSTMod use LandunitType , only : lun use ColumnType , only : col use PatchType , only : patch - use clm_instur , only : wt_lunit, wt_nat_patch ! dmleung added 24 Jul 2022 - use landunit_varcon , only : istsoil, istcrop ! dmleung added 24 Jul 2022 (refering to main/landunit_varcon.F90, for wt_lunit, istsoil=1 is nat veg, istcrop=2 is crop) + !use clm_instur , only : wt_lunit, wt_nat_patch ! dmleung added 24 Jul 2022 + !use landunit_varcon , only : istsoil, istcrop ! dmleung added 24 Jul 2022 (refering to main/landunit_varcon.F90, for wt_lunit, istsoil=1 is nat veg, istcrop=2 is crop) use pftconMod , only : noveg ! dmleung added 24 Jul 2022 ! ! !PUBLIC TYPES @@ -345,6 +345,9 @@ subroutine DustEmission (bounds, & ! !USES use shr_const_mod, only : SHR_CONST_RHOFW use subgridaveMod, only : p2g + !use clm_instur , only : wt_lunit, wt_nat_patch ! dmleung added 24 Jul 2022 + !use landunit_varcon , only : istsoil, istcrop ! dmleung added 24 Jul 2022 (refering to main/landunit_varcon.F90, for wt_lunit, istsoil=1 is nat veg, istcrop=2 is crop) + use pftconMod , only : noveg ! dmleung added 24 Jul 2022 ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds @@ -540,6 +543,8 @@ subroutine DustEmission (bounds, & end if end do + ! dmleung add output for bare_frc and veg_frc here if wanted !!!!!!!!!---------------------- + ! reset history output variables before next if-statement to avoid output = inf do fp = 1,num_nolakep @@ -654,11 +659,21 @@ subroutine DustEmission (bounds, & !frc_thr_rgh_fct = (rockfrc(p)*(roughfct(p))**3_r8 + (vegefrc(p)+sparfrc(p))*(ssr(p))**3_r8 )**(0.3333_r8) ! land cover weighted mean using static GLCNMo bare land fraction LC0, dmleung 20 Dec 2021i; dmleung commented 24 Jul 2022 ! dmleung added calculation of LUH2 bare vs veg fraction within a grid 24 Jul 2022 - bare_frc = wt_lunit(g,istsoil) * wt_nat_patch(g,noveg) - veg_frc = wt_lunit(g,istsoil) * sum(wt_nat_patch(g,(noveg+1):natpft_ub)) + wt_lunit(g,istcrop) + !bare_frc = wt_lunit(g,istsoil) * wt_nat_patch(g,noveg) + !veg_frc = wt_lunit(g,istsoil) * sum(wt_nat_patch(g,(noveg+1):natpft_ub)) + wt_lunit(g,istcrop) - frc_thr_rgh_fct = (bare_frc*(roughfct(p))**3_r8 + veg_frc*(ssr(p))**3_r8 )**(0.3333_r8) ! land cover weighted mean using LUH2 land cover, dmleung 24 Jul 2022 + !frc_thr_rgh_fct = (bare_frc*(roughfct(p))**3_r8 + veg_frc*(ssr(p))**3_r8 )**(0.3333_r8) ! land cover weighted mean using LUH2 land cover, dmleung 24 Jul 2022 + ! dmleung added calculation of LUH2 bare vs veg fraction within a grid 6 Oct 2022 + if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then + if (patch%itype(p) == noveg) then + frc_thr_rgh_fct = roughfct(p) + else + frc_thr_rgh_fct = ssr(p) + end if + else + frc_thr_rgh_fct = 1.0_r8 + end if wnd_frc_slt = fv(p) * frc_thr_rgh_fct ! wnd_frc_slt will be used in the dust emission equation -dmleung From c7e0cf8d001fee1772cadad518c2b7e4929e0f2c Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 21 Oct 2022 14:54:10 -0600 Subject: [PATCH 007/406] Add in externals to run a FHIST compset with CAM, CICE, and CICE5 from the cesm2_3_beta09 tag --- Externals.cfg | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Externals.cfg b/Externals.cfg index ff9882d574..a020acc12d 100644 --- a/Externals.cfg +++ b/Externals.cfg @@ -33,6 +33,29 @@ repo_url = https://github.com/nmizukami/mizuRoute hash = 34723c2 required = True +[cice5] +tag = cice5_20220204 +protocol = git +repo_url = https://github.com/ESCOMP/CESM_CICE5 +local_path = components/cice5 +required = True + +[cice6] +tag = cesm_cice6_2_0_22 +protocol = git +repo_url = https://github.com/ESCOMP/CESM_CICE +local_path = components/cice +externals = Externals.cfg +required = True + +[cam6] +tag = cam6_3_058 +protocol = git +repo_url = https://github.com/ESCOMP/CAM +local_path = components/cam +externals = Externals_CAM.cfg +required = True + [ccs_config] tag = ccs_config_cesm0.0.38 protocol = git From 5b6d00e9c8a6397ffbba7e51f80ef1b6bff24abf Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Fri, 11 Nov 2022 12:53:49 -0700 Subject: [PATCH 008/406] dmleung 11 Nov 2022: removed the added lines in lnd2atmMod.F90 that force the outgoing longwave radiation to be non-negative. --- src/main/lnd2atmMod.F90 | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/lnd2atmMod.F90 b/src/main/lnd2atmMod.F90 index 2638aa5f46..ff7324e428 100644 --- a/src/main/lnd2atmMod.F90 +++ b/src/main/lnd2atmMod.F90 @@ -140,14 +140,6 @@ subroutine lnd2atm_minimal(bounds, & p2c_scale_type='unity', c2l_scale_type= 'urbanf', l2g_scale_type='unity') do g = bounds%begg,bounds%endg - !#####added by dmleung to avoid negative eflx_lwrad_out_grc(g) 11 Dec 2021 ################### - if (lnd2atm_inst%eflx_lwrad_out_grc(g) < 0) then - write(iulog,*) 'FIRE = ', lnd2atm_inst%eflx_lwrad_out_grc(g) - write(iulog,*) 'gridcell index = ', g - !write(iulog,*) 'sb constant = ', sb - lnd2atm_inst%eflx_lwrad_out_grc(g) = 100_r8 - end if - !##### should not be here but I am not sure why sometimes LW radiation is negative yet ####### lnd2atm_inst%t_rad_grc(g) = sqrt(sqrt(lnd2atm_inst%eflx_lwrad_out_grc(g)/sb)) end do From 1592ebbf154782c3eac16f6ca2fa3843b2c57f6b Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Mon, 14 Nov 2022 16:37:39 -0700 Subject: [PATCH 009/406] dmleung changed the upper limit of the gragmentation exponent from 3 to 5. (14 Nov 2022) --- src/biogeochem/DUSTMod.F90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 56721e2536..421b046030 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -268,11 +268,11 @@ subroutine InitHistory(this, bounds) ptr_patch=this%thr_crs_rate_patch, set_lake=0._r8, set_urb=0._r8) this%prb_crs_fld_thr_patch(begp:endp) = spval call hist_addfld1d (fname='P_FT', units='', & - avgflag='A', long_name='probability of crossing fluid threshold', & + avgflag='A', long_name='probability of winds crossing fluid threshold', & ptr_patch=this%prb_crs_fld_thr_patch, set_lake=0._r8, set_urb=0._r8) this%prb_crs_impct_thr_patch(begp:endp) = spval call hist_addfld1d (fname='P_IT', units='', & - avgflag='A', long_name='probability of crossing impact threshold', & + avgflag='A', long_name='probability of winds crossing impact threshold', & ptr_patch=this%prb_crs_impct_thr_patch, set_lake=0._r8, set_urb=0._r8) this%intrmtncy_fct_patch(begp:endp) = spval call hist_addfld1d (fname='ETA', units='', & @@ -287,7 +287,7 @@ subroutine InitHistory(this, bounds) !#####added by dmleung 20 Dec 2021 ######################################## this%ssr_patch(begp:endp) = spval call hist_addfld1d (fname='SSR', units='m/s', & - avgflag='A', long_name='Okin-Pierre shear stress ratio', & + avgflag='A', long_name='Okin-Pierre vegetation shear stress ratio (drag partition factor)', & ptr_patch=this%ssr_patch, set_lake=0._r8, set_urb=0._r8) this%lai_patch(begp:endp) = spval call hist_addfld1d (fname='LAI', units='m/s', & @@ -611,7 +611,7 @@ subroutine DustEmission (bounds, & !####################################################################################################### ! calculate Shao & Lu (2000) dust emission threshold scheme here - ! use tmp1 from DUSTini for Iversen and White I&W (1982) (75 um is optimal); use tmp2 for S&L (2000) (107 um is optimal) + ! use tmp1 from DUSTini for Iversen and White I&W (1982) (75 um is optimal); use tmp2 for S&L (2000) (80 um is optimal) ! recoded to CLM5 27 Nov 2021 !####################################################################################################### @@ -633,8 +633,8 @@ subroutine DustEmission (bounds, & ! framentation exponent dmleung 27 Nov 2021; moved to this block 23 Dec 2021 frag_expt = (Ca * (wnd_frc_thr_slt_std - wnd_frc_thr_slt_std_min) / wnd_frc_thr_slt_std_min) ! fragmentation exponent, defined in Kok et al. (2014a) -dmleung 27 Nov 2021 - if (frag_expt > 3_r8) then ! set fragmentation exponent to be 3 or 5 at maximum, to avoid local AOD blowup - frag_expt = 3_r8 + if (frag_expt > 5_r8) then ! set fragmentation exponent to be 3 or 5 at maximum, to avoid local AOD blowup + frag_expt = 5_r8 end if !################ drag partition effect, and soil friction velocity############################ From 571dc5edb82fc3b12ed0cdb6f3543ca2f863c92c Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Mon, 14 Nov 2022 16:44:42 -0700 Subject: [PATCH 010/406] dmleung changed the upper limit of the fragmentation exponent from 3 to 5. (14 Nov 2022) --- src/biogeochem/DUSTMod.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 421b046030..0374d9e645 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -633,8 +633,8 @@ subroutine DustEmission (bounds, & ! framentation exponent dmleung 27 Nov 2021; moved to this block 23 Dec 2021 frag_expt = (Ca * (wnd_frc_thr_slt_std - wnd_frc_thr_slt_std_min) / wnd_frc_thr_slt_std_min) ! fragmentation exponent, defined in Kok et al. (2014a) -dmleung 27 Nov 2021 - if (frag_expt > 5_r8) then ! set fragmentation exponent to be 3 or 5 at maximum, to avoid local AOD blowup - frag_expt = 5_r8 + if (frag_expt > 5.0_r8) then ! set fragmentation exponent to be 3 or 5 at maximum, to avoid local AOD blowup + frag_expt = 5.0_r8 end if !################ drag partition effect, and soil friction velocity############################ From db585020d6c3742ba11115be4f367012030f0864 Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Mon, 14 Nov 2022 16:56:34 -0700 Subject: [PATCH 011/406] set 0.1 as the minimum LAI for the Okin-Pierre drag partioning equation; code cleanup dmleung 14 Nov 2022 --- src/biogeochem/DUSTMod.F90 | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 0374d9e645..8935c50585 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -648,10 +648,11 @@ subroutine DustEmission (bounds, & if (lnd_frc_mbl(p) > 0.0_r8 .AND. tlai_lu(l)<=1_r8) then ! vegetation drag partition equation following Gregory Okin (2008) + Caroline Pierre et al. (2014), dmleung 20 Dec 2021 - lai(p) = tlai_lu(l) ! LAI+SAI averaged to landunit level; saved for output - if (lai(p) < 0.1_r8) then - lai(p) = 0.1_r8 ! setting LAI ~ 0.1 to be a threshold value as computing K involves 1 / LAI - end if + lai(p) = tlai_lu(l)+0.1_r8 ! LAI+SAI averaged to landunit level; saved for output + if (lai(p) > 1_r8) then + lai(p) = 1_r8 ! setting LAI ~ 0.1 to be a min value (since the value goes to infinity when LAI=0) + end if ! and 1 to be a max value as computing K involves 1 / LAI + ! calculate Okin's shear stress ratio (which is drag partition factor) using Pierre's equation K_length = 2_r8 * (1_r8/lai(p) - 1_r8) ! Here LAI has to be non-zero to avoid blowup ssr(p) = (K_length+f_0*c_e)/(K_length+c_e) @@ -677,13 +678,13 @@ subroutine DustEmission (bounds, & wnd_frc_slt = fv(p) * frc_thr_rgh_fct ! wnd_frc_slt will be used in the dust emission equation -dmleung - frc_thr_rghn_fct(p) = frc_thr_rgh_fct ! save hybrid drag partition factor, dmleung 20 Dec 2021 + frc_thr_rghn_fct(p) = frc_thr_rgh_fct ! save and output hybrid drag partition factor, dmleung 20 Dec 2021 else wnd_frc_slt = fv(p) ! The value here is not important since once lnd_frc_mbl(p) <= 0.0_r8 there will be no emission. - frc_thr_rghn_fct(p) = 0.0_r8 ! save hybrid drag partition factor, dmleung 20 Dec 2021 + frc_thr_rghn_fct(p) = 0.0_r8 ! save and output hybrid drag partition factor, dmleung 20 Dec 2021 end if - !##########end of drag partition effect ####################################################### + !########## end of drag partition effect ####################################################### !############ Add Owen effect; if not, comment out this block !-dmleung, 27 Nov 2021 ########### ! the following if-block comes from subr. wnd_frc_slt_get @@ -855,7 +856,7 @@ subroutine DustEmission (bounds, & flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) * intrmtncy_fct(p) ! multiply dust flux by intermittency -dmleung end if - !############### end my subsection here -dmleung ######################################## + !############### end my intermittency subsection here -dmleung ######################################## end if ! lnd_frc_mbl > 0.0 From 5830686b1dbf5541a96648b6bc834b3c52ef29ec Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Mon, 14 Nov 2022 18:53:27 -0700 Subject: [PATCH 012/406] dmleung removed outputting ustar (USTAR) in the DustMod.F90 since Erik enabled outputting fv (FV) in FrictionVelocityMod.F90 14 Nov 2022 --- src/biogeochem/DUSTMod.F90 | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 8935c50585..5ec021a01d 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -82,8 +82,6 @@ module DUSTMod real(r8), pointer, private :: thr_crs_rate_patch (:) ! threshold crossing rate (unitless) real(r8), pointer, private :: prb_crs_fld_thr_patch (:) ! probability of wind speed crossing fluid threshold real(r8), pointer, private :: prb_crs_impct_thr_patch (:) ! probability of wind speed crossing impact threshold - !########### added by dmleung 17 Dec 2021 ######################################################################## - real(r8), pointer, private :: ustar_patch (:) ! output friction velocity for SP mode (m/s) !########### added by dmleung 20 Dec 2021 ######################################################################## real(r8), pointer, private :: ssr_patch (:) ! [dimless] integrated shear stress ratiio, defined by Okin (2008) and then integrated by Caroline Pierre et al. (2014) real(r8), pointer, private :: lai_patch (:) ! [m2 leaf /m2 land] LAI+SAI for calculating Okin's drag partition, averaged to landunit level @@ -161,8 +159,6 @@ subroutine InitAllocate(this, bounds) allocate(this%prb_crs_fld_thr_patch (begp:endp)) ; this%prb_crs_fld_thr_patch (:) = nan allocate(this%prb_crs_impct_thr_patch (begp:endp)) ; this%prb_crs_impct_thr_patch (:) = nan !#### added by dmleung 17 Dec 2021 ###################################### - allocate(this%ustar_patch (begp:endp)) ; this%ustar_patch (:) = nan - !#### added by dmleung 17 Dec 2021 ###################################### allocate(this%ssr_patch (begp:endp)) ; this%ssr_patch (:) = nan allocate(this%lai_patch (begp:endp)) ; this%lai_patch (:) = nan allocate(this%frc_thr_rghn_fct_patch (begp:endp)) ; this%frc_thr_rghn_fct_patch (:) = nan @@ -278,12 +274,6 @@ subroutine InitHistory(this, bounds) call hist_addfld1d (fname='ETA', units='', & avgflag='A', long_name='intermittency factor', & ptr_patch=this%intrmtncy_fct_patch, set_lake=0._r8, set_urb=0._r8) - !#####added by dmleung 2 Dec 2021 ######################################### - ! This is already output as FV in FrictionVelocityMod - !this%ustar_patch(begp:endp) = spval - !call hist_addfld1d (fname='USTAR', units='m/s', & - !avgflag='A', long_name='friction velocity', & - !ptr_patch=this%ustar_patch, set_lake=0._r8, set_urb=0._r8) !#####added by dmleung 20 Dec 2021 ######################################## this%ssr_patch(begp:endp) = spval call hist_addfld1d (fname='SSR', units='m/s', & @@ -402,7 +392,6 @@ subroutine DustEmission (bounds, & real(r8), parameter :: cst_slt = 2.61_r8 ! [frc] Saltation constant real(r8), parameter :: flx_mss_fdg_fct = 5.0e-4_r8 ! [frc] Empir. mass flx tuning eflx_lh_vegt !real(r8), parameter :: vai_mbl_thr = 0.3_r8 ! [m2 m-2] VAI threshold quenching dust mobilization - !real(r8), parameter :: vai_mbl_thr = 0.3_r8 ! [m2 m-2] VAI threshold quenching dust mobilization !####### added by dmleung 27 Nov 2021 ########################################################################### character(len=*),parameter :: subname = 'DUSTEmission' real(r8), parameter :: vai_mbl_thr = 1.0_r8 ! [m2 m-2] new VAI threshold; dmleung suggests 1 or 0.5, and the default 0.3 seems a bit too small -dmleung 27 Nov 2021 @@ -464,7 +453,6 @@ subroutine DustEmission (bounds, & prb_crs_impct_thr => dust_inst%prb_crs_impct_thr_patch , & ! added by dmleung 17 Dec 2021 roughfct => soilstate_inst%roughfct_patch , & - ustar => dust_inst%ustar_patch , & ! Output friction velocity for SP mode ! added by dmleung 20 Dec 2021 ssr => dust_inst%ssr_patch , & lai => dust_inst%lai_patch , & @@ -568,8 +556,6 @@ subroutine DustEmission (bounds, & prb_crs_fld_thr(p) = 0.0_r8 prb_crs_impct_thr(p) = 0.0_r8 intrmtncy_fct(p) = 0.0_r8 - ! dmleung's edit for including friction velcoity for SP mode output, 17 Dec 2021 - ustar(p) = 0.0_r8 ! dmleung's edit, 20 Dec 2021 ssr(p) = 0.0_r8 lai(p) = 0.0_r8 @@ -701,7 +687,6 @@ subroutine DustEmission (bounds, & ! save soil friction velocity and roughness effect before the if-statement, -dml, 1 Mar 2021, coded to CLM5 27 Nov 2021 wnd_frc_soil(p) = wnd_frc_slt ! save soil friction velocity for CLM output, which has drag partition and Owen effect -dml - ustar(p) = fv(p) ! save friction velocity for SP mode (use_cn=0) since only CN/BGC mode (use_cn=1) has FV output -dmleung 17 Dec 2021 ! save land mobile fraction lnd_frc_mble(p) = lnd_frc_mbl(p) ! save land mobile fraction first, before the if-statement, -dml, 1 Mar 2021 ! only perform the following calculations if lnd_frc_mbl is non-zero From 36d1feec1da582f8c4280ee5ea1393aa8ca5c8de Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Tue, 31 Jan 2023 01:29:30 -0700 Subject: [PATCH 013/406] 31 Jan 2023: dmleung added capabilities of reading stream files for Prigent's roughness data --- bld/CLMBuildNamelist.pm | 10 + bld/namelist_files/namelist_defaults_ctsm.xml | 11 + .../namelist_definition_ctsm.xml | 15 + src/biogeochem/DUSTMod.F90 | 42 +- .../share_esmf/PrigentRoughnessStreamType.F90 | 397 ++++++++++++++++++ 5 files changed, 469 insertions(+), 6 deletions(-) create mode 100644 src/cpl/share_esmf/PrigentRoughnessStreamType.F90 diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index cbd5e3ce44..d078b3869c 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -1560,6 +1560,7 @@ sub process_namelist_inline_logic { setup_logic_snowpack($opts, $nl_flags, $definition, $defaults, $nl); setup_logic_fates($opts, $nl_flags, $definition, $defaults, $nl); setup_logic_misc($opts, $nl_flags, $definition, $defaults, $nl); + setup_logic_prigent_roughness($opts, $nl_flags, $definition, $defaults, $nl); # dmleung added 31 Dec 2022 ######################################### # namelist group: atm2lnd_inparm @@ -4178,6 +4179,15 @@ sub setup_logic_misc { #------------------------------------------------------------------------------- +sub setup_logic_prigent_roughness { + # dmleung added on 31 Dec 2022 for reading Prigent's roughness stream file + my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldfilename_prigentroughness' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_meshfile_prigentroughness' ); +} + +#------------------------------------------------------------------------------- + sub write_output_files { my ($opts, $nl_flags, $defaults, $nl) = @_; diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 2686d62b9a..3d0f5ef5e1 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -2524,6 +2524,17 @@ lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne0np4.CONUS.ne30x8_hist_78pfts lnd/clm2/paramdata/finundated_inversiondata_0.9x1_ESMFmesh_cdf5_130621.nc + + + + + + +/glade/u/home/dleung/CESM2/ctsm_dustemis_dev/cdf5_Z0a_Prigent-Globe-025x025-01242023.nc +/glade/u/home/dleung/CESM2/ctsm_dustemis_dev/lnd_mesh.nc + diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index ddc5d8372c..6cdf4e3ce5 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1693,6 +1693,21 @@ Mapping method from Nitrogen deposition input file to the model resolution copy = copy using the same indices + + + + + + +Filename of input stream data for aeolian roughness length (from Prigent's roughness dataset) + + + +mesh filename of input stream data for aeolian roughness length (from Prigent's roughness dataset) + + diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 5ec021a01d..381ecfe41d 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -88,6 +88,9 @@ module DUSTMod real(r8), pointer, private :: frc_thr_rghn_fct_patch (:) ! [dimless] hybrid drag partition (or called roughness) factor !########### added by dmleung 28 Jul 2022 ######################################################################## real(r8), pointer, private :: wnd_frc_thr_std_patch (:) ! standardized fluid threshold friction velocity (m/s) + !########### added by dmleung 31 Dec 2022 ######################################################################## + type(prigentroughnessstream_type), private :: prigentroughnessstream ! Prigent roughness stream data + real(r8), pointer, private :: dpfct_rock_patch (:) ! [fraction] rock drag partition factor, time-constant contains procedure , public :: Init @@ -105,13 +108,15 @@ module DUSTMod contains !------------------------------------------------------------------------ - subroutine Init(this, bounds) + !##### dmleung edited for initializing stream files 31 Dec 2022 ######## + subroutine Init(this, bounds, NLFilename) class(dust_type) :: this type(bounds_type), intent(in) :: bounds call this%InitAllocate (bounds) call this%InitHistory (bounds) + call this%prigentroughnessstream%Init( bounds, NLFilename ) ! dmleung added 31 Dec 2022 call this%InitCold (bounds) call this%InitDustVars (bounds) @@ -164,6 +169,8 @@ subroutine InitAllocate(this, bounds) allocate(this%frc_thr_rghn_fct_patch (begp:endp)) ; this%frc_thr_rghn_fct_patch (:) = nan !#### added by dmleung 28 Jul 2022 ###################################### allocate(this%wnd_frc_thr_std_patch (begp:endp)) ; this%wnd_frc_thr_std_patch (:) = nan + !#### added by dmleung 31 Dec 2022 ###################################### + allocate(this%dpfct_rock_patch (begp:endp)) ; this%dpfct_rock_patch (:) = nan end subroutine InitAllocate !------------------------------------------------------------------------ @@ -292,16 +299,23 @@ subroutine InitHistory(this, bounds) call hist_addfld1d (fname='WND_FRC_FT_STD', units='m/s', & avgflag='A', long_name='standardized fluid threshold friction velocity', & ptr_patch=this%wnd_frc_thr_std_patch, set_lake=0._r8, set_urb=0._r8) + !#####added by dmleung 31 Dec 2022 ######################################## + this%dpfct_rock_patch(begp:endp) = spval + call hist_addfld1d (fname='DPFCT_ROCK', units='m/s', & + avgflag='A', long_name='rock drag partition factor', & + ptr_patch=this%dpfct_rock_patch, set_lake=0._r8, set_urb=0._r8) !########################################################################## end subroutine InitHistory !----------------------------------------------------------------------- - subroutine InitCold(this, bounds) + subroutine InitCold(this, bounds) !dmleung commented 31 Dec 2022 + !subroutine InitCold(this, bounds) ! ! !ARGUMENTS: - class (dust_type) :: this + class(dust_type), intent(inout) :: this ! dmleung used class instead of type, 31 Dec 2022 type(bounds_type), intent(in) :: bounds + !type(dust_type), intent(inout) :: dust_inst ! ! !LOCAL VARIABLES: integer :: c,l @@ -317,6 +331,19 @@ subroutine InitCold(this, bounds) end if end do + !associate( & + ! dpfct_rock => this%dpfct_rock_patch & + ! ) + ! Caulculate Drag Partition factor, dmleung added 31 Dec 2022 + if ( this%prigentroughnessstream%useStreams() )then !if usestreams == true, and it should be always true + call this%prigentroughnessstream%CalcDragPartition( bounds, & + num_nolakep, filter_nolakep, this%dpfct_rock_patch(bounds%begp:bounds%endp) ) + else + + call endrun( "ERROR:: Drag partitioning MUST now use a streams file of aeolian roughness length to calculate, it can no longer read from the fsurdat file" ) + end if + !end associate + end subroutine InitCold !------------------------------------------------------------------------ @@ -452,13 +479,15 @@ subroutine DustEmission (bounds, & prb_crs_fld_thr => dust_inst%prb_crs_fld_thr_patch , & prb_crs_impct_thr => dust_inst%prb_crs_impct_thr_patch , & ! added by dmleung 17 Dec 2021 - roughfct => soilstate_inst%roughfct_patch , & + roughfct => soilstate_inst%roughfct_patch , & ! dmleung replaced it by dpfct_rock below, 31 Dec 2022 ! added by dmleung 20 Dec 2021 ssr => dust_inst%ssr_patch , & lai => dust_inst%lai_patch , & frc_thr_rghn_fct => dust_inst%frc_thr_rghn_fct_patch , & ! added by dmleung 28 Jul 2022 - wnd_frc_thr_std => dust_inst%wnd_frc_thr_std_patch & + wnd_frc_thr_std => dust_inst%wnd_frc_thr_std_patch , & + ! added by dmleung 31 Dec 2022 + dpfct_rock => dust_inst%dpfct_rock_patch & ! dmleung used roughfct (roughness factor) instead of dpfct_rock (rock drag partition factor) here. Could change it back to dpfct_rock here and below later, 31 Dec 2022 ) ttlai(bounds%begp : bounds%endp) = 0._r8 @@ -654,7 +683,8 @@ subroutine DustEmission (bounds, & ! dmleung added calculation of LUH2 bare vs veg fraction within a grid 6 Oct 2022 if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then if (patch%itype(p) == noveg) then - frc_thr_rgh_fct = roughfct(p) + !frc_thr_rgh_fct = roughfct(p) ! dmleung commented out 13 Dec 2022 and added next line + frc_thr_rgh_fct = dpfct_rock(p) else frc_thr_rgh_fct = ssr(p) end if diff --git a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 new file mode 100644 index 0000000000..7da0e7bf50 --- /dev/null +++ b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 @@ -0,0 +1,397 @@ +module PrigentRoughnessStreamType +!dmleung modified based on ch4FInundatedStreamType on 17 Nov 2022 +#include "shr_assert.h" ! What is this? In many modules but not dust module + + !----------------------------------------------------------------------- + ! !DESCRIPTION: + ! Contains methods for reading in the Prigent et al. (1997) roughness length streams file. dmleung 22 Nov 2022 + ! + ! !USES + use ESMF + use dshr_strdata_mod , only : shr_strdata_type + use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_cl + use shr_log_mod , only : errMsg => shr_log_errMsg + use spmdMod , only : mpicom, masterproc + use clm_varctl , only : iulog + use abortutils , only : endrun + use decompMod , only : bounds_type + !use ch4varcon , only : finundation_mtd ! dmleung commented out. need to replace anything here? + + ! !PUBLIC TYPES: + implicit none + private + + type, public :: prigentroughnessstream_type + ! dmleung commented the next five lines. Does it need any replacement here? + real(r8), pointer, private :: prigent_rghn (:) ! Prigent et al. (1997) roughness length (m) + !real(r8), pointer, private :: zwt0_gdc (:) ! col coefficient for determining finundated (m) + !real(r8), pointer, private :: f0_gdc (:) ! col maximum inundated fraction for a gridcell (for methane code) + !real(r8), pointer, private :: p3_gdc (:) ! col coefficient for determining finundated (m) + !real(r8), pointer, private :: fws_slope_gdc (:) ! col slope in fws = slope * tws + intercept (A coefficient) + !real(r8), pointer, private :: fws_intercept_gdc (:) ! col slope in fws = slope * tws + intercept (B coefficient) + contains + + ! !PUBLIC MEMBER FUNCTIONS: + procedure, public :: Init ! Initialize and read data in + !procedure, public :: CalcFinundated ! Calculate finundated based on input streams + procedure, public :: CalcDragPartition ! Calculate drag partitioning based on input streams + procedure, public :: UseStreams ! If streams will be used -- dmleung set default as yes + + ! !PRIVATE MEMBER FUNCTIONS: + procedure, private :: InitAllocate ! Allocate data + + end type prigentroughnessstream_type + + ! ! PRIVATE DATA: + type, private :: streamcontrol_type + character(len=CL) :: stream_fldFileName_prigentroughness ! data Filename + character(len=CL) :: stream_meshfile_prigentroughness ! mesh Filename + character(len=CL) :: prigentroughnessmapalgo ! map algo + contains + procedure, private :: ReadNML ! Read in namelist + end type streamcontrol_type + + type(streamcontrol_type), private :: control ! Stream control data + + character(len=*), parameter, private :: sourcefile = & + __FILE__ + +!============================================================================== +contains +!============================================================================== + + subroutine Init(this, bounds, NLFilename) + ! + ! Initialize the prigent roughness stream object + ! + ! Uses: + use spmdMod , only : iam !what is this and do I need this + !use ch4varcon , only : finundation_mtd_h2osfc + !use ch4varcon , only : finundation_mtd_ZWT_inversion, finundation_mtd_TWS_inversion + use lnd_comp_shr , only : mesh, model_clock + use dshr_strdata_mod , only : shr_strdata_init_from_inline, shr_strdata_print + use dshr_strdata_mod , only : shr_strdata_advance + use dshr_methods_mod , only : dshr_fldbun_getfldptr !what is this and do I need this (read ncdata?) + ! + ! arguments + implicit none + class(prigentroughnessstream_type) :: this + type(bounds_type), intent(in) :: bounds + character(len=*), intent(in) :: NLFilename ! Namelist filename + ! + ! local variables + integer :: ig, g, n ! Indices + integer :: year ! year (0, ...) for nstep+1 + integer :: mon ! month (1, ..., 12) for nstep+1 + integer :: day ! day of month (1, ..., 31) for nstep+1 + integer :: sec ! seconds into current date for nstep+1 + integer :: mcdate ! Current model date (yyyymmdd) + type(shr_strdata_type) :: sdat_rghn ! input data stream + character(len=16), allocatable :: stream_varnames(:) ! array of stream field names + integer :: rc ! error code + real(r8), pointer :: dataptr1d(:) ! temporary pointer + !character(len=*), parameter :: stream_name = 'prigentroughness' + !----------------------------------------------------------------------- + + !if ( finundation_mtd /= finundation_mtd_h2osfc )then ! how should I change this? comment out for now + call this%InitAllocate( bounds ) + call control%ReadNML( bounds, NLFileName ) + + if ( this%useStreams() )then ! is this a namelist input and is it set in namelist default + + !if (finundation_mtd == finundation_mtd_ZWT_inversion )then + ! allocate(stream_varnames(3)) + ! stream_varnames = (/"ZWT0","F0 ","P3 "/) + !else if ( finundation_mtd == finundation_mtd_TWS_inversion )then + allocate(stream_varnames(1)) + stream_varnames = (/"Z0a"/) ! varname in cdf5_Z0a_Prigent-Globe-025x025-09262022.nc, in centimeter + !else + ! call endrun(msg=' ERROR do NOT know what list of variables to read for this finundation_mtd type'// & + ! errMsg(sourcefile, __LINE__)) + !end if + + if (masterproc) then + write(iulog,*) ' stream_varnames = ',stream_varnames + end if + + ! Initialize the cdeps data type sdat_rghn + call shr_strdata_init_from_inline(sdat_rghn, & ! what is this function and where does it come from? + my_task = iam, & + logunit = iulog, & + compname = 'LND', & + model_clock = model_clock, & + model_mesh = mesh, & + stream_meshfile = control%stream_meshfile_prigentroughness, & + stream_lev_dimname = 'null', & + stream_mapalgo = control%prigentroughnessmapalgo, & + stream_filenames = (/trim(control%stream_fldFileName_prigentroughness)/), & + stream_fldlistFile = stream_varnames, & + stream_fldListModel = stream_varnames, & + stream_yearFirst = 1997, & + stream_yearLast = 1997, & + stream_yearAlign = 1, & + stream_offset = 0, & + stream_taxmode = 'extend', & + stream_dtlimit = 1.0e30_r8, & + stream_tintalgo = 'linear', & + stream_name = 'Prigent roughness', & + rc = rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + ! Explicitly set current date to a hardcoded constant value. Otherwise + ! using the real date can cause roundoff differences that are + ! detrected as issues with exact restart. EBK M05/20/2017 + ! call get_curr_date(year, mon, day, sec) + year = 1997 + mon = 12 + day = 31 + sec = 0 + mcdate = year*10000 + mon*100 + day + + call shr_strdata_advance(sdat_rghn, ymd=mcdate, tod=sec, logunit=iulog, istr='prigentrghn', rc=rc) ! what is istr and do I need to change elsewhere because the change of istr here + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + ! Get pointer for stream data that is time and spatially interpolate to model time and grid + do n = 1,size(stream_varnames) + call dshr_fldbun_getFldPtr(sdat_rghn%pstrm(1)%fldbun_model, stream_varnames(n), fldptr1=dataptr1d, rc=rc) ! is the ncdf datasets read here, looping across all available variables in the files? Also, not sure about pstrm and fldbun_model + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + if (trim(stream_varnames(n)) == 'Z0a') then + ig = 0 + do g = bounds%begg,bounds%endg + ig = ig+1 ! not sure why +1 is needed but it's okay + this%prigent_rghn(g) = dataptr1d(ig) + end do + + end if + + end do + end if + !end if !comment out for now + + end subroutine Init + + !============================================================================== + logical function UseStreams(this) + ! + ! !DESCRIPTION: + ! Return true if + ! + ! !USES: + ! + ! !ARGUMENTS: + implicit none + class(prigentroughnessstream_type) :: this + ! + ! !LOCAL VARIABLES: + if ( trim(control%stream_fldFileName_prigentroughness) == '' )then + UseStreams = .false. ! dmleung: this won't happen and UseStreams will always be true + else + UseStreams = .true. + end if + end function UseStreams + + !============================================================================== + subroutine InitAllocate(this, bounds) + ! + ! !DESCRIPTION: + ! Allocate module variables and data structures + ! + ! !USES: + use shr_infnan_mod, only: nan => shr_infnan_nan, assignment(=) + !use ch4varcon , only: finundation_mtd_ZWT_inversion, finundation_mtd_TWS_inversion + ! + ! !ARGUMENTS: + implicit none + class(prigentroughnessstream_type) :: this + type(bounds_type), intent(in) :: bounds + ! + ! !LOCAL VARIABLES: + !integer :: begc, endc + integer :: begg, endg + !--------------------------------------------------------------------- + + !begc = bounds%begc; endc = bounds%endc + begg = bounds%begg; endg = bounds%endg + + !if( finundation_mtd == finundation_mtd_ZWT_inversion )then + allocate(this%prigent_rghn (begg:endg)) ; this%prigent_rghn (:) = nan + ! allocate(this%zwt0_gdc (begg:endg)) ; this%zwt0_gdc (:) = nan + ! allocate(this%f0_gdc (begg:endg)) ; this%f0_gdc (:) = nan + ! allocate(this%p3_gdc (begg:endg)) ; this%p3_gdc (:) = nan + !else if( finundation_mtd == finundation_mtd_TWS_inversion )then + ! allocate(this%fws_slope_gdc (begg:endg)) ; this%fws_slope_gdc (:) = nan + ! allocate(this%fws_intercept_gdc(begg:endg)) ; this%fws_intercept_gdc(:) = nan + !end if + + end subroutine InitAllocate + + !============================================================================== + !subroutine CalcFinundated(this, bounds, num_soilc, filter_soilc, soilhydrology_inst, & + ! waterdiagnosticbulk_inst, qflx_surf_lag_col, finundated ) + subroutine CalcDragPartition(this, bounds, num_nolakep, filter_nolakep, dpfct_rock) + ! + ! !DESCRIPTION: + ! Commented below by dmleung 31 Dec 2022 + ! Calculate the drag partition effect of friction velocity due to surface roughness following + ! Leung et al. (2022). This module is used in the dust emission module DUSTMod.F90 for + ! calculating drag partitioning. The drag partition equation comes from Marticorena and + ! Bergametti (1995) with constants modified by Darmenova et al. (2009). Here it is assumed + ! that this equation is used only over arid/desertic regions, such that Catherine Prigent's + ! roughness measurements represents mostly rocks. For more vegetated areas, the vegetation + ! roughness and drag partitioning are calculated in the DustEmission subroutine. This + ! subroutine is used in the InitCold subroutine of DUSTMod.F90. + ! + ! !USES: + !use ColumnType , only : col + use PatchType , only : patch + !use ch4varcon , only : finundation_mtd_h2osfc, finundation_mtd_ZWT_inversion + !use ch4varcon , only : finundation_mtd_TWS_inversion + !use clm_varpar , only : nlevsoi + !use SoilHydrologyType , only : soilhydrology_type + !use WaterDiagnosticBulkType , only : waterdiagnosticbulk_type + ! + ! !ARGUMENTS: + implicit none + class(prigentroughnessstream_type) :: this + type(bounds_type) , intent(in) :: bounds + !integer , intent(in) :: num_soilc ! number of column soil points in column filter + !integer , intent(in) :: filter_soilc(:) ! column filter for soil points + integer , intent(in) :: num_nolakep ! + integer , intent(in) :: filter_nolakep(num_nolakep) ! + !type(soilhydrology_type) , intent(in) :: soilhydrology_inst + !type(waterdiagnosticbulk_type) , intent(in) :: waterdiagnosticbulk_inst + !real(r8) , intent(in) :: qflx_surf_lag_col(bounds%begc:) !time-lagged surface runoff (mm H2O /s) + !real(r8) , intent(inout) :: finundated(bounds%begc:) ! fractional inundated area in soil column (excluding dedicated wetland columns) + real(r8) , intent(inout) :: dpfct_rock(bounds%begp:) ! [fraction] rock drag partition factor (roughness effect) + ! + ! !LOCAL VARIABLES: + !integer :: g, c, fc ! Indices + integer :: g, p, fp ! Indices + real(r8) :: z0s ! smooth roughness length (m) + !real(r8) :: zwt_actual ! Total water storage (ZWT) to use either perched or total depending on conditions + + ! constants + real(r8), parameter :: D_p = 130e-6_r8 ! [m] Medium soil particle diameter, assuming a global constant of ~130 um following Leung et al. (2022) + real(r8), parameter :: X = 10_r8 ! [m] distance downwind of the roughness element (rock). Assume estimating roughness effect at a distance of 10 m following Leung et al. (2022) + !--------------------------------------------------------------------- + + !SHR_ASSERT_ALL_FL((ubound(qflx_surf_lag_col) == (/bounds%endc/)), sourcefile, __LINE__) ! not sure what this is + !SHR_ASSERT_ALL_FL((ubound(finundated) == (/bounds%endc/)), sourcefile, __LINE__) + SHR_ASSERT_ALL_FL((ubound(dpfct_rock) == (/bounds%endp/)), sourcefile, __LINE__) !what's the use of this + + !associate( & + !z => col%z & ! Input: [real(r8) (:,:) ] layer depth (m) (-nlevsno+1:nlevsoi) + !zwt => soilhydrology_inst%zwt_col , & ! Input: [real(r8) (:) ] water table depth (m) + !zwt_perched => soilhydrology_inst%zwt_perched_col , & ! Input: [real(r8) (:) ] perched water table depth (m) + !tws => waterdiagnosticbulk_inst%tws_grc , & ! Input: [real(r8) (:) ] total water storage (kg m-2) + !frac_h2osfc => waterdiagnosticbulk_inst%frac_h2osfc_col & ! Input: [real(r8) (:) ] fraction of ground covered by surface water (0 to 1) + !) + + ! Calculate finundated + !do fc = 1, num_soilc + ! c = filter_soilc(fc) + ! g = col%gridcell(c) + ! select case( finundation_mtd ) + ! case ( finundation_mtd_h2osfc ) + ! finundated(c) = frac_h2osfc(c) + ! case ( finundation_mtd_ZWT_inversion ) + ! if (this%zwt0_gdc(g) > 0._r8) then + ! if (zwt_perched(c) < z(c,nlevsoi)-1.e-5_r8 .and. zwt_perched(c) < zwt(c)) then + ! zwt_actual = zwt_perched(c) + ! else + ! zwt_actual = zwt(c) + ! end if + ! finundated(c) = this%f0_gdc(g) * exp(-zwt_actual/this%zwt0_gdc(g)) + this%p3_gdc(g)*qflx_surf_lag_col(c) + ! else + ! finundated(c) = this%p3_gdc(g)*qflx_surf_lag_col(c) + ! end if + ! case ( finundation_mtd_TWS_inversion ) + ! finundated(c) = this%fws_slope_gdc(g) * tws(g) + this%fws_intercept_gdc(g) + ! end select + ! finundated(c) = min( 1.0_r8, max( 0.0_r8, finundated(c) ) ) + !end do + + ! dmleung: this loop calculates the drag partition effect (or roughness effect) of rocks. We save the drag partition factor as a patch level quantity. + z0s = 2_r8 * D_p / 30_r8 ! equation from Frank M. White (2006). Here we assume soil medium size is a global constant, and so is smooth roughness length. + do fp = 1,num_nolakep + p = filter_nolakep(fp) + g = patch%gridcell(p) + + dpfct_rock(p) = 1._r8 - ( log(this%prigent_rghn(g)*0.01_r8/z0s) / log(0.7_r8*(X/z0s)^0.8_r8) ) ! Calculating rock drag partition factor using Marticorena and Bergametti (1995). 0.01 is used to convert Z0a from centimeter to meter. + end do + + !end associate + + end subroutine CalcDragPartition + + !============================================================================== + subroutine ReadNML(this, bounds, NLFilename) + ! + ! Read the namelist data stream information. + ! + ! Uses: + use shr_nl_mod , only : shr_nl_find_group_name + use shr_log_mod , only : errMsg => shr_log_errMsg + use shr_mpi_mod , only : shr_mpi_bcast + !use ch4varcon , only : finundation_mtd_ZWT_inversion, finundation_mtd_TWS_inversion + ! + ! arguments + implicit none + class(streamcontrol_type) :: this + type(bounds_type), intent(in) :: bounds + character(len=*), intent(in) :: NLFilename ! Namelist filename + ! + ! local variables + integer :: nu_nml ! unit for namelist file + integer :: nml_error ! namelist i/o error flag + character(len=CL) :: stream_fldFileName_prigentroughness = ' ' + character(len=CL) :: stream_meshfile_prigentroughness = ' ' + character(len=CL) :: prigentroughnessmapalgo = 'bilinear' + character(len=*), parameter :: namelist_name = 'prigentroughness' ! MUST agree with group name in namelist definition to read. dmleung commented + !character(len=*), parameter :: subName = "('prigentroughness::ReadNML')" + !----------------------------------------------------------------------- + + namelist /prigentroughness/ & ! MUST agree with namelist_name above + prigentroughnessmapalgo, stream_fldFileName_prigentroughness, stream_meshfile_prigentroughness + + ! Default values for namelist + + ! Read prigentroughness namelist + if (masterproc) then + open( newunit=nu_nml, file=trim(NLFilename), status='old', iostat=nml_error ) + call shr_nl_find_group_name(nu_nml, namelist_name, status=nml_error) + if (nml_error == 0) then + read(nu_nml, nml=prigentroughness,iostat=nml_error) ! MUST agree with namelist_name above + if (nml_error /= 0) then + call endrun(msg=' ERROR reading '//namelist_name//' namelist'//errMsg(sourcefile, __LINE__)) + end if + else + call endrun(msg=' ERROR finding '//namelist_name//' namelist'//errMsg(sourcefile, __LINE__)) + end if + close(nu_nml) + endif + + call shr_mpi_bcast(prigentroughnessmapalgo , mpicom) + call shr_mpi_bcast(stream_fldFileName_prigentroughness , mpicom) + call shr_mpi_bcast(stream_meshfile_prigentroughness , mpicom) + + if (masterproc) then + write(iulog,*) ' ' + write(iulog,*) namelist_name, ' stream settings:' + write(iulog,*) ' stream_fldFileName_prigentroughness = ',stream_fldFileName_prigentroughness + write(iulog,*) ' stream_meshfile_prigentroughness = ',stream_meshfile_prigentroughness + write(iulog,*) ' prigentroughnessmapalgo = ',prigentroughnessmapalgo + endif + this%stream_fldFileName_prigentroughness = stream_fldFileName_prigentroughness + this%stream_meshfile_prigentroughness = stream_meshfile_prigentroughness + this%prigentroughnessmapalgo = prigentroughnessmapalgo + + end subroutine ReadNML + +end module PrigentRoughnessStreamType From 512c7027e86e18ee7826f0eaa9903c817d2e22ff Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Tue, 31 Jan 2023 16:36:45 -0700 Subject: [PATCH 014/406] dmleung fixed some bugs for the streams capability and further modified clm_instMod.F90. Now CTSM can compile successfully. 31 Jan 2023 --- src/biogeochem/DUSTMod.F90 | 20 ++++++++++++++----- .../share_esmf/PrigentRoughnessStreamType.F90 | 14 +++++++------ src/main/clm_instMod.F90 | 2 +- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 381ecfe41d..8425a413c5 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -34,6 +34,7 @@ module DUSTMod !use clm_instur , only : wt_lunit, wt_nat_patch ! dmleung added 24 Jul 2022 !use landunit_varcon , only : istsoil, istcrop ! dmleung added 24 Jul 2022 (refering to main/landunit_varcon.F90, for wt_lunit, istsoil=1 is nat veg, istcrop=2 is crop) use pftconMod , only : noveg ! dmleung added 24 Jul 2022 + use PrigentRoughnessStreamType , only : prigentroughnessstream_type ! ! !PUBLIC TYPES implicit none @@ -109,14 +110,19 @@ module DUSTMod !------------------------------------------------------------------------ !##### dmleung edited for initializing stream files 31 Dec 2022 ######## + !subroutine Init(this, bounds, NLFilename, num_nolakep, filter_nolakep) subroutine Init(this, bounds, NLFilename) class(dust_type) :: this - type(bounds_type), intent(in) :: bounds + type(bounds_type), intent(in) :: bounds + character(len=*), intent(in) :: NLFilename ! dmleung added 31 Dec 2022 + !integer , intent(in) :: num_nolakep ! number of column non-lake points in patch filter + !integer , intent(in) :: filter_nolakep(num_nolakep) ! patch filter for non-lake points call this%InitAllocate (bounds) call this%InitHistory (bounds) call this%prigentroughnessstream%Init( bounds, NLFilename ) ! dmleung added 31 Dec 2022 + !call this%InitCold (bounds, num_nolakep, filter_nolakep) call this%InitCold (bounds) call this%InitDustVars (bounds) @@ -309,12 +315,15 @@ subroutine InitHistory(this, bounds) end subroutine InitHistory !----------------------------------------------------------------------- - subroutine InitCold(this, bounds) !dmleung commented 31 Dec 2022 - !subroutine InitCold(this, bounds) + !subroutine InitCold(this, bounds, num_nolakep, filter_nolakep) !dmleung commented 31 Dec 2022 + subroutine InitCold(this, bounds) ! ! !ARGUMENTS: class(dust_type), intent(inout) :: this ! dmleung used class instead of type, 31 Dec 2022 - type(bounds_type), intent(in) :: bounds + type(bounds_type), intent(in) :: bounds + ! dmleung also added the below no-lake filter 31 Dec 2022 + !integer , intent(in) :: num_nolakep ! number of column non-lake points in patch filter + !integer , intent(in) :: filter_nolakep(num_nolakep) ! patch filter for non-lake points !type(dust_type), intent(inout) :: dust_inst ! ! !LOCAL VARIABLES: @@ -337,7 +346,8 @@ subroutine InitCold(this, bounds) !dmleung commented 31 Dec 2022 ! Caulculate Drag Partition factor, dmleung added 31 Dec 2022 if ( this%prigentroughnessstream%useStreams() )then !if usestreams == true, and it should be always true call this%prigentroughnessstream%CalcDragPartition( bounds, & - num_nolakep, filter_nolakep, this%dpfct_rock_patch(bounds%begp:bounds%endp) ) + ! num_nolakep, filter_nolakep, this%dpfct_rock_patch(bounds%begp:bounds%endp) ) + this%dpfct_rock_patch(bounds%begp:bounds%endp) ) else call endrun( "ERROR:: Drag partitioning MUST now use a streams file of aeolian roughness length to calculate, it can no longer read from the fsurdat file" ) diff --git a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 index 7da0e7bf50..246d25aee0 100644 --- a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 +++ b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 @@ -234,7 +234,8 @@ end subroutine InitAllocate !============================================================================== !subroutine CalcFinundated(this, bounds, num_soilc, filter_soilc, soilhydrology_inst, & ! waterdiagnosticbulk_inst, qflx_surf_lag_col, finundated ) - subroutine CalcDragPartition(this, bounds, num_nolakep, filter_nolakep, dpfct_rock) + !subroutine CalcDragPartition(this, bounds, num_nolakep, filter_nolakep, dpfct_rock) + subroutine CalcDragPartition(this, bounds, dpfct_rock) ! ! !DESCRIPTION: ! Commented below by dmleung 31 Dec 2022 @@ -262,8 +263,8 @@ subroutine CalcDragPartition(this, bounds, num_nolakep, filter_nolakep, dpfct_ro type(bounds_type) , intent(in) :: bounds !integer , intent(in) :: num_soilc ! number of column soil points in column filter !integer , intent(in) :: filter_soilc(:) ! column filter for soil points - integer , intent(in) :: num_nolakep ! - integer , intent(in) :: filter_nolakep(num_nolakep) ! + !integer , intent(in) :: num_nolakep ! + !integer , intent(in) :: filter_nolakep(num_nolakep) ! !type(soilhydrology_type) , intent(in) :: soilhydrology_inst !type(waterdiagnosticbulk_type) , intent(in) :: waterdiagnosticbulk_inst !real(r8) , intent(in) :: qflx_surf_lag_col(bounds%begc:) !time-lagged surface runoff (mm H2O /s) @@ -319,11 +320,12 @@ subroutine CalcDragPartition(this, bounds, num_nolakep, filter_nolakep, dpfct_ro ! dmleung: this loop calculates the drag partition effect (or roughness effect) of rocks. We save the drag partition factor as a patch level quantity. z0s = 2_r8 * D_p / 30_r8 ! equation from Frank M. White (2006). Here we assume soil medium size is a global constant, and so is smooth roughness length. - do fp = 1,num_nolakep - p = filter_nolakep(fp) + !do fp = 1,num_nolakep + ! p = filter_nolakep(fp) + do p = bounds%begp,bounds%endp g = patch%gridcell(p) - dpfct_rock(p) = 1._r8 - ( log(this%prigent_rghn(g)*0.01_r8/z0s) / log(0.7_r8*(X/z0s)^0.8_r8) ) ! Calculating rock drag partition factor using Marticorena and Bergametti (1995). 0.01 is used to convert Z0a from centimeter to meter. + dpfct_rock(p) = 1._r8 - ( log(this%prigent_rghn(g)*0.01_r8/z0s) / log(0.7_r8*(X/z0s)**0.8_r8) ) ! Calculating rock drag partition factor using Marticorena and Bergametti (1995). 0.01 is used to convert Z0a from centimeter to meter. end do !end associate diff --git a/src/main/clm_instMod.F90 b/src/main/clm_instMod.F90 index 7924c2111e..13390d3417 100644 --- a/src/main/clm_instMod.F90 +++ b/src/main/clm_instMod.F90 @@ -340,7 +340,7 @@ subroutine clm_instInit(bounds) call surfrad_inst%Init(bounds) - call dust_inst%Init(bounds) + call dust_inst%Init(bounds, NLFilename) ! dmleung added NLFilename 31 Dec 2022 allocate(scf_method, source = CreateAndInitSnowCoverFraction( & snow_cover_fraction_method = snow_cover_fraction_method, & From 059597c896e8c2593ff1b740f32df163208bc159 Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Mon, 6 Feb 2023 13:05:05 -0700 Subject: [PATCH 015/406] code cleanup by dmleung; 6 Feb 2023 --- bld/CLMBuildNamelist.pm | 1 + .../share_esmf/PrigentRoughnessStreamType.F90 | 88 ++----------------- 2 files changed, 10 insertions(+), 79 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index d078b3869c..0a548a857f 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4236,6 +4236,7 @@ sub write_output_files { push @groups, "ch4finundated"; push @groups, "soilbgc_decomp"; push @groups, "clm_canopy_inparm"; + push @groups, "prigentroughness"; if (remove_leading_and_trailing_quotes($nl->get_value('snow_cover_fraction_method')) eq 'SwensonLawrence2012') { push @groups, "scf_swenson_lawrence_2012_inparm"; } diff --git a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 index 246d25aee0..e3f8b68014 100644 --- a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 +++ b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 @@ -4,7 +4,7 @@ module PrigentRoughnessStreamType !----------------------------------------------------------------------- ! !DESCRIPTION: - ! Contains methods for reading in the Prigent et al. (1997) roughness length streams file. dmleung 22 Nov 2022 + ! Contains methods for reading in the Prigent et al. (2005) roughness length streams file. dmleung 22 Nov 2022 ! ! !USES use ESMF @@ -15,25 +15,17 @@ module PrigentRoughnessStreamType use clm_varctl , only : iulog use abortutils , only : endrun use decompMod , only : bounds_type - !use ch4varcon , only : finundation_mtd ! dmleung commented out. need to replace anything here? ! !PUBLIC TYPES: implicit none private type, public :: prigentroughnessstream_type - ! dmleung commented the next five lines. Does it need any replacement here? real(r8), pointer, private :: prigent_rghn (:) ! Prigent et al. (1997) roughness length (m) - !real(r8), pointer, private :: zwt0_gdc (:) ! col coefficient for determining finundated (m) - !real(r8), pointer, private :: f0_gdc (:) ! col maximum inundated fraction for a gridcell (for methane code) - !real(r8), pointer, private :: p3_gdc (:) ! col coefficient for determining finundated (m) - !real(r8), pointer, private :: fws_slope_gdc (:) ! col slope in fws = slope * tws + intercept (A coefficient) - !real(r8), pointer, private :: fws_intercept_gdc (:) ! col slope in fws = slope * tws + intercept (B coefficient) contains ! !PUBLIC MEMBER FUNCTIONS: procedure, public :: Init ! Initialize and read data in - !procedure, public :: CalcFinundated ! Calculate finundated based on input streams procedure, public :: CalcDragPartition ! Calculate drag partitioning based on input streams procedure, public :: UseStreams ! If streams will be used -- dmleung set default as yes @@ -65,13 +57,11 @@ subroutine Init(this, bounds, NLFilename) ! Initialize the prigent roughness stream object ! ! Uses: - use spmdMod , only : iam !what is this and do I need this - !use ch4varcon , only : finundation_mtd_h2osfc - !use ch4varcon , only : finundation_mtd_ZWT_inversion, finundation_mtd_TWS_inversion + use spmdMod , only : iam use lnd_comp_shr , only : mesh, model_clock use dshr_strdata_mod , only : shr_strdata_init_from_inline, shr_strdata_print use dshr_strdata_mod , only : shr_strdata_advance - use dshr_methods_mod , only : dshr_fldbun_getfldptr !what is this and do I need this (read ncdata?) + use dshr_methods_mod , only : dshr_fldbun_getfldptr ! ! arguments implicit none @@ -90,7 +80,7 @@ subroutine Init(this, bounds, NLFilename) character(len=16), allocatable :: stream_varnames(:) ! array of stream field names integer :: rc ! error code real(r8), pointer :: dataptr1d(:) ! temporary pointer - !character(len=*), parameter :: stream_name = 'prigentroughness' + character(len=*), parameter :: stream_name = 'prigentroughness' !----------------------------------------------------------------------- !if ( finundation_mtd /= finundation_mtd_h2osfc )then ! how should I change this? comment out for now @@ -99,16 +89,8 @@ subroutine Init(this, bounds, NLFilename) if ( this%useStreams() )then ! is this a namelist input and is it set in namelist default - !if (finundation_mtd == finundation_mtd_ZWT_inversion )then - ! allocate(stream_varnames(3)) - ! stream_varnames = (/"ZWT0","F0 ","P3 "/) - !else if ( finundation_mtd == finundation_mtd_TWS_inversion )then - allocate(stream_varnames(1)) - stream_varnames = (/"Z0a"/) ! varname in cdf5_Z0a_Prigent-Globe-025x025-09262022.nc, in centimeter - !else - ! call endrun(msg=' ERROR do NOT know what list of variables to read for this finundation_mtd type'// & - ! errMsg(sourcefile, __LINE__)) - !end if + allocate(stream_varnames(1)) + stream_varnames = (/"Z0a"/) ! varname in cdf5_Z0a_Prigent-Globe-025x025-09262022.nc, in centimeter if (masterproc) then write(iulog,*) ' stream_varnames = ',stream_varnames @@ -157,7 +139,7 @@ subroutine Init(this, bounds, NLFilename) ! Get pointer for stream data that is time and spatially interpolate to model time and grid do n = 1,size(stream_varnames) - call dshr_fldbun_getFldPtr(sdat_rghn%pstrm(1)%fldbun_model, stream_varnames(n), fldptr1=dataptr1d, rc=rc) ! is the ncdf datasets read here, looping across all available variables in the files? Also, not sure about pstrm and fldbun_model + call dshr_fldbun_getFldPtr(sdat_rghn%pstrm(1)%fldbun_model, stream_varnames(n), fldptr1=dataptr1d, rc=rc) if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then call ESMF_Finalize(endflag=ESMF_END_ABORT) end if @@ -204,7 +186,6 @@ subroutine InitAllocate(this, bounds) ! ! !USES: use shr_infnan_mod, only: nan => shr_infnan_nan, assignment(=) - !use ch4varcon , only: finundation_mtd_ZWT_inversion, finundation_mtd_TWS_inversion ! ! !ARGUMENTS: implicit none @@ -219,15 +200,7 @@ subroutine InitAllocate(this, bounds) !begc = bounds%begc; endc = bounds%endc begg = bounds%begg; endg = bounds%endg - !if( finundation_mtd == finundation_mtd_ZWT_inversion )then - allocate(this%prigent_rghn (begg:endg)) ; this%prigent_rghn (:) = nan - ! allocate(this%zwt0_gdc (begg:endg)) ; this%zwt0_gdc (:) = nan - ! allocate(this%f0_gdc (begg:endg)) ; this%f0_gdc (:) = nan - ! allocate(this%p3_gdc (begg:endg)) ; this%p3_gdc (:) = nan - !else if( finundation_mtd == finundation_mtd_TWS_inversion )then - ! allocate(this%fws_slope_gdc (begg:endg)) ; this%fws_slope_gdc (:) = nan - ! allocate(this%fws_intercept_gdc(begg:endg)) ; this%fws_intercept_gdc(:) = nan - !end if + allocate(this%prigent_rghn (begg:endg)) ; this%prigent_rghn (:) = nan end subroutine InitAllocate @@ -251,73 +224,31 @@ subroutine CalcDragPartition(this, bounds, dpfct_rock) ! !USES: !use ColumnType , only : col use PatchType , only : patch - !use ch4varcon , only : finundation_mtd_h2osfc, finundation_mtd_ZWT_inversion - !use ch4varcon , only : finundation_mtd_TWS_inversion - !use clm_varpar , only : nlevsoi - !use SoilHydrologyType , only : soilhydrology_type - !use WaterDiagnosticBulkType , only : waterdiagnosticbulk_type ! ! !ARGUMENTS: implicit none class(prigentroughnessstream_type) :: this type(bounds_type) , intent(in) :: bounds - !integer , intent(in) :: num_soilc ! number of column soil points in column filter - !integer , intent(in) :: filter_soilc(:) ! column filter for soil points !integer , intent(in) :: num_nolakep ! !integer , intent(in) :: filter_nolakep(num_nolakep) ! - !type(soilhydrology_type) , intent(in) :: soilhydrology_inst - !type(waterdiagnosticbulk_type) , intent(in) :: waterdiagnosticbulk_inst - !real(r8) , intent(in) :: qflx_surf_lag_col(bounds%begc:) !time-lagged surface runoff (mm H2O /s) - !real(r8) , intent(inout) :: finundated(bounds%begc:) ! fractional inundated area in soil column (excluding dedicated wetland columns) real(r8) , intent(inout) :: dpfct_rock(bounds%begp:) ! [fraction] rock drag partition factor (roughness effect) ! ! !LOCAL VARIABLES: !integer :: g, c, fc ! Indices integer :: g, p, fp ! Indices real(r8) :: z0s ! smooth roughness length (m) - !real(r8) :: zwt_actual ! Total water storage (ZWT) to use either perched or total depending on conditions ! constants real(r8), parameter :: D_p = 130e-6_r8 ! [m] Medium soil particle diameter, assuming a global constant of ~130 um following Leung et al. (2022) real(r8), parameter :: X = 10_r8 ! [m] distance downwind of the roughness element (rock). Assume estimating roughness effect at a distance of 10 m following Leung et al. (2022) !--------------------------------------------------------------------- - !SHR_ASSERT_ALL_FL((ubound(qflx_surf_lag_col) == (/bounds%endc/)), sourcefile, __LINE__) ! not sure what this is - !SHR_ASSERT_ALL_FL((ubound(finundated) == (/bounds%endc/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(dpfct_rock) == (/bounds%endp/)), sourcefile, __LINE__) !what's the use of this !associate( & !z => col%z & ! Input: [real(r8) (:,:) ] layer depth (m) (-nlevsno+1:nlevsoi) - !zwt => soilhydrology_inst%zwt_col , & ! Input: [real(r8) (:) ] water table depth (m) - !zwt_perched => soilhydrology_inst%zwt_perched_col , & ! Input: [real(r8) (:) ] perched water table depth (m) - !tws => waterdiagnosticbulk_inst%tws_grc , & ! Input: [real(r8) (:) ] total water storage (kg m-2) - !frac_h2osfc => waterdiagnosticbulk_inst%frac_h2osfc_col & ! Input: [real(r8) (:) ] fraction of ground covered by surface water (0 to 1) !) - ! Calculate finundated - !do fc = 1, num_soilc - ! c = filter_soilc(fc) - ! g = col%gridcell(c) - ! select case( finundation_mtd ) - ! case ( finundation_mtd_h2osfc ) - ! finundated(c) = frac_h2osfc(c) - ! case ( finundation_mtd_ZWT_inversion ) - ! if (this%zwt0_gdc(g) > 0._r8) then - ! if (zwt_perched(c) < z(c,nlevsoi)-1.e-5_r8 .and. zwt_perched(c) < zwt(c)) then - ! zwt_actual = zwt_perched(c) - ! else - ! zwt_actual = zwt(c) - ! end if - ! finundated(c) = this%f0_gdc(g) * exp(-zwt_actual/this%zwt0_gdc(g)) + this%p3_gdc(g)*qflx_surf_lag_col(c) - ! else - ! finundated(c) = this%p3_gdc(g)*qflx_surf_lag_col(c) - ! end if - ! case ( finundation_mtd_TWS_inversion ) - ! finundated(c) = this%fws_slope_gdc(g) * tws(g) + this%fws_intercept_gdc(g) - ! end select - ! finundated(c) = min( 1.0_r8, max( 0.0_r8, finundated(c) ) ) - !end do - ! dmleung: this loop calculates the drag partition effect (or roughness effect) of rocks. We save the drag partition factor as a patch level quantity. z0s = 2_r8 * D_p / 30_r8 ! equation from Frank M. White (2006). Here we assume soil medium size is a global constant, and so is smooth roughness length. !do fp = 1,num_nolakep @@ -341,7 +272,6 @@ subroutine ReadNML(this, bounds, NLFilename) use shr_nl_mod , only : shr_nl_find_group_name use shr_log_mod , only : errMsg => shr_log_errMsg use shr_mpi_mod , only : shr_mpi_bcast - !use ch4varcon , only : finundation_mtd_ZWT_inversion, finundation_mtd_TWS_inversion ! ! arguments implicit none @@ -356,7 +286,7 @@ subroutine ReadNML(this, bounds, NLFilename) character(len=CL) :: stream_meshfile_prigentroughness = ' ' character(len=CL) :: prigentroughnessmapalgo = 'bilinear' character(len=*), parameter :: namelist_name = 'prigentroughness' ! MUST agree with group name in namelist definition to read. dmleung commented - !character(len=*), parameter :: subName = "('prigentroughness::ReadNML')" + character(len=*), parameter :: subName = "('prigentroughness::ReadNML')" !----------------------------------------------------------------------- namelist /prigentroughness/ & ! MUST agree with namelist_name above From cc7696eb8c6f4fca96020e0679df0fc9c486d5eb Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Sat, 25 Feb 2023 21:47:12 -0700 Subject: [PATCH 016/406] dmleung added a nolake filter in calculating the drag partition effect (25 Feb 2023). --- src/biogeochem/DUSTMod.F90 | 10 +- .../share_esmf/PrigentRoughnessStreamType.F90 | 97 ++++++++++++++++--- 2 files changed, 93 insertions(+), 14 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 8425a413c5..8488fdf682 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -123,7 +123,7 @@ subroutine Init(this, bounds, NLFilename) call this%InitHistory (bounds) call this%prigentroughnessstream%Init( bounds, NLFilename ) ! dmleung added 31 Dec 2022 !call this%InitCold (bounds, num_nolakep, filter_nolakep) - call this%InitCold (bounds) + call this%InitCold (bounds) ! dmleung commented 31 Dec 2022 call this%InitDustVars (bounds) end subroutine Init @@ -309,7 +309,7 @@ subroutine InitHistory(this, bounds) this%dpfct_rock_patch(begp:endp) = spval call hist_addfld1d (fname='DPFCT_ROCK', units='m/s', & avgflag='A', long_name='rock drag partition factor', & - ptr_patch=this%dpfct_rock_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%dpfct_rock_patch) !########################################################################## end subroutine InitHistory @@ -317,6 +317,10 @@ end subroutine InitHistory !----------------------------------------------------------------------- !subroutine InitCold(this, bounds, num_nolakep, filter_nolakep) !dmleung commented 31 Dec 2022 subroutine InitCold(this, bounds) + ! + !USES dmleung added 31 Dec 2022 + !use landunit_varcon , only : istdlak + !use LandunitType , only : lun ! ! !ARGUMENTS: class(dust_type), intent(inout) :: this ! dmleung used class instead of type, 31 Dec 2022 @@ -346,7 +350,7 @@ subroutine InitCold(this, bounds) ! Caulculate Drag Partition factor, dmleung added 31 Dec 2022 if ( this%prigentroughnessstream%useStreams() )then !if usestreams == true, and it should be always true call this%prigentroughnessstream%CalcDragPartition( bounds, & - ! num_nolakep, filter_nolakep, this%dpfct_rock_patch(bounds%begp:bounds%endp) ) + ! num_nolakep, filter_nolakep, this%dpfct_rock_patch(bounds%begp:bounds%endp) ) this%dpfct_rock_patch(bounds%begp:bounds%endp) ) else diff --git a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 index e3f8b68014..ad8843b706 100644 --- a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 +++ b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 @@ -4,7 +4,7 @@ module PrigentRoughnessStreamType !----------------------------------------------------------------------- ! !DESCRIPTION: - ! Contains methods for reading in the Prigent et al. (2005) roughness length streams file. dmleung 22 Nov 2022 + ! Contains methods for reading in the Prigent et al. (1997) roughness length streams file. dmleung 22 Nov 2022 ! ! !USES use ESMF @@ -15,17 +15,25 @@ module PrigentRoughnessStreamType use clm_varctl , only : iulog use abortutils , only : endrun use decompMod , only : bounds_type + !use ch4varcon , only : finundation_mtd ! dmleung commented out. need to replace anything here? ! !PUBLIC TYPES: implicit none private type, public :: prigentroughnessstream_type + ! dmleung commented the next five lines. Does it need any replacement here? real(r8), pointer, private :: prigent_rghn (:) ! Prigent et al. (1997) roughness length (m) + !real(r8), pointer, private :: zwt0_gdc (:) ! col coefficient for determining finundated (m) + !real(r8), pointer, private :: f0_gdc (:) ! col maximum inundated fraction for a gridcell (for methane code) + !real(r8), pointer, private :: p3_gdc (:) ! col coefficient for determining finundated (m) + !real(r8), pointer, private :: fws_slope_gdc (:) ! col slope in fws = slope * tws + intercept (A coefficient) + !real(r8), pointer, private :: fws_intercept_gdc (:) ! col slope in fws = slope * tws + intercept (B coefficient) contains ! !PUBLIC MEMBER FUNCTIONS: procedure, public :: Init ! Initialize and read data in + !procedure, public :: CalcFinundated ! Calculate finundated based on input streams procedure, public :: CalcDragPartition ! Calculate drag partitioning based on input streams procedure, public :: UseStreams ! If streams will be used -- dmleung set default as yes @@ -57,11 +65,13 @@ subroutine Init(this, bounds, NLFilename) ! Initialize the prigent roughness stream object ! ! Uses: - use spmdMod , only : iam + use spmdMod , only : iam !what is this and do I need this + !use ch4varcon , only : finundation_mtd_h2osfc + !use ch4varcon , only : finundation_mtd_ZWT_inversion, finundation_mtd_TWS_inversion use lnd_comp_shr , only : mesh, model_clock use dshr_strdata_mod , only : shr_strdata_init_from_inline, shr_strdata_print use dshr_strdata_mod , only : shr_strdata_advance - use dshr_methods_mod , only : dshr_fldbun_getfldptr + use dshr_methods_mod , only : dshr_fldbun_getfldptr !what is this and do I need this (read ncdata?) ! ! arguments implicit none @@ -89,8 +99,16 @@ subroutine Init(this, bounds, NLFilename) if ( this%useStreams() )then ! is this a namelist input and is it set in namelist default - allocate(stream_varnames(1)) - stream_varnames = (/"Z0a"/) ! varname in cdf5_Z0a_Prigent-Globe-025x025-09262022.nc, in centimeter + !if (finundation_mtd == finundation_mtd_ZWT_inversion )then + ! allocate(stream_varnames(3)) + ! stream_varnames = (/"ZWT0","F0 ","P3 "/) + !else if ( finundation_mtd == finundation_mtd_TWS_inversion )then + allocate(stream_varnames(1)) + stream_varnames = (/"Z0a"/) ! varname in cdf5_Z0a_Prigent-Globe-025x025-09262022.nc, in centimeter + !else + ! call endrun(msg=' ERROR do NOT know what list of variables to read for this finundation_mtd type'// & + ! errMsg(sourcefile, __LINE__)) + !end if if (masterproc) then write(iulog,*) ' stream_varnames = ',stream_varnames @@ -139,7 +157,7 @@ subroutine Init(this, bounds, NLFilename) ! Get pointer for stream data that is time and spatially interpolate to model time and grid do n = 1,size(stream_varnames) - call dshr_fldbun_getFldPtr(sdat_rghn%pstrm(1)%fldbun_model, stream_varnames(n), fldptr1=dataptr1d, rc=rc) + call dshr_fldbun_getFldPtr(sdat_rghn%pstrm(1)%fldbun_model, stream_varnames(n), fldptr1=dataptr1d, rc=rc) ! is the ncdf datasets read here, looping across all available variables in the files? Also, not sure about pstrm and fldbun_model if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then call ESMF_Finalize(endflag=ESMF_END_ABORT) end if @@ -186,6 +204,7 @@ subroutine InitAllocate(this, bounds) ! ! !USES: use shr_infnan_mod, only: nan => shr_infnan_nan, assignment(=) + !use ch4varcon , only: finundation_mtd_ZWT_inversion, finundation_mtd_TWS_inversion ! ! !ARGUMENTS: implicit none @@ -200,7 +219,15 @@ subroutine InitAllocate(this, bounds) !begc = bounds%begc; endc = bounds%endc begg = bounds%begg; endg = bounds%endg - allocate(this%prigent_rghn (begg:endg)) ; this%prigent_rghn (:) = nan + !if( finundation_mtd == finundation_mtd_ZWT_inversion )then + allocate(this%prigent_rghn (begg:endg)) ; this%prigent_rghn (:) = nan + ! allocate(this%zwt0_gdc (begg:endg)) ; this%zwt0_gdc (:) = nan + ! allocate(this%f0_gdc (begg:endg)) ; this%f0_gdc (:) = nan + ! allocate(this%p3_gdc (begg:endg)) ; this%p3_gdc (:) = nan + !else if( finundation_mtd == finundation_mtd_TWS_inversion )then + ! allocate(this%fws_slope_gdc (begg:endg)) ; this%fws_slope_gdc (:) = nan + ! allocate(this%fws_intercept_gdc(begg:endg)) ; this%fws_intercept_gdc(:) = nan + !end if end subroutine InitAllocate @@ -224,39 +251,86 @@ subroutine CalcDragPartition(this, bounds, dpfct_rock) ! !USES: !use ColumnType , only : col use PatchType , only : patch + !USES dmleung added 31 Dec 2022 + use landunit_varcon , only : istdlak + use LandunitType , only : lun + !use ch4varcon , only : finundation_mtd_h2osfc, finundation_mtd_ZWT_inversion + !use ch4varcon , only : finundation_mtd_TWS_inversion + !use clm_varpar , only : nlevsoi + !use SoilHydrologyType , only : soilhydrology_type + !use WaterDiagnosticBulkType , only : waterdiagnosticbulk_type ! ! !ARGUMENTS: implicit none class(prigentroughnessstream_type) :: this type(bounds_type) , intent(in) :: bounds + !integer , intent(in) :: num_soilc ! number of column soil points in column filter + !integer , intent(in) :: filter_soilc(:) ! column filter for soil points !integer , intent(in) :: num_nolakep ! !integer , intent(in) :: filter_nolakep(num_nolakep) ! + !type(soilhydrology_type) , intent(in) :: soilhydrology_inst + !type(waterdiagnosticbulk_type) , intent(in) :: waterdiagnosticbulk_inst + !real(r8) , intent(in) :: qflx_surf_lag_col(bounds%begc:) !time-lagged surface runoff (mm H2O /s) + !real(r8) , intent(inout) :: finundated(bounds%begc:) ! fractional inundated area in soil column (excluding dedicated wetland columns) real(r8) , intent(inout) :: dpfct_rock(bounds%begp:) ! [fraction] rock drag partition factor (roughness effect) ! ! !LOCAL VARIABLES: !integer :: g, c, fc ! Indices - integer :: g, p, fp ! Indices + integer :: g, p, fp, l ! Indices real(r8) :: z0s ! smooth roughness length (m) + !real(r8) :: zwt_actual ! Total water storage (ZWT) to use either perched or total depending on conditions ! constants real(r8), parameter :: D_p = 130e-6_r8 ! [m] Medium soil particle diameter, assuming a global constant of ~130 um following Leung et al. (2022) real(r8), parameter :: X = 10_r8 ! [m] distance downwind of the roughness element (rock). Assume estimating roughness effect at a distance of 10 m following Leung et al. (2022) !--------------------------------------------------------------------- + !SHR_ASSERT_ALL_FL((ubound(qflx_surf_lag_col) == (/bounds%endc/)), sourcefile, __LINE__) ! not sure what this is + !SHR_ASSERT_ALL_FL((ubound(finundated) == (/bounds%endc/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(dpfct_rock) == (/bounds%endp/)), sourcefile, __LINE__) !what's the use of this !associate( & !z => col%z & ! Input: [real(r8) (:,:) ] layer depth (m) (-nlevsno+1:nlevsoi) + !zwt => soilhydrology_inst%zwt_col , & ! Input: [real(r8) (:) ] water table depth (m) + !zwt_perched => soilhydrology_inst%zwt_perched_col , & ! Input: [real(r8) (:) ] perched water table depth (m) + !tws => waterdiagnosticbulk_inst%tws_grc , & ! Input: [real(r8) (:) ] total water storage (kg m-2) + !frac_h2osfc => waterdiagnosticbulk_inst%frac_h2osfc_col & ! Input: [real(r8) (:) ] fraction of ground covered by surface water (0 to 1) !) + ! Calculate finundated + !do fc = 1, num_soilc + ! c = filter_soilc(fc) + ! g = col%gridcell(c) + ! select case( finundation_mtd ) + ! case ( finundation_mtd_h2osfc ) + ! finundated(c) = frac_h2osfc(c) + ! case ( finundation_mtd_ZWT_inversion ) + ! if (this%zwt0_gdc(g) > 0._r8) then + ! if (zwt_perched(c) < z(c,nlevsoi)-1.e-5_r8 .and. zwt_perched(c) < zwt(c)) then + ! zwt_actual = zwt_perched(c) + ! else + ! zwt_actual = zwt(c) + ! end if + ! finundated(c) = this%f0_gdc(g) * exp(-zwt_actual/this%zwt0_gdc(g)) + this%p3_gdc(g)*qflx_surf_lag_col(c) + ! else + ! finundated(c) = this%p3_gdc(g)*qflx_surf_lag_col(c) + ! end if + ! case ( finundation_mtd_TWS_inversion ) + ! finundated(c) = this%fws_slope_gdc(g) * tws(g) + this%fws_intercept_gdc(g) + ! end select + ! finundated(c) = min( 1.0_r8, max( 0.0_r8, finundated(c) ) ) + !end do + ! dmleung: this loop calculates the drag partition effect (or roughness effect) of rocks. We save the drag partition factor as a patch level quantity. z0s = 2_r8 * D_p / 30_r8 ! equation from Frank M. White (2006). Here we assume soil medium size is a global constant, and so is smooth roughness length. !do fp = 1,num_nolakep - ! p = filter_nolakep(fp) + !p = filter_nolakep(fp) do p = bounds%begp,bounds%endp g = patch%gridcell(p) - - dpfct_rock(p) = 1._r8 - ( log(this%prigent_rghn(g)*0.01_r8/z0s) / log(0.7_r8*(X/z0s)**0.8_r8) ) ! Calculating rock drag partition factor using Marticorena and Bergametti (1995). 0.01 is used to convert Z0a from centimeter to meter. + l = patch%landunit(p) + if (lun%itype(l) /= istdlak) then + dpfct_rock(p) = 1._r8 - ( log(this%prigent_rghn(g)*0.01_r8/z0s) / log(0.7_r8*(X/z0s)**0.8_r8) ) ! Calculating rock drag partition factor using Marticorena and Bergametti (1995). 0.01 is used to convert Z0a from centimeter to meter. + end if end do !end associate @@ -272,6 +346,7 @@ subroutine ReadNML(this, bounds, NLFilename) use shr_nl_mod , only : shr_nl_find_group_name use shr_log_mod , only : errMsg => shr_log_errMsg use shr_mpi_mod , only : shr_mpi_bcast + !use ch4varcon , only : finundation_mtd_ZWT_inversion, finundation_mtd_TWS_inversion ! ! arguments implicit none From 53ea16556f8de0c99e2bd1e9573a104ccc3c0ce0 Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Sat, 25 Feb 2023 22:10:07 -0700 Subject: [PATCH 017/406] code cleanup by dmleung 25 Feb 2023 --- .../share_esmf/PrigentRoughnessStreamType.F90 | 85 ++----------------- 1 file changed, 7 insertions(+), 78 deletions(-) diff --git a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 index ad8843b706..19ab3cb573 100644 --- a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 +++ b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 @@ -15,25 +15,17 @@ module PrigentRoughnessStreamType use clm_varctl , only : iulog use abortutils , only : endrun use decompMod , only : bounds_type - !use ch4varcon , only : finundation_mtd ! dmleung commented out. need to replace anything here? ! !PUBLIC TYPES: implicit none private type, public :: prigentroughnessstream_type - ! dmleung commented the next five lines. Does it need any replacement here? real(r8), pointer, private :: prigent_rghn (:) ! Prigent et al. (1997) roughness length (m) - !real(r8), pointer, private :: zwt0_gdc (:) ! col coefficient for determining finundated (m) - !real(r8), pointer, private :: f0_gdc (:) ! col maximum inundated fraction for a gridcell (for methane code) - !real(r8), pointer, private :: p3_gdc (:) ! col coefficient for determining finundated (m) - !real(r8), pointer, private :: fws_slope_gdc (:) ! col slope in fws = slope * tws + intercept (A coefficient) - !real(r8), pointer, private :: fws_intercept_gdc (:) ! col slope in fws = slope * tws + intercept (B coefficient) contains ! !PUBLIC MEMBER FUNCTIONS: procedure, public :: Init ! Initialize and read data in - !procedure, public :: CalcFinundated ! Calculate finundated based on input streams procedure, public :: CalcDragPartition ! Calculate drag partitioning based on input streams procedure, public :: UseStreams ! If streams will be used -- dmleung set default as yes @@ -65,13 +57,11 @@ subroutine Init(this, bounds, NLFilename) ! Initialize the prigent roughness stream object ! ! Uses: - use spmdMod , only : iam !what is this and do I need this - !use ch4varcon , only : finundation_mtd_h2osfc - !use ch4varcon , only : finundation_mtd_ZWT_inversion, finundation_mtd_TWS_inversion + use spmdMod , only : iam use lnd_comp_shr , only : mesh, model_clock use dshr_strdata_mod , only : shr_strdata_init_from_inline, shr_strdata_print use dshr_strdata_mod , only : shr_strdata_advance - use dshr_methods_mod , only : dshr_fldbun_getfldptr !what is this and do I need this (read ncdata?) + use dshr_methods_mod , only : dshr_fldbun_getfldptr ! ! arguments implicit none @@ -99,23 +89,15 @@ subroutine Init(this, bounds, NLFilename) if ( this%useStreams() )then ! is this a namelist input and is it set in namelist default - !if (finundation_mtd == finundation_mtd_ZWT_inversion )then - ! allocate(stream_varnames(3)) - ! stream_varnames = (/"ZWT0","F0 ","P3 "/) - !else if ( finundation_mtd == finundation_mtd_TWS_inversion )then allocate(stream_varnames(1)) stream_varnames = (/"Z0a"/) ! varname in cdf5_Z0a_Prigent-Globe-025x025-09262022.nc, in centimeter - !else - ! call endrun(msg=' ERROR do NOT know what list of variables to read for this finundation_mtd type'// & - ! errMsg(sourcefile, __LINE__)) - !end if if (masterproc) then write(iulog,*) ' stream_varnames = ',stream_varnames - end if + end if - ! Initialize the cdeps data type sdat_rghn - call shr_strdata_init_from_inline(sdat_rghn, & ! what is this function and where does it come from? + ! Initialize the cdeps data type sdat_rghn + call shr_strdata_init_from_inline(sdat_rghn, & ! what is this function and where does it come from? my_task = iam, & logunit = iulog, & compname = 'LND', & @@ -157,7 +139,7 @@ subroutine Init(this, bounds, NLFilename) ! Get pointer for stream data that is time and spatially interpolate to model time and grid do n = 1,size(stream_varnames) - call dshr_fldbun_getFldPtr(sdat_rghn%pstrm(1)%fldbun_model, stream_varnames(n), fldptr1=dataptr1d, rc=rc) ! is the ncdf datasets read here, looping across all available variables in the files? Also, not sure about pstrm and fldbun_model + call dshr_fldbun_getFldPtr(sdat_rghn%pstrm(1)%fldbun_model, stream_varnames(n), fldptr1=dataptr1d, rc=rc) if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then call ESMF_Finalize(endflag=ESMF_END_ABORT) end if @@ -204,7 +186,6 @@ subroutine InitAllocate(this, bounds) ! ! !USES: use shr_infnan_mod, only: nan => shr_infnan_nan, assignment(=) - !use ch4varcon , only: finundation_mtd_ZWT_inversion, finundation_mtd_TWS_inversion ! ! !ARGUMENTS: implicit none @@ -219,15 +200,7 @@ subroutine InitAllocate(this, bounds) !begc = bounds%begc; endc = bounds%endc begg = bounds%begg; endg = bounds%endg - !if( finundation_mtd == finundation_mtd_ZWT_inversion )then - allocate(this%prigent_rghn (begg:endg)) ; this%prigent_rghn (:) = nan - ! allocate(this%zwt0_gdc (begg:endg)) ; this%zwt0_gdc (:) = nan - ! allocate(this%f0_gdc (begg:endg)) ; this%f0_gdc (:) = nan - ! allocate(this%p3_gdc (begg:endg)) ; this%p3_gdc (:) = nan - !else if( finundation_mtd == finundation_mtd_TWS_inversion )then - ! allocate(this%fws_slope_gdc (begg:endg)) ; this%fws_slope_gdc (:) = nan - ! allocate(this%fws_intercept_gdc(begg:endg)) ; this%fws_intercept_gdc(:) = nan - !end if + allocate(this%prigent_rghn (begg:endg)) ; this%prigent_rghn (:) = nan end subroutine InitAllocate @@ -254,72 +227,29 @@ subroutine CalcDragPartition(this, bounds, dpfct_rock) !USES dmleung added 31 Dec 2022 use landunit_varcon , only : istdlak use LandunitType , only : lun - !use ch4varcon , only : finundation_mtd_h2osfc, finundation_mtd_ZWT_inversion - !use ch4varcon , only : finundation_mtd_TWS_inversion - !use clm_varpar , only : nlevsoi - !use SoilHydrologyType , only : soilhydrology_type - !use WaterDiagnosticBulkType , only : waterdiagnosticbulk_type ! ! !ARGUMENTS: implicit none class(prigentroughnessstream_type) :: this type(bounds_type) , intent(in) :: bounds - !integer , intent(in) :: num_soilc ! number of column soil points in column filter - !integer , intent(in) :: filter_soilc(:) ! column filter for soil points - !integer , intent(in) :: num_nolakep ! - !integer , intent(in) :: filter_nolakep(num_nolakep) ! - !type(soilhydrology_type) , intent(in) :: soilhydrology_inst - !type(waterdiagnosticbulk_type) , intent(in) :: waterdiagnosticbulk_inst - !real(r8) , intent(in) :: qflx_surf_lag_col(bounds%begc:) !time-lagged surface runoff (mm H2O /s) - !real(r8) , intent(inout) :: finundated(bounds%begc:) ! fractional inundated area in soil column (excluding dedicated wetland columns) real(r8) , intent(inout) :: dpfct_rock(bounds%begp:) ! [fraction] rock drag partition factor (roughness effect) ! ! !LOCAL VARIABLES: !integer :: g, c, fc ! Indices integer :: g, p, fp, l ! Indices real(r8) :: z0s ! smooth roughness length (m) - !real(r8) :: zwt_actual ! Total water storage (ZWT) to use either perched or total depending on conditions ! constants real(r8), parameter :: D_p = 130e-6_r8 ! [m] Medium soil particle diameter, assuming a global constant of ~130 um following Leung et al. (2022) real(r8), parameter :: X = 10_r8 ! [m] distance downwind of the roughness element (rock). Assume estimating roughness effect at a distance of 10 m following Leung et al. (2022) !--------------------------------------------------------------------- - !SHR_ASSERT_ALL_FL((ubound(qflx_surf_lag_col) == (/bounds%endc/)), sourcefile, __LINE__) ! not sure what this is - !SHR_ASSERT_ALL_FL((ubound(finundated) == (/bounds%endc/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(dpfct_rock) == (/bounds%endp/)), sourcefile, __LINE__) !what's the use of this !associate( & !z => col%z & ! Input: [real(r8) (:,:) ] layer depth (m) (-nlevsno+1:nlevsoi) - !zwt => soilhydrology_inst%zwt_col , & ! Input: [real(r8) (:) ] water table depth (m) - !zwt_perched => soilhydrology_inst%zwt_perched_col , & ! Input: [real(r8) (:) ] perched water table depth (m) - !tws => waterdiagnosticbulk_inst%tws_grc , & ! Input: [real(r8) (:) ] total water storage (kg m-2) - !frac_h2osfc => waterdiagnosticbulk_inst%frac_h2osfc_col & ! Input: [real(r8) (:) ] fraction of ground covered by surface water (0 to 1) !) - ! Calculate finundated - !do fc = 1, num_soilc - ! c = filter_soilc(fc) - ! g = col%gridcell(c) - ! select case( finundation_mtd ) - ! case ( finundation_mtd_h2osfc ) - ! finundated(c) = frac_h2osfc(c) - ! case ( finundation_mtd_ZWT_inversion ) - ! if (this%zwt0_gdc(g) > 0._r8) then - ! if (zwt_perched(c) < z(c,nlevsoi)-1.e-5_r8 .and. zwt_perched(c) < zwt(c)) then - ! zwt_actual = zwt_perched(c) - ! else - ! zwt_actual = zwt(c) - ! end if - ! finundated(c) = this%f0_gdc(g) * exp(-zwt_actual/this%zwt0_gdc(g)) + this%p3_gdc(g)*qflx_surf_lag_col(c) - ! else - ! finundated(c) = this%p3_gdc(g)*qflx_surf_lag_col(c) - ! end if - ! case ( finundation_mtd_TWS_inversion ) - ! finundated(c) = this%fws_slope_gdc(g) * tws(g) + this%fws_intercept_gdc(g) - ! end select - ! finundated(c) = min( 1.0_r8, max( 0.0_r8, finundated(c) ) ) - !end do ! dmleung: this loop calculates the drag partition effect (or roughness effect) of rocks. We save the drag partition factor as a patch level quantity. z0s = 2_r8 * D_p / 30_r8 ! equation from Frank M. White (2006). Here we assume soil medium size is a global constant, and so is smooth roughness length. @@ -346,7 +276,6 @@ subroutine ReadNML(this, bounds, NLFilename) use shr_nl_mod , only : shr_nl_find_group_name use shr_log_mod , only : errMsg => shr_log_errMsg use shr_mpi_mod , only : shr_mpi_bcast - !use ch4varcon , only : finundation_mtd_ZWT_inversion, finundation_mtd_TWS_inversion ! ! arguments implicit none From 23f44daea7cb26b21fcf0d357389be5aecf86a28 Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Fri, 31 Mar 2023 15:00:22 -0600 Subject: [PATCH 018/406] remove the older methods of reading in prigent's roughenss data (rough_fct) since the stream methods is here (dmleung 31 Mar 2023) --- bld/CLMBuildNamelist.pm | 7 ++++- .../namelist_definition_ctsm.xml | 8 +----- src/biogeochem/DUSTMod.F90 | 2 -- src/biogeophys/SoilStateInitTimeConstMod.F90 | 27 ------------------- src/biogeophys/SoilStateType.F90 | 6 ----- src/main/clm_varctl.F90 | 1 - src/main/controlMod.F90 | 13 --------- 7 files changed, 7 insertions(+), 57 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 0a548a857f..3b0c79d000 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -1663,6 +1663,11 @@ sub process_namelist_inline_logic { ################################## setup_logic_lai_streams($opts, $nl_flags, $definition, $defaults, $nl); + ################################## + # namelist group: dust_emis_streams # + ################################## + #setup_logic_dustemis_streams($opts, $nl_flags, $definition, $defaults, $nl); + ########################################## # namelist group: soil_moisture_streams # ########################################## @@ -4236,7 +4241,7 @@ sub write_output_files { push @groups, "ch4finundated"; push @groups, "soilbgc_decomp"; push @groups, "clm_canopy_inparm"; - push @groups, "prigentroughness"; + push @groups, "prigentroughness"; # dmleung 31 Dec 2022 if (remove_leading_and_trailing_quotes($nl->get_value('snow_cover_fraction_method')) eq 'SwensonLawrence2012') { push @groups, "scf_swenson_lawrence_2012_inparm"; } diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 6cdf4e3ce5..ec29018081 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1768,6 +1768,7 @@ If true, will ignore the prescribed soilm data for that point and let the model prescribed data. + @@ -2811,13 +2812,6 @@ use case.) - - - -Full pathname of time-invariant roughness factor dataset for calculating drag partition of dust emission. - - diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 8488fdf682..f1de13902a 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -492,8 +492,6 @@ subroutine DustEmission (bounds, & thr_crs_rate => dust_inst%thr_crs_rate_patch , & prb_crs_fld_thr => dust_inst%prb_crs_fld_thr_patch , & prb_crs_impct_thr => dust_inst%prb_crs_impct_thr_patch , & - ! added by dmleung 17 Dec 2021 - roughfct => soilstate_inst%roughfct_patch , & ! dmleung replaced it by dpfct_rock below, 31 Dec 2022 ! added by dmleung 20 Dec 2021 ssr => dust_inst%ssr_patch , & lai => dust_inst%lai_patch , & diff --git a/src/biogeophys/SoilStateInitTimeConstMod.F90 b/src/biogeophys/SoilStateInitTimeConstMod.F90 index c5e2a72977..fc96a878bc 100644 --- a/src/biogeophys/SoilStateInitTimeConstMod.F90 +++ b/src/biogeophys/SoilStateInitTimeConstMod.F90 @@ -172,7 +172,6 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) use clm_varcon , only : secspday, denh2o, denice, grlnd use clm_varctl , only : use_cn, use_lch4, use_fates use clm_varctl , only : iulog, fsurdat, paramfile, soil_layerstruct_predefined - use clm_varctl , only : rough_fct ! -dmleung added to CESM2/CLM5 17 Dec 2021 use landunit_varcon , only : istdlak, istwet, istsoil, istcrop, istice use column_varcon , only : icol_roof, icol_sunwall, icol_shadewall, icol_road_perv, icol_road_imperv use fileutils , only : getfil @@ -221,7 +220,6 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) real(r8) ,pointer :: sand3d (:,:) ! read in - soil texture: percent sand (needs to be a pointer for use in ncdio) real(r8) ,pointer :: clay3d (:,:) ! read in - soil texture: percent clay (needs to be a pointer for use in ncdio) real(r8) ,pointer :: organic3d (:,:) ! read in - organic matter: kg/m3 (needs to be a pointer for use in ncdio) - real(r8) ,pointer :: roughfct2d(:) ! read in - time-invariant roughness factor, dmleung added 17 Dec 2021 character(len=256) :: locfn ! local filename integer :: ipedof integer :: begp, endp @@ -371,31 +369,6 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) call ncd_pio_closefile(ncid) - !############ subsection for input data for new dust emission scheme ############## - - ! dmleung added to CESM2/CLM5 17 Dec 2021 - if (rough_fct /= ' ') then - allocate(roughfct2d(begg:endg)) ! dmleung, 16 Jul 2020 - ! here to read roughness factor file, 16 Jul 2020 - !write(iulog,*) 'Attempting to read roughness factor data, by dmleung .....' - call getfil (rough_fct, locfn, 0) - call ncd_pio_openfile (ncid, locfn, 0) - call ncd_io(ncid=ncid, varname='F_eff', flag='read', data=roughfct2d, dim1name=grlnd, readvar=readvar) - !write(iulog,*) 'initialize pft level roughness factor from roughfct2d(g) to roughfct(p)' - - do p = begp,endp - g = patch%gridcell(p) - soilstate_inst%roughfct_patch(p) = roughfct2d(g) - end do - - call ncd_pio_closefile(ncid) - else - do p = begp,endp - soilstate_inst%roughfct_patch(p) = 1.0_r8 - end do - - end if - !################################################################################## ! -------------------------------------------------------------------- ! get original soil depths to be used in interpolation of sand and clay diff --git a/src/biogeophys/SoilStateType.F90 b/src/biogeophys/SoilStateType.F90 index fa3d5a6aaf..2ee4894ece 100644 --- a/src/biogeophys/SoilStateType.F90 +++ b/src/biogeophys/SoilStateType.F90 @@ -30,9 +30,6 @@ module SoilStateType real(r8), pointer :: cellsand_col (:,:) ! sand value for gridcell containing column (1:nlevsoi) real(r8), pointer :: cellclay_col (:,:) ! clay value for gridcell containing column (1:nlevsoi) real(r8), pointer :: bd_col (:,:) ! col bulk density of dry soil material [kg/m^3] (CN) - !####################### for new dust emission scheme -dmleung ############################ - real(r8), pointer :: roughfct_patch (:) ! roughness factor, 17 dec 2021 - !########################################################################################### ! hydraulic properties real(r8), pointer :: hksat_col (:,:) ! col hydraulic conductivity at saturation (mm H2O /s) @@ -134,9 +131,6 @@ subroutine InitAllocate(this, bounds) allocate(this%cellclay_col (begc:endc,nlevsoi)) ; this%cellclay_col (:,:) = nan allocate(this%bd_col (begc:endc,nlevgrnd)) ; this%bd_col (:,:) = nan - !################ dmleung added 14 Dec 2021 ######################## - allocate(this%roughfct_patch (begp:endp)) ; this%roughfct_patch (:) = nan - !################################################################### allocate(this%hksat_col (begc:endc,nlevgrnd)) ; this%hksat_col (:,:) = spval allocate(this%hksat_min_col (begc:endc,nlevgrnd)) ; this%hksat_min_col (:,:) = spval allocate(this%hk_l_col (begc:endc,nlevgrnd)) ; this%hk_l_col (:,:) = nan diff --git a/src/main/clm_varctl.F90 b/src/main/clm_varctl.F90 index 3b7b1321ea..9be9af2f73 100644 --- a/src/main/clm_varctl.F90 +++ b/src/main/clm_varctl.F90 @@ -113,7 +113,6 @@ module clm_varctl character(len=fname_len), public :: nrevsn = ' ' ! restart data file name for branch run character(len=fname_len), public :: fsnowoptics = ' ' ! snow optical properties file name character(len=fname_len), public :: fsnowaging = ' ' ! snow aging parameters file name - character(len=fname_len), public :: rough_fct = ' ' ! roughness factor, dmleung, added 17 Dec 2021 character(len=fname_len), public :: fatmlndfrc = ' ' ! lnd frac file on atm grid ! only needed for LILAC and MCT drivers diff --git a/src/main/controlMod.F90 b/src/main/controlMod.F90 index 49788b351c..a7bca1aa87 100644 --- a/src/main/controlMod.F90 +++ b/src/main/controlMod.F90 @@ -282,9 +282,6 @@ subroutine control_init(dtime) use_grainproduct, use_snicar_frc, use_vancouver, use_mexicocity, use_noio, & use_nguardrail - ! dust emission, dmleung 14 Dec 2021 - namelist /clm_inparm/ & - rough_fct !-dmleung 17 Dec 2021 ! ---------------------------------------------------------------------- ! Default values @@ -656,9 +653,6 @@ subroutine control_spmd() call mpi_bcast (fsnowoptics, len(fsnowoptics), MPI_CHARACTER, 0, mpicom, ier) call mpi_bcast (fsnowaging, len(fsnowaging), MPI_CHARACTER, 0, mpicom, ier) - ! initialize input data for new dust emission module dmleung 14 Dec 2021 - call mpi_bcast (rough_fct, len(rough_fct), MPI_CHARACTER, 0, mpicom, ier)! added by dmleung, 17 Dec 2021 - ! Irrigation call mpi_bcast(irrigate, 1, MPI_LOGICAL, 0, mpicom, ier) @@ -910,13 +904,6 @@ subroutine control_print () write(iulog,*) ' Threshold above which the model keeps the lake landunit =', toosmall_lake write(iulog,*) ' Threshold above which the model keeps the wetland landunit =', toosmall_wetland write(iulog,*) ' Threshold above which the model keeps the urban landunits =', toosmall_urban - !##### for dmleung's input data for new dust emission module ##### - if (rough_fct == ' ') then ! -dmleung, 17 Dec 2021 - write(iulog,*) ' rough_fct surface roughness dataset not set' - else - write(iulog,*) ' surface roughness data = ',trim(rough_fct) - end if - !################################################################# if (use_cn) then if (suplnitro /= suplnNon)then write(iulog,*) ' Supplemental Nitrogen mode is set to run over Patches: ', & From 2bcfc13acde7b2b42b1a316fb3d6ea1c5770be38 Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Fri, 26 May 2023 13:57:31 -0600 Subject: [PATCH 019/406] 26 May 2023: dmleung removed the markers from the codes. Comments stay. --- bld/CLMBuildNamelist.pm | 5 +- bld/namelist_files/namelist_defaults_ctsm.xml | 2 - .../namelist_definition_ctsm.xml | 1 - src/biogeochem/DUSTMod.F90 | 300 ++++++------------ src/biogeophys/SoilStateInitTimeConstMod.F90 | 4 +- .../share_esmf/PrigentRoughnessStreamType.F90 | 43 +-- src/main/clm_instMod.F90 | 2 +- 7 files changed, 112 insertions(+), 245 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 3b0c79d000..ab1b6ac579 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -1560,7 +1560,7 @@ sub process_namelist_inline_logic { setup_logic_snowpack($opts, $nl_flags, $definition, $defaults, $nl); setup_logic_fates($opts, $nl_flags, $definition, $defaults, $nl); setup_logic_misc($opts, $nl_flags, $definition, $defaults, $nl); - setup_logic_prigent_roughness($opts, $nl_flags, $definition, $defaults, $nl); # dmleung added 31 Dec 2022 + setup_logic_prigent_roughness($opts, $nl_flags, $definition, $defaults, $nl); ######################################### # namelist group: atm2lnd_inparm @@ -4185,7 +4185,6 @@ sub setup_logic_misc { #------------------------------------------------------------------------------- sub setup_logic_prigent_roughness { - # dmleung added on 31 Dec 2022 for reading Prigent's roughness stream file my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldfilename_prigentroughness' ); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_meshfile_prigentroughness' ); @@ -4241,7 +4240,7 @@ sub write_output_files { push @groups, "ch4finundated"; push @groups, "soilbgc_decomp"; push @groups, "clm_canopy_inparm"; - push @groups, "prigentroughness"; # dmleung 31 Dec 2022 + push @groups, "prigentroughness"; if (remove_leading_and_trailing_quotes($nl->get_value('snow_cover_fraction_method')) eq 'SwensonLawrence2012') { push @groups, "scf_swenson_lawrence_2012_inparm"; } diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 3d0f5ef5e1..36719a55b7 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -2526,8 +2526,6 @@ lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne0np4.CONUS.ne30x8_hist_78pfts - - - shr_log_errMsg use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=) use clm_varpar , only : dst_src_nbr, ndst, sz_nbr, & - natpft_lb, natpft_ub, natpft_size ! -dmleung added 24 Jul 2022 + natpft_lb, natpft_ub, natpft_size use clm_varcon , only : grav, spval use landunit_varcon , only : istcrop, istsoil use clm_varctl , only : iulog @@ -31,9 +31,7 @@ module DUSTMod use LandunitType , only : lun use ColumnType , only : col use PatchType , only : patch - !use clm_instur , only : wt_lunit, wt_nat_patch ! dmleung added 24 Jul 2022 - !use landunit_varcon , only : istsoil, istcrop ! dmleung added 24 Jul 2022 (refering to main/landunit_varcon.F90, for wt_lunit, istsoil=1 is nat veg, istcrop=2 is crop) - use pftconMod , only : noveg ! dmleung added 24 Jul 2022 + use pftconMod , only : noveg use PrigentRoughnessStreamType , only : prigentroughnessstream_type ! ! !PUBLIC TYPES @@ -65,15 +63,13 @@ module DUSTMod real(r8), pointer, private :: vlc_trb_3_patch (:) ! turbulent deposition velocity 3(m/s) real(r8), pointer, private :: vlc_trb_4_patch (:) ! turbulent deposition velocity 4(m/s) real(r8), pointer, private :: mbl_bsn_fct_col (:) ! basin factor - !########### added by dmleung 27 Nov 2021 ######################################################################## real(r8), pointer, private :: dst_emiss_coeff_patch (:) ! dust emission coefficient (unitless) real(r8), pointer, private :: wnd_frc_thr_patch (:) ! wet fluid threshold (m/s) real(r8), pointer, private :: wnd_frc_thr_dry_patch (:) ! dry fluid threshold (m/s) - real(r8), pointer, private :: lnd_frc_mble_patch (:) ! land mobile fraction -dmleung + real(r8), pointer, private :: lnd_frc_mble_patch (:) ! land mobile fraction real(r8), pointer, private :: liq_frac_patch (:) ! liquid fraction of total water real(r8), pointer, private :: wnd_frc_soil_patch (:) ! soil wind friction velocity (m/s) real(r8), pointer, private :: gwc_patch (:) ! gravimetric water content (kg/kg) - !########### added by dmleung 2 Dec 2021 ######################################################################### real(r8), pointer, private :: intrmtncy_fct_patch (:) ! intermittency factor, accounting for turbulence shutting down dust emissions (unitless) real(r8), pointer, private :: stblty_patch (:) ! stability parameter for checking stability condition (stblty < 0 is unstable atmosphere) real(r8), pointer, private :: u_mean_slt_patch (:) ! wind speed 0.1 m level of dust saltation (m/s) @@ -83,13 +79,10 @@ module DUSTMod real(r8), pointer, private :: thr_crs_rate_patch (:) ! threshold crossing rate (unitless) real(r8), pointer, private :: prb_crs_fld_thr_patch (:) ! probability of wind speed crossing fluid threshold real(r8), pointer, private :: prb_crs_impct_thr_patch (:) ! probability of wind speed crossing impact threshold - !########### added by dmleung 20 Dec 2021 ######################################################################## real(r8), pointer, private :: ssr_patch (:) ! [dimless] integrated shear stress ratiio, defined by Okin (2008) and then integrated by Caroline Pierre et al. (2014) real(r8), pointer, private :: lai_patch (:) ! [m2 leaf /m2 land] LAI+SAI for calculating Okin's drag partition, averaged to landunit level real(r8), pointer, private :: frc_thr_rghn_fct_patch (:) ! [dimless] hybrid drag partition (or called roughness) factor - !########### added by dmleung 28 Jul 2022 ######################################################################## real(r8), pointer, private :: wnd_frc_thr_std_patch (:) ! standardized fluid threshold friction velocity (m/s) - !########### added by dmleung 31 Dec 2022 ######################################################################## type(prigentroughnessstream_type), private :: prigentroughnessstream ! Prigent roughness stream data real(r8), pointer, private :: dpfct_rock_patch (:) ! [fraction] rock drag partition factor, time-constant contains @@ -109,21 +102,16 @@ module DUSTMod contains !------------------------------------------------------------------------ - !##### dmleung edited for initializing stream files 31 Dec 2022 ######## - !subroutine Init(this, bounds, NLFilename, num_nolakep, filter_nolakep) subroutine Init(this, bounds, NLFilename) class(dust_type) :: this type(bounds_type), intent(in) :: bounds - character(len=*), intent(in) :: NLFilename ! dmleung added 31 Dec 2022 - !integer , intent(in) :: num_nolakep ! number of column non-lake points in patch filter - !integer , intent(in) :: filter_nolakep(num_nolakep) ! patch filter for non-lake points + character(len=*), intent(in) :: NLFilename call this%InitAllocate (bounds) call this%InitHistory (bounds) - call this%prigentroughnessstream%Init( bounds, NLFilename ) ! dmleung added 31 Dec 2022 - !call this%InitCold (bounds, num_nolakep, filter_nolakep) - call this%InitCold (bounds) ! dmleung commented 31 Dec 2022 + call this%prigentroughnessstream%Init( bounds, NLFilename ) + call this%InitCold (bounds) call this%InitDustVars (bounds) end subroutine Init @@ -151,7 +139,6 @@ subroutine InitAllocate(this, bounds) allocate(this%vlc_trb_3_patch (begp:endp)) ; this%vlc_trb_3_patch (:) = nan allocate(this%vlc_trb_4_patch (begp:endp)) ; this%vlc_trb_4_patch (:) = nan allocate(this%mbl_bsn_fct_col (begc:endc)) ; this%mbl_bsn_fct_col (:) = nan - !#### added by dmleung 27 Nov 2021 ##################################### allocate(this%dst_emiss_coeff_patch (begp:endp)) ; this%dst_emiss_coeff_patch (:) = nan allocate(this%wnd_frc_thr_patch (begp:endp)) ; this%wnd_frc_thr_patch (:) = nan allocate(this%wnd_frc_thr_dry_patch (begp:endp)) ; this%wnd_frc_thr_dry_patch (:) = nan @@ -159,7 +146,6 @@ subroutine InitAllocate(this, bounds) allocate(this%wnd_frc_soil_patch (begp:endp)) ; this%wnd_frc_soil_patch (:) = nan allocate(this%gwc_patch (begp:endp)) ; this%gwc_patch (:) = nan allocate(this%liq_frac_patch (begp:endp)) ; this%liq_frac_patch (:) = nan - !#### added by dmleung 2 Dec 2021 ###################################### allocate(this%intrmtncy_fct_patch (begp:endp)) ; this%intrmtncy_fct_patch (:) = nan allocate(this%stblty_patch (begp:endp)) ; this%stblty_patch (:) = nan allocate(this%u_mean_slt_patch (begp:endp)) ; this%u_mean_slt_patch (:) = nan @@ -169,13 +155,10 @@ subroutine InitAllocate(this, bounds) allocate(this%thr_crs_rate_patch (begp:endp)) ; this%thr_crs_rate_patch (:) = nan allocate(this%prb_crs_fld_thr_patch (begp:endp)) ; this%prb_crs_fld_thr_patch (:) = nan allocate(this%prb_crs_impct_thr_patch (begp:endp)) ; this%prb_crs_impct_thr_patch (:) = nan - !#### added by dmleung 17 Dec 2021 ###################################### allocate(this%ssr_patch (begp:endp)) ; this%ssr_patch (:) = nan allocate(this%lai_patch (begp:endp)) ; this%lai_patch (:) = nan allocate(this%frc_thr_rghn_fct_patch (begp:endp)) ; this%frc_thr_rghn_fct_patch (:) = nan - !#### added by dmleung 28 Jul 2022 ###################################### allocate(this%wnd_frc_thr_std_patch (begp:endp)) ; this%wnd_frc_thr_std_patch (:) = nan - !#### added by dmleung 31 Dec 2022 ###################################### allocate(this%dpfct_rock_patch (begp:endp)) ; this%dpfct_rock_patch (:) = nan end subroutine InitAllocate @@ -221,7 +204,6 @@ subroutine InitHistory(this, bounds) avgflag='A', long_name='turbulent deposition velocity 4', & ptr_patch=this%vlc_trb_4_patch, default='inactive') - !#####added by dmleung 27 Nov 2021######################################### this%dst_emiss_coeff_patch(begp:endp) = spval call hist_addfld1d (fname='C_d', units='dimensionless', & avgflag='A', long_name='dust emission coefficient', & @@ -250,7 +232,6 @@ subroutine InitHistory(this, bounds) call hist_addfld1d (fname='LIQ_FRAC', units='dimensionless', & avgflag='A', long_name='fraction of total water that is liquid', & ptr_patch=this%liq_frac_patch, set_lake=0._r8, set_urb=0._r8) - !#####added by dmleung 2 Dec 2021 ######################################### this%u_mean_slt_patch(begp:endp) = spval call hist_addfld1d (fname='U_S_MEAN', units='m/s', & avgflag='A', long_name='mean wind velocity at saltation level', & @@ -287,7 +268,6 @@ subroutine InitHistory(this, bounds) call hist_addfld1d (fname='ETA', units='', & avgflag='A', long_name='intermittency factor', & ptr_patch=this%intrmtncy_fct_patch, set_lake=0._r8, set_urb=0._r8) - !#####added by dmleung 20 Dec 2021 ######################################## this%ssr_patch(begp:endp) = spval call hist_addfld1d (fname='SSR', units='m/s', & avgflag='A', long_name='Okin-Pierre vegetation shear stress ratio (drag partition factor)', & @@ -300,35 +280,24 @@ subroutine InitHistory(this, bounds) call hist_addfld1d (fname='FRC_THR_RGHN_FCT', units='dimensionless', & avgflag='A', long_name='hybrid drag partition (or roughness) factor', & ptr_patch=this%frc_thr_rghn_fct_patch, set_lake=0._r8, set_urb=0._r8) - !#####added by dmleung 28 Jul 2022 ######################################## this%wnd_frc_thr_std_patch(begp:endp) = spval call hist_addfld1d (fname='WND_FRC_FT_STD', units='m/s', & avgflag='A', long_name='standardized fluid threshold friction velocity', & ptr_patch=this%wnd_frc_thr_std_patch, set_lake=0._r8, set_urb=0._r8) - !#####added by dmleung 31 Dec 2022 ######################################## this%dpfct_rock_patch(begp:endp) = spval call hist_addfld1d (fname='DPFCT_ROCK', units='m/s', & avgflag='A', long_name='rock drag partition factor', & ptr_patch=this%dpfct_rock_patch) - !########################################################################## end subroutine InitHistory !----------------------------------------------------------------------- - !subroutine InitCold(this, bounds, num_nolakep, filter_nolakep) !dmleung commented 31 Dec 2022 subroutine InitCold(this, bounds) ! - !USES dmleung added 31 Dec 2022 - !use landunit_varcon , only : istdlak - !use LandunitType , only : lun ! ! !ARGUMENTS: - class(dust_type), intent(inout) :: this ! dmleung used class instead of type, 31 Dec 2022 + class(dust_type), intent(inout) :: this ! Danny M. Leung used class instead of type type(bounds_type), intent(in) :: bounds - ! dmleung also added the below no-lake filter 31 Dec 2022 - !integer , intent(in) :: num_nolakep ! number of column non-lake points in patch filter - !integer , intent(in) :: filter_nolakep(num_nolakep) ! patch filter for non-lake points - !type(dust_type), intent(inout) :: dust_inst ! ! !LOCAL VARIABLES: integer :: c,l @@ -344,19 +313,14 @@ subroutine InitCold(this, bounds) end if end do - !associate( & - ! dpfct_rock => this%dpfct_rock_patch & - ! ) - ! Caulculate Drag Partition factor, dmleung added 31 Dec 2022 + ! Caulculate Drag Partition factor if ( this%prigentroughnessstream%useStreams() )then !if usestreams == true, and it should be always true call this%prigentroughnessstream%CalcDragPartition( bounds, & - ! num_nolakep, filter_nolakep, this%dpfct_rock_patch(bounds%begp:bounds%endp) ) this%dpfct_rock_patch(bounds%begp:bounds%endp) ) else call endrun( "ERROR:: Drag partitioning MUST now use a streams file of aeolian roughness length to calculate, it can no longer read from the fsurdat file" ) end if - !end associate end subroutine InitCold @@ -376,9 +340,7 @@ subroutine DustEmission (bounds, & ! !USES use shr_const_mod, only : SHR_CONST_RHOFW use subgridaveMod, only : p2g - !use clm_instur , only : wt_lunit, wt_nat_patch ! dmleung added 24 Jul 2022 - !use landunit_varcon , only : istsoil, istcrop ! dmleung added 24 Jul 2022 (refering to main/landunit_varcon.F90, for wt_lunit, istsoil=1 is nat veg, istcrop=2 is crop) - use pftconMod , only : noveg ! dmleung added 24 Jul 2022 + use pftconMod , only : noveg ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds @@ -404,7 +366,7 @@ subroutine DustEmission (bounds, & real(r8) :: flx_mss_vrt_dst_ttl(bounds%begp:bounds%endp) real(r8) :: frc_thr_wet_fct real(r8) :: frc_thr_rgh_fct - !real(r8) :: wnd_frc_thr_slt ! dmleung commented and put below 2 Dec 2021 + !real(r8) :: wnd_frc_thr_slt ! Danny M. Leung commented and put below real(r8) :: wnd_rfr_thr_slt real(r8) :: wnd_frc_slt real(r8) :: lnd_frc_mbl(bounds%begp:bounds%endp) @@ -415,16 +377,13 @@ subroutine DustEmission (bounds, & real(r8) :: sumwt(bounds%begl:bounds%endl) ! sum of weights logical :: found ! temporary for error check integer :: index - !########### added by dmleung 27 Nov 2021 ######################################### - real(r8) :: tmp2 ! calculates the dry fluid threshold using Shao and Lu (2000) scheme; replace the tmp1 (Iversen and White, 1982) that was passed from Dustini to DustEmission; tmp2 will be calculated here 23 May 2020 -dmleung - real(r8) :: wnd_frc_thr_slt_std ! [m/s] The soil threshold friction speed at standard air density (1.2250 kg/m3) -jfk - real(r8) :: frag_expt ! fragmentation exponent, -dmleung 22 Jun 2021 - !########### added by dmleung 2 Dec 2021 for intermittency scheme ################# - real(r8) :: wnd_frc_thr_slt_it ! [m/s] created for impact threshold friction velocity, dmleung 9 Jun 2021 - real(r8) :: wnd_frc_thr_slt ! [m/s] used for wet fluid threshold friction velocity, dmleung 9 Jun 2021 - !########### added by dmleung 20 Dec 2021 for drag partition effect ################# + + real(r8) :: tmp2 ! calculates the dry fluid threshold using Shao and Lu (2000) scheme; replace the tmp1 (Iversen and White, 1982) that was passed from Dustini to DustEmission; tmp2 will be calculated here + real(r8) :: wnd_frc_thr_slt_std ! [m/s] The soil threshold friction speed at standard air density (1.2250 kg/m3) + real(r8) :: frag_expt ! fragmentation exponent + real(r8) :: wnd_frc_thr_slt_it ! [m/s] created for impact threshold friction velocity + real(r8) :: wnd_frc_thr_slt ! [m/s] used for wet fluid threshold friction velocity real(r8) :: K_length ! [dimless] normalized mean interobstacle distance, or called gap length (Okin, 2008) - !########### added by dmleung 22 Jul 2022 for LUH2 land cover #################### real(r8) :: bare_frc ! LUH2 bare soil land cover fraction real(r8) :: veg_frc ! LUH2 natural vegetation + crop land cover fraction ! @@ -433,24 +392,19 @@ subroutine DustEmission (bounds, & real(r8), parameter :: cst_slt = 2.61_r8 ! [frc] Saltation constant real(r8), parameter :: flx_mss_fdg_fct = 5.0e-4_r8 ! [frc] Empir. mass flx tuning eflx_lh_vegt !real(r8), parameter :: vai_mbl_thr = 0.3_r8 ! [m2 m-2] VAI threshold quenching dust mobilization - !####### added by dmleung 27 Nov 2021 ########################################################################### character(len=*),parameter :: subname = 'DUSTEmission' - real(r8), parameter :: vai_mbl_thr = 1.0_r8 ! [m2 m-2] new VAI threshold; dmleung suggests 1 or 0.5, and the default 0.3 seems a bit too small -dmleung 27 Nov 2021 - real(r8), parameter :: Cd0 = 4.4e-5_r8 ! [dimless] proportionality constant in calculation of dust emission coefficient -jfk - real(r8), parameter :: Ca = 2.7_r8 ! [dimless] proportionality constant in scaling of dust emission exponent -jfk - real(r8), parameter :: Ce = 2.0_r8 ! [dimless] proportionality constant scaling exponential dependence of dust emission coefficient on standardized soil threshold friction speed -jfk - real(r8), parameter :: C_tune = 0.05_r8 ! [dimless] global tuning constant for vertical dust flux; set to produce ~same global dust flux in control sim (I_2000) as old parameterization -jfk - real(r8), parameter :: wnd_frc_thr_slt_std_min = 0.16_r8 ! [m/s] minimum standardized soil threshold friction speed -jfk - real(r8), parameter :: forc_rho_std = 1.2250_r8 ! [kg/m3] density of air at standard pressure (101325) and temperature (293 K) -jfk - real(r8), parameter :: dns_slt = 2650.0_r8 ! [kg m-3] Density of optimal saltation particles, dml 23 May 2020 - !####### added by dmleung 2 Dec 2021 for intermittency ########################################################## - real(r8), parameter :: B_it = 0.82_r8 ! [dimless] ratio = u_star_it / u_star_ft0 (may need to change into a fn of moisture later on) -dml - real(r8), parameter :: k = 0.4_r8 ! [dimless] von Karman constant -dml - !####### added by dmleung 2 Dec 2021 for Okin (2008) drag partition for plants ########################################################## - real(r8), parameter :: f_0 = 0.32_r8 ! [dimless] SSR in the immediate lee of a plant, dimensionless - real(r8), parameter :: c_e = 4.8_r8 ! [dimless] e-folding distance velocity recovery, dimensionless - !################################################################################################################ - !################################################################################################################ + real(r8), parameter :: vai_mbl_thr = 1.0_r8 ! [m2 m-2] new VAI threshold; Danny M. Leung suggests 1, and the old 0.3 seems a bit too small + real(r8), parameter :: Cd0 = 4.4e-5_r8 ! [dimless] proportionality constant in calculation of dust emission coefficient + real(r8), parameter :: Ca = 2.7_r8 ! [dimless] proportionality constant in scaling of dust emission exponent + real(r8), parameter :: Ce = 2.0_r8 ! [dimless] proportionality constant scaling exponential dependence of dust emission coefficient on standardized soil threshold friction speed + real(r8), parameter :: C_tune = 0.05_r8 ! [dimless] global tuning constant for vertical dust flux; set to produce ~same global dust flux in control sim (I_2000) as old parameterization + real(r8), parameter :: wnd_frc_thr_slt_std_min = 0.16_r8 ! [m/s] minimum standardized soil threshold friction speed + real(r8), parameter :: forc_rho_std = 1.2250_r8 ! [kg/m3] density of air at standard pressure (101325) and temperature (293 K) + real(r8), parameter :: dns_slt = 2650.0_r8 ! [kg m-3] Density of optimal saltation particles + real(r8), parameter :: B_it = 0.82_r8 ! [dimless] ratio = u_star_it / u_star_ft0 + real(r8), parameter :: k = 0.4_r8 ! [dimless] von Karman constant + real(r8), parameter :: f_0 = 0.32_r8 ! [dimless] SSR in the immediate lee of a plant + real(r8), parameter :: c_e = 4.8_r8 ! [dimless] e-folding distance velocity recovery !------------------------------------------------------------------------ associate( & @@ -474,15 +428,14 @@ subroutine DustEmission (bounds, & mbl_bsn_fct => dust_inst%mbl_bsn_fct_col , & ! Input: [real(r8) (:) ] basin factor flx_mss_vrt_dst => dust_inst%flx_mss_vrt_dst_patch , & ! Output: [real(r8) (:,:) ] surface dust emission (kg/m**2/s) flx_mss_vrt_dst_tot => dust_inst%flx_mss_vrt_dst_tot_patch , & ! Output: [real(r8) (:) ] total dust flux back to atmosphere (pft) - ! the following are added by dmleung 27 Nov 2021 + dst_emiss_coeff => dust_inst%dst_emiss_coeff_patch , & ! Output dust emission coefficient - wnd_frc_thr => dust_inst%wnd_frc_thr_patch , & ! output impact threshold -dmleung + wnd_frc_thr => dust_inst%wnd_frc_thr_patch , & ! output impact threshold wnd_frc_thr_dry => dust_inst%wnd_frc_thr_dry_patch , & ! output dry threshold - lnd_frc_mble => dust_inst%lnd_frc_mble_patch , & ! -dmleung, 3 Feb 2020 + lnd_frc_mble => dust_inst%lnd_frc_mble_patch , & ! output bare land fraction wnd_frc_soil => dust_inst%wnd_frc_soil_patch , & ! soil friction velocity u_*s = (u_*)(f_eff) gwc => dust_inst%gwc_patch , & ! output gravimetric water content liq_frac => dust_inst%liq_frac_patch , & - ! added by dmleung 8 Jul 2019, recoded 2 Dec 2021 intrmtncy_fct => dust_inst%intrmtncy_fct_patch , & stblty => dust_inst%stblty_patch , & u_mean_slt => dust_inst%u_mean_slt_patch , & @@ -492,14 +445,11 @@ subroutine DustEmission (bounds, & thr_crs_rate => dust_inst%thr_crs_rate_patch , & prb_crs_fld_thr => dust_inst%prb_crs_fld_thr_patch , & prb_crs_impct_thr => dust_inst%prb_crs_impct_thr_patch , & - ! added by dmleung 20 Dec 2021 ssr => dust_inst%ssr_patch , & lai => dust_inst%lai_patch , & frc_thr_rghn_fct => dust_inst%frc_thr_rghn_fct_patch , & - ! added by dmleung 28 Jul 2022 wnd_frc_thr_std => dust_inst%wnd_frc_thr_std_patch , & - ! added by dmleung 31 Dec 2022 - dpfct_rock => dust_inst%dpfct_rock_patch & ! dmleung used roughfct (roughness factor) instead of dpfct_rock (rock drag partition factor) here. Could change it back to dpfct_rock here and below later, 31 Dec 2022 + dpfct_rock => dust_inst%dpfct_rock_patch & ) ttlai(bounds%begp : bounds%endp) = 0._r8 @@ -572,14 +522,13 @@ subroutine DustEmission (bounds, & end if end do - ! dmleung add output for bare_frc and veg_frc here if wanted !!!!!!!!!---------------------- + ! dmleung add output for bare_frc and veg_frc here if wanted !!!!---------------------- ! reset history output variables before next if-statement to avoid output = inf do fp = 1,num_nolakep p = filter_nolakep(fp) flx_mss_vrt_dst_tot(p) = 0.0_r8 - ! the following are added by dmleung 27 Nov 2021 dst_emiss_coeff(p) = 0.0_r8 wnd_frc_thr(p) = 0.0_r8 wnd_frc_thr_dry(p) = 0.0_r8 @@ -587,7 +536,6 @@ subroutine DustEmission (bounds, & wnd_frc_soil(p) = 0.0_r8 gwc(p) = 0.0_r8 liq_frac(p) = 0.0_r8 - ! dmleung's edit, 8 Jul 2019; added by dmleung 2 Dec 2021 u_mean_slt(p) = 0.0_r8 u_sd_slt(p) = 0.0_r8 stblty(p) = 0.0_r8 @@ -597,11 +545,9 @@ subroutine DustEmission (bounds, & prb_crs_fld_thr(p) = 0.0_r8 prb_crs_impct_thr(p) = 0.0_r8 intrmtncy_fct(p) = 0.0_r8 - ! dmleung's edit, 20 Dec 2021 ssr(p) = 0.0_r8 lai(p) = 0.0_r8 frc_thr_rghn_fct(p) = 0.0_r8 - ! dmleung added 28 Jul 2022 wnd_frc_thr_std(p) = 0.0_r8 end do do n = 1, ndst @@ -617,9 +563,9 @@ subroutine DustEmission (bounds, & l = patch%landunit(p) g = patch%gridcell(p) - !################################################################################################ + !-------------------------------------------------------------------------------------------------- ! put dust emission calculation here to output threshold friction velocity for the whole globe, - ! not just when lnd_frc_mbl = 0. Edited by dmleung 27 Nov 2021 + ! not just when lnd_frc_mbl = 0. Danny M. Leung 27 Nov 2021 bd = (1._r8-watsat(c,1))*2.7e3_r8 ![kg m-3] Bulk density of dry surface soil gwc_sfc = h2osoi_vol(c,1)*SHR_CONST_RHOFW/bd ![kg kg-1] Gravimetric H2O cont if (gwc_sfc > gwc_thr(c)) then @@ -628,54 +574,52 @@ subroutine DustEmission (bounds, & frc_thr_wet_fct = 1.0_r8 end if - ! output moisture variables -dmleung, coded Jul 2020, recoded 18 Mar 2021, added to CLM5 27 Nov 2021 + ! output moisture variables gwc(p) = gwc_sfc ! output surface gravimetric water content ! slevis: adding liqfrac here, because related to effects from soil water - liqfrac = max( 0.0_r8, min( 1.0_r8, h2osoi_liq(c,1) / (h2osoi_ice(c,1)+h2osoi_liq(c,1)+1.0e-6_r8) ) ) !-dmleung 27 Nov 2021 - ! output liquid fraction -dmleung 27 Nov 2021 + liqfrac = max( 0.0_r8, min( 1.0_r8, h2osoi_liq(c,1) / (h2osoi_ice(c,1)+h2osoi_liq(c,1)+1.0e-6_r8) ) ) + ! output liquid fraction liq_frac(p) = liqfrac !####################################################################################################### ! calculate Shao & Lu (2000) dust emission threshold scheme here - ! use tmp1 from DUSTini for Iversen and White I&W (1982) (75 um is optimal); use tmp2 for S&L (2000) (80 um is optimal) - ! recoded to CLM5 27 Nov 2021 + ! use tmp1 from DUSTini for Iversen and White I&W (1982) (~75 um is optimal); use tmp2 for S&L (2000) (~80 um is optimal) !####################################################################################################### - tmp2 = 1.0_r8*sqrt(0.0123_r8 * (dns_slt*grav*130.0e-6_r8 + 1.65e-4_r8/130.0e-6_r8)) ! calculate S&L (2000) scheme here for threshold; gamma = 1.65e-4 following S&L00, D_p = 127 um ~ 130 um following dmleung's dust paper. As this is a global constant, this line can be put outside the loop to save computational power. + tmp2 = 1.0_r8*sqrt(0.0123_r8 * (dns_slt*grav*130.0e-6_r8 + 1.65e-4_r8/130.0e-6_r8)) ! calculate S&L (2000) scheme here for threshold; gamma = 1.65e-4 following S&L00, D_p = 127 um ~ 130 um following Leung et al. (2022). As this is a global constant, this line can be put outside the loop to save computational power. wnd_frc_thr_dry(p) = tmp2 / sqrt(forc_rho(c)) ! output dry fluid threshold - wnd_frc_thr_slt = tmp2 / sqrt(forc_rho(c)) * frc_thr_wet_fct !* frc_thr_rgh_fct ! use as threshold in this module - wnd_frc_thr_slt_it = B_it * tmp2 / sqrt(forc_rho(c)) ! define impact threshold -dml 9 Jun 2021, recoded to CLM5 27 Nov 2021 - - !wnd_frc_thr_dry(p) = tmp1 / sqrt(forc_rho(c)) ! output dry fluid threshold - !wnd_frc_thr_slt = tmp1 / sqrt(forc_rho(c)) * frc_thr_wet_fct !* frc_thr_rgh_fct ! use as threshold in this module - !wnd_frc_thr_slt_it = B_it * tmp1 / sqrt(forc_rho(c)) ! define impact threshold -dmleung 9 Jun 2021, recoded to CLM5 27 Nov 2021 - ! the above formula is true for Iversen and White (1982) and Shao and Lu (2000) scheme -dmleung, 23 Feb 2020, added to CLM5 27 Nov 2021 - wnd_frc_thr(p) = wnd_frc_thr_slt ! output fluid threshold -dmleung - - ! use emission threshold to calculate standardized threshold and dust emission coefficient dmleung 27 Nov 2021 - wnd_frc_thr_slt_std = wnd_frc_thr_slt * sqrt(forc_rho(c) / forc_rho_std) ! standardized soil threshold friction speed -jfk (defined using fluid threshold - wnd_frc_thr_std(p) = wnd_frc_thr_slt_std ! output standardized fluid threshold -dmleung added 28 Jul 2022 - dst_emiss_coeff(p) = Cd0 * exp(-Ce * (wnd_frc_thr_slt_std - wnd_frc_thr_slt_std_min) / wnd_frc_thr_slt_std_min) ! save dust emission coefficient here for all grids, -dml, 1 Mar 2021 - - ! framentation exponent dmleung 27 Nov 2021; moved to this block 23 Dec 2021 - frag_expt = (Ca * (wnd_frc_thr_slt_std - wnd_frc_thr_slt_std_min) / wnd_frc_thr_slt_std_min) ! fragmentation exponent, defined in Kok et al. (2014a) -dmleung 27 Nov 2021 + wnd_frc_thr_slt = tmp2 / sqrt(forc_rho(c)) * frc_thr_wet_fct !* frc_thr_rgh_fct ! fluid threshold + wnd_frc_thr_slt_it = B_it * tmp2 / sqrt(forc_rho(c)) ! define impact threshold + + !wnd_frc_thr_dry(p) = tmp1 / sqrt(forc_rho(c)) ! output dry fluid threshold; tmp1 uses I&W 1982 + !wnd_frc_thr_slt = tmp1 / sqrt(forc_rho(c)) * frc_thr_wet_fct !* frc_thr_rgh_fct ! fluid threshold + !wnd_frc_thr_slt_it = B_it * tmp1 / sqrt(forc_rho(c)) ! define impact threshold + ! the above formula is true for Iversen and White (1982) and Shao and Lu (2000) scheme + wnd_frc_thr(p) = wnd_frc_thr_slt ! output fluid threshold + + ! use emission threshold to calculate standardized threshold and dust emission coefficient + wnd_frc_thr_slt_std = wnd_frc_thr_slt * sqrt(forc_rho(c) / forc_rho_std) ! standardized soil threshold friction speed (defined using fluid threshold) + wnd_frc_thr_std(p) = wnd_frc_thr_slt_std ! output standardized fluid threshold + dst_emiss_coeff(p) = Cd0 * exp(-Ce * (wnd_frc_thr_slt_std - wnd_frc_thr_slt_std_min) / wnd_frc_thr_slt_std_min) ! save dust emission coefficient here for all grids + + ! framentation exponent + frag_expt = (Ca * (wnd_frc_thr_slt_std - wnd_frc_thr_slt_std_min) / wnd_frc_thr_slt_std_min) ! fragmentation exponent, defined in Kok et al. (2014a) if (frag_expt > 5.0_r8) then ! set fragmentation exponent to be 3 or 5 at maximum, to avoid local AOD blowup frag_expt = 5.0_r8 end if - !################ drag partition effect, and soil friction velocity############################ + !################ drag partition effect, and soil friction velocity ########################### ! subsection on computing vegetation drag partition and hybrid drag partition factors - ! in our scheme, drag partition effect is applied on the wind instead of the threshold - ! -dmleung, 7 Jul 2021 , coded to CLM5 27 Nov 2021 + ! in Leung et al. (2022), drag partition effect is applied on the wind instead of the threshold !############################################################################################## ! the following comes from subr. frc_thr_rgh_fct_get ! purpose: compute factor by which surface roughness increases threshold ! friction velocity (currently a constant) if (lnd_frc_mbl(p) > 0.0_r8 .AND. tlai_lu(l)<=1_r8) then - ! vegetation drag partition equation following Gregory Okin (2008) + Caroline Pierre et al. (2014), dmleung 20 Dec 2021 - lai(p) = tlai_lu(l)+0.1_r8 ! LAI+SAI averaged to landunit level; saved for output + ! vegetation drag partition equation following Gregory Okin (2008) + Caroline Pierre et al. (2014) + lai(p) = tlai_lu(l)+0.1_r8 ! LAI+SAI averaged to landunit level; the equation is undefined at lai=0 so we add in a small number. Then lai is saved for output if (lai(p) > 1_r8) then lai(p) = 1_r8 ! setting LAI ~ 0.1 to be a min value (since the value goes to infinity when LAI=0) end if ! and 1 to be a max value as computing K involves 1 / LAI @@ -684,18 +628,15 @@ subroutine DustEmission (bounds, & K_length = 2_r8 * (1_r8/lai(p) - 1_r8) ! Here LAI has to be non-zero to avoid blowup ssr(p) = (K_length+f_0*c_e)/(K_length+c_e) - !frc_thr_rgh_fct = (rockfrc(p)*(roughfct(p))**3_r8 + (vegefrc(p)+sparfrc(p))*(ssr(p))**3_r8 )**(0.3333_r8) ! land cover weighted mean using static GLCNMo bare land fraction LC0, dmleung 20 Dec 2021i; dmleung commented 24 Jul 2022 - - ! dmleung added calculation of LUH2 bare vs veg fraction within a grid 24 Jul 2022 + ! dmleung added calculation of LUH2 bare vs veg fraction within a grid !bare_frc = wt_lunit(g,istsoil) * wt_nat_patch(g,noveg) !veg_frc = wt_lunit(g,istsoil) * sum(wt_nat_patch(g,(noveg+1):natpft_ub)) + wt_lunit(g,istcrop) - !frc_thr_rgh_fct = (bare_frc*(roughfct(p))**3_r8 + veg_frc*(ssr(p))**3_r8 )**(0.3333_r8) ! land cover weighted mean using LUH2 land cover, dmleung 24 Jul 2022 + !frc_thr_rgh_fct = (bare_frc*(roughfct(p))**3_r8 + veg_frc*(ssr(p))**3_r8 )**(0.3333_r8) ! land cover weighted mean using LUH2 land cover - ! dmleung added calculation of LUH2 bare vs veg fraction within a grid 6 Oct 2022 + ! calculation of drag partition effect using LUH2 bare and veg fractions within a grid if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then if (patch%itype(p) == noveg) then - !frc_thr_rgh_fct = roughfct(p) ! dmleung commented out 13 Dec 2022 and added next line frc_thr_rgh_fct = dpfct_rock(p) else frc_thr_rgh_fct = ssr(p) @@ -704,17 +645,18 @@ subroutine DustEmission (bounds, & frc_thr_rgh_fct = 1.0_r8 end if - wnd_frc_slt = fv(p) * frc_thr_rgh_fct ! wnd_frc_slt will be used in the dust emission equation -dmleung + wnd_frc_slt = fv(p) * frc_thr_rgh_fct ! wnd_frc_slt will be used in the dust emission equation + + frc_thr_rghn_fct(p) = frc_thr_rgh_fct ! save and output hybrid drag partition factor - frc_thr_rghn_fct(p) = frc_thr_rgh_fct ! save and output hybrid drag partition factor, dmleung 20 Dec 2021 else wnd_frc_slt = fv(p) ! The value here is not important since once lnd_frc_mbl(p) <= 0.0_r8 there will be no emission. - frc_thr_rghn_fct(p) = 0.0_r8 ! save and output hybrid drag partition factor, dmleung 20 Dec 2021 + frc_thr_rghn_fct(p) = 0.0_r8 ! save and output hybrid drag partition factor end if !########## end of drag partition effect ####################################################### - !############ Add Owen effect; if not, comment out this block !-dmleung, 27 Nov 2021 ########### + !############ comment out Owen's offect this block; dust emission does not need to consider it ########### ! the following if-block comes from subr. wnd_frc_slt_get ! purpose: compute the saltating friction velocity ! theory: saltation roughens the boundary layer, AKA "Owen's effect" @@ -723,82 +665,41 @@ subroutine DustEmission (bounds, & ! wnd_rfr_dlt = u10(p) - wnd_rfr_thr_slt ! wnd_frc_slt_dlt = 0.003_r8 * wnd_rfr_dlt * wnd_rfr_dlt ! wnd_frc_slt = wnd_frc_slt + wnd_frc_slt_dlt ! careful that RHS is now wnd_frc_slt instead of fv(p) - ! ! because wnd_frc_slt takes drag partition effect into account, but fv(p) doesn't. dmleung 27 Nov 2021 + ! ! because wnd_frc_slt takes drag partition effect into account, but fv(p) doesn't. !end if !########## end of Owen effect ################################################################ - ! save soil friction velocity and roughness effect before the if-statement, -dml, 1 Mar 2021, coded to CLM5 27 Nov 2021 - wnd_frc_soil(p) = wnd_frc_slt ! save soil friction velocity for CLM output, which has drag partition and Owen effect -dml + ! save soil friction velocity and roughness effect before the if-statement + wnd_frc_soil(p) = wnd_frc_slt ! save soil friction velocity for CLM output, which has drag partition and Owen effect ! save land mobile fraction - lnd_frc_mble(p) = lnd_frc_mbl(p) ! save land mobile fraction first, before the if-statement, -dml, 1 Mar 2021 + lnd_frc_mble(p) = lnd_frc_mbl(p) ! save land mobile fraction first, before the if-statement ! only perform the following calculations if lnd_frc_mbl is non-zero - if (lnd_frc_mbl(p) > 0.0_r8) then - - ! the following comes from subr. frc_thr_rgh_fct_get - ! purpose: compute factor by which surface roughness increases threshold - ! friction velocity (currently a constant) - - !frc_thr_rgh_fct = 1.0_r8 - - ! the following comes from subr. frc_thr_wet_fct_get - ! purpose: compute factor by which soil moisture increases threshold friction velocity - ! adjust threshold velocity for inhibition by moisture - ! modified 4/5/2002 (slevis) to use gravimetric instead of volumetric - ! water content - - !bd = (1._r8-watsat(c,1))*2.7e3_r8 ![kg m-3] Bulk density of dry surface soil - !gwc_sfc = h2osoi_vol(c,1)*SHR_CONST_RHOFW/bd ![kg kg-1] Gravimetric H2O cont - !if (gwc_sfc > gwc_thr(c)) then - ! frc_thr_wet_fct = sqrt(1.0_r8 + 1.21_r8 * (100.0_r8*(gwc_sfc - gwc_thr(c)))**0.68_r8) - !else - ! frc_thr_wet_fct = 1.0_r8 - !end if - - ! slevis: adding liqfrac here, because related to effects from soil water - - !liqfrac = max( 0.0_r8, min( 1.0_r8, h2osoi_liq(c,1) / (h2osoi_ice(c,1)+h2osoi_liq(c,1)+1.0e-6_r8) ) ) - - ! the following lines come from subr. dst_mbl - ! purpose: adjust threshold friction velocity to acct for moisture and - ! roughness. The ratio tmp1 / sqrt(forc_rho) comes from - ! subr. wnd_frc_thr_slt_get which computes dry threshold - ! friction velocity for saltation - - !wnd_frc_thr_slt = tmp1 / sqrt(forc_rho(c)) * frc_thr_wet_fct * frc_thr_rgh_fct + if (lnd_frc_mbl(p) > 0.0_r8) then ! if bare land fraction is larger than 0 then calculate the dust emission equation ! reset these variables which will be updated in the following if-block - !wnd_frc_slt = fv(p) + !wnd_frc_slt = fv(p) ! we don't need this line because its calculation is moved upward flx_mss_hrz_slt_ttl = 0.0_r8 flx_mss_vrt_dst_ttl(p) = 0.0_r8 ! the following line comes from subr. dst_mbl ! purpose: threshold saltation wind speed - wnd_rfr_thr_slt = u10(p) * wnd_frc_thr_slt / fv(p) ! keep and use if I want Z03 scheme -dmleung - - ! the following if-block comes from subr. wnd_frc_slt_get - ! purpose: compute the saltating friction velocity - ! theory: saltation roughens the boundary layer, AKA "Owen's effect" - - !if (u10(p) >= wnd_rfr_thr_slt) then - ! wnd_rfr_dlt = u10(p) - wnd_rfr_thr_slt - ! wnd_frc_slt_dlt = 0.003_r8 * wnd_rfr_dlt * wnd_rfr_dlt - ! wnd_frc_slt = fv(p) + wnd_frc_slt_dlt - !end if + wnd_rfr_thr_slt = u10(p) * wnd_frc_thr_slt / fv(p) ! keep and use if we want the default Z03 scheme + ! the following comes from subr. flx_mss_hrz_slt_ttl_Whi79_get ! purpose: compute vertically integrated streamwise mass flux of particles - !if (wnd_frc_slt > wnd_frc_thr_slt) then! if want to use fluid threshold for dust emission, uncomment this one, -dmleung 2 Dec 2021 - if (wnd_frc_slt > wnd_frc_thr_slt_it) then! if want to use impact threshold for dust emission, uncomment this one, -dmleung 2 Dec 2021 + !if (wnd_frc_slt > wnd_frc_thr_slt) then! if using Zender's scheme, use fluid threshold for dust emission equation below + if (wnd_frc_slt > wnd_frc_thr_slt_it) then! if using Leung's scheme, use impact threshold for dust emission equation !################### for Zender et al. (2003) scheme -dmleung ########################### !################ uncomment the below block if want to use Z03 scheme ################### !wnd_frc_rat = wnd_frc_thr_slt / wnd_frc_slt !flx_mss_hrz_slt_ttl = cst_slt * forc_rho(c) * (wnd_frc_slt**3.0_r8) * & - ! (1.0_r8 - wnd_frc_rat) * (1.0_r8 + wnd_frc_rat) * (1.0_r8 + wnd_frc_rat) / grav + ! (1.0_r8 - wnd_frc_rat) * (1.0_r8 + wnd_frc_rat) * (1.0_r8 + wnd_frc_rat) / grav ! Zender dust emission equation for emission flux ! the following loop originates from subr. dst_mbl ! purpose: apply land sfc and veg limitations and global tuning factor @@ -808,40 +709,27 @@ subroutine DustEmission (bounds, & !flx_mss_hrz_slt_ttl = flx_mss_hrz_slt_ttl * lnd_frc_mbl(p) * mbl_bsn_fct(c) * & ! flx_mss_fdg_fct * liqfrac - ! dmleung moved to this block + ! !dst_slt_flx_rat_ttl = 100.0_r8 * exp( log(10.0_r8) * (13.4_r8 * mss_frc_cly_vld(c) - 6.0_r8) ) !flx_mss_vrt_dst_ttl(p) = flx_mss_hrz_slt_ttl * dst_slt_flx_rat_ttl !######################################################################################## - !################### for Kok et al. (2014) scheme -dmleung ############################## - !################ uncomment the below block if want to use K14 scheme ################### - - ! if want to use fluid threshold for dust emission, uncomment this one, -dmleung 27 Nov 2021 - !flx_mss_vrt_dst_ttl(p) = dst_emiss_coeff(p) * mss_frc_cly_vld(c) * forc_rho(c) * ((wnd_frc_slt**2.0_r8 - wnd_frc_thr_slt**2.0_r8) / wnd_frc_thr_slt_std) * (wnd_frc_slt / wnd_frc_thr_slt)**frag_expt ! change forc_rho(g) to forc_rho(c) to avoid passing Nan values to the coupler -Longlei ! if want to use fluid threshold for dust emission, uncomment this one, -dml 27 Nov 2021 + !################### for Leung et al. (2022) ################################################ + !################ uncomment the below block if want to use Leung's scheme ################### - ! if want to use impact threshold for dust emission, uncomment this one, -dmleung 2 Dec 2021 - flx_mss_vrt_dst_ttl(p) = dst_emiss_coeff(p) * mss_frc_cly_vld(c) * forc_rho(c) * ((wnd_frc_slt**2.0_r8 - wnd_frc_thr_slt_it**2.0_r8) / wnd_frc_thr_slt_std) * (wnd_frc_slt / wnd_frc_thr_slt_it)**frag_expt ! if want to use impact threshold for dust emission, uncomment this one, -dml 2 Dec 2021 + flx_mss_vrt_dst_ttl(p) = dst_emiss_coeff(p) * mss_frc_cly_vld(c) * forc_rho(c) * ((wnd_frc_slt**2.0_r8 - wnd_frc_thr_slt_it**2.0_r8) / wnd_frc_thr_slt_std) * (wnd_frc_slt / wnd_frc_thr_slt_it)**frag_expt ! Leung et al. (2022) uses Kok et al. (2014) dust emission euqation for emission flux ! account for bare soil fraction, frozen soil fraction, and apply global tuning parameter (Kok et al. 2014) flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) * lnd_frc_mbl(p) * C_tune * liqfrac !######################################################################################## end if - ! the following comes from subr. flx_mss_vrt_dst_ttl_MaB95_get - ! purpose: diagnose total vertical mass flux of dust from vertically - ! integrated streamwise mass flux - - !dst_slt_flx_rat_ttl = 100.0_r8 * exp( log(10.0_r8) * (13.4_r8 * mss_frc_cly_vld(c) - 6.0_r8) ) ! dmleung commented and moved to the previous block - !flx_mss_vrt_dst_ttl(p) = flx_mss_hrz_slt_ttl * dst_slt_flx_rat_ttl - - !############## added by dmleung 2 Dec 2021 ############################################# - ! subsection for intermittency factor calculation - ! need to use with impact threshold and cannot be used with fluid threshold - ! Danny M. Leung, 24 Jun 2019, readded into CLM5 by dmleung 2 Dec 2021 - ! 2 Dec 2021 note: assume no buoyancy contribution to the wind fluctuation (u_sd_slt), so no obul(p) is needed. It is shown to be important for the wind fluctuations contribute little to the intermittency factor. We might add this back in the future revisions. + !############## Danny M. Leung added the intermittency calculation ################################# + ! subsection for intermittency factor calculation (only used by Leung's scheme, not Zender's scheme) + ! 2 Dec 2021: assume no buoyancy contribution to the wind fluctuation (u_sd_slt), so no obul(p) is needed. It is shown to be important for the wind fluctuations contribute little to the intermittency factor. We might add this back in the future revisions. ! mean lowpass-filtered wind speed at 0.1 m saltation height (assuming aerodynamic roughness length = 1e-4 m globally for ease; also assuming neutral condition) - u_mean_slt(p) = (wnd_frc_slt/k) * log(0.1_r8 / 1e-4_r8) + u_mean_slt(p) = (wnd_frc_slt/k) * log(0.1_r8 / 1e-4_r8) ! translating from ustar (velocity scale) to actual wind ! sd of lowpass-filtered wind speed !if (obul(p)==0) then @@ -851,15 +739,15 @@ subroutine DustEmission (bounds, & ! zetaobu = 1000_r8 / obul(p) ! For now zii is a constant of 1000 m in CLM -dml, 24 Aug 2021 !end if !stblty(p) = zetaobu ! zetaobu get outputted as the Obukhov stability parameter - stblty(p) = 0 ! -dmleung 2 Dec 2021: use 0 for now, assuming no buoyancy contribution. Might uncomment the above lines in future revisions. - if ((12_r8 - 0.5_r8 * stblty(p)) .GE. 0.001_r8) then + stblty(p) = 0 ! -dmleung 2 Dec 2021: use stability = 0 for now, assuming no buoyancy contribution. Might uncomment the above lines in future revisions. + if ((12_r8 - 0.5_r8 * stblty(p)) .GE. 0.001_r8) then ! should have used 0 theoretically; used 0.001 here to avoid undefined values u_sd_slt(p) = wnd_frc_slt * (12_r8 - 0.5_r8 * stblty(p))**0.333_r8 else u_sd_slt(p) = 0.001_r8 ! should have used 0 theoretically; used 0.001 here to avoid undefined values end if ! threshold velocities - ! Here wnd_frc_thr_slt is the fluid threshold; wnd_frc_thr_dry(p) is the dry fluid threshold; B_it*wnd_frc_thr_dry(p) is the impact threshold, -dml, 1 Mar 2021 + ! Here wnd_frc_thr_slt is the fluid threshold; wnd_frc_thr_dry(p) is the dry fluid threshold; B_it*wnd_frc_thr_dry(p) is the impact threshold ! fluid threshold wind at 0.1 m saltation height u_fld_thr(p) = (wnd_frc_thr_slt/k) * log(0.1_r8 / 1e-4_r8) ! impact threshold wind at 0.1 m saltation height @@ -877,13 +765,13 @@ subroutine DustEmission (bounds, & intrmtncy_fct(p) = 1_r8 - prb_crs_fld_thr(p) + thr_crs_rate(p) * (prb_crs_fld_thr(p) - prb_crs_impct_thr(p)) ! multiply dust emission flux by intermittency factor - if (intrmtncy_fct(p) /= intrmtncy_fct(p)) then ! if intrmtncy_fct(p) is not NaN then multiply by intermittency factor; this statement is needed because dust emission flx_mss_vrt_dst_ttl(p) has to be non NaN (at least zero) to be outputted, dmleung 9 Jun 2021 - flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) ! -dmleung + if (intrmtncy_fct(p) /= intrmtncy_fct(p)) then ! if intrmtncy_fct(p) is not NaN then multiply by intermittency factor; this statement is needed because dust emission flx_mss_vrt_dst_ttl(p) has to be non NaN (at least zero) to be outputted + flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) else - flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) * intrmtncy_fct(p) ! multiply dust flux by intermittency -dmleung + flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) * intrmtncy_fct(p) ! multiply dust flux by intermittency end if - !############### end my intermittency subsection here -dmleung ######################################## + !############ end the intermittency subsection here; only use for Leung's scheme ########################## end if ! lnd_frc_mbl > 0.0 diff --git a/src/biogeophys/SoilStateInitTimeConstMod.F90 b/src/biogeophys/SoilStateInitTimeConstMod.F90 index fc96a878bc..0e31cc2c53 100644 --- a/src/biogeophys/SoilStateInitTimeConstMod.F90 +++ b/src/biogeophys/SoilStateInitTimeConstMod.F90 @@ -703,8 +703,8 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) do c = begc,endc g = col%gridcell(c) - soilstate_inst%gwc_thr_col(c) = 0.01_r8*(0.17_r8*clay3d(g,1) + 0.0014_r8*clay3d(g,1)*clay3d(g,1)) !Fecan et al. (1999) -jfk, dmleung coded 27 Nov 2021 for CLM clay fraction - !soilstate_inst%gwc_thr_col(c) = 0.17_r8 + 0.14_r8 * clay3d(g,1) * 0.01_r8 -dmleung commented 27 Nov 2021 + soilstate_inst%gwc_thr_col(c) = 0.01_r8*(0.17_r8*clay3d(g,1) + 0.0014_r8*clay3d(g,1)*clay3d(g,1)) ! Danny M. Leung modified the equation of soil moisture effect for dust emissions using a scale factor of 1. 0.01 is to convert from % to fraction. + !soilstate_inst%gwc_thr_col(c) = 0.17_r8 + 0.14_r8 * clay3d(g,1) * 0.01_r8 ! This was the original equation with 1 / (clay fraction) being the scaling factor. soilstate_inst%mss_frc_cly_vld_col(c) = min(clay3d(g,1) * 0.01_r8, 0.20_r8) end do diff --git a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 index 19ab3cb573..cef34682c9 100644 --- a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 +++ b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 @@ -1,11 +1,10 @@ module PrigentRoughnessStreamType -!dmleung modified based on ch4FInundatedStreamType on 17 Nov 2022 -#include "shr_assert.h" ! What is this? In many modules but not dust module +#include "shr_assert.h" !----------------------------------------------------------------------- ! !DESCRIPTION: - ! Contains methods for reading in the Prigent et al. (1997) roughness length streams file. dmleung 22 Nov 2022 - ! + ! Contains methods for reading in the Prigent et al. (1997) roughness length streams file + ! Created by Danny M. Leung 22 Nov 2022 ! !USES use ESMF use dshr_strdata_mod , only : shr_strdata_type @@ -83,11 +82,11 @@ subroutine Init(this, bounds, NLFilename) character(len=*), parameter :: stream_name = 'prigentroughness' !----------------------------------------------------------------------- - !if ( finundation_mtd /= finundation_mtd_h2osfc )then ! how should I change this? comment out for now + !if ( finundation_mtd /= finundation_mtd_h2osfc )then call this%InitAllocate( bounds ) call control%ReadNML( bounds, NLFileName ) - if ( this%useStreams() )then ! is this a namelist input and is it set in namelist default + if ( this%useStreams() )then allocate(stream_varnames(1)) stream_varnames = (/"Z0a"/) ! varname in cdf5_Z0a_Prigent-Globe-025x025-09262022.nc, in centimeter @@ -97,7 +96,7 @@ subroutine Init(this, bounds, NLFilename) end if ! Initialize the cdeps data type sdat_rghn - call shr_strdata_init_from_inline(sdat_rghn, & ! what is this function and where does it come from? + call shr_strdata_init_from_inline(sdat_rghn, & my_task = iam, & logunit = iulog, & compname = 'LND', & @@ -132,7 +131,7 @@ subroutine Init(this, bounds, NLFilename) sec = 0 mcdate = year*10000 + mon*100 + day - call shr_strdata_advance(sdat_rghn, ymd=mcdate, tod=sec, logunit=iulog, istr='prigentrghn', rc=rc) ! what is istr and do I need to change elsewhere because the change of istr here + call shr_strdata_advance(sdat_rghn, ymd=mcdate, tod=sec, logunit=iulog, istr='prigentrghn', rc=rc) if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then call ESMF_Finalize(endflag=ESMF_END_ABORT) end if @@ -146,7 +145,7 @@ subroutine Init(this, bounds, NLFilename) if (trim(stream_varnames(n)) == 'Z0a') then ig = 0 do g = bounds%begg,bounds%endg - ig = ig+1 ! not sure why +1 is needed but it's okay + ig = ig+1 this%prigent_rghn(g) = dataptr1d(ig) end do @@ -154,7 +153,7 @@ subroutine Init(this, bounds, NLFilename) end do end if - !end if !comment out for now + !end if !commented out end subroutine Init @@ -172,7 +171,7 @@ logical function UseStreams(this) ! ! !LOCAL VARIABLES: if ( trim(control%stream_fldFileName_prigentroughness) == '' )then - UseStreams = .false. ! dmleung: this won't happen and UseStreams will always be true + UseStreams = .false. ! this won't happen and UseStreams will always be true else UseStreams = .true. end if @@ -193,11 +192,9 @@ subroutine InitAllocate(this, bounds) type(bounds_type), intent(in) :: bounds ! ! !LOCAL VARIABLES: - !integer :: begc, endc integer :: begg, endg !--------------------------------------------------------------------- - !begc = bounds%begc; endc = bounds%endc begg = bounds%begg; endg = bounds%endg allocate(this%prigent_rghn (begg:endg)) ; this%prigent_rghn (:) = nan @@ -205,13 +202,10 @@ subroutine InitAllocate(this, bounds) end subroutine InitAllocate !============================================================================== - !subroutine CalcFinundated(this, bounds, num_soilc, filter_soilc, soilhydrology_inst, & - ! waterdiagnosticbulk_inst, qflx_surf_lag_col, finundated ) - !subroutine CalcDragPartition(this, bounds, num_nolakep, filter_nolakep, dpfct_rock) subroutine CalcDragPartition(this, bounds, dpfct_rock) ! ! !DESCRIPTION: - ! Commented below by dmleung 31 Dec 2022 + ! Commented below by Danny M. Leung 31 Dec 2022 ! Calculate the drag partition effect of friction velocity due to surface roughness following ! Leung et al. (2022). This module is used in the dust emission module DUSTMod.F90 for ! calculating drag partitioning. The drag partition equation comes from Marticorena and @@ -222,9 +216,7 @@ subroutine CalcDragPartition(this, bounds, dpfct_rock) ! subroutine is used in the InitCold subroutine of DUSTMod.F90. ! ! !USES: - !use ColumnType , only : col use PatchType , only : patch - !USES dmleung added 31 Dec 2022 use landunit_varcon , only : istdlak use LandunitType , only : lun ! @@ -235,7 +227,6 @@ subroutine CalcDragPartition(this, bounds, dpfct_rock) real(r8) , intent(inout) :: dpfct_rock(bounds%begp:) ! [fraction] rock drag partition factor (roughness effect) ! ! !LOCAL VARIABLES: - !integer :: g, c, fc ! Indices integer :: g, p, fp, l ! Indices real(r8) :: z0s ! smooth roughness length (m) @@ -244,17 +235,11 @@ subroutine CalcDragPartition(this, bounds, dpfct_rock) real(r8), parameter :: X = 10_r8 ! [m] distance downwind of the roughness element (rock). Assume estimating roughness effect at a distance of 10 m following Leung et al. (2022) !--------------------------------------------------------------------- - SHR_ASSERT_ALL_FL((ubound(dpfct_rock) == (/bounds%endp/)), sourcefile, __LINE__) !what's the use of this - - !associate( & - !z => col%z & ! Input: [real(r8) (:,:) ] layer depth (m) (-nlevsno+1:nlevsoi) - !) + SHR_ASSERT_ALL_FL((ubound(dpfct_rock) == (/bounds%endp/)), sourcefile, __LINE__) ! dmleung: this loop calculates the drag partition effect (or roughness effect) of rocks. We save the drag partition factor as a patch level quantity. z0s = 2_r8 * D_p / 30_r8 ! equation from Frank M. White (2006). Here we assume soil medium size is a global constant, and so is smooth roughness length. - !do fp = 1,num_nolakep - !p = filter_nolakep(fp) do p = bounds%begp,bounds%endp g = patch%gridcell(p) l = patch%landunit(p) @@ -263,8 +248,6 @@ subroutine CalcDragPartition(this, bounds, dpfct_rock) end if end do - !end associate - end subroutine CalcDragPartition !============================================================================== @@ -289,7 +272,7 @@ subroutine ReadNML(this, bounds, NLFilename) character(len=CL) :: stream_fldFileName_prigentroughness = ' ' character(len=CL) :: stream_meshfile_prigentroughness = ' ' character(len=CL) :: prigentroughnessmapalgo = 'bilinear' - character(len=*), parameter :: namelist_name = 'prigentroughness' ! MUST agree with group name in namelist definition to read. dmleung commented + character(len=*), parameter :: namelist_name = 'prigentroughness' ! MUST agree with group name in namelist definition to read. character(len=*), parameter :: subName = "('prigentroughness::ReadNML')" !----------------------------------------------------------------------- diff --git a/src/main/clm_instMod.F90 b/src/main/clm_instMod.F90 index 13390d3417..099959581f 100644 --- a/src/main/clm_instMod.F90 +++ b/src/main/clm_instMod.F90 @@ -340,7 +340,7 @@ subroutine clm_instInit(bounds) call surfrad_inst%Init(bounds) - call dust_inst%Init(bounds, NLFilename) ! dmleung added NLFilename 31 Dec 2022 + call dust_inst%Init(bounds, NLFilename) allocate(scf_method, source = CreateAndInitSnowCoverFraction( & snow_cover_fraction_method = snow_cover_fraction_method, & From 62b7bcd6f8f8d72b209accb1bff0daff992826c3 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Jun 2023 16:52:23 -0600 Subject: [PATCH 020/406] Truncate threshold crossing rate to zero, if the term going into the exp is going to be large and thus end up with a very small term --- src/biogeochem/DUSTMod.F90 | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index f1de13902a..496553dd24 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -432,14 +432,14 @@ subroutine DustEmission (bounds, & ! real(r8), parameter :: cst_slt = 2.61_r8 ! [frc] Saltation constant real(r8), parameter :: flx_mss_fdg_fct = 5.0e-4_r8 ! [frc] Empir. mass flx tuning eflx_lh_vegt - !real(r8), parameter :: vai_mbl_thr = 0.3_r8 ! [m2 m-2] VAI threshold quenching dust mobilization + !real(r8), parameter :: vai_mbl_thr = 0.3_r8 ! [m2 m-2] VAI threshold quenching dust mobilization !####### added by dmleung 27 Nov 2021 ########################################################################### character(len=*),parameter :: subname = 'DUSTEmission' real(r8), parameter :: vai_mbl_thr = 1.0_r8 ! [m2 m-2] new VAI threshold; dmleung suggests 1 or 0.5, and the default 0.3 seems a bit too small -dmleung 27 Nov 2021 real(r8), parameter :: Cd0 = 4.4e-5_r8 ! [dimless] proportionality constant in calculation of dust emission coefficient -jfk real(r8), parameter :: Ca = 2.7_r8 ! [dimless] proportionality constant in scaling of dust emission exponent -jfk real(r8), parameter :: Ce = 2.0_r8 ! [dimless] proportionality constant scaling exponential dependence of dust emission coefficient on standardized soil threshold friction speed -jfk - real(r8), parameter :: C_tune = 0.05_r8 ! [dimless] global tuning constant for vertical dust flux; set to produce ~same global dust flux in control sim (I_2000) as old parameterization -jfk + real(r8), parameter :: C_tune = 0.05_r8 ! [dimless] global tuning constant for vertical dust flux; set to produce ~same global dust flux in control sim (I_2000) as old parameterization -jfk real(r8), parameter :: wnd_frc_thr_slt_std_min = 0.16_r8 ! [m/s] minimum standardized soil threshold friction speed -jfk real(r8), parameter :: forc_rho_std = 1.2250_r8 ! [kg/m3] density of air at standard pressure (101325) and temperature (293 K) -jfk real(r8), parameter :: dns_slt = 2650.0_r8 ! [kg m-3] Density of optimal saltation particles, dml 23 May 2020 @@ -448,7 +448,9 @@ subroutine DustEmission (bounds, & real(r8), parameter :: k = 0.4_r8 ! [dimless] von Karman constant -dml !####### added by dmleung 2 Dec 2021 for Okin (2008) drag partition for plants ########################################################## real(r8), parameter :: f_0 = 0.32_r8 ! [dimless] SSR in the immediate lee of a plant, dimensionless - real(r8), parameter :: c_e = 4.8_r8 ! [dimless] e-folding distance velocity recovery, dimensionless + real(r8), parameter :: c_e = 4.8_r8 ! [dimless] e-folding distance velocity recovery, dimensionless + real(r8) :: numer ! Numerator term for threshold crossing rate + real(r8) :: denom ! Denominator term for threshold crossing rate !################################################################################################################ !################################################################################################################ !------------------------------------------------------------------------ @@ -866,7 +868,14 @@ subroutine DustEmission (bounds, & u_impct_thr(p) = (wnd_frc_thr_slt_it/k) * log(0.1_r8 / 1e-4_r8) ! to avoid model error ! threshold crossing rate - thr_crs_rate(p) = (exp((u_fld_thr(p)**2_r8 - u_impct_thr(p)**2_r8 - 2_r8 * u_mean_slt(p) * (u_fld_thr(p) - u_impct_thr(p))) / (2_r8 * u_sd_slt(p)**2_r8)) + 1_r8)**(-1_r8) + numer = (u_fld_thr(p)**2_r8 - u_impct_thr(p)**2_r8 - 2_r8 * u_mean_slt(p) * (u_fld_thr(p) - u_impct_thr(p))) + denom = (2_r8 * u_sd_slt(p)**2_r8) + ! Truncate to zero if the expression inside exp is becoming too large + if ( numer/denom < 30._r8 )then + thr_crs_rate(p) = (exp((u_fld_thr(p)**2_r8 - u_impct_thr(p)**2_r8 - 2_r8 * u_mean_slt(p) * (u_fld_thr(p) - u_impct_thr(p))) / (2_r8 * u_sd_slt(p)**2_r8)) + 1_r8)**(-1_r8) + else + thr_crs_rate(p) = 0.0_r8 + end if ! probability that lowpass-filtered wind speed does not exceed u_ft prb_crs_fld_thr(p) = 0.5_r8 * (1_r8 + erf((u_fld_thr(p) - u_mean_slt(p)) / (1.414_r8 * u_sd_slt(p)))) From 9208fde389712a1fc08a8f73760a781f2ff1cd02 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 26 Jan 2024 00:46:45 -0700 Subject: [PATCH 021/406] Update CAM and CICE6 to cesm2_3_beta16 versions --- Externals.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Externals.cfg b/Externals.cfg index ff0e28f4cd..d75b4683a6 100644 --- a/Externals.cfg +++ b/Externals.cfg @@ -41,7 +41,7 @@ local_path = components/cice5 required = True [cice6] -tag = cesm_cice6_2_0_22 +tag = cesm_cice6_4_1_10 protocol = git repo_url = https://github.com/ESCOMP/CESM_CICE local_path = components/cice @@ -49,7 +49,7 @@ externals = Externals.cfg required = True [cam6] -tag = cam6_3_058 +tag = cam6_3_133 protocol = git repo_url = https://github.com/ESCOMP/CAM local_path = components/cam From 896713bc0971108231cafb8dc0d392fce5e8131c Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 26 Jan 2024 00:47:04 -0700 Subject: [PATCH 022/406] Squashed 'manage_externals/' changes from 7b6d92ef6..0f884bfec 0f884bfec Merge pull request #205 from jedwards4b/sunset_svn_git_access 82a5edf79 merge in billsacks:svn_testing_no_github 17532c160 Use a local svn repo for testing 9c904341a different method to determine if in tests 539952ebd remove debug print statement cc5434fa7 fix submodule testing 1d7f28840 remove broken tests 04e94a519 provide a meaningful error message 38bcc0a8c Merge pull request #201 from jedwards4b/partial_match b4466a5aa remove debug print statement c3cf3ec35 fix issue with partial branch match git-subtree-dir: manage_externals git-subtree-split: 0f884bfec8e43d0c02261de858d6ec3f6d855e51 --- manic/repository_git.py | 18 +- manic/repository_svn.py | 3 + test/repos/README.md | 4 +- test/repos/simple-ext.svn/README.txt | 5 + test/repos/simple-ext.svn/conf/authz | 32 +++ test/repos/simple-ext.svn/conf/hooks-env.tmpl | 19 ++ test/repos/simple-ext.svn/conf/passwd | 8 + test/repos/simple-ext.svn/conf/svnserve.conf | 81 +++++++ test/repos/simple-ext.svn/db/current | 1 + test/repos/simple-ext.svn/db/format | 3 + test/repos/simple-ext.svn/db/fs-type | 1 + test/repos/simple-ext.svn/db/fsfs.conf | 200 ++++++++++++++++++ test/repos/simple-ext.svn/db/min-unpacked-rev | 1 + test/repos/simple-ext.svn/db/rep-cache.db | Bin 0 -> 8192 bytes .../simple-ext.svn/db/rep-cache.db-journal | 0 test/repos/simple-ext.svn/db/revprops/0/0 | 5 + test/repos/simple-ext.svn/db/revprops/0/1 | 13 ++ test/repos/simple-ext.svn/db/revprops/0/2 | 13 ++ test/repos/simple-ext.svn/db/revprops/0/3 | 13 ++ test/repos/simple-ext.svn/db/revs/0/0 | Bin 0 -> 253 bytes test/repos/simple-ext.svn/db/revs/0/1 | Bin 0 -> 725 bytes test/repos/simple-ext.svn/db/revs/0/2 | Bin 0 -> 816 bytes test/repos/simple-ext.svn/db/revs/0/3 | Bin 0 -> 769 bytes test/repos/simple-ext.svn/db/txn-current | 1 + test/repos/simple-ext.svn/db/txn-current-lock | 0 test/repos/simple-ext.svn/db/uuid | 2 + test/repos/simple-ext.svn/db/write-lock | 0 test/repos/simple-ext.svn/format | 1 + .../simple-ext.svn/hooks/post-commit.tmpl | 62 ++++++ .../repos/simple-ext.svn/hooks/post-lock.tmpl | 64 ++++++ .../hooks/post-revprop-change.tmpl | 69 ++++++ .../simple-ext.svn/hooks/post-unlock.tmpl | 61 ++++++ .../simple-ext.svn/hooks/pre-commit.tmpl | 91 ++++++++ test/repos/simple-ext.svn/hooks/pre-lock.tmpl | 95 +++++++++ .../hooks/pre-revprop-change.tmpl | 79 +++++++ .../simple-ext.svn/hooks/pre-unlock.tmpl | 87 ++++++++ .../simple-ext.svn/hooks/start-commit.tmpl | 81 +++++++ test/repos/simple-ext.svn/locks/db-logs.lock | 3 + test/repos/simple-ext.svn/locks/db.lock | 3 + test/test_sys_checkout.py | 51 ++--- 40 files changed, 1126 insertions(+), 44 deletions(-) create mode 100644 test/repos/simple-ext.svn/README.txt create mode 100644 test/repos/simple-ext.svn/conf/authz create mode 100644 test/repos/simple-ext.svn/conf/hooks-env.tmpl create mode 100644 test/repos/simple-ext.svn/conf/passwd create mode 100644 test/repos/simple-ext.svn/conf/svnserve.conf create mode 100644 test/repos/simple-ext.svn/db/current create mode 100644 test/repos/simple-ext.svn/db/format create mode 100644 test/repos/simple-ext.svn/db/fs-type create mode 100644 test/repos/simple-ext.svn/db/fsfs.conf create mode 100644 test/repos/simple-ext.svn/db/min-unpacked-rev create mode 100644 test/repos/simple-ext.svn/db/rep-cache.db create mode 100644 test/repos/simple-ext.svn/db/rep-cache.db-journal create mode 100644 test/repos/simple-ext.svn/db/revprops/0/0 create mode 100644 test/repos/simple-ext.svn/db/revprops/0/1 create mode 100644 test/repos/simple-ext.svn/db/revprops/0/2 create mode 100644 test/repos/simple-ext.svn/db/revprops/0/3 create mode 100644 test/repos/simple-ext.svn/db/revs/0/0 create mode 100644 test/repos/simple-ext.svn/db/revs/0/1 create mode 100644 test/repos/simple-ext.svn/db/revs/0/2 create mode 100644 test/repos/simple-ext.svn/db/revs/0/3 create mode 100644 test/repos/simple-ext.svn/db/txn-current create mode 100644 test/repos/simple-ext.svn/db/txn-current-lock create mode 100644 test/repos/simple-ext.svn/db/uuid create mode 100644 test/repos/simple-ext.svn/db/write-lock create mode 100644 test/repos/simple-ext.svn/format create mode 100755 test/repos/simple-ext.svn/hooks/post-commit.tmpl create mode 100755 test/repos/simple-ext.svn/hooks/post-lock.tmpl create mode 100755 test/repos/simple-ext.svn/hooks/post-revprop-change.tmpl create mode 100755 test/repos/simple-ext.svn/hooks/post-unlock.tmpl create mode 100755 test/repos/simple-ext.svn/hooks/pre-commit.tmpl create mode 100755 test/repos/simple-ext.svn/hooks/pre-lock.tmpl create mode 100755 test/repos/simple-ext.svn/hooks/pre-revprop-change.tmpl create mode 100755 test/repos/simple-ext.svn/hooks/pre-unlock.tmpl create mode 100755 test/repos/simple-ext.svn/hooks/start-commit.tmpl create mode 100644 test/repos/simple-ext.svn/locks/db-logs.lock create mode 100644 test/repos/simple-ext.svn/locks/db.lock mode change 100644 => 100755 test/test_sys_checkout.py diff --git a/manic/repository_git.py b/manic/repository_git.py index adc666cc57..aab1a468a8 100644 --- a/manic/repository_git.py +++ b/manic/repository_git.py @@ -7,6 +7,7 @@ import copy import os +import sys from .global_constants import EMPTY_STR, LOCAL_PATH_INDICATOR from .global_constants import VERBOSITY_VERBOSE @@ -380,7 +381,6 @@ def _check_for_valid_ref(self, ref, remote_name, dirname): is_tag = self._ref_is_tag(ref, dirname) is_branch = self._ref_is_branch(ref, remote_name, dirname) is_hash = self._ref_is_hash(ref, dirname) - is_valid = is_tag or is_branch or is_hash if not is_valid: msg = ('In repo "{0}": reference "{1}" does not appear to be a ' @@ -710,7 +710,10 @@ def _git_lsremote_branch(ref, remote_name, dirname): cmd = ('git -C {dirname} ls-remote --exit-code --heads ' '{remote_name} {ref}').format( dirname=dirname, remote_name=remote_name, ref=ref).split() - status = execute_subprocess(cmd, status_to_caller=True) + status, output = execute_subprocess(cmd, status_to_caller=True, output_to_caller=True) + if not status and not f"refs/heads/{ref}" in output: + # In this case the ref is contained in the branch name but is not the complete branch name + return -1 return status @staticmethod @@ -837,12 +840,19 @@ def _git_update_submodules(verbosity, dirname): """Run git submodule update for the side effect of updating this repo's submodules. """ + # due to https://vielmetti.typepad.com/logbook/2022/10/git-security-fixes-lead-to-fatal-transport-file-not-allowed-error-in-ci-systems-cve-2022-39253.html + # submodules from file doesn't work without overriding the protocol, this is done + # for testing submodule support but should not be done in practice + file_protocol = "" + if 'unittest' in sys.modules.keys(): + file_protocol = "-c protocol.file.allow=always" + # First, verify that we have a .gitmodules file if os.path.exists( os.path.join(dirname, ExternalsDescription.GIT_SUBMODULES_FILENAME)): - cmd = ('git -C {dirname} submodule update --init --recursive' - .format(dirname=dirname)).split() + cmd = ('git {file_protocol} -C {dirname} submodule update --init --recursive' + .format(file_protocol=file_protocol, dirname=dirname)).split() if verbosity >= VERBOSITY_VERBOSE: printlog(' {0}'.format(' '.join(cmd))) diff --git a/manic/repository_svn.py b/manic/repository_svn.py index 922855d34e..32a71184b4 100644 --- a/manic/repository_svn.py +++ b/manic/repository_svn.py @@ -42,6 +42,9 @@ def __init__(self, component_name, repo, ignore_ancestry=False): Parse repo (a XML element). """ Repository.__init__(self, component_name, repo) + if 'github.com' in self._url: + msg = "SVN access to github.com is no longer supported" + fatal_error(msg) self._ignore_ancestry = ignore_ancestry if self._url.endswith('/'): # there is already a '/' separator in the URL; no need to add another diff --git a/test/repos/README.md b/test/repos/README.md index 8a3502c35f..026b684ea3 100644 --- a/test/repos/README.md +++ b/test/repos/README.md @@ -1,6 +1,6 @@ -Git repositories for testing git-related behavior. For usage and terminology notes, see test/test_sys_checkout.py. +Git and svn repositories for testing git and svn-related behavior. For usage and terminology notes, see test/test_sys_checkout.py. -To list files and view file contents at HEAD: +For git repos: To list files and view file contents at HEAD: ``` cd git ls-tree --full-tree -r --name-only HEAD diff --git a/test/repos/simple-ext.svn/README.txt b/test/repos/simple-ext.svn/README.txt new file mode 100644 index 0000000000..9935818a1b --- /dev/null +++ b/test/repos/simple-ext.svn/README.txt @@ -0,0 +1,5 @@ +This is a Subversion repository; use the 'svnadmin' and 'svnlook' +tools to examine it. Do not add, delete, or modify files here +unless you know how to avoid corrupting the repository. + +Visit http://subversion.apache.org/ for more information. diff --git a/test/repos/simple-ext.svn/conf/authz b/test/repos/simple-ext.svn/conf/authz new file mode 100644 index 0000000000..0b9a41074e --- /dev/null +++ b/test/repos/simple-ext.svn/conf/authz @@ -0,0 +1,32 @@ +### This file is an example authorization file for svnserve. +### Its format is identical to that of mod_authz_svn authorization +### files. +### As shown below each section defines authorizations for the path and +### (optional) repository specified by the section name. +### The authorizations follow. An authorization line can refer to: +### - a single user, +### - a group of users defined in a special [groups] section, +### - an alias defined in a special [aliases] section, +### - all authenticated users, using the '$authenticated' token, +### - only anonymous users, using the '$anonymous' token, +### - anyone, using the '*' wildcard. +### +### A match can be inverted by prefixing the rule with '~'. Rules can +### grant read ('r') access, read-write ('rw') access, or no access +### (''). + +[aliases] +# joe = /C=XZ/ST=Dessert/L=Snake City/O=Snake Oil, Ltd./OU=Research Institute/CN=Joe Average + +[groups] +# harry_and_sally = harry,sally +# harry_sally_and_joe = harry,sally,&joe + +# [/foo/bar] +# harry = rw +# &joe = r +# * = + +# [repository:/baz/fuz] +# @harry_and_sally = rw +# * = r diff --git a/test/repos/simple-ext.svn/conf/hooks-env.tmpl b/test/repos/simple-ext.svn/conf/hooks-env.tmpl new file mode 100644 index 0000000000..ee965c316c --- /dev/null +++ b/test/repos/simple-ext.svn/conf/hooks-env.tmpl @@ -0,0 +1,19 @@ +### This file is an example hook script environment configuration file. +### Hook scripts run in an empty environment by default. +### As shown below each section defines environment variables for a +### particular hook script. The [default] section defines environment +### variables for all hook scripts, unless overridden by a hook-specific +### section. + +### This example configures a UTF-8 locale for all hook scripts, so that +### special characters, such as umlauts, may be printed to stderr. +### If UTF-8 is used with a mod_dav_svn server, the SVNUseUTF8 option must +### also be set to 'yes' in httpd.conf. +### With svnserve, the LANG environment variable of the svnserve process +### must be set to the same value as given here. +[default] +LANG = en_US.UTF-8 + +### This sets the PATH environment variable for the pre-commit hook. +[pre-commit] +PATH = /usr/local/bin:/usr/bin:/usr/sbin diff --git a/test/repos/simple-ext.svn/conf/passwd b/test/repos/simple-ext.svn/conf/passwd new file mode 100644 index 0000000000..ecaa08dcec --- /dev/null +++ b/test/repos/simple-ext.svn/conf/passwd @@ -0,0 +1,8 @@ +### This file is an example password file for svnserve. +### Its format is similar to that of svnserve.conf. As shown in the +### example below it contains one section labelled [users]. +### The name and password for each user follow, one account per line. + +[users] +# harry = harryssecret +# sally = sallyssecret diff --git a/test/repos/simple-ext.svn/conf/svnserve.conf b/test/repos/simple-ext.svn/conf/svnserve.conf new file mode 100644 index 0000000000..6cefc17b3e --- /dev/null +++ b/test/repos/simple-ext.svn/conf/svnserve.conf @@ -0,0 +1,81 @@ +### This file controls the configuration of the svnserve daemon, if you +### use it to allow access to this repository. (If you only allow +### access through http: and/or file: URLs, then this file is +### irrelevant.) + +### Visit http://subversion.apache.org/ for more information. + +[general] +### The anon-access and auth-access options control access to the +### repository for unauthenticated (a.k.a. anonymous) users and +### authenticated users, respectively. +### Valid values are "write", "read", and "none". +### Setting the value to "none" prohibits both reading and writing; +### "read" allows read-only access, and "write" allows complete +### read/write access to the repository. +### The sample settings below are the defaults and specify that anonymous +### users have read-only access to the repository, while authenticated +### users have read and write access to the repository. +# anon-access = read +# auth-access = write +### The password-db option controls the location of the password +### database file. Unless you specify a path starting with a /, +### the file's location is relative to the directory containing +### this configuration file. +### If SASL is enabled (see below), this file will NOT be used. +### Uncomment the line below to use the default password file. +# password-db = passwd +### The authz-db option controls the location of the authorization +### rules for path-based access control. Unless you specify a path +### starting with a /, the file's location is relative to the +### directory containing this file. The specified path may be a +### repository relative URL (^/) or an absolute file:// URL to a text +### file in a Subversion repository. If you don't specify an authz-db, +### no path-based access control is done. +### Uncomment the line below to use the default authorization file. +# authz-db = authz +### The groups-db option controls the location of the file with the +### group definitions and allows maintaining groups separately from the +### authorization rules. The groups-db file is of the same format as the +### authz-db file and should contain a single [groups] section with the +### group definitions. If the option is enabled, the authz-db file cannot +### contain a [groups] section. Unless you specify a path starting with +### a /, the file's location is relative to the directory containing this +### file. The specified path may be a repository relative URL (^/) or an +### absolute file:// URL to a text file in a Subversion repository. +### This option is not being used by default. +# groups-db = groups +### This option specifies the authentication realm of the repository. +### If two repositories have the same authentication realm, they should +### have the same password database, and vice versa. The default realm +### is repository's uuid. +# realm = My First Repository +### The force-username-case option causes svnserve to case-normalize +### usernames before comparing them against the authorization rules in the +### authz-db file configured above. Valid values are "upper" (to upper- +### case the usernames), "lower" (to lowercase the usernames), and +### "none" (to compare usernames as-is without case conversion, which +### is the default behavior). +# force-username-case = none +### The hooks-env options specifies a path to the hook script environment +### configuration file. This option overrides the per-repository default +### and can be used to configure the hook script environment for multiple +### repositories in a single file, if an absolute path is specified. +### Unless you specify an absolute path, the file's location is relative +### to the directory containing this file. +# hooks-env = hooks-env + +[sasl] +### This option specifies whether you want to use the Cyrus SASL +### library for authentication. Default is false. +### Enabling this option requires svnserve to have been built with Cyrus +### SASL support; to check, run 'svnserve --version' and look for a line +### reading 'Cyrus SASL authentication is available.' +# use-sasl = true +### These options specify the desired strength of the security layer +### that you want SASL to provide. 0 means no encryption, 1 means +### integrity-checking only, values larger than 1 are correlated +### to the effective key length for encryption (e.g. 128 means 128-bit +### encryption). The values below are the defaults. +# min-encryption = 0 +# max-encryption = 256 diff --git a/test/repos/simple-ext.svn/db/current b/test/repos/simple-ext.svn/db/current new file mode 100644 index 0000000000..00750edc07 --- /dev/null +++ b/test/repos/simple-ext.svn/db/current @@ -0,0 +1 @@ +3 diff --git a/test/repos/simple-ext.svn/db/format b/test/repos/simple-ext.svn/db/format new file mode 100644 index 0000000000..5dd0c22198 --- /dev/null +++ b/test/repos/simple-ext.svn/db/format @@ -0,0 +1,3 @@ +8 +layout sharded 1000 +addressing logical diff --git a/test/repos/simple-ext.svn/db/fs-type b/test/repos/simple-ext.svn/db/fs-type new file mode 100644 index 0000000000..4fdd95313f --- /dev/null +++ b/test/repos/simple-ext.svn/db/fs-type @@ -0,0 +1 @@ +fsfs diff --git a/test/repos/simple-ext.svn/db/fsfs.conf b/test/repos/simple-ext.svn/db/fsfs.conf new file mode 100644 index 0000000000..ac6877a727 --- /dev/null +++ b/test/repos/simple-ext.svn/db/fsfs.conf @@ -0,0 +1,200 @@ +### This file controls the configuration of the FSFS filesystem. + +[memcached-servers] +### These options name memcached servers used to cache internal FSFS +### data. See http://www.danga.com/memcached/ for more information on +### memcached. To use memcached with FSFS, run one or more memcached +### servers, and specify each of them as an option like so: +# first-server = 127.0.0.1:11211 +# remote-memcached = mymemcached.corp.example.com:11212 +### The option name is ignored; the value is of the form HOST:PORT. +### memcached servers can be shared between multiple repositories; +### however, if you do this, you *must* ensure that repositories have +### distinct UUIDs and paths, or else cached data from one repository +### might be used by another accidentally. Note also that memcached has +### no authentication for reads or writes, so you must ensure that your +### memcached servers are only accessible by trusted users. + +[caches] +### When a cache-related error occurs, normally Subversion ignores it +### and continues, logging an error if the server is appropriately +### configured (and ignoring it with file:// access). To make +### Subversion never ignore cache errors, uncomment this line. +# fail-stop = true + +[rep-sharing] +### To conserve space, the filesystem can optionally avoid storing +### duplicate representations. This comes at a slight cost in +### performance, as maintaining a database of shared representations can +### increase commit times. The space savings are dependent upon the size +### of the repository, the number of objects it contains and the amount of +### duplication between them, usually a function of the branching and +### merging process. +### +### The following parameter enables rep-sharing in the repository. It can +### be switched on and off at will, but for best space-saving results +### should be enabled consistently over the life of the repository. +### 'svnadmin verify' will check the rep-cache regardless of this setting. +### rep-sharing is enabled by default. +# enable-rep-sharing = true + +[deltification] +### To conserve space, the filesystem stores data as differences against +### existing representations. This comes at a slight cost in performance, +### as calculating differences can increase commit times. Reading data +### will also create higher CPU load and the data will be fragmented. +### Since deltification tends to save significant amounts of disk space, +### the overall I/O load can actually be lower. +### +### The options in this section allow for tuning the deltification +### strategy. Their effects on data size and server performance may vary +### from one repository to another. Versions prior to 1.8 will ignore +### this section. +### +### The following parameter enables deltification for directories. It can +### be switched on and off at will, but for best space-saving results +### should be enabled consistently over the lifetime of the repository. +### Repositories containing large directories will benefit greatly. +### In rarely accessed repositories, the I/O overhead may be significant +### as caches will most likely be low. +### directory deltification is enabled by default. +# enable-dir-deltification = true +### +### The following parameter enables deltification for properties on files +### and directories. Overall, this is a minor tuning option but can save +### some disk space if you merge frequently or frequently change node +### properties. You should not activate this if rep-sharing has been +### disabled because this may result in a net increase in repository size. +### property deltification is enabled by default. +# enable-props-deltification = true +### +### During commit, the server may need to walk the whole change history of +### of a given node to find a suitable deltification base. This linear +### process can impact commit times, svnadmin load and similar operations. +### This setting limits the depth of the deltification history. If the +### threshold has been reached, the node will be stored as fulltext and a +### new deltification history begins. +### Note, this is unrelated to svn log. +### Very large values rarely provide significant additional savings but +### can impact performance greatly - in particular if directory +### deltification has been activated. Very small values may be useful in +### repositories that are dominated by large, changing binaries. +### Should be a power of two minus 1. A value of 0 will effectively +### disable deltification. +### For 1.8, the default value is 1023; earlier versions have no limit. +# max-deltification-walk = 1023 +### +### The skip-delta scheme used by FSFS tends to repeatably store redundant +### delta information where a simple delta against the latest version is +### often smaller. By default, 1.8+ will therefore use skip deltas only +### after the linear chain of deltas has grown beyond the threshold +### specified by this setting. +### Values up to 64 can result in some reduction in repository size for +### the cost of quickly increasing I/O and CPU costs. Similarly, smaller +### numbers can reduce those costs at the cost of more disk space. For +### rarely read repositories or those containing larger binaries, this may +### present a better trade-off. +### Should be a power of two. A value of 1 or smaller will cause the +### exclusive use of skip-deltas (as in pre-1.8). +### For 1.8, the default value is 16; earlier versions use 1. +# max-linear-deltification = 16 +### +### After deltification, we compress the data to minimize on-disk size. +### This setting controls the compression algorithm, which will be used in +### future revisions. It can be used to either disable compression or to +### select between available algorithms (zlib, lz4). zlib is a general- +### purpose compression algorithm. lz4 is a fast compression algorithm +### which should be preferred for repositories with large and, possibly, +### incompressible files. Note that the compression ratio of lz4 is +### usually lower than the one provided by zlib, but using it can +### significantly speed up commits as well as reading the data. +### lz4 compression algorithm is supported, starting from format 8 +### repositories, available in Subversion 1.10 and higher. +### The syntax of this option is: +### compression = none | lz4 | zlib | zlib-1 ... zlib-9 +### Versions prior to Subversion 1.10 will ignore this option. +### The default value is 'lz4' if supported by the repository format and +### 'zlib' otherwise. 'zlib' is currently equivalent to 'zlib-5'. +# compression = lz4 +### +### DEPRECATED: The new 'compression' option deprecates previously used +### 'compression-level' option, which was used to configure zlib compression. +### For compatibility with previous versions of Subversion, this option can +### still be used (and it will result in zlib compression with the +### corresponding compression level). +### compression-level = 0 ... 9 (default is 5) + +[packed-revprops] +### This parameter controls the size (in kBytes) of packed revprop files. +### Revprops of consecutive revisions will be concatenated into a single +### file up to but not exceeding the threshold given here. However, each +### pack file may be much smaller and revprops of a single revision may be +### much larger than the limit set here. The threshold will be applied +### before optional compression takes place. +### Large values will reduce disk space usage at the expense of increased +### latency and CPU usage reading and changing individual revprops. +### Values smaller than 4 kByte will not improve latency any further and +### quickly render revprop packing ineffective. +### revprop-pack-size is 16 kBytes by default for non-compressed revprop +### pack files and 64 kBytes when compression has been enabled. +# revprop-pack-size = 16 +### +### To save disk space, packed revprop files may be compressed. Standard +### revprops tend to allow for very effective compression. Reading and +### even more so writing, become significantly more CPU intensive. +### Compressing packed revprops is disabled by default. +# compress-packed-revprops = false + +[io] +### Parameters in this section control the data access granularity in +### format 7 repositories and later. The defaults should translate into +### decent performance over a wide range of setups. +### +### When a specific piece of information needs to be read from disk, a +### data block is being read at once and its contents are being cached. +### If the repository is being stored on a RAID, the block size should be +### either 50% or 100% of RAID block size / granularity. Also, your file +### system blocks/clusters should be properly aligned and sized. In that +### setup, each access will hit only one disk (minimizes I/O load) but +### uses all the data provided by the disk in a single access. +### For SSD-based storage systems, slightly lower values around 16 kB +### may improve latency while still maximizing throughput. If block-read +### has not been enabled, this will be capped to 4 kBytes. +### Can be changed at any time but must be a power of 2. +### block-size is given in kBytes and with a default of 64 kBytes. +# block-size = 64 +### +### The log-to-phys index maps data item numbers to offsets within the +### rev or pack file. This index is organized in pages of a fixed maximum +### capacity. To access an item, the page table and the respective page +### must be read. +### This parameter only affects revisions with thousands of changed paths. +### If you have several extremely large revisions (~1 mio changes), think +### about increasing this setting. Reducing the value will rarely result +### in a net speedup. +### This is an expert setting. Must be a power of 2. +### l2p-page-size is 8192 entries by default. +# l2p-page-size = 8192 +### +### The phys-to-log index maps positions within the rev or pack file to +### to data items, i.e. describes what piece of information is being +### stored at any particular offset. The index describes the rev file +### in chunks (pages) and keeps a global list of all those pages. Large +### pages mean a shorter page table but a larger per-page description of +### data items in it. The latency sweetspot depends on the change size +### distribution but covers a relatively wide range. +### If the repository contains very large files, i.e. individual changes +### of tens of MB each, increasing the page size will shorten the index +### file at the expense of a slightly increased latency in sections with +### smaller changes. +### For source code repositories, this should be about 16x the block-size. +### Must be a power of 2. +### p2l-page-size is given in kBytes and with a default of 1024 kBytes. +# p2l-page-size = 1024 + +[debug] +### +### Whether to verify each new revision immediately before finalizing +### the commit. This is disabled by default except in maintainer-mode +### builds. +# verify-before-commit = false diff --git a/test/repos/simple-ext.svn/db/min-unpacked-rev b/test/repos/simple-ext.svn/db/min-unpacked-rev new file mode 100644 index 0000000000..573541ac97 --- /dev/null +++ b/test/repos/simple-ext.svn/db/min-unpacked-rev @@ -0,0 +1 @@ +0 diff --git a/test/repos/simple-ext.svn/db/rep-cache.db b/test/repos/simple-ext.svn/db/rep-cache.db new file mode 100644 index 0000000000000000000000000000000000000000..3193b2eaad046fba3704b0d28a50a68a94801da2 GIT binary patch literal 8192 zcmeIuK}!Nb6bJAbjiM;*RyW_VAR=4M^_JC;OPgEUhQTy(n&njm5*>#d|ehiI!uT zo%TC>#d3w1Jtzo300Izz00bZa0SG_<0uX?}pA`5U@~wkvm49vLDta+zt1RRiHZQ=L;CU@@4?#yhXLbg~~mc>fT`3nX?Ls&t(q_@;kd&|6q zPmnj!N-"j%`;)7F<&!^&p5-&NVNc`P`LVZ4M1vhI123LfW679Jk&yS}h4J?+gU zS3MZfY8^$e`Aiw@d{Ry%1dI`+SBZs%>uw7^VT{$-o>k4Xyk=x|K`9*$$d+#CX^Zv$ zf4Az(2{}^ad;78;-W-sx-=Fn>9+LNu&)3o0-}x~i0yI}C8O(!53mF)Lc9yx|g^G|c ej~cmPzoZI_YZ6{Gdr_88^Nk1hnf(AO7WJ=&dj!fDn0uL2;xcFgCGi};7P%MLA>_hL1!bWwMgk<=JI{-``-8Sx~2ler?X@>qvZhS z>jPT>)6KB%uk~`LVLed!QU%IMrh0nGt~zC~p7r~M2xW}B&Vh{`_(=}ATKsQ!Fzyy7 zc4uq7<>SMvwZ?-a) z)0B>oW87dZf4*6*J;dJdmgjkw&ZC`k2j2VA6K~^ji#5jLtJ2Wv5xP@BFE&4IU%WYq zems5i{p{Q%din14uL~NZ-LD^~H)6E)d2he;%)5-9fBCR^<=fA>YX}J*yI_JaqN6CX rO2(X~(lQ>UT$426lExq+3DoM8C~b*QiI&DOO{6l~rdCtJNj(1>+FjA$ literal 0 HcmV?d00001 diff --git a/test/repos/simple-ext.svn/db/revs/0/2 b/test/repos/simple-ext.svn/db/revs/0/2 new file mode 100644 index 0000000000000000000000000000000000000000..99a14cf4b7f76ee26eba753930dd53704627e1f5 GIT binary patch literal 816 zcmZvZJ!lj`6vt;bpA3nm2sW<~jBwfc+L_sFOu`))jUhw{0gGh577vfyac(dHi+~?R zh?R}CT|}_e+9r*qV5N;MStC{G3)m;lNk zCm~lv(?C<68Ar-%YL%o`uYzC#rkyZdVR9kD9Q-!%}%S;O#unUwLYMU_)@exaSbTJeGis^rnsBF zpQ$A6r{xuTL@SSJeWQcholp+qxI~k*M^FFKr;u_G6ohjsvpQAEc$GQHnKxDmExDoA zqy#EZIZ5|4d3k=%Gb>dAc}$Wl&XYP%bKGo< zTmjWejYqZH7n7Q%0^8yGT2lbUjl12Rz=7w$2rz4bdULXUmfC*4ic;URV zT5%(!r#bby^on`qSY{66?ef&aVF)RMbPpw2dC8>a+6zOarB*P`g_V_4QdROm3)2?cKX5He8X8kD zRTa09dUI>+4($Ie?{8&&x)?7E=6hH1%;vc$isp`@N5_6ekGpR>52FX2McUt&KhAE3 z8yY={3bg&={dRN#qtCB?emH(VI)R>Tecg&qWAyFm%XhPPH2SpjWe4}>(DxsApLgH< zK6?@&t~A)(Gf_HKN~e|J&g9-2E__xI=82{tXa=RNLR*{|XCxC^W?VX{y(YOLf>dnb E4; /dev/null || exit 1 + +# Check that the author of this commit has the rights to perform +# the commit on the files and directories being modified. +commit-access-control.pl "$REPOS" "$TXN" commit-access-control.cfg || exit 1 + +# All checks passed, so allow the commit. +exit 0 diff --git a/test/repos/simple-ext.svn/hooks/pre-lock.tmpl b/test/repos/simple-ext.svn/hooks/pre-lock.tmpl new file mode 100755 index 0000000000..148582a689 --- /dev/null +++ b/test/repos/simple-ext.svn/hooks/pre-lock.tmpl @@ -0,0 +1,95 @@ +#!/bin/sh + +# PRE-LOCK HOOK +# +# The pre-lock hook is invoked before an exclusive lock is +# created. Subversion runs this hook by invoking a program +# (script, executable, binary, etc.) named 'pre-lock' (for which +# this file is a template), with the following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] PATH (the path in the repository about to be locked) +# [3] USER (the user creating the lock) +# [4] COMMENT (the comment of the lock) +# [5] STEAL-LOCK (1 if the user is trying to steal the lock, else 0) +# +# If the hook program outputs anything on stdout, the output string will +# be used as the lock token for this lock operation. If you choose to use +# this feature, you must guarantee the tokens generated are unique across +# the repository each time. +# +# If the hook program exits with success, the lock is created; but +# if it exits with failure (non-zero), the lock action is aborted +# and STDERR is returned to the client. +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# On a Unix system, the normal procedure is to have 'pre-lock' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'pre-lock' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'pre-lock.bat' or 'pre-lock.exe', +# but the basic idea is the same. +# +# The hook program runs in an empty environment, unless the server is +# explicitly configured otherwise. For example, a common problem is for +# the PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# CAUTION: +# For security reasons, you MUST always properly quote arguments when +# you use them, as those arguments could contain whitespace or other +# problematic characters. Additionally, you should delimit the list +# of options with "--" before passing the arguments, so malicious +# clients cannot bootleg unexpected options to the commands your +# script aims to execute. +# For similar reasons, you should also add a trailing @ to URLs which +# are passed to SVN commands accepting URLs with peg revisions. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and +# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/ + + +REPOS="$1" +PATH="$2" +USER="$3" +COMMENT="$4" +STEAL="$5" + +# If a lock exists and is owned by a different person, don't allow it +# to be stolen (e.g., with 'svn lock --force ...'). + +# (Maybe this script could send email to the lock owner?) +SVNLOOK=/opt/homebrew/Cellar/subversion/1.14.2_1/bin/svnlook +GREP=/bin/grep +SED=/bin/sed + +LOCK_OWNER=`$SVNLOOK lock "$REPOS" "$PATH" | \ + $GREP '^Owner: ' | $SED 's/Owner: //'` + +# If we get no result from svnlook, there's no lock, allow the lock to +# happen: +if [ "$LOCK_OWNER" = "" ]; then + exit 0 +fi + +# If the person locking matches the lock's owner, allow the lock to +# happen: +if [ "$LOCK_OWNER" = "$USER" ]; then + exit 0 +fi + +# Otherwise, we've got an owner mismatch, so return failure: +echo "Error: $PATH already locked by ${LOCK_OWNER}." 1>&2 +exit 1 diff --git a/test/repos/simple-ext.svn/hooks/pre-revprop-change.tmpl b/test/repos/simple-ext.svn/hooks/pre-revprop-change.tmpl new file mode 100755 index 0000000000..8b065d7c79 --- /dev/null +++ b/test/repos/simple-ext.svn/hooks/pre-revprop-change.tmpl @@ -0,0 +1,79 @@ +#!/bin/sh + +# PRE-REVPROP-CHANGE HOOK +# +# The pre-revprop-change hook is invoked before a revision property +# is added, modified or deleted. Subversion runs this hook by invoking +# a program (script, executable, binary, etc.) named 'pre-revprop-change' +# (for which this file is a template), with the following ordered +# arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] REV (the revision being tweaked) +# [3] USER (the username of the person tweaking the property) +# [4] PROPNAME (the property being set on the revision) +# [5] ACTION (the property is being 'A'dded, 'M'odified, or 'D'eleted) +# +# [STDIN] PROPVAL ** the new property value is passed via STDIN. +# +# If the hook program exits with success, the propchange happens; but +# if it exits with failure (non-zero), the propchange doesn't happen. +# The hook program can use the 'svnlook' utility to examine the +# existing value of the revision property. +# +# WARNING: unlike other hooks, this hook MUST exist for revision +# properties to be changed. If the hook does not exist, Subversion +# will behave as if the hook were present, but failed. The reason +# for this is that revision properties are UNVERSIONED, meaning that +# a successful propchange is destructive; the old value is gone +# forever. We recommend the hook back up the old value somewhere. +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# On a Unix system, the normal procedure is to have 'pre-revprop-change' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'pre-revprop-change' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'pre-revprop-change.bat' or 'pre-revprop-change.exe', +# but the basic idea is the same. +# +# The hook program runs in an empty environment, unless the server is +# explicitly configured otherwise. For example, a common problem is for +# the PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# CAUTION: +# For security reasons, you MUST always properly quote arguments when +# you use them, as those arguments could contain whitespace or other +# problematic characters. Additionally, you should delimit the list +# of options with "--" before passing the arguments, so malicious +# clients cannot bootleg unexpected options to the commands your +# script aims to execute. +# For similar reasons, you should also add a trailing @ to URLs which +# are passed to SVN commands accepting URLs with peg revisions. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and +# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/ + + +REPOS="$1" +REV="$2" +USER="$3" +PROPNAME="$4" +ACTION="$5" + +if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi + +echo "Changing revision properties other than svn:log is prohibited" >&2 +exit 1 diff --git a/test/repos/simple-ext.svn/hooks/pre-unlock.tmpl b/test/repos/simple-ext.svn/hooks/pre-unlock.tmpl new file mode 100755 index 0000000000..9ba99d071b --- /dev/null +++ b/test/repos/simple-ext.svn/hooks/pre-unlock.tmpl @@ -0,0 +1,87 @@ +#!/bin/sh + +# PRE-UNLOCK HOOK +# +# The pre-unlock hook is invoked before an exclusive lock is +# destroyed. Subversion runs this hook by invoking a program +# (script, executable, binary, etc.) named 'pre-unlock' (for which +# this file is a template), with the following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] PATH (the path in the repository about to be unlocked) +# [3] USER (the user destroying the lock) +# [4] TOKEN (the lock token to be destroyed) +# [5] BREAK-UNLOCK (1 if the user is breaking the lock, else 0) +# +# If the hook program exits with success, the lock is destroyed; but +# if it exits with failure (non-zero), the unlock action is aborted +# and STDERR is returned to the client. +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# On a Unix system, the normal procedure is to have 'pre-unlock' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'pre-unlock' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'pre-unlock.bat' or 'pre-unlock.exe', +# but the basic idea is the same. +# +# The hook program runs in an empty environment, unless the server is +# explicitly configured otherwise. For example, a common problem is for +# the PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# CAUTION: +# For security reasons, you MUST always properly quote arguments when +# you use them, as those arguments could contain whitespace or other +# problematic characters. Additionally, you should delimit the list +# of options with "--" before passing the arguments, so malicious +# clients cannot bootleg unexpected options to the commands your +# script aims to execute. +# For similar reasons, you should also add a trailing @ to URLs which +# are passed to SVN commands accepting URLs with peg revisions. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and +# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/ + + +REPOS="$1" +PATH="$2" +USER="$3" +TOKEN="$4" +BREAK="$5" + +# If a lock is owned by a different person, don't allow it be broken. +# (Maybe this script could send email to the lock owner?) + +SVNLOOK=/opt/homebrew/Cellar/subversion/1.14.2_1/bin/svnlook +GREP=/bin/grep +SED=/bin/sed + +LOCK_OWNER=`$SVNLOOK lock "$REPOS" "$PATH" | \ + $GREP '^Owner: ' | $SED 's/Owner: //'` + +# If we get no result from svnlook, there's no lock, return success: +if [ "$LOCK_OWNER" = "" ]; then + exit 0 +fi + +# If the person unlocking matches the lock's owner, return success: +if [ "$LOCK_OWNER" = "$USER" ]; then + exit 0 +fi + +# Otherwise, we've got an owner mismatch, so return failure: +echo "Error: $PATH locked by ${LOCK_OWNER}." 1>&2 +exit 1 diff --git a/test/repos/simple-ext.svn/hooks/start-commit.tmpl b/test/repos/simple-ext.svn/hooks/start-commit.tmpl new file mode 100755 index 0000000000..1395e8315a --- /dev/null +++ b/test/repos/simple-ext.svn/hooks/start-commit.tmpl @@ -0,0 +1,81 @@ +#!/bin/sh + +# START-COMMIT HOOK +# +# The start-commit hook is invoked immediately after a Subversion txn is +# created and populated with initial revprops in the process of doing a +# commit. Subversion runs this hook by invoking a program (script, +# executable, binary, etc.) named 'start-commit' (for which this file +# is a template) with the following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] USER (the authenticated user attempting to commit) +# [3] CAPABILITIES (a colon-separated list of capabilities reported +# by the client; see note below) +# [4] TXN-NAME (the name of the commit txn just created) +# +# Note: The CAPABILITIES parameter is new in Subversion 1.5, and 1.5 +# clients will typically report at least the "mergeinfo" capability. +# If there are other capabilities, then the list is colon-separated, +# e.g.: "mergeinfo:some-other-capability" (the order is undefined). +# +# The list is self-reported by the client. Therefore, you should not +# make security assumptions based on the capabilities list, nor should +# you assume that clients reliably report every capability they have. +# +# Note: The TXN-NAME parameter is new in Subversion 1.8. Prior to version +# 1.8, the start-commit hook was invoked before the commit txn was even +# created, so the ability to inspect the commit txn and its metadata from +# within the start-commit hook was not possible. +# +# If the hook program exits with success, the commit continues; but +# if it exits with failure (non-zero), the commit is stopped before +# a Subversion txn is created, and STDERR is returned to the client. +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# On a Unix system, the normal procedure is to have 'start-commit' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'start-commit' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'start-commit.bat' or 'start-commit.exe', +# but the basic idea is the same. +# +# The hook program runs in an empty environment, unless the server is +# explicitly configured otherwise. For example, a common problem is for +# the PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# CAUTION: +# For security reasons, you MUST always properly quote arguments when +# you use them, as those arguments could contain whitespace or other +# problematic characters. Additionally, you should delimit the list +# of options with "--" before passing the arguments, so malicious +# clients cannot bootleg unexpected options to the commands your +# script aims to execute. +# For similar reasons, you should also add a trailing @ to URLs which +# are passed to SVN commands accepting URLs with peg revisions. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and +# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/ + + +REPOS="$1" +USER="$2" + +commit-allower.pl --repository "$REPOS" --user "$USER" || exit 1 +special-auth-check.py --user "$USER" --auth-level 3 || exit 1 + +# All checks passed, so allow the commit. +exit 0 diff --git a/test/repos/simple-ext.svn/locks/db-logs.lock b/test/repos/simple-ext.svn/locks/db-logs.lock new file mode 100644 index 0000000000..20dd6369be --- /dev/null +++ b/test/repos/simple-ext.svn/locks/db-logs.lock @@ -0,0 +1,3 @@ +This file is not used by Subversion 1.3.x or later. +However, its existence is required for compatibility with +Subversion 1.2.x or earlier. diff --git a/test/repos/simple-ext.svn/locks/db.lock b/test/repos/simple-ext.svn/locks/db.lock new file mode 100644 index 0000000000..20dd6369be --- /dev/null +++ b/test/repos/simple-ext.svn/locks/db.lock @@ -0,0 +1,3 @@ +This file is not used by Subversion 1.3.x or later. +However, its existence is required for compatibility with +Subversion 1.2.x or earlier. diff --git a/test/test_sys_checkout.py b/test/test_sys_checkout.py old mode 100644 new mode 100755 index ab4f77e88f..664160dc99 --- a/test/test_sys_checkout.py +++ b/test/test_sys_checkout.py @@ -97,6 +97,7 @@ SIMPLE_REPO = 'simple-ext.git' # Child repo SIMPLE_FORK_REPO = 'simple-ext-fork.git' # Child repo MIXED_REPO = 'mixed-cont-ext.git' # Both parent and child +SVN_TEST_REPO = 'simple-ext.svn' # Subversion repository # Standard (arbitrary) external names for test configs TAG_SECTION = 'simp_tag' @@ -120,8 +121,6 @@ # Branch that exists in both the simple and simple-fork repos. REMOTE_BRANCH_FEATURE2 = 'feature2' -SVN_TEST_REPO = 'https://github.com/escomp/cesm' - # Disable too-many-public-methods error # pylint: disable=R0904 @@ -354,7 +353,7 @@ def create_section_reference_to_subexternal(self, name): self._config.set(name, ExternalsDescription.EXTERNALS, CFG_SUB_NAME) - def create_svn_external(self, name, tag='', branch=''): + def create_svn_external(self, name, url, tag='', branch=''): """Create a config section for an svn repository. """ @@ -365,7 +364,7 @@ def create_svn_external(self, name, tag='', branch=''): self._config.set(name, ExternalsDescription.PROTOCOL, ExternalsDescription.PROTOCOL_SVN) - self._config.set(name, ExternalsDescription.REPO_URL, SVN_TEST_REPO) + self._config.set(name, ExternalsDescription.REPO_URL, url) self._config.set(name, ExternalsDescription.REQUIRED, str(True)) @@ -1387,36 +1386,10 @@ def test_container_sparse(self): 'simple_subdir', 'subdir_file.txt')) - class TestSysCheckoutSVN(BaseTestSysCheckout): """Run systems level tests of checkout_externals accessing svn repositories - SVN tests - these tests use the svn repository interface. Since - they require an active network connection, they are significantly - slower than the git tests. But svn testing is critical. So try to - design the tests to only test svn repository functionality - (checkout, switch) and leave generic testing of functionality like - 'optional' to the fast git tests. - - Example timing as of 2017-11: - - * All other git and unit tests combined take between 4-5 seconds - - * Just checking if svn is available for a single test takes 2 seconds. - - * The single svn test typically takes between 10 and 25 seconds - (depending on the network)! - - NOTE(bja, 2017-11) To enable CI testing we can't use a real remote - repository that restricts access and it seems inappropriate to hit - a random open source repo. For now we are just hitting one of our - own github repos using the github svn server interface. This - should be "good enough" for basic checkout and swich - functionality. But if additional svn functionality is required, a - better solution will be necessary. I think eventually we want to - create a small local svn repository on the fly (doesn't require an - svn server or network connection!) and use it for testing. - + SVN tests - these tests use the svn repository interface. """ @staticmethod @@ -1427,6 +1400,9 @@ def _svn_branch_name(): def _svn_tag_name(): return './{0}/svn_tag'.format(EXTERNALS_PATH) + def _svn_test_repo_url(self): + return 'file://' + os.path.join(self._bare_root, SVN_TEST_REPO) + def _check_tag_branch_svn_tag_clean(self, tree): self._check_sync_clean(tree[self._external_path(TAG_SECTION)], ExternalStatus.STATUS_OK, @@ -1438,13 +1414,12 @@ def _check_tag_branch_svn_tag_clean(self, tree): ExternalStatus.STATUS_OK, ExternalStatus.STATUS_OK) - @staticmethod - def _have_svn_access(): + def _have_svn_access(self): """Check if we have svn access so we can enable tests that use svn. """ have_svn = False - cmd = ['svn', 'ls', SVN_TEST_REPO, ] + cmd = ['svn', 'ls', self._svn_test_repo_url(), ] try: execute_subprocess(cmd) have_svn = True @@ -1472,8 +1447,8 @@ def test_container_simple_svn(self): self._generator.create_section(SIMPLE_REPO, TAG_SECTION, tag='tag1') # Svn repos. - self._generator.create_svn_external('svn_branch', branch='trunk') - self._generator.create_svn_external('svn_tag', tag='tags/cesm2.0.beta07') + self._generator.create_svn_external('svn_branch', self._svn_test_repo_url(), branch='trunk') + self._generator.create_svn_external('svn_tag', self._svn_test_repo_url(), tag='tags/cesm2.0.beta07') self._generator.write_config(cloned_repo_dir) @@ -1557,7 +1532,7 @@ def setUp(self): execute_subprocess(cmd) cmd = ['git', 'checkout', self._bare_branch_name] execute_subprocess(cmd) - cmd = ['git', 'submodule', 'add', fork_repo_dir] + cmd = ['git', '-c', 'protocol.file.allow=always','submodule', 'add', fork_repo_dir] execute_subprocess(cmd) cmd = ['git', 'commit', '-am', "'Added simple-ext-fork as a submodule'"] execute_subprocess(cmd) @@ -1571,7 +1546,7 @@ def setUp(self): execute_subprocess(cmd) cmd = ['git', 'checkout', self._config_branch_name] execute_subprocess(cmd) - cmd = ['git', 'submodule', 'add', '--name', SIMPLE_REPO, + cmd = ['git', '-c', 'protocol.file.allow=always', 'submodule', 'add', '--name', SIMPLE_REPO, simple_repo_dir, self._simple_ext_name] execute_subprocess(cmd) # Checkout feature2 From c4e14d165f90ac3dab2ba8bb2b778459c6c7ceaa Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 26 Jan 2024 02:06:07 -0700 Subject: [PATCH 023/406] Update to cam version that no longer uses svn, but git --- Externals.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Externals.cfg b/Externals.cfg index d75b4683a6..4e65a11aed 100644 --- a/Externals.cfg +++ b/Externals.cfg @@ -49,7 +49,7 @@ externals = Externals.cfg required = True [cam6] -tag = cam6_3_133 +tag = cam6_3_135 protocol = git repo_url = https://github.com/ESCOMP/CAM local_path = components/cam From 6abcc1d26a1d858ae0a035ee36ffee10c6a7aa72 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 26 Jan 2024 14:46:17 -0700 Subject: [PATCH 024/406] Update assign-to-project.yml --- .github/workflows/assign-to-project.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/assign-to-project.yml b/.github/workflows/assign-to-project.yml index 8c6c259c33..a5bcd10b64 100644 --- a/.github/workflows/assign-to-project.yml +++ b/.github/workflows/assign-to-project.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest name: Assign to High Priority project steps: - - name: Assign issues and pull requests with `priority: high` label to project 25 + - name: Assign issues and pull requests with priority high label to project 25 uses: srggrs/assign-one-project-github-action@1.3.1 if: | contains(github.event.issue.labels.*.name, 'priority: high') || From 9902724bb64579cc0aa84b567f62682c1f1d24c7 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Feb 2024 00:27:30 -0700 Subject: [PATCH 025/406] Move datasets to under inputdata --- bld/namelist_files/namelist_defaults_ctsm.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 57f5b3adf4..28a372de32 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -2749,9 +2749,9 @@ use_crop=".true.">lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_mpasa1 /glade/u/home/dleung/CESM2/ctsm_dustemis_dev/cdf5_Z0a_Prigent-Globe-025x025-01242023.nc +>lnd/clm2/dustemisdata/Prigent_2005_roughness_0.25x0.25_cdf5_c240127.nc /glade/u/home/dleung/CESM2/ctsm_dustemis_dev/lnd_mesh.nc +>lnd/clm2/dustemisdata/dust_2x2_ESMFmesh_cdf5_c230730.nc From 158c878451b3b0916657cf84c2f02dc262dc4b4c Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Feb 2024 01:20:16 -0700 Subject: [PATCH 026/406] Remove whitespace at end of lines, remove commented out code included commented out Zender code, remove unneeded markers by Danny Leung --- src/biogeochem/DUSTMod.F90 | 84 ++++++++------------------------------ 1 file changed, 18 insertions(+), 66 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 39aa5766f4..67c22e207a 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -16,7 +16,7 @@ module DUSTMod use shr_log_mod , only : errMsg => shr_log_errMsg use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=) use clm_varpar , only : dst_src_nbr, ndst, sz_nbr, & - natpft_lb, natpft_ub, natpft_size + natpft_lb, natpft_ub, natpft_size use clm_varcon , only : grav, spval use landunit_varcon , only : istcrop, istsoil use clm_varctl , only : iulog @@ -31,7 +31,7 @@ module DUSTMod use LandunitType , only : lun use ColumnType , only : col use PatchType , only : patch - use pftconMod , only : noveg + use pftconMod , only : noveg use PrigentRoughnessStreamType , only : prigentroughnessstream_type ! ! !PUBLIC TYPES @@ -105,7 +105,7 @@ module DUSTMod subroutine Init(this, bounds, NLFilename) class(dust_type) :: this - type(bounds_type), intent(in) :: bounds + type(bounds_type), intent(in) :: bounds character(len=*), intent(in) :: NLFilename call this%InitAllocate (bounds) @@ -296,8 +296,8 @@ subroutine InitCold(this, bounds) ! ! ! !ARGUMENTS: - class(dust_type), intent(inout) :: this ! Danny M. Leung used class instead of type - type(bounds_type), intent(in) :: bounds + class(dust_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds ! ! !LOCAL VARIABLES: integer :: c,l @@ -340,7 +340,7 @@ subroutine DustEmission (bounds, & ! !USES use shr_const_mod, only : SHR_CONST_RHOFW use subgridaveMod, only : p2g - use pftconMod , only : noveg + use pftconMod , only : noveg ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds @@ -366,7 +366,6 @@ subroutine DustEmission (bounds, & real(r8) :: flx_mss_vrt_dst_ttl(bounds%begp:bounds%endp) real(r8) :: frc_thr_wet_fct real(r8) :: frc_thr_rgh_fct - !real(r8) :: wnd_frc_thr_slt ! Danny M. Leung commented and put below real(r8) :: wnd_rfr_thr_slt real(r8) :: wnd_frc_slt real(r8) :: lnd_frc_mbl(bounds%begp:bounds%endp) @@ -377,8 +376,10 @@ subroutine DustEmission (bounds, & real(r8) :: sumwt(bounds%begl:bounds%endl) ! sum of weights logical :: found ! temporary for error check integer :: index - - real(r8) :: tmp2 ! calculates the dry fluid threshold using Shao and Lu (2000) scheme; replace the tmp1 (Iversen and White, 1982) that was passed from Dustini to DustEmission; tmp2 will be calculated here + + real(r8) :: tmp2 ! calculates the dry fluid threshold using Shao and Lu (2000) scheme; + ! replace the tmp1 (Iversen and White, 1982) that was passed from Dustini to DustEmission; + ! tmp2 will be calculated here real(r8) :: wnd_frc_thr_slt_std ! [m/s] The soil threshold friction speed at standard air density (1.2250 kg/m3) real(r8) :: frag_expt ! fragmentation exponent real(r8) :: wnd_frc_thr_slt_it ! [m/s] created for impact threshold friction velocity @@ -430,7 +431,7 @@ subroutine DustEmission (bounds, & mbl_bsn_fct => dust_inst%mbl_bsn_fct_col , & ! Input: [real(r8) (:) ] basin factor flx_mss_vrt_dst => dust_inst%flx_mss_vrt_dst_patch , & ! Output: [real(r8) (:,:) ] surface dust emission (kg/m**2/s) flx_mss_vrt_dst_tot => dust_inst%flx_mss_vrt_dst_tot_patch , & ! Output: [real(r8) (:) ] total dust flux back to atmosphere (pft) - + dst_emiss_coeff => dust_inst%dst_emiss_coeff_patch , & ! Output dust emission coefficient wnd_frc_thr => dust_inst%wnd_frc_thr_patch , & ! output impact threshold wnd_frc_thr_dry => dust_inst%wnd_frc_thr_dry_patch , & ! output dry threshold @@ -594,7 +595,7 @@ subroutine DustEmission (bounds, & wnd_frc_thr_slt = tmp2 / sqrt(forc_rho(c)) * frc_thr_wet_fct !* frc_thr_rgh_fct ! fluid threshold wnd_frc_thr_slt_it = B_it * tmp2 / sqrt(forc_rho(c)) ! define impact threshold - !wnd_frc_thr_dry(p) = tmp1 / sqrt(forc_rho(c)) ! output dry fluid threshold; tmp1 uses I&W 1982 + !wnd_frc_thr_dry(p) = tmp1 / sqrt(forc_rho(c)) ! output dry fluid threshold; tmp1 uses I&W 1982 !wnd_frc_thr_slt = tmp1 / sqrt(forc_rho(c)) * frc_thr_wet_fct !* frc_thr_rgh_fct ! fluid threshold !wnd_frc_thr_slt_it = B_it * tmp1 / sqrt(forc_rho(c)) ! define impact threshold ! the above formula is true for Iversen and White (1982) and Shao and Lu (2000) scheme @@ -612,7 +613,7 @@ subroutine DustEmission (bounds, & end if !################ drag partition effect, and soil friction velocity ########################### - ! subsection on computing vegetation drag partition and hybrid drag partition factors + ! subsection on computing vegetation drag partition and hybrid drag partition factors ! in Leung et al. (2022), drag partition effect is applied on the wind instead of the threshold !############################################################################################## ! the following comes from subr. frc_thr_rgh_fct_get @@ -626,24 +627,18 @@ subroutine DustEmission (bounds, & lai(p) = 1_r8 ! setting LAI ~ 0.1 to be a min value (since the value goes to infinity when LAI=0) end if ! and 1 to be a max value as computing K involves 1 / LAI - ! calculate Okin's shear stress ratio (which is drag partition factor) using Pierre's equation + ! calculate Okin's shear stress ratio (which is drag partition factor) using Pierre's equation K_length = 2_r8 * (1_r8/lai(p) - 1_r8) ! Here LAI has to be non-zero to avoid blowup ssr(p) = (K_length+f_0*c_e)/(K_length+c_e) - ! dmleung added calculation of LUH2 bare vs veg fraction within a grid - !bare_frc = wt_lunit(g,istsoil) * wt_nat_patch(g,noveg) - !veg_frc = wt_lunit(g,istsoil) * sum(wt_nat_patch(g,(noveg+1):natpft_ub)) + wt_lunit(g,istcrop) - - !frc_thr_rgh_fct = (bare_frc*(roughfct(p))**3_r8 + veg_frc*(ssr(p))**3_r8 )**(0.3333_r8) ! land cover weighted mean using LUH2 land cover - ! calculation of drag partition effect using LUH2 bare and veg fractions within a grid if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then if (patch%itype(p) == noveg) then frc_thr_rgh_fct = dpfct_rock(p) - else + else frc_thr_rgh_fct = ssr(p) end if - else + else frc_thr_rgh_fct = 1.0_r8 end if @@ -658,30 +653,16 @@ subroutine DustEmission (bounds, & !########## end of drag partition effect ####################################################### - !############ comment out Owen's offect this block; dust emission does not need to consider it ########### - ! the following if-block comes from subr. wnd_frc_slt_get - ! purpose: compute the saltating friction velocity - ! theory: saltation roughens the boundary layer, AKA "Owen's effect" - - !if (u10(p) >= wnd_rfr_thr_slt) then - ! wnd_rfr_dlt = u10(p) - wnd_rfr_thr_slt - ! wnd_frc_slt_dlt = 0.003_r8 * wnd_rfr_dlt * wnd_rfr_dlt - ! wnd_frc_slt = wnd_frc_slt + wnd_frc_slt_dlt ! careful that RHS is now wnd_frc_slt instead of fv(p) - ! ! because wnd_frc_slt takes drag partition effect into account, but fv(p) doesn't. - !end if - !########## end of Owen effect ################################################################ - ! save soil friction velocity and roughness effect before the if-statement wnd_frc_soil(p) = wnd_frc_slt ! save soil friction velocity for CLM output, which has drag partition and Owen effect ! save land mobile fraction lnd_frc_mble(p) = lnd_frc_mbl(p) ! save land mobile fraction first, before the if-statement - ! only perform the following calculations if lnd_frc_mbl is non-zero + ! only perform the following calculations if lnd_frc_mbl is non-zero if (lnd_frc_mbl(p) > 0.0_r8) then ! if bare land fraction is larger than 0 then calculate the dust emission equation ! reset these variables which will be updated in the following if-block - !wnd_frc_slt = fv(p) ! we don't need this line because its calculation is moved upward flx_mss_hrz_slt_ttl = 0.0_r8 flx_mss_vrt_dst_ttl(p) = 0.0_r8 @@ -690,32 +671,11 @@ subroutine DustEmission (bounds, & wnd_rfr_thr_slt = u10(p) * wnd_frc_thr_slt / fv(p) ! keep and use if we want the default Z03 scheme - ! the following comes from subr. flx_mss_hrz_slt_ttl_Whi79_get ! purpose: compute vertically integrated streamwise mass flux of particles - !if (wnd_frc_slt > wnd_frc_thr_slt) then! if using Zender's scheme, use fluid threshold for dust emission equation below if (wnd_frc_slt > wnd_frc_thr_slt_it) then! if using Leung's scheme, use impact threshold for dust emission equation - !################### for Zender et al. (2003) scheme -dmleung ########################### - !################ uncomment the below block if want to use Z03 scheme ################### - !wnd_frc_rat = wnd_frc_thr_slt / wnd_frc_slt - !flx_mss_hrz_slt_ttl = cst_slt * forc_rho(c) * (wnd_frc_slt**3.0_r8) * & - ! (1.0_r8 - wnd_frc_rat) * (1.0_r8 + wnd_frc_rat) * (1.0_r8 + wnd_frc_rat) / grav ! Zender dust emission equation for emission flux - - ! the following loop originates from subr. dst_mbl - ! purpose: apply land sfc and veg limitations and global tuning factor - ! slevis: multiply flx_mss_hrz_slt_ttl by liqfrac to incude the effect - ! of frozen soil - - !flx_mss_hrz_slt_ttl = flx_mss_hrz_slt_ttl * lnd_frc_mbl(p) * mbl_bsn_fct(c) * & - ! flx_mss_fdg_fct * liqfrac - - ! - !dst_slt_flx_rat_ttl = 100.0_r8 * exp( log(10.0_r8) * (13.4_r8 * mss_frc_cly_vld(c) - 6.0_r8) ) - !flx_mss_vrt_dst_ttl(p) = flx_mss_hrz_slt_ttl * dst_slt_flx_rat_ttl - !######################################################################################## - !################### for Leung et al. (2022) ################################################ !################ uncomment the below block if want to use Leung's scheme ################### @@ -733,14 +693,6 @@ subroutine DustEmission (bounds, & ! mean lowpass-filtered wind speed at 0.1 m saltation height (assuming aerodynamic roughness length = 1e-4 m globally for ease; also assuming neutral condition) u_mean_slt(p) = (wnd_frc_slt/k) * log(0.1_r8 / 1e-4_r8) ! translating from ustar (velocity scale) to actual wind - ! sd of lowpass-filtered wind speed - !if (obul(p)==0) then - ! zetaobu = 0 - !else - !zetaobu = zii(p) / obul(p) ! For now zii is a constant of 1000 m in CLM -dml, 24 Aug 2021 - ! zetaobu = 1000_r8 / obul(p) ! For now zii is a constant of 1000 m in CLM -dml, 24 Aug 2021 - !end if - !stblty(p) = zetaobu ! zetaobu get outputted as the Obukhov stability parameter stblty(p) = 0 ! -dmleung 2 Dec 2021: use stability = 0 for now, assuming no buoyancy contribution. Might uncomment the above lines in future revisions. if ((12_r8 - 0.5_r8 * stblty(p)) .GE. 0.001_r8) then ! should have used 0 theoretically; used 0.001 here to avoid undefined values u_sd_slt(p) = wnd_frc_slt * (12_r8 - 0.5_r8 * stblty(p))**0.333_r8 @@ -775,7 +727,7 @@ subroutine DustEmission (bounds, & ! multiply dust emission flux by intermittency factor if (intrmtncy_fct(p) /= intrmtncy_fct(p)) then ! if intrmtncy_fct(p) is not NaN then multiply by intermittency factor; this statement is needed because dust emission flx_mss_vrt_dst_ttl(p) has to be non NaN (at least zero) to be outputted - flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) + flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) else flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) * intrmtncy_fct(p) ! multiply dust flux by intermittency end if From 1e6d8bee191777e6c1580080a1419ba07f5aa2dd Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Feb 2024 01:22:53 -0700 Subject: [PATCH 027/406] Add use_prigent_roughness to determine if that streams dataset needs to be read in, this is important when we need to flip between Zender and Leung --- bld/CLMBuildNamelist.pm | 14 ++++++++++++-- bld/namelist_files/namelist_defaults_ctsm.xml | 1 + bld/namelist_files/namelist_definition_ctsm.xml | 5 +++++ bld/unit_testers/build-namelist_test.pl | 7 ++++++- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 33299da345..7e9ae0e2d2 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4575,9 +4575,19 @@ sub setup_logic_misc { #------------------------------------------------------------------------------- sub setup_logic_prigent_roughness { + # + # The Prigent roughness stream data set read in if needed + # my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldfilename_prigentroughness' ); - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_meshfile_prigentroughness' ); + my $var = "use_prigent_roughness"; + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var ); + my $use_prigent = $nl->get_value($var); + if ( &value_is_true($use_prigent) ) { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldfilename_prigentroughness' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_meshfile_prigentroughness' ); + } else { + $log->fatal_error("variable \"$var\" MUST be true when Leung_2023 dust emission method is being used" ); + } } #------------------------------------------------------------------------------- diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 28a372de32..a52477ce6c 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -2748,6 +2748,7 @@ use_crop=".true.">lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_mpasa1 +.true. lnd/clm2/dustemisdata/Prigent_2005_roughness_0.25x0.25_cdf5_c240127.nc + +If TRUE use the Prigent roughness dataset + + Filename of input stream data for aeolian roughness length (from Prigent's roughness dataset) diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index 9b579dd9ce..e05e0857b7 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -163,7 +163,7 @@ sub cat_and_create_namelistinfile { # # Figure out number of tests that will run # -my $ntests = 1999; +my $ntests = 2000; if ( defined($opts{'compare'}) ) { $ntests += 1353; @@ -531,6 +531,11 @@ sub cat_and_create_namelistinfile { GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, + "LeungDust_WO_Prigent" =>{ options=>" -envxml_dir . -bgc sp", + namelst=>"use_prigent_roughness=.false.", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_1", + }, "soilm_stream off w file" =>{ options=>"-res 0.9x1.25 -envxml_dir .", namelst=>"use_soil_moisture_streams = .false.,stream_fldfilename_soilm='file_provided_when_off'", GLC_TWO_WAY_COUPLING=>"FALSE", From b9d118f4481e30fa056ac476100b4fbc9249e82a Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Feb 2024 01:40:28 -0700 Subject: [PATCH 028/406] Remove more commented out code --- src/biogeochem/DUSTMod.F90 | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 67c22e207a..7a1bfcc092 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -595,9 +595,6 @@ subroutine DustEmission (bounds, & wnd_frc_thr_slt = tmp2 / sqrt(forc_rho(c)) * frc_thr_wet_fct !* frc_thr_rgh_fct ! fluid threshold wnd_frc_thr_slt_it = B_it * tmp2 / sqrt(forc_rho(c)) ! define impact threshold - !wnd_frc_thr_dry(p) = tmp1 / sqrt(forc_rho(c)) ! output dry fluid threshold; tmp1 uses I&W 1982 - !wnd_frc_thr_slt = tmp1 / sqrt(forc_rho(c)) * frc_thr_wet_fct !* frc_thr_rgh_fct ! fluid threshold - !wnd_frc_thr_slt_it = B_it * tmp1 / sqrt(forc_rho(c)) ! define impact threshold ! the above formula is true for Iversen and White (1982) and Shao and Lu (2000) scheme wnd_frc_thr(p) = wnd_frc_thr_slt ! output fluid threshold From 9a05842d4ad3d173b06b3d1559e62c51bfea2fb0 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 12 Feb 2024 22:43:10 -0700 Subject: [PATCH 029/406] Add in read of use_prigent_roughness to the FORTRAN source --- src/biogeochem/DUSTMod.F90 | 1 + .../share_esmf/PrigentRoughnessStreamType.F90 | 33 ++++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 7a1bfcc092..f3b3a0cd68 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -63,6 +63,7 @@ module DUSTMod real(r8), pointer, private :: vlc_trb_3_patch (:) ! turbulent deposition velocity 3(m/s) real(r8), pointer, private :: vlc_trb_4_patch (:) ! turbulent deposition velocity 4(m/s) real(r8), pointer, private :: mbl_bsn_fct_col (:) ! basin factor + real(r8), pointer, private :: dst_emiss_coeff_patch (:) ! dust emission coefficient (unitless) real(r8), pointer, private :: wnd_frc_thr_patch (:) ! wet fluid threshold (m/s) real(r8), pointer, private :: wnd_frc_thr_dry_patch (:) ! dry fluid threshold (m/s) diff --git a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 index cef34682c9..0766c0ca1a 100644 --- a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 +++ b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 @@ -39,7 +39,7 @@ module PrigentRoughnessStreamType character(len=CL) :: stream_meshfile_prigentroughness ! mesh Filename character(len=CL) :: prigentroughnessmapalgo ! map algo contains - procedure, private :: ReadNML ! Read in namelist + procedure, private :: ReadNML ! Read in namelist ! If will be used end type streamcontrol_type type(streamcontrol_type), private :: control ! Stream control data @@ -269,6 +269,7 @@ subroutine ReadNML(this, bounds, NLFilename) ! local variables integer :: nu_nml ! unit for namelist file integer :: nml_error ! namelist i/o error flag + logical :: use_prigent_roughness = .true. character(len=CL) :: stream_fldFileName_prigentroughness = ' ' character(len=CL) :: stream_meshfile_prigentroughness = ' ' character(len=CL) :: prigentroughnessmapalgo = 'bilinear' @@ -277,7 +278,8 @@ subroutine ReadNML(this, bounds, NLFilename) !----------------------------------------------------------------------- namelist /prigentroughness/ & ! MUST agree with namelist_name above - prigentroughnessmapalgo, stream_fldFileName_prigentroughness, stream_meshfile_prigentroughness + prigentroughnessmapalgo, stream_fldFileName_prigentroughness, stream_meshfile_prigentroughness, & + use_prigent_roughness ! Default values for namelist @@ -296,16 +298,37 @@ subroutine ReadNML(this, bounds, NLFilename) close(nu_nml) endif + call shr_mpi_bcast(use_prigent_roughness , mpicom) call shr_mpi_bcast(prigentroughnessmapalgo , mpicom) call shr_mpi_bcast(stream_fldFileName_prigentroughness , mpicom) call shr_mpi_bcast(stream_meshfile_prigentroughness , mpicom) + ! Error checking + if ( use_prigent_roughness == .false. )then + if ( len_trim(stream_fldFileName_prigentroughness) /= 0 )then + call endrun(msg=' ERROR stream_fldFileName_prigentroughness is set, but use_prigent_roughness is FALSE'//errMsg(sourcefile, __LINE__)) + end if + if ( len_trim(stream_meshfile_prigentroughness) /= 0 )then + call endrun(msg=' ERROR stream_meshfile_prigentroughness is set, but use_prigent_roughness is FALSE'//errMsg(sourcefile, __LINE__)) + end if + else + if ( len_trim(stream_fldFileName_prigentroughness) == 0 )then + call endrun(msg=' ERROR stream_fldFileName_prigentroughness is NOT set, but use_prigent_roughness is TRUE'//errMsg(sourcefile, __LINE__)) + end if + if ( len_trim(stream_meshfile_prigentroughness) == 0 )then + call endrun(msg=' ERROR stream_meshfile_prigentroughness is NOT set, but use_prigent_roughness is TRUE'//errMsg(sourcefile, __LINE__)) + end if + end if + if (masterproc) then write(iulog,*) ' ' write(iulog,*) namelist_name, ' stream settings:' - write(iulog,*) ' stream_fldFileName_prigentroughness = ',stream_fldFileName_prigentroughness - write(iulog,*) ' stream_meshfile_prigentroughness = ',stream_meshfile_prigentroughness - write(iulog,*) ' prigentroughnessmapalgo = ',prigentroughnessmapalgo + write(iulog,*) ' use_prigent_roughness = ',use_prigent_roughness + if ( use_prigent_roughness )then + write(iulog,*) ' stream_fldFileName_prigentroughness = ',trim(stream_fldFileName_prigentroughness) + write(iulog,*) ' stream_meshfile_prigentroughness = ',trim(stream_meshfile_prigentroughness) + write(iulog,*) ' prigentroughnessmapalgo = ',trim(prigentroughnessmapalgo) + end if endif this%stream_fldFileName_prigentroughness = stream_fldFileName_prigentroughness this%stream_meshfile_prigentroughness = stream_meshfile_prigentroughness From d8b6cb8f6c27186a252d23f8ae1d3521e9c1323e Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 13 Feb 2024 11:39:16 -0700 Subject: [PATCH 030/406] Remove an empty call the prigentRoughness call is what's being used --- bld/CLMBuildNamelist.pm | 5 ----- 1 file changed, 5 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 7e9ae0e2d2..8081f1277e 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -1706,11 +1706,6 @@ sub process_namelist_inline_logic { ################################## setup_logic_lai_streams($opts, $nl_flags, $definition, $defaults, $nl); - ################################## - # namelist group: dust_emis_streams # - ################################## - #setup_logic_dustemis_streams($opts, $nl_flags, $definition, $defaults, $nl); - ################################## # namelist group: cropcal_streams # ################################## From 4a72a1c9be9f1ba97a0f9ef98905a37addcc1771 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 13 Feb 2024 12:00:52 -0700 Subject: [PATCH 031/406] Work with some indentation, remove commented out code, add a deallocate that was missing, add underscore to a name for readability, always allocate, but allocate only 0th element if usestreams is false --- .../share_esmf/PrigentRoughnessStreamType.F90 | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 index 0766c0ca1a..dec9fa86fd 100644 --- a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 +++ b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 @@ -79,17 +79,18 @@ subroutine Init(this, bounds, NLFilename) character(len=16), allocatable :: stream_varnames(:) ! array of stream field names integer :: rc ! error code real(r8), pointer :: dataptr1d(:) ! temporary pointer - character(len=*), parameter :: stream_name = 'prigentroughness' + character(len=*), parameter :: stream_name = 'prigent_roughness' !----------------------------------------------------------------------- - !if ( finundation_mtd /= finundation_mtd_h2osfc )then - call this%InitAllocate( bounds ) call control%ReadNML( bounds, NLFileName ) + call this%InitAllocate( bounds ) + if ( this%useStreams() )then - allocate(stream_varnames(1)) - stream_varnames = (/"Z0a"/) ! varname in cdf5_Z0a_Prigent-Globe-025x025-09262022.nc, in centimeter + + allocate(stream_varnames(1)) + stream_varnames = (/"Z0a"/) ! varname in cdf5_Z0a_Prigent-Globe-025x025-09262022.nc, in centimeter if (masterproc) then write(iulog,*) ' stream_varnames = ',stream_varnames @@ -152,8 +153,8 @@ subroutine Init(this, bounds, NLFilename) end if end do + deallocate(stream_varnames) end if - !end if !commented out end subroutine Init @@ -161,7 +162,7 @@ end subroutine Init logical function UseStreams(this) ! ! !DESCRIPTION: - ! Return true if + ! Return true if the Prigent Roughness stream is being used ! ! !USES: ! @@ -171,7 +172,7 @@ logical function UseStreams(this) ! ! !LOCAL VARIABLES: if ( trim(control%stream_fldFileName_prigentroughness) == '' )then - UseStreams = .false. ! this won't happen and UseStreams will always be true + UseStreams = .false. ! Prigent streams are off without a filename given else UseStreams = .true. end if @@ -197,7 +198,12 @@ subroutine InitAllocate(this, bounds) begg = bounds%begg; endg = bounds%endg - allocate(this%prigent_rghn (begg:endg)) ; this%prigent_rghn (:) = nan + if ( this%useStreams() )then + allocate(this%prigent_rghn(begg:endg)) + else + allocate(this%prigent_rghn(0)) + end if + this%prigent_rghn(:) = nan end subroutine InitAllocate From 3291040ae8d1fbe9fbf60d4e2d97f177cfba1983 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 13 Feb 2024 12:54:44 -0700 Subject: [PATCH 032/406] Shorten many lines (will help with nag compiler), add extra error checking around making sure namelist is read in and stream initialization has been done, this builds and runs --- .../share_esmf/PrigentRoughnessStreamType.F90 | 105 +++++++++++++----- 1 file changed, 79 insertions(+), 26 deletions(-) diff --git a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 index dec9fa86fd..734c26ea8a 100644 --- a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 +++ b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 @@ -6,7 +6,7 @@ module PrigentRoughnessStreamType ! Contains methods for reading in the Prigent et al. (1997) roughness length streams file ! Created by Danny M. Leung 22 Nov 2022 ! !USES - use ESMF + use ESMF use dshr_strdata_mod , only : shr_strdata_type use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_cl use shr_log_mod , only : errMsg => shr_log_errMsg @@ -20,13 +20,14 @@ module PrigentRoughnessStreamType private type, public :: prigentroughnessstream_type - real(r8), pointer, private :: prigent_rghn (:) ! Prigent et al. (1997) roughness length (m) + real(r8), pointer, public :: prigent_rghn (:) ! Prigent et al. (1997) roughness length (m) contains ! !PUBLIC MEMBER FUNCTIONS: procedure, public :: Init ! Initialize and read data in procedure, public :: CalcDragPartition ! Calculate drag partitioning based on input streams - procedure, public :: UseStreams ! If streams will be used -- dmleung set default as yes + procedure, public :: UseStreams ! If Prigent rougness streams will be used + procedure, public :: IsStreamInit ! If the streams have been initialized and read in, so data can be used ! !PRIVATE MEMBER FUNCTIONS: procedure, private :: InitAllocate ! Allocate data @@ -39,12 +40,14 @@ module PrigentRoughnessStreamType character(len=CL) :: stream_meshfile_prigentroughness ! mesh Filename character(len=CL) :: prigentroughnessmapalgo ! map algo contains - procedure, private :: ReadNML ! Read in namelist ! If will be used + procedure, private :: ReadNML ! Read in control namelist end type streamcontrol_type - type(streamcontrol_type), private :: control ! Stream control data + type(streamcontrol_type), private :: control ! Stream control data + logical , private :: NMLRead = .false. ! If namelist has been read + logical , private :: InitDone = .false. ! If initialization of streams has been done - character(len=*), parameter, private :: sourcefile = & + character(len=*), parameter, private :: sourcefile = & __FILE__ !============================================================================== @@ -80,6 +83,7 @@ subroutine Init(this, bounds, NLFilename) integer :: rc ! error code real(r8), pointer :: dataptr1d(:) ! temporary pointer character(len=*), parameter :: stream_name = 'prigent_roughness' + character(len=*), parameter :: subname = 'PrigentRoughnessStream::Init' !----------------------------------------------------------------------- call control%ReadNML( bounds, NLFileName ) @@ -138,7 +142,7 @@ subroutine Init(this, bounds, NLFilename) end if ! Get pointer for stream data that is time and spatially interpolate to model time and grid - do n = 1,size(stream_varnames) + do n = 1,size(stream_varnames) call dshr_fldbun_getFldPtr(sdat_rghn%pstrm(1)%fldbun_model, stream_varnames(n), fldptr1=dataptr1d, rc=rc) if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then call ESMF_Finalize(endflag=ESMF_END_ABORT) @@ -154,6 +158,7 @@ subroutine Init(this, bounds, NLFilename) end do deallocate(stream_varnames) + InitDone = .true. end if end subroutine Init @@ -170,7 +175,11 @@ logical function UseStreams(this) implicit none class(prigentroughnessstream_type) :: this ! - ! !LOCAL VARIABLES: + character(len=*), parameter :: subname = 'PrigentRoughnessStream::UseStreams' + ! + if ( .not. NMLRead )then + call endrun(msg=subname//' ERROR Namelist has NOT been read first, call Init before this') + end if if ( trim(control%stream_fldFileName_prigentroughness) == '' )then UseStreams = .false. ! Prigent streams are off without a filename given else @@ -178,6 +187,30 @@ logical function UseStreams(this) end if end function UseStreams + !============================================================================== + logical function IsStreamInit(this) + ! + ! !DESCRIPTION: + ! Return true if the streams have been initialized + ! + ! !USES: + ! + ! !ARGUMENTS: + implicit none + class(prigentroughnessstream_type) :: this + ! + character(len=*), parameter :: subname = 'PrigentRoughnessStream::IsStreamInit' + ! + if ( .not. NMLRead )then + call endrun(msg=subname//' ERROR Namelist has NOT been read first, call Init before this') + end if + if ( InitDone )then + IsStreamInit = .true. + else + IsStreamInit = .false. + end if + end function IsStreamInit + !============================================================================== subroutine InitAllocate(this, bounds) ! @@ -212,13 +245,13 @@ subroutine CalcDragPartition(this, bounds, dpfct_rock) ! ! !DESCRIPTION: ! Commented below by Danny M. Leung 31 Dec 2022 - ! Calculate the drag partition effect of friction velocity due to surface roughness following - ! Leung et al. (2022). This module is used in the dust emission module DUSTMod.F90 for - ! calculating drag partitioning. The drag partition equation comes from Marticorena and - ! Bergametti (1995) with constants modified by Darmenova et al. (2009). Here it is assumed + ! Calculate the drag partition effect of friction velocity due to surface roughness following + ! Leung et al. (2022). This module is used in the dust emission module DUSTMod.F90 for + ! calculating drag partitioning. The drag partition equation comes from Marticorena and + ! Bergametti (1995) with constants modified by Darmenova et al. (2009). Here it is assumed ! that this equation is used only over arid/desertic regions, such that Catherine Prigent's ! roughness measurements represents mostly rocks. For more vegetated areas, the vegetation - ! roughness and drag partitioning are calculated in the DustEmission subroutine. This + ! roughness and drag partitioning are calculated in the DustEmission subroutine. This ! subroutine is used in the InitCold subroutine of DUSTMod.F90. ! ! !USES: @@ -228,29 +261,42 @@ subroutine CalcDragPartition(this, bounds, dpfct_rock) ! ! !ARGUMENTS: implicit none - class(prigentroughnessstream_type) :: this - type(bounds_type) , intent(in) :: bounds - real(r8) , intent(inout) :: dpfct_rock(bounds%begp:) ! [fraction] rock drag partition factor (roughness effect) + class(prigentroughnessstream_type) :: this + type(bounds_type) , intent(in) :: bounds + real(r8) , intent(inout) :: dpfct_rock(bounds%begp:) ! [fraction] rock drag partition factor (roughness effect) ! ! !LOCAL VARIABLES: integer :: g, p, fp, l ! Indices real(r8) :: z0s ! smooth roughness length (m) - + ! constants - real(r8), parameter :: D_p = 130e-6_r8 ! [m] Medium soil particle diameter, assuming a global constant of ~130 um following Leung et al. (2022) - real(r8), parameter :: X = 10_r8 ! [m] distance downwind of the roughness element (rock). Assume estimating roughness effect at a distance of 10 m following Leung et al. (2022) + real(r8), parameter :: D_p = 130e-6_r8 ! [m] Medium soil particle diameter, assuming a global constant + ! of ~130 um following Leung et al. (2022) + real(r8), parameter :: X = 10_r8 ! [m] distance downwind of the roughness element (rock). Assume + ! estimating roughness effect at a distance of 10 m following Leung et al. (2022) + character(len=*), parameter :: subname = 'PrigentRoughnessStream::CalcDragPartition' !--------------------------------------------------------------------- SHR_ASSERT_ALL_FL((ubound(dpfct_rock) == (/bounds%endp/)), sourcefile, __LINE__) + ! Make sure we've been initialized + if ( .not. this%IsStreamInit() )then + call endrun(msg=subname//' ERROR Streams have not been initialized, make sure Init is called first' & + //', and streams are on') + end if - ! dmleung: this loop calculates the drag partition effect (or roughness effect) of rocks. We save the drag partition factor as a patch level quantity. - z0s = 2_r8 * D_p / 30_r8 ! equation from Frank M. White (2006). Here we assume soil medium size is a global constant, and so is smooth roughness length. + ! dmleung: this loop calculates the drag partition effect (or roughness effect) of rocks. + ! We save the drag partition factor as a patch level quantity. + ! TODO: EBK 02/13/2024: Several magic numbers here that should become parameters so the meaning is preserved + z0s = 2_r8 * D_p / 30_r8 ! equation from Frank M. White (2006). + ! Here we assume soil medium size is a global constant, and so is smooth roughness length. do p = bounds%begp,bounds%endp g = patch%gridcell(p) l = patch%landunit(p) if (lun%itype(l) /= istdlak) then - dpfct_rock(p) = 1._r8 - ( log(this%prigent_rghn(g)*0.01_r8/z0s) / log(0.7_r8*(X/z0s)**0.8_r8) ) ! Calculating rock drag partition factor using Marticorena and Bergametti (1995). 0.01 is used to convert Z0a from centimeter to meter. + ! Calculating rock drag partition factor using Marticorena and Bergametti (1995). + ! 0.01 is used to convert Z0a from centimeter to meter. + dpfct_rock(p) = 1._r8 - ( log(this%prigent_rghn(g)*0.01_r8/z0s) / log(0.7_r8*(X/z0s)**0.8_r8) ) end if end do @@ -312,17 +358,21 @@ subroutine ReadNML(this, bounds, NLFilename) ! Error checking if ( use_prigent_roughness == .false. )then if ( len_trim(stream_fldFileName_prigentroughness) /= 0 )then - call endrun(msg=' ERROR stream_fldFileName_prigentroughness is set, but use_prigent_roughness is FALSE'//errMsg(sourcefile, __LINE__)) + call endrun(msg=' ERROR stream_fldFileName_prigentroughness is set, but use_prigent_roughness is FALSE' & + //errMsg(sourcefile, __LINE__)) end if if ( len_trim(stream_meshfile_prigentroughness) /= 0 )then - call endrun(msg=' ERROR stream_meshfile_prigentroughness is set, but use_prigent_roughness is FALSE'//errMsg(sourcefile, __LINE__)) + call endrun(msg=' ERROR stream_meshfile_prigentroughness is set, but use_prigent_roughness is FALSE' & + //errMsg(sourcefile, __LINE__)) end if else if ( len_trim(stream_fldFileName_prigentroughness) == 0 )then - call endrun(msg=' ERROR stream_fldFileName_prigentroughness is NOT set, but use_prigent_roughness is TRUE'//errMsg(sourcefile, __LINE__)) + call endrun(msg=' ERROR stream_fldFileName_prigentroughness is NOT set, but use_prigent_roughness is TRUE' & + //errMsg(sourcefile, __LINE__)) end if if ( len_trim(stream_meshfile_prigentroughness) == 0 )then - call endrun(msg=' ERROR stream_meshfile_prigentroughness is NOT set, but use_prigent_roughness is TRUE'//errMsg(sourcefile, __LINE__)) + call endrun(msg=' ERROR stream_meshfile_prigentroughness is NOT set, but use_prigent_roughness is TRUE' & + //errMsg(sourcefile, __LINE__)) end if end if @@ -340,6 +390,9 @@ subroutine ReadNML(this, bounds, NLFilename) this%stream_meshfile_prigentroughness = stream_meshfile_prigentroughness this%prigentroughnessmapalgo = prigentroughnessmapalgo + ! Mark namelist read as having been done + NMLRead = .true. + end subroutine ReadNML end module PrigentRoughnessStreamType From 2886a18151b35cf6ee4c60433ee696a2ecce4327 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 13 Feb 2024 15:35:29 -0700 Subject: [PATCH 033/406] Move CalcDragPartition to DUSTMod from PrigentRoughnessStream object --- src/biogeochem/DUSTMod.F90 | 75 +++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index f3b3a0cd68..e3f9dd669c 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -84,7 +84,7 @@ module DUSTMod real(r8), pointer, private :: lai_patch (:) ! [m2 leaf /m2 land] LAI+SAI for calculating Okin's drag partition, averaged to landunit level real(r8), pointer, private :: frc_thr_rghn_fct_patch (:) ! [dimless] hybrid drag partition (or called roughness) factor real(r8), pointer, private :: wnd_frc_thr_std_patch (:) ! standardized fluid threshold friction velocity (m/s) - type(prigentroughnessstream_type), private :: prigentroughnessstream ! Prigent roughness stream data + type(prigentroughnessstream_type), private :: prigent_roughness_stream ! Prigent roughness stream data real(r8), pointer, private :: dpfct_rock_patch (:) ! [fraction] rock drag partition factor, time-constant contains @@ -92,7 +92,8 @@ module DUSTMod procedure , private :: InitAllocate procedure , private :: InitHistory procedure , private :: InitCold - procedure , private :: InitDustVars ! Initialize variables used in subroutine Dust + procedure , private :: InitDustVars ! Initialize variables used in subroutine Dust + procedure , private :: CalcDragPartition ! Calculate drag partitioning based on Prigent roughness stream end type dust_type !------------------------------------------------------------------------ @@ -111,7 +112,7 @@ subroutine Init(this, bounds, NLFilename) call this%InitAllocate (bounds) call this%InitHistory (bounds) - call this%prigentroughnessstream%Init( bounds, NLFilename ) + call this%prigent_roughness_stream%Init( bounds, NLFilename ) call this%InitCold (bounds) call this%InitDustVars (bounds) @@ -315,9 +316,8 @@ subroutine InitCold(this, bounds) end do ! Caulculate Drag Partition factor - if ( this%prigentroughnessstream%useStreams() )then !if usestreams == true, and it should be always true - call this%prigentroughnessstream%CalcDragPartition( bounds, & - this%dpfct_rock_patch(bounds%begp:bounds%endp) ) + if ( this%prigent_roughness_stream%useStreams() )then !if usestreams == true, and it should be always true + call this%CalcDragPartition( bounds, this%prigent_roughness_stream ) else call endrun( "ERROR:: Drag partitioning MUST now use a streams file of aeolian roughness length to calculate, it can no longer read from the fsurdat file" ) @@ -1219,4 +1219,67 @@ subroutine InitDustVars(this, bounds) end subroutine InitDustVars + !============================================================================== + subroutine CalcDragPartition(this, bounds, prigent_roughness_stream ) + ! + ! !DESCRIPTION: + ! Commented below by Danny M. Leung 31 Dec 2022 + ! Calculate the drag partition effect of friction velocity due to surface roughness following + ! Leung et al. (2022). This module is used in the dust emission module DUSTMod.F90 for + ! calculating drag partitioning. The drag partition equation comes from Marticorena and + ! Bergametti (1995) with constants modified by Darmenova et al. (2009). Here it is assumed + ! that this equation is used only over arid/desertic regions, such that Catherine Prigent's + ! roughness measurements represents mostly rocks. For more vegetated areas, the vegetation + ! roughness and drag partitioning are calculated in the DustEmission subroutine. This + ! subroutine is used in the InitCold subroutine of DUSTMod.F90. + ! + ! !USES: + use PatchType , only : patch + use landunit_varcon , only : istdlak + use LandunitType , only : lun + ! + ! !ARGUMENTS: + implicit none + class(dust_type) , intent(inout) :: this + type(bounds_type), intent(in) :: bounds + type(prigentroughnessstream_type), intent(in) :: prigent_roughness_stream + ! + ! !LOCAL VARIABLES: + integer :: g, p, fp, l ! Indices + real(r8) :: z0s ! smooth roughness length (m) + + ! constants + real(r8), parameter :: D_p = 130e-6_r8 ! [m] Medium soil particle diameter, assuming a global constant + ! of ~130 um following Leung et al. (2022) + real(r8), parameter :: X = 10_r8 ! [m] distance downwind of the roughness element (rock). Assume + ! estimating roughness effect at a distance of 10 m following Leung et al. (2022) + character(len=*), parameter :: subname = 'PrigentRoughnessStream::CalcDragPartition' + !--------------------------------------------------------------------- + + ! Make sure we've been initialized + if ( .not. prigent_roughness_stream%IsStreamInit() )then + call endrun(msg=subname//' ERROR Streams have not been initialized, make sure Init is called first' & + //', and streams are on') + end if + + ! dmleung: this loop calculates the drag partition effect (or roughness effect) of rocks. + ! We save the drag partition factor as a patch level quantity. + ! TODO: EBK 02/13/2024: Several magic numbers here that should become parameters so the meaning is preserved + z0s = 2_r8 * D_p / 30_r8 ! equation from Frank M. White (2006). + ! Here we assume soil medium size is a global constant, and so is smooth roughness length. + do p = bounds%begp,bounds%endp + g = patch%gridcell(p) + l = patch%landunit(p) + if (lun%itype(l) /= istdlak) then + ! Calculating rock drag partition factor using Marticorena and Bergametti (1995). + ! 0.01 is used to convert Z0a from centimeter to meter. + this%dpfct_rock_patch(p) = 1._r8 - ( log(prigent_roughness_stream%prigent_rghn(g)*0.01_r8/z0s) & + / log(0.7_r8*(X/z0s)**0.8_r8) ) + end if + end do + + end subroutine CalcDragPartition + + !============================================================================== + end module DUSTMod From e2123b0215d8228e03f9f12d4b47ed34a3106bfe Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 13 Feb 2024 16:02:33 -0700 Subject: [PATCH 034/406] Move CalcDragPartition to DUSTMod from PrigentRoughnessStream object --- .../share_esmf/PrigentRoughnessStreamType.F90 | 73 ++----------------- 1 file changed, 5 insertions(+), 68 deletions(-) diff --git a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 index 734c26ea8a..751fe8458c 100644 --- a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 +++ b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 @@ -1,5 +1,5 @@ module PrigentRoughnessStreamType -#include "shr_assert.h" + !----------------------------------------------------------------------- ! !DESCRIPTION: @@ -25,7 +25,6 @@ module PrigentRoughnessStreamType ! !PUBLIC MEMBER FUNCTIONS: procedure, public :: Init ! Initialize and read data in - procedure, public :: CalcDragPartition ! Calculate drag partitioning based on input streams procedure, public :: UseStreams ! If Prigent rougness streams will be used procedure, public :: IsStreamInit ! If the streams have been initialized and read in, so data can be used @@ -101,15 +100,15 @@ subroutine Init(this, bounds, NLFilename) end if ! Initialize the cdeps data type sdat_rghn - call shr_strdata_init_from_inline(sdat_rghn, & + call shr_strdata_init_from_inline(sdat_rghn, & my_task = iam, & logunit = iulog, & compname = 'LND', & model_clock = model_clock, & model_mesh = mesh, & - stream_meshfile = control%stream_meshfile_prigentroughness, & + stream_meshfile = control%stream_meshfile_prigentroughness, & stream_lev_dimname = 'null', & - stream_mapalgo = control%prigentroughnessmapalgo, & + stream_mapalgo = control%prigentroughnessmapalgo, & stream_filenames = (/trim(control%stream_fldFileName_prigentroughness)/), & stream_fldlistFile = stream_varnames, & stream_fldListModel = stream_varnames, & @@ -120,7 +119,7 @@ subroutine Init(this, bounds, NLFilename) stream_taxmode = 'extend', & stream_dtlimit = 1.0e30_r8, & stream_tintalgo = 'linear', & - stream_name = 'Prigent roughness', & + stream_name = 'Prigent roughness', & rc = rc) if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then call ESMF_Finalize(endflag=ESMF_END_ABORT) @@ -240,68 +239,6 @@ subroutine InitAllocate(this, bounds) end subroutine InitAllocate - !============================================================================== - subroutine CalcDragPartition(this, bounds, dpfct_rock) - ! - ! !DESCRIPTION: - ! Commented below by Danny M. Leung 31 Dec 2022 - ! Calculate the drag partition effect of friction velocity due to surface roughness following - ! Leung et al. (2022). This module is used in the dust emission module DUSTMod.F90 for - ! calculating drag partitioning. The drag partition equation comes from Marticorena and - ! Bergametti (1995) with constants modified by Darmenova et al. (2009). Here it is assumed - ! that this equation is used only over arid/desertic regions, such that Catherine Prigent's - ! roughness measurements represents mostly rocks. For more vegetated areas, the vegetation - ! roughness and drag partitioning are calculated in the DustEmission subroutine. This - ! subroutine is used in the InitCold subroutine of DUSTMod.F90. - ! - ! !USES: - use PatchType , only : patch - use landunit_varcon , only : istdlak - use LandunitType , only : lun - ! - ! !ARGUMENTS: - implicit none - class(prigentroughnessstream_type) :: this - type(bounds_type) , intent(in) :: bounds - real(r8) , intent(inout) :: dpfct_rock(bounds%begp:) ! [fraction] rock drag partition factor (roughness effect) - ! - ! !LOCAL VARIABLES: - integer :: g, p, fp, l ! Indices - real(r8) :: z0s ! smooth roughness length (m) - - ! constants - real(r8), parameter :: D_p = 130e-6_r8 ! [m] Medium soil particle diameter, assuming a global constant - ! of ~130 um following Leung et al. (2022) - real(r8), parameter :: X = 10_r8 ! [m] distance downwind of the roughness element (rock). Assume - ! estimating roughness effect at a distance of 10 m following Leung et al. (2022) - character(len=*), parameter :: subname = 'PrigentRoughnessStream::CalcDragPartition' - !--------------------------------------------------------------------- - - SHR_ASSERT_ALL_FL((ubound(dpfct_rock) == (/bounds%endp/)), sourcefile, __LINE__) - - ! Make sure we've been initialized - if ( .not. this%IsStreamInit() )then - call endrun(msg=subname//' ERROR Streams have not been initialized, make sure Init is called first' & - //', and streams are on') - end if - - ! dmleung: this loop calculates the drag partition effect (or roughness effect) of rocks. - ! We save the drag partition factor as a patch level quantity. - ! TODO: EBK 02/13/2024: Several magic numbers here that should become parameters so the meaning is preserved - z0s = 2_r8 * D_p / 30_r8 ! equation from Frank M. White (2006). - ! Here we assume soil medium size is a global constant, and so is smooth roughness length. - do p = bounds%begp,bounds%endp - g = patch%gridcell(p) - l = patch%landunit(p) - if (lun%itype(l) /= istdlak) then - ! Calculating rock drag partition factor using Marticorena and Bergametti (1995). - ! 0.01 is used to convert Z0a from centimeter to meter. - dpfct_rock(p) = 1._r8 - ( log(this%prigent_rghn(g)*0.01_r8/z0s) / log(0.7_r8*(X/z0s)**0.8_r8) ) - end if - end do - - end subroutine CalcDragPartition - !============================================================================== subroutine ReadNML(this, bounds, NLFilename) ! From 3e9d6b67c2af06cd2a9d699640ee3d242245cc01 Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Tue, 20 Feb 2024 00:37:15 -0700 Subject: [PATCH 035/406] dmleung fixed many of ekluzek's comments: defining magic values into physical parameters, changing numbers into double precisions, and putting more comments into the code. 20 Feb 2024 --- src/biogeochem/DUSTMod.F90 | 117 +++++++++++-------- src/biogeophys/SoilStateInitTimeConstMod.F90 | 4 +- 2 files changed, 68 insertions(+), 53 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index e3f9dd669c..275f0a4c10 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -228,7 +228,7 @@ subroutine InitHistory(this, bounds) ptr_patch=this%lnd_frc_mble_patch, set_lake=0._r8, set_urb=0._r8) this%gwc_patch(begp:endp) = spval call hist_addfld1d (fname='GWC', units='kg/kg', & - avgflag='A', long_name='gravimetric water content', & + avgflag='A', long_name='gravimetric soil moisture at the topmost soil layer', & ptr_patch=this%gwc_patch, set_lake=0._r8, set_urb=0._r8) this%liq_frac_patch(begp:endp) = spval call hist_addfld1d (fname='LIQ_FRAC', units='dimensionless', & @@ -276,7 +276,7 @@ subroutine InitHistory(this, bounds) ptr_patch=this%ssr_patch, set_lake=0._r8, set_urb=0._r8) this%lai_patch(begp:endp) = spval call hist_addfld1d (fname='LAI', units='m/s', & - avgflag='A', long_name='landunit-mean LAI for Okin-Pierre scheme', & + avgflag='A', long_name='leaf area index for Okin-Pierre scheme', & ptr_patch=this%lai_patch, set_lake=0._r8, set_urb=0._r8) this%frc_thr_rghn_fct_patch(begp:endp) = spval call hist_addfld1d (fname='FRC_THR_RGHN_FCT', units='dimensionless', & @@ -395,7 +395,7 @@ subroutine DustEmission (bounds, & real(r8), parameter :: flx_mss_fdg_fct = 5.0e-4_r8 ! [frc] Empir. mass flx tuning eflx_lh_vegt !real(r8), parameter :: vai_mbl_thr = 0.3_r8 ! [m2 m-2] VAI threshold quenching dust mobilization character(len=*),parameter :: subname = 'DUSTEmission' - real(r8), parameter :: vai_mbl_thr = 1.0_r8 ! [m2 m-2] new VAI threshold; Danny M. Leung suggests 1, and the old 0.3 seems a bit too small + real(r8), parameter :: vai_mbl_thr = 1.0_r8 ! [m2 m-2] new VAI threshold; Danny M. Leung suggests 1, and Zender's scheme uses 0.3 real(r8), parameter :: Cd0 = 4.4e-5_r8 ! [dimless] proportionality constant in calculation of dust emission coefficient real(r8), parameter :: Ca = 2.7_r8 ! [dimless] proportionality constant in scaling of dust emission exponent real(r8), parameter :: Ce = 2.0_r8 ! [dimless] proportionality constant scaling exponential dependence of dust emission coefficient on standardized soil threshold friction speed @@ -407,6 +407,12 @@ subroutine DustEmission (bounds, & real(r8), parameter :: k = 0.4_r8 ! [dimless] von Karman constant real(r8), parameter :: f_0 = 0.32_r8 ! [dimless] SSR in the immediate lee of a plant real(r8), parameter :: c_e = 4.8_r8 ! [dimless] e-folding distance velocity recovery + real(r8), parameter :: D_p = 130e-6_r8 ! [m] Medium soil particle diameter, assuming a global constant of ~130 um following Leung et al. (2023). dmleung 16 Feb 2024 + real(r8), parameter :: gamma_Shao = 1.65e-4_r8 ! [kg s-2] interparticle cohesion: fitting parameter in Shao and Lu (2000) (S&L00). dmleung 16 Feb 2024 + real(r8), parameter :: A_Shao = 0.0123_r8 ! [dimless] coefficient for aerodynamic force: fitting parameter in Shao and Lu (2000). dmleung 16 Feb 2024 + real(r8), parameter :: frag_expt_thr = 5.0_r8 ! [dimless] threshold for fragmentation exponent defined in Leung et al. (2023), somewhere within 3 to 5. It is used to prevent a local AOD blowup (over Patagonia, Argentina), but one can test larger values and relax the threshold if wanted. dmleung 16 Feb 2024 + real(r8), parameter :: z0a_glob = 1e-4 ! [m] assumed globally constant aeolian roughness length value in Leung et al. (2023), for the log law of the wall for Comola et al. (2019) intermittency scheme. dmleung 20 Feb 2024 + real(r8), parameter :: hgt_sal = 0.1 ! [m] saltation height used by Comola et al. (2019) intermittency scheme for the log law of the wall. dmleung 20 Feb 2024 real(r8) :: numer ! Numerator term for threshold crossing rate real(r8) :: denom ! Denominator term for threshold crossing rate !------------------------------------------------------------------------ @@ -432,28 +438,28 @@ subroutine DustEmission (bounds, & mbl_bsn_fct => dust_inst%mbl_bsn_fct_col , & ! Input: [real(r8) (:) ] basin factor flx_mss_vrt_dst => dust_inst%flx_mss_vrt_dst_patch , & ! Output: [real(r8) (:,:) ] surface dust emission (kg/m**2/s) flx_mss_vrt_dst_tot => dust_inst%flx_mss_vrt_dst_tot_patch , & ! Output: [real(r8) (:) ] total dust flux back to atmosphere (pft) - + ! below variables are defined in Kok et al. (2014) or (mostly) Leung et al. (2023) dust emission scheme. dmleung 16 Feb 2024 dst_emiss_coeff => dust_inst%dst_emiss_coeff_patch , & ! Output dust emission coefficient wnd_frc_thr => dust_inst%wnd_frc_thr_patch , & ! output impact threshold wnd_frc_thr_dry => dust_inst%wnd_frc_thr_dry_patch , & ! output dry threshold lnd_frc_mble => dust_inst%lnd_frc_mble_patch , & ! output bare land fraction wnd_frc_soil => dust_inst%wnd_frc_soil_patch , & ! soil friction velocity u_*s = (u_*)(f_eff) gwc => dust_inst%gwc_patch , & ! output gravimetric water content - liq_frac => dust_inst%liq_frac_patch , & - intrmtncy_fct => dust_inst%intrmtncy_fct_patch , & - stblty => dust_inst%stblty_patch , & - u_mean_slt => dust_inst%u_mean_slt_patch , & - u_sd_slt => dust_inst%u_sd_slt_patch , & - u_fld_thr => dust_inst%u_fld_thr_patch , & - u_impct_thr => dust_inst%u_impct_thr_patch , & - thr_crs_rate => dust_inst%thr_crs_rate_patch , & - prb_crs_fld_thr => dust_inst%prb_crs_fld_thr_patch , & - prb_crs_impct_thr => dust_inst%prb_crs_impct_thr_patch , & - ssr => dust_inst%ssr_patch , & - lai => dust_inst%lai_patch , & - frc_thr_rghn_fct => dust_inst%frc_thr_rghn_fct_patch , & - wnd_frc_thr_std => dust_inst%wnd_frc_thr_std_patch , & - dpfct_rock => dust_inst%dpfct_rock_patch & + liq_frac => dust_inst%liq_frac_patch , & ! output fraction of liquid moisture + intrmtncy_fct => dust_inst%intrmtncy_fct_patch , & ! output intermittency factor eta (fraction of time that dust emission is active within a timestep) + stblty => dust_inst%stblty_patch , & ! stability in similarity theory (no need to output) + u_mean_slt => dust_inst%u_mean_slt_patch , & ! output mean wind speed at 0.1 m height translated from friction velocity using the log law of the wall, assuming neutral condition + u_sd_slt => dust_inst%u_sd_slt_patch , & ! output standard deviation of wind speed from similarity theory + u_fld_thr => dust_inst%u_fld_thr_patch , & ! output fluid threshold wind speed at 0.1 m height translated from the log law of the wall + u_impct_thr => dust_inst%u_impct_thr_patch , & ! output impact threshold wind speed at 0.1 m height translated from the log law of the wall + thr_crs_rate => dust_inst%thr_crs_rate_patch , & ! output threshold crossing rate in Comola 2019 intermittency parameterization + prb_crs_fld_thr => dust_inst%prb_crs_fld_thr_patch , & ! output probability of instantaneous wind crossing fluid threshold in Comola 2019 intermittency parameterization + prb_crs_impct_thr => dust_inst%prb_crs_impct_thr_patch , & ! output probability of instantaneous wind crossing impact threshold in Comola 2019 intermittency parameterization + ssr => dust_inst%ssr_patch , & ! output vegetation drag partition factor in Okin 2008 vegetation roughness effect (called shear stress ratio, SSR in Okin 2008) + lai => dust_inst%lai_patch , & ! leaf area index for calculating vegetation drag partitioning. lai=0 in the ssr equation will lead to infinity, so for now a small value is added into this lai dmleung defined. (no need to output) 16 Feb 2024 + frc_thr_rghn_fct => dust_inst%frc_thr_rghn_fct_patch , & ! output hybrid/total drag partition factor considering both rock and vegetation drag partition factors. + wnd_frc_thr_std => dust_inst%wnd_frc_thr_std_patch , & ! standardized dust emission threshold friction velocity defined in Jasper Kok et al. (2014). + dpfct_rock => dust_inst%dpfct_rock_patch & ! output rock drag partition factor defined in Marticorena and Bergametti 1995. A fraction between 0 and 1. ) ttlai(bounds%begp : bounds%endp) = 0._r8 @@ -570,10 +576,17 @@ subroutine DustEmission (bounds, & !-------------------------------------------------------------------------------------------------- ! put dust emission calculation here to output threshold friction velocity for the whole globe, ! not just when lnd_frc_mbl = 0. Danny M. Leung 27 Nov 2021 - bd = (1._r8-watsat(c,1))*2.7e3_r8 ![kg m-3] Bulk density of dry surface soil + + !#################################################################################################### + ! calculate soil moisture effect for dust emission threshold + ! following Fecan, Marticorena et al. (1999) + ! also see Zender et al. (2003) for DEAD emission scheme and Kok et al. (2014b) for K14 emission scheme in CESM + bd = (1._r8-watsat(c,1))*dns_slt ![kg m-3] Bulk density of dry surface soil (dmleung changed from 2700 to dns_slt, soil particle density, on 16 Feb 2024. Note that dns_slt=2650 kg m-3 so the value is changed by a tiny bit from 2700 to 2650. dns_slt has been here for many years so dns_slt should be used here instead of explicitly tpying the value out. dmleung 16 Feb 2024) + + ! Here convert h2osoi_vol (H2OSOI) at the topmost CTSM soil layer from volumetric (m3 water / m3 soil) to gravimetric soil moisture (kg water / kg soil) gwc_sfc = h2osoi_vol(c,1)*SHR_CONST_RHOFW/bd ![kg kg-1] Gravimetric H2O cont if (gwc_sfc > gwc_thr(c)) then - frc_thr_wet_fct = sqrt(1.0_r8 + 1.21_r8 * (100.0_r8*(gwc_sfc - gwc_thr(c)))**0.68_r8) + frc_thr_wet_fct = sqrt(1.0_r8 + 1.21_r8 * (100.0_r8*(gwc_sfc - gwc_thr(c)))**0.68_r8) ! dmleung's comment: this is an empirical equation by Fecan, Marticorena et al. (1999) on relating the soil moisture factor on enhancing dust emission threshold to gravimetric soil moisture. 1.21 and 0.68 are fitting parameters in the regression done by Fecan; 100 is to convert gracimetric soil moisture from fraction (kg water / kg soil) to percentage. Note that gwc_thr was defined in SoilStateInitConst.F90 as a fraction. 1.0_r8 means there is no soil moisture effect on enhancing dust emission threhsold. dmleung 16 Feb 2024. else frc_thr_wet_fct = 1.0_r8 end if @@ -583,17 +596,17 @@ subroutine DustEmission (bounds, & ! slevis: adding liqfrac here, because related to effects from soil water liqfrac = max( 0.0_r8, min( 1.0_r8, h2osoi_liq(c,1) / (h2osoi_ice(c,1)+h2osoi_liq(c,1)+1.0e-6_r8) ) ) - ! output liquid fraction + ! dmleung: output liquid fraction liq_frac(p) = liqfrac !####################################################################################################### ! calculate Shao & Lu (2000) dust emission threshold scheme here ! use tmp1 from DUSTini for Iversen and White I&W (1982) (~75 um is optimal); use tmp2 for S&L (2000) (~80 um is optimal) + ! see Danny M. Leung et al. (2023) !####################################################################################################### - - tmp2 = 1.0_r8*sqrt(0.0123_r8 * (dns_slt*grav*130.0e-6_r8 + 1.65e-4_r8/130.0e-6_r8)) ! calculate S&L (2000) scheme here for threshold; gamma = 1.65e-4 following S&L00, D_p = 127 um ~ 130 um following Leung et al. (2022). As this is a global constant, this line can be put outside the loop to save computational power. - wnd_frc_thr_dry(p) = tmp2 / sqrt(forc_rho(c)) ! output dry fluid threshold - wnd_frc_thr_slt = tmp2 / sqrt(forc_rho(c)) * frc_thr_wet_fct !* frc_thr_rgh_fct ! fluid threshold + tmp2 = sqrt(A_Shao * (dns_slt*grav*D_p + gamma_Shao/D_p)) ! calculate S&L (2000) scheme here for threshold; gamma = 1.65e-4 following S&L00, D_p = 127 um ~ 130 um following Leung et al. (2023). dmleung use defined parameters instead of typing numerical values 16 Feb 2024 + wnd_frc_thr_dry(p) = tmp2 / sqrt(forc_rho(c)) ! dry fluid threshold + wnd_frc_thr_slt = tmp2 / sqrt(forc_rho(c)) * frc_thr_wet_fct !* frc_thr_rgh_fct ! fluid threshold. dmleung commented out frc_thr_rgh_fct since it is used to modify the wind, not the wind threshold. wnd_frc_thr_slt_it = B_it * tmp2 / sqrt(forc_rho(c)) ! define impact threshold ! the above formula is true for Iversen and White (1982) and Shao and Lu (2000) scheme @@ -606,13 +619,13 @@ subroutine DustEmission (bounds, & ! framentation exponent frag_expt = (Ca * (wnd_frc_thr_slt_std - wnd_frc_thr_slt_std_min) / wnd_frc_thr_slt_std_min) ! fragmentation exponent, defined in Kok et al. (2014a) - if (frag_expt > 5.0_r8) then ! set fragmentation exponent to be 3 or 5 at maximum, to avoid local AOD blowup - frag_expt = 5.0_r8 + if (frag_expt > frag_expt_thr) then ! set fragmentation exponent to be 3 or 5 at maximum, to avoid local AOD blowup + frag_expt = frag_expt_thr end if !################ drag partition effect, and soil friction velocity ########################### ! subsection on computing vegetation drag partition and hybrid drag partition factors - ! in Leung et al. (2022), drag partition effect is applied on the wind instead of the threshold + ! in Leung et al. (2023), drag partition effect is applied on the wind instead of the threshold !############################################################################################## ! the following comes from subr. frc_thr_rgh_fct_get ! purpose: compute factor by which surface roughness increases threshold @@ -620,7 +633,7 @@ subroutine DustEmission (bounds, & if (lnd_frc_mbl(p) > 0.0_r8 .AND. tlai_lu(l)<=1_r8) then ! vegetation drag partition equation following Gregory Okin (2008) + Caroline Pierre et al. (2014) - lai(p) = tlai_lu(l)+0.1_r8 ! LAI+SAI averaged to landunit level; the equation is undefined at lai=0 so we add in a small number. Then lai is saved for output + lai(p) = tlai_lu(l)+0.01_r8 ! LAI+SAI averaged to landunit level; the equation is undefined at lai=0 so we add in a small number. if (lai(p) > 1_r8) then lai(p) = 1_r8 ! setting LAI ~ 0.1 to be a min value (since the value goes to infinity when LAI=0) end if ! and 1 to be a max value as computing K involves 1 / LAI @@ -629,7 +642,7 @@ subroutine DustEmission (bounds, & K_length = 2_r8 * (1_r8/lai(p) - 1_r8) ! Here LAI has to be non-zero to avoid blowup ssr(p) = (K_length+f_0*c_e)/(K_length+c_e) - ! calculation of drag partition effect using LUH2 bare and veg fractions within a grid + ! calculation of the hybrid/total drag partition effect considering both rock and vegetation drag partitioning using LUH2 bare and veg fractions within a grid if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then if (patch%itype(p) == noveg) then frc_thr_rgh_fct = dpfct_rock(p) @@ -646,7 +659,7 @@ subroutine DustEmission (bounds, & else wnd_frc_slt = fv(p) ! The value here is not important since once lnd_frc_mbl(p) <= 0.0_r8 there will be no emission. - frc_thr_rghn_fct(p) = 0.0_r8 ! save and output hybrid drag partition factor + frc_thr_rghn_fct(p) = 0.0_r8 ! When LAI > 1, the drag partition effect is zero. dmleung 16 Feb 2024. end if !########## end of drag partition effect ####################################################### @@ -674,7 +687,7 @@ subroutine DustEmission (bounds, & if (wnd_frc_slt > wnd_frc_thr_slt_it) then! if using Leung's scheme, use impact threshold for dust emission equation - !################### for Leung et al. (2022) ################################################ + !################### for Leung et al. (2023) ################################################ !################ uncomment the below block if want to use Leung's scheme ################### flx_mss_vrt_dst_ttl(p) = dst_emiss_coeff(p) * mss_frc_cly_vld(c) * forc_rho(c) * ((wnd_frc_slt**2.0_r8 - wnd_frc_thr_slt_it**2.0_r8) / wnd_frc_thr_slt_std) * (wnd_frc_slt / wnd_frc_thr_slt_it)**frag_expt ! Leung et al. (2022) uses Kok et al. (2014) dust emission euqation for emission flux @@ -688,8 +701,8 @@ subroutine DustEmission (bounds, & ! subsection for intermittency factor calculation (only used by Leung's scheme, not Zender's scheme) ! 2 Dec 2021: assume no buoyancy contribution to the wind fluctuation (u_sd_slt), so no obul(p) is needed. It is shown to be important for the wind fluctuations contribute little to the intermittency factor. We might add this back in the future revisions. - ! mean lowpass-filtered wind speed at 0.1 m saltation height (assuming aerodynamic roughness length = 1e-4 m globally for ease; also assuming neutral condition) - u_mean_slt(p) = (wnd_frc_slt/k) * log(0.1_r8 / 1e-4_r8) ! translating from ustar (velocity scale) to actual wind + ! mean lowpass-filtered wind speed at hgt_sal = 0.1 m saltation height (assuming aerodynamic roughness length z0a_glob = 1e-4 m globally for ease; also assuming neutral condition) + u_mean_slt(p) = (wnd_frc_slt/k) * log(hgt_sal / z0a_glob) ! translating from ustar (velocity scale) to actual wind stblty(p) = 0 ! -dmleung 2 Dec 2021: use stability = 0 for now, assuming no buoyancy contribution. Might uncomment the above lines in future revisions. if ((12_r8 - 0.5_r8 * stblty(p)) .GE. 0.001_r8) then ! should have used 0 theoretically; used 0.001 here to avoid undefined values @@ -701,27 +714,27 @@ subroutine DustEmission (bounds, & ! threshold velocities ! Here wnd_frc_thr_slt is the fluid threshold; wnd_frc_thr_dry(p) is the dry fluid threshold; B_it*wnd_frc_thr_dry(p) is the impact threshold ! fluid threshold wind at 0.1 m saltation height - u_fld_thr(p) = (wnd_frc_thr_slt/k) * log(0.1_r8 / 1e-4_r8) + u_fld_thr(p) = (wnd_frc_thr_slt/k) * log(hgt_sal / z0a_glob) ! assume a globally constant z0a value for the log law of the wall, but it can be z0m from CLM or, better, z0a from Prigent's roughness dataset. Danny M. Leung et al. (2023) chose to assume a global constant z0a = 1e-4 m. dmleung 20 Feb 2024 ! impact threshold wind at 0.1 m saltation height - u_impct_thr(p) = (wnd_frc_thr_slt_it/k) * log(0.1_r8 / 1e-4_r8) ! to avoid model error + u_impct_thr(p) = (wnd_frc_thr_slt_it/k) * log(hgt_sal / z0a_glob) ! threshold crossing rate - numer = (u_fld_thr(p)**2_r8 - u_impct_thr(p)**2_r8 - 2_r8 * u_mean_slt(p) * (u_fld_thr(p) - u_impct_thr(p))) - denom = (2_r8 * u_sd_slt(p)**2_r8) + numer = (u_fld_thr(p)**2.0_r8 - u_impct_thr(p)**2.0_r8 - 2.0_r8 * u_mean_slt(p) * (u_fld_thr(p) - u_impct_thr(p))) + denom = (2.0_r8 * u_sd_slt(p)**2.0_r8) ! Truncate to zero if the expression inside exp is becoming too large if ( numer/denom < 30._r8 )then - thr_crs_rate(p) = (exp((u_fld_thr(p)**2_r8 - u_impct_thr(p)**2_r8 - 2_r8 * u_mean_slt(p) * (u_fld_thr(p) - u_impct_thr(p))) / (2_r8 * u_sd_slt(p)**2_r8)) + 1_r8)**(-1_r8) + thr_crs_rate(p) = (exp((u_fld_thr(p)**2.0_r8 - u_impct_thr(p)**2.0_r8 - 2.0_r8 * u_mean_slt(p) * (u_fld_thr(p) - u_impct_thr(p))) / (2.0_r8 * u_sd_slt(p)**2.0_r8)) + 1.0_r8)**(-1.0_r8) else thr_crs_rate(p) = 0.0_r8 end if ! probability that lowpass-filtered wind speed does not exceed u_ft - prb_crs_fld_thr(p) = 0.5_r8 * (1_r8 + erf((u_fld_thr(p) - u_mean_slt(p)) / (1.414_r8 * u_sd_slt(p)))) + prb_crs_fld_thr(p) = 0.5_r8 * (1.0_r8 + erf((u_fld_thr(p) - u_mean_slt(p)) / ( sqrt(2.0_r8) * u_sd_slt(p)))) ! probability that lowpass-filtered wind speed does not exceed u_it - prb_crs_impct_thr(p) = 0.5_r8 * (1_r8 + erf((u_impct_thr(p) - u_mean_slt(p)) / (1.414_r8 * u_sd_slt(p)))) + prb_crs_impct_thr(p) = 0.5_r8 * (1.0_r8 + erf((u_impct_thr(p) - u_mean_slt(p)) / ( sqrt(2.0_r8 * u_sd_slt(p)))) - ! intermittency factor (from 0 to 1) - intrmtncy_fct(p) = 1_r8 - prb_crs_fld_thr(p) + thr_crs_rate(p) * (prb_crs_fld_thr(p) - prb_crs_impct_thr(p)) + ! intermittency factor (eta; ranging from 0 to 1) + intrmtncy_fct(p) = 1.0_r8 - prb_crs_fld_thr(p) + thr_crs_rate(p) * (prb_crs_fld_thr(p) - prb_crs_impct_thr(p)) ! multiply dust emission flux by intermittency factor if (intrmtncy_fct(p) /= intrmtncy_fct(p)) then ! if intrmtncy_fct(p) is not NaN then multiply by intermittency factor; this statement is needed because dust emission flx_mss_vrt_dst_ttl(p) has to be non NaN (at least zero) to be outputted @@ -1250,31 +1263,33 @@ subroutine CalcDragPartition(this, bounds, prigent_roughness_stream ) ! constants real(r8), parameter :: D_p = 130e-6_r8 ! [m] Medium soil particle diameter, assuming a global constant - ! of ~130 um following Leung et al. (2022) + ! of ~130 um following Leung et al. (2023) real(r8), parameter :: X = 10_r8 ! [m] distance downwind of the roughness element (rock). Assume - ! estimating roughness effect at a distance of 10 m following Leung et al. (2022) + ! estimating roughness effect at a distance of 10 m following Leung et al. (2023) + real(r8), parameter :: b1 = 0.7_r8 ! [dimless] first fitting coefficient for the drag partition equation by Marticorena and Bergametti (1995), later modified by Darmenova et al. (2009). + real(r8), parameter :: b2 = 0.8_r8 ! [dimless] second fitting coefficient for the drag partition equation by Marticorena and Bergametti (1995), later modified by Darmenova et al. (2009). character(len=*), parameter :: subname = 'PrigentRoughnessStream::CalcDragPartition' !--------------------------------------------------------------------- - ! Make sure we've been initialized + ! Make sure we've initialized the Prigent roughness streams if ( .not. prigent_roughness_stream%IsStreamInit() )then - call endrun(msg=subname//' ERROR Streams have not been initialized, make sure Init is called first' & + call endrun(msg=subname//' ERROR: Streams have not been initialized, make sure Init is called first' & //', and streams are on') end if ! dmleung: this loop calculates the drag partition effect (or roughness effect) of rocks. ! We save the drag partition factor as a patch level quantity. ! TODO: EBK 02/13/2024: Several magic numbers here that should become parameters so the meaning is preserved - z0s = 2_r8 * D_p / 30_r8 ! equation from Frank M. White (2006). + z0s = 2_r8/30_r8 * D_p ! equation for smooth roughness length for soil grain. See Danny M. Leung et al. (2023) and Martin Klose et al. (2021) for instance. 1/15 is a coefficient that relates roughness to soil particle diameter D_p. ! Here we assume soil medium size is a global constant, and so is smooth roughness length. do p = bounds%begp,bounds%endp g = patch%gridcell(p) l = patch%landunit(p) if (lun%itype(l) /= istdlak) then ! Calculating rock drag partition factor using Marticorena and Bergametti (1995). - ! 0.01 is used to convert Z0a from centimeter to meter. + ! 0.01 is used to convert Prigent's roughness length dataset from centimeter to meter. this%dpfct_rock_patch(p) = 1._r8 - ( log(prigent_roughness_stream%prigent_rghn(g)*0.01_r8/z0s) & - / log(0.7_r8*(X/z0s)**0.8_r8) ) + / log(b1 * (X/z0s)**b2 ) ) end if end do diff --git a/src/biogeophys/SoilStateInitTimeConstMod.F90 b/src/biogeophys/SoilStateInitTimeConstMod.F90 index 0e31cc2c53..bf2de65a29 100644 --- a/src/biogeophys/SoilStateInitTimeConstMod.F90 +++ b/src/biogeophys/SoilStateInitTimeConstMod.F90 @@ -703,8 +703,8 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) do c = begc,endc g = col%gridcell(c) - soilstate_inst%gwc_thr_col(c) = 0.01_r8*(0.17_r8*clay3d(g,1) + 0.0014_r8*clay3d(g,1)*clay3d(g,1)) ! Danny M. Leung modified the equation of soil moisture effect for dust emissions using a scale factor of 1. 0.01 is to convert from % to fraction. - !soilstate_inst%gwc_thr_col(c) = 0.17_r8 + 0.14_r8 * clay3d(g,1) * 0.01_r8 ! This was the original equation with 1 / (clay fraction) being the scaling factor. + !soilstate_inst%gwc_thr_col(c) = 0.01_r8*(0.17_r8*clay3d(g,1) + 0.0014_r8*clay3d(g,1)*clay3d(g,1)) ! Jasper Kok et al. (2014) modified the equation of soil moisture effect for dust emissions using a tuning factor of a = 1. 0.01 is to convert the threshold gravimetric soil moisture from % to fraction. Danny M. Leung et al. (2023) followed K14 and used this equation for the paper's results. However, Danny Leung later (Dec 2023) decided to revert to Zender's choice and use a = 1 / (clay fraction), which overall encourages more dust emissions from seriamid and more marginal dust sources. dmleung added this detailed comment on 19 Feb 2024. + soilstate_inst%gwc_thr_col(c) = 0.17_r8 + 0.14_r8 * clay3d(g,1) * 0.01_r8 ! This was the original equation with a = 1 / (clay fraction) being the tuning factor for soil moisture effect in Zender's dust emission scheme. Danny M. Leung decided (Dec, 2023) that the Leung et al. (2023) dust emission scheme in the CESM will use Zender's tuning of a = 1 / (clay fraction), which overall encourages more dust emissions from seriamid and more marginal dust sources. Another advantage of using this tuning factor instead of a = 1 is that the dust emission threshold is linearly dependent on the clay fraction instead of parabolically dependent on clay fraction as in the above line. This means that dust emission becomes a little less sensitive to clay content (soil texture). 0.17 and 0.14 are fitting coefficients in Fecan et al. (1999), and 0.01 is used to convert surface clay fraction from percentage to fraction. dmleung added this detailed comment on 19 Feb 2024. soilstate_inst%mss_frc_cly_vld_col(c) = min(clay3d(g,1) * 0.01_r8, 0.20_r8) end do From fcf1fb93aef1d813d4eeeeb2bc5b4b2920a9775c Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Tue, 20 Feb 2024 00:44:54 -0700 Subject: [PATCH 036/406] small bug fix to the previous commit. dmleung 20 Feb 2024 --- src/biogeochem/DUSTMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 275f0a4c10..d096cc9e2a 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -731,7 +731,7 @@ subroutine DustEmission (bounds, & ! probability that lowpass-filtered wind speed does not exceed u_ft prb_crs_fld_thr(p) = 0.5_r8 * (1.0_r8 + erf((u_fld_thr(p) - u_mean_slt(p)) / ( sqrt(2.0_r8) * u_sd_slt(p)))) ! probability that lowpass-filtered wind speed does not exceed u_it - prb_crs_impct_thr(p) = 0.5_r8 * (1.0_r8 + erf((u_impct_thr(p) - u_mean_slt(p)) / ( sqrt(2.0_r8 * u_sd_slt(p)))) + prb_crs_impct_thr(p) = 0.5_r8 * (1.0_r8 + erf((u_impct_thr(p) - u_mean_slt(p)) / ( sqrt(2.0_r8) * u_sd_slt(p)))) ! intermittency factor (eta; ranging from 0 to 1) intrmtncy_fct(p) = 1.0_r8 - prb_crs_fld_thr(p) + thr_crs_rate(p) * (prb_crs_fld_thr(p) - prb_crs_impct_thr(p)) From b1fad336249cc7e9c0870d85375ec4f30c366716 Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Tue, 20 Feb 2024 16:43:46 -0700 Subject: [PATCH 037/406] dmleung added more comments in the code blocks for the dust emission process in the DustEmission subroutine. 20 Feb 2024 --- src/biogeochem/DUSTMod.F90 | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index d096cc9e2a..9b72549e3c 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -530,7 +530,7 @@ subroutine DustEmission (bounds, & write(iulog,*)'Error dstmbl: pft= ',p,' lnd_frc_mbl(p)= ',lnd_frc_mbl(p) call endrun(subgrid_index=p, subgrid_level=subgrid_level_patch, msg=errMsg(sourcefile, __LINE__)) end if - end do + end doflx_mss_hrz_slt_ttl = 0.0_r8 ! dmleung add output for bare_frc and veg_frc here if wanted !!!!---------------------- @@ -581,7 +581,9 @@ subroutine DustEmission (bounds, & ! calculate soil moisture effect for dust emission threshold ! following Fecan, Marticorena et al. (1999) ! also see Zender et al. (2003) for DEAD emission scheme and Kok et al. (2014b) for K14 emission scheme in CESM - bd = (1._r8-watsat(c,1))*dns_slt ![kg m-3] Bulk density of dry surface soil (dmleung changed from 2700 to dns_slt, soil particle density, on 16 Feb 2024. Note that dns_slt=2650 kg m-3 so the value is changed by a tiny bit from 2700 to 2650. dns_slt has been here for many years so dns_slt should be used here instead of explicitly tpying the value out. dmleung 16 Feb 2024) + bd = (1._r8-watsat(c,1))*dns_slt ![kg m-3] Bulk density of dry surface soil (dmleung changed from 2700 to dns_slt, soil particle density, on 16 Feb 2024. Note that dns_slt=2650 kg m-3 so the value is changed by a tiny bit from 2700 to 2650. dns_slt has been here for many years so dns_slt should be used here instead of explicitly typing the value out. dmleung 16 Feb 2024) + + ! use emission threshold to calculate standardized threshold and dust emission coefficient ! Here convert h2osoi_vol (H2OSOI) at the topmost CTSM soil layer from volumetric (m3 water / m3 soil) to gravimetric soil moisture (kg water / kg soil) gwc_sfc = h2osoi_vol(c,1)*SHR_CONST_RHOFW/bd ![kg kg-1] Gravimetric H2O cont @@ -612,18 +614,22 @@ subroutine DustEmission (bounds, & ! the above formula is true for Iversen and White (1982) and Shao and Lu (2000) scheme wnd_frc_thr(p) = wnd_frc_thr_slt ! output fluid threshold - ! use emission threshold to calculate standardized threshold and dust emission coefficient + !############################################################################################## + ! dmleung: here, calculate quantities relevant to the fluid threshold + ! standardized fluid threshold wnd_frc_thr_slt_std = wnd_frc_thr_slt * sqrt(forc_rho(c) / forc_rho_std) ! standardized soil threshold friction speed (defined using fluid threshold) wnd_frc_thr_std(p) = wnd_frc_thr_slt_std ! output standardized fluid threshold + ! dust emission coefficient or soil erodibility coefficient (this is analogous to the soil erodibility map or prefenertial source filter in Zender; see zendersoilerodstream) dst_emiss_coeff(p) = Cd0 * exp(-Ce * (wnd_frc_thr_slt_std - wnd_frc_thr_slt_std_min) / wnd_frc_thr_slt_std_min) ! save dust emission coefficient here for all grids - ! framentation exponent + ! framentation exponent (dependent on fluid threshold) frag_expt = (Ca * (wnd_frc_thr_slt_std - wnd_frc_thr_slt_std_min) / wnd_frc_thr_slt_std_min) ! fragmentation exponent, defined in Kok et al. (2014a) if (frag_expt > frag_expt_thr) then ! set fragmentation exponent to be 3 or 5 at maximum, to avoid local AOD blowup frag_expt = frag_expt_thr end if - !################ drag partition effect, and soil friction velocity ########################### + !############################################################################################## + !################ drag partition effect, and soil-surface friction velocity ################### ! subsection on computing vegetation drag partition and hybrid drag partition factors ! in Leung et al. (2023), drag partition effect is applied on the wind instead of the threshold !############################################################################################## @@ -644,12 +650,12 @@ subroutine DustEmission (bounds, & ! calculation of the hybrid/total drag partition effect considering both rock and vegetation drag partitioning using LUH2 bare and veg fractions within a grid if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then - if (patch%itype(p) == noveg) then + if (patch%itype(p) == noveg) then ! if bare, uses rock drag partition factor frc_thr_rgh_fct = dpfct_rock(p) - else - frc_thr_rgh_fct = ssr(p) + else ! if vegetation, uses vegetation drag partition factor + frc_thr_rgh_fct = ssr(p) end if - else + else frc_thr_rgh_fct = 1.0_r8 end if @@ -657,7 +663,7 @@ subroutine DustEmission (bounds, & frc_thr_rghn_fct(p) = frc_thr_rgh_fct ! save and output hybrid drag partition factor - else + else ! for lnd_frc_mbl=0, do not change friction velocity and assume drag partition factor = 0 wnd_frc_slt = fv(p) ! The value here is not important since once lnd_frc_mbl(p) <= 0.0_r8 there will be no emission. frc_thr_rghn_fct(p) = 0.0_r8 ! When LAI > 1, the drag partition effect is zero. dmleung 16 Feb 2024. end if @@ -666,6 +672,8 @@ subroutine DustEmission (bounds, & ! save soil friction velocity and roughness effect before the if-statement wnd_frc_soil(p) = wnd_frc_slt ! save soil friction velocity for CLM output, which has drag partition and Owen effect + ! 20 Feb 2024: dmleung notes that Leung does not consider the Owen's effect. This is Jasper Kok's decision. The Owen's effect should be in Zender's DEAD emission scheme. + ! save land mobile fraction lnd_frc_mble(p) = lnd_frc_mbl(p) ! save land mobile fraction first, before the if-statement ! only perform the following calculations if lnd_frc_mbl is non-zero @@ -673,19 +681,18 @@ subroutine DustEmission (bounds, & if (lnd_frc_mbl(p) > 0.0_r8) then ! if bare land fraction is larger than 0 then calculate the dust emission equation ! reset these variables which will be updated in the following if-block - flx_mss_hrz_slt_ttl = 0.0_r8 flx_mss_vrt_dst_ttl(p) = 0.0_r8 ! the following line comes from subr. dst_mbl ! purpose: threshold saltation wind speed - wnd_rfr_thr_slt = u10(p) * wnd_frc_thr_slt / fv(p) ! keep and use if we want the default Z03 scheme + wnd_rfr_thr_slt = u10(p) * wnd_frc_thr_slt / fv(p) ! use if we want the default Z03 scheme; for Leung 2023 this is not needed ! the following comes from subr. flx_mss_hrz_slt_ttl_Whi79_get ! purpose: compute vertically integrated streamwise mass flux of particles - if (wnd_frc_slt > wnd_frc_thr_slt_it) then! if using Leung's scheme, use impact threshold for dust emission equation + if (wnd_frc_slt > wnd_frc_thr_slt_it) then! if using Leung's scheme, use impact threshold for dust emission equation; if Zender, uses fluid threshold (wnd_frc_thr_slt) for dust emission equation !################### for Leung et al. (2023) ################################################ !################ uncomment the below block if want to use Leung's scheme ################### @@ -699,7 +706,10 @@ subroutine DustEmission (bounds, & !############## Danny M. Leung added the intermittency calculation ################################# ! subsection for intermittency factor calculation (only used by Leung's scheme, not Zender's scheme) + ! Leung et al. (2023) uses the Comola et al. (2019) intermittency scheme for the calculation of intermittent dust emissions. + ! This part takes care of the sub-timestep, high-frequency (< 1 minute period) turblent wind fluctuations occuring at the planetary boundary layer (PBL) near surface. Subtimestep wind gusts and episodes are important for generating emissions in marginal dust source regions, such as semiarid areas and high-latitude polar deserts. ! 2 Dec 2021: assume no buoyancy contribution to the wind fluctuation (u_sd_slt), so no obul(p) is needed. It is shown to be important for the wind fluctuations contribute little to the intermittency factor. We might add this back in the future revisions. + ! 20 Feb 2024: dmleung notes that dmleung may revise Comola's scheme in the future to improve Comola's formulation of the statistical parameterization. ! mean lowpass-filtered wind speed at hgt_sal = 0.1 m saltation height (assuming aerodynamic roughness length z0a_glob = 1e-4 m globally for ease; also assuming neutral condition) u_mean_slt(p) = (wnd_frc_slt/k) * log(hgt_sal / z0a_glob) ! translating from ustar (velocity scale) to actual wind From 4d732449d6dcd6094ca028aacbadca9908ab5212 Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Tue, 20 Feb 2024 17:23:18 -0700 Subject: [PATCH 038/406] dmleung: small bug fix to the previous commit. 20 Feb 2024 --- src/biogeochem/DUSTMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 9b72549e3c..f0b8931459 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -530,7 +530,7 @@ subroutine DustEmission (bounds, & write(iulog,*)'Error dstmbl: pft= ',p,' lnd_frc_mbl(p)= ',lnd_frc_mbl(p) call endrun(subgrid_index=p, subgrid_level=subgrid_level_patch, msg=errMsg(sourcefile, __LINE__)) end if - end doflx_mss_hrz_slt_ttl = 0.0_r8 + end do ! dmleung add output for bare_frc and veg_frc here if wanted !!!!---------------------- From 5aa94945e03fff81ef9c76ad30e74820dd2402b8 Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Thu, 22 Feb 2024 21:46:37 -0700 Subject: [PATCH 039/406] A bug fix for z0s inside CalcDragPartition in DUSTMod.F90. dmleung 22 Feb 2024 --- bld/namelist_files/namelist_defaults_ctsm.xml | 2 +- src/biogeochem/DUSTMod.F90 | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index a52477ce6c..e57a768e67 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -2752,7 +2752,7 @@ use_crop=".true.">lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_mpasa1 lnd/clm2/dustemisdata/Prigent_2005_roughness_0.25x0.25_cdf5_c240127.nc lnd/clm2/dustemisdata/dust_2x2_ESMFmesh_cdf5_c230730.nc +>lnd/clm2/dustemisdata/dust_2x2_ESMFmesh_cdf5_c240221.nc diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index f0b8931459..8b77d355e1 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -1290,7 +1290,8 @@ subroutine CalcDragPartition(this, bounds, prigent_roughness_stream ) ! dmleung: this loop calculates the drag partition effect (or roughness effect) of rocks. ! We save the drag partition factor as a patch level quantity. ! TODO: EBK 02/13/2024: Several magic numbers here that should become parameters so the meaning is preserved - z0s = 2_r8/30_r8 * D_p ! equation for smooth roughness length for soil grain. See Danny M. Leung et al. (2023) and Martin Klose et al. (2021) for instance. 1/15 is a coefficient that relates roughness to soil particle diameter D_p. + !z0s = 2_r8/30_r8 * D_p ! equation for smooth roughness length for soil grain. See Danny M. Leung et al. (2023) and Martina Klose et al. (2021) for instance. 1/15 is a coefficient that relates roughness to soil particle diameter D_p. + z0s = 2_r8 * D_p / 30_r8 ! equation for smooth roughness length for soil grain. See Danny M. Leung et al. (2023) and Martina Klose et al. (2021) for instance. 1/15 is a coefficient that relates roughness to soil particle diameter D_p. ! Here we assume soil medium size is a global constant, and so is smooth roughness length. do p = bounds%begp,bounds%endp g = patch%gridcell(p) From 0c62e5bde7016a60bf07d4d9eb9ef990d37d3b9a Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sat, 24 Feb 2024 21:39:22 -0700 Subject: [PATCH 040/406] Fix meshfile names so the resolution name is correct, as pointed out by @dmleung --- bld/namelist_files/namelist_defaults_ctsm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index e57a768e67..7bb58dcd61 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -2752,7 +2752,7 @@ use_crop=".true.">lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_mpasa1 lnd/clm2/dustemisdata/Prigent_2005_roughness_0.25x0.25_cdf5_c240127.nc lnd/clm2/dustemisdata/dust_2x2_ESMFmesh_cdf5_c240221.nc +>lnd/clm2/dustemisdata/dust_0.25x0.25_ESMFmesh_cdf5_c240222.nc From 942a3aecdc985c056b4231acf4422cb978da04f2 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sun, 25 Feb 2024 20:50:25 -0700 Subject: [PATCH 041/406] Fix a logical comparison so will compile with gnu on Derecho --- src/cpl/share_esmf/PrigentRoughnessStreamType.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 index 751fe8458c..6287b5f552 100644 --- a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 +++ b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 @@ -293,7 +293,7 @@ subroutine ReadNML(this, bounds, NLFilename) call shr_mpi_bcast(stream_meshfile_prigentroughness , mpicom) ! Error checking - if ( use_prigent_roughness == .false. )then + if ( .not. use_prigent_roughness )then if ( len_trim(stream_fldFileName_prigentroughness) /= 0 )then call endrun(msg=' ERROR stream_fldFileName_prigentroughness is set, but use_prigent_roughness is FALSE' & //errMsg(sourcefile, __LINE__)) From c27f2093b1a803af46d666de8f464d25b41f9b85 Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Mon, 26 Feb 2024 18:13:33 -0700 Subject: [PATCH 042/406] dmleung: improved LAI treatment in Okin's vegetation drag partitioning. Also added precision in other defined parameters. 26 Feb 2024 --- src/biogeochem/DUSTMod.F90 | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 8b77d355e1..4eb33c625d 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -275,10 +275,10 @@ subroutine InitHistory(this, bounds) avgflag='A', long_name='Okin-Pierre vegetation shear stress ratio (drag partition factor)', & ptr_patch=this%ssr_patch, set_lake=0._r8, set_urb=0._r8) this%lai_patch(begp:endp) = spval - call hist_addfld1d (fname='LAI', units='m/s', & - avgflag='A', long_name='leaf area index for Okin-Pierre scheme', & - ptr_patch=this%lai_patch, set_lake=0._r8, set_urb=0._r8) - this%frc_thr_rghn_fct_patch(begp:endp) = spval + !call hist_addfld1d (fname='LAI', units='m/s', & + ! avgflag='A', long_name='leaf area index for Okin-Pierre scheme', & + ! ptr_patch=this%lai_patch, set_lake=0._r8, set_urb=0._r8) + !this%frc_thr_rghn_fct_patch(begp:endp) = spval call hist_addfld1d (fname='FRC_THR_RGHN_FCT', units='dimensionless', & avgflag='A', long_name='hybrid drag partition (or roughness) factor', & ptr_patch=this%frc_thr_rghn_fct_patch, set_lake=0._r8, set_urb=0._r8) @@ -411,8 +411,9 @@ subroutine DustEmission (bounds, & real(r8), parameter :: gamma_Shao = 1.65e-4_r8 ! [kg s-2] interparticle cohesion: fitting parameter in Shao and Lu (2000) (S&L00). dmleung 16 Feb 2024 real(r8), parameter :: A_Shao = 0.0123_r8 ! [dimless] coefficient for aerodynamic force: fitting parameter in Shao and Lu (2000). dmleung 16 Feb 2024 real(r8), parameter :: frag_expt_thr = 5.0_r8 ! [dimless] threshold for fragmentation exponent defined in Leung et al. (2023), somewhere within 3 to 5. It is used to prevent a local AOD blowup (over Patagonia, Argentina), but one can test larger values and relax the threshold if wanted. dmleung 16 Feb 2024 - real(r8), parameter :: z0a_glob = 1e-4 ! [m] assumed globally constant aeolian roughness length value in Leung et al. (2023), for the log law of the wall for Comola et al. (2019) intermittency scheme. dmleung 20 Feb 2024 - real(r8), parameter :: hgt_sal = 0.1 ! [m] saltation height used by Comola et al. (2019) intermittency scheme for the log law of the wall. dmleung 20 Feb 2024 + real(r8), parameter :: z0a_glob = 1e-4_r8 ! [m] assumed globally constant aeolian roughness length value in Leung et al. (2023), for the log law of the wall for Comola et al. (2019) intermittency scheme. dmleung 20 Feb 2024 + real(r8), parameter :: hgt_sal = 0.1_r8 ! [m] saltation height used by Comola et al. (2019) intermittency scheme for the log law of the wall. dmleung 20 Feb 2024 + real(r8), parameter :: lai0_Okin = 0.1_r8 ! [m2/m2] minimum LAI needed for Okin-Pierre's vegetation drag partition equation. lai=0 in the equation will lead to infinity, so a small value is added into this lai dmleung defined. real(r8) :: numer ! Numerator term for threshold crossing rate real(r8) :: denom ! Denominator term for threshold crossing rate !------------------------------------------------------------------------ @@ -456,7 +457,7 @@ subroutine DustEmission (bounds, & prb_crs_fld_thr => dust_inst%prb_crs_fld_thr_patch , & ! output probability of instantaneous wind crossing fluid threshold in Comola 2019 intermittency parameterization prb_crs_impct_thr => dust_inst%prb_crs_impct_thr_patch , & ! output probability of instantaneous wind crossing impact threshold in Comola 2019 intermittency parameterization ssr => dust_inst%ssr_patch , & ! output vegetation drag partition factor in Okin 2008 vegetation roughness effect (called shear stress ratio, SSR in Okin 2008) - lai => dust_inst%lai_patch , & ! leaf area index for calculating vegetation drag partitioning. lai=0 in the ssr equation will lead to infinity, so for now a small value is added into this lai dmleung defined. (no need to output) 16 Feb 2024 + lai => dust_inst%lai_patch , & ! leaf area index for calculating Okin-Pierre vegetation drag partitioning. lai=0 in the ssr equation will lead to infinity, so a small value is added into this lai dmleung defined. (no need to output) 16 Feb 2024 frc_thr_rghn_fct => dust_inst%frc_thr_rghn_fct_patch , & ! output hybrid/total drag partition factor considering both rock and vegetation drag partition factors. wnd_frc_thr_std => dust_inst%wnd_frc_thr_std_patch , & ! standardized dust emission threshold friction velocity defined in Jasper Kok et al. (2014). dpfct_rock => dust_inst%dpfct_rock_patch & ! output rock drag partition factor defined in Marticorena and Bergametti 1995. A fraction between 0 and 1. @@ -639,14 +640,15 @@ subroutine DustEmission (bounds, & if (lnd_frc_mbl(p) > 0.0_r8 .AND. tlai_lu(l)<=1_r8) then ! vegetation drag partition equation following Gregory Okin (2008) + Caroline Pierre et al. (2014) - lai(p) = tlai_lu(l)+0.01_r8 ! LAI+SAI averaged to landunit level; the equation is undefined at lai=0 so we add in a small number. + !lai(p) = tlai_lu(l)+0.1_r8 ! LAI+SAI averaged to landunit level; the equation is undefined at lai=0, and LAI in CTSM has some zeros over deserts, so we add in a small number. + lai(p) = ttlai(p) + lai0_Okin ! ttlai = tlai+tsai. Okin-Pierre's equation is undefined at lai=0, and LAI in CTSM has some zeros over deserts, so we add in a small number. On 26 Feb 2024, dmleung changed from tlai_lu(l) to ttlai(p) if (lai(p) > 1_r8) then - lai(p) = 1_r8 ! setting LAI ~ 0.1 to be a min value (since the value goes to infinity when LAI=0) - end if ! and 1 to be a max value as computing K involves 1 / LAI + lai(p) = 1_r8 ! setting LAI = 1 to be a max value (since K_length goes to negative when LAI>1) + end if ! ! calculate Okin's shear stress ratio (which is drag partition factor) using Pierre's equation - K_length = 2_r8 * (1_r8/lai(p) - 1_r8) ! Here LAI has to be non-zero to avoid blowup - ssr(p) = (K_length+f_0*c_e)/(K_length+c_e) + K_length = 2_r8 * (1_r8/lai(p) - 1_r8) ! Here LAI has to be non-zero to avoid blowup, and < 1 to avoid -ve K_length. See this equation in Leung et al. (2023) + ssr(p) = (K_length+f_0*c_e)/(K_length+c_e) ! see this equation in Caroline Pierre et al. (2014) or Leung et al. (2023). ! calculation of the hybrid/total drag partition effect considering both rock and vegetation drag partitioning using LUH2 bare and veg fractions within a grid if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then From ac5873b968f0a9dabaa460b9d052a1d76c276683 Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Tue, 27 Feb 2024 15:47:48 -0700 Subject: [PATCH 043/406] dmleung: Now we call the vegetation in Okin scheme VAI instead of LAI. Also set output for impact threshold. 27 Feb 2024 --- src/biogeochem/DUSTMod.F90 | 63 +++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 4eb33c625d..17deda7375 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -67,6 +67,7 @@ module DUSTMod real(r8), pointer, private :: dst_emiss_coeff_patch (:) ! dust emission coefficient (unitless) real(r8), pointer, private :: wnd_frc_thr_patch (:) ! wet fluid threshold (m/s) real(r8), pointer, private :: wnd_frc_thr_dry_patch (:) ! dry fluid threshold (m/s) + real(r8), pointer, private :: wnd_frc_thr_it_patch (:) ! impact threshold (m/s) real(r8), pointer, private :: lnd_frc_mble_patch (:) ! land mobile fraction real(r8), pointer, private :: liq_frac_patch (:) ! liquid fraction of total water real(r8), pointer, private :: wnd_frc_soil_patch (:) ! soil wind friction velocity (m/s) @@ -81,7 +82,7 @@ module DUSTMod real(r8), pointer, private :: prb_crs_fld_thr_patch (:) ! probability of wind speed crossing fluid threshold real(r8), pointer, private :: prb_crs_impct_thr_patch (:) ! probability of wind speed crossing impact threshold real(r8), pointer, private :: ssr_patch (:) ! [dimless] integrated shear stress ratiio, defined by Okin (2008) and then integrated by Caroline Pierre et al. (2014) - real(r8), pointer, private :: lai_patch (:) ! [m2 leaf /m2 land] LAI+SAI for calculating Okin's drag partition, averaged to landunit level + real(r8), pointer, private :: vai_Okin_patch (:) ! [m2 leaf /m2 land] LAI+SAI for calculating Okin drag partition real(r8), pointer, private :: frc_thr_rghn_fct_patch (:) ! [dimless] hybrid drag partition (or called roughness) factor real(r8), pointer, private :: wnd_frc_thr_std_patch (:) ! standardized fluid threshold friction velocity (m/s) type(prigentroughnessstream_type), private :: prigent_roughness_stream ! Prigent roughness stream data @@ -144,6 +145,7 @@ subroutine InitAllocate(this, bounds) allocate(this%dst_emiss_coeff_patch (begp:endp)) ; this%dst_emiss_coeff_patch (:) = nan allocate(this%wnd_frc_thr_patch (begp:endp)) ; this%wnd_frc_thr_patch (:) = nan allocate(this%wnd_frc_thr_dry_patch (begp:endp)) ; this%wnd_frc_thr_dry_patch (:) = nan + allocate(this%wnd_frc_thr_it_patch (begp:endp)) ; this%wnd_frc_thr_it_patch (:) = nan allocate(this%lnd_frc_mble_patch (begp:endp)) ; this%lnd_frc_mble_patch (:) = nan allocate(this%wnd_frc_soil_patch (begp:endp)) ; this%wnd_frc_soil_patch (:) = nan allocate(this%gwc_patch (begp:endp)) ; this%gwc_patch (:) = nan @@ -158,7 +160,7 @@ subroutine InitAllocate(this, bounds) allocate(this%prb_crs_fld_thr_patch (begp:endp)) ; this%prb_crs_fld_thr_patch (:) = nan allocate(this%prb_crs_impct_thr_patch (begp:endp)) ; this%prb_crs_impct_thr_patch (:) = nan allocate(this%ssr_patch (begp:endp)) ; this%ssr_patch (:) = nan - allocate(this%lai_patch (begp:endp)) ; this%lai_patch (:) = nan + allocate(this%vai_Okin_patch (begp:endp)) ; this%vai_Okin_patch (:) = nan allocate(this%frc_thr_rghn_fct_patch (begp:endp)) ; this%frc_thr_rghn_fct_patch (:) = nan allocate(this%wnd_frc_thr_std_patch (begp:endp)) ; this%wnd_frc_thr_std_patch (:) = nan allocate(this%dpfct_rock_patch (begp:endp)) ; this%dpfct_rock_patch (:) = nan @@ -207,8 +209,8 @@ subroutine InitHistory(this, bounds) ptr_patch=this%vlc_trb_4_patch, default='inactive') this%dst_emiss_coeff_patch(begp:endp) = spval - call hist_addfld1d (fname='C_d', units='dimensionless', & - avgflag='A', long_name='dust emission coefficient', & + call hist_addfld1d (fname='DUST_EMIS_COEFF', units='dimensionless', & + avgflag='A', long_name='soil erodibility or dust emission coefficient for Kok emission scheme', & ptr_patch=this%dst_emiss_coeff_patch, set_lake=0._r8, set_urb=0._r8) this%wnd_frc_thr_patch(begp:endp) = spval call hist_addfld1d (fname='WND_FRC_FT', units='m/s', & @@ -218,6 +220,10 @@ subroutine InitHistory(this, bounds) call hist_addfld1d (fname='WND_FRC_FT_DRY', units='m/s', & avgflag='A', long_name='dry fluid threshold friction velocity', & ptr_patch=this%wnd_frc_thr_dry_patch, set_lake=0._r8, set_urb=0._r8) + this%wnd_frc_thr_it_patch(begp:endp) = spval + call hist_addfld1d (fname='WND_FRC_IT', units='m/s', & + avgflag='A', long_name='impact threshold friction velocity', & + ptr_patch=this%wnd_frc_thr_it_patch, set_lake=0._r8, set_urb=0._r8) this%wnd_frc_soil_patch(begp:endp) = spval call hist_addfld1d (fname='WND_FRC_SOIL', units='m/s', & avgflag='A', long_name='soil surface wind friction velocity', & @@ -274,11 +280,11 @@ subroutine InitHistory(this, bounds) call hist_addfld1d (fname='SSR', units='m/s', & avgflag='A', long_name='Okin-Pierre vegetation shear stress ratio (drag partition factor)', & ptr_patch=this%ssr_patch, set_lake=0._r8, set_urb=0._r8) - this%lai_patch(begp:endp) = spval - !call hist_addfld1d (fname='LAI', units='m/s', & - ! avgflag='A', long_name='leaf area index for Okin-Pierre scheme', & - ! ptr_patch=this%lai_patch, set_lake=0._r8, set_urb=0._r8) - !this%frc_thr_rghn_fct_patch(begp:endp) = spval + this%vai_Okin_patch(begp:endp) = spval + call hist_addfld1d (fname='VAI_OKIN', units='m/s', & + avgflag='A', long_name='vegetation area index used in the Okin-Pierre plant drag partition scheme', & + ptr_patch=this%vai_Okin_patch, set_lake=0._r8, set_urb=0._r8) + this%frc_thr_rghn_fct_patch(begp:endp) = spval call hist_addfld1d (fname='FRC_THR_RGHN_FCT', units='dimensionless', & avgflag='A', long_name='hybrid drag partition (or roughness) factor', & ptr_patch=this%frc_thr_rghn_fct_patch, set_lake=0._r8, set_urb=0._r8) @@ -315,7 +321,7 @@ subroutine InitCold(this, bounds) end if end do - ! Caulculate Drag Partition factor + ! Caulculate Drag Partition factor (Marticorena and Bergametti 1995 formulation) if ( this%prigent_roughness_stream%useStreams() )then !if usestreams == true, and it should be always true call this%CalcDragPartition( bounds, this%prigent_roughness_stream ) else @@ -413,7 +419,7 @@ subroutine DustEmission (bounds, & real(r8), parameter :: frag_expt_thr = 5.0_r8 ! [dimless] threshold for fragmentation exponent defined in Leung et al. (2023), somewhere within 3 to 5. It is used to prevent a local AOD blowup (over Patagonia, Argentina), but one can test larger values and relax the threshold if wanted. dmleung 16 Feb 2024 real(r8), parameter :: z0a_glob = 1e-4_r8 ! [m] assumed globally constant aeolian roughness length value in Leung et al. (2023), for the log law of the wall for Comola et al. (2019) intermittency scheme. dmleung 20 Feb 2024 real(r8), parameter :: hgt_sal = 0.1_r8 ! [m] saltation height used by Comola et al. (2019) intermittency scheme for the log law of the wall. dmleung 20 Feb 2024 - real(r8), parameter :: lai0_Okin = 0.1_r8 ! [m2/m2] minimum LAI needed for Okin-Pierre's vegetation drag partition equation. lai=0 in the equation will lead to infinity, so a small value is added into this lai dmleung defined. + real(r8), parameter :: vai0_Okin = 0.1_r8 ! [m2/m2] minimum VAI needed for Okin-Pierre's vegetation drag partition equation. lai=0 in the equation will lead to infinity, so a small value is added into this lai dmleung defined. real(r8) :: numer ! Numerator term for threshold crossing rate real(r8) :: denom ! Denominator term for threshold crossing rate !------------------------------------------------------------------------ @@ -441,8 +447,9 @@ subroutine DustEmission (bounds, & flx_mss_vrt_dst_tot => dust_inst%flx_mss_vrt_dst_tot_patch , & ! Output: [real(r8) (:) ] total dust flux back to atmosphere (pft) ! below variables are defined in Kok et al. (2014) or (mostly) Leung et al. (2023) dust emission scheme. dmleung 16 Feb 2024 dst_emiss_coeff => dust_inst%dst_emiss_coeff_patch , & ! Output dust emission coefficient - wnd_frc_thr => dust_inst%wnd_frc_thr_patch , & ! output impact threshold - wnd_frc_thr_dry => dust_inst%wnd_frc_thr_dry_patch , & ! output dry threshold + wnd_frc_thr => dust_inst%wnd_frc_thr_patch , & ! output fluid threshold + wnd_frc_thr_dry => dust_inst%wnd_frc_thr_dry_patch , & ! output dry fluid threshold + wnd_frc_thr_it => dust_inst%wnd_frc_thr_it_patch , & ! output impact threshold lnd_frc_mble => dust_inst%lnd_frc_mble_patch , & ! output bare land fraction wnd_frc_soil => dust_inst%wnd_frc_soil_patch , & ! soil friction velocity u_*s = (u_*)(f_eff) gwc => dust_inst%gwc_patch , & ! output gravimetric water content @@ -457,7 +464,7 @@ subroutine DustEmission (bounds, & prb_crs_fld_thr => dust_inst%prb_crs_fld_thr_patch , & ! output probability of instantaneous wind crossing fluid threshold in Comola 2019 intermittency parameterization prb_crs_impct_thr => dust_inst%prb_crs_impct_thr_patch , & ! output probability of instantaneous wind crossing impact threshold in Comola 2019 intermittency parameterization ssr => dust_inst%ssr_patch , & ! output vegetation drag partition factor in Okin 2008 vegetation roughness effect (called shear stress ratio, SSR in Okin 2008) - lai => dust_inst%lai_patch , & ! leaf area index for calculating Okin-Pierre vegetation drag partitioning. lai=0 in the ssr equation will lead to infinity, so a small value is added into this lai dmleung defined. (no need to output) 16 Feb 2024 + vai_Okin => dust_inst%vai_Okin_patch , & ! vegetation area index for calculating Okin-Pierre vegetation drag partitioning. vai=0 in the ssr equation will lead to infinity, so a small value is added into this vai dmleung defined. (no need to output) 16 Feb 2024 frc_thr_rghn_fct => dust_inst%frc_thr_rghn_fct_patch , & ! output hybrid/total drag partition factor considering both rock and vegetation drag partition factors. wnd_frc_thr_std => dust_inst%wnd_frc_thr_std_patch , & ! standardized dust emission threshold friction velocity defined in Jasper Kok et al. (2014). dpfct_rock => dust_inst%dpfct_rock_patch & ! output rock drag partition factor defined in Marticorena and Bergametti 1995. A fraction between 0 and 1. @@ -557,7 +564,7 @@ subroutine DustEmission (bounds, & prb_crs_impct_thr(p) = 0.0_r8 intrmtncy_fct(p) = 0.0_r8 ssr(p) = 0.0_r8 - lai(p) = 0.0_r8 + vai_Okin(p) = 0.0_r8 frc_thr_rghn_fct(p) = 0.0_r8 wnd_frc_thr_std(p) = 0.0_r8 end do @@ -614,6 +621,7 @@ subroutine DustEmission (bounds, & ! the above formula is true for Iversen and White (1982) and Shao and Lu (2000) scheme wnd_frc_thr(p) = wnd_frc_thr_slt ! output fluid threshold + wnd_frc_thr_it(p) = wnd_frc_thr_slt_it ! output impact threshold !############################################################################################## ! dmleung: here, calculate quantities relevant to the fluid threshold @@ -638,17 +646,22 @@ subroutine DustEmission (bounds, & ! purpose: compute factor by which surface roughness increases threshold ! friction velocity (currently a constant) - if (lnd_frc_mbl(p) > 0.0_r8 .AND. tlai_lu(l)<=1_r8) then + !if (lnd_frc_mbl(p) > 0.0_r8 .AND. tlai_lu(l)<=1_r8) then + if (lnd_frc_mbl(p) > 0.0_r8 .AND. ttlai(p)<=1_r8) then ! vegetation drag partition equation following Gregory Okin (2008) + Caroline Pierre et al. (2014) !lai(p) = tlai_lu(l)+0.1_r8 ! LAI+SAI averaged to landunit level; the equation is undefined at lai=0, and LAI in CTSM has some zeros over deserts, so we add in a small number. - lai(p) = ttlai(p) + lai0_Okin ! ttlai = tlai+tsai. Okin-Pierre's equation is undefined at lai=0, and LAI in CTSM has some zeros over deserts, so we add in a small number. On 26 Feb 2024, dmleung changed from tlai_lu(l) to ttlai(p) - if (lai(p) > 1_r8) then - lai(p) = 1_r8 ! setting LAI = 1 to be a max value (since K_length goes to negative when LAI>1) - end if ! + !lai(p) = ttlai(p) + lai0_Okin ! ttlai = tlai+tsai. Okin-Pierre's equation is undefined at lai=0, and LAI in CTSM has some zeros over deserts, so we add in a small number. On 26 Feb 2024, dmleung changed from tlai_lu(l) to ttlai(p) + !if (lai(p) > 1_r8) then + ! lai(p) = 1_r8 ! setting LAI = 1 to be a max value (since K_length goes to negative when LAI>1) + !end if ! - ! calculate Okin's shear stress ratio (which is drag partition factor) using Pierre's equation - K_length = 2_r8 * (1_r8/lai(p) - 1_r8) ! Here LAI has to be non-zero to avoid blowup, and < 1 to avoid -ve K_length. See this equation in Leung et al. (2023) - ssr(p) = (K_length+f_0*c_e)/(K_length+c_e) ! see this equation in Caroline Pierre et al. (2014) or Leung et al. (2023). + if (ttlai(p) + vai0_Okin <= 1_r8) then + vai_Okin(p) = ttlai(p) + vai0_Okin ! ttlai = vai = tlai+tsai. Okin-Pierre's equation is undefined at vai=0, and VAI in CTSM has some zeros over deserts, so we add in a small number. On 26 Feb 2024, dmleung changed from tlai_lu(l) to ttlai(p) + end if ! In the Okin-Pierre formulation, VAI has to be 0 < VAI <= 1. + + ! calculate Okin's shear stress ratio (SSR, which is vegetation drag partition factor) using Pierre's equation + K_length = 2_r8 * (1_r8/vai_Okin(p) - 1_r8) ! Here LAI has to be non-zero to avoid blowup, and < 1 to avoid -ve K_length. See this equation in Leung et al. (2023). This line is Okin's formulation + ssr(p) = (K_length+f_0*c_e)/(K_length+c_e) ! see this equation in Caroline Pierre et al. (2014) or Leung et al. (2023). This line is Pierre's formulation. ! calculation of the hybrid/total drag partition effect considering both rock and vegetation drag partitioning using LUH2 bare and veg fractions within a grid if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then @@ -1250,7 +1263,7 @@ subroutine CalcDragPartition(this, bounds, prigent_roughness_stream ) ! !DESCRIPTION: ! Commented below by Danny M. Leung 31 Dec 2022 ! Calculate the drag partition effect of friction velocity due to surface roughness following - ! Leung et al. (2022). This module is used in the dust emission module DUSTMod.F90 for + ! Leung et al. (2023). This module is used in the dust emission module DUSTMod.F90 for ! calculating drag partitioning. The drag partition equation comes from Marticorena and ! Bergametti (1995) with constants modified by Darmenova et al. (2009). Here it is assumed ! that this equation is used only over arid/desertic regions, such that Catherine Prigent's @@ -1299,7 +1312,7 @@ subroutine CalcDragPartition(this, bounds, prigent_roughness_stream ) g = patch%gridcell(p) l = patch%landunit(p) if (lun%itype(l) /= istdlak) then - ! Calculating rock drag partition factor using Marticorena and Bergametti (1995). + ! Calculating rock drag partition factor using the Marticorena and Bergametti (1995) formulation. ! 0.01 is used to convert Prigent's roughness length dataset from centimeter to meter. this%dpfct_rock_patch(p) = 1._r8 - ( log(prigent_roughness_stream%prigent_rghn(g)*0.01_r8/z0s) & / log(b1 * (X/z0s)**b2 ) ) From eede6f8fa03d00f997fa8b4a5b39535be6d89a00 Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Mon, 1 Apr 2024 17:08:52 -0600 Subject: [PATCH 044/406] dmleung reverted to the landunit level VAI for Okin's vegetation drag partitioning, which worked much better than the patch level VAI. 1 Apr 2024 --- src/biogeochem/DUSTMod.F90 | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 17deda7375..9161076e4f 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -646,8 +646,8 @@ subroutine DustEmission (bounds, & ! purpose: compute factor by which surface roughness increases threshold ! friction velocity (currently a constant) - !if (lnd_frc_mbl(p) > 0.0_r8 .AND. tlai_lu(l)<=1_r8) then - if (lnd_frc_mbl(p) > 0.0_r8 .AND. ttlai(p)<=1_r8) then + if (lnd_frc_mbl(p) > 0.0_r8 .AND. tlai_lu(l)<=1_r8) then + !if (lnd_frc_mbl(p) > 0.0_r8 .AND. ttlai(p)<=1_r8) then ! vegetation drag partition equation following Gregory Okin (2008) + Caroline Pierre et al. (2014) !lai(p) = tlai_lu(l)+0.1_r8 ! LAI+SAI averaged to landunit level; the equation is undefined at lai=0, and LAI in CTSM has some zeros over deserts, so we add in a small number. !lai(p) = ttlai(p) + lai0_Okin ! ttlai = tlai+tsai. Okin-Pierre's equation is undefined at lai=0, and LAI in CTSM has some zeros over deserts, so we add in a small number. On 26 Feb 2024, dmleung changed from tlai_lu(l) to ttlai(p) @@ -655,9 +655,17 @@ subroutine DustEmission (bounds, & ! lai(p) = 1_r8 ! setting LAI = 1 to be a max value (since K_length goes to negative when LAI>1) !end if ! - if (ttlai(p) + vai0_Okin <= 1_r8) then - vai_Okin(p) = ttlai(p) + vai0_Okin ! ttlai = vai = tlai+tsai. Okin-Pierre's equation is undefined at vai=0, and VAI in CTSM has some zeros over deserts, so we add in a small number. On 26 Feb 2024, dmleung changed from tlai_lu(l) to ttlai(p) - end if ! In the Okin-Pierre formulation, VAI has to be 0 < VAI <= 1. + !if (ttlai(p) + vai0_Okin <= 1_r8) then + ! vai_Okin(p) = ttlai(p) + vai0_Okin ! ttlai = vai = tlai+tsai. Okin-Pierre's equation is undefined at vai=0, and VAI in CTSM has some zeros over deserts, so we add in a small number. On 26 Feb 2024, dmleung changed from tlai_lu(l) to ttlai(p) + !if (tlai_lu(l) + vai0_Okin <= 1_r8) then + ! vai_Okin(p) = tlai_lu(l) + vai0_Okin ! testing on 27 Feb 2024 + !end if ! In the Okin-Pierre formulation, VAI has to be 0 < VAI <= 1. + + vai_Okin(p) = tlai_lu(l)+vai0_Okin ! LAI+SAI averaged to landunit level; the equation is undefined at lai=0, and LAI in CTSM has some zeros over deserts, so we add in a small number. + if (vai_Okin(p) > 1_r8) then + vai_Okin(p) = 1_r8 ! setting LAI = 1 to be a max value (since K_length goes to negative when LAI>1) + end if + ! calculate Okin's shear stress ratio (SSR, which is vegetation drag partition factor) using Pierre's equation K_length = 2_r8 * (1_r8/vai_Okin(p) - 1_r8) ! Here LAI has to be non-zero to avoid blowup, and < 1 to avoid -ve K_length. See this equation in Leung et al. (2023). This line is Okin's formulation From 53871f47af026a51210e5feacb6fd43f9b3c5bd2 Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Mon, 1 Apr 2024 17:15:21 -0600 Subject: [PATCH 045/406] Typo fix: Some users told me that the denominator of the dust emission equation in Leung 2023 should be impact threshold instead of standardized fluid threshold. So, that was corrected in the code. dmleung 1 Apr 2024 --- src/biogeochem/DUSTMod.F90 | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 9161076e4f..438468a532 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -657,15 +657,12 @@ subroutine DustEmission (bounds, & !if (ttlai(p) + vai0_Okin <= 1_r8) then ! vai_Okin(p) = ttlai(p) + vai0_Okin ! ttlai = vai = tlai+tsai. Okin-Pierre's equation is undefined at vai=0, and VAI in CTSM has some zeros over deserts, so we add in a small number. On 26 Feb 2024, dmleung changed from tlai_lu(l) to ttlai(p) - !if (tlai_lu(l) + vai0_Okin <= 1_r8) then - ! vai_Okin(p) = tlai_lu(l) + vai0_Okin ! testing on 27 Feb 2024 !end if ! In the Okin-Pierre formulation, VAI has to be 0 < VAI <= 1. vai_Okin(p) = tlai_lu(l)+vai0_Okin ! LAI+SAI averaged to landunit level; the equation is undefined at lai=0, and LAI in CTSM has some zeros over deserts, so we add in a small number. if (vai_Okin(p) > 1_r8) then vai_Okin(p) = 1_r8 ! setting LAI = 1 to be a max value (since K_length goes to negative when LAI>1) - end if - + end if ! calculate Okin's shear stress ratio (SSR, which is vegetation drag partition factor) using Pierre's equation K_length = 2_r8 * (1_r8/vai_Okin(p) - 1_r8) ! Here LAI has to be non-zero to avoid blowup, and < 1 to avoid -ve K_length. See this equation in Leung et al. (2023). This line is Okin's formulation @@ -720,7 +717,8 @@ subroutine DustEmission (bounds, & !################### for Leung et al. (2023) ################################################ !################ uncomment the below block if want to use Leung's scheme ################### - flx_mss_vrt_dst_ttl(p) = dst_emiss_coeff(p) * mss_frc_cly_vld(c) * forc_rho(c) * ((wnd_frc_slt**2.0_r8 - wnd_frc_thr_slt_it**2.0_r8) / wnd_frc_thr_slt_std) * (wnd_frc_slt / wnd_frc_thr_slt_it)**frag_expt ! Leung et al. (2022) uses Kok et al. (2014) dust emission euqation for emission flux + !flx_mss_vrt_dst_ttl(p) = dst_emiss_coeff(p) * mss_frc_cly_vld(c) * forc_rho(c) * ((wnd_frc_slt**2.0_r8 - wnd_frc_thr_slt_it**2.0_r8) / wnd_frc_thr_slt_std) * (wnd_frc_slt / wnd_frc_thr_slt_it)**frag_expt ! Leung et al. (2022) uses Kok et al. (2014) dust emission euqation for emission flux + flx_mss_vrt_dst_ttl(p) = dst_emiss_coeff(p) * mss_frc_cly_vld(c) * forc_rho(c) * ((wnd_frc_slt**2.0_r8 - wnd_frc_thr_slt_it**2.0_r8) / wnd_frc_thr_slt_it) * (wnd_frc_slt / wnd_frc_thr_slt_it)**frag_expt ! Leung et al. (2022) uses Kok et al. (2014) dust emission euqation for emission flux ! account for bare soil fraction, frozen soil fraction, and apply global tuning parameter (Kok et al. 2014) flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) * lnd_frc_mbl(p) * C_tune * liqfrac From f488309777bd6cd3ba8ac61bc1d60c4448e905f2 Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Mon, 1 Apr 2024 17:25:47 -0600 Subject: [PATCH 046/406] Important change: Used Monin-Obukhov length to calculate the buoyancy contribution to the subtimestep wind fluctuations, instead of assuming zero buoyancy contributions. dmleung 1 Apr 2024 --- src/biogeochem/DUSTMod.F90 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 438468a532..2986ed579b 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -420,6 +420,7 @@ subroutine DustEmission (bounds, & real(r8), parameter :: z0a_glob = 1e-4_r8 ! [m] assumed globally constant aeolian roughness length value in Leung et al. (2023), for the log law of the wall for Comola et al. (2019) intermittency scheme. dmleung 20 Feb 2024 real(r8), parameter :: hgt_sal = 0.1_r8 ! [m] saltation height used by Comola et al. (2019) intermittency scheme for the log law of the wall. dmleung 20 Feb 2024 real(r8), parameter :: vai0_Okin = 0.1_r8 ! [m2/m2] minimum VAI needed for Okin-Pierre's vegetation drag partition equation. lai=0 in the equation will lead to infinity, so a small value is added into this lai dmleung defined. + real(r8), parameter :: zii = 1000.0_r8 ! [m] convective boundary layer height added by dmleung 20 Feb 2024, following other CTSM modules (e.g., CanopyFluxesMod). Should we transfer PBL height (PBLH) from CAM? real(r8) :: numer ! Numerator term for threshold crossing rate real(r8) :: denom ! Denominator term for threshold crossing rate !------------------------------------------------------------------------ @@ -467,7 +468,8 @@ subroutine DustEmission (bounds, & vai_Okin => dust_inst%vai_Okin_patch , & ! vegetation area index for calculating Okin-Pierre vegetation drag partitioning. vai=0 in the ssr equation will lead to infinity, so a small value is added into this vai dmleung defined. (no need to output) 16 Feb 2024 frc_thr_rghn_fct => dust_inst%frc_thr_rghn_fct_patch , & ! output hybrid/total drag partition factor considering both rock and vegetation drag partition factors. wnd_frc_thr_std => dust_inst%wnd_frc_thr_std_patch , & ! standardized dust emission threshold friction velocity defined in Jasper Kok et al. (2014). - dpfct_rock => dust_inst%dpfct_rock_patch & ! output rock drag partition factor defined in Marticorena and Bergametti 1995. A fraction between 0 and 1. + dpfct_rock => dust_inst%dpfct_rock_patch , & ! output rock drag partition factor defined in Marticorena and Bergametti 1995. A fraction between 0 and 1. + obu => frictionvel_inst%obu_patch & ! Input: [real(r8) (:) ] Monin-Obukhov length from the friction Velocity module ) ttlai(bounds%begp : bounds%endp) = 0._r8 @@ -735,7 +737,8 @@ subroutine DustEmission (bounds, & ! mean lowpass-filtered wind speed at hgt_sal = 0.1 m saltation height (assuming aerodynamic roughness length z0a_glob = 1e-4 m globally for ease; also assuming neutral condition) u_mean_slt(p) = (wnd_frc_slt/k) * log(hgt_sal / z0a_glob) ! translating from ustar (velocity scale) to actual wind - stblty(p) = 0 ! -dmleung 2 Dec 2021: use stability = 0 for now, assuming no buoyancy contribution. Might uncomment the above lines in future revisions. + !stblty(p) = 0_r8 ! -dmleung 2 Dec 2021: use stability = 0 for now, assuming no buoyancy contribution. Might uncomment the above lines in future revisions. + stblty(p) = zii / obu(p) ! -dmleung 20 Feb 2024: use obu from CTSM and PBL height = zii (= 1000_r8) which is default in CTSM. Should we transfer PBL height from CAM? if ((12_r8 - 0.5_r8 * stblty(p)) .GE. 0.001_r8) then ! should have used 0 theoretically; used 0.001 here to avoid undefined values u_sd_slt(p) = wnd_frc_slt * (12_r8 - 0.5_r8 * stblty(p))**0.333_r8 else From 3e0ee1a0a4150e3cc3daf80549c0cf665001afe3 Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Thu, 11 Apr 2024 05:14:22 -0600 Subject: [PATCH 047/406] Add tsoi coldstart, get streams out of waterstate and plumb them back --- src/biogeophys/TemperatureType.F90 | 25 ++++++--- src/biogeophys/WaterStateType.F90 | 90 +++++++++++++----------------- src/main/clm_instMod.F90 | 22 +++++++- 3 files changed, 77 insertions(+), 60 deletions(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index ab310650c8..7906773b26 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -8,7 +8,7 @@ module TemperatureType use decompMod , only : bounds_type use abortutils , only : endrun use clm_varctl , only : use_cndv, iulog, use_luna, use_crop, use_biomass_heat_storage - use clm_varpar , only : nlevsno, nlevgrnd, nlevlak, nlevurb, nlevmaxurbgrnd + use clm_varpar , only : nlevsno, nlevgrnd, nlevlak, nlevurb, nlevmaxurbgrnd, nlevsoi use clm_varcon , only : spval, ispval use GridcellType , only : grc use LandunitType , only : lun @@ -141,7 +141,7 @@ module TemperatureType !------------------------------------------------------------------------ subroutine Init(this, bounds, & em_roof_lun, em_wall_lun, em_improad_lun, em_perroad_lun, & - is_simple_buildtemp, is_prog_buildtemp) + is_simple_buildtemp, is_prog_buildtemp, exice_init_stream_col) ! ! !DESCRIPTION: ! @@ -156,6 +156,7 @@ subroutine Init(this, bounds, & real(r8) , intent(in) :: em_perroad_lun(bounds%begl:) logical , intent(in) :: is_simple_buildtemp ! Simple building temp is being used logical , intent(in) :: is_prog_buildtemp ! Prognostic building temp is being used + real(r8) , intent(in) :: exice_init_stream_col(bounds%begc:) ! initial excess ice concentration from the stream file call this%InitAllocate ( bounds ) call this%InitHistory ( bounds, is_simple_buildtemp, is_prog_buildtemp ) @@ -164,7 +165,8 @@ subroutine Init(this, bounds, & em_wall_lun(bounds%begl:bounds%endl), & em_improad_lun(bounds%begl:bounds%endl), & em_perroad_lun(bounds%begl:bounds%endl), & - is_simple_buildtemp, is_prog_buildtemp) + is_simple_buildtemp, is_prog_buildtemp, & + exice_init_stream_col(bounds%begc:bounds%endc) ) end subroutine Init @@ -639,7 +641,7 @@ end subroutine InitHistory !----------------------------------------------------------------------- subroutine InitCold(this, bounds, & em_roof_lun, em_wall_lun, em_improad_lun, em_perroad_lun, & - is_simple_buildtemp, is_prog_buildtemp) + is_simple_buildtemp, is_prog_buildtemp, exice_init_stream_col) ! ! !DESCRIPTION: ! Initialize cold start conditions for module variables @@ -647,11 +649,12 @@ subroutine InitCold(this, bounds, & ! !USES: use shr_kind_mod , only : r8 => shr_kind_r8 use shr_const_mod , only : SHR_CONST_TKFRZ - use clm_varcon , only : denice, denh2o + use clm_varcon , only : denice, denh2o, zisoi use landunit_varcon, only : istwet, istsoil, istdlak, istice, istcrop use column_varcon , only : icol_road_imperv, icol_roof, icol_sunwall use column_varcon , only : icol_shadewall, icol_road_perv use clm_varctl , only : iulog, use_vancouver, use_mexicocity, use_excess_ice + use initVerticalMod , only : find_soil_layer_containing_depth ! ! !ARGUMENTS: class(temperature_type) :: this @@ -662,6 +665,7 @@ subroutine InitCold(this, bounds, & real(r8) , intent(in) :: em_perroad_lun(bounds%begl:) logical , intent(in) :: is_simple_buildtemp ! Simple building temp is being used logical , intent(in) :: is_prog_buildtemp ! Prognostic building temp is being used + real(r8) , intent(in) :: exice_init_stream_col(bounds%begc:) ! initial ammount of excess ice from the stream file ! ! !LOCAL VARIABLES: integer :: j,l,c,p ! indices @@ -669,6 +673,7 @@ subroutine InitCold(this, bounds, & real(r8) :: snowbd ! temporary calculation of snow bulk density (kg/m3) real(r8) :: fmelt ! snowbd/100 integer :: lev + integer :: n05m ! layer number containing 0.5 meter depth !----------------------------------------------------------------------- SHR_ASSERT_ALL_FL((ubound(em_roof_lun) == (/bounds%endl/)), sourcefile, __LINE__) @@ -742,8 +747,14 @@ subroutine InitCold(this, bounds, & end if else this%t_soisno_col(c,1:nlevgrnd) = 272._r8 - if (use_excess_ice .and. (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop)) then - this%t_soisno_col(c,1:nlevgrnd) = SHR_CONST_TKFRZ - 5.0_r8 !needs to be below freezing to properly initiate excess ice + if (use_excess_ice .and. exice_init_stream_col(c) > 0.0_r8) then + n05m = nlevsoi - 1 + if (zisoi(nlevsoi) >= 0.5_r8) then + call find_soil_layer_containing_depth(0.5_r8,n05m) + else + n05m=nlevsoi-1 + endif + this%t_soisno_col(c,n05m:nlevgrnd) = SHR_CONST_TKFRZ - 12.15_r8 !needs to be below freezing to properly initiate excess ice end if endif endif diff --git a/src/biogeophys/WaterStateType.F90 b/src/biogeophys/WaterStateType.F90 index 390e9e8691..4c82e4efa6 100644 --- a/src/biogeophys/WaterStateType.F90 +++ b/src/biogeophys/WaterStateType.F90 @@ -56,8 +56,6 @@ module WaterStateType real(r8), pointer :: excess_ice_col (:,:) ! col excess ice (kg/m2) (new) (-nlevsno+1:nlevgrnd) real(r8), pointer :: exice_bulk_init (:) ! inital value for excess ice (new) (unitless) - type(excessicestream_type), private :: exicestream ! stream type for excess ice initialization NUOPC only - ! Hillslope stream variables real(r8), pointer :: stream_water_volume_lun(:) ! landunit volume of water in the streams (m3) @@ -82,7 +80,7 @@ module WaterStateType !------------------------------------------------------------------------ subroutine Init(this, bounds, info, tracer_vars, & - h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer, NLFilename) + h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer, NLFilename, exice_init_stream_col) class(waterstate_type), intent(inout) :: this type(bounds_type) , intent(in) :: bounds @@ -91,8 +89,9 @@ subroutine Init(this, bounds, info, tracer_vars, & real(r8) , intent(in) :: h2osno_input_col(bounds%begc:) real(r8) , intent(in) :: watsat_col(bounds%begc:, 1:) ! volumetric soil water at saturation (porosity) real(r8) , intent(in) :: t_soisno_col(bounds%begc:, -nlevsno+1:) ! col soil temperature (Kelvin) - logical , intent(in) :: use_aquifer_layer ! whether an aquifer layer is used in this run - character(len=*) , intent(in) :: NLFilename ! Namelist filename + logical , intent(in) :: use_aquifer_layer ! whether an aquifer layer is used in this run + character(len=*) , intent(in) :: NLFilename ! Namelist filename + real(r8) , intent(in) :: exice_init_stream_col(bounds%begc:bounds%endc) ! initial ammount of excess ice from stream this%info => info @@ -104,7 +103,7 @@ subroutine Init(this, bounds, info, tracer_vars, & watsat_col = watsat_col, & t_soisno_col = t_soisno_col, & use_aquifer_layer = use_aquifer_layer, & - NLFilename = NLFilename) + NLFilename = NLFilename, exice_init_stream_col = exice_init_stream_col) end subroutine Init @@ -323,7 +322,7 @@ end subroutine InitHistory !----------------------------------------------------------------------- subroutine InitCold(this, bounds, & - h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer, NLFilename) + h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer, NLFilename, exice_init_stream_col) ! ! !DESCRIPTION: ! Initialize time constant variables and cold start conditions @@ -344,6 +343,7 @@ subroutine InitCold(this, bounds, & real(r8) , intent(in) :: t_soisno_col(bounds%begc:, -nlevsno+1:) ! col soil temperature (Kelvin) logical , intent(in) :: use_aquifer_layer ! whether an aquifer layer is used in this run character(len=*) , intent(in) :: NLFilename ! Namelist filename + real(r8) , intent(in) :: exice_init_stream_col(bounds%begc:bounds%endc) ! initial ammount of excess ice from stream ! ! !LOCAL VARIABLES: integer :: c,j,l,nlevs,g @@ -550,52 +550,40 @@ subroutine InitCold(this, bounds, & this%dynbal_baseline_ice_col(bounds%begc:bounds%endc) = 0._r8 !Initialize excess ice - if (use_excess_ice .and. NLFilename /= '') then - ! enforce initialization with 0 for everything - this%excess_ice_col(bounds%begc:bounds%endc,-nlevsno+1:nlevmaxurbgrnd)=0.0_r8 - this%exice_bulk_init(bounds%begc:bounds%endc)=0.0_r8 - call this%exicestream%Init(bounds, NLFilename) ! get initial fraction of excess ice per column - ! - ! If excess ice is being read from streams, use the streams to - ! initialize - ! - if ( UseExcessIceStreams() )then - call this%exicestream%CalcExcessIce(bounds, this%exice_bulk_init) - do c = bounds%begc,bounds%endc - g = col%gridcell(c) - l = col%landunit(c) - if (.not. lun%lakpoi(l)) then !not lake - if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then - if (zisoi(nlevsoi) >= 0.5_r8) then - call find_soil_layer_containing_depth(0.5_r8,n05m) - else - n05m=nlevsoi-1 - endif - if (use_bedrock .and. col%nbedrock(c) <=nlevsoi) then - nbedrock = col%nbedrock(c) - else - nbedrock = nlevsoi - endif - do j = 2, nlevmaxurbgrnd ! ignore first layer - if (n05m= n05m .and. j= 0.5_r8) then + call find_soil_layer_containing_depth(0.5_r8,n05m) + else + n05m=nlevsoi-1 + endif + if (use_bedrock .and. col%nbedrock(c) <=nlevsoi) then + nbedrock = col%nbedrock(c) + else + nbedrock = nlevsoi endif - else ! just in case zeros for lakes and other columns - this%excess_ice_col(c,-nlevsno+1:nlevmaxurbgrnd) = 0.0_r8 + do j = 2, nlevmaxurbgrnd ! ignore first layer + if (n05m= n05m .and. j Date: Thu, 11 Apr 2024 05:14:52 -0600 Subject: [PATCH 048/406] Add use_streams default for coldstart --- bld/CLMBuildNamelist.pm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index fd8def8845..4a338201d5 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4564,6 +4564,10 @@ sub setup_logic_exice { my $use_exice = $nl->get_value( 'use_excess_ice' ); my $use_exice_streams = $nl->get_value( 'use_excess_ice_streams' ); # IF excess ice streams is on + if ( (not defined($use_exice_streams) && value_is_true($use_exice)) && ($nl_flags->{'clm_start_type'} == /cold/ || $nl_flags->{'clm_start_type'} == /arb_ic/ ) ) { + $nl->set_variable_value('exice_streams', 'use_excess_ice_streams' , '.true.'); + $use_exice_streams = '.true.' + } if (defined($use_exice_streams) && value_is_true($use_exice_streams)) { # Can only be true if excess ice is also on, otherwise fail if (defined($use_exice) && not value_is_true($use_exice)) { From a3179995bcd7149edf1de4cc5aa32e23ae71ac9c Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Thu, 11 Apr 2024 05:15:04 -0600 Subject: [PATCH 049/406] plumbing --- src/biogeophys/WaterStateBulkType.F90 | 5 +++-- src/biogeophys/WaterType.F90 | 23 +++++++++++++++-------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/biogeophys/WaterStateBulkType.F90 b/src/biogeophys/WaterStateBulkType.F90 index ba3f0513c5..4f03ba177e 100644 --- a/src/biogeophys/WaterStateBulkType.F90 +++ b/src/biogeophys/WaterStateBulkType.F90 @@ -47,7 +47,7 @@ module WaterStateBulkType !------------------------------------------------------------------------ subroutine InitBulk(this, bounds, info, vars, & - h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer, NLFilename) + h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer, NLFilename, exice_init_stream_col) class(waterstatebulk_type), intent(inout) :: this type(bounds_type) , intent(in) :: bounds @@ -58,6 +58,7 @@ subroutine InitBulk(this, bounds, info, vars, & real(r8) , intent(in) :: t_soisno_col(bounds%begc:, -nlevsno+1:) ! col soil temperature (Kelvin) logical , intent(in) :: use_aquifer_layer ! whether an aquifer layer is used in this run character(len=*) , intent(in) :: NLFilename ! Namelist filename + real(r8) , intent(in) :: exice_init_stream_col(bounds%begc:) ! initial ammount of excess ice from stream call this%Init(bounds = bounds, & info = info, & @@ -66,7 +67,7 @@ subroutine InitBulk(this, bounds, info, vars, & watsat_col = watsat_col, & t_soisno_col = t_soisno_col, & use_aquifer_layer = use_aquifer_layer, & - NLFilename = NLFilename) + NLFilename = NLFilename, exice_init_stream_col = exice_init_stream_col(bounds%begc:bounds%endc)) call this%InitBulkAllocate(bounds) diff --git a/src/biogeophys/WaterType.F90 b/src/biogeophys/WaterType.F90 index bc257f27ad..e92dc0c759 100644 --- a/src/biogeophys/WaterType.F90 +++ b/src/biogeophys/WaterType.F90 @@ -213,7 +213,7 @@ end function water_params_constructor !----------------------------------------------------------------------- subroutine Init(this, bounds, NLFilename, & - h2osno_col, snow_depth_col, watsat_col, t_soisno_col, use_aquifer_layer) + h2osno_col, snow_depth_col, watsat_col, t_soisno_col, use_aquifer_layer, exice_init_stream_col) ! ! !DESCRIPTION: ! Initialize all water variables @@ -227,6 +227,7 @@ subroutine Init(this, bounds, NLFilename, & real(r8) , intent(in) :: watsat_col(bounds%begc:, 1:) ! volumetric soil water at saturation (porosity) real(r8) , intent(in) :: t_soisno_col(bounds%begc:, -nlevsno+1:) ! col soil temperature (Kelvin) logical , intent(in) :: use_aquifer_layer ! whether an aquifer layer is used in this run + real(r8) , intent(in) :: exice_init_stream_col(bounds%begc:) ! initial ammount of excess ice from stream ! ! !LOCAL VARIABLES: @@ -240,14 +241,14 @@ subroutine Init(this, bounds, NLFilename, & watsat_col = watsat_col, & t_soisno_col = t_soisno_col, & use_aquifer_layer = use_aquifer_layer, & - NLFilename = NLFilename) + NLFilename = NLFilename, exice_init_stream_col = exice_init_stream_col) end subroutine Init !----------------------------------------------------------------------- subroutine InitForTesting(this, bounds, params, & h2osno_col, snow_depth_col, watsat_col, & - t_soisno_col, use_aquifer_layer, NLFilename) + t_soisno_col, use_aquifer_layer, NLFilename, exice_init_stream_col) ! ! !DESCRIPTION: ! Version of Init routine just for unit tests @@ -262,8 +263,9 @@ subroutine InitForTesting(this, bounds, params, & real(r8) , intent(in) :: snow_depth_col(bounds%begc:) real(r8) , intent(in) :: watsat_col(bounds%begc:, 1:) ! volumetric soil water at saturation (porosity) real(r8) , intent(in) :: t_soisno_col(bounds%begc:, -nlevsno+1:) ! col soil temperature (Kelvin) - character(len=*) , intent(in) :: NLFilename ! Namelist filename + character(len=*) , intent(in) :: NLFilename ! Namelist filename logical , intent(in), optional :: use_aquifer_layer ! whether an aquifer layer is used in this run (false by default) + real(r8) , intent(in) :: exice_init_stream_col(bounds%begc:) ! initial ammount of excess ice from stream ! ! !LOCAL VARIABLES: logical :: l_use_aquifer_layer @@ -283,13 +285,14 @@ subroutine InitForTesting(this, bounds, params, & watsat_col = watsat_col, & t_soisno_col = t_soisno_col, & use_aquifer_layer = l_use_aquifer_layer, & - NLFilename = NLFilename) + NLFilename = NLFilename, & + exice_init_stream_col = exice_init_stream_col ) end subroutine InitForTesting !----------------------------------------------------------------------- subroutine DoInit(this, bounds, & - h2osno_col, snow_depth_col, watsat_col, t_soisno_col, use_aquifer_layer, NLFilename) + h2osno_col, snow_depth_col, watsat_col, t_soisno_col, use_aquifer_layer, NLFilename, exice_init_stream_col) ! ! !DESCRIPTION: ! Actually do the initialization (shared between main Init routine and InitForTesting) @@ -305,6 +308,7 @@ subroutine DoInit(this, bounds, & real(r8) , intent(in) :: t_soisno_col(bounds%begc:, -nlevsno+1:) ! col soil temperature (Kelvin) logical , intent(in) :: use_aquifer_layer ! whether an aquifer layer is used in this run character(len=*) , intent(in) :: NLFilename ! Namelist filename + real(r8) , intent(in) :: exice_init_stream_col(bounds%begc:) ! initial ammount of excess ice from stream ! ! !LOCAL VARIABLES: integer :: begc, endc @@ -321,6 +325,7 @@ subroutine DoInit(this, bounds, & SHR_ASSERT_ALL_FL((ubound(snow_depth_col) == [endc]), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(watsat_col, 1) == endc), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(t_soisno_col, 1) == endc), sourcefile, __LINE__) + SHR_ASSERT_ALL_FL((ubound(exice_init_stream_col, 1) == endc), sourcefile, __LINE__) call this%SetupTracerInfo() @@ -340,7 +345,8 @@ subroutine DoInit(this, bounds, & watsat_col = watsat_col(begc:endc, 1:), & t_soisno_col = t_soisno_col(begc:endc, -nlevsno+1:), & use_aquifer_layer = use_aquifer_layer, & - NLFilename = NLFilename) + NLFilename = NLFilename, & + exice_init_stream_col = exice_init_stream_col) call this%waterdiagnosticbulk_inst%InitBulk(bounds, & bulk_info, & @@ -381,7 +387,8 @@ subroutine DoInit(this, bounds, & watsat_col = watsat_col(begc:endc, 1:), & t_soisno_col = t_soisno_col(begc:endc, -nlevsno+1:), & use_aquifer_layer = use_aquifer_layer, & - NLFilename = NLFilename) + NLFilename = NLFilename, & + exice_init_stream_col = exice_init_stream_col) call this%bulk_and_tracers(i)%waterdiagnostic_inst%Init(bounds, & this%bulk_and_tracers(i)%info, & From a8fad1df11260ce6f106a067399b2d008e926a1a Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Thu, 11 Apr 2024 06:50:53 -0600 Subject: [PATCH 050/406] add parameters for coldstart initialization. rename vars. --- bld/CLMBuildNamelist.pm | 4 +++- bld/namelist_files/namelist_defaults_ctsm.xml | 2 ++ bld/namelist_files/namelist_definition_ctsm.xml | 11 +++++++++++ src/biogeophys/TemperatureType.F90 | 15 ++++++++------- src/main/clm_varctl.F90 | 6 +++++- src/main/controlMod.F90 | 11 ++++++++++- 6 files changed, 39 insertions(+), 10 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 4a338201d5..1a69d59d0c 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4575,7 +4575,7 @@ sub setup_logic_exice { } # Otherwise if ice streams are off } else { - my @list = ( "stream_meshfile_exice", "stream_fldfilename_exice" ); + my @list = ( "stream_meshfile_exice", "stream_fldfilename_exice" , "excess_ice_coldstart_temp" , "excess_ice_coldstart_depth"); # fail is excess ice streams files are set foreach my $var ( @list ) { if ( defined($nl->get_value($var)) ) { @@ -4594,6 +4594,8 @@ sub setup_logic_exice { if (defined($use_exice_streams) && value_is_true($use_exice_streams)) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldfilename_exice'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_mapalgo_exice'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'excess_ice_coldstart_temp'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'excess_ice_coldstart_depth'); # If excess ice streams on, but NOT the NUOPC driver fail if ( not $opts->{'driver'} eq "nuopc" ) { $log->fatal_error("nuopc driver is required when use_excess_ice_streams is set to true" ); diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index a01839836d..380e3f875a 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -2891,6 +2891,8 @@ use_crop=".true.">lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_mpasa1 .false. +-12.15 +1.5 lnd/clm2/paramdata/exice_init_0.125x0.125_c20220516.nc lnd/clm2/paramdata/exice_init_0.125x0.125_ESMFmesh_cdf5_c20220802.nc bilinear diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index a2e9fb86da..61c16cd4cb 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -3050,6 +3050,17 @@ use case.) If TRUE turn on the excess ice physics, (Lee et al., 2014; Cai et al., 2020) + +Initial soil temperature to use for gridcells with excess ice present during a run starting with coldstart (deg C) + + + +Soil depth below which initial excess ice concentration will be applied during a run starting with coldstart (m) + + + If TRUE and use_excess_ice is TRUE, use the excess ice stream to determine the initial values of the excess ice field diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 7906773b26..0a31bba6e6 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -653,7 +653,8 @@ subroutine InitCold(this, bounds, & use landunit_varcon, only : istwet, istsoil, istdlak, istice, istcrop use column_varcon , only : icol_road_imperv, icol_roof, icol_sunwall use column_varcon , only : icol_shadewall, icol_road_perv - use clm_varctl , only : iulog, use_vancouver, use_mexicocity, use_excess_ice + use clm_varctl , only : iulog, use_vancouver, use_mexicocity + use clm_varctl , only : use_excess_ice, excess_ice_coldstart_depth, excess_ice_coldstart_temp use initVerticalMod , only : find_soil_layer_containing_depth ! ! !ARGUMENTS: @@ -673,7 +674,7 @@ subroutine InitCold(this, bounds, & real(r8) :: snowbd ! temporary calculation of snow bulk density (kg/m3) real(r8) :: fmelt ! snowbd/100 integer :: lev - integer :: n05m ! layer number containing 0.5 meter depth + integer :: nexice_start ! layer number containing 0.5 meter depth !----------------------------------------------------------------------- SHR_ASSERT_ALL_FL((ubound(em_roof_lun) == (/bounds%endl/)), sourcefile, __LINE__) @@ -748,13 +749,13 @@ subroutine InitCold(this, bounds, & else this%t_soisno_col(c,1:nlevgrnd) = 272._r8 if (use_excess_ice .and. exice_init_stream_col(c) > 0.0_r8) then - n05m = nlevsoi - 1 - if (zisoi(nlevsoi) >= 0.5_r8) then - call find_soil_layer_containing_depth(0.5_r8,n05m) + nexice_start = nlevsoi - 1 + if (zisoi(nlevsoi) >= excess_ice_coldstart_depth) then + call find_soil_layer_containing_depth(0.5_r8,nexice_start) else - n05m=nlevsoi-1 + nexice_start=nlevsoi-1 endif - this%t_soisno_col(c,n05m:nlevgrnd) = SHR_CONST_TKFRZ - 12.15_r8 !needs to be below freezing to properly initiate excess ice + this%t_soisno_col(c,nexice_start:nlevgrnd) = SHR_CONST_TKFRZ + excess_ice_coldstart_temp !needs to be below freezing to properly initiate excess ice end if endif endif diff --git a/src/main/clm_varctl.F90 b/src/main/clm_varctl.F90 index 389ed50f88..09f3dd176a 100644 --- a/src/main/clm_varctl.F90 +++ b/src/main/clm_varctl.F90 @@ -411,10 +411,14 @@ module clm_varctl logical, public :: use_hillslope_routing = .false. ! true => use surface water routing in hillslope hydrology !---------------------------------------------------------- - ! excess ice physics switch + ! excess ice physics switch and params !---------------------------------------------------------- logical, public :: use_excess_ice = .false. ! true. => use excess ice physics + real(r8), public :: excess_ice_coldstart_temp = rundef ! initial coldstart soil temperature for gridcells where excess ice is present + + real(r8), public :: excess_ice_coldstart_depth = rundef ! initial coldstart depth at which excess ice might be present and excess_ice_coldstart_temp will be applied + !---------------------------------------------------------- ! plant hydraulic stress switch !---------------------------------------------------------- diff --git a/src/main/controlMod.F90 b/src/main/controlMod.F90 index dc9622ddac..8ec95c9e81 100644 --- a/src/main/controlMod.F90 +++ b/src/main/controlMod.F90 @@ -256,7 +256,8 @@ subroutine control_init(dtime) namelist /clm_inparm/ use_soil_moisture_streams - namelist /clm_inparm/ use_excess_ice + ! excess ice flag and parameters + namelist /clm_inparm/ use_excess_ice , excess_ice_coldstart_depth, excess_ice_coldstart_temp namelist /clm_inparm/ use_lai_streams @@ -823,6 +824,10 @@ subroutine control_spmd() call mpi_bcast (use_excess_ice, 1, MPI_LOGICAL, 0, mpicom,ier) + call mpi_bcast (excess_ice_coldstart_depth, 1, MPI_REAL8, 0, mpicom, ier) + + call mpi_bcast (excess_ice_coldstart_temp, 1, MPI_REAL8, 0, mpicom, ier) + call mpi_bcast (use_lai_streams, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_cropcal_streams, 1, MPI_LOGICAL, 0, mpicom, ier) @@ -977,6 +982,10 @@ subroutine control_print () write(iulog,*) ' use_extralakelayers = ', use_extralakelayers write(iulog,*) ' use_vichydro = ', use_vichydro write(iulog,*) ' use_excess_ice = ', use_excess_ice + if (use_excess_ice) then + write(iulog,*) ' excess_ice_coldstart_depth = ', excess_ice_coldstart_depth + write(iulog,*) ' excess_ice_coldstart_temp = ', excess_ice_coldstart_temp + endif write(iulog,*) ' use_cn = ', use_cn write(iulog,*) ' use_cndv = ', use_cndv write(iulog,*) ' use_crop = ', use_crop From 9b3527e333ae50e300a363af1a20b9e2e2a3c04c Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Thu, 11 Apr 2024 07:04:41 -0600 Subject: [PATCH 051/406] change default values for new namelist vars --- bld/namelist_files/namelist_defaults_ctsm.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 380e3f875a..2e346038f4 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -2891,8 +2891,8 @@ use_crop=".true.">lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_mpasa1 .false. --12.15 -1.5 +-2.15 +0.5 lnd/clm2/paramdata/exice_init_0.125x0.125_c20220516.nc lnd/clm2/paramdata/exice_init_0.125x0.125_ESMFmesh_cdf5_c20220802.nc bilinear From 66c100482afdb26bb431d5c1e71deda28e3cdb69 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 26 Apr 2024 13:56:24 -0600 Subject: [PATCH 052/406] Update cmeps to the branch --- Externals.cfg | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Externals.cfg b/Externals.cfg index 185f412cab..1aa8991883 100644 --- a/Externals.cfg +++ b/Externals.cfg @@ -48,9 +48,12 @@ tag = cime6.0.217_httpsbranch03 required = True [cmeps] -tag = cmeps0.14.50 +#tag = cmeps0.14.50 +#branch = dust_emis_mod +hash = d79e34f67df825f9265bdaaf3091f8995d5df1a2 protocol = git -repo_url = https://github.com/ESCOMP/CMEPS.git +#repo_url = https://github.com/ESCOMP/CMEPS.git +repo_url = https://github.com/ekluzek/CMEPS.git local_path = components/cmeps required = True From f8aa6eac32a00c821824478860c06a26fbdb9010 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 26 Apr 2024 15:02:04 -0600 Subject: [PATCH 053/406] Add shr_dust_emis_mod.F90 to the files needed for unit testing, fails, because ESMF is currently needed and not available --- src/CMakeLists.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 568b53cd15..9d2e6041b5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,9 +20,11 @@ add_subdirectory(${CLM_ROOT}/share/src csm_share) add_subdirectory(${CLM_ROOT}/share/unit_test_stubs/util csm_share_stubs) add_subdirectory(${CLM_ROOT}/share/src/esmf_wrf_timemgr esmf_wrf_timemgr) -# Add the single file we need from CMEPS -set (drv_sources_needed - ${CLM_ROOT}/components/cmeps/cesm/nuopc_cap_share/glc_elevclass_mod.F90) +# Add files needed from CMEPS +list ( APPEND drv_sources_needed + ${CLM_ROOT}/components/cmeps/cesm/nuopc_cap_share/glc_elevclass_mod.F90 + ${CLM_ROOT}/components/cmeps/cesm/nuopc_cap_share/shr_dust_emis_mod.F90 + ) # Add CLM source directories add_subdirectory(${CLM_ROOT}/src/utils clm_utils) From b767a82b5eb448817e2e36609bde1633e105e358 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 29 Apr 2024 18:59:30 -0600 Subject: [PATCH 054/406] Start adding the unit testing for dust emission in cmeps --- Externals.cfg | 2 +- src/CMakeLists.txt | 4 ++ src/drv_test/CMakeLists.txt | 1 + .../shr_dust_emis_test/CMakeLists.txt | 3 ++ .../shr_dust_emis_test/test_shr_dust_emis.pf | 50 +++++++++++++++++++ 5 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/drv_test/CMakeLists.txt create mode 100644 src/drv_test/shr_dust_emis_test/CMakeLists.txt create mode 100644 src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf diff --git a/Externals.cfg b/Externals.cfg index 1aa8991883..70c0bd6124 100644 --- a/Externals.cfg +++ b/Externals.cfg @@ -50,7 +50,7 @@ required = True [cmeps] #tag = cmeps0.14.50 #branch = dust_emis_mod -hash = d79e34f67df825f9265bdaaf3091f8995d5df1a2 +hash = 492faf0014ad9d91c22bb92e2570e558a76093fe protocol = git #repo_url = https://github.com/ESCOMP/CMEPS.git repo_url = https://github.com/ekluzek/CMEPS.git diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9d2e6041b5..9388e65bc2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -105,3 +105,7 @@ add_subdirectory(${CLM_ROOT}/src/dyn_subgrid/test clm_dyn_subgrid_test) add_subdirectory(${CLM_ROOT}/src/main/test clm_main_test) add_subdirectory(${CLM_ROOT}/src/init_interp/test clm_init_interp_test) add_subdirectory(${CLM_ROOT}/src/self_tests/test clm_self_tests_test) + +# Add driver unit test directories +add_subdirectory(${CLM_ROOT}/src/drv_test drv_test) + diff --git a/src/drv_test/CMakeLists.txt b/src/drv_test/CMakeLists.txt new file mode 100644 index 0000000000..adf66b8b92 --- /dev/null +++ b/src/drv_test/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(shr_dust_emis_test) diff --git a/src/drv_test/shr_dust_emis_test/CMakeLists.txt b/src/drv_test/shr_dust_emis_test/CMakeLists.txt new file mode 100644 index 0000000000..21fb9c8d47 --- /dev/null +++ b/src/drv_test/shr_dust_emis_test/CMakeLists.txt @@ -0,0 +1,3 @@ +add_pfunit_ctest(dust_emis + TEST_SOURCES "test_shr_dust_emis.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf new file mode 100644 index 0000000000..aa60427eac --- /dev/null +++ b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf @@ -0,0 +1,50 @@ +module test_shr_dust_emis + + ! Tests of shr_dust_emis_mod.F90 from CMEPS nuopc_cap_share + + use funit + use shr_dust_emis_mod, only : check_if_initialized + use unittestUtils, only : endrun_msg + + implicit none + + @TestCase + type, extends(TestCase) :: TestDustEmis + contains + procedure :: setUp + procedure :: tearDown + end type TestDustEmis + +contains + + ! ======================================================================== + ! Helper routines + ! ======================================================================== + + subroutine setUp(this) + class(TestDustEmis), intent(inout) :: this + end subroutine setUp + + subroutine tearDown(this) + class(TestDustEmis), intent(inout) :: this + + end subroutine tearDown + + + + ! ======================================================================== + ! Begin tests + ! ======================================================================== + + @Test + subroutine check_if_initialized_aborts(this) + ! Test that the check_if_initialized check aborts when called initially + class(TestDustEmis), intent(inout) :: this + + call check_if_initialized() + @assertExceptionRaised(endrun_msg('ERROR: dust emission namelist has NOT been read in yet, shr_dust_emis_mod is NOT initialized') ) + + end subroutine check_if_initialized_aborts + + +end module test_shr_dust_emis From 82704fe7e86aa68f2bd7254d83bb62c53f4aabeb Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 30 Apr 2024 09:21:16 -0600 Subject: [PATCH 055/406] Change call to is_NOT_initialized function, unit test works --- src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf index aa60427eac..a619618e1d 100644 --- a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf +++ b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf @@ -3,7 +3,7 @@ module test_shr_dust_emis ! Tests of shr_dust_emis_mod.F90 from CMEPS nuopc_cap_share use funit - use shr_dust_emis_mod, only : check_if_initialized + use shr_dust_emis_mod, only : is_NOT_initialized use unittestUtils, only : endrun_msg implicit none @@ -40,9 +40,11 @@ contains subroutine check_if_initialized_aborts(this) ! Test that the check_if_initialized check aborts when called initially class(TestDustEmis), intent(inout) :: this + logical :: not_init - call check_if_initialized() + not_init = is_NOT_initialized() @assertExceptionRaised(endrun_msg('ERROR: dust emission namelist has NOT been read in yet, shr_dust_emis_mod is NOT initialized') ) + @assertTrue(not_init) end subroutine check_if_initialized_aborts From 118010da9524fa08401ba6af3c2b6be560b0d2c7 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 30 Apr 2024 10:51:45 -0600 Subject: [PATCH 056/406] add a test that is expected to fail --- .../shr_dust_emis_test/test_shr_dust_emis.pf | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf index a619618e1d..646b3cfce1 100644 --- a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf +++ b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf @@ -3,8 +3,8 @@ module test_shr_dust_emis ! Tests of shr_dust_emis_mod.F90 from CMEPS nuopc_cap_share use funit - use shr_dust_emis_mod, only : is_NOT_initialized - use unittestUtils, only : endrun_msg + use shr_dust_emis_mod + use unittestUtils , only : endrun_msg implicit none @@ -48,5 +48,16 @@ contains end subroutine check_if_initialized_aborts + @Test + subroutine check_when_initialized_runs(this) + ! Test that the initializiation check runs when it is initialized + class(TestDustEmis), intent(inout) :: this + logical :: not_init + + call dust_emis_set_options( 'Zender_2003', 'lnd') + not_init = is_NOT_initialized() + @assertFalse(not_init) + + end subroutine check_when_initialized_runs end module test_shr_dust_emis From 516d14721ae3f29c40316c6fb7fbd4fd7d9970d2 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 30 Apr 2024 11:10:03 -0600 Subject: [PATCH 057/406] Update cmeps version so will work --- Externals.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Externals.cfg b/Externals.cfg index 70c0bd6124..3753aac874 100644 --- a/Externals.cfg +++ b/Externals.cfg @@ -50,7 +50,7 @@ required = True [cmeps] #tag = cmeps0.14.50 #branch = dust_emis_mod -hash = 492faf0014ad9d91c22bb92e2570e558a76093fe +hash = ecb6d45aa00a4960564ea963b00754fb4c3b7d2c protocol = git #repo_url = https://github.com/ESCOMP/CMEPS.git repo_url = https://github.com/ekluzek/CMEPS.git From 5cf5dc04734ff81fc77b43e8a003af826e9553a4 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 30 Apr 2024 14:31:23 -0600 Subject: [PATCH 058/406] Add more tests that pass --- .../shr_dust_emis_test/test_shr_dust_emis.pf | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf index 646b3cfce1..efe6a5c2a8 100644 --- a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf +++ b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf @@ -60,4 +60,35 @@ contains end subroutine check_when_initialized_runs + @Test + subroutine check_dust_emis(this) + ! Test that the dust_emis logical functions work as expected + class(TestDustEmis), intent(inout) :: this + logical :: not_init + + call dust_emis_set_options( 'Zender_2003', 'lnd') + @assertTrue( is_dust_emis_zender() ) + @assertFalse( is_dust_emis_leung() ) + call dust_emis_set_options( 'Leung_2023', 'lnd') + @assertFalse( is_dust_emis_zender() ) + @assertTrue( is_dust_emis_leung() ) + + end subroutine check_dust_emis + + + @Test + subroutine check_zender_soil(this) + ! Test that the dust_emis_Zender logical functions work as expected + class(TestDustEmis), intent(inout) :: this + logical :: not_init + + call dust_emis_set_options( 'Zender_2003', 'lnd') + @assertTrue( is_zender_soil_erod_from_land() ) + @assertFalse( is_zender_soil_erod_from_atm() ) + call dust_emis_set_options( 'Zender_2003', 'atm') + @assertFalse( is_zender_soil_erod_from_land() ) + @assertTrue( is_zender_soil_erod_from_atm() ) + + end subroutine check_zender_soil + end module test_shr_dust_emis From c2d226c20c1ae3cbbf5f3e2b91a7bee67fd0e5aa Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 1 May 2024 13:50:29 -0600 Subject: [PATCH 059/406] Add new tests and get working again --- .../shr_dust_emis_test/test_shr_dust_emis.pf | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf index efe6a5c2a8..467d00de59 100644 --- a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf +++ b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf @@ -69,7 +69,7 @@ contains call dust_emis_set_options( 'Zender_2003', 'lnd') @assertTrue( is_dust_emis_zender() ) @assertFalse( is_dust_emis_leung() ) - call dust_emis_set_options( 'Leung_2023', 'lnd') + call dust_emis_set_options( 'Leung_2023', 'none') @assertFalse( is_dust_emis_zender() ) @assertTrue( is_dust_emis_leung() ) @@ -90,5 +90,25 @@ contains @assertTrue( is_zender_soil_erod_from_atm() ) end subroutine check_zender_soil + + + @Test + subroutine check_options(this) + ! Test that the check_options subroutine catches errors that should die + class(TestDustEmis), intent(inout) :: this + logical :: not_init + + call dust_emis_set_options( 'zztop', 'zztop') + @assertExceptionRaised(endrun_msg('(check_options_finish_init) ERROR: dust_emis_method namelist item is not valid')) + call dust_emis_set_options( 'Leung_2023', 'lnd') + @assertExceptionRaised(endrun_msg('(check_options_finish_init) ERROR: zender_soil_erod_source should NOT be set, when dust_emis_method=Leung_2023')) + call dust_emis_set_options( 'Leung_2023', 'atm') + @assertExceptionRaised(endrun_msg('(check_options_finish_init) ERROR: zender_soil_erod_source should NOT be set, when dust_emis_method=Leung_2023')) + call dust_emis_set_options( 'Zender_2003', 'none') + @assertExceptionRaised(endrun_msg('(check_options_finish_init) ERROR: zender_soil_erod_source can only be lnd or atm')) + call dust_emis_set_options( 'Zender_2003', 'zztop') + @assertExceptionRaised(endrun_msg('(check_options_finish_init) ERROR: zender_soil_erod_source can only be lnd or atm')) + + end subroutine check_options end module test_shr_dust_emis From 0ff68d77dc824f6dbb0c8f6cac0a9ec3773e0b8c Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 1 May 2024 14:05:25 -0600 Subject: [PATCH 060/406] Update cmeps with unit-testing completed --- Externals.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Externals.cfg b/Externals.cfg index 3753aac874..39818b0fdc 100644 --- a/Externals.cfg +++ b/Externals.cfg @@ -50,7 +50,7 @@ required = True [cmeps] #tag = cmeps0.14.50 #branch = dust_emis_mod -hash = ecb6d45aa00a4960564ea963b00754fb4c3b7d2c +hash = 0285eeb59605df44bc3abf38a61e8436361e7e8c protocol = git #repo_url = https://github.com/ESCOMP/CMEPS.git repo_url = https://github.com/ekluzek/CMEPS.git From f65de8fc0968316fe7e9a4c99bbc6e897dee5a92 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sat, 4 May 2024 11:08:24 -0600 Subject: [PATCH 061/406] Add LND_SETS_DUST_EMIS_DRV_FLDS which will be used to NOT set dust_emis drv_flds_in settings when CAM is going to set them --- cime_config/config_component.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index f7adab268f..c474b7e0c1 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -162,6 +162,20 @@ This is typically set by the compset. + + logical + TRUE,FALSE + TRUE + + + + run_component_cpl + env_run.xml + If CTSM will set the dust settings in drv_flds_in (TRUE), or if ATM (i.e. CAM) will - DO NOT EDIT (set by compset name) + + char clm,nwp From 89f08710f61275cb0ac1dea22b5de849787682b2 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sat, 4 May 2024 11:17:34 -0600 Subject: [PATCH 062/406] Move dust_emis and soil_erod namelist items to the drv_fld_in namelist file in CMEPS --- bld/CLMBuildNamelist.pm | 13 ++++++++----- bld/namelist_files/namelist_defaults_ctsm.xml | 2 -- bld/namelist_files/namelist_definition_ctsm.xml | 14 -------------- .../namelist_definition_drv_flds.xml | 13 +++++++++++++ bld/unit_testers/build-namelist_test.pl | 14 +++++++++++++- 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index fb44023cd5..ce2c2a81d2 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -517,6 +517,7 @@ sub read_namelist_defaults { "$cfgdir/namelist_files/namelist_defaults_ctsm.xml", "$cfgdir/namelist_files/namelist_defaults_drv.xml", "$cfgdir/namelist_files/namelist_defaults_fire_emis.xml", + "$cfgdir/namelist_files/namelist_defaults_dust_emis.xml", "$cfgdir/namelist_files/namelist_defaults_drydep.xml" ); # Add the location of the use case defaults files to the options hash @@ -3988,7 +3989,7 @@ sub setup_logic_fire_emis { #------------------------------------------------------------------------------- sub setup_logic_dust_emis { - # Logic to handle the dust emissions + # Logic to handle the dust emissions namelists, both drv_flds_in and lnd_in files my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; # First get the dust emission method @@ -4011,14 +4012,16 @@ sub setup_logic_dust_emis { 'dust_emis_method'=>$dust_emis_method, 'zender_soil_erod_source'=>$zender_source, 'hgrid'=>$nl_flags->{'res'}, 'lnd_tuning_mod'=>$nl_flags->{'lnd_tuning_mode'} ); } - } else { + } elsif ( $zender_source eq "atm" ) { foreach my $option ( @zender_files_in_lnd_opts ) { if ( defined($nl->get_value($option)) ) { - $log->fatal_error("zender_soil_erod_source is NOT lnd, but the file option $option is being set" . - " and should NOT be unless you want it handled here in the LAND model, " . + $log->fatal_error("zender_soil_erod_source is atm, and the file option $option is being set" . + " which should NOT be unless you want it handled here in the LAND model, " . "otherwise the equivalent option is set in CAM" ); } } + } elsif ( $zender_source eq "none" ) { + $log->fatal_error("zender_soil_erod_source is set to none and only atm or lnd should be used when $var is Zender_2002" ); } } else { # Verify that NONE of the Zender options are being set if Zender is NOT being used @@ -4736,7 +4739,7 @@ sub write_output_files { $log->verbose_message("Writing clm namelist to $outfile"); # Drydep, fire-emission or MEGAN namelist for driver - @groups = qw(drydep_inparm megan_emis_nl fire_emis_nl carma_inparm); + @groups = qw(drydep_inparm megan_emis_nl fire_emis_nl carma_inparm dust_emis_inparm); $outfile = "$opts->{'dir'}/drv_flds_in"; $nl->write($outfile, 'groups'=>\@groups, 'note'=>"$note" ); $log->verbose_message("Writing @groups namelists to $outfile"); diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 147a23f49a..6fde63ee02 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1991,8 +1991,6 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c -Zender_2003 -atm bilinear lnd/clm2/dustemisdata/dst_source2x2tunedcam6-2x2-forCLM_cdf5_c230312.nc diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 37c457141c..e730082e90 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1612,20 +1612,6 @@ Mapping method from Nitrogen deposition input file to the model resolution - -Which dust emission method is going to be used. Either the Zender 2003 scheme or the Leung 2023 -scheme. -(NOTE: The Leung 2023 method is NOT currently available) - - - -Option only applying for the Zender_2003 method for whether the soil erodibility file is handled -here in CTSM, or in the ATM model. -(only used when dust_emis_method is Zender_2003) - - Option only applying for the Zender_2003 method for whether the soil erodibility file is handled diff --git a/bld/namelist_files/namelist_definition_drv_flds.xml b/bld/namelist_files/namelist_definition_drv_flds.xml index 088f5c5fa9..89bab07f4f 100644 --- a/bld/namelist_files/namelist_definition_drv_flds.xml +++ b/bld/namelist_files/namelist_definition_drv_flds.xml @@ -123,4 +123,17 @@ List of fluxes needed by the CARMA model, from CLM to CAM. + + Which dust emission method is going to be used. Either the Zender 2003 scheme or the Leung 2023 scheme. + (NOTE: The Leung 2023 method is NOT currently available) + + + + Option only applying for the Zender_2003 method for whether the soil erodibility file is handled + in the active LAND model or in the ATM model. + (only used when dust_emis_method is Zender_2003) + + diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index 7b654337af..114fb0c36c 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -163,7 +163,7 @@ sub cat_and_create_namelistinfile { # # Figure out number of tests that will run # -my $ntests = 2513; +my $ntests = 2515; if ( defined($opts{'compare'}) ) { $ntests += 1545; @@ -1255,6 +1255,18 @@ sub cat_and_create_namelistinfile { GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_1", }, + "soil_erod_none_w_Zender" =>{ options=>"--envxml_dir .", + namelst=>"dust_emis_method='Zender_2003', " . + "zender_soil_erod_source='none'", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_1", + }, + "soil_erod_bad_w_Zender" =>{ options=>"--envxml_dir .", + namelst=>"dust_emis_method='Zender_2003', " . + "zender_soil_erod_source='zztop'", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_1", + }, ); foreach my $key ( keys(%failtest) ) { print( "$key\n" ); From 6f8021cb9be81521f4f905b27b353ce9de1980be Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 7 May 2024 03:30:20 -0600 Subject: [PATCH 063/406] Start adding reference for LND_SETS_DUST_EMIS_DRV_FLDS in unit tester --- bld/unit_testers/build-namelist_test.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index 114fb0c36c..30110d92f7 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -42,7 +42,7 @@ sub make_env_run { my %settings = @_; # Set default settings - my %env_vars = ( DIN_LOC_ROOT=>"MYDINLOCROOT", GLC_TWO_WAY_COUPLING=>"FALSE", NEONSITE=>"" ); + my %env_vars = ( DIN_LOC_ROOT=>"MYDINLOCROOT", GLC_TWO_WAY_COUPLING=>"FALSE", LND_SETS_DUST_EMIS_DRV_FLDS=>"TRUE", NEONSITE=>"" ); # Set any settings that came in from function call foreach my $item ( keys(%settings) ) { $env_vars{$item} = $settings{$item}; From f52f76fc0c71f3521a0680207141b1545dd6098a Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 7 May 2024 03:31:04 -0600 Subject: [PATCH 064/406] Add dust emis namelist default for drv_flds_in --- .../namelist_defaults_dust_emis.xml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 bld/namelist_files/namelist_defaults_dust_emis.xml diff --git a/bld/namelist_files/namelist_defaults_dust_emis.xml b/bld/namelist_files/namelist_defaults_dust_emis.xml new file mode 100644 index 0000000000..40bf3d9078 --- /dev/null +++ b/bld/namelist_files/namelist_defaults_dust_emis.xml @@ -0,0 +1,21 @@ + + + + + + + + + +Zender_2003 + +atm + + From 8aa0a708ca8751aa770ccc40a66fb962d26f5acc Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 26 Apr 2024 13:56:24 -0600 Subject: [PATCH 065/406] Update cmeps to the branch --- Externals.cfg | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Externals.cfg b/Externals.cfg index 185f412cab..1aa8991883 100644 --- a/Externals.cfg +++ b/Externals.cfg @@ -48,9 +48,12 @@ tag = cime6.0.217_httpsbranch03 required = True [cmeps] -tag = cmeps0.14.50 +#tag = cmeps0.14.50 +#branch = dust_emis_mod +hash = d79e34f67df825f9265bdaaf3091f8995d5df1a2 protocol = git -repo_url = https://github.com/ESCOMP/CMEPS.git +#repo_url = https://github.com/ESCOMP/CMEPS.git +repo_url = https://github.com/ekluzek/CMEPS.git local_path = components/cmeps required = True From 603d2677f85eff3a15a284ded3d89722371b81be Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 26 Apr 2024 15:02:04 -0600 Subject: [PATCH 066/406] Add shr_dust_emis_mod.F90 to the files needed for unit testing, fails, because ESMF is currently needed and not available --- src/CMakeLists.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 568b53cd15..9d2e6041b5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,9 +20,11 @@ add_subdirectory(${CLM_ROOT}/share/src csm_share) add_subdirectory(${CLM_ROOT}/share/unit_test_stubs/util csm_share_stubs) add_subdirectory(${CLM_ROOT}/share/src/esmf_wrf_timemgr esmf_wrf_timemgr) -# Add the single file we need from CMEPS -set (drv_sources_needed - ${CLM_ROOT}/components/cmeps/cesm/nuopc_cap_share/glc_elevclass_mod.F90) +# Add files needed from CMEPS +list ( APPEND drv_sources_needed + ${CLM_ROOT}/components/cmeps/cesm/nuopc_cap_share/glc_elevclass_mod.F90 + ${CLM_ROOT}/components/cmeps/cesm/nuopc_cap_share/shr_dust_emis_mod.F90 + ) # Add CLM source directories add_subdirectory(${CLM_ROOT}/src/utils clm_utils) From 0b48097c502c98521b8014171a2d4f3cdaa5dec2 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 29 Apr 2024 18:59:30 -0600 Subject: [PATCH 067/406] Start adding the unit testing for dust emission in cmeps --- Externals.cfg | 2 +- src/CMakeLists.txt | 4 ++ src/drv_test/CMakeLists.txt | 1 + .../shr_dust_emis_test/CMakeLists.txt | 3 ++ .../shr_dust_emis_test/test_shr_dust_emis.pf | 50 +++++++++++++++++++ 5 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/drv_test/CMakeLists.txt create mode 100644 src/drv_test/shr_dust_emis_test/CMakeLists.txt create mode 100644 src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf diff --git a/Externals.cfg b/Externals.cfg index 1aa8991883..70c0bd6124 100644 --- a/Externals.cfg +++ b/Externals.cfg @@ -50,7 +50,7 @@ required = True [cmeps] #tag = cmeps0.14.50 #branch = dust_emis_mod -hash = d79e34f67df825f9265bdaaf3091f8995d5df1a2 +hash = 492faf0014ad9d91c22bb92e2570e558a76093fe protocol = git #repo_url = https://github.com/ESCOMP/CMEPS.git repo_url = https://github.com/ekluzek/CMEPS.git diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9d2e6041b5..9388e65bc2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -105,3 +105,7 @@ add_subdirectory(${CLM_ROOT}/src/dyn_subgrid/test clm_dyn_subgrid_test) add_subdirectory(${CLM_ROOT}/src/main/test clm_main_test) add_subdirectory(${CLM_ROOT}/src/init_interp/test clm_init_interp_test) add_subdirectory(${CLM_ROOT}/src/self_tests/test clm_self_tests_test) + +# Add driver unit test directories +add_subdirectory(${CLM_ROOT}/src/drv_test drv_test) + diff --git a/src/drv_test/CMakeLists.txt b/src/drv_test/CMakeLists.txt new file mode 100644 index 0000000000..adf66b8b92 --- /dev/null +++ b/src/drv_test/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(shr_dust_emis_test) diff --git a/src/drv_test/shr_dust_emis_test/CMakeLists.txt b/src/drv_test/shr_dust_emis_test/CMakeLists.txt new file mode 100644 index 0000000000..21fb9c8d47 --- /dev/null +++ b/src/drv_test/shr_dust_emis_test/CMakeLists.txt @@ -0,0 +1,3 @@ +add_pfunit_ctest(dust_emis + TEST_SOURCES "test_shr_dust_emis.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf new file mode 100644 index 0000000000..aa60427eac --- /dev/null +++ b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf @@ -0,0 +1,50 @@ +module test_shr_dust_emis + + ! Tests of shr_dust_emis_mod.F90 from CMEPS nuopc_cap_share + + use funit + use shr_dust_emis_mod, only : check_if_initialized + use unittestUtils, only : endrun_msg + + implicit none + + @TestCase + type, extends(TestCase) :: TestDustEmis + contains + procedure :: setUp + procedure :: tearDown + end type TestDustEmis + +contains + + ! ======================================================================== + ! Helper routines + ! ======================================================================== + + subroutine setUp(this) + class(TestDustEmis), intent(inout) :: this + end subroutine setUp + + subroutine tearDown(this) + class(TestDustEmis), intent(inout) :: this + + end subroutine tearDown + + + + ! ======================================================================== + ! Begin tests + ! ======================================================================== + + @Test + subroutine check_if_initialized_aborts(this) + ! Test that the check_if_initialized check aborts when called initially + class(TestDustEmis), intent(inout) :: this + + call check_if_initialized() + @assertExceptionRaised(endrun_msg('ERROR: dust emission namelist has NOT been read in yet, shr_dust_emis_mod is NOT initialized') ) + + end subroutine check_if_initialized_aborts + + +end module test_shr_dust_emis From d2d2da45ec2662c404424096b03a9b32debd9e01 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 30 Apr 2024 09:21:16 -0600 Subject: [PATCH 068/406] Change call to is_NOT_initialized function, unit test works --- src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf index aa60427eac..a619618e1d 100644 --- a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf +++ b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf @@ -3,7 +3,7 @@ module test_shr_dust_emis ! Tests of shr_dust_emis_mod.F90 from CMEPS nuopc_cap_share use funit - use shr_dust_emis_mod, only : check_if_initialized + use shr_dust_emis_mod, only : is_NOT_initialized use unittestUtils, only : endrun_msg implicit none @@ -40,9 +40,11 @@ contains subroutine check_if_initialized_aborts(this) ! Test that the check_if_initialized check aborts when called initially class(TestDustEmis), intent(inout) :: this + logical :: not_init - call check_if_initialized() + not_init = is_NOT_initialized() @assertExceptionRaised(endrun_msg('ERROR: dust emission namelist has NOT been read in yet, shr_dust_emis_mod is NOT initialized') ) + @assertTrue(not_init) end subroutine check_if_initialized_aborts From 66578f0efd1f575b3adfe49bf1682e9c6fe703cb Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 30 Apr 2024 10:51:45 -0600 Subject: [PATCH 069/406] add a test that is expected to fail --- .../shr_dust_emis_test/test_shr_dust_emis.pf | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf index a619618e1d..646b3cfce1 100644 --- a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf +++ b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf @@ -3,8 +3,8 @@ module test_shr_dust_emis ! Tests of shr_dust_emis_mod.F90 from CMEPS nuopc_cap_share use funit - use shr_dust_emis_mod, only : is_NOT_initialized - use unittestUtils, only : endrun_msg + use shr_dust_emis_mod + use unittestUtils , only : endrun_msg implicit none @@ -48,5 +48,16 @@ contains end subroutine check_if_initialized_aborts + @Test + subroutine check_when_initialized_runs(this) + ! Test that the initializiation check runs when it is initialized + class(TestDustEmis), intent(inout) :: this + logical :: not_init + + call dust_emis_set_options( 'Zender_2003', 'lnd') + not_init = is_NOT_initialized() + @assertFalse(not_init) + + end subroutine check_when_initialized_runs end module test_shr_dust_emis From a6927691757bdb91dc4d911461446a4e0746c3aa Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 30 Apr 2024 11:10:03 -0600 Subject: [PATCH 070/406] Update cmeps version so will work --- Externals.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Externals.cfg b/Externals.cfg index 70c0bd6124..3753aac874 100644 --- a/Externals.cfg +++ b/Externals.cfg @@ -50,7 +50,7 @@ required = True [cmeps] #tag = cmeps0.14.50 #branch = dust_emis_mod -hash = 492faf0014ad9d91c22bb92e2570e558a76093fe +hash = ecb6d45aa00a4960564ea963b00754fb4c3b7d2c protocol = git #repo_url = https://github.com/ESCOMP/CMEPS.git repo_url = https://github.com/ekluzek/CMEPS.git From a65752d3d1abfc626631390c6b2378a5813e079f Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 30 Apr 2024 14:31:23 -0600 Subject: [PATCH 071/406] Add more tests that pass --- .../shr_dust_emis_test/test_shr_dust_emis.pf | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf index 646b3cfce1..efe6a5c2a8 100644 --- a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf +++ b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf @@ -60,4 +60,35 @@ contains end subroutine check_when_initialized_runs + @Test + subroutine check_dust_emis(this) + ! Test that the dust_emis logical functions work as expected + class(TestDustEmis), intent(inout) :: this + logical :: not_init + + call dust_emis_set_options( 'Zender_2003', 'lnd') + @assertTrue( is_dust_emis_zender() ) + @assertFalse( is_dust_emis_leung() ) + call dust_emis_set_options( 'Leung_2023', 'lnd') + @assertFalse( is_dust_emis_zender() ) + @assertTrue( is_dust_emis_leung() ) + + end subroutine check_dust_emis + + + @Test + subroutine check_zender_soil(this) + ! Test that the dust_emis_Zender logical functions work as expected + class(TestDustEmis), intent(inout) :: this + logical :: not_init + + call dust_emis_set_options( 'Zender_2003', 'lnd') + @assertTrue( is_zender_soil_erod_from_land() ) + @assertFalse( is_zender_soil_erod_from_atm() ) + call dust_emis_set_options( 'Zender_2003', 'atm') + @assertFalse( is_zender_soil_erod_from_land() ) + @assertTrue( is_zender_soil_erod_from_atm() ) + + end subroutine check_zender_soil + end module test_shr_dust_emis From dd73ca699a83c80f5d2dc36fb9fa7482b4eec5eb Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 1 May 2024 13:50:29 -0600 Subject: [PATCH 072/406] Add new tests and get working again --- .../shr_dust_emis_test/test_shr_dust_emis.pf | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf index efe6a5c2a8..467d00de59 100644 --- a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf +++ b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf @@ -69,7 +69,7 @@ contains call dust_emis_set_options( 'Zender_2003', 'lnd') @assertTrue( is_dust_emis_zender() ) @assertFalse( is_dust_emis_leung() ) - call dust_emis_set_options( 'Leung_2023', 'lnd') + call dust_emis_set_options( 'Leung_2023', 'none') @assertFalse( is_dust_emis_zender() ) @assertTrue( is_dust_emis_leung() ) @@ -90,5 +90,25 @@ contains @assertTrue( is_zender_soil_erod_from_atm() ) end subroutine check_zender_soil + + + @Test + subroutine check_options(this) + ! Test that the check_options subroutine catches errors that should die + class(TestDustEmis), intent(inout) :: this + logical :: not_init + + call dust_emis_set_options( 'zztop', 'zztop') + @assertExceptionRaised(endrun_msg('(check_options_finish_init) ERROR: dust_emis_method namelist item is not valid')) + call dust_emis_set_options( 'Leung_2023', 'lnd') + @assertExceptionRaised(endrun_msg('(check_options_finish_init) ERROR: zender_soil_erod_source should NOT be set, when dust_emis_method=Leung_2023')) + call dust_emis_set_options( 'Leung_2023', 'atm') + @assertExceptionRaised(endrun_msg('(check_options_finish_init) ERROR: zender_soil_erod_source should NOT be set, when dust_emis_method=Leung_2023')) + call dust_emis_set_options( 'Zender_2003', 'none') + @assertExceptionRaised(endrun_msg('(check_options_finish_init) ERROR: zender_soil_erod_source can only be lnd or atm')) + call dust_emis_set_options( 'Zender_2003', 'zztop') + @assertExceptionRaised(endrun_msg('(check_options_finish_init) ERROR: zender_soil_erod_source can only be lnd or atm')) + + end subroutine check_options end module test_shr_dust_emis From ccd976af2ace8896eef72013d80ba3bccd8684af Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 1 May 2024 14:05:25 -0600 Subject: [PATCH 073/406] Update cmeps with unit-testing completed --- Externals.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Externals.cfg b/Externals.cfg index 3753aac874..39818b0fdc 100644 --- a/Externals.cfg +++ b/Externals.cfg @@ -50,7 +50,7 @@ required = True [cmeps] #tag = cmeps0.14.50 #branch = dust_emis_mod -hash = ecb6d45aa00a4960564ea963b00754fb4c3b7d2c +hash = 0285eeb59605df44bc3abf38a61e8436361e7e8c protocol = git #repo_url = https://github.com/ESCOMP/CMEPS.git repo_url = https://github.com/ekluzek/CMEPS.git From 65fc5536bd17b81f3b532ac764e15991b61a1e41 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sat, 4 May 2024 11:08:24 -0600 Subject: [PATCH 074/406] Add LND_SETS_DUST_EMIS_DRV_FLDS which will be used to NOT set dust_emis drv_flds_in settings when CAM is going to set them --- cime_config/config_component.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index f7adab268f..c474b7e0c1 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -162,6 +162,20 @@ This is typically set by the compset. + + logical + TRUE,FALSE + TRUE + + + + run_component_cpl + env_run.xml + If CTSM will set the dust settings in drv_flds_in (TRUE), or if ATM (i.e. CAM) will - DO NOT EDIT (set by compset name) + + char clm,nwp From d9c94eb04ebbf31ef4d5441d26eb59d360b2251d Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sat, 4 May 2024 11:17:34 -0600 Subject: [PATCH 075/406] Move dust_emis and soil_erod namelist items to the drv_fld_in namelist file in CMEPS --- bld/CLMBuildNamelist.pm | 13 ++++++++----- bld/namelist_files/namelist_defaults_ctsm.xml | 2 -- bld/namelist_files/namelist_definition_ctsm.xml | 14 -------------- .../namelist_definition_drv_flds.xml | 13 +++++++++++++ bld/unit_testers/build-namelist_test.pl | 14 +++++++++++++- 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index fb44023cd5..ce2c2a81d2 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -517,6 +517,7 @@ sub read_namelist_defaults { "$cfgdir/namelist_files/namelist_defaults_ctsm.xml", "$cfgdir/namelist_files/namelist_defaults_drv.xml", "$cfgdir/namelist_files/namelist_defaults_fire_emis.xml", + "$cfgdir/namelist_files/namelist_defaults_dust_emis.xml", "$cfgdir/namelist_files/namelist_defaults_drydep.xml" ); # Add the location of the use case defaults files to the options hash @@ -3988,7 +3989,7 @@ sub setup_logic_fire_emis { #------------------------------------------------------------------------------- sub setup_logic_dust_emis { - # Logic to handle the dust emissions + # Logic to handle the dust emissions namelists, both drv_flds_in and lnd_in files my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; # First get the dust emission method @@ -4011,14 +4012,16 @@ sub setup_logic_dust_emis { 'dust_emis_method'=>$dust_emis_method, 'zender_soil_erod_source'=>$zender_source, 'hgrid'=>$nl_flags->{'res'}, 'lnd_tuning_mod'=>$nl_flags->{'lnd_tuning_mode'} ); } - } else { + } elsif ( $zender_source eq "atm" ) { foreach my $option ( @zender_files_in_lnd_opts ) { if ( defined($nl->get_value($option)) ) { - $log->fatal_error("zender_soil_erod_source is NOT lnd, but the file option $option is being set" . - " and should NOT be unless you want it handled here in the LAND model, " . + $log->fatal_error("zender_soil_erod_source is atm, and the file option $option is being set" . + " which should NOT be unless you want it handled here in the LAND model, " . "otherwise the equivalent option is set in CAM" ); } } + } elsif ( $zender_source eq "none" ) { + $log->fatal_error("zender_soil_erod_source is set to none and only atm or lnd should be used when $var is Zender_2002" ); } } else { # Verify that NONE of the Zender options are being set if Zender is NOT being used @@ -4736,7 +4739,7 @@ sub write_output_files { $log->verbose_message("Writing clm namelist to $outfile"); # Drydep, fire-emission or MEGAN namelist for driver - @groups = qw(drydep_inparm megan_emis_nl fire_emis_nl carma_inparm); + @groups = qw(drydep_inparm megan_emis_nl fire_emis_nl carma_inparm dust_emis_inparm); $outfile = "$opts->{'dir'}/drv_flds_in"; $nl->write($outfile, 'groups'=>\@groups, 'note'=>"$note" ); $log->verbose_message("Writing @groups namelists to $outfile"); diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index cc407961ff..b232c865cb 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1993,8 +1993,6 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c -Zender_2003 -atm bilinear lnd/clm2/dustemisdata/dst_source2x2tunedcam6-2x2-forCLM_cdf5_c230312.nc diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 37c457141c..e730082e90 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1612,20 +1612,6 @@ Mapping method from Nitrogen deposition input file to the model resolution - -Which dust emission method is going to be used. Either the Zender 2003 scheme or the Leung 2023 -scheme. -(NOTE: The Leung 2023 method is NOT currently available) - - - -Option only applying for the Zender_2003 method for whether the soil erodibility file is handled -here in CTSM, or in the ATM model. -(only used when dust_emis_method is Zender_2003) - - Option only applying for the Zender_2003 method for whether the soil erodibility file is handled diff --git a/bld/namelist_files/namelist_definition_drv_flds.xml b/bld/namelist_files/namelist_definition_drv_flds.xml index 088f5c5fa9..89bab07f4f 100644 --- a/bld/namelist_files/namelist_definition_drv_flds.xml +++ b/bld/namelist_files/namelist_definition_drv_flds.xml @@ -123,4 +123,17 @@ List of fluxes needed by the CARMA model, from CLM to CAM. + + Which dust emission method is going to be used. Either the Zender 2003 scheme or the Leung 2023 scheme. + (NOTE: The Leung 2023 method is NOT currently available) + + + + Option only applying for the Zender_2003 method for whether the soil erodibility file is handled + in the active LAND model or in the ATM model. + (only used when dust_emis_method is Zender_2003) + + diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index 7b654337af..114fb0c36c 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -163,7 +163,7 @@ sub cat_and_create_namelistinfile { # # Figure out number of tests that will run # -my $ntests = 2513; +my $ntests = 2515; if ( defined($opts{'compare'}) ) { $ntests += 1545; @@ -1255,6 +1255,18 @@ sub cat_and_create_namelistinfile { GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_1", }, + "soil_erod_none_w_Zender" =>{ options=>"--envxml_dir .", + namelst=>"dust_emis_method='Zender_2003', " . + "zender_soil_erod_source='none'", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_1", + }, + "soil_erod_bad_w_Zender" =>{ options=>"--envxml_dir .", + namelst=>"dust_emis_method='Zender_2003', " . + "zender_soil_erod_source='zztop'", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_1", + }, ); foreach my $key ( keys(%failtest) ) { print( "$key\n" ); From 0c23d08c2bad4677b70afe22640e2a8bb5f4543a Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 2 May 2024 14:26:53 -0600 Subject: [PATCH 076/406] Update ChangeLog and ChangeSum. --- doc/ChangeLog | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++ doc/ChangeSum | 1 + 2 files changed, 82 insertions(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index e5c2466085..e0a5e45cb6 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,85 @@ =============================================================== +Tag name: ctsm5.2.003 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Thu May 2 14:06:54 MDT 2024 +One-line Summary: Merge b4b-dev + +Purpose and description of changes +---------------------------------- + +Brings in 4 PRs from b4b-dev to master: +- Regional CTSM Simulations and Capability of Creating Mesh Files (ESCOMP/CTSM#1892; Negin Sobhani and Adrianna Foster) +- Add line about documentation in PR template (ESCOMP/CTSM#2488; Sam Rabin) +- CTSM5.2 2000 fsurdat T42 64x128 file (ESCOMP/CTSM#2495; Sam Levis) +- Move plumber2 scripts to python directory (ESCOMP/CTSM#2505; Teagan King) + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm6_0 + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- + +List of CTSM issues fixed (include CTSM Issue # and description): +- Resolves ESCOMP/CTSM#1513: Need a process to subset ESMF mesh files from global ones for regional grids +- Resolves ESCOMP/CTSM#1773: High resolution regional simulations +- Resolves ESCOMP/CTSM#2187: Move new PLUMBER2 scripts to python directory to enable python testing +- Resolves ESCOMP/CTSM#2486: Temporarily add back a T42 dataset for CAM + + +Notes of particular relevance for users +--------------------------------------- + +Changes to documentation: +- Adds documentation for making mesh files + + +Notes of particular relevance for developers: +--------------------------------------------- + +Changes to tests or testing: +- Adds testing for mesh-making Python scripts +- Adds testing for plumber2_surf_wrapper + + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +- ESCOMP/CTSM#2513: Merge b4b-dev 2024-05-02 +- Constituent PRs: + - ESCOMP/CTSM#1892: Regional CTSM Simulations and Capability of Creating Mesh Files (https://github.com/ESCOMP/CTSM/pull/1892) + - ESCOMP/CTSM#2488: Add line about documentation in PR template (https://github.com/ESCOMP/CTSM/pull/2488) + - ESCOMP/CTSM#2495: CTSM5.2 2000 fsurdat T42 64x128 file (https://github.com/ESCOMP/CTSM/pull/2495) + - ESCOMP/CTSM#2505: Move plumber2 scripts to python directory (https://github.com/ESCOMP/CTSM/pull/2505) + +=============================================================== +=============================================================== Tag name: ctsm5.2.002 Originator(s): glemieux (Gregory Lemieux, LBNL, glemieux@lbl.gov) Date: Fri 26 Apr 2024 11:13:46 AM MDT diff --git a/doc/ChangeSum b/doc/ChangeSum index 40033a7d6f..b48e04889d 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.2.003 samrabin 05/02/2024 Merge b4b-dev ctsm5.2.002 glemieux 04/26/2024 FATES default allometry parameter file update ctsm5.2.001 erik 04/22/2024 Merge b4b-dev ctsm5.2.0 many 04/20/2024 New mksurfdata_esmf tool to create new surface datasets that are in place From 747d104c156733f520008819359612c0b8b1dd83 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 7 May 2024 03:30:20 -0600 Subject: [PATCH 077/406] Start adding reference for LND_SETS_DUST_EMIS_DRV_FLDS in unit tester --- bld/unit_testers/build-namelist_test.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index 114fb0c36c..30110d92f7 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -42,7 +42,7 @@ sub make_env_run { my %settings = @_; # Set default settings - my %env_vars = ( DIN_LOC_ROOT=>"MYDINLOCROOT", GLC_TWO_WAY_COUPLING=>"FALSE", NEONSITE=>"" ); + my %env_vars = ( DIN_LOC_ROOT=>"MYDINLOCROOT", GLC_TWO_WAY_COUPLING=>"FALSE", LND_SETS_DUST_EMIS_DRV_FLDS=>"TRUE", NEONSITE=>"" ); # Set any settings that came in from function call foreach my $item ( keys(%settings) ) { $env_vars{$item} = $settings{$item}; From c36f3a9e6aa9e58f8ff05d4db7ee7349690841f5 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 7 May 2024 03:31:04 -0600 Subject: [PATCH 078/406] Add dust emis namelist default for drv_flds_in --- .../namelist_defaults_dust_emis.xml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 bld/namelist_files/namelist_defaults_dust_emis.xml diff --git a/bld/namelist_files/namelist_defaults_dust_emis.xml b/bld/namelist_files/namelist_defaults_dust_emis.xml new file mode 100644 index 0000000000..40bf3d9078 --- /dev/null +++ b/bld/namelist_files/namelist_defaults_dust_emis.xml @@ -0,0 +1,21 @@ + + + + + + + + + +Zender_2003 + +atm + + From 8ab4063d65dff584758d054c05287e49a00d9130 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 15 May 2024 11:14:02 -0600 Subject: [PATCH 079/406] Git rebase didn't correct the change files for some reason, so going back to the b4b-dev version of those --- doc/ChangeLog | 81 --------------------------------------------------- doc/ChangeSum | 1 - 2 files changed, 82 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index e0a5e45cb6..e5c2466085 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,85 +1,4 @@ =============================================================== -Tag name: ctsm5.2.003 -Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) -Date: Thu May 2 14:06:54 MDT 2024 -One-line Summary: Merge b4b-dev - -Purpose and description of changes ----------------------------------- - -Brings in 4 PRs from b4b-dev to master: -- Regional CTSM Simulations and Capability of Creating Mesh Files (ESCOMP/CTSM#1892; Negin Sobhani and Adrianna Foster) -- Add line about documentation in PR template (ESCOMP/CTSM#2488; Sam Rabin) -- CTSM5.2 2000 fsurdat T42 64x128 file (ESCOMP/CTSM#2495; Sam Levis) -- Move plumber2 scripts to python directory (ESCOMP/CTSM#2505; Teagan King) - - -Significant changes to scientifically-supported configurations --------------------------------------------------------------- - -Does this tag change answers significantly for any of the following physics configurations? -(Details of any changes will be given in the "Answer changes" section below.) - - [Put an [X] in the box for any configuration with significant answer changes.] - -[ ] clm6_0 - -[ ] clm5_1 - -[ ] clm5_0 - -[ ] ctsm5_0-nwp - -[ ] clm4_5 - - -Bugs fixed ----------- - -List of CTSM issues fixed (include CTSM Issue # and description): -- Resolves ESCOMP/CTSM#1513: Need a process to subset ESMF mesh files from global ones for regional grids -- Resolves ESCOMP/CTSM#1773: High resolution regional simulations -- Resolves ESCOMP/CTSM#2187: Move new PLUMBER2 scripts to python directory to enable python testing -- Resolves ESCOMP/CTSM#2486: Temporarily add back a T42 dataset for CAM - - -Notes of particular relevance for users ---------------------------------------- - -Changes to documentation: -- Adds documentation for making mesh files - - -Notes of particular relevance for developers: ---------------------------------------------- - -Changes to tests or testing: -- Adds testing for mesh-making Python scripts -- Adds testing for plumber2_surf_wrapper - - -Testing summary: ----------------- - - regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): - - derecho ----- OK - izumi ------- OK - - -Other details -------------- - -Pull Requests that document the changes (include PR ids): -- ESCOMP/CTSM#2513: Merge b4b-dev 2024-05-02 -- Constituent PRs: - - ESCOMP/CTSM#1892: Regional CTSM Simulations and Capability of Creating Mesh Files (https://github.com/ESCOMP/CTSM/pull/1892) - - ESCOMP/CTSM#2488: Add line about documentation in PR template (https://github.com/ESCOMP/CTSM/pull/2488) - - ESCOMP/CTSM#2495: CTSM5.2 2000 fsurdat T42 64x128 file (https://github.com/ESCOMP/CTSM/pull/2495) - - ESCOMP/CTSM#2505: Move plumber2 scripts to python directory (https://github.com/ESCOMP/CTSM/pull/2505) - -=============================================================== -=============================================================== Tag name: ctsm5.2.002 Originator(s): glemieux (Gregory Lemieux, LBNL, glemieux@lbl.gov) Date: Fri 26 Apr 2024 11:13:46 AM MDT diff --git a/doc/ChangeSum b/doc/ChangeSum index b48e04889d..40033a7d6f 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,6 +1,5 @@ Tag Who Date Summary ============================================================================================================================ - ctsm5.2.003 samrabin 05/02/2024 Merge b4b-dev ctsm5.2.002 glemieux 04/26/2024 FATES default allometry parameter file update ctsm5.2.001 erik 04/22/2024 Merge b4b-dev ctsm5.2.0 many 04/20/2024 New mksurfdata_esmf tool to create new surface datasets that are in place From b706c5a717652f24ca30e7dd43285b9e527b0f16 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 16 May 2024 16:22:11 -0600 Subject: [PATCH 080/406] Have build-namelist not do the dust emission namelist settings, when the XML variable is set for CAM to do it, and add a test for this --- bld/CLMBuildNamelist.pm | 120 +++++++++-------- bld/unit_testers/build-namelist_test.pl | 169 ++---------------------- 2 files changed, 80 insertions(+), 209 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index ce2c2a81d2..98fdd861a0 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -97,7 +97,7 @@ OPTIONS CENTURY or MIMICS decomposition This toggles on the namelist variables: use_fates. use_lch4 and use_nitrif_denitrif are optional - + (Only for CLM4.5/CLM5.0) -[no-]chk_res Also check [do NOT check] to make sure the resolution and land-mask is valid. @@ -1012,7 +1012,7 @@ sub setup_cmdl_fire_light_res { if ( ! &value_is_true($nl_flags->{'neon'}) ) { if ( defined($opts->{'clm_usr_name'}) ) { $log->warning("The NEON lightning dataset does NOT cover the entire globe, make sure it covers the region for your grid"); - } else { + } else { $log->fatal_error("The NEON lightning dataset can NOT be used for global grids or regions or points outside of its area as it does NOT cover the entire globe."); } } @@ -1702,7 +1702,7 @@ sub process_namelist_inline_logic { ###################################### # namelist options for dust emissions ###################################### - setup_logic_dust_emis($opts, $nl_flags, $definition, $defaults, $nl); + setup_logic_dust_emis($opts, $nl_flags, $definition, $defaults, $nl, $envxml_ref); ################################# # namelist group: megan_emis_nl # @@ -2273,7 +2273,7 @@ sub setup_logic_crop_inparm { } add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, "initial_seed_at_planting", 'use_crop'=>$nl->get_value('use_crop') ); - + my $crop_residue_removal_frac = $nl->get_value('crop_residue_removal_frac'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'crop_residue_removal_frac' ); if ( $crop_residue_removal_frac < 0.0 or $crop_residue_removal_frac > 1.0 ) { @@ -2614,8 +2614,8 @@ SIMYR: foreach my $sim_yr ( @sim_years ) { if ( defined($use_init_interp_default) ) { $log->fatal_error($err_msg." but can't find one without $useinitvar being set to true, change it to true in your user_nl_clm file in your case"); } else { - my $set = "Relevent settings: use_cndv = ". $nl_flags->{'use_cndv'} . " phys = " . - $physv->as_string() . " hgrid = " . $nl_flags->{'res'} . " sim_year = " . + my $set = "Relevent settings: use_cndv = ". $nl_flags->{'use_cndv'} . " phys = " . + $physv->as_string() . " hgrid = " . $nl_flags->{'res'} . " sim_year = " . $settings{'sim_year'} . " lnd_tuning_mode = " . $nl_flags->{'lnd_tuning_mode'} . "use_fates = " . $nl_flags->{'use_fates'}; $log->fatal_error($err_msg." but the default setting of $useinitvar is false, so set both $var to a startup file and $useinitvar==TRUE, or developers should modify the namelist_defaults file".$set); @@ -3158,7 +3158,6 @@ sub setup_logic_supplemental_nitrogen { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'suplnitro', 'use_fates'=>$nl_flags->{'use_fates'}); } - # # Error checking for suplnitro # @@ -3683,10 +3682,10 @@ sub setup_logic_nitrogen_deposition { 'use_cn'=>$nl_flags->{'use_cn'}, 'hgrid'=>$nl_flags->{'res'}, 'clm_accelerated_spinup'=>$nl_flags->{'clm_accelerated_spinup'} ); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'ndep_taxmode', 'phys'=>$nl_flags->{'phys'}, - 'use_cn'=>$nl_flags->{'use_cn'}, + 'use_cn'=>$nl_flags->{'use_cn'}, 'lnd_tuning_mode'=>$nl_flags->{'lnd_tuning_mode'} ); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'ndep_varlist', 'phys'=>$nl_flags->{'phys'}, - 'use_cn'=>$nl_flags->{'use_cn'}, + 'use_cn'=>$nl_flags->{'use_cn'}, 'lnd_tuning_mode'=>$nl_flags->{'lnd_tuning_mode'} ); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_first_ndep', 'phys'=>$nl_flags->{'phys'}, 'use_cn'=>$nl_flags->{'use_cn'}, 'sim_year'=>$nl_flags->{'sim_year'}, @@ -3990,51 +3989,64 @@ sub setup_logic_fire_emis { sub setup_logic_dust_emis { # Logic to handle the dust emissions namelists, both drv_flds_in and lnd_in files - my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; + my ($opts, $nl_flags, $definition, $defaults, $nl, $envxml_ref) = @_; - # First get the dust emission method - my $var = "dust_emis_method"; - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var ); - - my $dust_emis_method = remove_leading_and_trailing_quotes( $nl->get_value($var) ); - - my @zender_files_in_lnd_opts = ( "stream_fldfilename_zendersoilerod", "stream_meshfile_zendersoilerod", - "zendersoilerod_mapalgo" ); - if ( $dust_emis_method eq "Zender_2003" ) { - # get the zender_soil_erod_source - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, - "zender_soil_erod_source", 'dust_emis_method'=>$dust_emis_method ); - - my $zender_source = remove_leading_and_trailing_quotes( $nl->get_value('zender_soil_erod_source') ); - if ( $zender_source eq "lnd" ) { - foreach my $option ( @zender_files_in_lnd_opts ) { - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $option, - 'dust_emis_method'=>$dust_emis_method, 'zender_soil_erod_source'=>$zender_source, - 'hgrid'=>$nl_flags->{'res'}, 'lnd_tuning_mod'=>$nl_flags->{'lnd_tuning_mode'} ); - } - } elsif ( $zender_source eq "atm" ) { - foreach my $option ( @zender_files_in_lnd_opts ) { - if ( defined($nl->get_value($option)) ) { - $log->fatal_error("zender_soil_erod_source is atm, and the file option $option is being set" . - " which should NOT be unless you want it handled here in the LAND model, " . - "otherwise the equivalent option is set in CAM" ); - } - } - } elsif ( $zender_source eq "none" ) { - $log->fatal_error("zender_soil_erod_source is set to none and only atm or lnd should be used when $var is Zender_2002" ); - } + # Only set dust emission settings -- if not connected to CAM + my $lnd_sets_dust = logical_to_fortran($envxml_ref->{'LND_SETS_DUST_EMIS_DRV_FLDS'}); + if ( &value_is_true( $lnd_sets_dust)) { + + # First get the dust emission method + my $var = "dust_emis_method"; + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var ); + my $dust_emis_method = remove_leading_and_trailing_quotes( $nl->get_value($var) ); + + my @zender_files_in_lnd_opts = ( "stream_fldfilename_zendersoilerod", "stream_meshfile_zendersoilerod", + "zendersoilerod_mapalgo" ); + if ( $dust_emis_method eq "Zender_2003" ) { + # get the zender_soil_erod_source + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, + "zender_soil_erod_source", 'dust_emis_method'=>$dust_emis_method ); + + my $zender_source = remove_leading_and_trailing_quotes( $nl->get_value('zender_soil_erod_source') ); + if ( $zender_source eq "lnd" ) { + foreach my $option ( @zender_files_in_lnd_opts ) { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $option, + 'dust_emis_method'=>$dust_emis_method, 'zender_soil_erod_source'=>$zender_source, + 'hgrid'=>$nl_flags->{'res'}, 'lnd_tuning_mod'=>$nl_flags->{'lnd_tuning_mode'} ); + } + } elsif ( $zender_source eq "atm" ) { + foreach my $option ( @zender_files_in_lnd_opts ) { + if ( defined($nl->get_value($option)) ) { + $log->fatal_error("zender_soil_erod_source is atm, and the file option $option is being set" . + " which should NOT be unless you want it handled here in the LAND model, " . + "otherwise the equivalent option is set in CAM" ); + } + } + } elsif ( $zender_source eq "none" ) { + $log->fatal_error("zender_soil_erod_source is set to none and only atm or lnd should be used when $var is Zender_2002" ); + } + } else { + # Verify that NONE of the Zender options are being set if Zender is NOT being used + push @zender_files_in_lnd_opts, "zender_soil_erod_source"; + foreach my $option ( @zender_files_in_lnd_opts ) { + if ( defined($nl->get_value($option)) ) { + $log->fatal_error("dust_emis_method is NOT set to Zender_2003, but one of it's options " . + "$option is being set, need to change one or the other" ); + } + } + if ( $dust_emis_method eq "Leung_2023" ) { + $log->warning("dust_emis_method is Leung_2023 and that option has NOT been brought into CTSM yet"); + } + } + # Otherwise make sure dust settings are NOT being set in CLM } else { - # Verify that NONE of the Zender options are being set if Zender is NOT being used - push @zender_files_in_lnd_opts, "zender_soil_erod_source"; - foreach my $option ( @zender_files_in_lnd_opts ) { - if ( defined($nl->get_value($option)) ) { - $log->fatal_error("dust_emis_method is NOT set to Zender_2003, but one of it's options " . - "$option is being set, need to change one or the other" ); - } - } - if ( $dust_emis_method eq "Leung_2023" ) { - $log->warning("dust_emis_method is Leung_2023 and that option has NOT been brought into CTSM yet"); - } + my @vars = ( "dust_emis_method", "zender_soil_erod_source" ); + foreach my $option ( @vars ) { + if ( defined($nl->get_value($option)) ) { + $log->fatal_error("Dust emission variable is being set in CTSM, which should NOT be done when" . + " connected to CAM as CAM should set them"); + } + } } } @@ -4528,8 +4540,8 @@ sub setup_logic_fates { my $suplnitro = $nl->get_value('suplnitro'); my $parteh_mode = $nl->get_value('fates_parteh_mode'); if ( ($parteh_mode == 1) && ($suplnitro !~ /ALL/) && not &value_is_true( $nl_flags->{'use_fates_sp'}) ) { - $log->fatal_error("supplemental Nitrogen (suplnitro) is NOT set to ALL, FATES is on, " . - "but and FATES-SP is not active, but fates_parteh_mode is 1, so Nitrogen is not active" . + $log->fatal_error("supplemental Nitrogen (suplnitro) is NOT set to ALL, FATES is on, " . + "but and FATES-SP is not active, but fates_parteh_mode is 1, so Nitrogen is not active" . "Change suplnitro back to ALL"); } # diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index 30110d92f7..d017997c6a 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -163,7 +163,7 @@ sub cat_and_create_namelistinfile { # # Figure out number of tests that will run # -my $ntests = 2515; +my $ntests = 2516; if ( defined($opts{'compare'}) ) { $ntests += 1545; @@ -500,447 +500,358 @@ sub cat_and_create_namelistinfile { my %failtest = ( "coldstart but with IC file"=>{ options=>"-clm_start_type cold -envxml_dir .", namelst=>"finidat='$finidat'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "clm_demand on finidat" =>{ options=>"-clm_demand finidat -envxml_dir .", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "blank IC file, not cold" =>{ options=>"-clm_start_type startup -envxml_dir .", namelst=>"finidat=' '", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "startup without interp" =>{ options=>"-clm_start_type startup -envxml_dir . -bgc sp -sim_year 1850", namelst=>"use_init_interp=.false., start_ymd=19200901", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "use_crop without -crop" =>{ options=>" -envxml_dir .", namelst=>"use_crop=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "soilm_stream off w file" =>{ options=>"-res 0.9x1.25 -envxml_dir .", namelst=>"use_soil_moisture_streams = .false.,stream_fldfilename_soilm='file_provided_when_off'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "exice_stream off w file" =>{ options=>"-res 0.9x1.25 -envxml_dir .", namelst=>"use_excess_ice=.true., use_excess_ice_streams = .false.,stream_fldfilename_exice='file_provided_when_off'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "exice_stream off w mesh" =>{ options=>"-res 0.9x1.25 -envxml_dir .", namelst=>"use_excess_ice=.true., use_excess_ice_streams = .false.,stream_meshfile_exice='file_provided_when_off'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "exice off, but stream on" =>{ options=>"-res 0.9x1.25 -envxml_dir .", namelst=>"use_excess_ice=.false., use_excess_ice_streams = .true.,stream_fldfilename_exice='file_provided', stream_meshfile_exice='file_provided'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "exice stream off, but setmap"=>{ options=>"-res 0.9x1.25 -envxml_dir .", namelst=>"use_excess_ice=.true., use_excess_ice_streams = .false.,stream_mapalgo_exice='bilinear'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "exice stream on, but mct" =>{ options=>"--res 0.9x1.25 --envxml_dir . --driver mct --lnd_frac $DOMFILE ", namelst=>"use_excess_ice=.true., use_excess_ice_streams=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "clm50CNDVwtransient" =>{ options=>" -envxml_dir . -use_case 20thC_transient -dynamic_vegetation -res 10x15 -ignore_warnings", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "decomp_without_cn" =>{ options=>" -envxml_dir . -bgc sp", namelst=>"soil_decomp_method='CENTURYKoven2013'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "bgc_with_no_decomp" =>{ options=>" -envxml_dir . -bgc bgc", namelst=>"soil_decomp_method='None'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "reseed without CN" =>{ options=>" -envxml_dir . -bgc sp", namelst=>"reseed_dead_plants=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "onset_threh w SP" =>{ options=>" -envxml_dir . -bgc sp", namelst=>"onset_thresh_depends_on_veg=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "dribble_crphrv w/o CN" =>{ options=>" -envxml_dir . -bgc sp", namelst=>"dribble_crophrv_xsmrpool_2atm=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "dribble_crphrv w/o crop" =>{ options=>" -envxml_dir . -bgc bgc -no-crop", namelst=>"dribble_crophrv_xsmrpool_2atm=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "CNDV with flanduse_timeseries - clm4_5"=>{ options=>"-bgc bgc -dynamic_vegetation -envxml_dir . -ignore_warnings", namelst=>"flanduse_timeseries='my_flanduse_timeseries_file.nc'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "use_cndv=T without bldnml op"=>{ options=>"-bgc bgc -envxml_dir . -ignore_warnings", namelst=>"use_cndv=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "use_cndv=F with dyn_veg op"=>{ options=>"-bgc bgc -dynamic_vegetation -envxml_dir . -ignore_warnings", namelst=>"use_cndv=.false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "crop with use_crop false" =>{ options=>"-crop -bgc bgc -envxml_dir .", namelst=>"use_crop=.false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "crop without CN" =>{ options=>"-crop -bgc sp -envxml_dir .", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "toosmall soil w trans" =>{ options=>"-envxml_dir .", namelst=>"toosmall_soil=10, dyn_transient_pfts=T", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "toosmall lake w trans" =>{ options=>"-envxml_dir .", namelst=>"toosmall_lake=10, dyn_transient_pfts=T", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "toosmall crop w trans" =>{ options=>"-bgc bgc -crop -envxml_dir .", namelst=>"toosmall_crop=10, dyn_transient_pfts=T", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "toosmall wetl w trans" =>{ options=>"-bgc bgc -envxml_dir .", namelst=>"toosmall_wetland=10, dyn_transient_pfts=T", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "toosmall glc w trans" =>{ options=>"-bgc sp -envxml_dir .", namelst=>"toosmall_glacier=10, dyn_transient_pfts=T", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "toosmall urban w trans" =>{ options=>"-bgc sp -envxml_dir .", namelst=>"toosmall_urban=10, dyn_transient_pfts=T", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "collapse_urban w trans" =>{ options=>"-bgc sp -envxml_dir .", namelst=>"collapse_urban=T, dyn_transient_crops=T", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "n_dom_landunits w trans" =>{ options=>"-bgc sp -envxml_dir .", namelst=>"n_dom_landunits=2, dyn_transient_crops=T", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "n_dom_pfts w trans" =>{ options=>"-bgc sp -envxml_dir .", namelst=>"n_dom_pfts=2, dyn_transient_crops=T", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "baset_map without crop" =>{ options=>"-bgc bgc -envxml_dir . -no-crop", namelst=>"baset_mapping='constant'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "mapvary var w/o varymap" =>{ options=>"-crop -bgc bgc -envxml_dir . -crop", namelst=>"baset_mapping='constant', baset_latvary_slope=1.0, baset_latvary_intercept=10.0", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "grainproductWOcrop" =>{ options=>"-bgc bgc -no-crop -envxml_dir .", namelst=>"use_grainproduct=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "interp without finidat" =>{ options=>"-bgc sp -envxml_dir .", namelst=>"use_init_interp=.true. finidat=' '", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "sp and c13" =>{ options=>"-bgc sp -envxml_dir .", namelst=>"use_c13=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "sp and c14" =>{ options=>"-bgc sp -envxml_dir .", namelst=>"use_c14=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "bombspike no c14" =>{ options=>"-bgc bgc -envxml_dir .", namelst=>"use_c14=.false. use_c14_bombspike=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "use c13 timeseries no cn" =>{ options=>"-bgc sp -envxml_dir .", namelst=>"use_c13_timeseries=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "use c13 timeseries no c13"=>{ options=>"-bgc bgc -envxml_dir .", namelst=>"use_c13=.false. use_c13_timeseries=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "bombspike no cn" =>{ options=>"-bgc sp -envxml_dir .", namelst=>"use_c14_bombspike=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "lightres no cn" =>{ options=>"-bgc sp -envxml_dir . -light_res 360x720", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "NEONlightresButGlobal" =>{ options=>"--res 4x5 --bgc bgc --envxml_dir . --light_res 106x740", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "spno-fire" =>{ options=>"-bgc sp -envxml_dir . -use_case 2000_control", namelst=>"fire_method='nofire'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "lightres no fire" =>{ options=>"-bgc bgc -envxml_dir . -light_res 360x720", namelst=>"fire_method='nofire'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "lightres none bgc" =>{ options=>"-bgc bgc -envxml_dir . -light_res none", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "lightresnotnone-nofire" =>{ options=>"-bgc bgc -envxml_dir . -light_res 94x192", namelst=>"fire_method='nofire'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "lightresnonenofirelightfil"=>{ options=>"-bgc bgc -envxml_dir . -light_res none", namelst=>"fire_method='nofire',stream_fldfilename_lightng='build-namelist_test.pl'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "lightrescontradictlightfil"=>{ options=>"-bgc bgc -envxml_dir . -light_res 360x720", namelst=>"stream_fldfilename_lightng='build-namelist_test.pl'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "finundated and not methane"=>{ options=>"-bgc bgc -envxml_dir .", namelst=>"use_lch4=.false.,finundation_method='h2osfc'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "use_cn=true bgc=sp" =>{ options=>"-bgc sp -envxml_dir .", namelst=>"use_cn=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "freeliv wo fun" =>{ options=>"-bgc bgc -envxml_dir .", namelst=>"freelivfix_intercept=9.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "use_cn=false bgc=bgc" =>{ options=>"-bgc bgc -envxml_dir .", namelst=>"use_cn=.false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "lower=aqu-45 with/o Zeng" =>{ options=>"-envxml_dir .", namelst=>"lower_boundary_condition=4,soilwater_movement_method=1,use_bedrock=.false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "Zeng w lower=flux" =>{ options=>"-envxml_dir .", namelst=>"lower_boundary_condition=1,soilwater_movement_method=0,use_bedrock=.false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "Zeng w lower=zeroflux" =>{ options=>"-envxml_dir .", namelst=>"lower_boundary_condition=2,soilwater_movement_method=0", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "Zeng w lower=table" =>{ options=>"-envxml_dir .", namelst=>"lower_boundary_condition=3,soilwater_movement_method=0,use_bedrock=.false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "use_vic=F with -vic op" =>{ options=>"-vichydro -envxml_dir .", namelst=>"use_vichydro=.false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "-vic with l_bnd=flux" =>{ options=>"-vichydro -envxml_dir .", namelst=>"lower_boundary_condition=1", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "-vic with l_bnd=zeroflux" =>{ options=>"-vichydro -envxml_dir .", namelst=>"lower_boundary_condition=2", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "bedrock with l_bnc=flux" =>{ options=>"-envxml_dir .", namelst=>"use_bedrock=.true., lower_boundary_condition=1", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "bedrock with l_bnc=tabl" =>{ options=>"-envxml_dir .", namelst=>"use_bedrock=.true., lower_boundary_condition=3", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "bedrock with l_bnc=aqui" =>{ options=>"-envxml_dir .", namelst=>"use_bedrock=.true., lower_boundary_condition=4", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "zengdeck with l_bnc=flux" =>{ options=>"-envxml_dir .", namelst=>"soilwater_movement_method=0, lower_boundary_condition=1", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "zengdeck with l_bnc=z-flux"=>{ options=>"-envxml_dir .", namelst=>"soilwater_movement_method=0, lower_boundary_condition=2", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "zengdeck with l_bnc=tabl" =>{ options=>"-envxml_dir .", namelst=>"soilwater_movement_method=0, lower_boundary_condition=3", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "l_bnd=tabl with h2osfcfl=0"=>{ options=>"-envxml_dir .", namelst=>"h2osfcflag=0, lower_boundary_condition=3", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "l_bnd=flux with h2osfcfl=0"=>{ options=>"-envxml_dir .", namelst=>"h2osfcflag=0, lower_boundary_condition=1", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "l_bnd=zflux with h2osfcfl=0"=>{ options=>"-envxml_dir .", namelst=>"h2osfcflag=0, lower_boundary_condition=2", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "h2osfcfl=0 with clm5.0" =>{ options=>"-envxml_dir .", namelst=>"h2osfcflag=0", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "45bad lnd_tuning_mode value" =>{ options=>"-lnd_tuning_mode clm5_0_GSWP3 -envxml_dir .", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "50bad lnd_tuning_mode value" =>{ options=>"-lnd_tuning_mode clm4_5_CRUNCEP -envxml_dir .", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "bgc_spinup without cn" =>{ options=>"-clm_accelerated_spinup on -bgc sp -envxml_dir .", namelst=>"spinup_state=1", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "spinup=1 without bldnml op"=>{ options=>"-clm_accelerated_spinup off -bgc bgc -envxml_dir .", namelst=>"spinup_state=1",, - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "bgc_spinup without cn" =>{ options=>"-clm_accelerated_spinup on -bgc sp -envxml_dir .", namelst=>"spinup_state=1", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "baseflow w aquifer" =>{ options=>"-bgc sp -envxml_dir .", namelst=>"baseflow_scalar=1.0, lower_boundary_condition=4,use_bedrock=.false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "baseflow w table" =>{ options=>"-bgc sp -envxml_dir .", namelst=>"baseflow_scalar=1.0, lower_boundary_condition=3,use_bedrock=.false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "br_root and bgc=sp" =>{ options=>"-bgc sp -envxml_dir .", namelst=>"br_root=1.0", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "both co2_type and on nml" =>{ options=>"-co2_type constant -envxml_dir .", namelst=>"co2_type='prognostic'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "both lnd_frac and on nml" =>{ options=>"-driver mct -lnd_frac $DOMFILE -envxml_dir .", namelst=>"fatmlndfrc='frac.nc'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "lnd_frac set to UNSET" =>{ options=>"-driver mct -lnd_frac UNSET -envxml_dir .", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "lnd_frac set but nuopc" =>{ options=>"-driver nuopc -lnd_frac $DOMFILE -envxml_dir .", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "lnd_frac not set but lilac"=>{ options=>"-driver nuopc -lilac -envxml_dir . -lnd_frac UNSET", namelst=>"fsurdat='surfdata.nc'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "fatmlndfrc set but nuopc" =>{ options=>"-driver nuopc -envxml_dir .", namelst=>"fatmlndfrc='frac.nc'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "force_send but not nuopc" =>{ options=>"-driver mct -lnd_frac $DOMFILE -envxml_dir .", namelst=>"force_send_to_atm = .false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "branch but NO nrevsn" =>{ options=>"-clm_start_type branch -envxml_dir .", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "glc_nec inconsistent" =>{ options=>"-envxml_dir .", namelst=>"maxpatch_glc=5", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "NoGLCMec" =>{ options=>"-envxml_dir . -glc_nec 0", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "UpdateGlcContradict" =>{ options=>"-envxml_dir .", @@ -950,321 +861,263 @@ sub cat_and_create_namelistinfile { }, "useFATESContradict" =>{ options=>"-bgc fates -envxml_dir . -no-megan", namelst=>"use_fates=.false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "useFATESContradict2" =>{ options=>"-envxml_dir . -no-megan", namelst=>"use_fates=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "useFATESWCN" =>{ options=>"-bgc fates -envxml_dir . -no-megan", namelst=>"use_cn=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "useFATESWcrop" =>{ options=>"-bgc fates -envxml_dir . -no-megan -crop", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "useFATESWcreatecrop" =>{ options=>"-bgc fates -envxml_dir . -no-megan", namelst=>"create_crop_landunit=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "useFATESWn_dom_pft" =>{ options=>"-bgc fates -envxml_dir . -no-megan", namelst=>"n_dom_pfts = 1", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "useFATESWbMH" =>{ options=>"-bgc fates -envxml_dir . -no-megan", namelst=>"use_biomass_heat_storage=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "FireNoneButFATESfireon" =>{ options=>"-bgc fates -envxml_dir . -no-megan -light_res none", namelst=>"fates_spitfire_mode=4", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "FATESwspitfireOffLigtOn" =>{ options=>"-bgc fates -envxml_dir . -no-megan -light_res 360x720", namelst=>"fates_spitfire_mode=0", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "useFATESWluna" =>{ options=>"--bgc fates --envxml_dir . --no-megan", namelst=>"use_luna=TRUE", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "useFATESWfun" =>{ options=>"--bgc fates --envxml_dir . --no-megan", namelst=>"use_fun=TRUE", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "useFATESWOsuplnitro" =>{ options=>"--bgc fates --envxml_dir . --no-megan", namelst=>"suplnitro='NONE'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "FireNoneButBGCfireon" =>{ options=>"-bgc bgc -envxml_dir . -light_res none", namelst=>"fire_method='li2021gswpfrc'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "createcropFalse" =>{ options=>"-bgc bgc -envxml_dir . -no-megan", namelst=>"create_crop_landunit=.false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "usespitfireButNOTFATES" =>{ options=>"-envxml_dir . -no-megan", namelst=>"fates_spitfire_mode=1", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "usespitfireusefatessp" =>{ options=>"-envxml_dir . --bgc fates", namelst=>"fates_spitfire_mode=1,use_fates_sp=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "usefatesspusefateshydro" =>{ options=>"-envxml_dir . --bgc fates", namelst=>"use_fates_sp=.true.,use_fates_planthydro=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "useloggingButNOTFATES" =>{ options=>"-envxml_dir . -no-megan", namelst=>"use_fates_logging=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "useinventorybutnotfile" =>{ options=>"-bgc fates -envxml_dir . -no-megan", namelst=>"use_fates_inventory_init=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "inventoryfileDNE" =>{ options=>"-bgc fates -envxml_dir . -no-megan", namelst=>"use_fates_inventory_init=.true., fates_inventory_ctrl_filename='zztop'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "useinventorybutnotfile" =>{ options=>"--res 0.9x1.25 --bgc fates --envxml_dir . --no-megan", namelst=>"use_fates_luh=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "inventoryfileDNE" =>{ options=>"-bgc fates -envxml_dir . -no-megan", namelst=>"use_fates_luh=.true., fluh_timeseries='zztop'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "useMEGANwithFATES" =>{ options=>"-bgc fates -envxml_dir . -megan", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "useFIREEMISwithFATES" =>{ options=>"-bgc fates -envxml_dir . -fire_emis --no-megan", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "useDRYDEPwithFATES" =>{ options=>"--bgc fates --envxml_dir . --no-megan --drydep", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, "useFATESSPWONOCOMP" =>{ options=>"-bgc fates -envxml_dir . -no-megan", namelst=>"use_fates_sp=T,use_fates_nocomp=F", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "useFATESTRANSWdynPFT" =>{ options=>"-bgc fates -envxml_dir . -use_case 20thC_transient -no-megan", namelst=>"do_transient_pfts=T", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "useHYDSTwithFATES" =>{ options=>"-bgc fates -envxml_dir . -no-megan", namelst=>"use_hydrstress=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "useHYDSTwithdynroot" =>{ options=>"-bgc bgc -envxml_dir . -megan", namelst=>"use_hydrstress=.true., use_dynroot=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "specWOfireemis" =>{ options=>"-envxml_dir . -no-fire_emis", namelst=>"fire_emis_specifier='bc_a1 = BC'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "elevWOfireemis" =>{ options=>"-envxml_dir . -no-fire_emis", namelst=>"fire_emis_elevated=.false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "noanthro_w_crop" =>{ options=>"-envxml_dir . -res 0.9x1.25 -bgc bgc -crop -use_case 1850_noanthro_control", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "noanthro_w_irrig" =>{ options=>"-envxml_dir . -res 0.9x1.25 -bgc bgc -use_case 1850_noanthro_control", namelst=>"irrigate=T", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "spdotransconflict" =>{ options=>"-envxml_dir . -bgc sp -use_case 20thC_transient", namelst=>"do_transient_pfts=T,do_transient_crops=.false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "dogrossandsp" =>{ options=>"--envxml_dir . --bgc sp --use_case 20thC_transient", namelst=>"do_grossunrep=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "dogrossandfates" =>{ options=>"--envxml_dir . --bgc fates --use_case 20thC_transient --no-megan", namelst=>"do_grossunrep=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "dogrossandnottrans" =>{ options=>"--envxml_dir . --bgc bgc --use_case 2000_control", namelst=>"do_grossunrep=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "nocropwfert" =>{ options=>"-envxml_dir . -bgc sp -no-crop", namelst=>"use_fertilizer=T", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "lmr1WOcn" =>{ options=>"-envxml_dir . -bgc sp", namelst=>"leafresp_method=1", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "lmr2WOcn" =>{ options=>"-envxml_dir . -bgc sp", namelst=>"leafresp_method=2", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "lmr0Wcn" =>{ options=>"-envxml_dir . -bgc bgc", namelst=>"leafresp_method=0", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "nofireButSetcli_scale" =>{ options=>"-envxml_dir . -bgc bgc", namelst=>"fire_method='nofire', cli_scale=5.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "nocnButSetrh_low" =>{ options=>"-envxml_dir . -bgc sp", namelst=>"rh_low=5.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "funWOcn" =>{ options=>"-envxml_dir . -bgc sp", namelst=>"use_fun=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "flexCNWOcn" =>{ options=>"-envxml_dir . -bgc sp", namelst=>"use_flexibleCN=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "flexCNFUNwcarbonresp" =>{ options=>"-envxml_dir . -bgc bgc", namelst=>"use_flexibleCN=.true.,use_FUN=.true.,carbon_resp_opt=1", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "funWOnitrif" =>{ options=>"-envxml_dir .", namelst=>"use_fun=.true., use_nitrif_denitrif=.false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "SPModeWNitrifNMethane" =>{ options=>"-envxml_dir . -bgc sp", namelst=>"use_lch4=.true., use_nitrif_denitrif=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "knitrmaxWOnitrif" =>{ options=>"-envxml_dir . -bgc bgc", namelst=>"use_nitrif_denitrif=.false., k_nitr_max=1.0", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "respcoefWOnitrif" =>{ options=>"-envxml_dir . -bgc bgc", namelst=>"use_nitrif_denitrif=.false., denitrif_respiration_coefficient=1.0", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "respexpWOnitrif" =>{ options=>"-envxml_dir . -bgc bgc", namelst=>"use_nitrif_denitrif=.false., denitrif_respiration_exponent=1.0", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "lunaWSPandlnctrue" =>{ options=>"-envxml_dir . -bgc sp", namelst=>"use_luna=.true., lnc_opt=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "NOlunabutsetJmaxb1" =>{ options=>"-envxml_dir . -bgc sp", namelst=>"use_luna=.false., jmaxb1=1.0", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "envxml_not_dir" =>{ options=>"-envxml_dir myuser_nl_clm", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "envxml_emptydir" =>{ options=>"-envxml_dir xFail", namelst=>"", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "fates_non_sp_laistreams" =>{ options=>"--envxml_dir . --bgc fates", namelst=>"use_lai_streams=.true., use_fates_sp=.false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "bgc_non_sp_laistreams" =>{ options=>"--envxml_dir . -bgc bgc", namelst=>"use_lai_streams=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "bgc_laistreams_input" =>{ options=>"--envxml_dir . --bgc bgc", namelst=>"stream_year_first_lai=1999", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "crop_laistreams_input" =>{ options=>"--envxml_dir . --bgc sp --crop", namelst=>"use_lai_streams=.true.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, "soil_erod_wo_Zender" =>{ options=>"--envxml_dir . --ignore_warnings", namelst=>"dust_emis_method='Leung_2023', " . "stream_meshfile_zendersoilerod = '/dev/null'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_1", }, "soil_erod_wo_lnd_source" =>{ options=>"--envxml_dir .", namelst=>"dust_emis_method='Zender_2003', " . "stream_fldfilename_zendersoilerod = '/dev/null', zender_soil_erod_source='atm'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_1", }, "soil_erod_none_w_Zender" =>{ options=>"--envxml_dir .", namelst=>"dust_emis_method='Zender_2003', " . "zender_soil_erod_source='none'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_1", }, "soil_erod_bad_w_Zender" =>{ options=>"--envxml_dir .", namelst=>"dust_emis_method='Zender_2003', " . "zender_soil_erod_source='zztop'", - GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_1", + }, + "Set_Dust_When_CAM_Sets" =>{ options=>"--envxml_dir .", + namelst=>"dust_emis_method='Zender_2003'", + LND_SETS_DUST_EMIS_DRV_FLDS=>"FALSE", phys=>"clm5_1", }, ); @@ -1273,7 +1126,13 @@ sub cat_and_create_namelistinfile { &make_config_cache($failtest{$key}{"phys"}); my $options = $failtest{$key}{"options"}; my $namelist = $failtest{$key}{"namelst"}; - &make_env_run( GLC_TWO_WAY_COUPLING=>$failtest{$key}{"GLC_TWO_WAY_COUPLING"} ); + my %settings; + foreach my $xmlvar ( "GLC_TWO_WAY_COUPLING", "LND_SETS_DUST_EMIS_DRV_FLDS") { + if ( defined($failtest{$key}{$xmlvar}) ) { + $settings{$xmlvar} = $failtest{$key}{$xmlvar}; + } + } + &make_env_run( %settings ); eval{ system( "$bldnml $options -namelist \"&clmexp $namelist /\" > $tempfile 2>&1 " ); }; isnt( $?, 0, $key ); system( "cat $tempfile" ); @@ -1515,7 +1374,7 @@ sub cat_and_create_namelistinfile { # cases; I'm not sure if it's actually important to test this with all # of the different use cases. my @glc_res = ( "0.9x1.25", "1.9x2.5" ); -my @use_cases = ( +my @use_cases = ( "1850-2100_SSP2-4.5_transient", "1850_control", "2000_control", From 03f89ea926cf5f4cfba56b079ce91e1536a5787f Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Thu, 23 May 2024 22:44:24 -0600 Subject: [PATCH 081/406] dmleung added a minor edit on subtimestep wind fluctuation (23 May 2024) --- src/biogeochem/DUSTMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 2986ed579b..e1348b8ebf 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -742,7 +742,7 @@ subroutine DustEmission (bounds, & if ((12_r8 - 0.5_r8 * stblty(p)) .GE. 0.001_r8) then ! should have used 0 theoretically; used 0.001 here to avoid undefined values u_sd_slt(p) = wnd_frc_slt * (12_r8 - 0.5_r8 * stblty(p))**0.333_r8 else - u_sd_slt(p) = 0.001_r8 ! should have used 0 theoretically; used 0.001 here to avoid undefined values + u_sd_slt(p) = wnd_frc_slt * (0.001_r8)**0.333_r8 ! should have used 0 theoretically; used 0.001 here to avoid undefined values end if ! threshold velocities From 956ea907541cd514f44ea11c693e29bc72e4eca2 Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Fri, 24 May 2024 13:18:42 -0600 Subject: [PATCH 082/406] dmleung added more comments on the impact of soil moisture threshold on dust emission calculation. 24 May 2024 --- src/biogeophys/SoilStateInitTimeConstMod.F90 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/biogeophys/SoilStateInitTimeConstMod.F90 b/src/biogeophys/SoilStateInitTimeConstMod.F90 index bf2de65a29..3c8945e9d2 100644 --- a/src/biogeophys/SoilStateInitTimeConstMod.F90 +++ b/src/biogeophys/SoilStateInitTimeConstMod.F90 @@ -703,8 +703,12 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) do c = begc,endc g = col%gridcell(c) - !soilstate_inst%gwc_thr_col(c) = 0.01_r8*(0.17_r8*clay3d(g,1) + 0.0014_r8*clay3d(g,1)*clay3d(g,1)) ! Jasper Kok et al. (2014) modified the equation of soil moisture effect for dust emissions using a tuning factor of a = 1. 0.01 is to convert the threshold gravimetric soil moisture from % to fraction. Danny M. Leung et al. (2023) followed K14 and used this equation for the paper's results. However, Danny Leung later (Dec 2023) decided to revert to Zender's choice and use a = 1 / (clay fraction), which overall encourages more dust emissions from seriamid and more marginal dust sources. dmleung added this detailed comment on 19 Feb 2024. - soilstate_inst%gwc_thr_col(c) = 0.17_r8 + 0.14_r8 * clay3d(g,1) * 0.01_r8 ! This was the original equation with a = 1 / (clay fraction) being the tuning factor for soil moisture effect in Zender's dust emission scheme. Danny M. Leung decided (Dec, 2023) that the Leung et al. (2023) dust emission scheme in the CESM will use Zender's tuning of a = 1 / (clay fraction), which overall encourages more dust emissions from seriamid and more marginal dust sources. Another advantage of using this tuning factor instead of a = 1 is that the dust emission threshold is linearly dependent on the clay fraction instead of parabolically dependent on clay fraction as in the above line. This means that dust emission becomes a little less sensitive to clay content (soil texture). 0.17 and 0.14 are fitting coefficients in Fecan et al. (1999), and 0.01 is used to convert surface clay fraction from percentage to fraction. dmleung added this detailed comment on 19 Feb 2024. + ! dmleung++ added 24 May 2024: The below calculates the threshold gravimetric water content for the dust emission calculation in DUSTMod.F90. The equation comes from Eq. 14 of Fecan et al. (1999; https://doi.org/10.1007/s00585-999-0149-7). + !gwc_thr_col = 0.17*clay3d + 0.0014*(clay3d**2), and we only concern the topmost soil layer. + ! Charlie Zender later on added a tuning factor (a) such that the equation becomes gwc_thr_col = a*[0.17*clay3d + 0.0014*(clay3d**2)]. (Zender et al., 2003a; https://doi.org/10.1029/2002JD002775) + ! There are different options: E.g., a = 1 gives the first line below. E.g., Kok et al. (2014a, b) chose to use a = 1. A second choice is proposed by Charlie Zender (2003a): a = 1/clay3d, which gives the second line below. + !soilstate_inst%gwc_thr_col(c) = 0.01_r8*(0.17_r8*clay3d(g,1) + 0.0014_r8*clay3d(g,1)*clay3d(g,1)) ! Jasper Kok et al. (2014) modified the equation of soil moisture effect for dust emissions using a tuning factor of a = 1. 0.01 is to convert the threshold gravimetric soil moisture from % to fraction. Danny M. Leung et al. (2023) followed K14 and used this equation for offline dust emission calculations using MERRA-2 met fields. Later, Leung et al. (2024) changed to a = 2 for CESM2 simulations. However, Danny Leung later (Dec 2023) decided to revert to Zender's choice and use a = 1 / (clay3d), which overall encourages more dust emissions from seriamid and more marginal dust sources. This note should probably be on the CLM tech note since dmleung has made a different decision for CESM than his own paper. dmleung added this detailed comment on 19 Feb 2024; edited 24 May 2024. + soilstate_inst%gwc_thr_col(c) = 0.17_r8 + 0.14_r8 * clay3d(g,1) * 0.01_r8 ! This was the original equation with a = 1 / (%clay) being the tuning factor for soil moisture effect in Zender's dust emission scheme. Danny M. Leung decided (Dec, 2023) that the Leung et al. (2023) dust emission scheme in the CESM will use Zender's tuning of a = 1 / (%clay), which overall encourages more dust emissions from seriamid and more marginal dust sources. Another advantage of using this tuning factor instead of a = 1 is that the dust emission threshold is linearly dependent on the clay fraction instead of parabolically dependent on clay fraction as in the above line. This means that dust emission becomes a little less sensitive to clay content (soil texture). 0.17 and 0.14 are fitting coefficients in Fecan et al. (1999), and 0.01 is used to convert surface clay fraction from percentage to fraction. dmleung added this detailed comment on 19 Feb 2024. soilstate_inst%mss_frc_cly_vld_col(c) = min(clay3d(g,1) * 0.01_r8, 0.20_r8) end do From 51d9f095ebe73eb5e1af0731cc85e36988b2b9b7 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jan 2024 13:42:59 -0700 Subject: [PATCH 083/406] Break up a long line. --- src/main/clm_driver.F90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clm_driver.F90 b/src/main/clm_driver.F90 index 00a98e61b4..c4fa1b53f4 100644 --- a/src/main/clm_driver.F90 +++ b/src/main/clm_driver.F90 @@ -1078,7 +1078,8 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro ! ============================================================================ ! Update crop calendars ! ============================================================================ - call cropcal_interp(bounds_clump, filter_inactive_and_active(nc)%num_pcropp, filter_inactive_and_active(nc)%pcropp, crop_inst) + call cropcal_interp(bounds_clump, filter_inactive_and_active(nc)%num_pcropp, & + filter_inactive_and_active(nc)%pcropp, crop_inst) end if ! ============================================================================ From c763d664f23d15cc44d7afbb807359eea6cde30e Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jan 2024 13:45:32 -0700 Subject: [PATCH 084/406] use_cropcal_rx_cultivar_gdds not needed in CropPhenology(). --- src/biogeochem/CNPhenologyMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index fffb19bc46..bf91532e5a 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -1808,7 +1808,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & use clm_varctl , only : use_fertilizer use clm_varctl , only : use_c13, use_c14 use clm_varcon , only : c13ratio, c14ratio - use clm_varctl , only : use_cropcal_rx_swindows, use_cropcal_rx_cultivar_gdds, use_cropcal_streams + use clm_varctl , only : use_cropcal_rx_swindows, use_cropcal_streams ! ! !ARGUMENTS: integer , intent(in) :: num_pcropp ! number of prog crop patches in filter From 846f1cc0485e6f69682159d9c3a54b0cd6c8d941 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jan 2024 13:54:13 -0700 Subject: [PATCH 085/406] PlantCrop(): Be strict about PFT identification. --- src/biogeochem/CNPhenologyMod.F90 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index bf91532e5a..09515042b8 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2679,8 +2679,7 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & if (ivt(p) == ntmp_soybean .or. ivt(p) == nirrig_tmp_soybean .or. & ivt(p) == ntrp_soybean .or. ivt(p) == nirrig_trp_soybean) then gddmaturity(p) = min(gdd1020(p), hybgdd(ivt(p))) - end if - if (ivt(p) == ntmp_corn .or. ivt(p) == nirrig_tmp_corn .or. & + else if (ivt(p) == ntmp_corn .or. ivt(p) == nirrig_tmp_corn .or. & ivt(p) == ntrp_corn .or. ivt(p) == nirrig_trp_corn .or. & ivt(p) == nsugarcane .or. ivt(p) == nirrig_sugarcane .or. & ivt(p) == nmiscanthus .or. ivt(p) == nirrig_miscanthus .or. & @@ -2689,11 +2688,13 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & if (do_plant_normal) then gddmaturity(p) = max(950._r8, min(gddmaturity(p)+150._r8, 1850._r8)) end if - end if - if (ivt(p) == nswheat .or. ivt(p) == nirrig_swheat .or. & + else if (ivt(p) == nswheat .or. ivt(p) == nirrig_swheat .or. & ivt(p) == ncotton .or. ivt(p) == nirrig_cotton .or. & ivt(p) == nrice .or. ivt(p) == nirrig_rice) then gddmaturity(p) = min(gdd020(p), hybgdd(ivt(p))) + else + write(iulog, *) 'ERROR: PlantCrop(): unrecognized ivt ',ivt(p) + call endrun(msg="Stopping") end if endif From 5b056b744e64ac59aae5cbc44c99a1b3d9815f59 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jan 2024 14:07:42 -0700 Subject: [PATCH 086/406] PlantCrop: Get gdd20 before setting gddmaturity. --- src/biogeochem/CNPhenologyMod.F90 | 36 ++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index 09515042b8..efc581cffc 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2590,6 +2590,7 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & ! LOCAL VARAIBLES: integer s ! growing season index integer k ! grain pool index + real(r8) gdd20 ! GDD*20 value for this crop type real(r8) gdd_target ! cultivar GDD target this growing season real(r8) this_sowing_reason ! number representing sowing reason(s) logical did_rx_gdds ! did this patch use a prescribed harvest requirement? @@ -2668,6 +2669,26 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & endif endif + ! which GDD*20 variable does this crop use? + if (ivt(p) == ntmp_soybean .or. ivt(p) == nirrig_tmp_soybean .or. & + ivt(p) == ntrp_soybean .or. ivt(p) == nirrig_trp_soybean) then + gdd20 = gdd1020(p) + else if (ivt(p) == ntmp_corn .or. ivt(p) == nirrig_tmp_corn .or. & + ivt(p) == ntrp_corn .or. ivt(p) == nirrig_trp_corn .or. & + ivt(p) == nsugarcane .or. ivt(p) == nirrig_sugarcane .or. & + ivt(p) == nmiscanthus .or. ivt(p) == nirrig_miscanthus .or. & + ivt(p) == nswitchgrass .or. ivt(p) == nirrig_switchgrass) then + gdd20 = gdd820(p) + else if (ivt(p) == nswheat .or. ivt(p) == nirrig_swheat .or. & + ivt(p) == nwwheat .or. ivt(p) == nirrig_wwheat .or. & + ivt(p) == ncotton .or. ivt(p) == nirrig_cotton .or. & + ivt(p) == nrice .or. ivt(p) == nirrig_rice) then + gdd20 = gdd020(p) + else + write(iulog, *) 'ERROR: PlantCrop(): unrecognized ivt for gdd20: ',ivt(p) + call endrun(msg="Stopping") + end if + ! set GDD target did_rx_gdds = .false. if (use_cropcal_rx_cultivar_gdds .and. crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) .ge. 0._r8) then @@ -2677,23 +2698,22 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & gddmaturity(p) = hybgdd(ivt(p)) else if (ivt(p) == ntmp_soybean .or. ivt(p) == nirrig_tmp_soybean .or. & - ivt(p) == ntrp_soybean .or. ivt(p) == nirrig_trp_soybean) then - gddmaturity(p) = min(gdd1020(p), hybgdd(ivt(p))) + ivt(p) == ntrp_soybean .or. ivt(p) == nirrig_trp_soybean .or. & + ivt(p) == nswheat .or. ivt(p) == nirrig_swheat .or. & + ivt(p) == ncotton .or. ivt(p) == nirrig_cotton .or. & + ivt(p) == nrice .or. ivt(p) == nirrig_rice) then + gddmaturity(p) = min(gdd20, hybgdd(ivt(p))) else if (ivt(p) == ntmp_corn .or. ivt(p) == nirrig_tmp_corn .or. & ivt(p) == ntrp_corn .or. ivt(p) == nirrig_trp_corn .or. & ivt(p) == nsugarcane .or. ivt(p) == nirrig_sugarcane .or. & ivt(p) == nmiscanthus .or. ivt(p) == nirrig_miscanthus .or. & ivt(p) == nswitchgrass .or. ivt(p) == nirrig_switchgrass) then - gddmaturity(p) = max(950._r8, min(gdd820(p)*0.85_r8, hybgdd(ivt(p)))) + gddmaturity(p) = max(950._r8, min(gdd20*0.85_r8, hybgdd(ivt(p)))) if (do_plant_normal) then gddmaturity(p) = max(950._r8, min(gddmaturity(p)+150._r8, 1850._r8)) end if - else if (ivt(p) == nswheat .or. ivt(p) == nirrig_swheat .or. & - ivt(p) == ncotton .or. ivt(p) == nirrig_cotton .or. & - ivt(p) == nrice .or. ivt(p) == nirrig_rice) then - gddmaturity(p) = min(gdd020(p), hybgdd(ivt(p))) else - write(iulog, *) 'ERROR: PlantCrop(): unrecognized ivt ',ivt(p) + write(iulog, *) 'ERROR: PlantCrop(): unrecognized ivt for GDD target: ',ivt(p) call endrun(msg="Stopping") end if From 08906bb13054f32bb48e9a412beefd02bff9a3e2 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jan 2024 14:25:50 -0700 Subject: [PATCH 087/406] Correct a comment. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 0ea63f2c6d..23f00a1759 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -35,7 +35,7 @@ module cropcalStreamMod integer, allocatable :: g_to_ig(:) ! Array matching gridcell index to data index type(shr_strdata_type) :: sdat_cropcal_swindow_start ! sowing window start input data stream type(shr_strdata_type) :: sdat_cropcal_swindow_end ! sowing window end input data stream - type(shr_strdata_type) :: sdat_cropcal_cultivar_gdds ! sdate input data stream + type(shr_strdata_type) :: sdat_cropcal_cultivar_gdds ! maturity requirement input data stream character(len=CS), allocatable :: stream_varnames_sdate(:) ! used for both start and end dates character(len=CS), allocatable :: stream_varnames_cultivar_gdds(:) integer :: ncft ! Number of crop functional types (excl. generic crops) From 6ef52cdc8a980d62a596e21d9e7e57878145a5bb Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jan 2024 14:26:11 -0700 Subject: [PATCH 088/406] Remove a troubleshooting write to iulog. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 23f00a1759..75c3820e7e 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -490,7 +490,6 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, crop_inst) call ESMF_Finalize(endflag=ESMF_END_ABORT) endif end do - write(iulog,*) 'cropcal_interp(): Reading cultivar_gdds file DONE' end if ! use_cropcal_rx_cultivar_gdds deallocate(dataptr2d_cultivar_gdds) From 98f68e784fe4ab30fd559be0126cb847675b74fc Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jan 2024 14:43:16 -0700 Subject: [PATCH 089/406] Optionally adjust rx maturity reqt based on recent climate. --- bld/CLMBuildNamelist.pm | 7 ++ .../namelist_definition_ctsm.xml | 5 + src/biogeochem/CNPhenologyMod.F90 | 6 +- src/biogeochem/CropType.F90 | 2 + src/cpl/share_esmf/cropcalStreamMod.F90 | 113 +++++++++++++++++- src/main/clm_driver.F90 | 2 +- src/main/clm_initializeMod.F90 | 2 +- src/main/clm_varctl.F90 | 1 + 8 files changed, 133 insertions(+), 5 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index fb44023cd5..54752b6dbc 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4198,10 +4198,14 @@ sub setup_logic_cropcal_streams { my $swindow_start_file = $nl->get_value('stream_fldFileName_swindow_start') ; my $swindow_end_file = $nl->get_value('stream_fldFileName_swindow_end') ; my $gdd_file = $nl->get_value('stream_fldFileName_cultivar_gdds') ; + my $gdd20_baseline_file = $nl->get_value('stream_fldFileName_gdd20_baseline') ; my $mesh_file = $nl->get_value('stream_meshfile_cropcal') ; if ( ($swindow_start_file eq '' and $swindow_start_file ne '') or ($swindow_start_file ne '' and $swindow_start_file eq '') ) { $log->fatal_error("When specifying sowing window dates, you must provide both swindow_start_file and swindow_end_file. To specify exact sowing dates, use the same file." ); } + if ( $gdd_file eq '' and $gdd20_baseline_file ne '' ) { + $log->fatal_error("If not providing stream_fldFileName_cultivar_gdds, don't provide stream_fldFileName_gdd20_baseline"); + } if ( $generate_crop_gdds eq '.true.' ) { if ( $use_mxmat eq '.true.' ) { $log->fatal_error("If generate_crop_gdds is true, you must also set use_mxmat to false" ); @@ -4215,6 +4219,9 @@ sub setup_logic_cropcal_streams { if ( $gdd_file ne '' ) { $log->fatal_error("If generate_crop_gdds is true, do not specify stream_fldFileName_cultivar_gdds") } + if ( $gdd20_baseline_file ne '' ) { + $log->fatal_error("If generate_crop_gdds is true, do not specify stream_fldFileName_gdd20_baseline") + } } if ( $mesh_file eq '' and ( $swindow_start_file ne '' or $gdd_file ne '' ) ) { $log->fatal_error("If prescribing crop sowing dates and/or maturity requirements, you must specify stream_meshfile_cropcal") diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 37c457141c..c88e987756 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1808,6 +1808,11 @@ Filename of input stream data for date (day of year) of end of sowing window. Ce Filename of input stream data for cultivar growing degree-day targets + +Filename of input stream data for baseline GDD20 values + + Filename of input stream data for crop calendar inputs diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index efc581cffc..f1f2d5fcc0 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2559,7 +2559,7 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & ! !USES: use clm_varctl , only : use_c13, use_c14 - use clm_varctl , only : use_cropcal_rx_cultivar_gdds, use_cropcal_streams + use clm_varctl , only : use_cropcal_rx_cultivar_gdds, use_cropcal_streams, adapt_cropcal_rx_cultivar_gdds use clm_varcon , only : c13ratio, c14ratio use clm_varpar , only : mxsowings use pftconMod , only : ntmp_corn, nswheat, nwwheat, ntmp_soybean @@ -2694,6 +2694,10 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & if (use_cropcal_rx_cultivar_gdds .and. crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) .ge. 0._r8) then gddmaturity(p) = crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) did_rx_gdds = .true. + if (adapt_cropcal_rx_cultivar_gdds) then + gddmaturity(p) = gddmaturity(p) * gdd20 / crop_inst%gdd20_baseline_patch(p) + !TODO SSR: Set maximum gddmaturity + end if else if (ivt(p) == nwwheat .or. ivt(p) == nirrig_wwheat) then gddmaturity(p) = hybgdd(ivt(p)) else diff --git a/src/biogeochem/CropType.F90 b/src/biogeochem/CropType.F90 index 760aa21b76..a7bccf1a73 100644 --- a/src/biogeochem/CropType.F90 +++ b/src/biogeochem/CropType.F90 @@ -52,6 +52,7 @@ module CropType integer , pointer :: rx_swindow_starts_thisyr_patch(:,:) ! all prescribed sowing window start dates for this patch this year (day of year) [patch, mxsowings] integer , pointer :: rx_swindow_ends_thisyr_patch (:,:) ! all prescribed sowing window end dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: rx_cultivar_gdds_thisyr_patch (:,:) ! all cultivar GDD targets for this patch this year (ddays) [patch, mxsowings] + real(r8), pointer :: gdd20_baseline_patch (:) ! GDD20 baseline for this patch (ddays) [patch] real(r8), pointer :: sdates_thisyr_patch (:,:) ! all actual sowing dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: swindow_starts_thisyr_patch(:,:) ! all sowing window start dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: swindow_ends_thisyr_patch (:,:) ! all sowing window end dates for this patch this year (day of year) [patch, mxsowings] @@ -235,6 +236,7 @@ subroutine InitAllocate(this, bounds) allocate(this%rx_swindow_starts_thisyr_patch(begp:endp,1:mxsowings)); this%rx_swindow_starts_thisyr_patch(:,:) = -1 allocate(this%rx_swindow_ends_thisyr_patch(begp:endp,1:mxsowings)) ; this%rx_swindow_ends_thisyr_patch (:,:) = -1 allocate(this%rx_cultivar_gdds_thisyr_patch(begp:endp,1:mxsowings)) ; this%rx_cultivar_gdds_thisyr_patch(:,:) = spval + allocate(this%gdd20_baseline_patch(begp:endp)) ; this%gdd20_baseline_patch(:) = spval allocate(this%sdates_thisyr_patch(begp:endp,1:mxsowings)) ; this%sdates_thisyr_patch(:,:) = spval allocate(this%swindow_starts_thisyr_patch(begp:endp,1:mxsowings)) ; this%swindow_starts_thisyr_patch(:,:) = spval allocate(this%swindow_ends_thisyr_patch (begp:endp,1:mxsowings)) ; this%swindow_ends_thisyr_patch (:,:) = spval diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 75c3820e7e..83f7fc0eb0 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -15,6 +15,7 @@ module cropcalStreamMod use abortutils , only : endrun use clm_varctl , only : iulog use clm_varctl , only : use_cropcal_rx_swindows, use_cropcal_rx_cultivar_gdds, use_cropcal_streams + use clm_varctl , only : adapt_cropcal_rx_cultivar_gdds use clm_varpar , only : mxpft use clm_varpar , only : mxsowings use perf_mod , only : t_startf, t_stopf @@ -36,13 +37,16 @@ module cropcalStreamMod type(shr_strdata_type) :: sdat_cropcal_swindow_start ! sowing window start input data stream type(shr_strdata_type) :: sdat_cropcal_swindow_end ! sowing window end input data stream type(shr_strdata_type) :: sdat_cropcal_cultivar_gdds ! maturity requirement input data stream + type(shr_strdata_type) :: sdat_cropcal_gdd20_baseline ! GDD20 baseline input data stream character(len=CS), allocatable :: stream_varnames_sdate(:) ! used for both start and end dates character(len=CS), allocatable :: stream_varnames_cultivar_gdds(:) + character(len=CS), allocatable :: stream_varnames_gdd20_baseline(:) integer :: ncft ! Number of crop functional types (excl. generic crops) logical :: allow_invalid_swindow_inputs ! Fall back on paramfile sowing windows in cases of invalid values in stream_fldFileName_swindow_start and _end? character(len=CL) :: stream_fldFileName_swindow_start ! sowing window start stream filename to read character(len=CL) :: stream_fldFileName_swindow_end ! sowing window end stream filename to read character(len=CL) :: stream_fldFileName_cultivar_gdds ! cultivar growing degree-days stream filename to read + character(len=CL) :: stream_fldFileName_gdd20_baseline ! GDD20 baseline stream filename to read character(len=*), parameter :: sourcefile = & __FILE__ @@ -90,6 +94,7 @@ subroutine cropcal_init(bounds) stream_fldFileName_swindow_start, & stream_fldFileName_swindow_end, & stream_fldFileName_cultivar_gdds, & + stream_fldFileName_gdd20_baseline, & stream_meshfile_cropcal ! Default values for namelist @@ -101,14 +106,17 @@ subroutine cropcal_init(bounds) stream_fldFileName_swindow_start = '' stream_fldFileName_swindow_end = '' stream_fldFileName_cultivar_gdds = '' + stream_fldFileName_gdd20_baseline = '' ! Will need modification to work with mxsowings > 1 ncft = mxpft - npcropmin + 1 ! Ignores generic crops allocate(stream_varnames_sdate(ncft)) allocate(stream_varnames_cultivar_gdds(ncft)) + allocate(stream_varnames_gdd20_baseline(ncft)) do n = 1,ncft ivt = npcropmin + n - 1 write(stream_varnames_sdate(n),'(a,i0)') "sdate1_",ivt write(stream_varnames_cultivar_gdds(n),'(a,i0)') "gdd1_",ivt + write(stream_varnames_gdd20_baseline(n),'(a,i0)') "gdd20bl_",ivt end do ! Read cropcal_streams namelist @@ -132,6 +140,7 @@ subroutine cropcal_init(bounds) call shr_mpi_bcast(stream_fldFileName_swindow_start, mpicom) call shr_mpi_bcast(stream_fldFileName_swindow_end , mpicom) call shr_mpi_bcast(stream_fldFileName_cultivar_gdds, mpicom) + call shr_mpi_bcast(stream_fldFileName_gdd20_baseline, mpicom) call shr_mpi_bcast(stream_meshfile_cropcal , mpicom) if (masterproc) then @@ -144,10 +153,12 @@ subroutine cropcal_init(bounds) write(iulog,'(a,a)' ) ' stream_fldFileName_swindow_start = ',trim(stream_fldFileName_swindow_start) write(iulog,'(a,a)' ) ' stream_fldFileName_swindow_end = ',trim(stream_fldFileName_swindow_end) write(iulog,'(a,a)' ) ' stream_fldFileName_cultivar_gdds = ',trim(stream_fldFileName_cultivar_gdds) + write(iulog,'(a,a)' ) ' stream_fldFileName_gdd20_baseline = ',trim(stream_fldFileName_gdd20_baseline) write(iulog,'(a,a)' ) ' stream_meshfile_cropcal = ',trim(stream_meshfile_cropcal) do n = 1,ncft write(iulog,'(a,a)' ) ' stream_varnames_sdate = ',trim(stream_varnames_sdate(n)) write(iulog,'(a,a)' ) ' stream_varnames_cultivar_gdds = ',trim(stream_varnames_cultivar_gdds(n)) + write(iulog,'(a,a)' ) ' stream_varnames_gdd20_baseline = ',trim(stream_varnames_gdd20_baseline(n)) end do write(iulog,*) endif @@ -155,6 +166,7 @@ subroutine cropcal_init(bounds) ! CLMBuildNamelist checks that both start and end files are provided if either is use_cropcal_rx_swindows = stream_fldFileName_swindow_start /= '' use_cropcal_rx_cultivar_gdds = stream_fldFileName_cultivar_gdds /= '' + adapt_cropcal_rx_cultivar_gdds = stream_fldFileName_gdd20_baseline /= '' use_cropcal_streams = use_cropcal_rx_swindows .or. use_cropcal_rx_cultivar_gdds if (use_cropcal_rx_swindows) then @@ -242,6 +254,36 @@ subroutine cropcal_init(bounds) end if end if + ! Initialize the cdeps data type sdat_cropcal_gdd20_baseline + ! NOTE: stream_dtlimit 1.5 didn't work for some reason + !TODO SSR: Do not allow time axis length > 1 + if (adapt_cropcal_rx_cultivar_gdds) then + call shr_strdata_init_from_inline(sdat_cropcal_gdd20_baseline, & + my_task = iam, & + logunit = iulog, & + compname = 'LND', & + model_clock = model_clock, & + model_mesh = mesh, & + stream_meshfile = trim(stream_meshfile_cropcal), & + stream_lev_dimname = 'null', & + stream_mapalgo = 'bilinear', & + stream_filenames = (/trim(stream_fldFileName_gdd20_baseline)/), & + stream_fldlistFile = stream_varnames_gdd20_baseline, & + stream_fldListModel = stream_varnames_gdd20_baseline, & + stream_yearFirst = 2000, & + stream_yearLast = 2000, & + stream_yearAlign = 2000, & + stream_offset = cropcal_offset, & + stream_taxmode = 'extend', & + stream_dtlimit = 1.0e30_r8, & + stream_tintalgo = cropcal_tintalgo, & + stream_name = 'GDD20 baseline data', & + rc = rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + end if + end subroutine cropcal_init !================================================================ @@ -285,6 +327,8 @@ subroutine cropcal_advance( bounds ) end if end if + ! GDD20 baseline values do not have an associated time axis and thus will not be advanced here + if ( .not. allocated(g_to_ig) )then allocate (g_to_ig(bounds%begg:bounds%endg) ) ig = 0 @@ -298,7 +342,7 @@ end subroutine cropcal_advance !================================================================ - subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, crop_inst) + subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! ! Interpolate data stream information for crop calendars. ! @@ -314,6 +358,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, crop_inst) type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_pcropp ! number of prog. crop patches in filter integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches + logical , intent(in) :: init ! is this being called as initialization? type(crop_type) , intent(inout) :: crop_inst ! ! !LOCAL VARIABLES: @@ -327,9 +372,11 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, crop_inst) real(r8), pointer :: dataptr1d_swindow_start(:) real(r8), pointer :: dataptr1d_swindow_end (:) real(r8), pointer :: dataptr1d_cultivar_gdds(:) + real(r8), pointer :: dataptr1d_gdd20_baseline(:) real(r8), pointer :: dataptr2d_swindow_start(:,:) real(r8), pointer :: dataptr2d_swindow_end (:,:) real(r8), pointer :: dataptr2d_cultivar_gdds(:,:) + real(r8), pointer :: dataptr2d_gdd20_baseline(:,:) !----------------------------------------------------------------------- associate( & @@ -345,7 +392,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, crop_inst) lsize = bounds%endg - bounds%begg + 1 begp = bounds%begp - endp= bounds%endp + endp = bounds%endp dayspyr = get_curr_days_per_year() @@ -494,6 +541,68 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, crop_inst) deallocate(dataptr2d_cultivar_gdds) + allocate(dataptr2d_gdd20_baseline(lsize, ncft)) + if (adapt_cropcal_rx_cultivar_gdds .and. init) then + ! Read GDD20 baselines from input files + ! Starting with npcropmin will skip generic crops + do n = 1, ncft + call dshr_fldbun_getFldPtr(sdat_cropcal_gdd20_baseline%pstrm(1)%fldbun_model, trim(stream_varnames_gdd20_baseline(n)), & + fldptr1=dataptr1d_gdd20_baseline, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize + ! So an explicit loop is required here + do g = 1,lsize + + ! Ensure valid values + if (dataptr1d_gdd20_baseline(g) < 0 .or. dataptr1d_gdd20_baseline(g) > 1000000._r8) then + write(iulog, *) 'ERROR: invalid read-in gdd20_baseline value: ',dataptr1d_gdd20_baseline(g) + call ESMF_Finalize(endflag=ESMF_END_ABORT) + else if (dataptr1d_gdd20_baseline(g) == 0) then + write(iulog, *) 'ERROR: read-in gdd20_baseline value 0 will cause inf' + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + dataptr2d_gdd20_baseline(g,n) = dataptr1d_gdd20_baseline(g) + end do + end do + + ! Set gdd20_baseline_patch for each gridcell/patch combination + do fp = 1, num_pcropp + p = filter_pcropp(fp) + + ivt = patch%itype(p) + ! Will skip generic crops + if (ivt >= npcropmin) then + n = ivt - npcropmin + 1 + + if (n > ncft) then + write(iulog,'(a,i0,a,i0,a)') 'n (',n,') > ncft (',ncft,')' + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + ! vegetated pft + ig = g_to_ig(patch%gridcell(p)) + + if (ig > lsize) then + write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') > lsize (',lsize,')' + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + crop_inst%gdd20_baseline_patch(p) = dataptr2d_gdd20_baseline(ig,n) + + else + write(iulog,'(a,i0)') 'cropcal_interp(), rx_gdd20_baseline: Crop patch has ivt ',ivt + call ESMF_Finalize(endflag=ESMF_END_ABORT) + endif + end do + end if ! adapt_cropcal_rx_cultivar_gdds + + deallocate(dataptr2d_gdd20_baseline) + + end associate end subroutine cropcal_interp diff --git a/src/main/clm_driver.F90 b/src/main/clm_driver.F90 index c4fa1b53f4..2a3cef4b8b 100644 --- a/src/main/clm_driver.F90 +++ b/src/main/clm_driver.F90 @@ -1079,7 +1079,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro ! Update crop calendars ! ============================================================================ call cropcal_interp(bounds_clump, filter_inactive_and_active(nc)%num_pcropp, & - filter_inactive_and_active(nc)%pcropp, crop_inst) + filter_inactive_and_active(nc)%pcropp, .false., crop_inst) end if ! ============================================================================ diff --git a/src/main/clm_initializeMod.F90 b/src/main/clm_initializeMod.F90 index 9bf0cc59a2..e8f70bdef8 100644 --- a/src/main/clm_initializeMod.F90 +++ b/src/main/clm_initializeMod.F90 @@ -667,7 +667,7 @@ subroutine initialize2(ni,nj) do nc = 1,nclumps call get_clump_bounds(nc, bounds_clump) call cropcal_interp(bounds_clump, filter_inactive_and_active(nc)%num_pcropp, & - filter_inactive_and_active(nc)%pcropp, crop_inst) + filter_inactive_and_active(nc)%pcropp, .true., crop_inst) end do !$OMP END PARALLEL DO end if diff --git a/src/main/clm_varctl.F90 b/src/main/clm_varctl.F90 index 7d0b2b55ad..fe67380fd2 100644 --- a/src/main/clm_varctl.F90 +++ b/src/main/clm_varctl.F90 @@ -391,6 +391,7 @@ module clm_varctl logical, public :: use_cropcal_streams = .false. logical, public :: use_cropcal_rx_swindows = .false. logical, public :: use_cropcal_rx_cultivar_gdds = .false. + logical, public :: adapt_cropcal_rx_cultivar_gdds = .false. !---------------------------------------------------------- ! biomass heat storage switch From b73bcfdd104d8d591afb41ef6d6c18398cf8ef69 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 23 May 2024 15:09:16 -0600 Subject: [PATCH 090/406] =?UTF-8?q?Don't=20adapt=20rx=20cultivar=20gdd=20r?= =?UTF-8?q?eqts=20if=20baseline=20gdd20=20is=20=E2=89=A40.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/biogeochem/CNPhenologyMod.F90 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index f1f2d5fcc0..cfeee0b867 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -143,6 +143,9 @@ module CNPhenologyMod logical, public :: generate_crop_gdds = .false. ! If true, harvest the day before next sowing logical, public :: use_mxmat = .true. ! If true, ignore crop maximum growing season length + ! For use with adapt_cropcal_rx_cultivar_gdds .true. + real(r8), parameter :: min_gdd20_baseline = 0._r8 ! If gdd20_baseline_patch is ≤ this, do not consider baseline. + ! Constants for seasonal decidious leaf onset and offset logical, private :: onset_thresh_depends_on_veg = .false. ! If onset threshold depends on vegetation type integer, public, parameter :: critical_daylight_constant = 1 @@ -2694,9 +2697,9 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & if (use_cropcal_rx_cultivar_gdds .and. crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) .ge. 0._r8) then gddmaturity(p) = crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) did_rx_gdds = .true. - if (adapt_cropcal_rx_cultivar_gdds) then + if (adapt_cropcal_rx_cultivar_gdds .and. crop_inst%gdd20_baseline_patch(p) > min_gdd20_baseline) then gddmaturity(p) = gddmaturity(p) * gdd20 / crop_inst%gdd20_baseline_patch(p) - !TODO SSR: Set maximum gddmaturity + !TODO SSR: Set maximum and minimum gddmaturity end if else if (ivt(p) == nwwheat .or. ivt(p) == nirrig_wwheat) then gddmaturity(p) = hybgdd(ivt(p)) From 20f32c1d70d709a96416c70a1d143d142b52fecf Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 24 May 2024 12:45:18 -0600 Subject: [PATCH 091/406] Ignore 'invalid' values in stream_fldFileName_gdd20_baseline on read. They're being handled in PlantCrop() instead. (cherry picked from commit 045b3d72c54319769e50500b2bd445215e07446d) --- src/cpl/share_esmf/cropcalStreamMod.F90 | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 83f7fc0eb0..f60ab51000 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -555,16 +555,6 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here do g = 1,lsize - - ! Ensure valid values - if (dataptr1d_gdd20_baseline(g) < 0 .or. dataptr1d_gdd20_baseline(g) > 1000000._r8) then - write(iulog, *) 'ERROR: invalid read-in gdd20_baseline value: ',dataptr1d_gdd20_baseline(g) - call ESMF_Finalize(endflag=ESMF_END_ABORT) - else if (dataptr1d_gdd20_baseline(g) == 0) then - write(iulog, *) 'ERROR: read-in gdd20_baseline value 0 will cause inf' - call ESMF_Finalize(endflag=ESMF_END_ABORT) - end if - dataptr2d_gdd20_baseline(g,n) = dataptr1d_gdd20_baseline(g) end do end do From a7cd0573ddfdef805f97c1f9aba3083492515ca3 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 24 May 2024 14:05:36 -0600 Subject: [PATCH 092/406] Add generate_gdd20_baseline tool. (cherry picked from commit 3ae50b7402fb1e3580f11ba09883e9ddaa563fda) --- .../crop_calendars/generate_gdd20_baseline.py | 243 ++++++++++++++++++ tools/crop_calendars/generate_gdd20_baseline | 19 ++ 2 files changed, 262 insertions(+) create mode 100644 python/ctsm/crop_calendars/generate_gdd20_baseline.py create mode 100755 tools/crop_calendars/generate_gdd20_baseline diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py new file mode 100644 index 0000000000..d5cbd779e0 --- /dev/null +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -0,0 +1,243 @@ +""" +Generate stream_fldFileName_gdd20_baseline file from CTSM outputs +""" + +import sys +import argparse +import os +import datetime as dt +import numpy as np +import xarray as xr +import cftime + +# -- add python/ctsm to path (needed if we want to run generate_gdd20_baseline stand-alone) +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir) +sys.path.insert(1, _CTSM_PYTHON) + +# pylint: disable=wrong-import-position +from ctsm.crop_calendars.import_ds import import_ds +import ctsm.crop_calendars.cropcal_utils as utils + +VAR_LIST_IN = ["GDD0", "GDD8", "GDD10"] +VAR_LIST_IN = [x + "20" for x in VAR_LIST_IN] # TODO: Delete this once using the right variables +MISSING_FILL = -1 # Something negative to ensure that gddmaturity never changes (see PlantCrop) +STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of + # shr_strdata_init_from_inline() for sdat_cropcal_gdd20_baseline + + +def _parse_args(): + """ + Set up and parse input arguments + """ + parser = argparse.ArgumentParser( + description=( + "Given a list of CTSM history files, generate stream_fldFileName_gdd20_baseline input" + + "file from the GDD0, GDD8, and GDD10 variables." + ) + ) + + # Required + parser.add_argument( + "-i", + "--input-files", + help="Space-separated string of CTSM history files", + type=str, + required=True, + ) + parser.add_argument( + "-o", + "--output-file", + help="Path to which output file should be saved", + type=str, + required=True, + ) + parser.add_argument( + "-a", + "--author", + help=( + "String to be saved in author attribute of output files." + + "E.g., 'Author Name (authorname@ucar.edu)'" + ), + type=str, + required=True, + ) + + # Optional + parser.add_argument( + "--overwrite", + help="Overwrite existing output file, if any", + action="store_true", + ) + + # Get arguments + args = parser.parse_args(sys.argv[1:]) + + # Check arguments + if os.path.exists(args.output_file) and not args.overwrite: + raise FileExistsError("Output file exists but --overwrite is not specified") + + return args + + +def _get_cft_list(crop_list): + """ + Given a list of strings, return a list of CFT names that contain any of those strings. + Will include both irrigated and rainfed! + + Args: + crop_list (list): List of crops to look for. + E.g.: ["corn", "cotton"] + + Returns: + cft_str_list: List of CFTs containing any of the crop names in crop_list. + E.g.: ["tropical_corn", "irrigated_tropical_corn", + "temperate_corn", "irrigated_temperate_corn", + "cotton", "irrigated_cotton"] + """ + + mgdcrop_list = utils.define_mgdcrop_list() + cft_str_list = [] + for crop_str in crop_list: + cft_str_list += [x for x in mgdcrop_list if crop_str in x] + return cft_str_list + + +def _get_gddn_for_cft(cft_str): + """ + Given a CFT name, return the GDDN variable it uses. + + Args: + cft_str (str): E.g., "irrigated_temperate_corn" + + Returns: + str or None: Name of variable to use (e.g., "GDD8"). If crop isn't yet handled, return None. + """ + + gddn = None + + gdd0_list_str = ["wheat", "cotton", "rice"] + if cft_str in _get_cft_list(gdd0_list_str): + gddn = "GDD0" + + gdd8_list_str = ["corn", "sugarcane", "miscanthus", "switchgrass"] + if cft_str in _get_cft_list(gdd8_list_str): + gddn = "GDD8" + + gdd10_list_str = ["soybean"] + if cft_str in _get_cft_list(gdd10_list_str): + gddn = "GDD10" + + # TODO: Delete this once using the right variables + if gddn is not None: + gddn += "20" + + return gddn + + +def _get_output_varname(cft_str): + cft_int = utils.vegtype_str2int(cft_str)[0] + return f"gdd20bl_{cft_int}" + + +def _add_time_axis(da_in): + """ + Adds a size-1 time axis to a DataArray. Needed because CDEPS streams code requires a time axis, + even if the data in question is not supposed to vary over time. + + Args: + da_in (DataArray): xarray DataArray which needs a time axis added + + Returns: + DataArray: da_in with a new 1-step time axis + """ + this_date = np.array(cftime.DatetimeNoLeap(STREAM_YEAR, 1, 1, 0, 0, 0, 0, has_year_zero=True)) + this_date = np.expand_dims(this_date, axis=0) + da_time = xr.DataArray( + data=this_date, + dims={"time": this_date}, + ) + da_out = da_in.expand_dims(time=da_time) + return da_out + + +def generate_gdd20_baseline(input_files, output_file, author): + """ + Generate stream_fldFileName_gdd20_baseline file from CTSM outputs + """ + + # Get input file list + input_files = input_files.split(sep=" ") + # Get unique values and sort + input_files = list(set(input_files)) + input_files.sort() + + # Import history files and ensure they have lat/lon dims + ds_in = import_ds(input_files, VAR_LIST_IN) + if not all(x in ds_in.dims for x in ["lat", "lon"]): + raise RuntimeError("Input files must have lat and lon dimensions") + + # If needed, find mean over time + if "time" in ds_in.dims: + ds_in = ds_in.mean(dim="time", skipna=True) + + # Set up a dummy DataArray to use for crops without an assigned GDDN variable + dummy_da = xr.DataArray( + data=MISSING_FILL * np.ones_like(ds_in[VAR_LIST_IN[0]].values), + dims=ds_in[VAR_LIST_IN[0]].dims, + coords=ds_in[VAR_LIST_IN[0]].coords, + ) + dummy_da = _add_time_axis(dummy_da) + + # Process all crops + ds_out = xr.Dataset( + data_vars=None, + attrs={ + "author": author, + "created": dt.datetime.now().astimezone().isoformat(), + }, + ) + for cft_str in utils.define_mgdcrop_list(): + cft_int = utils.vegtype_str2int(cft_str)[0] + print(f"{cft_str} ({cft_int})") + + # Which GDDN history variable does this crop use? E.g., GDD0, GDD10 + gddn = _get_gddn_for_cft(cft_str) + + # Fill any missing values with MISSING_FILL. This will mean that gddmaturity in these cells + # never changes. + if gddn is None: + # Crop not handled yet? Fill it entirely with missing value + this_da = dummy_da + long_name = "Dummy GDD20" + print(" dummy GDD20") + else: + this_da = ds_in[gddn].fillna(MISSING_FILL) + this_da = _add_time_axis(this_da) + long_name = gddn + print(f" {gddn}") + + # Add attributes + this_da.attrs["long_name"] = long_name + f" baseline for {cft_str}" + this_da.attrs["units"] = "°C days" + + # Copy that to ds_out + var_out = _get_output_varname(cft_str) + print(f" Output variable {var_out}") + ds_out[var_out] = this_da + + # Save + ds_out.to_netcdf(output_file) + + print("Done!") + + +def main(): + """ + main() function for calling generate_gdd20_baseline.py from command line. + """ + args = _parse_args() + generate_gdd20_baseline( + args.input_files, + args.output_file, + args.author, + ) diff --git a/tools/crop_calendars/generate_gdd20_baseline b/tools/crop_calendars/generate_gdd20_baseline new file mode 100755 index 0000000000..a0238c8d0f --- /dev/null +++ b/tools/crop_calendars/generate_gdd20_baseline @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +""" +For description and instructions, please see README. +""" + +import os +import sys + +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), + os.pardir, + os.pardir, + 'python') +sys.path.insert(1, _CTSM_PYTHON) + +from ctsm.crop_calendars.generate_gdd20_baseline import main + +if __name__ == "__main__": + main() + From fd5f177131d63d39e79a13918390bdfb642d781e Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Tue, 28 May 2024 11:27:09 -0600 Subject: [PATCH 093/406] Format generate_gdd20_baseline.py with black. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index d5cbd779e0..d28bfda0e7 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -22,7 +22,7 @@ VAR_LIST_IN = [x + "20" for x in VAR_LIST_IN] # TODO: Delete this once using the right variables MISSING_FILL = -1 # Something negative to ensure that gddmaturity never changes (see PlantCrop) STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of - # shr_strdata_init_from_inline() for sdat_cropcal_gdd20_baseline +# shr_strdata_init_from_inline() for sdat_cropcal_gdd20_baseline def _parse_args(): From 5eafae73aeeaec3950123a955d9c781d3840e157 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Tue, 28 May 2024 11:28:14 -0600 Subject: [PATCH 094/406] Add previous commit to .git-blame-ignore-revs. --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index e769d3187c..2620839223 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -26,6 +26,7 @@ d866510188d26d51bcd6d37239283db690af7e82 0dcd0a3c1abcaffe5529f8d79a6bc34734b195c7 e096358c832ab292ddfd22dd5878826c7c788968 475831f0fb0e31e97f630eac4e078c886558b61c +fd5f177131d63d39e79a13918390bdfb642d781e # Ran SystemTests and python/ctsm through black python formatter 5364ad66eaceb55dde2d3d598fe4ce37ac83a93c 8056ae649c1b37f5e10aaaac79005d6e3a8b2380 From 348c39422bec5a28f99572bba353196d574491b5 Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Tue, 28 May 2024 13:18:19 -0600 Subject: [PATCH 095/406] dmleung 28 May 2024: a small edit of DUSTMod.F90 to deal with the situation of dpfct_rock(p) = NaN, which likely causes the run to die when turning on debug mode --- src/biogeochem/DUSTMod.F90 | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index e1348b8ebf..ffe74e6861 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -673,7 +673,14 @@ subroutine DustEmission (bounds, & ! calculation of the hybrid/total drag partition effect considering both rock and vegetation drag partitioning using LUH2 bare and veg fractions within a grid if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then if (patch%itype(p) == noveg) then ! if bare, uses rock drag partition factor - frc_thr_rgh_fct = dpfct_rock(p) + if (dpfct_rock(p) /= dpfct_rock(p)) then ! dmleung added 24 May 2024: dpfct_rock(p) could be NaN; CLM could run when DEBUG=FALSE in env_build.xml but dies when DEBUG=TRUE (usually when checking if wnd_frc_slt > wnd_frc_thr_slt_it and if numer/denom < 30._r8 below) + !write(iulog,*) 'dpfct_rock(p) == NaN; dpfct_rock(p) = ', dpfct_rock(p) + frc_thr_rgh_fct = 0.001_r8 ! Set drag partition effect to be a very small value (or zero) such that there is no emission whenever dpfct_rock(p) = NaN; dmleung 24 May 2024 + else + !write(iulog,*) 'dpfct_rock(p) = ', dpfct_rock(p) + frc_thr_rgh_fct = dpfct_rock(p) + end if + !frc_thr_rgh_fct = dpfct_rock(p) ! This should be the original code when dpfct_rock(p) has values everywhere else ! if vegetation, uses vegetation drag partition factor frc_thr_rgh_fct = ssr(p) end if @@ -681,7 +688,7 @@ subroutine DustEmission (bounds, & frc_thr_rgh_fct = 1.0_r8 end if - wnd_frc_slt = fv(p) * frc_thr_rgh_fct ! wnd_frc_slt will be used in the dust emission equation + wnd_frc_slt = fv(p) * frc_thr_rgh_fct ! wnd_frc_slt is the drag-parition-modified wind speed and will be used in the dust emission equation below frc_thr_rghn_fct(p) = frc_thr_rgh_fct ! save and output hybrid drag partition factor @@ -756,7 +763,7 @@ subroutine DustEmission (bounds, & numer = (u_fld_thr(p)**2.0_r8 - u_impct_thr(p)**2.0_r8 - 2.0_r8 * u_mean_slt(p) * (u_fld_thr(p) - u_impct_thr(p))) denom = (2.0_r8 * u_sd_slt(p)**2.0_r8) ! Truncate to zero if the expression inside exp is becoming too large - if ( numer/denom < 30._r8 )then + if ( numer/denom < 30._r8 .and. denom/=0.0_r8)then ! set numer/denom to be < 30 given exp(30) below is already very large; also set denom to be non-zero (and denom should be autonamtically non-negative given the standard deviation u_sd_slt of the subtimestep wind fluctuation is non-negative) thr_crs_rate(p) = (exp((u_fld_thr(p)**2.0_r8 - u_impct_thr(p)**2.0_r8 - 2.0_r8 * u_mean_slt(p) * (u_fld_thr(p) - u_impct_thr(p))) / (2.0_r8 * u_sd_slt(p)**2.0_r8)) + 1.0_r8)**(-1.0_r8) else thr_crs_rate(p) = 0.0_r8 From f8eb462ca90c5d0d3edb122d0abeb0ce703ccb46 Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Tue, 28 May 2024 17:36:41 -0600 Subject: [PATCH 096/406] dmleung added comments on the positive denom required for dust emission intermittency in DUSTMod.F90. 28 May 2024 --- src/biogeochem/DUSTMod.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index ffe74e6861..b23a26b429 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -761,9 +761,9 @@ subroutine DustEmission (bounds, & ! threshold crossing rate numer = (u_fld_thr(p)**2.0_r8 - u_impct_thr(p)**2.0_r8 - 2.0_r8 * u_mean_slt(p) * (u_fld_thr(p) - u_impct_thr(p))) - denom = (2.0_r8 * u_sd_slt(p)**2.0_r8) + denom = (2.0_r8 * u_sd_slt(p)**2.0_r8) ! note that u_sd_slt should be always positive ! Truncate to zero if the expression inside exp is becoming too large - if ( numer/denom < 30._r8 .and. denom/=0.0_r8)then ! set numer/denom to be < 30 given exp(30) below is already very large; also set denom to be non-zero (and denom should be autonamtically non-negative given the standard deviation u_sd_slt of the subtimestep wind fluctuation is non-negative) + if ( numer/denom < 30._r8 ) then ! set numer/denom to be < 30 given exp(30) below is already very large; also denom itself should be non-zero and non-negative given the standard deviation (u_sd_slt) of the subtimestep wind fluctuation is non-negative. dmleung 28 May 2024 thr_crs_rate(p) = (exp((u_fld_thr(p)**2.0_r8 - u_impct_thr(p)**2.0_r8 - 2.0_r8 * u_mean_slt(p) * (u_fld_thr(p) - u_impct_thr(p))) / (2.0_r8 * u_sd_slt(p)**2.0_r8)) + 1.0_r8)**(-1.0_r8) else thr_crs_rate(p) = 0.0_r8 From 38d6888fd04cef4f2d507df99e9c4d1a3a16c4ac Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 07:50:21 -0600 Subject: [PATCH 097/406] Add testmods RxCropCals, RxCropCalsAdapt. --- .../testmods_dirs/clm/RxCropCals/include_user_mods | 1 + .../testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm | 8 ++++++++ .../testmods_dirs/clm/RxCropCalsAdapt/include_user_mods | 1 + .../testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm | 3 +++ 4 files changed, 13 insertions(+) create mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCals/include_user_mods create mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm create mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods create mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCals/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCals/include_user_mods new file mode 100644 index 0000000000..23ea3745e6 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCals/include_user_mods @@ -0,0 +1 @@ +../crop diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm new file mode 100644 index 0000000000..9cbfea97f0 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm @@ -0,0 +1,8 @@ + +! Sowing windows +stream_fldFileName_swindow_start = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc' +stream_fldFileName_swindow_end = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc' +stream_fldFileName_cultivar_gdds = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc' +stream_year_first_cropcal = 2000 +stream_year_last_cropcal = 2000 +stream_meshfile_cropcal = '$DIN_LOC_ROOT/share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc' diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods new file mode 100644 index 0000000000..88c9694365 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods @@ -0,0 +1 @@ +../RxCropCals diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm new file mode 100644 index 0000000000..1bba9d2a89 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm @@ -0,0 +1,3 @@ + +! Eventually replace this with half-degree version in the proper place +stream_fldFileName_gdd20_baseline = '/glade/u/home/samrabin/ctsm_scale-mat-reqs/tools/crop_calendars/test.nc' From 317f77d7b692a58644e679d48c8f4ec026c8a697 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 11:30:54 -0600 Subject: [PATCH 098/406] Add cropcals_rx namelist boolean. --- bld/namelist_files/namelist_defaults_ctsm.xml | 15 +++++++++++---- bld/namelist_files/namelist_definition_ctsm.xml | 9 +++++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 655e97c47c..08f67a2712 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1686,10 +1686,17 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c nn nn - -1850 -2100 -1850 + +.false. +2000 +2000 +2000 + + +lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc +lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc +lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc +share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index c88e987756..366b7498e8 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1773,14 +1773,19 @@ Mapping method from LAI input file to the model resolution + +Flag to enable prescribed crop calendars (sowing window dates and maturity requirement) + + -First year to loop over for crop calendar data +First year to loop over for crop calendar data (not including gdd20_baseline file) -Last year to loop over for crop calendar data +Last year to loop over for crop calendar data (not including gdd20_baseline file) Date: Wed, 29 May 2024 11:38:29 -0600 Subject: [PATCH 099/406] CLMBuildNamelist: Fix logic and message for check of sowing window start/end files. --- bld/CLMBuildNamelist.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 54752b6dbc..9753549db1 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4200,8 +4200,8 @@ sub setup_logic_cropcal_streams { my $gdd_file = $nl->get_value('stream_fldFileName_cultivar_gdds') ; my $gdd20_baseline_file = $nl->get_value('stream_fldFileName_gdd20_baseline') ; my $mesh_file = $nl->get_value('stream_meshfile_cropcal') ; - if ( ($swindow_start_file eq '' and $swindow_start_file ne '') or ($swindow_start_file ne '' and $swindow_start_file eq '') ) { - $log->fatal_error("When specifying sowing window dates, you must provide both swindow_start_file and swindow_end_file. To specify exact sowing dates, use the same file." ); + if ( ($swindow_start_file eq '' and $swindow_end_file ne '') or ($swindow_start_file ne '' and $swindow_end_file eq '') ) { + $log->fatal_error("When specifying sowing window dates, you must provide both stream_fldFileName_swindow_start and stream_fldFileName_swindow_end. To specify exact sowing dates, use the same file." ); } if ( $gdd_file eq '' and $gdd20_baseline_file ne '' ) { $log->fatal_error("If not providing stream_fldFileName_cultivar_gdds, don't provide stream_fldFileName_gdd20_baseline"); From b19573466744c3426de70a1edbb9a3725821de67 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 11:42:11 -0600 Subject: [PATCH 100/406] CLMBuildNamelist: Use &string_is_undef_or_empty() instead of comparing to ''. --- bld/CLMBuildNamelist.pm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 9753549db1..833a254f1a 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4200,30 +4200,30 @@ sub setup_logic_cropcal_streams { my $gdd_file = $nl->get_value('stream_fldFileName_cultivar_gdds') ; my $gdd20_baseline_file = $nl->get_value('stream_fldFileName_gdd20_baseline') ; my $mesh_file = $nl->get_value('stream_meshfile_cropcal') ; - if ( ($swindow_start_file eq '' and $swindow_end_file ne '') or ($swindow_start_file ne '' and $swindow_end_file eq '') ) { + if ( (&string_is_undef_or_empty($swindow_start_file) and !&string_is_undef_or_empty($swindow_end_file)) or (!&string_is_undef_or_empty($swindow_start_file) and &string_is_undef_or_empty($swindow_end_file)) ) { $log->fatal_error("When specifying sowing window dates, you must provide both stream_fldFileName_swindow_start and stream_fldFileName_swindow_end. To specify exact sowing dates, use the same file." ); } - if ( $gdd_file eq '' and $gdd20_baseline_file ne '' ) { + if ( &string_is_undef_or_empty($gdd_file) and ! &string_is_undef_or_empty($gdd20_baseline_file) ) { $log->fatal_error("If not providing stream_fldFileName_cultivar_gdds, don't provide stream_fldFileName_gdd20_baseline"); } if ( $generate_crop_gdds eq '.true.' ) { if ( $use_mxmat eq '.true.' ) { $log->fatal_error("If generate_crop_gdds is true, you must also set use_mxmat to false" ); } - if ( $swindow_start_file eq '' or $swindow_end_file eq '' ) { + if ( &string_is_undef_or_empty($swindow_start_file) or &string_is_undef_or_empty($swindow_end_file) ) { $log->fatal_error("If generate_crop_gdds is true, you must specify stream_fldFileName_swindow_start and stream_fldFileName_swindow_end") } if ( $swindow_start_file ne $swindow_end_file ) { $log->fatal_error("If generate_crop_gdds is true, you must specify exact sowing dates by setting stream_fldFileName_swindow_start and stream_fldFileName_swindow_end to the same file") } - if ( $gdd_file ne '' ) { + if ( ! &string_is_undef_or_empty($gdd_file) ) { $log->fatal_error("If generate_crop_gdds is true, do not specify stream_fldFileName_cultivar_gdds") } - if ( $gdd20_baseline_file ne '' ) { + if ( ! &string_is_undef_or_empty($gdd20_baseline_file) ) { $log->fatal_error("If generate_crop_gdds is true, do not specify stream_fldFileName_gdd20_baseline") } } - if ( $mesh_file eq '' and ( $swindow_start_file ne '' or $gdd_file ne '' ) ) { + if ( &string_is_undef_or_empty($mesh_file) and ( ! &string_is_undef_or_empty($swindow_start_file) or ! &string_is_undef_or_empty($gdd_file) ) ) { $log->fatal_error("If prescribing crop sowing dates and/or maturity requirements, you must specify stream_meshfile_cropcal") } } From 4b18a5bb159e1a98a5905ec939dffb5981e55185 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 11:48:50 -0600 Subject: [PATCH 101/406] CLMBuildNamelist: Use &value_is_true() instead of comparing to '.true.' --- bld/CLMBuildNamelist.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 833a254f1a..d35649bef4 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4206,8 +4206,8 @@ sub setup_logic_cropcal_streams { if ( &string_is_undef_or_empty($gdd_file) and ! &string_is_undef_or_empty($gdd20_baseline_file) ) { $log->fatal_error("If not providing stream_fldFileName_cultivar_gdds, don't provide stream_fldFileName_gdd20_baseline"); } - if ( $generate_crop_gdds eq '.true.' ) { - if ( $use_mxmat eq '.true.' ) { + if ( &value_is_true($generate_crop_gdds) ) { + if ( &value_is_true($use_mxmat) ) { $log->fatal_error("If generate_crop_gdds is true, you must also set use_mxmat to false" ); } if ( &string_is_undef_or_empty($swindow_start_file) or &string_is_undef_or_empty($swindow_end_file) ) { From e76fbbc134ab5c4c6186f35732d11b46879a5a13 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 12:02:17 -0600 Subject: [PATCH 102/406] CLMBuildNamelist: Update setup_logic_cropcal_streams() with cropcals_rx. --- bld/CLMBuildNamelist.pm | 77 ++++++++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 24 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index d35649bef4..aff46be3f0 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4172,41 +4172,73 @@ sub setup_logic_lai_streams { sub setup_logic_cropcal_streams { my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; - # Set first and last stream years - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_first_cropcal', - 'sim_year'=>$nl_flags->{'sim_year'}, - 'sim_year_range'=>$nl_flags->{'sim_year_range'}); - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_last_cropcal', - 'sim_year'=>$nl_flags->{'sim_year'}, - 'sim_year_range'=>$nl_flags->{'sim_year_range'}); - - # Set align year, if first and last years are different - if ( $nl->get_value('stream_year_first_cropcal') != - $nl->get_value('stream_year_last_cropcal') ) { - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, - 'model_year_align_cropcal', 'sim_year'=>$nl_flags->{'sim_year'}, - 'sim_year_range'=>$nl_flags->{'sim_year_range'}); - } - # Set up other crop calendar parameters + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'cropcals_rx'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'generate_crop_gdds'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_mxmat'); - # Option checks - my $generate_crop_gdds = $nl->get_value('generate_crop_gdds') ; - my $use_mxmat = $nl->get_value('use_mxmat') ; + # Add defaults if using prescribed crop calendars + my $cropcals_rx = $nl->get_value('cropcals_rx') ; + if ( &value_is_true($cropcals_rx) ) { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_swindow_start'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_swindow_end'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldfilename_cultivar_gdds'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_meshfile_cropcal'); + } + + # Add defaults if using any potentially time-varying crop calendar input files my $swindow_start_file = $nl->get_value('stream_fldFileName_swindow_start') ; my $swindow_end_file = $nl->get_value('stream_fldFileName_swindow_end') ; my $gdd_file = $nl->get_value('stream_fldFileName_cultivar_gdds') ; my $gdd20_baseline_file = $nl->get_value('stream_fldFileName_gdd20_baseline') ; my $mesh_file = $nl->get_value('stream_meshfile_cropcal') ; - if ( (&string_is_undef_or_empty($swindow_start_file) and !&string_is_undef_or_empty($swindow_end_file)) or (!&string_is_undef_or_empty($swindow_start_file) and &string_is_undef_or_empty($swindow_end_file)) ) { - $log->fatal_error("When specifying sowing window dates, you must provide both stream_fldFileName_swindow_start and stream_fldFileName_swindow_end. To specify exact sowing dates, use the same file." ); + if ( !&string_is_undef_or_empty($swindow_start_file) or !&string_is_undef_or_empty($swindow_end_file) or !&string_is_undef_or_empty($gdd_file) or !&string_is_undef_or_empty($gdd20_baseline_file) or !&string_is_undef_or_empty($mesh_file)) { + + # User gave an input file without specifying cropcals_rx = .true. + # Changing this means nothing to the code, but helps namelist make more sense + if ( ! &value_is_true($cropcals_rx) ){ + $log->fatal_error("If providing any crop calendar input file(s), cropcals_rx must be true" ); + } + + # User provided an input file but set mesh file to empty + if ( &string_is_undef_or_empty($mesh_file) ) { + $log->fatal_error("If providing any crop calendar input file(s), you must provide stream_meshfile_cropcal" ); + } + + # Set first and last stream years + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_first_cropcal', + 'sim_year'=>$nl_flags->{'sim_year'}, + 'sim_year_range'=>$nl_flags->{'sim_year_range'}); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_last_cropcal', + 'sim_year'=>$nl_flags->{'sim_year'}, + 'sim_year_range'=>$nl_flags->{'sim_year_range'}); + + # Set align year, if first and last years are different + if ( $nl->get_value('stream_year_first_cropcal') != + $nl->get_value('stream_year_last_cropcal') ) { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, + 'model_year_align_cropcal', 'sim_year'=>$nl_flags->{'sim_year'}, + 'sim_year_range'=>$nl_flags->{'sim_year_range'}); + } } + + # If running with prescribed crop calendars, certain files must be provided + my $generate_crop_gdds = $nl->get_value('generate_crop_gdds') ; + if ( &value_is_true($cropcals_rx) ) { + if ( &string_is_undef_or_empty($swindow_start_file) or &string_is_undef_or_empty($swindow_end_file) ) { + $log->fatal_error("If cropcals_rx is true, sowing window start and end files must be provided. To specify exact sowing dates, use the same file." ); + } + if ( &string_is_undef_or_empty($gdd_file) and (! &value_is_true($generate_crop_gdds)) ){ + $log->fatal_error("If cropcals_rx is true and generate_crop_gdds is false, maturity requirement file stream_fldFileName_cultivar_gdds must be provided" ); + } + } + + # Option checks if ( &string_is_undef_or_empty($gdd_file) and ! &string_is_undef_or_empty($gdd20_baseline_file) ) { $log->fatal_error("If not providing stream_fldFileName_cultivar_gdds, don't provide stream_fldFileName_gdd20_baseline"); } if ( &value_is_true($generate_crop_gdds) ) { + my $use_mxmat = $nl->get_value('use_mxmat') ; if ( &value_is_true($use_mxmat) ) { $log->fatal_error("If generate_crop_gdds is true, you must also set use_mxmat to false" ); } @@ -4223,9 +4255,6 @@ sub setup_logic_cropcal_streams { $log->fatal_error("If generate_crop_gdds is true, do not specify stream_fldFileName_gdd20_baseline") } } - if ( &string_is_undef_or_empty($mesh_file) and ( ! &string_is_undef_or_empty($swindow_start_file) or ! &string_is_undef_or_empty($gdd_file) ) ) { - $log->fatal_error("If prescribing crop sowing dates and/or maturity requirements, you must specify stream_meshfile_cropcal") - } } #------------------------------------------------------------------------------- From 0f729d8edcec2fa97db471bb766cc8964b01ed81 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 14:16:45 -0600 Subject: [PATCH 103/406] Bugfix: cropcalStreamMod now handles cropcals_rx. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index f60ab51000..101ad9f2a4 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -47,6 +47,7 @@ module cropcalStreamMod character(len=CL) :: stream_fldFileName_swindow_end ! sowing window end stream filename to read character(len=CL) :: stream_fldFileName_cultivar_gdds ! cultivar growing degree-days stream filename to read character(len=CL) :: stream_fldFileName_gdd20_baseline ! GDD20 baseline stream filename to read + logical :: cropcals_rx ! Used only for setting input files in namelist; does nothing in code, but needs to be here so namelist read doesn't crash character(len=*), parameter :: sourcefile = & __FILE__ @@ -95,7 +96,8 @@ subroutine cropcal_init(bounds) stream_fldFileName_swindow_end, & stream_fldFileName_cultivar_gdds, & stream_fldFileName_gdd20_baseline, & - stream_meshfile_cropcal + stream_meshfile_cropcal, & + cropcals_rx ! Default values for namelist stream_year_first_cropcal = 1 ! first year in stream to use From 74411ea31189969410205a3edba96c14670a38f4 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 14:17:05 -0600 Subject: [PATCH 104/406] Update RxCropCals testmod to use cropcals_rx true. --- .../testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm index 9cbfea97f0..8a0b4a91be 100644 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm @@ -1,8 +1,2 @@ -! Sowing windows -stream_fldFileName_swindow_start = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc' -stream_fldFileName_swindow_end = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc' -stream_fldFileName_cultivar_gdds = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc' -stream_year_first_cropcal = 2000 -stream_year_last_cropcal = 2000 -stream_meshfile_cropcal = '$DIN_LOC_ROOT/share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc' +cropcals_rx = .true. From 9d1f88d15ac942cb7176b30bc48163ab6e5031fc Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 15:30:41 -0600 Subject: [PATCH 105/406] Add RxCropCals and RxCropCalsAdapt tests to new suite crop_calendars. --- cime_config/testdefs/testlist_clm.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 9cfba6f5b3..bf763c4775 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3719,5 +3719,23 @@ + + + + + + + + + + + + + + + + + + From 7e5257a94efa920f7b608a97b041fef63b1d9aa7 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 16:12:06 -0600 Subject: [PATCH 106/406] Add cropcals_rx_adapt option. --- bld/CLMBuildNamelist.pm | 50 +++++++++++++++---- bld/namelist_files/namelist_defaults_ctsm.xml | 6 +++ .../namelist_definition_ctsm.xml | 5 ++ .../clm/RxCropCalsAdapt/user_nl_clm | 4 +- src/cpl/share_esmf/cropcalStreamMod.F90 | 4 +- 5 files changed, 55 insertions(+), 14 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index aff46be3f0..fc80379201 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4174,19 +4174,29 @@ sub setup_logic_cropcal_streams { # Set up other crop calendar parameters add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'cropcals_rx'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'cropcals_rx_adapt'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'generate_crop_gdds'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_mxmat'); - # Add defaults if using prescribed crop calendars + # These can't both be true my $cropcals_rx = $nl->get_value('cropcals_rx') ; - if ( &value_is_true($cropcals_rx) ) { + my $cropcals_rx_adapt = $nl->get_value('cropcals_rx_adapt') ; + if (&value_is_true($cropcals_rx) and &value_is_true($cropcals_rx_adapt)) { + $log->fatal_error("cropcals_rx and cropcals_rx_adapt may not both be true" ); + } + + # Add defaults if using prescribed crop calendars + if ( &value_is_true($cropcals_rx) or &value_is_true($cropcals_rx_adapt) ) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_swindow_start'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_swindow_end'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldfilename_cultivar_gdds'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_meshfile_cropcal'); + if ( &value_is_true($cropcals_rx_adapt) ) { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_gdd20_baseline'); + } } - # Add defaults if using any potentially time-varying crop calendar input files + # Add defaults if using any crop calendar input files my $swindow_start_file = $nl->get_value('stream_fldFileName_swindow_start') ; my $swindow_end_file = $nl->get_value('stream_fldFileName_swindow_end') ; my $gdd_file = $nl->get_value('stream_fldFileName_cultivar_gdds') ; @@ -4194,10 +4204,20 @@ sub setup_logic_cropcal_streams { my $mesh_file = $nl->get_value('stream_meshfile_cropcal') ; if ( !&string_is_undef_or_empty($swindow_start_file) or !&string_is_undef_or_empty($swindow_end_file) or !&string_is_undef_or_empty($gdd_file) or !&string_is_undef_or_empty($gdd20_baseline_file) or !&string_is_undef_or_empty($mesh_file)) { - # User gave an input file without specifying cropcals_rx = .true. - # Changing this means nothing to the code, but helps namelist make more sense - if ( ! &value_is_true($cropcals_rx) ){ - $log->fatal_error("If providing any crop calendar input file(s), cropcals_rx must be true" ); + # User gave an input file without specifying cropcals_rx or cropcals_rx_adapt = .true. + # Requiring this means nothing to the code, but helps namelist make more sense + if ( !&value_is_true($cropcals_rx) and !&value_is_true($cropcals_rx_adapt) ){ + $log->fatal_error("If providing any crop calendar input file(s), cropcals_rx or cropcals_rx_adapt must be true" ); + } + + # User set cropcals_rx_adapt to true but set stream_fldFileName_gdd20_baseline to empty + if ( &value_is_true($cropcals_rx_adapt) and &string_is_undef_or_empty($gdd20_baseline_file) ) { + $log->fatal_error("If cropcals_rx_adapt is true, stream_fldFileName_gdd20_baseline must be provided" ); + } + + # cropcals_rx_adapt is false but user provided stream_fldFileName_gdd20_baseline + if ( !&value_is_true($cropcals_rx_adapt) and !&string_is_undef_or_empty($gdd20_baseline_file) ) { + $log->fatal_error("If stream_fldFileName_gdd20_baseline provided, cropcals_rx_adapt must be true" ); } # User provided an input file but set mesh file to empty @@ -4213,9 +4233,17 @@ sub setup_logic_cropcal_streams { 'sim_year'=>$nl_flags->{'sim_year'}, 'sim_year_range'=>$nl_flags->{'sim_year_range'}); - # Set align year, if first and last years are different + # Check/set things if first and last years are different if ( $nl->get_value('stream_year_first_cropcal') != $nl->get_value('stream_year_last_cropcal') ) { + + # Do not allow maturity requirements to change over time if stream_fldFileName_gdd20_baseline is provided. That would be nonsensical. + # Note that this restricts sowing windows from changing over time as well, because there are not separate stream_year settings for that. + if ( !&string_is_undef_or_empty($gdd20_baseline_file) ) { + $log->fatal_error("If cropcals_rx_adapt is true (i.e., stream_fldFileName_gdd20_baseline is provided), no crop calendar input is allowed to vary over time (i.e., stream_year_first_cropcal and stream_year_last_cropcal must be the same)." ); + } + + # Set align year add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'model_year_align_cropcal', 'sim_year'=>$nl_flags->{'sim_year'}, 'sim_year_range'=>$nl_flags->{'sim_year_range'}); @@ -4224,12 +4252,12 @@ sub setup_logic_cropcal_streams { # If running with prescribed crop calendars, certain files must be provided my $generate_crop_gdds = $nl->get_value('generate_crop_gdds') ; - if ( &value_is_true($cropcals_rx) ) { + if ( &value_is_true($cropcals_rx) or &value_is_true($cropcals_rx_adapt) ) { if ( &string_is_undef_or_empty($swindow_start_file) or &string_is_undef_or_empty($swindow_end_file) ) { - $log->fatal_error("If cropcals_rx is true, sowing window start and end files must be provided. To specify exact sowing dates, use the same file." ); + $log->fatal_error("If cropcals_rx or cropcals_rx_adapt is true, sowing window start and end files must be provided. To specify exact sowing dates, use the same file." ); } if ( &string_is_undef_or_empty($gdd_file) and (! &value_is_true($generate_crop_gdds)) ){ - $log->fatal_error("If cropcals_rx is true and generate_crop_gdds is false, maturity requirement file stream_fldFileName_cultivar_gdds must be provided" ); + $log->fatal_error("If cropcals_rx or cropcals_rx_adapt is true and generate_crop_gdds is false, maturity requirement file stream_fldFileName_cultivar_gdds must be provided" ); } } diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 08f67a2712..85f37a22fd 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1688,6 +1688,7 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c .false. +.false. 2000 2000 2000 @@ -1697,6 +1698,11 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc +lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc +lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc +lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc +lnd/clm2/testdata/gdd20baseline.tmp_dontupload.nc +share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 366b7498e8..f37ada6847 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1778,6 +1778,11 @@ Mapping method from LAI input file to the model resolution Flag to enable prescribed crop calendars (sowing window dates and maturity requirement) + +Flag to enable prescribed crop calendars (sowing window dates and maturity requirement), with maturity requirement adaptive based on recent climate + + First year to loop over for crop calendar data (not including gdd20_baseline file) diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm index 1bba9d2a89..709c7221e0 100644 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm @@ -1,3 +1,3 @@ -! Eventually replace this with half-degree version in the proper place -stream_fldFileName_gdd20_baseline = '/glade/u/home/samrabin/ctsm_scale-mat-reqs/tools/crop_calendars/test.nc' +cropcals_rx = .false. +cropcals_rx_adapt = .true. diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 101ad9f2a4..3333b6cfdc 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -48,6 +48,7 @@ module cropcalStreamMod character(len=CL) :: stream_fldFileName_cultivar_gdds ! cultivar growing degree-days stream filename to read character(len=CL) :: stream_fldFileName_gdd20_baseline ! GDD20 baseline stream filename to read logical :: cropcals_rx ! Used only for setting input files in namelist; does nothing in code, but needs to be here so namelist read doesn't crash + logical :: cropcals_rx_adapt ! Used only for setting input files in namelist; does nothing in code, but needs to be here so namelist read doesn't crash character(len=*), parameter :: sourcefile = & __FILE__ @@ -97,7 +98,8 @@ subroutine cropcal_init(bounds) stream_fldFileName_cultivar_gdds, & stream_fldFileName_gdd20_baseline, & stream_meshfile_cropcal, & - cropcals_rx + cropcals_rx, & + cropcals_rx_adapt ! Default values for namelist stream_year_first_cropcal = 1 ! first year in stream to use From 5911cea434581d33f1a4892699f029c32132e63e Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 31 May 2024 13:13:02 -0600 Subject: [PATCH 107/406] Move add_default() of model_year_align_cropcal to be with other year params. --- bld/CLMBuildNamelist.pm | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index fc80379201..bbe83a3dc5 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4225,13 +4225,16 @@ sub setup_logic_cropcal_streams { $log->fatal_error("If providing any crop calendar input file(s), you must provide stream_meshfile_cropcal" ); } - # Set first and last stream years + # Set stream years add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_first_cropcal', 'sim_year'=>$nl_flags->{'sim_year'}, 'sim_year_range'=>$nl_flags->{'sim_year_range'}); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_last_cropcal', 'sim_year'=>$nl_flags->{'sim_year'}, 'sim_year_range'=>$nl_flags->{'sim_year_range'}); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'model_year_align_cropcal', + 'sim_year'=>$nl_flags->{'sim_year'}, + 'sim_year_range'=>$nl_flags->{'sim_year_range'}); # Check/set things if first and last years are different if ( $nl->get_value('stream_year_first_cropcal') != @@ -4242,11 +4245,6 @@ sub setup_logic_cropcal_streams { if ( !&string_is_undef_or_empty($gdd20_baseline_file) ) { $log->fatal_error("If cropcals_rx_adapt is true (i.e., stream_fldFileName_gdd20_baseline is provided), no crop calendar input is allowed to vary over time (i.e., stream_year_first_cropcal and stream_year_last_cropcal must be the same)." ); } - - # Set align year - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, - 'model_year_align_cropcal', 'sim_year'=>$nl_flags->{'sim_year'}, - 'sim_year_range'=>$nl_flags->{'sim_year_range'}); } } From d89cb9d4bc3cd8ab5761d23cf398b9b682a047fa Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 31 May 2024 13:38:31 -0600 Subject: [PATCH 108/406] Split streams year params for swindows vs maturity reqts. --- bld/CLMBuildNamelist.pm | 29 +++++---- bld/namelist_files/namelist_defaults_ctsm.xml | 9 ++- .../namelist_definition_ctsm.xml | 27 ++++++-- cime_config/SystemTests/rxcropmaturity.py | 6 +- .../clm/sowingWindows/user_nl_clm | 4 +- .../Running-with-custom-crop-calendars.rst | 10 +-- src/cpl/share_esmf/cropcalStreamMod.F90 | 63 ++++++++++++------- 7 files changed, 93 insertions(+), 55 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index bbe83a3dc5..26725b7d96 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4226,25 +4226,30 @@ sub setup_logic_cropcal_streams { } # Set stream years - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_first_cropcal', + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_first_cropcal_swindows', 'sim_year'=>$nl_flags->{'sim_year'}, 'sim_year_range'=>$nl_flags->{'sim_year_range'}); - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_last_cropcal', + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_last_cropcal_swindows', 'sim_year'=>$nl_flags->{'sim_year'}, 'sim_year_range'=>$nl_flags->{'sim_year_range'}); - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'model_year_align_cropcal', + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'model_year_align_cropcal_swindows', + 'sim_year'=>$nl_flags->{'sim_year'}, + 'sim_year_range'=>$nl_flags->{'sim_year_range'}); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_first_cropcal_cultivar_gdds', + 'sim_year'=>$nl_flags->{'sim_year'}, + 'sim_year_range'=>$nl_flags->{'sim_year_range'}); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_last_cropcal_cultivar_gdds', + 'sim_year'=>$nl_flags->{'sim_year'}, + 'sim_year_range'=>$nl_flags->{'sim_year_range'}); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'model_year_align_cropcal_cultivar_gdds', 'sim_year'=>$nl_flags->{'sim_year'}, 'sim_year_range'=>$nl_flags->{'sim_year_range'}); - # Check/set things if first and last years are different - if ( $nl->get_value('stream_year_first_cropcal') != - $nl->get_value('stream_year_last_cropcal') ) { - - # Do not allow maturity requirements to change over time if stream_fldFileName_gdd20_baseline is provided. That would be nonsensical. - # Note that this restricts sowing windows from changing over time as well, because there are not separate stream_year settings for that. - if ( !&string_is_undef_or_empty($gdd20_baseline_file) ) { - $log->fatal_error("If cropcals_rx_adapt is true (i.e., stream_fldFileName_gdd20_baseline is provided), no crop calendar input is allowed to vary over time (i.e., stream_year_first_cropcal and stream_year_last_cropcal must be the same)." ); - } + # Do not allow maturity requirements to change over time if stream_fldFileName_gdd20_baseline is provided. That would be nonsensical. + if ( $nl->get_value('stream_year_first_cropcal_cultivar_gdds') != + $nl->get_value('stream_year_last_cropcal_cultivar_gdds') + and !&string_is_undef_or_empty($gdd20_baseline_file) ) { + $log->fatal_error("If cropcals_rx_adapt is true (i.e., stream_fldFileName_gdd20_baseline is provided), baseline maturity requirements are allowed to vary over time (i.e., stream_year_first_cropcal_cultivar_gdds and stream_year_last_cropcal_cultivar_gdds must be the same)." ); } } diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 85f37a22fd..da0a523adc 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1689,9 +1689,12 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c .false. .false. -2000 -2000 -2000 +2000 +2000 +2000 +2000 +2000 +2000 lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index f37ada6847..50e645519c 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1783,19 +1783,34 @@ Flag to enable prescribed crop calendars (sowing window dates and maturity requi Flag to enable prescribed crop calendars (sowing window dates and maturity requirement), with maturity requirement adaptive based on recent climate - -First year to loop over for crop calendar data (not including gdd20_baseline file) +First year to loop over for crop sowing windows - -Last year to loop over for crop calendar data (not including gdd20_baseline file) +Last year to loop over for crop sowing windows - -Simulation year that aligns with stream_year_first_cropcal value +Simulation year that aligns with stream_year_first_cropcal_swindows value + + + +First year to loop over for crop maturity requirements + + + +Last year to loop over for crop maturity requirements + + + +Simulation year that aligns with stream_year_first_cropcal_cultivar_gdds value Date: Fri, 31 May 2024 13:55:29 -0600 Subject: [PATCH 109/406] Explain hard-coding of 2000 when reading GDD20 baseline file. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 8f444a0479..413ddbefce 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -274,8 +274,9 @@ subroutine cropcal_init(bounds) end if ! Initialize the cdeps data type sdat_cropcal_gdd20_baseline - ! NOTE: stream_dtlimit 1.5 didn't work for some reason - !TODO SSR: Do not allow time axis length > 1 + ! NOTE: Hard-coded to one particular year because it should NOT vary over time. Note that the + ! particular year chosen doesn't matter. Users can base their file on whatever baseline they + ! want; they just need to put 2000 on the time axis. if (adapt_cropcal_rx_cultivar_gdds) then call shr_strdata_init_from_inline(sdat_cropcal_gdd20_baseline, & my_task = iam, & From 75d7df65dc721083f19a92e801f883c6e4b5d8cb Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 31 May 2024 15:07:12 -0600 Subject: [PATCH 110/406] Functionize UpdateAccVars_CropGDDs(). --- src/biogeophys/TemperatureType.F90 | 121 ++++++++++++++++------------- 1 file changed, 69 insertions(+), 52 deletions(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index ab310650c8..1b28b2e1aa 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -129,6 +129,7 @@ module TemperatureType procedure, public :: InitAccBuffer procedure, public :: InitAccVars procedure, public :: UpdateAccVars + procedure, private :: UpdateAccVars_CropGDDs end type temperature_type @@ -1357,6 +1358,71 @@ subroutine InitAccVars(this, bounds) end subroutine InitAccVars + subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, dtime, nstep, basetemp_int, gddx_patch) + ! + ! USES + use shr_const_mod , only : SHR_CONST_CDAY, SHR_CONST_TKFRZ + use accumulMod , only : update_accum_field, extract_accum_field, accumResetVal + ! + ! !ARGUMENTS + class(temperature_type) :: this + real(r8), intent(inout), pointer, dimension(:) :: rbufslp ! temporary single level - pft level + integer, intent(in) :: begp, endp + integer, intent(in) :: month, day, secs, dtime, nstep + integer, intent(in) :: basetemp_int ! Crop base temperature. Integer to avoid possible float weirdness + real(r8), intent(inout), pointer, dimension(:) :: gddx_patch ! E.g., gdd0_patch + ! + ! !LOCAL VARIABLES + real(r8) :: basetemp_r8 ! real(r8) version of basetemp for arithmetic + real(r8) :: max_accum ! Maximum daily accumulation + character(8) :: field_name ! E.g., GDD0 + character(32) :: format_string + integer :: p, g + + basetemp_r8 = real(basetemp_int, r8) + + ! Get maximum daily accumulation + if (basetemp_int == 0) then + ! SSR 2024-05-31: I'm not sure why this was different for base temp 0, but I'm keeping it as I refactor into UpdateAccVars_CropGDDs() + max_accum = 26._r8 + else + max_accum = 30._r8 + end if + + do p = begp,endp + + ! Avoid unnecessary calculations over inactive points + if (.not. patch%active(p)) then + cycle + end if + + g = patch%gridcell(p) + if (month==1 .and. day==1 .and. secs==dtime) then + rbufslp(p) = accumResetVal ! reset gdd + else if (( month > 3 .and. month < 10 .and. grc%latdeg(g) >= 0._r8) .or. & + ((month > 9 .or. month < 4) .and. grc%latdeg(g) < 0._r8) ) then + rbufslp(p) = max(0._r8, min(max_accum, & + this%t_ref2m_patch(p)-(SHR_CONST_TKFRZ + basetemp_r8))) * dtime/SHR_CONST_CDAY + else + rbufslp(p) = 0._r8 ! keeps gdd unchanged at other times (eg, through Dec in NH) + end if + end do + + ! Get field name + if (basetemp_int < 10) then + format_string = "(A3,I1)" + else if (basetemp_int < 100) then + format_string = "(A3,I2)" + else + format_string = "(A3,I3)" + end if + write(field_name, format_string) "GDD",basetemp_int + + ! Save + call update_accum_field (trim(field_name), rbufslp, nstep) + call extract_accum_field (trim(field_name), gddx_patch, nstep) + end subroutine UpdateAccVars_CropGDDs + !----------------------------------------------------------------------- subroutine UpdateAccVars (this, bounds) ! @@ -1538,63 +1604,14 @@ subroutine UpdateAccVars (this, bounds) ! Accumulate and extract GDD0 - - do p = begp,endp - ! Avoid unnecessary calculations over inactive points - if (patch%active(p)) then - g = patch%gridcell(p) - if (month==1 .and. day==1 .and. secs==dtime) then - rbufslp(p) = accumResetVal ! reset gdd - else if (( month > 3 .and. month < 10 .and. grc%latdeg(g) >= 0._r8) .or. & - ((month > 9 .or. month < 4) .and. grc%latdeg(g) < 0._r8) ) then - rbufslp(p) = max(0._r8, min(26._r8, this%t_ref2m_patch(p)-SHR_CONST_TKFRZ)) * dtime/SHR_CONST_CDAY - else - rbufslp(p) = 0._r8 ! keeps gdd unchanged at other times (eg, through Dec in NH) - end if - end if - end do - call update_accum_field ('GDD0', rbufslp, nstep) - call extract_accum_field ('GDD0', this%gdd0_patch, nstep) + call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 0, this%gdd0_patch) ! Accumulate and extract GDD8 - - do p = begp,endp - ! Avoid unnecessary calculations over inactive points - if (patch%active(p)) then - g = patch%gridcell(p) - if (month==1 .and. day==1 .and. secs==dtime) then - rbufslp(p) = accumResetVal ! reset gdd - else if (( month > 3 .and. month < 10 .and. grc%latdeg(g) >= 0._r8) .or. & - ((month > 9 .or. month < 4) .and. grc%latdeg(g) < 0._r8) ) then - rbufslp(p) = max(0._r8, min(30._r8, & - this%t_ref2m_patch(p)-(SHR_CONST_TKFRZ + 8._r8))) * dtime/SHR_CONST_CDAY - else - rbufslp(p) = 0._r8 ! keeps gdd unchanged at other times (eg, through Dec in NH) - end if - end if - end do - call update_accum_field ('GDD8', rbufslp, nstep) - call extract_accum_field ('GDD8', this%gdd8_patch, nstep) + call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 8, this%gdd8_patch) ! Accumulate and extract GDD10 + call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 10, this%gdd10_patch) - do p = begp,endp - ! Avoid unnecessary calculations over inactive points - if (patch%active(p)) then - g = patch%gridcell(p) - if (month==1 .and. day==1 .and. secs==dtime) then - rbufslp(p) = accumResetVal ! reset gdd - else if (( month > 3 .and. month < 10 .and. grc%latdeg(g) >= 0._r8) .or. & - ((month > 9 .or. month < 4) .and. grc%latdeg(g) < 0._r8) ) then - rbufslp(p) = max(0._r8, min(30._r8, & - this%t_ref2m_patch(p)-(SHR_CONST_TKFRZ + 10._r8))) * dtime/SHR_CONST_CDAY - else - rbufslp(p) = 0._r8 ! keeps gdd unchanged at other times (eg, through Dec in NH) - end if - end if - end do - call update_accum_field ('GDD10', rbufslp, nstep) - call extract_accum_field ('GDD10', this%gdd10_patch, nstep) ! Accumulate and extract running 20-year means if (is_end_curr_year()) then From 45aff290b33baed02b48b089858de6f0f4a89a74 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 31 May 2024 17:08:01 -0600 Subject: [PATCH 111/406] Optionally read/use gdd20 accum seasons from stream files. --- bld/CLMBuildNamelist.pm | 15 ++ bld/namelist_files/namelist_defaults_ctsm.xml | 3 + .../namelist_definition_ctsm.xml | 15 ++ src/biogeochem/CropType.F90 | 4 + src/biogeophys/TemperatureType.F90 | 54 +++-- src/cpl/share_esmf/cropcalStreamMod.F90 | 198 ++++++++++++++++-- src/main/clm_driver.F90 | 2 +- 7 files changed, 265 insertions(+), 26 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 26725b7d96..dddd17ced9 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4175,6 +4175,7 @@ sub setup_logic_cropcal_streams { # Set up other crop calendar parameters add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'cropcals_rx'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'cropcals_rx_adapt'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_gdd20_seasons'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'generate_crop_gdds'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_mxmat'); @@ -4185,6 +4186,20 @@ sub setup_logic_cropcal_streams { $log->fatal_error("cropcals_rx and cropcals_rx_adapt may not both be true" ); } + # Add defaults if reading gdd20 seasons from stream files + my $stream_gdd20_seasons = $nl->get_value('stream_gdd20_seasons') ; + my $gdd20_season_start_file = $nl->get_value('stream_fldFileName_gdd20_season_start') ; + my $gdd20_season_end_file = $nl->get_value('stream_fldFileName_gdd20_season_end') ; + if ( &value_is_true($stream_gdd20_seasons)) { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_gdd20_season_start'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_gdd20_season_end'); + + # Check + if ( &string_is_undef_or_empty($gdd20_season_start_file) or &string_is_undef_or_empty($gdd20_season_end_file) ) { + $log->fatal_error("If stream_gdd20_seasons is true, gdd20 season start and end files must be provided." ); + } + } + # Add defaults if using prescribed crop calendars if ( &value_is_true($cropcals_rx) or &value_is_true($cropcals_rx_adapt) ) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_swindow_start'); diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index da0a523adc..7e3ddb7c64 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1689,6 +1689,7 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c .false. .false. +.false. 2000 2000 2000 @@ -1705,6 +1706,8 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc lnd/clm2/testdata/gdd20baseline.tmp_dontupload.nc +lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc +lnd/clm2/cropdata/calendars/processed/hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 50e645519c..036b9aca72 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1838,6 +1838,21 @@ Filename of input stream data for cultivar growing degree-day targets Filename of input stream data for baseline GDD20 values + +Filename of input stream data for date (day of year) of start of gdd20 accumulation season. + + + +Set this to true to read gdd20 accumulation season start and end dates from stream files, rather than using hard-coded hemisphere-specific "warm seasons." + + + +Filename of input stream data for date (day of year) of end of gdd20 accumulation season. + + Filename of input stream data for crop calendar inputs diff --git a/src/biogeochem/CropType.F90 b/src/biogeochem/CropType.F90 index a7bccf1a73..04806f9349 100644 --- a/src/biogeochem/CropType.F90 +++ b/src/biogeochem/CropType.F90 @@ -53,6 +53,8 @@ module CropType integer , pointer :: rx_swindow_ends_thisyr_patch (:,:) ! all prescribed sowing window end dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: rx_cultivar_gdds_thisyr_patch (:,:) ! all cultivar GDD targets for this patch this year (ddays) [patch, mxsowings] real(r8), pointer :: gdd20_baseline_patch (:) ! GDD20 baseline for this patch (ddays) [patch] + integer , pointer :: gdd20_season_start_patch(:) ! gdd20 season start date for this patch (day of year) [patch] + integer , pointer :: gdd20_season_end_patch (:) ! gdd20 season end date for this patch (day of year) [patch] real(r8), pointer :: sdates_thisyr_patch (:,:) ! all actual sowing dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: swindow_starts_thisyr_patch(:,:) ! all sowing window start dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: swindow_ends_thisyr_patch (:,:) ! all sowing window end dates for this patch this year (day of year) [patch, mxsowings] @@ -237,6 +239,8 @@ subroutine InitAllocate(this, bounds) allocate(this%rx_swindow_ends_thisyr_patch(begp:endp,1:mxsowings)) ; this%rx_swindow_ends_thisyr_patch (:,:) = -1 allocate(this%rx_cultivar_gdds_thisyr_patch(begp:endp,1:mxsowings)) ; this%rx_cultivar_gdds_thisyr_patch(:,:) = spval allocate(this%gdd20_baseline_patch(begp:endp)) ; this%gdd20_baseline_patch(:) = spval + allocate(this%gdd20_season_start_patch(begp:endp)); this%gdd20_season_start_patch(:) = -1 + allocate(this%gdd20_season_end_patch(begp:endp)) ; this%gdd20_season_end_patch (:) = -1 allocate(this%sdates_thisyr_patch(begp:endp,1:mxsowings)) ; this%sdates_thisyr_patch(:,:) = spval allocate(this%swindow_starts_thisyr_patch(begp:endp,1:mxsowings)) ; this%swindow_starts_thisyr_patch(:,:) = spval allocate(this%swindow_ends_thisyr_patch (begp:endp,1:mxsowings)) ; this%swindow_ends_thisyr_patch (:,:) = spval diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 1b28b2e1aa..31fba16274 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -1358,11 +1358,13 @@ subroutine InitAccVars(this, bounds) end subroutine InitAccVars - subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, dtime, nstep, basetemp_int, gddx_patch) + subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, dtime, nstep, basetemp_int, gddx_patch, crop_inst) ! ! USES use shr_const_mod , only : SHR_CONST_CDAY, SHR_CONST_TKFRZ use accumulMod , only : update_accum_field, extract_accum_field, accumResetVal + use clm_time_manager , only : is_doy_in_interval + use CropType, only : crop_type ! ! !ARGUMENTS class(temperature_type) :: this @@ -1371,13 +1373,22 @@ subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, d integer, intent(in) :: month, day, secs, dtime, nstep integer, intent(in) :: basetemp_int ! Crop base temperature. Integer to avoid possible float weirdness real(r8), intent(inout), pointer, dimension(:) :: gddx_patch ! E.g., gdd0_patch + type(crop_type), intent(inout) :: crop_inst ! ! !LOCAL VARIABLES real(r8) :: basetemp_r8 ! real(r8) version of basetemp for arithmetic real(r8) :: max_accum ! Maximum daily accumulation character(8) :: field_name ! E.g., GDD0 character(32) :: format_string - integer :: p, g + integer :: p + logical :: in_accumulation_season + real(r8) :: lat ! latitude + integer :: gdd20_season_start, gdd20_season_end + + associate( & + gdd20_season_starts => crop_inst%gdd20_season_start_patch, & + gdd20_season_ends => crop_inst%gdd20_season_end_patch & + ) basetemp_r8 = real(basetemp_int, r8) @@ -1396,15 +1407,28 @@ subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, d cycle end if - g = patch%gridcell(p) + ! Is this patch in its gdd20 accumulation season? + ! First, check based on latitude. This will be fallback if read-in gdd20 accumulation season is invalid. + lat = grc%latdeg(patch%gridcell(p)) + in_accumulation_season = & + ((month > 3 .and. month < 10) .and. lat >= 0._r8) .or. & + ((month > 9 .or. month < 4) .and. lat < 0._r8) + ! Replace with read-in gdd20 accumulation season, if valid + ! (If these aren't being read in or they're invalid, they'll be -1) + gdd20_season_start = crop_inst%gdd20_season_start_patch(p) + gdd20_season_end = crop_inst%gdd20_season_end_patch(p) + if (gdd20_season_start >= 1 .and. gdd20_season_end >= 1) then + in_accumulation_season = is_doy_in_interval( & + gdd20_season_starts(p), gdd20_season_ends(p), day) + end if + if (month==1 .and. day==1 .and. secs==dtime) then rbufslp(p) = accumResetVal ! reset gdd - else if (( month > 3 .and. month < 10 .and. grc%latdeg(g) >= 0._r8) .or. & - ((month > 9 .or. month < 4) .and. grc%latdeg(g) < 0._r8) ) then + else if (in_accumulation_season) then rbufslp(p) = max(0._r8, min(max_accum, & this%t_ref2m_patch(p)-(SHR_CONST_TKFRZ + basetemp_r8))) * dtime/SHR_CONST_CDAY else - rbufslp(p) = 0._r8 ! keeps gdd unchanged at other times (eg, through Dec in NH) + rbufslp(p) = 0._r8 ! keeps gdd unchanged outside accumulation season end if end do @@ -1421,24 +1445,28 @@ subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, d ! Save call update_accum_field (trim(field_name), rbufslp, nstep) call extract_accum_field (trim(field_name), gddx_patch, nstep) + + end associate end subroutine UpdateAccVars_CropGDDs !----------------------------------------------------------------------- - subroutine UpdateAccVars (this, bounds) + subroutine UpdateAccVars (this, bounds, crop_inst) ! ! USES - use shr_const_mod , only : SHR_CONST_CDAY, SHR_CONST_TKFRZ + use shr_const_mod , only : SHR_CONST_TKFRZ use clm_time_manager , only : get_step_size, get_nstep, is_end_curr_day, get_curr_date, is_end_curr_year - use accumulMod , only : update_accum_field, extract_accum_field, accumResetVal + use accumulMod , only : update_accum_field, extract_accum_field use CNSharedParamsMod, only : upper_soil_layer + use CropType , only : crop_type ! ! !ARGUMENTS: class(temperature_type) :: this type(bounds_type) , intent(in) :: bounds + type(crop_type), intent(inout) :: crop_inst ! ! !LOCAL VARIABLES: - integer :: m,g,l,c,p ! indices + integer :: m,l,c,p ! indices integer :: ier ! error status integer :: dtime ! timestep size [seconds] integer :: nstep ! timestep number @@ -1604,13 +1632,13 @@ subroutine UpdateAccVars (this, bounds) ! Accumulate and extract GDD0 - call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 0, this%gdd0_patch) + call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 0, this%gdd0_patch, crop_inst) ! Accumulate and extract GDD8 - call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 8, this%gdd8_patch) + call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 8, this%gdd8_patch, crop_inst) ! Accumulate and extract GDD10 - call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 10, this%gdd10_patch) + call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 10, this%gdd10_patch, crop_inst) ! Accumulate and extract running 20-year means diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 413ddbefce..8196ca1dfb 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -38,9 +38,12 @@ module cropcalStreamMod type(shr_strdata_type) :: sdat_cropcal_swindow_end ! sowing window end input data stream type(shr_strdata_type) :: sdat_cropcal_cultivar_gdds ! maturity requirement input data stream type(shr_strdata_type) :: sdat_cropcal_gdd20_baseline ! GDD20 baseline input data stream + type(shr_strdata_type) :: sdat_cropcal_gdd20_season_start ! gdd20 season start input data stream + type(shr_strdata_type) :: sdat_cropcal_gdd20_season_end ! gdd20 season end input data stream character(len=CS), allocatable :: stream_varnames_sdate(:) ! used for both start and end dates character(len=CS), allocatable :: stream_varnames_cultivar_gdds(:) character(len=CS), allocatable :: stream_varnames_gdd20_baseline(:) + character(len=CS), allocatable :: stream_varnames_gdd20_season_enddate(:) ! start uses stream_varnames_sdate integer :: ncft ! Number of crop functional types (excl. generic crops) logical :: allow_invalid_swindow_inputs ! Fall back on paramfile sowing windows in cases of invalid values in stream_fldFileName_swindow_start and _end? character(len=CL) :: stream_fldFileName_swindow_start ! sowing window start stream filename to read @@ -49,6 +52,10 @@ module cropcalStreamMod character(len=CL) :: stream_fldFileName_gdd20_baseline ! GDD20 baseline stream filename to read logical :: cropcals_rx ! Used only for setting input files in namelist; does nothing in code, but needs to be here so namelist read doesn't crash logical :: cropcals_rx_adapt ! Used only for setting input files in namelist; does nothing in code, but needs to be here so namelist read doesn't crash + logical :: stream_gdd20_seasons ! Read stream file for GDD20 accumulation seasons, instead of per-hemisphere periods + logical :: allow_invalid_gdd20_season_inputs ! Fall back on hemisphere "warm periods" in cases of invalid values in stream_fldFileName_gdd20_season_start and _end? + character(len=CL) :: stream_fldFileName_gdd20_season_start ! Stream filename to read for start of gdd20 season + character(len=CL) :: stream_fldFileName_gdd20_season_end ! Stream filename to read for end of gdd20 season character(len=*), parameter :: sourcefile = & __FILE__ @@ -105,7 +112,11 @@ subroutine cropcal_init(bounds) stream_fldFileName_gdd20_baseline, & stream_meshfile_cropcal, & cropcals_rx, & - cropcals_rx_adapt + cropcals_rx_adapt, & + stream_gdd20_seasons, & + allow_invalid_gdd20_season_inputs, & + stream_fldFileName_gdd20_season_start, & + stream_fldFileName_gdd20_season_end ! Default values for namelist stream_year_first_cropcal_swindows = 1 ! first year in sowing window streams to use @@ -120,16 +131,22 @@ subroutine cropcal_init(bounds) stream_fldFileName_swindow_end = '' stream_fldFileName_cultivar_gdds = '' stream_fldFileName_gdd20_baseline = '' + stream_gdd20_seasons = .false. + allow_invalid_gdd20_season_inputs = .false. + stream_fldFileName_gdd20_season_start = '' + stream_fldFileName_gdd20_season_end = '' ! Will need modification to work with mxsowings > 1 ncft = mxpft - npcropmin + 1 ! Ignores generic crops allocate(stream_varnames_sdate(ncft)) allocate(stream_varnames_cultivar_gdds(ncft)) allocate(stream_varnames_gdd20_baseline(ncft)) + allocate(stream_varnames_gdd20_season_enddate(ncft)) do n = 1,ncft ivt = npcropmin + n - 1 write(stream_varnames_sdate(n),'(a,i0)') "sdate1_",ivt write(stream_varnames_cultivar_gdds(n),'(a,i0)') "gdd1_",ivt write(stream_varnames_gdd20_baseline(n),'(a,i0)') "gdd20bl_",ivt + write(stream_varnames_gdd20_season_enddate(n),'(a,i0)') "hdate1_",ivt end do ! Read cropcal_streams namelist @@ -158,6 +175,10 @@ subroutine cropcal_init(bounds) call shr_mpi_bcast(stream_fldFileName_cultivar_gdds, mpicom) call shr_mpi_bcast(stream_fldFileName_gdd20_baseline, mpicom) call shr_mpi_bcast(stream_meshfile_cropcal , mpicom) + call shr_mpi_bcast(stream_gdd20_seasons, mpicom) + call shr_mpi_bcast(allow_invalid_gdd20_season_inputs, mpicom) + call shr_mpi_bcast(stream_fldFileName_gdd20_season_start, mpicom) + call shr_mpi_bcast(stream_fldFileName_gdd20_season_end, mpicom) if (masterproc) then write(iulog,*) @@ -174,9 +195,14 @@ subroutine cropcal_init(bounds) write(iulog,'(a,a)' ) ' stream_fldFileName_cultivar_gdds = ',trim(stream_fldFileName_cultivar_gdds) write(iulog,'(a,a)' ) ' stream_fldFileName_gdd20_baseline = ',trim(stream_fldFileName_gdd20_baseline) write(iulog,'(a,a)' ) ' stream_meshfile_cropcal = ',trim(stream_meshfile_cropcal) + write(iulog,'(a,l1)') ' stream_gdd20_seasons = ',stream_gdd20_seasons + write(iulog,'(a,l1)') ' allow_invalid_gdd20_season_inputs = ',allow_invalid_gdd20_season_inputs + write(iulog,'(a,a)' ) ' stream_fldFileName_gdd20_season_start = ',stream_fldFileName_gdd20_season_start + write(iulog,'(a,a)' ) ' stream_fldFileName_gdd20_season_end = ',stream_fldFileName_gdd20_season_end do n = 1,ncft write(iulog,'(a,a)' ) ' stream_varnames_sdate = ',trim(stream_varnames_sdate(n)) write(iulog,'(a,a)' ) ' stream_varnames_cultivar_gdds = ',trim(stream_varnames_cultivar_gdds(n)) + write(iulog,'(a,a)' ) ' stream_varnames_gdd20_season_enddate = ',trim(stream_varnames_gdd20_season_enddate(n)) write(iulog,'(a,a)' ) ' stream_varnames_gdd20_baseline = ',trim(stream_varnames_gdd20_baseline(n)) end do write(iulog,*) @@ -304,6 +330,65 @@ subroutine cropcal_init(bounds) end if end if + if (stream_gdd20_seasons) then + ! Initialize the cdeps data type sdat_cropcal_gdd20_season_start + ! NOTE: Hard-coded to one particular year because it should NOT vary over time. Note that the + ! particular year chosen doesn't matter. + call shr_strdata_init_from_inline(sdat_cropcal_gdd20_season_start, & + my_task = iam, & + logunit = iulog, & + compname = 'LND', & + model_clock = model_clock, & + model_mesh = mesh, & + stream_meshfile = trim(stream_meshfile_cropcal), & + stream_lev_dimname = 'null', & + stream_mapalgo = trim(cropcal_mapalgo), & + stream_filenames = (/trim(stream_fldFileName_gdd20_season_start)/), & + stream_fldlistFile = stream_varnames_sdate, & + stream_fldListModel = stream_varnames_sdate, & + stream_yearFirst = 2000, & + stream_yearLast = 2000, & + stream_yearAlign = 2000, & + stream_offset = cropcal_offset, & + stream_taxmode = 'extend', & + stream_dtlimit = 1.0e30_r8, & + stream_tintalgo = cropcal_tintalgo, & + stream_name = 'gdd20 season start data', & + rc = rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + ! Initialize the cdeps data type sdat_cropcal_gdd20_season_end + ! NOTE: Hard-coded to one particular year because it should NOT vary over time. Note that the + ! particular year chosen doesn't matter. + call shr_strdata_init_from_inline(sdat_cropcal_gdd20_season_end, & + my_task = iam, & + logunit = iulog, & + compname = 'LND', & + model_clock = model_clock, & + model_mesh = mesh, & + stream_meshfile = trim(stream_meshfile_cropcal), & + stream_lev_dimname = 'null', & + stream_mapalgo = trim(cropcal_mapalgo), & + stream_filenames = (/trim(stream_fldFileName_gdd20_season_end)/), & + stream_fldlistFile = stream_varnames_gdd20_season_enddate, & + stream_fldListModel = stream_varnames_gdd20_season_enddate, & + stream_yearFirst = 2000, & + stream_yearLast = 2000, & + stream_yearAlign = 2000, & + stream_offset = cropcal_offset, & + stream_taxmode = 'extend', & + stream_dtlimit = 1.0e30_r8, & + stream_tintalgo = cropcal_tintalgo, & + stream_name = 'gdd20 season start data', & + rc = rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + end if + end subroutine cropcal_init !================================================================ @@ -347,7 +432,10 @@ subroutine cropcal_advance( bounds ) end if end if - ! GDD20 baseline values do not have an associated time axis and thus will not be advanced here + ! The following should not have an associated time axis and thus will not be advanced here: + ! - GDD20 baseline values + ! - GDD20 season start dates + ! - GDD20 season end dates if ( .not. allocated(g_to_ig) )then allocate (g_to_ig(bounds%begg:bounds%endg) ) @@ -393,15 +481,21 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) real(r8), pointer :: dataptr1d_swindow_end (:) real(r8), pointer :: dataptr1d_cultivar_gdds(:) real(r8), pointer :: dataptr1d_gdd20_baseline(:) + real(r8), pointer :: dataptr1d_gdd20_season_start(:) + real(r8), pointer :: dataptr1d_gdd20_season_end (:) real(r8), pointer :: dataptr2d_swindow_start(:,:) real(r8), pointer :: dataptr2d_swindow_end (:,:) real(r8), pointer :: dataptr2d_cultivar_gdds(:,:) real(r8), pointer :: dataptr2d_gdd20_baseline(:,:) + real(r8), pointer :: dataptr2d_gdd20_season_start(:,:) + real(r8), pointer :: dataptr2d_gdd20_season_end (:,:) !----------------------------------------------------------------------- associate( & - starts => crop_inst%rx_swindow_starts_thisyr_patch, & - ends => crop_inst%rx_swindow_ends_thisyr_patch & + swindow_starts => crop_inst%rx_swindow_starts_thisyr_patch, & + swindow_ends => crop_inst%rx_swindow_ends_thisyr_patch, & + gdd20_season_starts => crop_inst%gdd20_season_start_patch, & + gdd20_season_ends => crop_inst%gdd20_season_end_patch & ) SHR_ASSERT_FL( (lbound(g_to_ig,1) <= bounds%begg ), sourcefile, __LINE__) @@ -459,8 +553,8 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) n = ivt - npcropmin + 1 ! vegetated pft ig = g_to_ig(patch%gridcell(p)) - starts(p,1) = dataptr2d_swindow_start(ig,n) - ends(p,1) = dataptr2d_swindow_end (ig,n) + swindow_starts(p,1) = dataptr2d_swindow_start(ig,n) + swindow_ends(p,1) = dataptr2d_swindow_end (ig,n) else write(iulog,'(a,i0)') 'cropcal_interp(), prescribed sowing windows: Crop patch has ivt ',ivt call ESMF_Finalize(endflag=ESMF_END_ABORT) @@ -469,23 +563,23 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Ensure that, if mxsowings > 1, sowing windows are ordered such that ENDS are monotonically increasing. This is necessary because of how get_swindow() works. if (mxsowings > 1) then - if (any(ends(begp:endp,2:mxsowings) <= ends(begp:endp,1:mxsowings-1) .and. & - ends(begp:endp,2:mxsowings) >= 1)) then + if (any(swindow_ends(begp:endp,2:mxsowings) <= swindow_ends(begp:endp,1:mxsowings-1) .and. & + swindow_ends(begp:endp,2:mxsowings) >= 1)) then write(iulog, *) 'Sowing window inputs must be ordered such that end dates are monotonically increasing.' call ESMF_Finalize(endflag=ESMF_END_ABORT) end if end if ! Handle invalid sowing window values - if (any(starts(begp:endp,:) < 1 .or. ends(begp:endp,:) < 1)) then + if (any(swindow_starts(begp:endp,:) < 1 .or. swindow_ends(begp:endp,:) < 1)) then ! Fail if not allowing fallback to paramfile sowing windows - if ((.not. allow_invalid_swindow_inputs) .and. any(all(starts(begp:endp,:) < 1, dim=2) .and. patch%wtgcell > 0._r8 .and. patch%itype >= npcropmin)) then + if ((.not. allow_invalid_swindow_inputs) .and. any(all(swindow_starts(begp:endp,:) < 1, dim=2) .and. patch%wtgcell > 0._r8 .and. patch%itype >= npcropmin)) then write(iulog, *) 'At least one crop in one gridcell has invalid prescribed sowing window start date(s). To ignore and fall back to paramfile sowing windows, set allow_invalid_swindow_inputs to .true.' write(iulog, *) 'Affected crops:' do ivt = npcropmin, mxpft do fp = 1, num_pcropp p = filter_pcropp(fp) - if (ivt == patch%itype(p) .and. patch%wtgcell(p) > 0._r8 .and. all(starts(p,:) < 1)) then + if (ivt == patch%itype(p) .and. patch%wtgcell(p) > 0._r8 .and. all(swindow_starts(p,:) < 1)) then write(iulog, *) ' ',pftname(ivt),' (',ivt,')' exit ! Stop looking for patches of this type end if @@ -494,7 +588,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) call ESMF_Finalize(endflag=ESMF_END_ABORT) ! Fail if a sowing window start date is prescribed without an end date (or vice versa) - else if (any((starts(begp:endp,:) >= 1 .and. ends(begp:endp,:) < 1) .or. (starts(begp:endp,:) < 1 .and. ends(begp:endp,:) >= 1))) then + else if (any((swindow_starts(begp:endp,:) >= 1 .and. swindow_ends(begp:endp,:) < 1) .or. (swindow_starts(begp:endp,:) < 1 .and. swindow_ends(begp:endp,:) >= 1))) then write(iulog, *) 'Every prescribed sowing window start date must have a corresponding end date.' call ESMF_Finalize(endflag=ESMF_END_ABORT) end if @@ -613,6 +707,86 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) deallocate(dataptr2d_gdd20_baseline) + ! Read prescribed gdd20 season start dates from input files + allocate(dataptr2d_gdd20_season_start(lsize, ncft)) + dataptr2d_gdd20_season_start(:,:) = -1._r8 + allocate(dataptr2d_gdd20_season_end (lsize, ncft)) + dataptr2d_gdd20_season_end(:,:) = -1._r8 + if (stream_gdd20_seasons .and. init) then + ! Starting with npcropmin will skip generic crops + do n = 1, ncft + call dshr_fldbun_getFldPtr(sdat_cropcal_gdd20_season_start%pstrm(1)%fldbun_model, trim(stream_varnames_sdate(n)), & + fldptr1=dataptr1d_gdd20_season_start, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + call dshr_fldbun_getFldPtr(sdat_cropcal_gdd20_season_end%pstrm(1)%fldbun_model, trim(stream_varnames_gdd20_season_enddate(n)), & + fldptr1=dataptr1d_gdd20_season_end, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize + ! So an explicit loop is required here + do g = 1,lsize + + ! If read-in value is invalid, set to -1. Will be handled later in this subroutine. + if (dataptr1d_gdd20_season_start(g) <= 0 .or. dataptr1d_gdd20_season_start(g) > 366 & + .or. dataptr1d_gdd20_season_end(g) <= 0 .or. dataptr1d_gdd20_season_end(g) > 366) then + dataptr1d_gdd20_season_start(g) = -1 + dataptr1d_gdd20_season_end (g) = -1 + end if + + dataptr2d_gdd20_season_start(g,n) = dataptr1d_gdd20_season_start(g) + dataptr2d_gdd20_season_end (g,n) = dataptr1d_gdd20_season_end (g) + end do + end do + + ! Set gdd20 season for each gridcell/patch combination + do fp = 1, num_pcropp + p = filter_pcropp(fp) + ivt = patch%itype(p) + ! Will skip generic crops + if (ivt >= npcropmin) then + n = ivt - npcropmin + 1 + ! vegetated pft + ig = g_to_ig(patch%gridcell(p)) + gdd20_season_starts(p) = dataptr2d_gdd20_season_start(ig,n) + gdd20_season_ends(p) = dataptr2d_gdd20_season_end (ig,n) + else + write(iulog,'(a,i0)') 'cropcal_interp(), gdd20 seasons: Crop patch has ivt ',ivt + call ESMF_Finalize(endflag=ESMF_END_ABORT) + endif + end do + + ! Handle invalid gdd20 season values + if (any(gdd20_season_starts(begp:endp) < 1 .or. gdd20_season_ends(begp:endp) < 1)) then + ! Fail if not allowing fallback to paramfile sowing windows + if ((.not. allow_invalid_gdd20_season_inputs) .and. any(gdd20_season_starts(begp:endp) < 1 .and. patch%wtgcell(begp:endp) > 0._r8 .and. patch%itype(begp:endp) >= npcropmin)) then + write(iulog, *) 'At least one crop in one gridcell has invalid gdd20 season start date(s). To ignore and fall back to paramfile sowing windows, set allow_invalid_gdd20_season_inputs to .true.' + write(iulog, *) 'Affected crops:' + do ivt = npcropmin, mxpft + do fp = 1, num_pcropp + p = filter_pcropp(fp) + if (ivt == patch%itype(p) .and. patch%wtgcell(p) > 0._r8 .and. gdd20_season_starts(p) < 1) then + write(iulog, *) ' ',pftname(ivt),' (',ivt,')' + exit ! Stop looking for patches of this type + end if + end do + end do + call ESMF_Finalize(endflag=ESMF_END_ABORT) + + ! Fail if a gdd20 season start date is given without an end date (or vice versa) + else if (any((gdd20_season_starts(begp:endp) >= 1 .and. gdd20_season_ends(begp:endp) < 1) .or. (gdd20_season_starts(begp:endp) < 1 .and. gdd20_season_ends(begp:endp) >= 1))) then + write(iulog, *) 'Every gdd20 season start date must have a corresponding end date.' + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + end if + + end if ! stream_gdd20_seasons and init + deallocate(dataptr2d_gdd20_season_start) + deallocate(dataptr2d_gdd20_season_end) + + end associate end subroutine cropcal_interp diff --git a/src/main/clm_driver.F90 b/src/main/clm_driver.F90 index 2a3cef4b8b..e660ab9d8d 100644 --- a/src/main/clm_driver.F90 +++ b/src/main/clm_driver.F90 @@ -1374,7 +1374,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro call atm2lnd_inst%UpdateAccVars(bounds_proc) - call temperature_inst%UpdateAccVars(bounds_proc) + call temperature_inst%UpdateAccVars(bounds_proc, crop_inst) call canopystate_inst%UpdateAccVars(bounds_proc) From 0feff1f3d641ec47c616d41d8c6fe3e4b9208db1 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 31 May 2024 18:26:51 -0600 Subject: [PATCH 112/406] Fix check of invalid swindow inputs. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 8196ca1dfb..4c4925f88e 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -573,7 +573,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Handle invalid sowing window values if (any(swindow_starts(begp:endp,:) < 1 .or. swindow_ends(begp:endp,:) < 1)) then ! Fail if not allowing fallback to paramfile sowing windows - if ((.not. allow_invalid_swindow_inputs) .and. any(all(swindow_starts(begp:endp,:) < 1, dim=2) .and. patch%wtgcell > 0._r8 .and. patch%itype >= npcropmin)) then + if ((.not. allow_invalid_swindow_inputs) .and. any(all(swindow_starts(begp:endp,:) < 1, dim=2) .and. patch%wtgcell(begp:endp) > 0._r8 .and. patch%itype(begp:endp) >= npcropmin)) then write(iulog, *) 'At least one crop in one gridcell has invalid prescribed sowing window start date(s). To ignore and fall back to paramfile sowing windows, set allow_invalid_swindow_inputs to .true.' write(iulog, *) 'Affected crops:' do ivt = npcropmin, mxpft From 3729b6db9f2139399618e39c4c48084144eecf12 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 31 May 2024 22:35:35 -0600 Subject: [PATCH 113/406] Add RxCropCalsAdaptGGCMI testmod and test. --- cime_config/testdefs/testlist_clm.xml | 9 +++++++++ .../clm/RxCropCalsAdaptGGCMI/include_user_mods | 1 + .../testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm | 2 ++ 3 files changed, 12 insertions(+) create mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods create mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index bf763c4775..edb932d24e 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3737,5 +3737,14 @@ + + + + + + + + + diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods new file mode 100644 index 0000000000..af5fe8591e --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods @@ -0,0 +1 @@ +../RxCropCalsAdapt diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm new file mode 100644 index 0000000000..fa0e4ec663 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm @@ -0,0 +1,2 @@ + +stream_gdd20_seasons = .true. From d020eada257e99055b2b90600f78c630286492c1 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 09:10:40 -0600 Subject: [PATCH 114/406] Fix check that gdd20 season files are provided. --- bld/CLMBuildNamelist.pm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index dddd17ced9..d5c2ffd54f 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4188,14 +4188,17 @@ sub setup_logic_cropcal_streams { # Add defaults if reading gdd20 seasons from stream files my $stream_gdd20_seasons = $nl->get_value('stream_gdd20_seasons') ; - my $gdd20_season_start_file = $nl->get_value('stream_fldFileName_gdd20_season_start') ; - my $gdd20_season_end_file = $nl->get_value('stream_fldFileName_gdd20_season_end') ; if ( &value_is_true($stream_gdd20_seasons)) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_gdd20_season_start'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_gdd20_season_end'); # Check + my $gdd20_season_start_file = $nl->get_value('stream_fldFileName_gdd20_season_start') ; + my $gdd20_season_end_file = $nl->get_value('stream_fldFileName_gdd20_season_end') ; if ( &string_is_undef_or_empty($gdd20_season_start_file) or &string_is_undef_or_empty($gdd20_season_end_file) ) { + $log->message($gdd20_season_start_file); + $log->message('abcd'); + $log->message($gdd20_season_end_file); $log->fatal_error("If stream_gdd20_seasons is true, gdd20 season start and end files must be provided." ); } } From 42b0dbb9f7e02ab2138710904af129704867e466 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 09:11:09 -0600 Subject: [PATCH 115/406] Minor rearrangement in cropcal namelist definition. --- bld/namelist_files/namelist_definition_ctsm.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 036b9aca72..5b6dcef87e 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1838,16 +1838,16 @@ Filename of input stream data for cultivar growing degree-day targets Filename of input stream data for baseline GDD20 values - -Filename of input stream data for date (day of year) of start of gdd20 accumulation season. - - Set this to true to read gdd20 accumulation season start and end dates from stream files, rather than using hard-coded hemisphere-specific "warm seasons." + +Filename of input stream data for date (day of year) of start of gdd20 accumulation season. + + Filename of input stream data for date (day of year) of end of gdd20 accumulation season. From 92555b9c394a4542ff7412335a459dfab57a8979 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 12:13:20 -0600 Subject: [PATCH 116/406] generate_gdd20_baseline.py now saves NETCDF3_CLASSIC. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index d28bfda0e7..7b5fd625d6 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -226,7 +226,7 @@ def generate_gdd20_baseline(input_files, output_file, author): ds_out[var_out] = this_da # Save - ds_out.to_netcdf(output_file) + ds_out.to_netcdf(output_file, format="NETCDF3_CLASSIC") print("Done!") From 30eb31ce8e92a05bd104268e01e3d62b964ee486 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 12:16:49 -0600 Subject: [PATCH 117/406] Update default to point to classic version. --- bld/namelist_files/namelist_defaults_ctsm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 7e3ddb7c64..2683c2e535 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1705,7 +1705,7 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc -lnd/clm2/testdata/gdd20baseline.tmp_dontupload.nc +lnd/clm2/testdata/gdd20baseline.tmp_dontupload.classic.nc lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc lnd/clm2/cropdata/calendars/processed/hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc From a6e2c1e23964b6e9c3bf35d1778a14187a0b62e9 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 12:59:29 -0600 Subject: [PATCH 118/406] Add allow_invalid_gdd20_season_inputs to namelist XML. --- bld/namelist_files/namelist_definition_ctsm.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 5b6dcef87e..675c912900 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1843,6 +1843,11 @@ Filename of input stream data for baseline GDD20 values Set this to true to read gdd20 accumulation season start and end dates from stream files, rather than using hard-coded hemisphere-specific "warm seasons." + +By default, a value in stream_fldFileName_gdd20_season_start or _end outside the range [1, 365] (or 366 in leap years) will cause the run to fail. Set this to .true. to instead have such cells fall back to the hard-coded hemisphere-specific "warm seasons." + + Filename of input stream data for date (day of year) of start of gdd20 accumulation season. From 9f04d71e0ac7f5e4f0bcadd195281dfcc2134f81 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 13:22:59 -0600 Subject: [PATCH 119/406] Update missing values from generate_gdd20_baseline.py. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 7b5fd625d6..fd56d61550 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -20,7 +20,8 @@ VAR_LIST_IN = ["GDD0", "GDD8", "GDD10"] VAR_LIST_IN = [x + "20" for x in VAR_LIST_IN] # TODO: Delete this once using the right variables -MISSING_FILL = -1 # Something negative to ensure that gddmaturity never changes (see PlantCrop) +MISSING_FILL = -1 # Something impossible to ensure that you can mark it as a missing value, to be +# bilinear-interpolated STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of # shr_strdata_init_from_inline() for sdat_cropcal_gdd20_baseline @@ -211,7 +212,8 @@ def generate_gdd20_baseline(input_files, output_file, author): long_name = "Dummy GDD20" print(" dummy GDD20") else: - this_da = ds_in[gddn].fillna(MISSING_FILL) + # this_da = ds_in[gddn].fillna(MISSING_FILL) + this_da = ds_in[gddn] this_da = _add_time_axis(this_da) long_name = gddn print(f" {gddn}") @@ -219,6 +221,7 @@ def generate_gdd20_baseline(input_files, output_file, author): # Add attributes this_da.attrs["long_name"] = long_name + f" baseline for {cft_str}" this_da.attrs["units"] = "°C days" + # this_da.attrs["_FillValue"] = MISSING_FILL # Copy that to ds_out var_out = _get_output_varname(cft_str) From 625fd5e7b9eebc61bf8e93824e92a3079bf937cd Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 13:23:16 -0600 Subject: [PATCH 120/406] RxCropCalsAdaptGGCMI now uses allow_invalid_gdd20_season_inputs. --- .../testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm index fa0e4ec663..42e57a675c 100644 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm @@ -1,2 +1,4 @@ stream_gdd20_seasons = .true. +!TODO SSR: Try without this once you have half-degree inputs +allow_invalid_gdd20_season_inputs = .true. From 0156d1aa928f5bc002c1eb8508962138c76f4a1d Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 13:25:02 -0600 Subject: [PATCH 121/406] Move hist_addfld1d() for GDD0 to be with its siblings. --- src/biogeophys/TemperatureType.F90 | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 31fba16274..89dfd11074 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -597,9 +597,7 @@ subroutine InitHistory(this, bounds, is_simple_buildtemp, is_prog_buildtemp ) call hist_addfld1d (fname='GDD0', units='ddays', & avgflag='A', long_name='Growing degree days base 0C from planting', & ptr_patch=this%gdd0_patch, default='inactive') - end if - if (use_crop) then this%gdd8_patch(begp:endp) = spval call hist_addfld1d (fname='GDD8', units='ddays', & avgflag='A', long_name='Growing degree days base 8C from planting', & From b7c34ffcd7b4421f5266051d3bb7aeef2f790744 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 13:34:16 -0600 Subject: [PATCH 122/406] Add max versions of GDD season accum outputs. --- src/biogeophys/TemperatureType.F90 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 89dfd11074..f8d5206a1e 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -608,6 +608,21 @@ subroutine InitHistory(this, bounds, is_simple_buildtemp, is_prog_buildtemp ) avgflag='A', long_name='Growing degree days base 10C from planting', & ptr_patch=this%gdd10_patch, default='inactive') + this%gdd0_patch(begp:endp) = spval + call hist_addfld1d (fname='GDD0X', units='ddays', & + avgflag='X', long_name='Growing degree days base 0C from planting, max', & + ptr_patch=this%gdd0_patch, default='inactive') + + this%gdd8_patch(begp:endp) = spval + call hist_addfld1d (fname='GDD8X', units='ddays', & + avgflag='X', long_name='Growing degree days base 8C from planting, max', & + ptr_patch=this%gdd8_patch, default='inactive') + + this%gdd10_patch(begp:endp) = spval + call hist_addfld1d (fname='GDD10X', units='ddays', & + avgflag='X', long_name='Growing degree days base 10C from planting, max', & + ptr_patch=this%gdd10_patch, default='inactive') + this%gdd020_patch(begp:endp) = spval call hist_addfld1d (fname='GDD020', units='ddays', & avgflag='A', long_name='Twenty year average of growing degree days base 0C from planting', & From 535e2c5a43f309ea3bbe8eb851c963ee7cb33dcf Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 17:01:44 -0600 Subject: [PATCH 123/406] Even gdd20 files need to be advance()d. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 4c4925f88e..faa13b5fda 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -432,10 +432,26 @@ subroutine cropcal_advance( bounds ) end if end if - ! The following should not have an associated time axis and thus will not be advanced here: + ! The following should not have an associated time axis, but still need to be here ! - GDD20 baseline values ! - GDD20 season start dates ! - GDD20 season end dates + if (adapt_cropcal_rx_cultivar_gdds) then + call shr_strdata_advance(sdat_cropcal_gdd20_baseline, ymd=mcdate, tod=sec, logunit=iulog, istr='cropcaldyn', rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + end if + if (stream_gdd20_seasons) then + call shr_strdata_advance(sdat_cropcal_gdd20_season_start, ymd=mcdate, tod=sec, logunit=iulog, istr='cropcaldyn', rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + call shr_strdata_advance(sdat_cropcal_gdd20_season_end, ymd=mcdate, tod=sec, logunit=iulog, istr='cropcaldyn', rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + end if if ( .not. allocated(g_to_ig) )then allocate (g_to_ig(bounds%begg:bounds%endg) ) From 3e524db1aa4f34a70cd08c48c740c8c35033db9d Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 17:03:43 -0600 Subject: [PATCH 124/406] generate_gdd20_baseline.py now saves to double instead of float. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index fd56d61550..8cf30bf44d 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -197,6 +197,7 @@ def generate_gdd20_baseline(input_files, output_file, author): "created": dt.datetime.now().astimezone().isoformat(), }, ) + encoding_dict = {} for cft_str in utils.define_mgdcrop_list(): cft_int = utils.vegtype_str2int(cft_str)[0] print(f"{cft_str} ({cft_int})") @@ -227,9 +228,10 @@ def generate_gdd20_baseline(input_files, output_file, author): var_out = _get_output_varname(cft_str) print(f" Output variable {var_out}") ds_out[var_out] = this_da + encoding_dict[var_out] = {"dtype": "float64"} # Save - ds_out.to_netcdf(output_file, format="NETCDF3_CLASSIC") + ds_out.to_netcdf(output_file, format="NETCDF3_CLASSIC", encoding=encoding_dict) print("Done!") From 18df6b4a04ac901737ccb5d9b806619e66ebe532 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 17:06:19 -0600 Subject: [PATCH 125/406] stream_fldFileName_gdd20_baseline now defaults to half-deg fake file. --- bld/namelist_files/namelist_defaults_ctsm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 2683c2e535..bd5f7132ca 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1705,7 +1705,7 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc -lnd/clm2/testdata/gdd20baseline.tmp_dontupload.classic.nc +lnd/clm2/cropdata/calendars/processed/gdd20bl.copied_from.gdds_20230829_161011.nc lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc lnd/clm2/cropdata/calendars/processed/hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc From a3feed96de079cdc04f42e60a5cfc4ff22959606 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sun, 2 Jun 2024 08:25:06 -0600 Subject: [PATCH 126/406] Use fixed gdd20 baseline file. --- bld/namelist_files/namelist_defaults_ctsm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index bd5f7132ca..8ed75be01a 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1705,7 +1705,7 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc -lnd/clm2/cropdata/calendars/processed/gdd20bl.copied_from.gdds_20230829_161011.nc +lnd/clm2/cropdata/calendars/processed/gdd20bl.copied_from.gdds_20230829_161011.v2.nc lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc lnd/clm2/cropdata/calendars/processed/hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc From 5144b36a7993ae6363bd8bb16b41219ed3e032ed Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 10:58:43 -0600 Subject: [PATCH 127/406] gdd20 baseline now interpolated w/ nearest-neighbor. Revert this! --- src/cpl/share_esmf/cropcalStreamMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index faa13b5fda..ce595c0d70 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -312,7 +312,7 @@ subroutine cropcal_init(bounds) model_mesh = mesh, & stream_meshfile = trim(stream_meshfile_cropcal), & stream_lev_dimname = 'null', & - stream_mapalgo = 'bilinear', & + stream_mapalgo = 'nn', & stream_filenames = (/trim(stream_fldFileName_gdd20_baseline)/), & stream_fldlistFile = stream_varnames_gdd20_baseline, & stream_fldListModel = stream_varnames_gdd20_baseline, & From 90b938b5bbf8bc3e13d86e1211bb9519409b477d Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 10:59:37 -0600 Subject: [PATCH 128/406] generate_gdd20_nbaseline: Use GDDNX. --- .../ctsm/crop_calendars/generate_gdd20_baseline.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 8cf30bf44d..7f60fb6a68 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -18,8 +18,7 @@ from ctsm.crop_calendars.import_ds import import_ds import ctsm.crop_calendars.cropcal_utils as utils -VAR_LIST_IN = ["GDD0", "GDD8", "GDD10"] -VAR_LIST_IN = [x + "20" for x in VAR_LIST_IN] # TODO: Delete this once using the right variables +VAR_LIST_IN = ["GDD0X", "GDD8X", "GDD10X"] MISSING_FILL = -1 # Something impossible to ensure that you can mark it as a missing value, to be # bilinear-interpolated STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of @@ -111,26 +110,25 @@ def _get_gddn_for_cft(cft_str): cft_str (str): E.g., "irrigated_temperate_corn" Returns: - str or None: Name of variable to use (e.g., "GDD8"). If crop isn't yet handled, return None. + str or None: Name of variable to use (e.g., "GDD8X"). If crop isn't yet handled, return None. """ gddn = None gdd0_list_str = ["wheat", "cotton", "rice"] if cft_str in _get_cft_list(gdd0_list_str): - gddn = "GDD0" + gddn = 0 gdd8_list_str = ["corn", "sugarcane", "miscanthus", "switchgrass"] if cft_str in _get_cft_list(gdd8_list_str): - gddn = "GDD8" + gddn = 8 gdd10_list_str = ["soybean"] if cft_str in _get_cft_list(gdd10_list_str): - gddn = "GDD10" + gddn = 10 - # TODO: Delete this once using the right variables if gddn is not None: - gddn += "20" + gddn = f"GDD{gddn}X" return gddn From e82679df56e1a514f28ee4a38ae4d9c9b581b120 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 11:25:37 -0600 Subject: [PATCH 129/406] generate_gdd20_baseline: Grid, if needed. --- .../ctsm/crop_calendars/generate_gdd20_baseline.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 7f60fb6a68..71125e332d 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -17,8 +17,10 @@ # pylint: disable=wrong-import-position from ctsm.crop_calendars.import_ds import import_ds import ctsm.crop_calendars.cropcal_utils as utils +from ctsm.crop_calendars.grid_one_variable import grid_one_variable VAR_LIST_IN = ["GDD0X", "GDD8X", "GDD10X"] +GRIDDING_VAR_LIST = ["patches1d_ixy", "patches1d_jxy", "lat", "lon"] MISSING_FILL = -1 # Something impossible to ensure that you can mark it as a missing value, to be # bilinear-interpolated STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of @@ -171,7 +173,7 @@ def generate_gdd20_baseline(input_files, output_file, author): input_files.sort() # Import history files and ensure they have lat/lon dims - ds_in = import_ds(input_files, VAR_LIST_IN) + ds_in = import_ds(input_files, VAR_LIST_IN + GRIDDING_VAR_LIST) if not all(x in ds_in.dims for x in ["lat", "lon"]): raise RuntimeError("Input files must have lat and lon dimensions") @@ -188,8 +190,11 @@ def generate_gdd20_baseline(input_files, output_file, author): dummy_da = _add_time_axis(dummy_da) # Process all crops + data_var_dict = {} + for v in GRIDDING_VAR_LIST: + data_var_dict[v] = ds_in[v] ds_out = xr.Dataset( - data_vars=None, + data_vars=data_var_dict, attrs={ "author": author, "created": dt.datetime.now().astimezone().isoformat(), @@ -228,6 +233,10 @@ def generate_gdd20_baseline(input_files, output_file, author): ds_out[var_out] = this_da encoding_dict[var_out] = {"dtype": "float64"} + # Grid, if needed + if any(x not in this_da.dims for x in ["lat", "lon"]): + ds_out[var_out] = grid_one_variable(ds_out, var_out) + # Save ds_out.to_netcdf(output_file, format="NETCDF3_CLASSIC", encoding=encoding_dict) From 8b43fec5168d56df1b7c5ac6e22635d70024c3b0 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 11:28:32 -0600 Subject: [PATCH 130/406] generate_gdd20_baseline: Improve long names. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 71125e332d..89a38a0a08 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -219,7 +219,7 @@ def generate_gdd20_baseline(input_files, output_file, author): # this_da = ds_in[gddn].fillna(MISSING_FILL) this_da = ds_in[gddn] this_da = _add_time_axis(this_da) - long_name = gddn + long_name = gddn.replace("X", "20") print(f" {gddn}") # Add attributes From dcacbd4003585edf1a8608bc9d70232285f01202 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 11:47:38 -0600 Subject: [PATCH 131/406] Minor cleanup in UpdateAccVars_CropGDDs. --- src/biogeophys/TemperatureType.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index f8d5206a1e..5ae0b7b20c 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -1428,11 +1428,11 @@ subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, d ((month > 9 .or. month < 4) .and. lat < 0._r8) ! Replace with read-in gdd20 accumulation season, if valid ! (If these aren't being read in or they're invalid, they'll be -1) - gdd20_season_start = crop_inst%gdd20_season_start_patch(p) - gdd20_season_end = crop_inst%gdd20_season_end_patch(p) + gdd20_season_start = gdd20_season_starts(p) + gdd20_season_end = gdd20_season_ends(p) if (gdd20_season_start >= 1 .and. gdd20_season_end >= 1) then in_accumulation_season = is_doy_in_interval( & - gdd20_season_starts(p), gdd20_season_ends(p), day) + gdd20_season_start, gdd20_season_end, day) end if if (month==1 .and. day==1 .and. secs==dtime) then From 2cf491da0127f1caeec5ae6d7152d637f2975599 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 11:53:44 -0600 Subject: [PATCH 132/406] Add outputs: GDD20_BASELINE, GDD20_SEASON_START/END. --- src/biogeochem/CropType.F90 | 28 +++++++++++++++++++++---- src/biogeophys/TemperatureType.F90 | 5 +++-- src/cpl/share_esmf/cropcalStreamMod.F90 | 18 ++++++++++------ 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/biogeochem/CropType.F90 b/src/biogeochem/CropType.F90 index 04806f9349..fdfdaa05aa 100644 --- a/src/biogeochem/CropType.F90 +++ b/src/biogeochem/CropType.F90 @@ -53,8 +53,11 @@ module CropType integer , pointer :: rx_swindow_ends_thisyr_patch (:,:) ! all prescribed sowing window end dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: rx_cultivar_gdds_thisyr_patch (:,:) ! all cultivar GDD targets for this patch this year (ddays) [patch, mxsowings] real(r8), pointer :: gdd20_baseline_patch (:) ! GDD20 baseline for this patch (ddays) [patch] - integer , pointer :: gdd20_season_start_patch(:) ! gdd20 season start date for this patch (day of year) [patch] - integer , pointer :: gdd20_season_end_patch (:) ! gdd20 season end date for this patch (day of year) [patch] + + ! REAL FOR DEVELOPMENT ONLY; REVERT TO INTEGER BEFORE MERGE + real(r8), pointer :: gdd20_season_start_patch(:) ! gdd20 season start date for this patch (day of year) [patch] + real(r8), pointer :: gdd20_season_end_patch (:) ! gdd20 season end date for this patch (day of year) [patch] + real(r8), pointer :: sdates_thisyr_patch (:,:) ! all actual sowing dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: swindow_starts_thisyr_patch(:,:) ! all sowing window start dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: swindow_ends_thisyr_patch (:,:) ! all sowing window end dates for this patch this year (day of year) [patch, mxsowings] @@ -239,8 +242,11 @@ subroutine InitAllocate(this, bounds) allocate(this%rx_swindow_ends_thisyr_patch(begp:endp,1:mxsowings)) ; this%rx_swindow_ends_thisyr_patch (:,:) = -1 allocate(this%rx_cultivar_gdds_thisyr_patch(begp:endp,1:mxsowings)) ; this%rx_cultivar_gdds_thisyr_patch(:,:) = spval allocate(this%gdd20_baseline_patch(begp:endp)) ; this%gdd20_baseline_patch(:) = spval - allocate(this%gdd20_season_start_patch(begp:endp)); this%gdd20_season_start_patch(:) = -1 - allocate(this%gdd20_season_end_patch(begp:endp)) ; this%gdd20_season_end_patch (:) = -1 + + ! REAL FOR DEVELOPMENT ONLY; REVERT TO INTEGER BEFORE MERGE + allocate(this%gdd20_season_start_patch(begp:endp)); this%gdd20_season_start_patch(:) = spval + allocate(this%gdd20_season_end_patch(begp:endp)) ; this%gdd20_season_end_patch (:) = spval + allocate(this%sdates_thisyr_patch(begp:endp,1:mxsowings)) ; this%sdates_thisyr_patch(:,:) = spval allocate(this%swindow_starts_thisyr_patch(begp:endp,1:mxsowings)) ; this%swindow_starts_thisyr_patch(:,:) = spval allocate(this%swindow_ends_thisyr_patch (begp:endp,1:mxsowings)) ; this%swindow_ends_thisyr_patch (:,:) = spval @@ -364,6 +370,20 @@ subroutine InitHistory(this, bounds) avgflag='I', long_name='Reason for each crop harvest; should only be output annually', & ptr_patch=this%harvest_reason_thisyr_patch, default='inactive') + ! DEVELOPMENT ONLY; DELETE BEFORE MERGE + this%gdd20_baseline_patch(begp:endp) = spval + call hist_addfld1d (fname='GDD20_BASELINE', units='ddays', & + avgflag='A', long_name='Baseline mean growing-degree days accumulated during accumulation period (from input)', & + ptr_patch=this%gdd20_baseline_patch, default='inactive') + this%gdd20_season_start_patch(begp:endp) = spval + call hist_addfld1d (fname='GDD20_SEASON_START', units='day of year', & + avgflag='A', long_name='Start of the GDD20 accumulation season (from input)', & + ptr_patch=this%gdd20_season_start_patch, default='inactive') + this%gdd20_season_end_patch(begp:endp) = spval + call hist_addfld1d (fname='GDD20_SEASON_END', units='day of year', & + avgflag='A', long_name='End of the GDD20 accumulation season (from input)', & + ptr_patch=this%gdd20_season_end_patch, default='inactive') + end subroutine InitHistory subroutine InitCold(this, bounds) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 5ae0b7b20c..ae5703237e 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -1428,8 +1428,9 @@ subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, d ((month > 9 .or. month < 4) .and. lat < 0._r8) ! Replace with read-in gdd20 accumulation season, if valid ! (If these aren't being read in or they're invalid, they'll be -1) - gdd20_season_start = gdd20_season_starts(p) - gdd20_season_end = gdd20_season_ends(p) + ! REAL FOR DEVELOPMENT ONLY; REVERT TO INTEGER BEFORE MERGE + gdd20_season_start = int(gdd20_season_starts(p)) + gdd20_season_end = int(gdd20_season_ends(p)) if (gdd20_season_start >= 1 .and. gdd20_season_end >= 1) then in_accumulation_season = is_doy_in_interval( & gdd20_season_start, gdd20_season_end, day) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index ce595c0d70..802b669905 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -766,8 +766,10 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) n = ivt - npcropmin + 1 ! vegetated pft ig = g_to_ig(patch%gridcell(p)) - gdd20_season_starts(p) = dataptr2d_gdd20_season_start(ig,n) - gdd20_season_ends(p) = dataptr2d_gdd20_season_end (ig,n) + + ! REAL FOR DEVELOPMENT ONLY; REVERT TO INTEGER BEFORE MERGE + gdd20_season_starts(p) = real(dataptr2d_gdd20_season_start(ig,n), r8) + gdd20_season_ends(p) = real(dataptr2d_gdd20_season_end (ig,n), r8) else write(iulog,'(a,i0)') 'cropcal_interp(), gdd20 seasons: Crop patch has ivt ',ivt call ESMF_Finalize(endflag=ESMF_END_ABORT) @@ -775,15 +777,18 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) end do ! Handle invalid gdd20 season values - if (any(gdd20_season_starts(begp:endp) < 1 .or. gdd20_season_ends(begp:endp) < 1)) then + ! gdd20_season_starts and gdd20_season_ends REAL FOR DEVELOPMENT ONLY; REVERT TO INTEGER BEFORE MERGE + if (any(gdd20_season_starts(begp:endp) < 1._r8 .or. gdd20_season_ends(begp:endp) < 1._r8)) then ! Fail if not allowing fallback to paramfile sowing windows - if ((.not. allow_invalid_gdd20_season_inputs) .and. any(gdd20_season_starts(begp:endp) < 1 .and. patch%wtgcell(begp:endp) > 0._r8 .and. patch%itype(begp:endp) >= npcropmin)) then + ! gdd20_season_starts REAL FOR DEVELOPMENT ONLY; REVERT TO INTEGER BEFORE MERGE + if ((.not. allow_invalid_gdd20_season_inputs) .and. any(gdd20_season_starts(begp:endp) < 1._r8 .and. patch%wtgcell(begp:endp) > 0._r8 .and. patch%itype(begp:endp) >= npcropmin)) then write(iulog, *) 'At least one crop in one gridcell has invalid gdd20 season start date(s). To ignore and fall back to paramfile sowing windows, set allow_invalid_gdd20_season_inputs to .true.' write(iulog, *) 'Affected crops:' do ivt = npcropmin, mxpft do fp = 1, num_pcropp p = filter_pcropp(fp) - if (ivt == patch%itype(p) .and. patch%wtgcell(p) > 0._r8 .and. gdd20_season_starts(p) < 1) then + ! gdd20_season_starts REAL FOR DEVELOPMENT ONLY; REVERT TO INTEGER BEFORE MERGE + if (ivt == patch%itype(p) .and. patch%wtgcell(p) > 0._r8 .and. gdd20_season_starts(p) < 1._r8) then write(iulog, *) ' ',pftname(ivt),' (',ivt,')' exit ! Stop looking for patches of this type end if @@ -792,7 +797,8 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) call ESMF_Finalize(endflag=ESMF_END_ABORT) ! Fail if a gdd20 season start date is given without an end date (or vice versa) - else if (any((gdd20_season_starts(begp:endp) >= 1 .and. gdd20_season_ends(begp:endp) < 1) .or. (gdd20_season_starts(begp:endp) < 1 .and. gdd20_season_ends(begp:endp) >= 1))) then + ! gdd20_season_starts and gdd20_season_ends REAL FOR DEVELOPMENT ONLY; REVERT TO INTEGER BEFORE MERGE + else if (any((gdd20_season_starts(begp:endp) >= 1._r8 .and. gdd20_season_ends(begp:endp) < 1._r8) .or. (gdd20_season_starts(begp:endp) < 1._r8 .and. gdd20_season_ends(begp:endp) >= 1._r8))) then write(iulog, *) 'Every gdd20 season start date must have a corresponding end date.' call ESMF_Finalize(endflag=ESMF_END_ABORT) end if From 0083a08432859948707cf4e8e7b4937dc7f1d92c Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 12:03:15 -0600 Subject: [PATCH 133/406] rxcropmaturity.py is now parent rxcropmaturityshared.py. --- ...ropmaturity.py => rxcropmaturityshared.py} | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) rename cime_config/SystemTests/{rxcropmaturity.py => rxcropmaturityshared.py} (97%) diff --git a/cime_config/SystemTests/rxcropmaturity.py b/cime_config/SystemTests/rxcropmaturityshared.py similarity index 97% rename from cime_config/SystemTests/rxcropmaturity.py rename to cime_config/SystemTests/rxcropmaturityshared.py index 75fff8a0e0..f8e4b1c9bb 100644 --- a/cime_config/SystemTests/rxcropmaturity.py +++ b/cime_config/SystemTests/rxcropmaturityshared.py @@ -11,6 +11,10 @@ code do the interpolation. However, that wouldn't act on harvest dates (which are needed for generate_gdds.py). I could have Python interpolate those, but this would cause a potential inconsistency. + +Note that this is just a parent class. The actual tests are RXCROPMATURITY and +RXCROPMATURITY_SKIPRUN, the latter of which does everything except perform and +check the CTSM runs. """ import os @@ -25,7 +29,7 @@ logger = logging.getLogger(__name__) -class RXCROPMATURITY(SystemTestsCommon): +class RXCROPMATURITYSHARED(SystemTestsCommon): def __init__(self, case): # initialize an object interface to the SMS system test SystemTestsCommon.__init__(self, case) @@ -84,7 +88,7 @@ def __init__(self, case): # Which conda environment should we use? self._get_conda_env() - def run_phase(self): + def _run_phase(self, skip_run=False): # Modeling this after the SSP test, we create a clone to be the case whose outputs we don't # want to be saved as baseline. @@ -146,9 +150,10 @@ def run_phase(self): # "No history files expected, set suffix=None to avoid compare error" # We *do* expect history files here, but anyway. This works. self._skip_pnl = False - self.run_indv(suffix=None, st_archive=True) - self._run_generate_gdds(case_gddgen) + if not skip_run: + self.run_indv(suffix=None, st_archive=True) + self._run_generate_gdds(case_gddgen) # ------------------------------------------------------------------- # (3) Set up and perform Prescribed Calendars run @@ -168,13 +173,15 @@ def run_phase(self): ] ) - self.run_indv() + if not skip_run: + self.run_indv() # ------------------------------------------------------------------- # (4) Check Prescribed Calendars run # ------------------------------------------------------------------- logger.info("RXCROPMATURITY log: output check: Prescribed Calendars") - self._run_check_rxboth_run() + if not skip_run: + self._run_check_rxboth_run() # Get sowing and harvest dates for this resolution. def _get_rx_dates(self): From f728f9676e5103298dd255fcec2a6796d22e0144 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 13:44:07 -0600 Subject: [PATCH 134/406] Added back RXCROPMATURITY. --- cime_config/SystemTests/rxcropmaturity.py | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 cime_config/SystemTests/rxcropmaturity.py diff --git a/cime_config/SystemTests/rxcropmaturity.py b/cime_config/SystemTests/rxcropmaturity.py new file mode 100644 index 0000000000..3eadccfeb3 --- /dev/null +++ b/cime_config/SystemTests/rxcropmaturity.py @@ -0,0 +1,5 @@ +from RXCROPMATURITYSHARED import RXCROPMATURITYSHARED + +class RXCROPMATURITY(RXCROPMATURITYSHARED): + def run_phase(self): + self._run_phase() \ No newline at end of file From e4b97a233cd722ab915a9b0be27f5ece509c8ca9 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 13:44:51 -0600 Subject: [PATCH 135/406] Add RXCROPMATURITYSKIPRUN. --- cime_config/SystemTests/rxcropmaturityskiprun.py | 6 ++++++ cime_config/config_tests.xml | 10 ++++++++++ 2 files changed, 16 insertions(+) create mode 100644 cime_config/SystemTests/rxcropmaturityskiprun.py diff --git a/cime_config/SystemTests/rxcropmaturityskiprun.py b/cime_config/SystemTests/rxcropmaturityskiprun.py new file mode 100644 index 0000000000..d52742f95a --- /dev/null +++ b/cime_config/SystemTests/rxcropmaturityskiprun.py @@ -0,0 +1,6 @@ +print("pre-import") +from RXCROPMATURITYSHARED import RXCROPMATURITYSHARED + +class RXCROPMATURITYSKIPRUN(RXCROPMATURITYSHARED): + def run_phase(self): + self._run_phase(skip_run=True) \ No newline at end of file diff --git a/cime_config/config_tests.xml b/cime_config/config_tests.xml index c0b6afed9d..98434da10e 100644 --- a/cime_config/config_tests.xml +++ b/cime_config/config_tests.xml @@ -133,6 +133,16 @@ This defines various CTSM-specific system tests $STOP_N + + As RXCROPMATURITY but don't actually run or postprocess; just setup + 1 + FALSE + FALSE + never + $STOP_OPTION + $STOP_N + + lnd/clm2/surfdata_esmf/ctsm5.2.0/landuse.timeseries_1x1_smallvilleIA_SSP2-4.5_1850-1855_78pfts_c240221.nc + + + diff --git a/bld/namelist_files/namelist_defaults_overall.xml b/bld/namelist_files/namelist_defaults_overall.xml index 479b2a02b7..5b7ae1bdd9 100644 --- a/bld/namelist_files/namelist_defaults_overall.xml +++ b/bld/namelist_files/namelist_defaults_overall.xml @@ -62,6 +62,7 @@ determine default values for namelists. 1x1_urbanc_alpha 1x1_numaIA 1x1_smallvilleIA +1x1_cidadinhoBR 2000 @@ -110,6 +111,7 @@ determine default values for namelists. test navy test +test gx1v7 diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index b0ddb1e448..5c8aa3468d 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -1590,21 +1590,23 @@ sub cat_and_create_namelistinfile { print "==================================================\n"; # Check for crop resolutions -my $crop1850_res = "1x1_smallvilleIA"; -$options = "-bgc bgc -crop -res $crop1850_res -use_case 1850_control -envxml_dir ."; -&make_env_run(); -eval{ system( "$bldnml $options > $tempfile 2>&1 " ); }; -is( $@, '', "$options" ); -$cfiles->checkfilesexist( "$options", $mode ); -$cfiles->shownmldiff( "default", "standard" ); -if ( defined($opts{'compare'}) ) { - $cfiles->doNOTdodiffonfile( "$tempfile", "$options", $mode ); - $cfiles->comparefiles( "$options", $mode, $opts{'compare'} ); -} -if ( defined($opts{'generate'}) ) { - $cfiles->copyfiles( "$options", $mode ); +my @crop1850_res = ( "1x1_smallvilleIA", "1x1_cidadinhoBR" ); +foreach my $res ( @crop1850_res ) { + $options = "-bgc bgc -crop -res $res -use_case 1850_control -envxml_dir ."; + &make_env_run(); + eval{ system( "$bldnml $options > $tempfile 2>&1 " ); }; + is( $@, '', "$options" ); + $cfiles->checkfilesexist( "$options", $mode ); + $cfiles->shownmldiff( "default", "standard" ); + if ( defined($opts{'compare'}) ) { + $cfiles->doNOTdodiffonfile( "$tempfile", "$options", $mode ); + $cfiles->comparefiles( "$options", $mode, $opts{'compare'} ); + } + if ( defined($opts{'generate'}) ) { + $cfiles->copyfiles( "$options", $mode ); + } + &cleanup(); } -&cleanup(); my @crop_res = ( "1x1_numaIA", "4x5", "10x15", "0.9x1.25", "1.9x2.5", "ne3np4.pg3", "ne30np4", "ne30np4.pg3", "C96", "mpasa120" ); foreach my $res ( @crop_res ) { diff --git a/tools/mksurfdata_esmf/Makefile b/tools/mksurfdata_esmf/Makefile index d8bacdc5dd..c344843d06 100644 --- a/tools/mksurfdata_esmf/Makefile +++ b/tools/mksurfdata_esmf/Makefile @@ -54,7 +54,10 @@ SUBSETDATA_POINT_URBAN = $(SUBSETDATA_POINT) --include-nonveg # Subset data sites... SUBSETDATA_1X1_BRAZIL := --lat -7 --lon -55 --site 1x1_brazil SUBSETDATA_1X1_NUMAIA := --lat 40.6878 --lon 267.0228 --site 1x1_numaIA -SUBSETDATA_1X1_SMALL := --lat 40.6878 --lon 267.0228 --site 1x1_smallvilleIA \ +SUBSETDATA_1X1_SMALL_IA := --lat 40.6878 --lon 267.0228 --site 1x1_smallvilleIA \ + --dompft 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 \ + --pctpft 6.5 1.5 1.6 1.7 1.8 1.9 1.5 1.6 1.7 1.8 1.9 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 +SUBSETDATA_1X1_SMALL_BR := --lat -12.9952 --lon 305.3233 --site 1x1_cidadinhoBR \ --dompft 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 \ --pctpft 6.5 1.5 1.6 1.7 1.8 1.9 1.5 1.6 1.7 1.8 1.9 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 # NOTE: The 1850 smallvilleIA site is constructed to start with 100% natural vegetation, so we can test transition to crops @@ -112,6 +115,7 @@ all-subset : \ 1x1-smallville-present \ 1x1-smallville-1850 \ 1x1-smallville-transient \ + 1x1-cidadinho-present \ urban DEBUG: @@ -239,7 +243,7 @@ crop-global-hist-ne30 : FORCE $(SUBSETDATA_POINT_ALLLU) --create-surface $(SUBSETDATA_1X1_NUMAIA) 1x1-smallville-present : FORCE - $(SUBSETDATA_POINT) --create-surface $(SUBSETDATA_1X1_SMALL) + $(SUBSETDATA_POINT) --create-surface $(SUBSETDATA_1X1_SMALL_IA) # Note that the smallville 1850 dataset is entirely natural vegetation. This # facilitates testing a transient case that starts with no crop, and then later @@ -254,6 +258,9 @@ crop-global-hist-ne30 : FORCE $(SUBSETDATA_POINT) --create-landuse $(SUBSETDATA_1X1_SMALLTRANSIENT) ../modify_input_files/modify_smallville.sh +1x1-cidadinho-present : FORCE + $(SUBSETDATA_POINT) --create-surface $(SUBSETDATA_1X1_SMALL_BR) + # # Crop with future scenarios # From 46307bca0a850876c67a3041bf786a76441ca853 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 14 Jun 2024 14:19:01 -0600 Subject: [PATCH 187/406] generate_gdds: Don't fail if making figures but GDDHARV alll NaN. --- .../crop_calendars/generate_gdds_functions.py | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdds_functions.py b/python/ctsm/crop_calendars/generate_gdds_functions.py index 8af2fdc049..38c5d44384 100644 --- a/python/ctsm/crop_calendars/generate_gdds_functions.py +++ b/python/ctsm/crop_calendars/generate_gdds_functions.py @@ -575,6 +575,7 @@ def import_and_process_1yr( this_crop_gddaccum_da = this_crop_ds[clm_gdd_var] if save_figs: this_crop_gddharv_da = this_crop_ds["GDDHARV"] + check_gddharv = True if not this_crop_gddaccum_da.size: continue log(logger, f" {vegtype_str}...") @@ -625,11 +626,18 @@ def import_and_process_1yr( + "NaN after extracting GDDs accumulated at harvest", ) if save_figs and np.any(np.isnan(gddharv_atharv_p)): - log( - logger, - f" ❗ {np.sum(np.isnan(gddharv_atharv_p))}/{len(gddharv_atharv_p)} " - + "NaN after extracting GDDHARV", - ) + if np.all(np.isnan(gddharv_atharv_p)): + log( + logger, + " ❗ All GDDHARV are NaN; should only affect figure" + ) + check_gddharv = False + else: + log( + logger, + f" ❗ {np.sum(np.isnan(gddharv_atharv_p))}/{len(gddharv_atharv_p)} " + + "NaN after extracting GDDHARV", + ) # Assign these to growing seasons based on whether gs crossed new year this_year_active_patch_indices = [ @@ -712,7 +720,7 @@ def import_and_process_1yr( ) else: error(logger, "Unexpected NaN for last season's GDD accumulation.") - if save_figs and np.any( + if save_figs and check_gddharv and np.any( np.isnan( gddharv_yp_list[var][year_index - 1, active_this_year_where_gs_lastyr_indices] ) @@ -1160,9 +1168,13 @@ def make_figures( else: error(logger, f"layout {layout} not recognized") - this_min = int(np.round(np.nanmin(gddharv_map_yx))) - this_max = int(np.round(np.nanmax(gddharv_map_yx))) - this_title = f"{run1_name} (range {this_min}–{this_max})" + gddharv_all_nan = np.all(np.isnan(gddharv_map_yx.values)) + if gddharv_all_nan: + this_title = f"{run1_name} (GDDHARV all NaN?)" + else: + this_min = int(np.round(np.nanmin(gddharv_map_yx))) + this_max = int(np.round(np.nanmax(gddharv_map_yx))) + this_title = f"{run1_name} (range {this_min}–{this_max})" make_gengdd_map( this_axis, gddharv_map_yx, @@ -1195,7 +1207,7 @@ def make_figures( ) # Difference - if layout == "3x2": + if not gddharv_all_nan and layout == "3x2": this_axis = fig.add_subplot(spec[2, 0], projection=ccrs.PlateCarree()) this_min = int(np.round(np.nanmin(gdd_map_yx))) this_max = int(np.round(np.nanmax(gdd_map_yx))) From faa536eb00c0afb70cacce239423a5af82ccaf5a Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 14 Jun 2024 14:47:19 -0600 Subject: [PATCH 188/406] Revert "PlantCrop: GDD-gen runs get gddmaturity 1e36." GDD-generating runs should get the CLM default gddmaturity values. This reverts commit f2518b611408c12ae262c1b71b9b0a128196c934. --- src/biogeochem/CNPhenologyMod.F90 | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index 41d4107f3f..b346cb119f 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2694,10 +2694,7 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & ! set GDD target did_rx_gdds = .false. - if (generate_crop_gdds) then - ! Value mostly doesn't matter; it just needs to be large enough to avoid divide-by-zero errors. - gddmaturity(p) = 1.e36_r8 - else if (use_cropcal_rx_cultivar_gdds .and. crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) .ge. 0._r8) then + if (use_cropcal_rx_cultivar_gdds .and. crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) .ge. 0._r8) then gddmaturity(p) = crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) did_rx_gdds = .true. if (adapt_cropcal_rx_cultivar_gdds .and. crop_inst%gdd20_baseline_patch(p) > min_gdd20_baseline) then From 81177dd169e3c2f15f97a23216eb9793de4d8f3a Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 14 Jun 2024 16:10:34 -0600 Subject: [PATCH 189/406] Revert all RXCROPMATURITY changes. --- cime_config/SystemTests/rxcropmaturity.py | 445 ++++++++++++++++- .../SystemTests/rxcropmaturityshared.py | 459 ------------------ .../SystemTests/rxcropmaturityskipbuild.py | 9 - .../SystemTests/rxcropmaturityskiprun.py | 9 - cime_config/config_tests.xml | 20 - 5 files changed, 442 insertions(+), 500 deletions(-) delete mode 100644 cime_config/SystemTests/rxcropmaturityshared.py delete mode 100644 cime_config/SystemTests/rxcropmaturityskipbuild.py delete mode 100644 cime_config/SystemTests/rxcropmaturityskiprun.py diff --git a/cime_config/SystemTests/rxcropmaturity.py b/cime_config/SystemTests/rxcropmaturity.py index 206476f051..75fff8a0e0 100644 --- a/cime_config/SystemTests/rxcropmaturity.py +++ b/cime_config/SystemTests/rxcropmaturity.py @@ -1,5 +1,444 @@ -from rxcropmaturityshared import RXCROPMATURITYSHARED +""" +CTSM-specific test that first performs a GDD-generating run, then calls +Python code to generate the maturity requirement file. This is then used +in a sowing+maturity forced run, which finally is tested to ensure +correct behavior. + +Currently only supports 0.9x1.25, 1.9x2.5, and 10x15 resolutions. Eventually, +this test should be able to generate its own files at whatever resolution it's +called at. Well, really, the ultimate goal would be to give CLM the files +at the original resolution (for GGCMI phase 3, 0.5°) and have the stream +code do the interpolation. However, that wouldn't act on harvest dates +(which are needed for generate_gdds.py). I could have Python interpolate +those, but this would cause a potential inconsistency. +""" + +import os +import re +import systemtest_utils as stu +import subprocess +from CIME.SystemTests.system_tests_common import SystemTestsCommon +from CIME.XML.standard_module_setup import * +from CIME.SystemTests.test_utils.user_nl_utils import append_to_user_nl_files +import shutil, glob + +logger = logging.getLogger(__name__) + + +class RXCROPMATURITY(SystemTestsCommon): + def __init__(self, case): + # initialize an object interface to the SMS system test + SystemTestsCommon.__init__(self, case) + + # Ensure run length is at least 5 years. Minimum to produce one complete growing season (i.e., two complete calendar years) actually 4 years, but that only gets you 1 season usable for GDD generation, so you can't check for season-to-season consistency. + stop_n = self._case.get_value("STOP_N") + stop_option = self._case.get_value("STOP_OPTION") + stop_n_orig = stop_n + stop_option_orig = stop_option + if "nsecond" in stop_option: + stop_n /= 60 + stop_option = "nminutes" + if "nminute" in stop_option: + stop_n /= 60 + stop_option = "nhours" + if "nhour" in stop_option: + stop_n /= 24 + stop_option = "ndays" + if "nday" in stop_option: + stop_n /= 365 + stop_option = "nyears" + if "nmonth" in stop_option: + stop_n /= 12 + stop_option = "nyears" + error_message = None + if "nyear" not in stop_option: + error_message = ( + f"STOP_OPTION ({stop_option_orig}) must be nsecond(s), nminute(s), " + + "nhour(s), nday(s), nmonth(s), or nyear(s)" + ) + elif stop_n < 5: + error_message = ( + "RXCROPMATURITY must be run for at least 5 years; you requested " + + f"{stop_n_orig} {stop_option_orig[1:]}" + ) + if error_message is not None: + logger.error(error_message) + raise RuntimeError(error_message) + + # Get the number of complete years that will be run + self._run_Nyears = int(stop_n) + + # Only allow RXCROPMATURITY to be called with test cropMonthOutput + casebaseid = self._case.get_value("CASEBASEID") + if casebaseid.split("-")[-1] != "cropMonthOutput": + error_message = ( + "Only call RXCROPMATURITY with test cropMonthOutput " + + "to avoid potentially huge sets of daily outputs." + ) + logger.error(error_message) + raise RuntimeError(error_message) + + # Get files with prescribed sowing and harvest dates + self._get_rx_dates() + + # Which conda environment should we use? + self._get_conda_env() -class RXCROPMATURITY(RXCROPMATURITYSHARED): def run_phase(self): - self._run_phase() \ No newline at end of file + # Modeling this after the SSP test, we create a clone to be the case whose outputs we don't + # want to be saved as baseline. + + # ------------------------------------------------------------------- + # (1) Set up GDD-generating run + # ------------------------------------------------------------------- + # Create clone to be GDD-Generating case + logger.info("RXCROPMATURITY log: cloning setup") + case_rxboth = self._case + caseroot = self._case.get_value("CASEROOT") + clone_path = f"{caseroot}.gddgen" + self._path_gddgen = clone_path + if os.path.exists(self._path_gddgen): + shutil.rmtree(self._path_gddgen) + logger.info("RXCROPMATURITY log: cloning") + case_gddgen = self._case.create_clone(clone_path, keepexe=True) + logger.info("RXCROPMATURITY log: done cloning") + + os.chdir(self._path_gddgen) + self._set_active_case(case_gddgen) + + # Set up stuff that applies to both tests + self._setup_all() + + # Add stuff specific to GDD-Generating run + logger.info("RXCROPMATURITY log: modify user_nl files: generate GDDs") + self._append_to_user_nl_clm( + [ + "generate_crop_gdds = .true.", + "use_mxmat = .false.", + " ", + "! (h2) Daily outputs for GDD generation and figure-making", + "hist_fincl3 = 'GDDACCUM', 'GDDHARV'", + "hist_nhtfrq(3) = -24", + "hist_mfilt(3) = 365", + "hist_type1d_pertape(3) = 'PFTS'", + "hist_dov2xy(3) = .false.", + ] + ) + + # If flanduse_timeseries is defined, we need to make a static version for this test. This + # should have every crop in most of the world. + self._get_flanduse_timeseries_in(case_gddgen) + if self._flanduse_timeseries_in is not None: + + # Download files from the server, if needed + case_gddgen.check_all_input_data() + + # Make custom version of surface file + logger.info("RXCROPMATURITY log: run fsurdat_modifier") + self._run_fsurdat_modifier() + + # ------------------------------------------------------------------- + # (2) Perform GDD-generating run and generate prescribed GDDs file + # ------------------------------------------------------------------- + logger.info("RXCROPMATURITY log: Start GDD-Generating run") + + # As per SSP test: + # "No history files expected, set suffix=None to avoid compare error" + # We *do* expect history files here, but anyway. This works. + self._skip_pnl = False + self.run_indv(suffix=None, st_archive=True) + + self._run_generate_gdds(case_gddgen) + + # ------------------------------------------------------------------- + # (3) Set up and perform Prescribed Calendars run + # ------------------------------------------------------------------- + os.chdir(caseroot) + self._set_active_case(case_rxboth) + + # Set up stuff that applies to both tests + self._setup_all() + + # Add stuff specific to Prescribed Calendars run + logger.info("RXCROPMATURITY log: modify user_nl files: Prescribed Calendars") + self._append_to_user_nl_clm( + [ + "generate_crop_gdds = .false.", + f"stream_fldFileName_cultivar_gdds = '{self._gdds_file}'", + ] + ) + + self.run_indv() + + # ------------------------------------------------------------------- + # (4) Check Prescribed Calendars run + # ------------------------------------------------------------------- + logger.info("RXCROPMATURITY log: output check: Prescribed Calendars") + self._run_check_rxboth_run() + + # Get sowing and harvest dates for this resolution. + def _get_rx_dates(self): + # Eventually, I want to remove these hard-coded resolutions so that this test can generate + # its own sowing and harvest date files at whatever resolution is requested. + lnd_grid = self._case.get_value("LND_GRID") + input_data_root = self._case.get_value("DIN_LOC_ROOT") + processed_crop_dates_dir = f"{input_data_root}/lnd/clm2/cropdata/calendars/processed" + if lnd_grid == "10x15": + self._sdatefile = os.path.join( + processed_crop_dates_dir, + "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f10_f10_mg37.2000-2000.20230330_165301.nc", + ) + self._hdatefile = os.path.join( + processed_crop_dates_dir, + "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f10_f10_mg37.2000-2000.20230330_165301.nc", + ) + elif lnd_grid == "1.9x2.5": + self._sdatefile = os.path.join( + processed_crop_dates_dir, + "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f19_g17.2000-2000.20230102_175625.nc", + ) + self._hdatefile = os.path.join( + processed_crop_dates_dir, + "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f19_g17.2000-2000.20230102_175625.nc", + ) + elif lnd_grid == "0.9x1.25": + self._sdatefile = os.path.join( + processed_crop_dates_dir, + "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f09_g17.2000-2000.20230520_134417.nc", + ) + self._hdatefile = os.path.join( + processed_crop_dates_dir, + "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f09_g17.2000-2000.20230520_134418.nc", + ) + else: + error_message = "ERROR: RXCROPMATURITY currently only supports 0.9x1.25, 1.9x2.5, and 10x15 resolutions" + logger.error(error_message) + raise RuntimeError(error_message) + + # Ensure files exist + error_message = None + if not os.path.exists(self._sdatefile): + error_message = f"ERROR: Sowing date file not found: {self._sdatefile}" + elif not os.path.exists(self._hdatefile): + error_message = f"ERROR: Harvest date file not found: {self._sdatefile}" + if error_message is not None: + logger.error(error_message) + raise RuntimeError(error_message) + + def _setup_all(self): + logger.info("RXCROPMATURITY log: _setup_all start") + + # Get some info + self._ctsm_root = self._case.get_value("COMP_ROOT_DIR_LND") + run_startdate = self._case.get_value("RUN_STARTDATE") + self._run_startyear = int(run_startdate.split("-")[0]) + + # Set sowing dates file (and other crop calendar settings) for all runs + logger.info("RXCROPMATURITY log: modify user_nl files: all tests") + self._modify_user_nl_allruns() + logger.info("RXCROPMATURITY log: _setup_all done") + + # Make a surface dataset that has every crop in every gridcell + def _run_fsurdat_modifier(self): + + # fsurdat should be defined. Where is it? + self._fsurdat_in = None + with open(self._lnd_in_path, "r") as lnd_in: + for line in lnd_in: + fsurdat_in = re.match(r" *fsurdat *= *'(.*)'", line) + if fsurdat_in: + self._fsurdat_in = fsurdat_in.group(1) + break + if self._fsurdat_in is None: + error_message = "fsurdat not defined" + logger.error(error_message) + raise RuntimeError(error_message) + + # Where we will save the fsurdat version for this test + path, ext = os.path.splitext(self._fsurdat_in) + dir_in, filename_in_noext = os.path.split(path) + self._fsurdat_out = os.path.join( + self._path_gddgen, f"{filename_in_noext}.all_crops_everywhere{ext}" + ) + + # Make fsurdat for this test, if not already done + if not os.path.exists(self._fsurdat_out): + tool_path = os.path.join( + self._ctsm_root, + "tools", + "modify_input_files", + "fsurdat_modifier", + ) + + # Create configuration file for fsurdat_modifier + self._cfg_path = os.path.join( + self._path_gddgen, + "modify_fsurdat_allcropseverywhere.cfg", + ) + self._create_config_file_evenlysplitcrop() + + command = f"python3 {tool_path} {self._cfg_path} " + stu.run_python_script( + self._get_caseroot(), + self._this_conda_env, + command, + tool_path, + ) + + # Modify namelist + logger.info("RXCROPMATURITY log: modify user_nl files: new fsurdat") + self._append_to_user_nl_clm( + [ + "fsurdat = '{}'".format(self._fsurdat_out), + "do_transient_crops = .false.", + "flanduse_timeseries = ''", + "use_init_interp = .true.", + ] + ) + + def _create_config_file_evenlysplitcrop(self): + """ + Open the new and the template .cfg files + Loop line by line through the template .cfg file + When string matches, replace that line's content + """ + cfg_template_path = os.path.join( + self._ctsm_root, "tools/modify_input_files/modify_fsurdat_template.cfg" + ) + + with open(self._cfg_path, "w", encoding="utf-8") as cfg_out: + # Copy template, replacing some lines + with open(cfg_template_path, "r", encoding="utf-8") as cfg_in: + for line in cfg_in: + if re.match(r" *evenly_split_cropland *=", line): + line = f"evenly_split_cropland = True" + elif re.match(r" *fsurdat_in *=", line): + line = f"fsurdat_in = {self._fsurdat_in}" + elif re.match(r" *fsurdat_out *=", line): + line = f"fsurdat_out = {self._fsurdat_out}" + elif re.match(r" *process_subgrid_section *=", line): + line = f"process_subgrid_section = True" + cfg_out.write(line) + + # Add new lines + cfg_out.write("\n") + cfg_out.write("[modify_fsurdat_subgrid_fractions]\n") + cfg_out.write("PCT_CROP = 100.0\n") + cfg_out.write("PCT_NATVEG = 0.0\n") + cfg_out.write("PCT_GLACIER = 0.0\n") + cfg_out.write("PCT_WETLAND = 0.0\n") + cfg_out.write("PCT_LAKE = 0.0\n") + cfg_out.write("PCT_OCEAN = 0.0\n") + cfg_out.write("PCT_URBAN = 0.0 0.0 0.0\n") + + def _run_check_rxboth_run(self): + + output_dir = os.path.join(self._get_caseroot(), "run") + first_usable_year = self._run_startyear + 2 + last_usable_year = self._run_startyear + self._run_Nyears - 2 + + tool_path = os.path.join( + self._ctsm_root, "python", "ctsm", "crop_calendars", "check_rxboth_run.py" + ) + command = ( + f"python3 {tool_path} " + + f"--directory {output_dir} " + + f"-y1 {first_usable_year} " + + f"-yN {last_usable_year} " + + f"--rx-sdates-file {self._sdatefile} " + + f"--rx-gdds-file {self._gdds_file} " + ) + stu.run_python_script( + self._get_caseroot(), + self._this_conda_env, + command, + tool_path, + ) + + def _modify_user_nl_allruns(self): + nl_additions = [ + "stream_meshfile_cropcal = '{}'".format(self._case.get_value("LND_DOMAIN_MESH")), + "stream_fldFileName_swindow_start = '{}'".format(self._sdatefile), + "stream_fldFileName_swindow_end = '{}'".format(self._sdatefile), + "stream_year_first_cropcal_swindows = 2000", + "stream_year_last_cropcal_swindows = 2000", + "model_year_align_cropcal_swindows = 2000", + " ", + "! (h1) Annual outputs on sowing or harvest axis", + "hist_fincl2 = 'GRAINC_TO_FOOD_PERHARV', 'GRAINC_TO_FOOD_ANN', 'SDATES', 'SDATES_PERHARV', 'SYEARS_PERHARV', 'HDATES', 'GDDHARV_PERHARV', 'GDDACCUM_PERHARV', 'HUI_PERHARV', 'SOWING_REASON_PERHARV', 'HARVEST_REASON_PERHARV'", + "hist_nhtfrq(2) = 17520", + "hist_mfilt(2) = 999", + "hist_type1d_pertape(2) = 'PFTS'", + "hist_dov2xy(2) = .false.", + ] + self._append_to_user_nl_clm(nl_additions) + + def _run_generate_gdds(self, case_gddgen): + self._generate_gdds_dir = os.path.join(self._path_gddgen, "generate_gdds_out") + os.makedirs(self._generate_gdds_dir) + + # Get arguments to generate_gdds.py + dout_sr = case_gddgen.get_value("DOUT_S_ROOT") + input_dir = os.path.join(dout_sr, "lnd", "hist") + first_season = self._run_startyear + 2 + last_season = self._run_startyear + self._run_Nyears - 2 + sdates_file = self._sdatefile + hdates_file = self._hdatefile + + # It'd be much nicer to call generate_gdds.main(), but I can't import generate_gdds. + tool_path = os.path.join( + self._ctsm_root, "python", "ctsm", "crop_calendars", "generate_gdds.py" + ) + command = " ".join( + [ + f"python3 {tool_path}", + f"--input-dir {input_dir}", + f"--first-season {first_season}", + f"--last-season {last_season}", + f"--sdates-file {sdates_file}", + f"--hdates-file {hdates_file}", + f"--output-dir generate_gdds_out", + f"--skip-crops miscanthus,irrigated_miscanthus", + ] + ) + stu.run_python_script( + self._get_caseroot(), + self._this_conda_env, + command, + tool_path, + ) + + # Where were the prescribed maturity requirements saved? + generated_gdd_files = glob.glob(os.path.join(self._generate_gdds_dir, "gdds_*.nc")) + if len(generated_gdd_files) != 1: + error_message = f"ERROR: Expected one matching prescribed maturity requirements file; found {len(generated_gdd_files)}: {generated_gdd_files}" + logger.error(error_message) + raise RuntimeError(error_message) + self._gdds_file = generated_gdd_files[0] + + def _get_conda_env(self): + conda_setup_commands = stu.cmds_to_setup_conda(self._get_caseroot()) + + # If npl conda environment is available, use that (It has dask, which + # enables chunking, which makes reading daily 1-degree netCDF files + # much more efficient. + if "npl " in os.popen(conda_setup_commands + "conda env list").read(): + self._this_conda_env = "npl" + else: + self._this_conda_env = "ctsm_pylib" + + def _append_to_user_nl_clm(self, additions): + caseroot = self._get_caseroot() + append_to_user_nl_files(caseroot=caseroot, component="clm", contents=additions) + + # Is flanduse_timeseries defined? If so, where is it? + def _get_flanduse_timeseries_in(self, case): + case.create_namelists(component="lnd") + self._lnd_in_path = os.path.join(self._path_gddgen, "CaseDocs", "lnd_in") + self._flanduse_timeseries_in = None + with open(self._lnd_in_path, "r") as lnd_in: + for line in lnd_in: + flanduse_timeseries_in = re.match(r" *flanduse_timeseries *= *'(.*)'", line) + if flanduse_timeseries_in: + self._flanduse_timeseries_in = flanduse_timeseries_in.group(1) + break diff --git a/cime_config/SystemTests/rxcropmaturityshared.py b/cime_config/SystemTests/rxcropmaturityshared.py deleted file mode 100644 index 8a944efdf9..0000000000 --- a/cime_config/SystemTests/rxcropmaturityshared.py +++ /dev/null @@ -1,459 +0,0 @@ -""" -CTSM-specific test that first performs a GDD-generating run, then calls -Python code to generate the maturity requirement file. This is then used -in a sowing+maturity forced run, which finally is tested to ensure -correct behavior. - -Currently only supports 0.9x1.25, 1.9x2.5, and 10x15 resolutions. Eventually, -this test should be able to generate its own files at whatever resolution it's -called at. Well, really, the ultimate goal would be to give CLM the files -at the original resolution (for GGCMI phase 3, 0.5°) and have the stream -code do the interpolation. However, that wouldn't act on harvest dates -(which are needed for generate_gdds.py). I could have Python interpolate -those, but this would cause a potential inconsistency. - -Note that this is just a parent class. The actual tests are RXCROPMATURITY and -RXCROPMATURITY_SKIPRUN, the latter of which does everything except perform and -check the CTSM runs. -""" - -import os -import re -import systemtest_utils as stu -import subprocess -from CIME.SystemTests.system_tests_common import SystemTestsCommon -from CIME.XML.standard_module_setup import * -from CIME.SystemTests.test_utils.user_nl_utils import append_to_user_nl_files -import shutil, glob - -logger = logging.getLogger(__name__) - - -class RXCROPMATURITYSHARED(SystemTestsCommon): - def __init__(self, case): - # initialize an object interface to the SMS system test - SystemTestsCommon.__init__(self, case) - - # Ensure run length is at least 5 years. Minimum to produce one complete growing season (i.e., two complete calendar years) actually 4 years, but that only gets you 1 season usable for GDD generation, so you can't check for season-to-season consistency. - stop_n = self._case.get_value("STOP_N") - stop_option = self._case.get_value("STOP_OPTION") - stop_n_orig = stop_n - stop_option_orig = stop_option - if "nsecond" in stop_option: - stop_n /= 60 - stop_option = "nminutes" - if "nminute" in stop_option: - stop_n /= 60 - stop_option = "nhours" - if "nhour" in stop_option: - stop_n /= 24 - stop_option = "ndays" - if "nday" in stop_option: - stop_n /= 365 - stop_option = "nyears" - if "nmonth" in stop_option: - stop_n /= 12 - stop_option = "nyears" - error_message = None - if "nyear" not in stop_option: - error_message = ( - f"STOP_OPTION ({stop_option_orig}) must be nsecond(s), nminute(s), " - + "nhour(s), nday(s), nmonth(s), or nyear(s)" - ) - elif stop_n < 5: - error_message = ( - "RXCROPMATURITY must be run for at least 5 years; you requested " - + f"{stop_n_orig} {stop_option_orig[1:]}" - ) - if error_message is not None: - logger.error(error_message) - raise RuntimeError(error_message) - - # Get the number of complete years that will be run - self._run_Nyears = int(stop_n) - - # Only allow RXCROPMATURITY to be called with test cropMonthOutput - casebaseid = self._case.get_value("CASEBASEID") - if casebaseid.split("-")[-1] != "cropMonthOutput": - error_message = ( - "Only call RXCROPMATURITY with test cropMonthOutput " - + "to avoid potentially huge sets of daily outputs." - ) - logger.error(error_message) - raise RuntimeError(error_message) - - # Get files with prescribed sowing and harvest dates - self._get_rx_dates() - - # Which conda environment should we use? - self._get_conda_env() - - def _run_phase(self, skip_run=False): - # Modeling this after the SSP test, we create a clone to be the case whose outputs we don't - # want to be saved as baseline. - - # ------------------------------------------------------------------- - # (1) Set up GDD-generating run - # ------------------------------------------------------------------- - # Create clone to be GDD-Generating case - logger.info("RXCROPMATURITY log: cloning setup") - case_rxboth = self._case - caseroot = self._case.get_value("CASEROOT") - clone_path = f"{caseroot}.gddgen" - self._path_gddgen = clone_path - if os.path.exists(self._path_gddgen): - shutil.rmtree(self._path_gddgen) - logger.info("RXCROPMATURITY log: cloning") - case_gddgen = self._case.create_clone(clone_path, keepexe=True) - logger.info("RXCROPMATURITY log: done cloning") - - os.chdir(self._path_gddgen) - self._set_active_case(case_gddgen) - - # Set up stuff that applies to both tests - self._setup_all() - - # Add stuff specific to GDD-Generating run - logger.info("RXCROPMATURITY log: modify user_nl files: generate GDDs") - self._append_to_user_nl_clm( - [ - "generate_crop_gdds = .true.", - "stream_fldFileName_cultivar_gdds = ''", - "use_mxmat = .false.", - " ", - "! (h2) Daily outputs for GDD generation and figure-making", - "hist_fincl3 = 'GDDACCUM', 'GDDHARV'", - "hist_nhtfrq(3) = -24", - "hist_mfilt(3) = 365", - "hist_type1d_pertape(3) = 'PFTS'", - "hist_dov2xy(3) = .false.", - ] - ) - - # If flanduse_timeseries is defined, we need to make a static version for this test. This - # should have every crop in most of the world. - self._get_flanduse_timeseries_in(case_gddgen) - if self._flanduse_timeseries_in is not None: - - # Download files from the server, if needed - case_gddgen.check_all_input_data() - - # Copy needed file from original to gddgen directory - shutil.copyfile( - os.path.join(caseroot, ".env_mach_specific.sh"), - os.path.join(self._path_gddgen, ".env_mach_specific.sh"), - ) - - # Make custom version of surface file - logger.info("RXCROPMATURITY log: run fsurdat_modifier") - self._run_fsurdat_modifier() - - # ------------------------------------------------------------------- - # (2) Perform GDD-generating run and generate prescribed GDDs file - # ------------------------------------------------------------------- - logger.info("RXCROPMATURITY log: Start GDD-Generating run") - - # As per SSP test: - # "No history files expected, set suffix=None to avoid compare error" - # We *do* expect history files here, but anyway. This works. - self._skip_pnl = False - - if not skip_run: - self.run_indv(suffix=None, st_archive=True) - self._run_generate_gdds(case_gddgen) - - # ------------------------------------------------------------------- - # (3) Set up and perform Prescribed Calendars run - # ------------------------------------------------------------------- - os.chdir(caseroot) - self._set_active_case(case_rxboth) - - # Set up stuff that applies to both tests - self._setup_all() - - # Add stuff specific to Prescribed Calendars run - logger.info("RXCROPMATURITY log: modify user_nl files: Prescribed Calendars") - self._append_to_user_nl_clm( - [ - "generate_crop_gdds = .false.", - f"stream_fldFileName_cultivar_gdds = '{self._gdds_file}'", - ] - ) - - if not skip_run: - self.run_indv() - - # ------------------------------------------------------------------- - # (4) Check Prescribed Calendars run - # ------------------------------------------------------------------- - logger.info("RXCROPMATURITY log: output check: Prescribed Calendars") - if not skip_run: - self._run_check_rxboth_run() - - # Get sowing and harvest dates for this resolution. - def _get_rx_dates(self): - # Eventually, I want to remove these hard-coded resolutions so that this test can generate - # its own sowing and harvest date files at whatever resolution is requested. - lnd_grid = self._case.get_value("LND_GRID") - input_data_root = self._case.get_value("DIN_LOC_ROOT") - processed_crop_dates_dir = f"{input_data_root}/lnd/clm2/cropdata/calendars/processed" - if lnd_grid == "10x15": - self._sdatefile = os.path.join( - processed_crop_dates_dir, - "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f10_f10_mg37.2000-2000.20230330_165301.nc", - ) - self._hdatefile = os.path.join( - processed_crop_dates_dir, - "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f10_f10_mg37.2000-2000.20230330_165301.nc", - ) - elif lnd_grid == "1.9x2.5": - self._sdatefile = os.path.join( - processed_crop_dates_dir, - "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f19_g17.2000-2000.20230102_175625.nc", - ) - self._hdatefile = os.path.join( - processed_crop_dates_dir, - "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f19_g17.2000-2000.20230102_175625.nc", - ) - elif lnd_grid == "0.9x1.25": - self._sdatefile = os.path.join( - processed_crop_dates_dir, - "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f09_g17.2000-2000.20230520_134417.nc", - ) - self._hdatefile = os.path.join( - processed_crop_dates_dir, - "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f09_g17.2000-2000.20230520_134418.nc", - ) - else: - error_message = "ERROR: RXCROPMATURITY currently only supports 0.9x1.25, 1.9x2.5, and 10x15 resolutions" - logger.error(error_message) - raise RuntimeError(error_message) - - # Ensure files exist - error_message = None - if not os.path.exists(self._sdatefile): - error_message = f"ERROR: Sowing date file not found: {self._sdatefile}" - elif not os.path.exists(self._hdatefile): - error_message = f"ERROR: Harvest date file not found: {self._sdatefile}" - if error_message is not None: - logger.error(error_message) - raise RuntimeError(error_message) - - def _setup_all(self): - logger.info("RXCROPMATURITY log: _setup_all start") - - # Get some info - self._ctsm_root = self._case.get_value("COMP_ROOT_DIR_LND") - run_startdate = self._case.get_value("RUN_STARTDATE") - self._run_startyear = int(run_startdate.split("-")[0]) - - # Set sowing dates file (and other crop calendar settings) for all runs - logger.info("RXCROPMATURITY log: modify user_nl files: all tests") - self._modify_user_nl_allruns() - logger.info("RXCROPMATURITY log: _setup_all done") - - # Make a surface dataset that has every crop in every gridcell - def _run_fsurdat_modifier(self): - - # fsurdat should be defined. Where is it? - self._fsurdat_in = None - with open(self._lnd_in_path, "r") as lnd_in: - for line in lnd_in: - fsurdat_in = re.match(r" *fsurdat *= *'(.*)'", line) - if fsurdat_in: - self._fsurdat_in = fsurdat_in.group(1) - break - if self._fsurdat_in is None: - error_message = "fsurdat not defined" - logger.error(error_message) - raise RuntimeError(error_message) - - # Where we will save the fsurdat version for this test - path, ext = os.path.splitext(self._fsurdat_in) - dir_in, filename_in_noext = os.path.split(path) - self._fsurdat_out = os.path.join( - self._path_gddgen, f"{filename_in_noext}.all_crops_everywhere{ext}" - ) - - # Make fsurdat for this test, if not already done - if not os.path.exists(self._fsurdat_out): - tool_path = os.path.join( - self._ctsm_root, - "tools", - "modify_input_files", - "fsurdat_modifier", - ) - - # Create configuration file for fsurdat_modifier - self._cfg_path = os.path.join( - self._path_gddgen, - "modify_fsurdat_allcropseverywhere.cfg", - ) - self._create_config_file_evenlysplitcrop() - - command = f"python3 {tool_path} {self._cfg_path} " - stu.run_python_script( - self._get_caseroot(), - self._this_conda_env, - command, - tool_path, - ) - - # Modify namelist - logger.info("RXCROPMATURITY log: modify user_nl files: new fsurdat") - self._append_to_user_nl_clm( - [ - "fsurdat = '{}'".format(self._fsurdat_out), - "do_transient_crops = .false.", - "flanduse_timeseries = ''", - "use_init_interp = .true.", - ] - ) - - def _create_config_file_evenlysplitcrop(self): - """ - Open the new and the template .cfg files - Loop line by line through the template .cfg file - When string matches, replace that line's content - """ - cfg_template_path = os.path.join( - self._ctsm_root, "tools/modify_input_files/modify_fsurdat_template.cfg" - ) - - with open(self._cfg_path, "w", encoding="utf-8") as cfg_out: - # Copy template, replacing some lines - with open(cfg_template_path, "r", encoding="utf-8") as cfg_in: - for line in cfg_in: - if re.match(r" *evenly_split_cropland *=", line): - line = f"evenly_split_cropland = True" - elif re.match(r" *fsurdat_in *=", line): - line = f"fsurdat_in = {self._fsurdat_in}" - elif re.match(r" *fsurdat_out *=", line): - line = f"fsurdat_out = {self._fsurdat_out}" - elif re.match(r" *process_subgrid_section *=", line): - line = f"process_subgrid_section = True" - cfg_out.write(line) - - # Add new lines - cfg_out.write("\n") - cfg_out.write("[modify_fsurdat_subgrid_fractions]\n") - cfg_out.write("PCT_CROP = 100.0\n") - cfg_out.write("PCT_NATVEG = 0.0\n") - cfg_out.write("PCT_GLACIER = 0.0\n") - cfg_out.write("PCT_WETLAND = 0.0\n") - cfg_out.write("PCT_LAKE = 0.0\n") - cfg_out.write("PCT_OCEAN = 0.0\n") - cfg_out.write("PCT_URBAN = 0.0 0.0 0.0\n") - - def _run_check_rxboth_run(self): - - output_dir = os.path.join(self._get_caseroot(), "run") - first_usable_year = self._run_startyear + 2 - last_usable_year = self._run_startyear + self._run_Nyears - 2 - - tool_path = os.path.join( - self._ctsm_root, "python", "ctsm", "crop_calendars", "check_rxboth_run.py" - ) - command = ( - f"python3 {tool_path} " - + f"--directory {output_dir} " - + f"-y1 {first_usable_year} " - + f"-yN {last_usable_year} " - + f"--rx-sdates-file {self._sdatefile} " - + f"--rx-gdds-file {self._gdds_file} " - ) - stu.run_python_script( - self._get_caseroot(), - self._this_conda_env, - command, - tool_path, - ) - - def _modify_user_nl_allruns(self): - nl_additions = [ - "cropcals_rx = .true.", - "stream_meshfile_cropcal = '{}'".format(self._case.get_value("LND_DOMAIN_MESH")), - "stream_fldFileName_swindow_start = '{}'".format(self._sdatefile), - "stream_fldFileName_swindow_end = '{}'".format(self._sdatefile), - "stream_year_first_cropcal_swindows = 2000", - "stream_year_last_cropcal_swindows = 2000", - "model_year_align_cropcal_swindows = 2000", - " ", - "! (h1) Annual outputs on sowing or harvest axis", - "hist_fincl2 = 'GRAINC_TO_FOOD_PERHARV', 'GRAINC_TO_FOOD_ANN', 'SDATES', 'SDATES_PERHARV', 'SYEARS_PERHARV', 'HDATES', 'GDDHARV_PERHARV', 'GDDACCUM_PERHARV', 'HUI_PERHARV', 'SOWING_REASON_PERHARV', 'HARVEST_REASON_PERHARV'", - "hist_nhtfrq(2) = 17520", - "hist_mfilt(2) = 999", - "hist_type1d_pertape(2) = 'PFTS'", - "hist_dov2xy(2) = .false.", - ] - self._append_to_user_nl_clm(nl_additions) - - def _run_generate_gdds(self, case_gddgen): - self._generate_gdds_dir = os.path.join(self._path_gddgen, "generate_gdds_out") - os.makedirs(self._generate_gdds_dir) - - # Get arguments to generate_gdds.py - dout_sr = case_gddgen.get_value("DOUT_S_ROOT") - input_dir = os.path.join(dout_sr, "lnd", "hist") - first_season = self._run_startyear + 2 - last_season = self._run_startyear + self._run_Nyears - 2 - sdates_file = self._sdatefile - hdates_file = self._hdatefile - - # It'd be much nicer to call generate_gdds.main(), but I can't import generate_gdds. - tool_path = os.path.join( - self._ctsm_root, "python", "ctsm", "crop_calendars", "generate_gdds.py" - ) - command = " ".join( - [ - f"python3 {tool_path}", - f"--input-dir {input_dir}", - f"--first-season {first_season}", - f"--last-season {last_season}", - f"--sdates-file {sdates_file}", - f"--hdates-file {hdates_file}", - f"--output-dir generate_gdds_out", - f"--skip-crops miscanthus,irrigated_miscanthus", - ] - ) - stu.run_python_script( - self._get_caseroot(), - self._this_conda_env, - command, - tool_path, - ) - - # Where were the prescribed maturity requirements saved? - generated_gdd_files = glob.glob(os.path.join(self._generate_gdds_dir, "gdds_*.nc")) - if len(generated_gdd_files) != 1: - error_message = f"ERROR: Expected one matching prescribed maturity requirements file; found {len(generated_gdd_files)}: {generated_gdd_files}" - logger.error(error_message) - raise RuntimeError(error_message) - self._gdds_file = generated_gdd_files[0] - - def _get_conda_env(self): - conda_setup_commands = stu.cmds_to_setup_conda(self._get_caseroot()) - - # If npl conda environment is available, use that (It has dask, which - # enables chunking, which makes reading daily 1-degree netCDF files - # much more efficient. - if "npl " in os.popen(conda_setup_commands + "conda env list").read(): - self._this_conda_env = "npl" - else: - self._this_conda_env = "ctsm_pylib" - - def _append_to_user_nl_clm(self, additions): - caseroot = self._get_caseroot() - append_to_user_nl_files(caseroot=caseroot, component="clm", contents=additions) - - # Is flanduse_timeseries defined? If so, where is it? - def _get_flanduse_timeseries_in(self, case): - case.create_namelists(component="lnd") - self._lnd_in_path = os.path.join(self._path_gddgen, "CaseDocs", "lnd_in") - self._flanduse_timeseries_in = None - with open(self._lnd_in_path, "r") as lnd_in: - for line in lnd_in: - flanduse_timeseries_in = re.match(r" *flanduse_timeseries *= *'(.*)'", line) - if flanduse_timeseries_in: - self._flanduse_timeseries_in = flanduse_timeseries_in.group(1) - break diff --git a/cime_config/SystemTests/rxcropmaturityskipbuild.py b/cime_config/SystemTests/rxcropmaturityskipbuild.py deleted file mode 100644 index 4c3a69fb47..0000000000 --- a/cime_config/SystemTests/rxcropmaturityskipbuild.py +++ /dev/null @@ -1,9 +0,0 @@ -from rxcropmaturityshared import RXCROPMATURITYSHARED - -class RXCROPMATURITYSKIPBUILD(RXCROPMATURITYSHARED): - def build_indv(self, sharedlib_only=False, model_only=False): - self._case.set_value("BUILD_COMPLETE", "TRUE") - - - def run_phase(self): - self._run_phase(skip_run=False) \ No newline at end of file diff --git a/cime_config/SystemTests/rxcropmaturityskiprun.py b/cime_config/SystemTests/rxcropmaturityskiprun.py deleted file mode 100644 index 7fd217c4ff..0000000000 --- a/cime_config/SystemTests/rxcropmaturityskiprun.py +++ /dev/null @@ -1,9 +0,0 @@ -from rxcropmaturityshared import RXCROPMATURITYSHARED - -class RXCROPMATURITYSKIPRUN(RXCROPMATURITYSHARED): - def build_indv(self, sharedlib_only=False, model_only=False): - self._case.set_value("BUILD_COMPLETE", "TRUE") - - - def run_phase(self): - self._run_phase(skip_run=True) \ No newline at end of file diff --git a/cime_config/config_tests.xml b/cime_config/config_tests.xml index 115f4c0e89..c0b6afed9d 100644 --- a/cime_config/config_tests.xml +++ b/cime_config/config_tests.xml @@ -133,26 +133,6 @@ This defines various CTSM-specific system tests $STOP_N - - As RXCROPMATURITY but don't actually run or postprocess; just setup - 1 - FALSE - FALSE - never - $STOP_OPTION - $STOP_N - - - - As RXCROPMATURITY but don't actually build; just setup and then start "run." Will fail when run start is requested, but should successfully test preprocessing. - 1 - FALSE - FALSE - never - $STOP_OPTION - $STOP_N - - From d13508bef6c0b31e8569daa758c44c379f9309b8 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Tue, 25 Jun 2024 12:05:45 -0600 Subject: [PATCH 213/406] run_sys_tests: Check Python env for RXCROPMATURITY. --- python/ctsm/run_sys_tests.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/python/ctsm/run_sys_tests.py b/python/ctsm/run_sys_tests.py index de93081504..9961fd325d 100644 --- a/python/ctsm/run_sys_tests.py +++ b/python/ctsm/run_sys_tests.py @@ -736,13 +736,29 @@ def _check_py_env(test_attributes): # whether import is possible. # pylint: disable=import-error disable - # Check requirements for FSURDATMODIFYCTSM, if needed - if any("FSURDATMODIFYCTSM" in t for t in test_attributes): + # Check requirements for using modify_fsurdat, if needed + modify_fsurdat_users = ["FSURDATMODIFYCTSM", "RXCROPMATURITY"] + if any(any(u in t for u in modify_fsurdat_users) for t in test_attributes): try: import ctsm.modify_input_files.modify_fsurdat except ModuleNotFoundError as err: raise ModuleNotFoundError("modify_fsurdat" + err_msg) from err + # Check requirements for RXCROPMATURITY, if needed + if any("RXCROPMATURITY" in t for t in test_attributes): + try: + import ctsm.crop_calendars.check_rxboth_run + except ModuleNotFoundError as err: + raise ModuleNotFoundError("check_rxboth_run" + err_msg) from err + try: + import ctsm.crop_calendars.generate_gdds + except ModuleNotFoundError as err: + raise ModuleNotFoundError("generate_gdds" + err_msg) from err + try: + import ctsm.crop_calendars.interpolate_gdds + except ModuleNotFoundError as err: + raise ModuleNotFoundError("interpolate_gdds" + err_msg) from err + # Check that list for any testmods that use modify_fates_paramfile.py testmods_to_check = ["clm-FatesColdTwoStream", "clm-FatesColdTwoStreamNoCompFixedBioGeo"] testmods = _get_testmod_list(test_attributes) From bb1bd9ca8ed2cfe2e8b8d6ae756925af3100bee3 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 25 Jun 2024 22:21:10 -0600 Subject: [PATCH 214/406] Make Leung_2023 the default, remove error checking about using it --- bld/CLMBuildNamelist.pm | 3 --- bld/namelist_files/namelist_defaults_ctsm.xml | 2 +- bld/unit_testers/build-namelist_test.pl | 7 +------ 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 6a24e6c899..549622a765 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4008,9 +4008,6 @@ sub setup_logic_dust_emis { "$option is being set, need to change one or the other" ); } } - if ( $dust_emis_method eq "Leung_2023" ) { - $log->warning("dust_emis_method is Leung_2023 and that option has NOT been brought into CTSM yet"); - } } } diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 7fe7fb18c1..753a08b081 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -2780,7 +2780,7 @@ use_crop=".true.">lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_mpasa1 -Zender_2003 +Leung_2023 atm bilinear { options=>"-envxml_dir .", - namelst=>"dust_emis_method = 'Leung_2023'", - GLC_TWO_WAY_COUPLING=>"FALSE", - phys=>"clm5_1", - }, "coldwfinidat" =>{ options=>"-envxml_dir . -clm_start_type cold", namelst=>"finidat = 'testfile.nc'", GLC_TWO_WAY_COUPLING=>"FALSE", From 00e3d62d1f4c38a651178a77fd075b21b3572f3c Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 26 Jun 2024 12:26:05 -0600 Subject: [PATCH 215/406] Remove abort on Leung dust method Add some log statements around this. --- src/biogeochem/DUSTMod.F90 | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index c6a48b8145..4af9fe0acd 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -330,14 +330,7 @@ subroutine InitCold(this, bounds) ! Set basin factor to 1 for now if (dust_emis_method == 'Leung_2023') then - !do c = bounds%begc, bounds%endc - ! l = col%landunit(c) - - ! if (.not.lun%lakpoi(l)) then - ! this%mbl_bsn_fct_col(c) = 1.0_r8 - ! end if - !end do - call endrun( msg="Leung_2023 dust_emis_method is currently not available"//errMsg(sourcefile, __LINE__)) + write(iulog,*) 'Using the new Leung_2023 dust emission scheme' else if (dust_emis_method == 'Zender_2003') then if ( this%soil_erod_stream%UseStreams() )then call this%soil_erod_stream%CalcDustSource( bounds, & @@ -345,6 +338,7 @@ subroutine InitCold(this, bounds) else this%mbl_bsn_fct_col(:) = 1.0_r8 end if + write(iulog,*) 'WARNING: Asked to use the old Zender_2003 dust emission scheme, which is not currently setup completely' else write(iulog,*) 'dust_emis_method not recognized = ', trim(dust_emis_method) call endrun( msg="dust_emis_method namelist item is not valid "//errMsg(sourcefile, __LINE__)) From a5da93ec5d016aabc11452870d8c3fab1677ef25 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 26 Jun 2024 12:31:05 -0600 Subject: [PATCH 216/406] generate_gdd20_baseline: Add optional -y1 and -yN args. --- .../crop_calendars/generate_gdd20_baseline.py | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 89a38a0a08..4357bd6dd1 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -70,6 +70,24 @@ def _parse_args(): help="Overwrite existing output file, if any", action="store_true", ) + parser.add_argument( + "-y1", + "--first-year", + help=( + "First calendar year to include" + ), + type=int, + required=False, + ) + parser.add_argument( + "-yN", + "--last-year", + help=( + "Last calendar year to include" + ), + type=int, + required=False, + ) # Get arguments args = parser.parse_args(sys.argv[1:]) @@ -78,7 +96,19 @@ def _parse_args(): if os.path.exists(args.output_file) and not args.overwrite: raise FileExistsError("Output file exists but --overwrite is not specified") - return args + # Process time slice + # Assumes CESM behavior where data for e.g. 1987 is saved as 1988-01-01 + if args.first_year is not None: + date_1 = f"{args.first_year+1}-01-01" + else: + date_1 = "0000-01-01" + if args.last_year is not None: + date_n = f"{args.last_year+1}-01-01" + else: + date_n = "9999-12-31" + time_slice = slice(date_1, date_n) + + return args, time_slice def _get_cft_list(crop_list): @@ -161,7 +191,7 @@ def _add_time_axis(da_in): return da_out -def generate_gdd20_baseline(input_files, output_file, author): +def generate_gdd20_baseline(input_files, output_file, author, time_slice): """ Generate stream_fldFileName_gdd20_baseline file from CTSM outputs """ @@ -173,7 +203,7 @@ def generate_gdd20_baseline(input_files, output_file, author): input_files.sort() # Import history files and ensure they have lat/lon dims - ds_in = import_ds(input_files, VAR_LIST_IN + GRIDDING_VAR_LIST) + ds_in = import_ds(input_files, VAR_LIST_IN + GRIDDING_VAR_LIST, time_slice=time_slice) if not all(x in ds_in.dims for x in ["lat", "lon"]): raise RuntimeError("Input files must have lat and lon dimensions") @@ -247,9 +277,10 @@ def main(): """ main() function for calling generate_gdd20_baseline.py from command line. """ - args = _parse_args() + args, time_slice = _parse_args() generate_gdd20_baseline( args.input_files, args.output_file, args.author, + time_slice ) From 23c8743ae67e8b550ee3ba89c10f2a2d17a51c3b Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 26 Jun 2024 14:27:22 -0600 Subject: [PATCH 217/406] generate_gdds: Map sdate in/out diffs. --- .../crop_calendars/generate_gdd20_baseline.py | 5 +++- python/ctsm/crop_calendars/generate_gdds.py | 1 + .../crop_calendars/generate_gdds_functions.py | 28 +++++++++++++++++-- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 4357bd6dd1..476717b887 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -97,7 +97,10 @@ def _parse_args(): raise FileExistsError("Output file exists but --overwrite is not specified") # Process time slice - # Assumes CESM behavior where data for e.g. 1987 is saved as 1988-01-01 + # Assumes CESM behavior where data for e.g. 1987 is saved as 1988-01-01. + # It would be more robust, accounting for upcoming behavior (where timestamp for a year is the + # middle of that year), to do slice("YEAR1-01-03", "YEARN-01-02"), but that's not compatible + # with ctsm_pylib as of the version using python 3.7.9. See safer_timeslice() in cropcal_utils. if args.first_year is not None: date_1 = f"{args.first_year+1}-01-01" else: diff --git a/python/ctsm/crop_calendars/generate_gdds.py b/python/ctsm/crop_calendars/generate_gdds.py index 156ebfb20e..49226e6f75 100644 --- a/python/ctsm/crop_calendars/generate_gdds.py +++ b/python/ctsm/crop_calendars/generate_gdds.py @@ -178,6 +178,7 @@ def main( mxmats, cc.get_gs_len_da, skip_crops, + outdir_figs, logger, ) diff --git a/python/ctsm/crop_calendars/generate_gdds_functions.py b/python/ctsm/crop_calendars/generate_gdds_functions.py index 38c5d44384..2658e1de87 100644 --- a/python/ctsm/crop_calendars/generate_gdds_functions.py +++ b/python/ctsm/crop_calendars/generate_gdds_functions.py @@ -22,6 +22,7 @@ # pylint: disable=import-error from ctsm.crop_calendars.cropcal_figs_module import * from matplotlib.transforms import Bbox + import matplotlib.pyplot as plt warnings.filterwarnings( "ignore", @@ -63,7 +64,7 @@ def error(logger, string): raise RuntimeError(string) -def check_sdates(dates_ds, sdates_rx, logger, verbose=False): +def check_sdates(dates_ds, sdates_rx, outdir_figs, logger, verbose=False): """ Checking that input and output sdates match """ @@ -106,8 +107,28 @@ def check_sdates(dates_ds, sdates_rx, logger, verbose=False): log(logger, out_map_notnan[here][0:4]) log(logger, "diff:") log(logger, diff_map_notnan[here][0:4]) + first_diff = all_ok all_ok = False + if CAN_PLOT: + sdate_diffs_dir = os.path.join(outdir_figs, "sdate_diffs") + if first_diff: + log(logger, f"Saving sdate difference figures to {sdate_diffs_dir}") + if not os.path.exists(sdate_diffs_dir): + os.makedirs(sdate_diffs_dir) + in_map.where(~np.isnan(out_map)).plot() + plt.title(f"{vegtype_str} sdates in (masked)") + plt.savefig(os.path.join(sdate_diffs_dir, f"{vegtype_str}.in.png")) + plt.close() + out_map.plot() + plt.title(f"{vegtype_str} sdates out") + plt.savefig(os.path.join(sdate_diffs_dir, f"{vegtype_str}.out.png")) + plt.close() + diff_map.plot() + plt.title(f"{vegtype_str} sdates diff (out - in)") + plt.savefig(os.path.join(sdate_diffs_dir, f"{vegtype_str}.diff.png")) + plt.close() + if not any_found: error(logger, "No matching variables found in sdates_rx!") @@ -234,6 +255,7 @@ def import_and_process_1yr( mxmats, get_gs_len_da, skip_crops, + outdir_figs, logger, ): """ @@ -272,6 +294,8 @@ def import_and_process_1yr( time_slice=slice(f"{this_year}-01-01", f"{this_year}-12-31"), chunks=chunks, ) + for timestep in dates_ds["time"].values: + print(timestep) if dates_ds.dims["time"] > 1: if dates_ds.dims["time"] == 365: @@ -466,7 +490,7 @@ def import_and_process_1yr( # Import expected sowing dates. This will also be used as our template output file. imported_sdates = isinstance(sdates_rx, str) sdates_rx = import_rx_dates("s", sdates_rx, incl_patches1d_itype_veg, mxsowings, logger) - check_sdates(dates_incl_ds, sdates_rx, logger) + check_sdates(dates_incl_ds, sdates_rx, outdir_figs, logger) # Import hdates, if needed imported_hdates = isinstance(hdates_rx, str) From 19f793f2ff0764f7a5877440f765cdd8a5d9f8b3 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 26 Jun 2024 17:53:03 -0600 Subject: [PATCH 218/406] Update cam and cice6 tags to that in cesm2_3_beta17 --- Externals.cfg | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Externals.cfg b/Externals.cfg index 5ae9973d32..58639eafb3 100644 --- a/Externals.cfg +++ b/Externals.cfg @@ -41,15 +41,15 @@ local_path = components/cice5 required = True [cice6] -tag = cesm_cice6_4_1_10 +tag = cesm_cice6_5_0_7 protocol = git repo_url = https://github.com/ESCOMP/CESM_CICE local_path = components/cice externals = Externals.cfg required = True -[cam6] -tag = cam6_3_135 +[cam] +tag = cam6_3_160 protocol = git repo_url = https://github.com/ESCOMP/CAM local_path = components/cam From 0c9b5489e5bc81b75478499461c19ec33ecd0775 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Thu, 27 Jun 2024 12:22:42 -0400 Subject: [PATCH 219/406] changing prescribed N and P uptake to 1 in the PRT2 test --- .../testmods_dirs/clm/FatesColdPRT2/shell_commands | 11 +++++++++++ .../testmods_dirs/clm/FatesColdPRT2/user_nl_clm | 1 + 2 files changed, 12 insertions(+) create mode 100644 cime_config/testdefs/testmods_dirs/clm/FatesColdPRT2/shell_commands diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdPRT2/shell_commands b/cime_config/testdefs/testmods_dirs/clm/FatesColdPRT2/shell_commands new file mode 100644 index 0000000000..632b61bf15 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdPRT2/shell_commands @@ -0,0 +1,11 @@ +SRCDIR=`./xmlquery SRCROOT --value` +CASEDIR=`./xmlquery CASEROOT --value` +FATESDIR=$SRCDIR/src/fates/ +FATESPARAMFILE=$CASEDIR/fates_params_prt2_prescribed_np.nc + +ncgen -o $FATESPARAMFILE $FATESDIR/parameter_files/fates_params_default.cdl + +$FATESDIR/tools/modify_fates_paramfile.py --O --fin $FATESPARAMFILE --fout $FATESPARAMFILE --var fates_cnp_prescribed_nuptake --val 1.0 --allpfts + +$FATESDIR/tools/modify_fates_paramfile.py --O --fin $FATESPARAMFILE --fout $FATESPARAMFILE --var fates_cnp_prescribed_puptake --val 1.0 --allpfts + diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdPRT2/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdPRT2/user_nl_clm index 679f025b60..196d559cb7 100644 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdPRT2/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdPRT2/user_nl_clm @@ -1,3 +1,4 @@ +fates_paramfile = '$CASEROOT/fates_params_prt2_prescribed_np.nc' fates_parteh_mode = 2 hist_fincl1 = 'FATES_L2FR','FATES_L2FR_CANOPY_REC_PF','FATES_L2FR_USTORY_REC_PF', 'FATES_NH4UPTAKE_SZPF','FATES_NO3UPTAKE_SZPF','FATES_NEFFLUX_SZPF', From d940ee014cb52f92cdc0da415084f349ab45abae Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Sat, 6 Jul 2024 13:00:34 -0600 Subject: [PATCH 220/406] Add ._r8 to hardwired constants --- src/biogeochem/VOCEmissionMod.F90 | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/biogeochem/VOCEmissionMod.F90 b/src/biogeochem/VOCEmissionMod.F90 index 9833fe3de9..7ba90fd457 100644 --- a/src/biogeochem/VOCEmissionMod.F90 +++ b/src/biogeochem/VOCEmissionMod.F90 @@ -835,13 +835,13 @@ function get_gamma_SM(btran_in) real(r8),intent(in) :: btran_in !!!------- the drought algorithm-------- - real(r8), parameter :: a1 = -7.4463 - real(r8), parameter :: b1 = 3.2552 + real(r8), parameter :: a1 = -7.4463_r8 + real(r8), parameter :: b1 = 3.2552_r8 real(r8) :: get_gamma_SM - if (btran_in >= 1.) then + if (btran_in >= 1._r8) then get_gamma_SM = 1 else - get_gamma_SM = 1/(1+b1*exp(a1*(btran_in-0.2))) + get_gamma_SM = 1._r8 / (1._r8 + b1 * exp(a1 * (btran_in - 0.2_r8))) endif end function get_gamma_SM @@ -968,9 +968,9 @@ function get_gamma_T(t_veg240_in, t_veg24_in,t_veg_in, ct1_in, ct2_in, betaT_in, topt = co1 + (co2 * (t_veg240_in-tstd0)) if ( (ivt_in == nbrdlf_dcd_brl_shrub) ) then ! boreal-deciduous-shrub ! coming from BEAR-oNS campaign willows results - Eopt = 7.9 * exp (0.217_r8 * (t_veg24_in-273.15_r8-24.0_r8)) + Eopt = 7.9_r8 * exp (0.217_r8 * (t_veg24_in - 273.15_r8 - 24.0_r8)) else if ( (ivt_in == nc3_arctic_grass ) ) then ! boreal-grass - Eopt = exp(0.12*(t_veg240_in-288.15_r8)) + Eopt = exp(0.12_r8 * (t_veg240_in - 288.15_r8)) else Eopt = Ceo_in * exp (co4 * (t_veg24_in-tstd0)) * exp(co4 * (t_veg240_in -tstd0)) endif @@ -982,13 +982,13 @@ function get_gamma_T(t_veg240_in, t_veg24_in,t_veg_in, ct1_in, ct2_in, betaT_in, x = ( (1._r8/topt) - (1._r8/(t_veg_in)) ) / ct3 ! for the boreal grass from BEAR-oNS campaign if ( (ivt_in == nc3_arctic_grass ) ) then ! boreal-grass - bet_arc_c3 = 95+9.49*exp(0.53*(288.15_r8-t_veg240_in)) - if (bet_arc_c3 .gt. 300) then - bet_arc_c3 = 300 + bet_arc_c3 = 95._r8 + 9.49_r8 * exp(0.53_r8 * (288.15_r8 - t_veg240_in)) + if (bet_arc_c3 > 300._r8) then + bet_arc_c3 = 300._r8 endif - gamma_t_LDF = Eopt * exp(bet_arc_c3*((1/303.15_r8 - 1.0_r8/(t_veg_in))/ct3)) + gamma_t_LDF = Eopt * exp(bet_arc_c3 * ((1._r8 / 303.15_r8 - 1.0_r8 / t_veg_in) / ct3)) else - gamma_t_LDF = Eopt * ( ct2_in * exp(ct1_in * x)/(ct2_in - ct1_in * (1._r8 - exp(ct2_in * x))) ) + gamma_t_LDF = Eopt * ( ct2_in * exp(ct1_in * x) / (ct2_in - ct1_in * (1._r8 - exp(ct2_in * x))) ) endif From 1356d0ba0aa4582f1b7b83ae416c5d908f4cb02a Mon Sep 17 00:00:00 2001 From: "Danny M. Leung" Date: Mon, 8 Jul 2024 17:04:15 -0600 Subject: [PATCH 221/406] dmleung add a little tuning to reduce the sensitivity of dust emission to clay fraction. dmleung also corrected the output of soil surface friction velocity (wnd_frc_slt). Coded on 5 Jul 2024 and committed on 8 Jul 2024 --- src/biogeochem/DUSTMod.F90 | 6 +++++- src/biogeophys/SoilStateInitTimeConstMod.F90 | 15 ++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index b23a26b429..197ede35fd 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -693,8 +693,9 @@ subroutine DustEmission (bounds, & frc_thr_rghn_fct(p) = frc_thr_rgh_fct ! save and output hybrid drag partition factor else ! for lnd_frc_mbl=0, do not change friction velocity and assume drag partition factor = 0 - wnd_frc_slt = fv(p) ! The value here is not important since once lnd_frc_mbl(p) <= 0.0_r8 there will be no emission. + !wnd_frc_slt = fv(p) ! The value here is not important since once lnd_frc_mbl(p) <= 0.0_r8 there will be no emission. frc_thr_rghn_fct(p) = 0.0_r8 ! When LAI > 1, the drag partition effect is zero. dmleung 16 Feb 2024. + wnd_frc_slt = fv(p) * frc_thr_rghn_fct(p) ! The value here is not important since once lnd_frc_mbl(p) <= 0.0_r8 there will be no emission. dmleung added 5 Jul 2024 end if !########## end of drag partition effect ####################################################### @@ -727,7 +728,10 @@ subroutine DustEmission (bounds, & !################ uncomment the below block if want to use Leung's scheme ################### !flx_mss_vrt_dst_ttl(p) = dst_emiss_coeff(p) * mss_frc_cly_vld(c) * forc_rho(c) * ((wnd_frc_slt**2.0_r8 - wnd_frc_thr_slt_it**2.0_r8) / wnd_frc_thr_slt_std) * (wnd_frc_slt / wnd_frc_thr_slt_it)**frag_expt ! Leung et al. (2022) uses Kok et al. (2014) dust emission euqation for emission flux + + ! dmleung: instead of using mss_frc_cly_vld(c) with a range of [0,0.2] , which makes dust too sensitive to input clay surface dataset), for now use 0.1 + mss_frc_cly_vld(c) * 0.1 / 0.20 with a range of [0.1,0.2]. So, instead of scaling dust emission to 1/20 times for El Djouf (western Sahara) because of its 1 % clay fraction, scale its emission to 1/2 times. This reduces the sensitivity of dust emission to the clay input dataset. In particular, because dust emission is a small-scale process and the grid-averaged clay from the new WISE surface dataset over El Djouf is 1 %, much lower than the former FAO estimation of 5 %, dmleung is not sure if the clay value of 1 % suppresses too much of El Djouf's small-scale dust emission process. Similar to Charlie Zender's feeling suspicious about the soil texture datasets (or the dust emission schemes' sandblasting process), dmleung feels the same and for now decides to still allow dust emission to weakly scale with clay fraction, however limiting the scaling factor to 0.1-0.2. See modification in SoilStateInitTimeConst.F90. dmleung 5 Jul 2024 flx_mss_vrt_dst_ttl(p) = dst_emiss_coeff(p) * mss_frc_cly_vld(c) * forc_rho(c) * ((wnd_frc_slt**2.0_r8 - wnd_frc_thr_slt_it**2.0_r8) / wnd_frc_thr_slt_it) * (wnd_frc_slt / wnd_frc_thr_slt_it)**frag_expt ! Leung et al. (2022) uses Kok et al. (2014) dust emission euqation for emission flux + !flx_mss_vrt_dst_ttl(p) = dst_emiss_coeff(p) * ( 0.1_r8 + mss_frc_cly_vld(c) * 0.1_r8 / 0.20_r8 ) * forc_rho(c) * ((wnd_frc_slt**2.0_r8 - wnd_frc_thr_slt_it**2.0_r8) / wnd_frc_thr_slt_it) * (wnd_frc_slt / wnd_frc_thr_slt_it)**frag_expt ! account for bare soil fraction, frozen soil fraction, and apply global tuning parameter (Kok et al. 2014) flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) * lnd_frc_mbl(p) * C_tune * liqfrac diff --git a/src/biogeophys/SoilStateInitTimeConstMod.F90 b/src/biogeophys/SoilStateInitTimeConstMod.F90 index 3c8945e9d2..d0315e76ff 100644 --- a/src/biogeophys/SoilStateInitTimeConstMod.F90 +++ b/src/biogeophys/SoilStateInitTimeConstMod.F90 @@ -697,7 +697,16 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) end do ! -------------------------------------------------------------------- - ! Initialize threshold soil moisture and mass fracion of clay limited to 0.20 + ! Initialize threshold soil moisture, and mass fraction of clay as + ! scaling coefficient of dust emission flux (kg/m2/s) in DUSTMod.F90 + ! dmleung modified 5 Jul 2024, reducing sensitivity of dust emission + ! flux to clay fraction. + ! Also, for threshold soil moisture, dmleung followed Zender (2003) + ! DEAD scheme to again avoid dust flux being too sensitive to the choice + ! of clay dataset. This is different from what Leung et al. (2023) + ! intended to do. + ! Both decisions are based on tuning preference and could subject to + ! future changes. dmleung 5 Jul 2024 ! -------------------------------------------------------------------- do c = begc,endc @@ -709,7 +718,11 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) ! There are different options: E.g., a = 1 gives the first line below. E.g., Kok et al. (2014a, b) chose to use a = 1. A second choice is proposed by Charlie Zender (2003a): a = 1/clay3d, which gives the second line below. !soilstate_inst%gwc_thr_col(c) = 0.01_r8*(0.17_r8*clay3d(g,1) + 0.0014_r8*clay3d(g,1)*clay3d(g,1)) ! Jasper Kok et al. (2014) modified the equation of soil moisture effect for dust emissions using a tuning factor of a = 1. 0.01 is to convert the threshold gravimetric soil moisture from % to fraction. Danny M. Leung et al. (2023) followed K14 and used this equation for offline dust emission calculations using MERRA-2 met fields. Later, Leung et al. (2024) changed to a = 2 for CESM2 simulations. However, Danny Leung later (Dec 2023) decided to revert to Zender's choice and use a = 1 / (clay3d), which overall encourages more dust emissions from seriamid and more marginal dust sources. This note should probably be on the CLM tech note since dmleung has made a different decision for CESM than his own paper. dmleung added this detailed comment on 19 Feb 2024; edited 24 May 2024. soilstate_inst%gwc_thr_col(c) = 0.17_r8 + 0.14_r8 * clay3d(g,1) * 0.01_r8 ! This was the original equation with a = 1 / (%clay) being the tuning factor for soil moisture effect in Zender's dust emission scheme. Danny M. Leung decided (Dec, 2023) that the Leung et al. (2023) dust emission scheme in the CESM will use Zender's tuning of a = 1 / (%clay), which overall encourages more dust emissions from seriamid and more marginal dust sources. Another advantage of using this tuning factor instead of a = 1 is that the dust emission threshold is linearly dependent on the clay fraction instead of parabolically dependent on clay fraction as in the above line. This means that dust emission becomes a little less sensitive to clay content (soil texture). 0.17 and 0.14 are fitting coefficients in Fecan et al. (1999), and 0.01 is used to convert surface clay fraction from percentage to fraction. dmleung added this detailed comment on 19 Feb 2024. + + ! dust emission scaling factor dependent on clay fraction soilstate_inst%mss_frc_cly_vld_col(c) = min(clay3d(g,1) * 0.01_r8, 0.20_r8) + soilstate_inst%mss_frc_cly_vld_col(c) = 0.1_r8 + soilstate_inst%mss_frc_cly_vld_col(c) * 0.1_r8 / 0.20_r8 ! dmleung added this line to reduce the sensitivity of dust emission flux to clay fraction in DUSTMod. 5 Jul 2024 + end do ! -------------------------------------------------------------------- From cb7376eb55987f0785dd9a857c2fd24a720e6017 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 9 Jul 2024 01:25:57 -0600 Subject: [PATCH 222/406] Remove whitespace at end of lines --- src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf index 467d00de59..c811cab5f5 100644 --- a/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf +++ b/src/drv_test/shr_dust_emis_test/test_shr_dust_emis.pf @@ -59,7 +59,7 @@ contains @assertFalse(not_init) end subroutine check_when_initialized_runs - + @Test subroutine check_dust_emis(this) ! Test that the dust_emis logical functions work as expected @@ -75,7 +75,6 @@ contains end subroutine check_dust_emis - @Test subroutine check_zender_soil(this) ! Test that the dust_emis_Zender logical functions work as expected @@ -91,7 +90,6 @@ contains end subroutine check_zender_soil - @Test subroutine check_options(this) ! Test that the check_options subroutine catches errors that should die @@ -110,5 +108,5 @@ contains @assertExceptionRaised(endrun_msg('(check_options_finish_init) ERROR: zender_soil_erod_source can only be lnd or atm')) end subroutine check_options - + end module test_shr_dust_emis From 237f84815c06b9ea86eb00f96b29cc35abd044c8 Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Wed, 10 Jul 2024 15:11:49 -0600 Subject: [PATCH 223/406] Revert "Add ._r8 to hardwired constants" This reverts commit d940ee014cb52f92cdc0da415084f349ab45abae because answers were changing from the baseline in tests ERS_D_Ld15.f45_f45_mg37.I2000Clm50FatesRs.derecho_intel.clm-FatesColdTwoStream ERS_D_Ld15.f45_f45_mg37.I2000Clm50FatesRs.izumi_nag.clm-FatesColdTwoStream --- src/biogeochem/VOCEmissionMod.F90 | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/biogeochem/VOCEmissionMod.F90 b/src/biogeochem/VOCEmissionMod.F90 index 7ba90fd457..9833fe3de9 100644 --- a/src/biogeochem/VOCEmissionMod.F90 +++ b/src/biogeochem/VOCEmissionMod.F90 @@ -835,13 +835,13 @@ function get_gamma_SM(btran_in) real(r8),intent(in) :: btran_in !!!------- the drought algorithm-------- - real(r8), parameter :: a1 = -7.4463_r8 - real(r8), parameter :: b1 = 3.2552_r8 + real(r8), parameter :: a1 = -7.4463 + real(r8), parameter :: b1 = 3.2552 real(r8) :: get_gamma_SM - if (btran_in >= 1._r8) then + if (btran_in >= 1.) then get_gamma_SM = 1 else - get_gamma_SM = 1._r8 / (1._r8 + b1 * exp(a1 * (btran_in - 0.2_r8))) + get_gamma_SM = 1/(1+b1*exp(a1*(btran_in-0.2))) endif end function get_gamma_SM @@ -968,9 +968,9 @@ function get_gamma_T(t_veg240_in, t_veg24_in,t_veg_in, ct1_in, ct2_in, betaT_in, topt = co1 + (co2 * (t_veg240_in-tstd0)) if ( (ivt_in == nbrdlf_dcd_brl_shrub) ) then ! boreal-deciduous-shrub ! coming from BEAR-oNS campaign willows results - Eopt = 7.9_r8 * exp (0.217_r8 * (t_veg24_in - 273.15_r8 - 24.0_r8)) + Eopt = 7.9 * exp (0.217_r8 * (t_veg24_in-273.15_r8-24.0_r8)) else if ( (ivt_in == nc3_arctic_grass ) ) then ! boreal-grass - Eopt = exp(0.12_r8 * (t_veg240_in - 288.15_r8)) + Eopt = exp(0.12*(t_veg240_in-288.15_r8)) else Eopt = Ceo_in * exp (co4 * (t_veg24_in-tstd0)) * exp(co4 * (t_veg240_in -tstd0)) endif @@ -982,13 +982,13 @@ function get_gamma_T(t_veg240_in, t_veg24_in,t_veg_in, ct1_in, ct2_in, betaT_in, x = ( (1._r8/topt) - (1._r8/(t_veg_in)) ) / ct3 ! for the boreal grass from BEAR-oNS campaign if ( (ivt_in == nc3_arctic_grass ) ) then ! boreal-grass - bet_arc_c3 = 95._r8 + 9.49_r8 * exp(0.53_r8 * (288.15_r8 - t_veg240_in)) - if (bet_arc_c3 > 300._r8) then - bet_arc_c3 = 300._r8 + bet_arc_c3 = 95+9.49*exp(0.53*(288.15_r8-t_veg240_in)) + if (bet_arc_c3 .gt. 300) then + bet_arc_c3 = 300 endif - gamma_t_LDF = Eopt * exp(bet_arc_c3 * ((1._r8 / 303.15_r8 - 1.0_r8 / t_veg_in) / ct3)) + gamma_t_LDF = Eopt * exp(bet_arc_c3*((1/303.15_r8 - 1.0_r8/(t_veg_in))/ct3)) else - gamma_t_LDF = Eopt * ( ct2_in * exp(ct1_in * x) / (ct2_in - ct1_in * (1._r8 - exp(ct2_in * x))) ) + gamma_t_LDF = Eopt * ( ct2_in * exp(ct1_in * x)/(ct2_in - ct1_in * (1._r8 - exp(ct2_in * x))) ) endif From 17fb3d8ee57c94c757405b8f77afcf97de5b927f Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 11 Jul 2024 01:27:49 -0600 Subject: [PATCH 224/406] Changes to remove dust_emis namelist items from CTSM fortran code and use shr_dust_emis_mod from CMEPS --- components/cmeps | 2 +- src/biogeochem/DustEmisBase.F90 | 1 - src/biogeochem/DustEmisFactory.F90 | 14 ++++++-------- .../DustEmis_test/test_DustEmisZender2003.pf | 2 ++ src/cpl/nuopc/lnd_import_export.F90 | 5 +++++ .../share_esmf/ZenderSoilErodStreamType.F90 | 18 ++++++------------ src/main/clm_varctl.F90 | 5 ----- src/main/controlMod.F90 | 6 ------ 8 files changed, 20 insertions(+), 33 deletions(-) diff --git a/components/cmeps b/components/cmeps index 6384ff4e4a..e1335d32fc 160000 --- a/components/cmeps +++ b/components/cmeps @@ -1 +1 @@ -Subproject commit 6384ff4e4a6bc82a678f9419a43ffbd5d53ac209 +Subproject commit e1335d32fca0e5571a0d5a7edb0d1473266b7923 diff --git a/src/biogeochem/DustEmisBase.F90 b/src/biogeochem/DustEmisBase.F90 index c5e4260634..47f2f32688 100644 --- a/src/biogeochem/DustEmisBase.F90 +++ b/src/biogeochem/DustEmisBase.F90 @@ -31,7 +31,6 @@ module DustEmisBase use LandunitType , only : lun use ColumnType , only : col use PatchType , only : patch - use clm_varctl , only : dust_emis_method ! ! !PUBLIC TYPES implicit none diff --git a/src/biogeochem/DustEmisFactory.F90 b/src/biogeochem/DustEmisFactory.F90 index 371e77d6dc..24162fde24 100644 --- a/src/biogeochem/DustEmisFactory.F90 +++ b/src/biogeochem/DustEmisFactory.F90 @@ -28,9 +28,9 @@ function create_dust_emissions(bounds, NLFilename) result(dust_emis) !--------------------------------------------------------------------------- use DustEmisBase , only : dust_emis_base_type use DustEmisZender2003, only : dust_emis_zender2003_type - use clm_varctl , only : dust_emis_method use decompMod , only : bounds_type use shr_kind_mod , only : CL => shr_kind_cl + use shr_dust_emis_mod , only : is_dust_emis_zender, is_dust_emis_leung implicit none ! Arguments class(dust_emis_base_type), allocatable :: dust_emis @@ -38,20 +38,18 @@ function create_dust_emissions(bounds, NLFilename) result(dust_emis) character(len=*), intent(in) :: NLFilename ! Local variables - select case ( trim(dust_emis_method) ) - - case( "Zender_2003" ) + if ( is_dust_emis_zender() )then allocate(dust_emis, source=dust_emis_zender2003_type() ) ! This will be added when the Leung2023 comes in - !case( "Leung_2023" ) + !else if ( is_dust_emis_leung() ) ! allocate(dust_emis, source=dust_emis_zender2003_type() ) - case default - write(iulog,*) 'ERROR: unknown dust_emis_method: ', dust_emis_method, & + else + write(iulog,*) 'ERROR: unknown dust_emis_method: ', & errMsg(sourcefile, __LINE__) call endrun( "Unrecognized dust_emis_method" ) - end select + end if call dust_emis%Init(bounds, NLFilename) diff --git a/src/biogeochem/test/DustEmis_test/test_DustEmisZender2003.pf b/src/biogeochem/test/DustEmis_test/test_DustEmisZender2003.pf index 8641b8cceb..0289cadabc 100644 --- a/src/biogeochem/test/DustEmis_test/test_DustEmisZender2003.pf +++ b/src/biogeochem/test/DustEmis_test/test_DustEmisZender2003.pf @@ -9,6 +9,7 @@ module test_DustEmisZender2003 use DustEmisZender2003 use shr_kind_mod , only : r8 => shr_kind_r8 use DustEmisFactory, only : create_dust_emissions + use shr_dust_emis_mod, only : dust_emis_set_options implicit none @@ -37,6 +38,7 @@ contains call this%input%setUp() ! Create the dust emission object last + call dust_emis_set_options( 'Zender_2003', 'atm') allocate(this%dust_emis, source = create_dust_emissions(bounds, NLFilename)) end subroutine setUp diff --git a/src/cpl/nuopc/lnd_import_export.F90 b/src/cpl/nuopc/lnd_import_export.F90 index b9966f81e9..624590b9a6 100644 --- a/src/cpl/nuopc/lnd_import_export.F90 +++ b/src/cpl/nuopc/lnd_import_export.F90 @@ -160,9 +160,11 @@ subroutine advertise_fields(gcomp, flds_scalar_name, glc_present, cism_evolve, r use shr_carma_mod , only : shr_carma_readnl use shr_ndep_mod , only : shr_ndep_readnl + use shr_dust_emis_mod , only : shr_dust_emis_readnl use shr_fire_emis_mod , only : shr_fire_emis_readnl use clm_varctl , only : ndep_from_cpl use controlMod , only : NLFilename + use spmdMod , only : mpicom ! input/output variables type(ESMF_GridComp) :: gcomp @@ -237,6 +239,9 @@ subroutine advertise_fields(gcomp, flds_scalar_name, glc_present, cism_evolve, r ! The following namelist reads should always be called regardless of the send_to_atm value + ! Dust emissions from land to atmosphere + call shr_dust_emis_readnl( mpicom, "drv_flds_in") + ! Dry Deposition velocities from land - ALSO initialize drydep here call shr_drydep_readnl("drv_flds_in", drydep_nflds) diff --git a/src/cpl/share_esmf/ZenderSoilErodStreamType.F90 b/src/cpl/share_esmf/ZenderSoilErodStreamType.F90 index 194e022132..32e776063b 100644 --- a/src/cpl/share_esmf/ZenderSoilErodStreamType.F90 +++ b/src/cpl/share_esmf/ZenderSoilErodStreamType.F90 @@ -42,7 +42,6 @@ module ZenderSoilErodStreamType ! ! PRIVATE DATA: type, private :: streamcontrol_type - character(len=CL) :: zender_soil_erod_source ! if calculed in lnd or atm character(len=CL) :: stream_fldFileName_zendersoilerod ! data Filename character(len=CL) :: stream_meshfile_zendersoilerod ! mesh Filename character(len=CL) :: zendersoilerod_mapalgo ! map algo @@ -179,7 +178,7 @@ logical function UseStreams(this) ! file is being used with it ! ! !USES: - use clm_varctl, only : dust_emis_method + use shr_dust_emis_mod, only : is_dust_emis_zender, is_zender_soil_erod_from_land ! ! !ARGUMENTS: implicit none @@ -189,7 +188,7 @@ logical function UseStreams(this) if ( .not. control%namelist_set )then call endrun(msg=' ERROR namelist NOT set before being used'//errMsg(sourcefile, __LINE__)) end if - if ( (trim(dust_emis_method) == 'Zender_2003') .and. (control%zender_soil_erod_source == "lnd") )then + if ( is_dust_emis_zender() .and. is_zender_soil_erod_from_land() )then UseStreams = .true. else UseStreams = .false. @@ -289,6 +288,7 @@ subroutine ReadNML(this, bounds, NLFilename) use shr_nl_mod , only : shr_nl_find_group_name use shr_log_mod , only : errMsg => shr_log_errMsg use shr_mpi_mod , only : shr_mpi_bcast + use shr_dust_emis_mod, only : is_zender_soil_erod_from_land ! ! arguments implicit none @@ -304,14 +304,13 @@ subroutine ReadNML(this, bounds, NLFilename) character(len=CL) :: stream_meshfile_zendersoilerod = ' ' character(len=CL) :: zendersoilerod_mapalgo = ' ' character(len=CL) :: tmp_file_array(3) - character(len=3) :: zender_soil_erod_source = 'atm' character(len=*), parameter :: namelist_name = 'zendersoilerod' ! MUST agree with group name in namelist definition to read. character(len=*), parameter :: subName = "('zendersoilerod::ReadNML')" !----------------------------------------------------------------------- namelist /zendersoilerod/ & ! MUST agree with namelist_name above zendersoilerod_mapalgo, stream_fldFileName_zendersoilerod, & - stream_meshfile_zendersoilerod, zender_soil_erod_source + stream_meshfile_zendersoilerod ! Default values for namelist @@ -330,12 +329,11 @@ subroutine ReadNML(this, bounds, NLFilename) close(nu_nml) endif - call shr_mpi_bcast(zender_soil_erod_source , mpicom) call shr_mpi_bcast(zendersoilerod_mapalgo , mpicom) call shr_mpi_bcast(stream_fldFileName_zendersoilerod , mpicom) call shr_mpi_bcast(stream_meshfile_zendersoilerod , mpicom) - if (masterproc .and. (zender_soil_erod_source == "lnd") ) then + if (masterproc .and. is_zender_soil_erod_from_land() ) then write(iulog,*) ' ' write(iulog,*) namelist_name, ' stream settings:' write(iulog,*) ' stream_fldFileName_zendersoilerod = ',stream_fldFileName_zendersoilerod @@ -343,13 +341,10 @@ subroutine ReadNML(this, bounds, NLFilename) write(iulog,*) ' zendersoilerod_mapalgo = ',zendersoilerod_mapalgo endif - if ( (trim(zender_soil_erod_source) /= 'atm') .and. (trim(zender_soil_erod_source) /= 'lnd') )then - call endrun(msg=' ERROR zender_soil_erod_source must be either lnd or atm and is NOT'//errMsg(sourcefile, __LINE__)) - end if tmp_file_array(1) = stream_fldFileName_zendersoilerod tmp_file_array(2) = stream_meshfile_zendersoilerod tmp_file_array(3) = zendersoilerod_mapalgo - if ( trim(zender_soil_erod_source) == 'lnd' )then + if ( is_zender_soil_erod_from_land() ) then do i = 1, size(tmp_file_array) if ( len_trim(tmp_file_array(i)) == 0 )then call endrun(msg=' ERROR '//trim(tmp_file_array(i))//' must be set when Zender_2003 is being used and zender_soil_erod_source is lnd'//errMsg(sourcefile, __LINE__)) @@ -365,7 +360,6 @@ subroutine ReadNML(this, bounds, NLFilename) this%stream_fldFileName_zendersoilerod = stream_fldFileName_zendersoilerod this%stream_meshfile_zendersoilerod = stream_meshfile_zendersoilerod this%zendersoilerod_mapalgo = zendersoilerod_mapalgo - this%zender_soil_erod_source = zender_soil_erod_source this%namelist_set = .true. diff --git a/src/main/clm_varctl.F90 b/src/main/clm_varctl.F90 index cbfcbfd8ea..97c818af94 100644 --- a/src/main/clm_varctl.F90 +++ b/src/main/clm_varctl.F90 @@ -276,11 +276,6 @@ module clm_varctl ! option to activate OC in snow in SNICAR logical, public :: do_sno_oc = .false. ! control to include organic carbon (OC) in snow - !---------------------------------------------------------- - ! DUST emission method - !---------------------------------------------------------- - character(len=25), public :: dust_emis_method = 'Zender_2003' ! Dust emisison method to use: Zender_2003 or Leung_2023 - !---------------------------------------------------------- ! C isotopes !---------------------------------------------------------- diff --git a/src/main/controlMod.F90 b/src/main/controlMod.F90 index 4773cc6921..c92732806c 100644 --- a/src/main/controlMod.F90 +++ b/src/main/controlMod.F90 @@ -207,12 +207,6 @@ subroutine control_init(dtime) for_testing_no_crop_seed_replenishment, & z0param_method, use_z0m_snowmelt - ! NOTE: EBK 02/26/2024: dust_emis_method is here in CTSM temporarily until it's moved to CMEPS - ! See: https://github.com/ESCOMP/CMEPS/pull/429 - ! Normally this should also need error checking and a broadcast, but since - ! there is only one hardcoded option right now that is unneeded. - namelist /clm_inparm/ dust_emis_method - ! vertical soil mixing variables namelist /clm_inparm/ & som_adv_flux, max_depth_cryoturb From 7dd9af0195592c2a2d9c49fc6767f53ea1bff128 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 11 Jul 2024 02:48:19 -0600 Subject: [PATCH 225/406] Update test number --- bld/unit_testers/build-namelist_test.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index a24b308620..cf5aedef9b 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -163,7 +163,7 @@ sub cat_and_create_namelistinfile { # # Figure out number of tests that will run # -my $ntests = 3259; +my $ntests = 3256; if ( defined($opts{'compare'}) ) { $ntests += 2001; From 976347ddcd0e158a54483b72f7a48d8825c96f37 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 11 Jul 2024 13:16:32 -0600 Subject: [PATCH 226/406] Update cmeps and ccs_config to versions used in #2590 which allows cases to build --- .gitmodules | 4 ++-- ccs_config | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index b48068c9f6..ee06997ce5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -36,7 +36,7 @@ fxDONOTUSEurl = https://github.com/ESCOMP/mizuRoute [submodule "ccs_config"] path = ccs_config url = https://github.com/ESMCI/ccs_config_cesm.git -fxtag = ccs_config_cesm0.0.106 +fxtag = ccs_config_cesm1.0.0 fxrequired = ToplevelRequired fxDONOTUSEurl = https://github.com/ESMCI/ccs_config_cesm.git @@ -50,7 +50,7 @@ fxDONOTUSEurl = https://github.com/ESMCI/cime [submodule "cmeps"] path = components/cmeps url = https://github.com/ESCOMP/CMEPS.git -fxtag = cmeps0.14.73 +fxtag = cmeps0.14.77 fxrequired = ToplevelRequired fxDONOTUSEurl = https://github.com/ESCOMP/CMEPS.git diff --git a/ccs_config b/ccs_config index 2ff978f92a..69a958581e 160000 --- a/ccs_config +++ b/ccs_config @@ -1 +1 @@ -Subproject commit 2ff978f92a5ac9a6ab243e5c14d06a7e2d2f5799 +Subproject commit 69a958581ecd2d32ee9cb1c38bcd3847b8b920bf From 170b3a518701e7fb0494423bcc9ef11d90e87f62 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 11 Jul 2024 13:17:21 -0600 Subject: [PATCH 227/406] Update cmeps and ccs_config to versions used in #2590 which allows cases to build --- components/cmeps | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/cmeps b/components/cmeps index e1335d32fc..47fb4e633a 160000 --- a/components/cmeps +++ b/components/cmeps @@ -1 +1 @@ -Subproject commit e1335d32fca0e5571a0d5a7edb0d1473266b7923 +Subproject commit 47fb4e633a76ec6d60969b1af751f90790387246 From ba28bea5ca49e44e48d3739647baae04915d805f Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 11 Jul 2024 13:20:10 -0600 Subject: [PATCH 228/406] Update number of tests and comment out the whole values section as it fails without at least one thing set --- bld/unit_testers/build-namelist_test.pl | 2 +- cime_config/config_component.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index cf5aedef9b..5abb381c16 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -166,7 +166,7 @@ sub cat_and_create_namelistinfile { my $ntests = 3256; if ( defined($opts{'compare'}) ) { - $ntests += 2001; + $ntests += 1965; } plan( tests=>$ntests ); diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index 01990e162f..7500668905 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -166,11 +166,11 @@ logical TRUE,FALSE TRUE + + --> run_component_cpl env_run.xml If CTSM will set the dust settings in drv_flds_in (TRUE), or if ATM (i.e. CAM) will - DO NOT EDIT (set by compset name) From 7994c82a4427860494c79611fc32fa0efecadeb3 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 11 Jul 2024 16:29:12 -0600 Subject: [PATCH 229/406] Save crop date variables in testmods more frequently. --- cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm | 7 +++++-- .../testdefs/testmods_dirs/clm/cropMonthOutput/user_nl_clm | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm index a368e97593..aea8e39e6c 100644 --- a/cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm @@ -9,9 +9,12 @@ hist_fincl2 += 'DYN_COL_SOIL_ADJUSTMENTS_C' -! Annual instantaneous crop variables (including per-sowing/per-harvest axes), per PFT. +! Instantaneous crop variables (including per-sowing/per-harvest axes), per PFT. +! Note that, under normal circumstances, these should only be saved annually. +! That's needed for the mxsowings and mxharvests axes to make sense. +! However, for testing purposes, it makes sense to save more frequently. hist_fincl3 = 'SDATES', 'SDATES_PERHARV', 'SYEARS_PERHARV', 'HDATES', 'GRAINC_TO_FOOD_PERHARV', 'GRAINC_TO_FOOD_ANN', 'GRAINN_TO_FOOD_PERHARV', 'GRAINN_TO_FOOD_ANN', 'GRAINC_TO_SEED_PERHARV', 'GRAINC_TO_SEED_ANN', 'GRAINN_TO_SEED_PERHARV', 'GRAINN_TO_SEED_ANN', 'HDATES', 'GDDHARV_PERHARV', 'GDDACCUM_PERHARV', 'HUI_PERHARV', 'SOWING_REASON_PERHARV', 'HARVEST_REASON_PERHARV', 'SWINDOW_STARTS', 'SWINDOW_ENDS', 'GDD20_BASELINE', 'GDD20_SEASON_START', 'GDD20_SEASON_END' -hist_nhtfrq(3) = 17520 +hist_nhtfrq(3) = -24 hist_mfilt(3) = 1 hist_type1d_pertape(3) = 'PFTS' hist_dov2xy(3) = .false. diff --git a/cime_config/testdefs/testmods_dirs/clm/cropMonthOutput/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/cropMonthOutput/user_nl_clm index 8f779ed011..18220de5ef 100644 --- a/cime_config/testdefs/testmods_dirs/clm/cropMonthOutput/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/cropMonthOutput/user_nl_clm @@ -1,4 +1,4 @@ - hist_nhtfrq = 0,-240,17520 + hist_nhtfrq = 0,-240,0 hist_mfilt = 1,1,1 ! NOTE slevis (2024/2/23) Adding option for tests to pass. In the long term From 014800df4825a909f3a0297951fcd4f8f05f232b Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 11 Jul 2024 16:32:25 -0600 Subject: [PATCH 230/406] Add tweak_latlons.py. --- python/ctsm/crop_calendars/tweak_latlons.py | 168 ++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 python/ctsm/crop_calendars/tweak_latlons.py diff --git a/python/ctsm/crop_calendars/tweak_latlons.py b/python/ctsm/crop_calendars/tweak_latlons.py new file mode 100644 index 0000000000..de35ced0d1 --- /dev/null +++ b/python/ctsm/crop_calendars/tweak_latlons.py @@ -0,0 +1,168 @@ +# %% +import numpy as np +import xarray as xr +import os +import sys +from netCDF4 import Dataset +import contextlib + +# -- add python/ctsm to path (needed if we want to run this stand-alone) +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir) +sys.path.insert(1, _CTSM_PYTHON) +# pylint: disable=wrong-import-position +from ctsm.mesh_maker import main as mesh_maker + +topdir = "/glade/campaign/cesm/cesmdata/inputdata/lnd/clm2/cropdata/calendars/processed/" +file_list_in = [ + "swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc", + "swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc", + "gdds_20230829_161011.nc", +] +file_mesh_in = ( + "/glade/campaign/cesm/cesmdata/inputdata/share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc" +) + +file_list_out = [] +coord_list = ["lat", "lon"] + +# %% + + +def apply_tweak(ds, coord_str, tweak=1e-6): + # Apply tweak + da = ds[coord_str] + coord2 = da.values + coord2 += tweak + while np.any(coord2 == da.values): + tweak *= 10 + coord2 = da.values + coord2 += tweak + coord_tweak = np.full_like(coord2, tweak) + + # Ensure that no value is above maximum in input data. This is needed for mesh_maker to work. + max_coord = np.max(da.values) + where_toohigh = np.where(coord2 > max_coord) + Ntoohigh = len(where_toohigh[0]) + if Ntoohigh != 1: + raise RuntimeError(f"Expected 1 coordinate value too high; got {Ntoohigh}") + coord2[where_toohigh] = max_coord + coord_tweak[where_toohigh] = max_coord + + # Convert to DataArray + new_coords_dict = {coord_str: coord2} + da2 = xr.DataArray( + data=coord2, + coords=new_coords_dict, + dims=da.dims, + attrs=da.attrs, + ) + + # Replace coordinate in dataset + ds[coord_str] = da2 + + # Add a variable with the amount of the tweak + tweak_attrs = {} + if "standard_name" in da: + coord_name = da.attrs["standard_name"] + else: + coord_name = da.attrs["long_name"].replace("coordinate_", "") + tweak_attrs["standard_name"] = coord_name + "_tweak" + tweak_attrs[ + "long_name" + ] = f"Amount {coord_name} was shifted to avoid ambiguous nearest neighbors" + tweak_attrs["units"] = da.attrs["units"] + da_tweak = xr.DataArray( + data=coord_tweak, + coords=new_coords_dict, + dims=da.dims, + attrs=tweak_attrs, + ) + tweak_name = coord_str + "_tweak" + ds[tweak_name] = da_tweak + + return ds + + +# %% Apply tweak to all files + +for filename in file_list_in: + file_in = os.path.join(topdir, filename) + ds = xr.open_dataset(file_in) + + for coord in coord_list: + ds = apply_tweak(ds, coord, tweak=1e-6) + + # Set up for save + file_out = file_in.replace(".nc", ".tweaked_latlons.nc") + with Dataset(file_in, "r") as netcdf_file: + netcdf_format = netcdf_file.data_model + + # Save + print(f"Saving {file_out}") + ds.to_netcdf(file_out, format=netcdf_format) + file_list_out.append(file_out) +print("Done") + + +# %% Ensure all files got the same tweaks + +ds = xr.open_dataset(file_list_out[0]) + +for filename in file_list_out[1:]: + ds2 = xr.open_dataset(filename) + for coord in coord_list: + # Ensure that coordinates are the same + var = coord + assert np.array_equal(ds[var].values, ds2[var].values) + # Ensure that tweaks were the same + var = coord + "_tweak" + assert np.array_equal(ds[var].values, ds2[var].values) +print("All good!") + + +# %% Save new mesh file + +outfile_name = os.path.basename(file_mesh_in) +outfile_name = outfile_name.replace(".nc", ".tweaked_latlons.nc") +outdir = os.path.dirname(file_list_out[0]) +file_mesh_out = os.path.join(outdir, outfile_name) + +@contextlib.contextmanager +def redirect_argv(arglist): + argv_tmp = sys.argv[:] + sys.argv=arglist + yield + sys.argv = argv_tmp + + +mesh_maker_args = [ + "mesh_maker", + "--input", + file_list_out[0], + "--output", + file_mesh_out, + "--lat", + "lat", + "--lon", + "lon", + "--overwrite", +] +print(f"Saving {file_mesh_out}...") +with redirect_argv(mesh_maker_args): + mesh_maker() + +# Change format, if needed +with Dataset(file_mesh_in, "r") as netcdf_file: + netcdf_format_in = netcdf_file.data_model +with Dataset(file_mesh_out, "r") as netcdf_file: + netcdf_format_out = netcdf_file.data_model +if netcdf_format_in != netcdf_format_out: + file_mesh_out_tmp = file_mesh_out + ".tmp" + os.rename(file_mesh_out, file_mesh_out_tmp) + ds = xr.open_dataset(file_mesh_out_tmp) + ds.to_netcdf(file_mesh_out, format=netcdf_format_in) + os.remove(file_mesh_out_tmp) + + +print("Done") +# %% From 4c1d66f88985340b2b95dc909b55587475d75514 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 11 Jul 2024 16:37:20 -0600 Subject: [PATCH 231/406] mesh_type.py: Handle a broadcast error. --- python/ctsm/site_and_regional/mesh_type.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/python/ctsm/site_and_regional/mesh_type.py b/python/ctsm/site_and_regional/mesh_type.py index be785e745d..0260f1c816 100644 --- a/python/ctsm/site_and_regional/mesh_type.py +++ b/python/ctsm/site_and_regional/mesh_type.py @@ -235,8 +235,16 @@ def create_2d_coords(self): lons_size = self.center_lons.size # -- convert center points from 1d to 2d - self.center_lat2d = np.broadcast_to(self.center_lats[:], (lons_size, lats_size)) - self.center_lon2d = np.broadcast_to(self.center_lons[:], (lons_size, lats_size)) + try: + self.center_lat2d = np.broadcast_to(self.center_lats[:], (lons_size, lats_size)) + self.center_lon2d = np.broadcast_to(self.center_lons[:], (lons_size, lats_size)) + except ValueError: + self.center_lat2d = np.broadcast_to( + np.expand_dims(self.center_lats[:], 0), (lons_size, lats_size) + ) + self.center_lon2d = np.broadcast_to( + np.expand_dims(self.center_lons[:], 1), (lons_size, lats_size) + ) elif self.lat_dims == 2: # -- 2D lats and lons dims = self.center_lons.shape From f11003ba4e751e7749ebf2ddfb7a26a73ebb04f7 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 11 Jul 2024 16:37:46 -0600 Subject: [PATCH 232/406] mesh_type.py: Improve error for lon > 360. --- python/ctsm/site_and_regional/mesh_type.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctsm/site_and_regional/mesh_type.py b/python/ctsm/site_and_regional/mesh_type.py index 0260f1c816..47c0295593 100644 --- a/python/ctsm/site_and_regional/mesh_type.py +++ b/python/ctsm/site_and_regional/mesh_type.py @@ -359,7 +359,7 @@ def calculate_corners(self, unit="degrees"): ) # Longitudes should stay within 0 to 360 if np.any(self.corner_lons > 360.0): - abort("Corners have longitudes greater than 360") + abort(f"Corners have longitudes greater than 360 (max: {np.max(self.corner_lons)})") if np.any(self.corner_lons < 0.0): logger.warning( "Corners have longitudes less than zero -- %s %s", From 231b4ac4372421a714106ec10178416dcb3cb2c6 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 11 Jul 2024 16:44:31 -0600 Subject: [PATCH 233/406] tweak_latlons.py: Add more files. --- python/ctsm/crop_calendars/tweak_latlons.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/ctsm/crop_calendars/tweak_latlons.py b/python/ctsm/crop_calendars/tweak_latlons.py index de35ced0d1..89144a80ae 100644 --- a/python/ctsm/crop_calendars/tweak_latlons.py +++ b/python/ctsm/crop_calendars/tweak_latlons.py @@ -17,6 +17,9 @@ "swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc", "swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc", "gdds_20230829_161011.nc", + "gdd20bl.copied_from.gdds_20230829_161011.v2.nc", + "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc", + "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc", ] file_mesh_in = ( "/glade/campaign/cesm/cesmdata/inputdata/share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc" From 9f186d489d9ce2d3b982cd367756dd43102262a8 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 11 Jul 2024 16:45:50 -0600 Subject: [PATCH 234/406] Use tweaked_latlon cropcal inputs. This avoids different "nearest neighbors" being chosen when using a different number of processors. See https://github.com/orgs/esmf-org/discussions/261 --- bld/namelist_files/namelist_defaults_ctsm.xml | 22 +++++++++---------- .../clm/sowingWindows/user_nl_clm | 6 ++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index ae9f9dee6a..d1f9d8545c 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1704,17 +1704,17 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c 2000 -lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc -lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc -lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc -share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc -lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc -lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc -lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc -lnd/clm2/cropdata/calendars/processed/gdd20bl.copied_from.gdds_20230829_161011.v2.nc -lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc -lnd/clm2/cropdata/calendars/processed/hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc -share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc +lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/gdd20bl.copied_from.gdds_20230829_161011.v2.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc diff --git a/cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm index 545e5f6ebd..7024e99b96 100644 --- a/cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm @@ -1,5 +1,5 @@ -stream_fldFileName_swindow_start = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc' -stream_fldFileName_swindow_end = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc' -stream_meshfile_cropcal = '$DIN_LOC_ROOT/share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc' +stream_fldFileName_swindow_start = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc' +stream_fldFileName_swindow_end = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc' +stream_meshfile_cropcal = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc' stream_year_first_cropcal_swindows = 2000 stream_year_last_cropcal_swindows = 2000 From 1377d48022c5db5a7352744b7356aa4cce1e8413 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 12 Jul 2024 10:12:26 -0600 Subject: [PATCH 235/406] Fix default stream_meshfile_cropcal. --- bld/namelist_files/namelist_defaults_ctsm.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index d1f9d8545c..3f8bc1f382 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1707,14 +1707,14 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.tweaked_latlons.nc -lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/360x720_120830_ESMFmesh_c20210507_cdf5.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/gdd20bl.copied_from.gdds_20230829_161011.v2.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.tweaked_latlons.nc -lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/360x720_120830_ESMFmesh_c20210507_cdf5.tweaked_latlons.nc From d60dc80d74609096b63c942ca41e0ca38177683b Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 12 Jul 2024 10:13:06 -0600 Subject: [PATCH 236/406] Fix reset of sowing_reason_perharv_patch. --- src/biogeochem/CNPhenologyMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index d4f486acb7..d1d3c85b21 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -1976,7 +1976,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & cnveg_state_inst%gddmaturity_thisyr(p,s) = -1._r8 crop_inst%gddaccum_thisyr_patch(p,s) = -1._r8 crop_inst%hui_thisyr_patch(p,s) = -1._r8 - crop_inst%sowing_reason_perharv_patch = -1._r8 + crop_inst%sowing_reason_perharv_patch(p,s) = -1._r8 crop_inst%harvest_reason_thisyr_patch(p,s) = -1._r8 do k = repr_grain_min, repr_grain_max cnveg_carbonflux_inst%repr_grainc_to_food_perharv_patch(p,s,k) = 0._r8 From 5c3e71fe608c73c47f8f3e9b8ac6ee4102ce63a5 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 12 Jul 2024 11:46:58 -0600 Subject: [PATCH 237/406] Remove midDecStart-RxCropCalsAdaptGGCMI test from expected fails. --- cime_config/testdefs/ExpectedTestFails.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cime_config/testdefs/ExpectedTestFails.xml b/cime_config/testdefs/ExpectedTestFails.xml index 0fac518374..03eb6a157d 100644 --- a/cime_config/testdefs/ExpectedTestFails.xml +++ b/cime_config/testdefs/ExpectedTestFails.xml @@ -170,13 +170,6 @@ - - - FAIL - #2593 - - - From dc488c77a715bba7115c92c43948b668c6c38bc0 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 12 Jul 2024 14:46:59 -0600 Subject: [PATCH 238/406] Improve interpolate_gdds.py. --- python/ctsm/crop_calendars/interpolate_gdds.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/python/ctsm/crop_calendars/interpolate_gdds.py b/python/ctsm/crop_calendars/interpolate_gdds.py index 2aa0b79997..809be98826 100755 --- a/python/ctsm/crop_calendars/interpolate_gdds.py +++ b/python/ctsm/crop_calendars/interpolate_gdds.py @@ -64,6 +64,14 @@ def _setup_process_args(): type=str, required=True, ) + parser.add_argument( + "-p", + "--variable-prefix", + help="Interpolate variables whose names start with this string", + type=str, + required=False, + default="gdd1_" + ) parser.add_argument( "--overwrite", help="If output file exists, overwrite it.", @@ -110,8 +118,12 @@ def interpolate_gdds(args): for var in ds_in: # Check variable - if "gdd1_" not in var: - raise RuntimeError(f"Unexpected variable {var} on input file") + if "lat" not in ds_in[var].dims and "lon" not in ds_in[var].dims: + print(f"Skipping variable {var} with dimensions {ds_in[var].dims}") + continue + elif args.variable_prefix not in var: + print(f"Unexpected variable {var} on input file. Skipping.") + continue if args.dry_run: continue From 3db9bf2788141d9eaea0d9f8dd331c96f7d01165 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 12 Jul 2024 15:08:03 -0600 Subject: [PATCH 239/406] tweak_latlons: Include gdd20 files. --- python/ctsm/crop_calendars/tweak_latlons.py | 105 +++++++++++++++----- 1 file changed, 80 insertions(+), 25 deletions(-) diff --git a/python/ctsm/crop_calendars/tweak_latlons.py b/python/ctsm/crop_calendars/tweak_latlons.py index 89144a80ae..0031d492e0 100644 --- a/python/ctsm/crop_calendars/tweak_latlons.py +++ b/python/ctsm/crop_calendars/tweak_latlons.py @@ -20,6 +20,8 @@ "gdd20bl.copied_from.gdds_20230829_161011.v2.nc", "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc", "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc", + "/glade/work/samrabin/cropCals_testing_20240626/gdds_20240712_114642_10x15_interpd_halfdeg.nc", + "/glade/work/samrabin/gdd20_baselines/gswp3.10x15_interpd_halfdeg.1980-2009.nc", ] file_mesh_in = ( "/glade/campaign/cesm/cesmdata/inputdata/share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc" @@ -27,19 +29,43 @@ file_list_out = [] coord_list = ["lat", "lon"] +COORD_DATATYPE = np.float64 -# %% - +# %% Define functions -def apply_tweak(ds, coord_str, tweak=1e-6): - # Apply tweak - da = ds[coord_str] - coord2 = da.values +def get_ds(topdir, file_in): + if not os.path.exists(file_in): + file_in = os.path.join(topdir, file_in) + ds = xr.open_dataset(file_in) + return file_in, ds + +def get_tweak(ds_in, coord_str, init_tweak): + """ + Get the tweak that will be applied to all datasets' lat/lon coordinates + """ + da = ds_in[coord_str] + coord2_orig = da.values.astype(COORD_DATATYPE) + coord2 = coord2_orig + tweak = init_tweak coord2 += tweak + + # This is necessary if precision is lower than float64 + max_tweak = 1e-2 while np.any(coord2 == da.values): tweak *= 10 - coord2 = da.values + if tweak > max_tweak: + raise RuntimeError(f"Tweaking by +{max_tweak} failed to 'take'") + coord2 = coord2_orig coord2 += tweak + return tweak + +def apply_tweak(ds_in, coord_str, tweak): + # Apply tweak + da = ds_in[coord_str] + coord2 = da.values.astype(COORD_DATATYPE) + coord2 += tweak + if np.any(coord2 == da.values): + raise RuntimeError('Tweak didn''t "take"') coord_tweak = np.full_like(coord2, tweak) # Ensure that no value is above maximum in input data. This is needed for mesh_maker to work. @@ -61,19 +87,22 @@ def apply_tweak(ds, coord_str, tweak=1e-6): ) # Replace coordinate in dataset - ds[coord_str] = da2 + ds_in[coord_str] = da2 # Add a variable with the amount of the tweak tweak_attrs = {} - if "standard_name" in da: + if "standard_name" in da.attrs: coord_name = da.attrs["standard_name"] - else: + elif "long_name" in da.attrs: coord_name = da.attrs["long_name"].replace("coordinate_", "") + else: + coord_name = coord_str tweak_attrs["standard_name"] = coord_name + "_tweak" tweak_attrs[ "long_name" ] = f"Amount {coord_name} was shifted to avoid ambiguous nearest neighbors" - tweak_attrs["units"] = da.attrs["units"] + if "units" in da.attrs: + tweak_attrs["units"] = da.attrs["units"] da_tweak = xr.DataArray( data=coord_tweak, coords=new_coords_dict, @@ -81,19 +110,48 @@ def apply_tweak(ds, coord_str, tweak=1e-6): attrs=tweak_attrs, ) tweak_name = coord_str + "_tweak" - ds[tweak_name] = da_tweak + ds_in[tweak_name] = da_tweak + + return ds_in + +def check(ds, f0_base, ds2, f_base, var): + if not np.array_equal(ds[var].values, ds2[var].values): + if not np.array_equal(ds[var].shape, ds2[var].shape): + msg = f"{var} shapes differ b/w {f0_base} ({ds[var].shape}) and {f_base} ({ds2[var].shape})" + raise RuntimeError(msg) + max_diff = np.max(np.abs(ds[var].values - ds2[var].values)) + msg = f"{var}s differ between {f0_base} and {f_base}; max = {max_diff}" + type0 = type(ds[var].values[0]) + type2 = type(ds2[var].values[0]) + if type0 != type2: + msg += f"\nTypes also differ: {type0} vs. {type2}" + raise RuntimeError(msg) - return ds +# %% Apply tweak to all files +# Set up empty dicts +tweak_dict = {} +coord_type_dict = {} +for coord in coord_list: + tweak_dict[coord] = -np.inf -# %% Apply tweak to all files +# Get tweaks +for file_in in file_list_in: + file_in, ds = get_ds(topdir, file_in) + for coord in coord_list: + this_tweak = get_tweak(ds, coord, init_tweak=1e-6) + if this_tweak > tweak_dict[coord]: + tweak_dict[coord] = this_tweak +for coord in coord_list: + print(f"Tweaking {coord} by {tweak_dict[coord]}") +print(" ") -for filename in file_list_in: - file_in = os.path.join(topdir, filename) - ds = xr.open_dataset(file_in) +# Apply tweaks +for file_in in file_list_in: + file_in, ds = get_ds(topdir, file_in) for coord in coord_list: - ds = apply_tweak(ds, coord, tweak=1e-6) + ds = apply_tweak(ds, coord, tweak_dict[coord]) # Set up for save file_out = file_in.replace(".nc", ".tweaked_latlons.nc") @@ -110,16 +168,14 @@ def apply_tweak(ds, coord_str, tweak=1e-6): # %% Ensure all files got the same tweaks ds = xr.open_dataset(file_list_out[0]) +f0_base = os.path.basename(file_list_out[0]) for filename in file_list_out[1:]: ds2 = xr.open_dataset(filename) + f_base = os.path.basename(filename) for coord in coord_list: - # Ensure that coordinates are the same - var = coord - assert np.array_equal(ds[var].values, ds2[var].values) - # Ensure that tweaks were the same - var = coord + "_tweak" - assert np.array_equal(ds[var].values, ds2[var].values) + check(ds, f0_base, ds2, f_base, coord) + check(ds, f0_base, ds2, f_base, coord + "_tweak") print("All good!") @@ -168,4 +224,3 @@ def redirect_argv(arglist): print("Done") -# %% From 64302ad0c48b2afd7a074659c6509ae3344a4ac3 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 12 Jul 2024 16:54:46 -0600 Subject: [PATCH 240/406] Additions/tweaks to CLM testlist. --- cime_config/testdefs/testlist_clm.xml | 44 +++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 40f351ab4a..3fde176440 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3663,6 +3663,8 @@ + + @@ -3673,6 +3675,7 @@ + @@ -3684,6 +3687,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3754,13 +3793,14 @@ - + + @@ -3773,7 +3813,7 @@ - + From 0da6eb5c706217606c1323b2b1a57eb136e32f1a Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sun, 14 Jul 2024 12:01:58 -0600 Subject: [PATCH 241/406] Remove Clm50 RXCROPMATURITY tests. --- cime_config/testdefs/testlist_clm.xml | 37 +-------------------------- 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 3fde176440..86210256ff 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3660,42 +3660,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + From 87d1fa1a4b2a69c2d01898be9d8f9e661af8d163 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sun, 14 Jul 2024 12:43:31 -0600 Subject: [PATCH 242/406] Move tweak_latlons.py to tools/contrib/. This is hopefully not something that will be needed permanently or even in the medium term, so it's not worth cleaning up and unit-/system-testing. --- {python/ctsm/crop_calendars => tools/contrib}/tweak_latlons.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {python/ctsm/crop_calendars => tools/contrib}/tweak_latlons.py (100%) diff --git a/python/ctsm/crop_calendars/tweak_latlons.py b/tools/contrib/tweak_latlons.py similarity index 100% rename from python/ctsm/crop_calendars/tweak_latlons.py rename to tools/contrib/tweak_latlons.py From f6ff13409d58a50299d0d62207e99a12b2c426a6 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sun, 14 Jul 2024 14:07:51 -0600 Subject: [PATCH 243/406] Rework tweak_latlons.py to run from command line. --- tools/contrib/tweak_latlons.py | 285 +++++++++++++++++++-------------- 1 file changed, 164 insertions(+), 121 deletions(-) diff --git a/tools/contrib/tweak_latlons.py b/tools/contrib/tweak_latlons.py index 0031d492e0..2bae06d229 100644 --- a/tools/contrib/tweak_latlons.py +++ b/tools/contrib/tweak_latlons.py @@ -1,44 +1,23 @@ -# %% -import numpy as np -import xarray as xr +""" +'Tweak' the latitude and longitude coordinates to avoid ambiguous nearest neighbors +""" import os import sys -from netCDF4 import Dataset import contextlib +import argparse +import numpy as np +import xarray as xr +from netCDF4 import Dataset # pylint: disable=no-name-in-module # -- add python/ctsm to path (needed if we want to run this stand-alone) -_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir) +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir, "python") sys.path.insert(1, _CTSM_PYTHON) # pylint: disable=wrong-import-position from ctsm.mesh_maker import main as mesh_maker -topdir = "/glade/campaign/cesm/cesmdata/inputdata/lnd/clm2/cropdata/calendars/processed/" -file_list_in = [ - "swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc", - "swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc", - "gdds_20230829_161011.nc", - "gdd20bl.copied_from.gdds_20230829_161011.v2.nc", - "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc", - "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc", - "/glade/work/samrabin/cropCals_testing_20240626/gdds_20240712_114642_10x15_interpd_halfdeg.nc", - "/glade/work/samrabin/gdd20_baselines/gswp3.10x15_interpd_halfdeg.1980-2009.nc", -] -file_mesh_in = ( - "/glade/campaign/cesm/cesmdata/inputdata/share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc" -) - -file_list_out = [] -coord_list = ["lat", "lon"] +COORD_LIST = ["lat", "lon"] COORD_DATATYPE = np.float64 -# %% Define functions - -def get_ds(topdir, file_in): - if not os.path.exists(file_in): - file_in = os.path.join(topdir, file_in) - ds = xr.open_dataset(file_in) - return file_in, ds - def get_tweak(ds_in, coord_str, init_tweak): """ Get the tweak that will be applied to all datasets' lat/lon coordinates @@ -73,7 +52,9 @@ def apply_tweak(ds_in, coord_str, tweak): where_toohigh = np.where(coord2 > max_coord) Ntoohigh = len(where_toohigh[0]) if Ntoohigh != 1: - raise RuntimeError(f"Expected 1 coordinate value too high; got {Ntoohigh}") + raise RuntimeError( + f"Expected 1 coordinate value too high; got {Ntoohigh}" + ) coord2[where_toohigh] = max_coord coord_tweak[where_toohigh] = max_coord @@ -127,100 +108,162 @@ def check(ds, f0_base, ds2, f_base, var): msg += f"\nTypes also differ: {type0} vs. {type2}" raise RuntimeError(msg) -# %% Apply tweak to all files - -# Set up empty dicts -tweak_dict = {} -coord_type_dict = {} -for coord in coord_list: - tweak_dict[coord] = -np.inf - -# Get tweaks -for file_in in file_list_in: - file_in, ds = get_ds(topdir, file_in) - for coord in coord_list: - this_tweak = get_tweak(ds, coord, init_tweak=1e-6) - if this_tweak > tweak_dict[coord]: - tweak_dict[coord] = this_tweak -for coord in coord_list: - print(f"Tweaking {coord} by {tweak_dict[coord]}") -print(" ") - -# Apply tweaks -for file_in in file_list_in: - file_in, ds = get_ds(topdir, file_in) - - for coord in coord_list: - ds = apply_tweak(ds, coord, tweak_dict[coord]) - - # Set up for save - file_out = file_in.replace(".nc", ".tweaked_latlons.nc") - with Dataset(file_in, "r") as netcdf_file: - netcdf_format = netcdf_file.data_model - - # Save - print(f"Saving {file_out}") - ds.to_netcdf(file_out, format=netcdf_format) - file_list_out.append(file_out) -print("Done") - - -# %% Ensure all files got the same tweaks - -ds = xr.open_dataset(file_list_out[0]) -f0_base = os.path.basename(file_list_out[0]) - -for filename in file_list_out[1:]: - ds2 = xr.open_dataset(filename) - f_base = os.path.basename(filename) - for coord in coord_list: - check(ds, f0_base, ds2, f_base, coord) - check(ds, f0_base, ds2, f_base, coord + "_tweak") -print("All good!") - - -# %% Save new mesh file - -outfile_name = os.path.basename(file_mesh_in) -outfile_name = outfile_name.replace(".nc", ".tweaked_latlons.nc") -outdir = os.path.dirname(file_list_out[0]) -file_mesh_out = os.path.join(outdir, outfile_name) - @contextlib.contextmanager def redirect_argv(arglist): + """ + Preserve actual arg list while giving a new one to mesh_maker + """ argv_tmp = sys.argv[:] - sys.argv=arglist + sys.argv = arglist yield sys.argv = argv_tmp +def main(input_files, mesh_file_in, output_files): + """ + Apply tweak to all files + """ + + # Set up + tweak_dict = {} + for coord in COORD_LIST: + tweak_dict[coord] = -np.inf + mesh_file_out = output_files[-1] + output_files = output_files[:-1] + + # Get tweaks + for file_in in input_files: + ds = xr.open_dataset(file_in) + for coord in COORD_LIST: + this_tweak = get_tweak(ds, coord, init_tweak=1e-6) + if this_tweak > tweak_dict[coord]: + tweak_dict[coord] = this_tweak + for coord in COORD_LIST: + print(f"Tweaking {coord} by {tweak_dict[coord]}") + print(" ") + + # Apply tweaks + for i, file_in in enumerate(input_files): + ds = xr.open_dataset(file_in) + + for coord in COORD_LIST: + ds = apply_tweak(ds, coord, tweak_dict[coord]) + + # Set up for save + file_out = output_files[i] + with Dataset(file_in, "r") as netcdf_file: + netcdf_format = netcdf_file.data_model + + # Make output dir, if needed + output_dir = os.path.dirname(file_out) + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + # Save + print(f"Saving {file_out}") + ds.to_netcdf(file_out, format=netcdf_format) + print("Done") + + + # Ensure all files got the same tweaks + ds = xr.open_dataset(output_files[0]) + f0_base = os.path.basename(output_files[0]) + for file_out in output_files[1:]: + ds2 = xr.open_dataset(file_out) + f_base = os.path.basename(file_out) + for coord in COORD_LIST: + check(ds, f0_base, ds2, f_base, coord) + check(ds, f0_base, ds2, f_base, coord + "_tweak") + + + # Save new mesh file + mesh_maker_args = [ + "mesh_maker", + "--input", + output_files[0], + "--output", + mesh_file_out, + "--lat", + "lat", + "--lon", + "lon", + "--overwrite", + ] + print(f"Saving {mesh_file_out}...") + with redirect_argv(mesh_maker_args): + mesh_maker() + + # Change format, if needed + with Dataset(mesh_file_in, "r") as netcdf_file: + netcdf_format_in = netcdf_file.data_model + with Dataset(mesh_file_out, "r") as netcdf_file: + netcdf_format_out = netcdf_file.data_model + if netcdf_format_in != netcdf_format_out: + mesh_file_out_tmp = mesh_file_out + ".tmp" + os.rename(mesh_file_out, mesh_file_out_tmp) + ds = xr.open_dataset(mesh_file_out_tmp) + ds.to_netcdf(mesh_file_out, format=netcdf_format_in) + os.remove(mesh_file_out_tmp) + + print("Done") + + + + +if __name__ == "__main__": + ############################### + ### Process input arguments ### + ############################### + parser = argparse.ArgumentParser( + description="'Tweak' the latitude and longitude coordinates to avoid ambiguous nearest neighbors", + ) + + # Required + parser.add_argument( + "-i", + "--input-files", + help="Comma-separated stream files whose coordinates need tweaking", + required=True, + ) + parser.add_argument( + "-m", + "--mesh-file", + help="Mesh file associated with input files", + required=True, + ) + + # Optional + parser.add_argument( + "--overwrite", + help="Overwrite any existing output files", + action="store_true", + default=False, + ) + default_output_dir = os.getcwd() + parser.add_argument( + "-o", + "--output-dir", + help=f"Directory where output files should be saved. Default is current working directory: {default_output_dir}", + default=default_output_dir, + ) -mesh_maker_args = [ - "mesh_maker", - "--input", - file_list_out[0], - "--output", - file_mesh_out, - "--lat", - "lat", - "--lon", - "lon", - "--overwrite", -] -print(f"Saving {file_mesh_out}...") -with redirect_argv(mesh_maker_args): - mesh_maker() - -# Change format, if needed -with Dataset(file_mesh_in, "r") as netcdf_file: - netcdf_format_in = netcdf_file.data_model -with Dataset(file_mesh_out, "r") as netcdf_file: - netcdf_format_out = netcdf_file.data_model -if netcdf_format_in != netcdf_format_out: - file_mesh_out_tmp = file_mesh_out + ".tmp" - os.rename(file_mesh_out, file_mesh_out_tmp) - ds = xr.open_dataset(file_mesh_out_tmp) - ds.to_netcdf(file_mesh_out, format=netcdf_format_in) - os.remove(file_mesh_out_tmp) - - -print("Done") + # Get arguments + args = parser.parse_args(sys.argv[1:]) + + # Check/process input and output files + _input_files = args.input_files.split(",") + _output_files = [] + for file in _input_files + [args.mesh_file]: + if not os.path.exists(file): + raise FileNotFoundError(f"File not found: {file}") + + filename, ext = os.path.splitext(os.path.basename(file)) + output_file = os.path.join( + args.output_dir, filename + ".tweaked_latlons" + ext + ) + if os.path.exists(output_file) and not args.overwrite: + raise FileExistsError( + f"Output file exists but --overwrite not specified: {output_file}" + ) + _output_files.append(output_file) + + main(_input_files, args.mesh_file, _output_files) From 227426345fcd0bd552e755b5c4cceb503cea94c6 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sun, 14 Jul 2024 14:28:50 -0600 Subject: [PATCH 244/406] Refine RxCropCals* testlist. --- cime_config/testdefs/testlist_clm.xml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 86210256ff..c2926a25e7 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3755,7 +3755,7 @@ - + @@ -3765,7 +3765,8 @@ - + + @@ -3782,6 +3783,17 @@ + + + + + + + + + + + From f957f21066335c99f10336560e66b1d72cb4e217 Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Mon, 15 Jul 2024 11:12:50 -0600 Subject: [PATCH 245/406] fix argument passing --- src/main/clm_instMod.F90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clm_instMod.F90 b/src/main/clm_instMod.F90 index f3cfa9923d..04c4af6611 100644 --- a/src/main/clm_instMod.F90 +++ b/src/main/clm_instMod.F90 @@ -316,7 +316,8 @@ subroutine clm_instInit(bounds) em_wall_lun=urbanparams_inst%em_wall(begl:endl), & em_improad_lun=urbanparams_inst%em_improad(begl:endl), & em_perroad_lun=urbanparams_inst%em_perroad(begl:endl), & - is_simple_buildtemp=IsSimpleBuildTemp(), is_prog_buildtemp=IsProgBuildTemp() ) + is_simple_buildtemp=IsSimpleBuildTemp(), is_prog_buildtemp=IsProgBuildTemp(), & + exice_init_stream_col=exice_init_stream_col(bounds%begc:bounds%endc) ) call active_layer_inst%Init(bounds) From 5686defdd31c03cdba0bcc247e3d590a0069d9d4 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 15 Jul 2024 11:40:38 -0600 Subject: [PATCH 246/406] Update bld/unit_testers/build-namelist_test.pl Syntax of breaking up into two lines was confusing, so just making a longer line that is more clear. --- bld/unit_testers/build-namelist_test.pl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index 5abb381c16..a22cdd4059 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -1095,8 +1095,7 @@ sub cat_and_create_namelistinfile { phys=>"clm5_1", }, "soil_erod_none_w_Zender" =>{ options=>"--envxml_dir .", - namelst=>"dust_emis_method='Zender_2003', " . - "zender_soil_erod_source='none'", + namelst=>"dust_emis_method='Zender_2003', zender_soil_erod_source='none'", GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_1", }, From 7dbc538216ed01ed92dd656b7133238e0bad8ea6 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 15 Jul 2024 15:08:44 -0600 Subject: [PATCH 247/406] Remove duplicated tests, and new dust tests with clm6_0 --- bld/unit_testers/build-namelist_test.pl | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index 5abb381c16..0b26e86476 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -1072,39 +1072,27 @@ sub cat_and_create_namelistinfile { "soil_erod_wo_Zender" =>{ options=>"--envxml_dir . --ignore_warnings", namelst=>"dust_emis_method='Leung_2023', " . "stream_meshfile_zendersoilerod = '/dev/null'", - phys=>"clm5_1", + phys=>"clm6_0", }, "soil_erod_wo_lnd_source" =>{ options=>"--envxml_dir .", namelst=>"dust_emis_method='Zender_2003', " . "stream_fldfilename_zendersoilerod = '/dev/null', zender_soil_erod_source='atm'", - phys=>"clm5_1", + phys=>"clm6_0", }, "soil_erod_none_w_Zender" =>{ options=>"--envxml_dir .", namelst=>"dust_emis_method='Zender_2003', " . "zender_soil_erod_source='none'", - phys=>"clm5_1", + phys=>"clm6_0", }, "soil_erod_bad_w_Zender" =>{ options=>"--envxml_dir .", namelst=>"dust_emis_method='Zender_2003', " . "zender_soil_erod_source='zztop'", - phys=>"clm5_1", + phys=>"clm6_0", }, "Set_Dust_When_CAM_Sets" =>{ options=>"--envxml_dir .", namelst=>"dust_emis_method='Zender_2003'", LND_SETS_DUST_EMIS_DRV_FLDS=>"FALSE", - phys=>"clm5_1", - }, - "soil_erod_none_w_Zender" =>{ options=>"--envxml_dir .", - namelst=>"dust_emis_method='Zender_2003', " . - "zender_soil_erod_source='none'", - GLC_TWO_WAY_COUPLING=>"FALSE", - phys=>"clm5_1", - }, - "soil_erod_bad_w_Zender" =>{ options=>"--envxml_dir .", - namelst=>"dust_emis_method='Zender_2003', " . - "zender_soil_erod_source='zztop'", - GLC_TWO_WAY_COUPLING=>"FALSE", - phys=>"clm5_1", + phys=>"clm6_0", }, ); foreach my $key ( keys(%failtest) ) { From 02f9cb325d3884add3ecfb45904c34bb3edb0f62 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 15 Jul 2024 15:25:35 -0600 Subject: [PATCH 248/406] Make fail tests lines where the namelst option is split up into two lines into one long line, because the way they were concatonated look obscure and possibly wrong, the long lines are easier to read --- bld/unit_testers/build-namelist_test.pl | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index 0b26e86476..d72331b1c2 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -1070,23 +1070,19 @@ sub cat_and_create_namelistinfile { phys=>"clm5_0", }, "soil_erod_wo_Zender" =>{ options=>"--envxml_dir . --ignore_warnings", - namelst=>"dust_emis_method='Leung_2023', " . - "stream_meshfile_zendersoilerod = '/dev/null'", + namelst=>"dust_emis_method='Leung_2023', stream_meshfile_zendersoilerod = '/dev/null'", phys=>"clm6_0", }, "soil_erod_wo_lnd_source" =>{ options=>"--envxml_dir .", - namelst=>"dust_emis_method='Zender_2003', " . - "stream_fldfilename_zendersoilerod = '/dev/null', zender_soil_erod_source='atm'", + namelst=>"dust_emis_method='Zender_2003', stream_fldfilename_zendersoilerod = '/dev/null', zender_soil_erod_source='atm'", phys=>"clm6_0", }, "soil_erod_none_w_Zender" =>{ options=>"--envxml_dir .", - namelst=>"dust_emis_method='Zender_2003', " . - "zender_soil_erod_source='none'", + namelst=>"dust_emis_method='Zender_2003', zender_soil_erod_source='none'", phys=>"clm6_0", }, "soil_erod_bad_w_Zender" =>{ options=>"--envxml_dir .", - namelst=>"dust_emis_method='Zender_2003', " . - "zender_soil_erod_source='zztop'", + namelst=>"dust_emis_method='Zender_2003', zender_soil_erod_source='zztop'", phys=>"clm6_0", }, "Set_Dust_When_CAM_Sets" =>{ options=>"--envxml_dir .", From 6e806b5a0d0b1db561f01a403cbae2fc5b450e82 Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Tue, 16 Jul 2024 08:50:08 -0600 Subject: [PATCH 249/406] update defaults --- bld/namelist_files/namelist_defaults_ctsm.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 2c6b375140..47f9802aff 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -2058,8 +2058,9 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c .false. - --2.15 +0.0 +1.0 +-3.15 0.5 lnd/clm2/paramdata/exice_init_0.125x0.125_c20220516.nc lnd/clm2/paramdata/exice_init_0.125x0.125_ESMFmesh_cdf5_c20220802.nc From 8478b730663ee4a6698b70804e95a1f029662aab Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 16 Jul 2024 13:58:00 -0600 Subject: [PATCH 250/406] Comments about move to CMEPS --- src/drv_test/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/drv_test/CMakeLists.txt b/src/drv_test/CMakeLists.txt index adf66b8b92..938e55a598 100644 --- a/src/drv_test/CMakeLists.txt +++ b/src/drv_test/CMakeLists.txt @@ -1 +1,3 @@ +# This test should be moved to be under CMEPS +# See: https://github.com/ESCOMP/CMEPS/issues/458 add_subdirectory(shr_dust_emis_test) From 525c78bf49e9c6d71a082cf35f979c48fa6683ab Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 16 Jul 2024 19:12:39 -0600 Subject: [PATCH 251/406] Remove leftover conflict header, so can compile --- src/biogeochem/DUSTMod.F90 | 1 - 1 file changed, 1 deletion(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index 24c4f13069..bf04311cbe 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -743,7 +743,6 @@ subroutine DustEmission (bounds, & !################### for Leung et al. (2023) ################################################ !################ uncomment the below block if want to use Leung's scheme ################### -<<<<<<< HEAD !flx_mss_vrt_dst_ttl(p) = dst_emiss_coeff(p) * mss_frc_cly_vld(c) * forc_rho(c) * ((wnd_frc_slt**2.0_r8 - wnd_frc_thr_slt_it**2.0_r8) / wnd_frc_thr_slt_std) * (wnd_frc_slt / wnd_frc_thr_slt_it)**frag_expt ! Leung et al. (2022) uses Kok et al. (2014) dust emission euqation for emission flux ! dmleung: instead of using mss_frc_cly_vld(c) with a range of [0,0.2] , which makes dust too sensitive to input clay surface dataset), for now use 0.1 + mss_frc_cly_vld(c) * 0.1 / 0.20 with a range of [0.1,0.2]. So, instead of scaling dust emission to 1/20 times for El Djouf (western Sahara) because of its 1 % clay fraction, scale its emission to 1/2 times. This reduces the sensitivity of dust emission to the clay input dataset. In particular, because dust emission is a small-scale process and the grid-averaged clay from the new WISE surface dataset over El Djouf is 1 %, much lower than the former FAO estimation of 5 %, dmleung is not sure if the clay value of 1 % suppresses too much of El Djouf's small-scale dust emission process. Similar to Charlie Zender's feeling suspicious about the soil texture datasets (or the dust emission schemes' sandblasting process), dmleung feels the same and for now decides to still allow dust emission to weakly scale with clay fraction, however limiting the scaling factor to 0.1-0.2. See modification in SoilStateInitTimeConst.F90. dmleung 5 Jul 2024 From e14313858df6e9e6f2488951af2551349e349c65 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 10:11:33 -0600 Subject: [PATCH 252/406] generate_gdd20_baseline: Clarity rearrangement. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 476717b887..5d4fac3aff 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -246,16 +246,18 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice): if gddn is None: # Crop not handled yet? Fill it entirely with missing value this_da = dummy_da - long_name = "Dummy GDD20" print(" dummy GDD20") else: # this_da = ds_in[gddn].fillna(MISSING_FILL) this_da = ds_in[gddn] this_da = _add_time_axis(this_da) - long_name = gddn.replace("X", "20") print(f" {gddn}") - # Add attributes + # Add attributes of output file + if gddn is None: + long_name = "Dummy GDD20" + else: + long_name = gddn.replace("X", "20") this_da.attrs["long_name"] = long_name + f" baseline for {cft_str}" this_da.attrs["units"] = "°C days" # this_da.attrs["_FillValue"] = MISSING_FILL From 699936eef5097066b4faea29de920541be1d36d0 Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Thu, 18 Jul 2024 10:47:24 -0600 Subject: [PATCH 253/406] Move namelist vars, add checks --- bld/CLMBuildNamelist.pm | 43 +++++++-- .../namelist_definition_ctsm.xml | 9 +- src/biogeophys/TemperatureType.F90 | 91 ++++++++++++++++++- src/main/clm_instMod.F90 | 4 +- src/main/clm_varctl.F90 | 4 - src/main/controlMod.F90 | 12 +-- 6 files changed, 132 insertions(+), 31 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 45c0a7bdb8..498de96a4d 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -1812,10 +1812,15 @@ sub process_namelist_inline_logic { ######################################### setup_logic_initinterp($opts, $nl_flags, $definition, $defaults, $nl); - ############################### - # namelist group: exice_streams # - ############################### + ################################# + # namelist group: exice_streams # + ################################# setup_logic_exice($opts, $nl_flags, $definition, $defaults, $nl); + + ########################################## + # namelist group: clm_temperature_inparm # + ########################################## + setup_logic_coldstart_temp($opts,$nl_flags, $definition, $defaults, $nl); } #------------------------------------------------------------------------------- @@ -4599,7 +4604,7 @@ sub setup_logic_exice { } # Otherwise if ice streams are off } else { - my @list = ( "stream_meshfile_exice", "stream_fldfilename_exice" , "excess_ice_coldstart_temp" , "excess_ice_coldstart_depth"); + my @list = ( "stream_meshfile_exice", "stream_fldfilename_exice" ); # fail is excess ice streams files are set foreach my $var ( @list ) { if ( defined($nl->get_value($var)) ) { @@ -4618,8 +4623,6 @@ sub setup_logic_exice { if (defined($use_exice_streams) && value_is_true($use_exice_streams)) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldfilename_exice'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_mapalgo_exice'); - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'excess_ice_coldstart_temp'); - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'excess_ice_coldstart_depth'); # If excess ice streams on, but NOT the NUOPC driver fail if ( not $opts->{'driver'} eq "nuopc" ) { $log->fatal_error("nuopc driver is required when use_excess_ice_streams is set to true" ); @@ -4633,6 +4636,33 @@ sub setup_logic_exice { } # end exice streams +sub setup_logic_coldstart_temp { + + my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; + + # set initial temperatures for excess ice gridcells: + + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'excess_ice_coldstart_temp'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'excess_ice_coldstart_depth'); + + my $use_exice = $nl->get_value( 'use_excess_ice' ); + my $use_exice_streams = $nl->get_value( 'use_excess_ice_streams' ); + my $exice_cs_temp = $nl->get_value( 'excess_ice_coldstart_temp' ); + my $exice_cs_depth = $nl->get_value( 'excess_ice_coldstart_depth' ); + + if (defined($use_exice) && value_is_true($use_exice)) { + # Checking this setting only needed IF excess ice streams are on get the stream defaults + if (defined($use_exice_streams) && value_is_true($use_exice_streams)) { + if (defined($exice_cs_depth) && $exice_cs_depth <= 0.0 ) { + $log->fatal_error("excess_ice_coldstart_depth is <= 0.0" ); + } + if (defined($exice_cs_temp) && $exice_cs_temp >= 0.0 ) { + $log->fatal_error("excess_ice_coldstart_temp is >= 0.0, no excess ice will be present in this run" ); + } + } + } +} + #------------------------------------------------------------------------------- sub setup_logic_z0param { @@ -4728,6 +4758,7 @@ sub write_output_files { push @groups, "lifire_inparm"; push @groups, "ch4finundated"; push @groups, "exice_streams"; + push @groups, "clm_temperature_inparm"; push @groups, "soilbgc_decomp"; push @groups, "clm_canopy_inparm"; push @groups, "zendersoilerod"; diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 2eb8a67e8c..575d72856f 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -2825,13 +2825,14 @@ If TRUE turn on the excess ice physics, (Lee et al., 2014; Cai et al., 2020) -Initial soil temperature to use for gridcells with excess ice present during a run starting with coldstart (deg C) + group="clm_temperature_inparm" > +Initial soil temperature to use for gridcells with excess ice present during a run starting with coldstart (deg C). Value is only apply if use_excess_ice is true. -Soil depth below which initial excess ice concentration will be applied during a run starting with coldstart (m) + group="clm_temperature_inparm" > +Soil depth below which initial excess ice concentration will be applied during a run starting with coldstart (m). Value is only apply if use_excess_ice is true. +If this is set below depth of the soil depth, only the last soil layer will get excess ice. diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 0a31bba6e6..6084c7ee2c 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -118,6 +118,10 @@ module TemperatureType real(r8), pointer :: xmf_h2osfc_col (:) ! latent heat of phase change of surface water real(r8), pointer :: fact_col (:,:) ! used in computing tridiagonal matrix real(r8), pointer :: c_h2osfc_col (:) ! heat capacity of surface water + + ! Namelist parameters for initialization + real(r8), private :: excess_ice_coldstart_depth ! depth below which excess ice will be present + real(r8), private :: excess_ice_coldstart_temp ! coldstart temperature of layers with excess ice present contains @@ -130,6 +134,8 @@ module TemperatureType procedure, public :: InitAccVars procedure, public :: UpdateAccVars + procedure, private :: ReadNL + end type temperature_type character(len=*), parameter, private :: sourcefile = & @@ -141,7 +147,7 @@ module TemperatureType !------------------------------------------------------------------------ subroutine Init(this, bounds, & em_roof_lun, em_wall_lun, em_improad_lun, em_perroad_lun, & - is_simple_buildtemp, is_prog_buildtemp, exice_init_stream_col) + is_simple_buildtemp, is_prog_buildtemp, exice_init_stream_col, NLFileName) ! ! !DESCRIPTION: ! @@ -157,7 +163,10 @@ subroutine Init(this, bounds, & logical , intent(in) :: is_simple_buildtemp ! Simple building temp is being used logical , intent(in) :: is_prog_buildtemp ! Prognostic building temp is being used real(r8) , intent(in) :: exice_init_stream_col(bounds%begc:) ! initial excess ice concentration from the stream file + character(len=*) , intent(in) :: NLFilename ! Namelist filename + + call this%ReadNL(NLFilename) call this%InitAllocate ( bounds ) call this%InitHistory ( bounds, is_simple_buildtemp, is_prog_buildtemp ) call this%InitCold ( bounds, & @@ -654,7 +663,7 @@ subroutine InitCold(this, bounds, & use column_varcon , only : icol_road_imperv, icol_roof, icol_sunwall use column_varcon , only : icol_shadewall, icol_road_perv use clm_varctl , only : iulog, use_vancouver, use_mexicocity - use clm_varctl , only : use_excess_ice, excess_ice_coldstart_depth, excess_ice_coldstart_temp + use clm_varctl , only : use_excess_ice use initVerticalMod , only : find_soil_layer_containing_depth ! ! !ARGUMENTS: @@ -750,12 +759,21 @@ subroutine InitCold(this, bounds, & this%t_soisno_col(c,1:nlevgrnd) = 272._r8 if (use_excess_ice .and. exice_init_stream_col(c) > 0.0_r8) then nexice_start = nlevsoi - 1 - if (zisoi(nlevsoi) >= excess_ice_coldstart_depth) then - call find_soil_layer_containing_depth(0.5_r8,nexice_start) + if (this%excess_ice_coldstart_depth <= 0.0_r8) then + ! we double check this here, and when building namelists + call endrun(msg="ERROR excess_ice_coldstart_depth <= 0.0. Set a positive value in the namelist"//errmsg(sourcefile, __LINE__)) + endif + if (zisoi(nlevsoi) >= this%excess_ice_coldstart_depth) then + call find_soil_layer_containing_depth(this%excess_ice_coldstart_depth,nexice_start) else nexice_start=nlevsoi-1 endif - this%t_soisno_col(c,nexice_start:nlevgrnd) = SHR_CONST_TKFRZ + excess_ice_coldstart_temp !needs to be below freezing to properly initiate excess ice + if (this%excess_ice_coldstart_temp >= 0.0_r8) then + ! this is here, since we care about excess_ice_coldstart_temp only when there is excess ice in the gridcell + ! which happens only when the streams are read. + call endrun(msg="ERROR excess_ice_coldstart_temp is not below freezing point"//errmsg(sourcefile, __LINE__)) + endif + this%t_soisno_col(c,nexice_start:nlevgrnd) = SHR_CONST_TKFRZ + this%excess_ice_coldstart_temp end if endif endif @@ -1646,4 +1664,67 @@ subroutine Clean(this) end subroutine Clean + !----------------------------------------------------------------------- + subroutine ReadNL( this, NLFilename ) + ! + ! !DESCRIPTION: + ! Read namelist for Temperature type + ! right now (17.07.2024) it only reads variables related to excess ice coldstart initialization + ! but can be extended to replace hardocded values in InitCold by namelist variables + ! + ! !USES: + use shr_mpi_mod , only : shr_mpi_bcast + use shr_log_mod , only : errMsg => shr_log_errMsg + use spmdMod , only : masterproc, mpicom + use fileutils , only : getavu, relavu, opnfil + use clm_nlUtilsMod , only : find_nlgroup_name + use clm_varctl , only : iulog + use abortutils , only : endrun + ! + ! !ARGUMENTS: + class(temperature_type) :: this + character(len=*), intent(in) :: NLFilename ! Namelist filename + ! + ! !LOCAL VARIABLES: + integer :: ierr ! error code + integer :: unitn ! unit for namelist file + real(r8) :: excess_ice_coldstart_temp = spval ! coldstart temperature of layers with excess ice present (deg C) + real(r8) :: excess_ice_coldstart_depth = spval ! depth below which excess ice will be present (m) + character(len=32) :: subname = 'Temperature_readnl' ! subroutine name + !----------------------------------------------------------------------- + + namelist / clm_temperature_inparm / excess_ice_coldstart_depth, excess_ice_coldstart_temp + + if ( masterproc )then + + unitn = getavu() + write(iulog,*) 'Read in clm_temperature_inparm namelist' + call opnfil (NLFilename, unitn, 'F') + call find_nlgroup_name(unitn, 'clm_temperature_inparm', status=ierr) + if (ierr == 0) then + read(unitn, nml=clm_temperature_inparm, iostat=ierr) + if (ierr /= 0) then + call endrun(msg="ERROR reading clm_temperature_inparm namelist"//errmsg(sourcefile, __LINE__)) + end if + else + call endrun(msg="ERROR finding clm_temperature_inparm namelist"//errmsg(sourcefile, __LINE__)) + end if + call relavu( unitn ) + ! namelist might be read but the values not properly set + if ( excess_ice_coldstart_depth == spval ) then + call endrun(msg="ERROR exice_coldstart_depth namelist value is not properly set"//errmsg(sourcefile, __LINE__)) + endif + if ( excess_ice_coldstart_temp == spval ) then + call endrun(msg="ERROR exice_coldstart_temp namelist value is not properly set"//errmsg(sourcefile, __LINE__)) + endif + end if + + call shr_mpi_bcast(excess_ice_coldstart_depth, mpicom) + call shr_mpi_bcast(excess_ice_coldstart_temp, mpicom) + + this%excess_ice_coldstart_depth = excess_ice_coldstart_depth + this%excess_ice_coldstart_temp = excess_ice_coldstart_temp + + end subroutine ReadNL + end module TemperatureType diff --git a/src/main/clm_instMod.F90 b/src/main/clm_instMod.F90 index 04c4af6611..242df92c43 100644 --- a/src/main/clm_instMod.F90 +++ b/src/main/clm_instMod.F90 @@ -222,7 +222,7 @@ subroutine clm_instInit(bounds) type(file_desc_t) :: params_ncid ! pio netCDF file id for parameter file real(r8), allocatable :: h2osno_col(:) real(r8), allocatable :: snow_depth_col(:) - real(r8), allocatable :: exice_init_stream_col(:) + real(r8), allocatable :: exice_init_stream_col(:) ! initial concentration of excess ice in the soil (-) type(excessicestream_type) :: exice_stream integer :: dummy_to_make_pgi_happy @@ -317,7 +317,7 @@ subroutine clm_instInit(bounds) em_improad_lun=urbanparams_inst%em_improad(begl:endl), & em_perroad_lun=urbanparams_inst%em_perroad(begl:endl), & is_simple_buildtemp=IsSimpleBuildTemp(), is_prog_buildtemp=IsProgBuildTemp(), & - exice_init_stream_col=exice_init_stream_col(bounds%begc:bounds%endc) ) + exice_init_stream_col=exice_init_stream_col(bounds%begc:bounds%endc) , NLFileName=NLFilename) call active_layer_inst%Init(bounds) diff --git a/src/main/clm_varctl.F90 b/src/main/clm_varctl.F90 index eff05511cd..8804ad2c01 100644 --- a/src/main/clm_varctl.F90 +++ b/src/main/clm_varctl.F90 @@ -422,10 +422,6 @@ module clm_varctl !---------------------------------------------------------- logical, public :: use_excess_ice = .false. ! true. => use excess ice physics - real(r8), public :: excess_ice_coldstart_temp = rundef ! initial coldstart soil temperature for gridcells where excess ice is present - - real(r8), public :: excess_ice_coldstart_depth = rundef ! initial coldstart depth at which excess ice might be present and excess_ice_coldstart_temp will be applied - !---------------------------------------------------------- ! plant hydraulic stress switch !---------------------------------------------------------- diff --git a/src/main/controlMod.F90 b/src/main/controlMod.F90 index a5321e0797..47ce6dce7c 100644 --- a/src/main/controlMod.F90 +++ b/src/main/controlMod.F90 @@ -256,8 +256,8 @@ subroutine control_init(dtime) namelist /clm_inparm/ use_soil_moisture_streams - ! excess ice flag and parameters - namelist /clm_inparm/ use_excess_ice , excess_ice_coldstart_depth, excess_ice_coldstart_temp + ! excess ice flag + namelist /clm_inparm/ use_excess_ice namelist /clm_inparm/ use_lai_streams @@ -829,10 +829,6 @@ subroutine control_spmd() call mpi_bcast (use_excess_ice, 1, MPI_LOGICAL, 0, mpicom,ier) - call mpi_bcast (excess_ice_coldstart_depth, 1, MPI_REAL8, 0, mpicom, ier) - - call mpi_bcast (excess_ice_coldstart_temp, 1, MPI_REAL8, 0, mpicom, ier) - call mpi_bcast (use_lai_streams, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_cropcal_streams, 1, MPI_LOGICAL, 0, mpicom, ier) @@ -989,10 +985,6 @@ subroutine control_print () write(iulog,*) ' use_extralakelayers = ', use_extralakelayers write(iulog,*) ' use_vichydro = ', use_vichydro write(iulog,*) ' use_excess_ice = ', use_excess_ice - if (use_excess_ice) then - write(iulog,*) ' excess_ice_coldstart_depth = ', excess_ice_coldstart_depth - write(iulog,*) ' excess_ice_coldstart_temp = ', excess_ice_coldstart_temp - endif write(iulog,*) ' use_cn = ', use_cn write(iulog,*) ' use_cndv = ', use_cndv write(iulog,*) ' use_crop = ', use_crop From 647fc52df1eb32b10646eb54ed4240d4cc9cfc00 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 11:07:00 -0600 Subject: [PATCH 254/406] import_ds: Workaround to import multiple files without dask. --- python/ctsm/crop_calendars/import_ds.py | 53 +++++++++++++++++-------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/python/ctsm/crop_calendars/import_ds.py b/python/ctsm/crop_calendars/import_ds.py index 77a22b626b..0526d3c720 100644 --- a/python/ctsm/crop_calendars/import_ds.py +++ b/python/ctsm/crop_calendars/import_ds.py @@ -41,6 +41,29 @@ def compute_derived_vars(ds_in, var): return ds_in +def manual_mfdataset(filelist, my_vars, my_vegtypes, time_slice): + """ + Opening a list of files with Xarray's open_mfdataset requires dask. This function is a + workaround for Python environments that don't have dask. + """ + ds_out = None + for filename in filelist: + ds_in = xr.open_dataset(filename) + ds_in = mfdataset_preproc(ds_in, my_vars, my_vegtypes, time_slice) + if ds_out is None: + ds_out = ds_in + else: + ds_out = xr.concat( + [ds_out, ds_in], + data_vars="minimal", + compat="override", + coords="all", + dim="time", + # combine="nested", + ) + return ds_out + + def mfdataset_preproc(ds_in, vars_to_import, vegtypes_to_import, time_slice): """ Function to drop unwanted variables in preprocessing of open_mfdataset(). @@ -221,22 +244,20 @@ def import_ds( if isinstance(filelist, list): with warnings.catch_warnings(): warnings.filterwarnings(action="ignore", category=DeprecationWarning) - if find_spec("dask") is None: - raise ModuleNotFoundError( - "You have asked xarray to import a list of files as a single Dataset using" - " open_mfdataset(), but this requires dask, which is not available.\nFile" - f" list: {filelist}" - ) - this_ds = xr.open_mfdataset( - sorted(filelist), - data_vars="minimal", - preprocess=mfdataset_preproc_closure, - compat="override", - coords="all", - concat_dim="time", - combine="nested", - chunks=chunks, - ) + dask_unavailable = find_spec("dask") is None + if dask_unavailable: + this_ds = manual_mfdataset(filelist, my_vars, my_vegtypes, time_slice) + else: + this_ds = xr.open_mfdataset( + sorted(filelist), + data_vars="minimal", + preprocess=mfdataset_preproc_closure, + compat="override", + coords="all", + concat_dim="time", + combine="nested", + chunks=chunks, + ) elif isinstance(filelist, str): this_ds = xr.open_dataset(filelist, chunks=chunks) this_ds = mfdataset_preproc(this_ds, my_vars, my_vegtypes, time_slice) From 96015dacbc7a22d57bcd242e358356f5c0cb028b Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 11:11:52 -0600 Subject: [PATCH 255/406] generate_gdd20_baseline: Can now specify -v/--variable GDDBX or GDDB20. --- .../crop_calendars/generate_gdd20_baseline.py | 64 +++++++++++++------ 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 5d4fac3aff..4b8e68e8f9 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -19,7 +19,6 @@ import ctsm.crop_calendars.cropcal_utils as utils from ctsm.crop_calendars.grid_one_variable import grid_one_variable -VAR_LIST_IN = ["GDD0X", "GDD8X", "GDD10X"] GRIDDING_VAR_LIST = ["patches1d_ixy", "patches1d_jxy", "lat", "lon"] MISSING_FILL = -1 # Something impossible to ensure that you can mark it as a missing value, to be # bilinear-interpolated @@ -88,6 +87,16 @@ def _parse_args(): type=int, required=False, ) + parser.add_argument( + "-v", + "--variable", + help=( + "Which type of variable should be processed?" + ), + required=False, + default="GDDBX", + choices=["GDDBX", "GDDB20"], + ) # Get arguments args = parser.parse_args(sys.argv[1:]) @@ -96,6 +105,12 @@ def _parse_args(): if os.path.exists(args.output_file) and not args.overwrite: raise FileExistsError("Output file exists but --overwrite is not specified") + # Get and check input files + args.input_files = args.input_files.split(" ") + for filename in args.input_files: + if not os.path.exists(filename): + raise FileNotFoundError(f"Input file not found: {filename}") + # Process time slice # Assumes CESM behavior where data for e.g. 1987 is saved as 1988-01-01. # It would be more robust, accounting for upcoming behavior (where timestamp for a year is the @@ -137,7 +152,7 @@ def _get_cft_list(crop_list): return cft_str_list -def _get_gddn_for_cft(cft_str): +def _get_gddn_for_cft(cft_str, variable): """ Given a CFT name, return the GDDN variable it uses. @@ -149,6 +164,7 @@ def _get_gddn_for_cft(cft_str): """ gddn = None + gddn_str = None gdd0_list_str = ["wheat", "cotton", "rice"] if cft_str in _get_cft_list(gdd0_list_str): @@ -163,9 +179,9 @@ def _get_gddn_for_cft(cft_str): gddn = 10 if gddn is not None: - gddn = f"GDD{gddn}X" + gddn_str = variable.replace("B", str(gddn)) - return gddn + return gddn, gddn_str def _get_output_varname(cft_str): @@ -194,19 +210,28 @@ def _add_time_axis(da_in): return da_out -def generate_gdd20_baseline(input_files, output_file, author, time_slice): +def generate_gdd20_baseline(input_files, output_file, author, time_slice, variable): """ Generate stream_fldFileName_gdd20_baseline file from CTSM outputs """ - # Get input file list - input_files = input_files.split(sep=" ") + # Define variables to process + if variable == "GDDBX": + suffix = "X" + elif variable == "GDDB20": + suffix = "20" + else: + raise ValueError(f"-v/--variable {variable} not recoginzed") + var_list_in = [] + for base_temp in [0, 8, 10]: + var_list_in.append(f"GDD{base_temp}{suffix}") + # Get unique values and sort input_files = list(set(input_files)) input_files.sort() # Import history files and ensure they have lat/lon dims - ds_in = import_ds(input_files, VAR_LIST_IN + GRIDDING_VAR_LIST, time_slice=time_slice) + ds_in = import_ds(input_files, var_list_in + GRIDDING_VAR_LIST, time_slice=time_slice) if not all(x in ds_in.dims for x in ["lat", "lon"]): raise RuntimeError("Input files must have lat and lon dimensions") @@ -216,9 +241,9 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice): # Set up a dummy DataArray to use for crops without an assigned GDDN variable dummy_da = xr.DataArray( - data=MISSING_FILL * np.ones_like(ds_in[VAR_LIST_IN[0]].values), - dims=ds_in[VAR_LIST_IN[0]].dims, - coords=ds_in[VAR_LIST_IN[0]].coords, + data=MISSING_FILL * np.ones_like(ds_in[var_list_in[0]].values), + dims=ds_in[var_list_in[0]].dims, + coords=ds_in[var_list_in[0]].coords, ) dummy_da = _add_time_axis(dummy_da) @@ -239,25 +264,27 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice): print(f"{cft_str} ({cft_int})") # Which GDDN history variable does this crop use? E.g., GDD0, GDD10 - gddn = _get_gddn_for_cft(cft_str) + gddn, gddn_str = _get_gddn_for_cft(cft_str, variable) # Fill any missing values with MISSING_FILL. This will mean that gddmaturity in these cells # never changes. - if gddn is None: + if gddn_str is None: # Crop not handled yet? Fill it entirely with missing value this_da = dummy_da print(" dummy GDD20") else: # this_da = ds_in[gddn].fillna(MISSING_FILL) - this_da = ds_in[gddn] + this_da = ds_in[gddn_str] this_da = _add_time_axis(this_da) - print(f" {gddn}") + print(f" {gddn_str}") # Add attributes of output file - if gddn is None: + if (gddn is None) != (gddn_str is None): + raise RuntimeError("gddn and gddn_str must either both be None or both be not None") + if gddn_str is None: long_name = "Dummy GDD20" else: - long_name = gddn.replace("X", "20") + long_name = f"GDD{gddn}20" this_da.attrs["long_name"] = long_name + f" baseline for {cft_str}" this_da.attrs["units"] = "°C days" # this_da.attrs["_FillValue"] = MISSING_FILL @@ -287,5 +314,6 @@ def main(): args.input_files, args.output_file, args.author, - time_slice + time_slice, + args.variable, ) From 3cc1d0fafb3290c1b249d22f828429c92a3719c4 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 11:14:33 -0600 Subject: [PATCH 256/406] generate_gdd20_baseline: Satisfy pylint. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 4b8e68e8f9..d2f96965b7 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -160,7 +160,7 @@ def _get_gddn_for_cft(cft_str, variable): cft_str (str): E.g., "irrigated_temperate_corn" Returns: - str or None: Name of variable to use (e.g., "GDD8X"). If crop isn't yet handled, return None. + str or None: Name of variable to use (e.g., "GDD8X"). If crop not yet handled, return None. """ gddn = None @@ -249,8 +249,8 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab # Process all crops data_var_dict = {} - for v in GRIDDING_VAR_LIST: - data_var_dict[v] = ds_in[v] + for gridding_var in GRIDDING_VAR_LIST: + data_var_dict[gridding_var] = ds_in[gridding_var] ds_out = xr.Dataset( data_vars=data_var_dict, attrs={ From b1bbe15eb03a57a26eb010a850a285fd600dfe3b Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 11:27:10 -0600 Subject: [PATCH 257/406] generate_gdd20_baseline: Save input file list as attribute(s). --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index d2f96965b7..b03d1fd658 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -247,7 +247,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab ) dummy_da = _add_time_axis(dummy_da) - # Process all crops + # Set up output Dataset data_var_dict = {} for gridding_var in GRIDDING_VAR_LIST: data_var_dict[gridding_var] = ds_in[gridding_var] @@ -258,6 +258,14 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab "created": dt.datetime.now().astimezone().isoformat(), }, ) + all_files_in_same_dir = len(np.unique([os.path.dirname(file) for file in input_files])) == 1 + if all_files_in_same_dir: + ds_out.attrs["input_files_dir"] = os.path.dirname(input_files[0]) + ds_out.attrs["input_files"] = ", ".join([os.path.basename(file) for file in input_files]) + else: + ds_out.attrs["input_files"] = ", ".join(input_files) + + # Process all crops encoding_dict = {} for cft_str in utils.define_mgdcrop_list(): cft_int = utils.vegtype_str2int(cft_str)[0] From a703cb6549576c63cedde68756292fab630543a5 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 11:31:46 -0600 Subject: [PATCH 258/406] generate_gdd20_baseline: Save input year range as attribute. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index b03d1fd658..14e2d5974f 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -210,7 +210,7 @@ def _add_time_axis(da_in): return da_out -def generate_gdd20_baseline(input_files, output_file, author, time_slice, variable): +def generate_gdd20_baseline(input_files, output_file, author, time_slice, variable, year_args): """ Generate stream_fldFileName_gdd20_baseline file from CTSM outputs """ @@ -256,6 +256,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab attrs={ "author": author, "created": dt.datetime.now().astimezone().isoformat(), + "input_year_range": f"{year_args[0]}-{year_args[1]}", }, ) all_files_in_same_dir = len(np.unique([os.path.dirname(file) for file in input_files])) == 1 @@ -324,4 +325,5 @@ def main(): args.author, time_slice, args.variable, + [args.first_year, args.last_year], ) From 7a5e067dd09970d74708ed9c678f1de7d8f29af6 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 11:32:33 -0600 Subject: [PATCH 259/406] generate_gdd20_baseline: Save input variable type as attribute. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 14e2d5974f..a87a2edb79 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -257,6 +257,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab "author": author, "created": dt.datetime.now().astimezone().isoformat(), "input_year_range": f"{year_args[0]}-{year_args[1]}", + "input_variable": variable, }, ) all_files_in_same_dir = len(np.unique([os.path.dirname(file) for file in input_files])) == 1 From 25896e460ac556a5cef78201f55b8f04fc6ce154 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 11:34:19 -0600 Subject: [PATCH 260/406] generate_gdd20_baseline: Functionize setup_output_dataset(). --- .../crop_calendars/generate_gdd20_baseline.py | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index a87a2edb79..5d6809f7ff 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -210,6 +210,31 @@ def _add_time_axis(da_in): return da_out +def setup_output_dataset(input_files, author, variable, year_args, ds_in): + """ + Set up output Dataset + """ + data_var_dict = {} + for gridding_var in GRIDDING_VAR_LIST: + data_var_dict[gridding_var] = ds_in[gridding_var] + ds_out = xr.Dataset( + data_vars=data_var_dict, + attrs={ + "author": author, + "created": dt.datetime.now().astimezone().isoformat(), + "input_year_range": f"{year_args[0]}-{year_args[1]}", + "input_variable": variable, + }, + ) + all_files_in_same_dir = len(np.unique([os.path.dirname(file) for file in input_files])) == 1 + if all_files_in_same_dir: + ds_out.attrs["input_files_dir"] = os.path.dirname(input_files[0]) + ds_out.attrs["input_files"] = ", ".join([os.path.basename(file) for file in input_files]) + else: + ds_out.attrs["input_files"] = ", ".join(input_files) + return ds_out + + def generate_gdd20_baseline(input_files, output_file, author, time_slice, variable, year_args): """ Generate stream_fldFileName_gdd20_baseline file from CTSM outputs @@ -248,24 +273,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab dummy_da = _add_time_axis(dummy_da) # Set up output Dataset - data_var_dict = {} - for gridding_var in GRIDDING_VAR_LIST: - data_var_dict[gridding_var] = ds_in[gridding_var] - ds_out = xr.Dataset( - data_vars=data_var_dict, - attrs={ - "author": author, - "created": dt.datetime.now().astimezone().isoformat(), - "input_year_range": f"{year_args[0]}-{year_args[1]}", - "input_variable": variable, - }, - ) - all_files_in_same_dir = len(np.unique([os.path.dirname(file) for file in input_files])) == 1 - if all_files_in_same_dir: - ds_out.attrs["input_files_dir"] = os.path.dirname(input_files[0]) - ds_out.attrs["input_files"] = ", ".join([os.path.basename(file) for file in input_files]) - else: - ds_out.attrs["input_files"] = ", ".join(input_files) + ds_out = setup_output_dataset(input_files, author, variable, year_args, ds_in) # Process all crops encoding_dict = {} From 7c2f5968702c3f17ab13e34d99cbbed642ba7604 Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Thu, 18 Jul 2024 11:45:33 -0600 Subject: [PATCH 261/406] Revisions according to review, plus some clean-up I used this test to confirm same answers: ./create_test ERS_D_Ld15.f45_f45_mg37.I2000Clm50FatesRs.derecho_intel.clm-FatesColdTwoStream -c /glade/campaign/cgd/tss/ctsm_baselines/ctsm5.2.012 This test had indicated differences with an earlier commit that I subsequently reverted. --- src/biogeochem/VOCEmissionMod.F90 | 140 +++++++----------------------- 1 file changed, 31 insertions(+), 109 deletions(-) diff --git a/src/biogeochem/VOCEmissionMod.F90 b/src/biogeochem/VOCEmissionMod.F90 index 9833fe3de9..b84d6a4ae8 100644 --- a/src/biogeochem/VOCEmissionMod.F90 +++ b/src/biogeochem/VOCEmissionMod.F90 @@ -423,7 +423,6 @@ subroutine VOCEmission (bounds, num_soilp, filter_soilp, & type(photosyns_type) , intent(in) :: photosyns_inst type(temperature_type) , intent(in) :: temperature_inst type(vocemis_type) , intent(inout) :: vocemis_inst - !by Hui, type(energyflux_type) , intent(in) :: energyflux_inst ! ! !REVISION HISTORY: @@ -479,15 +478,7 @@ subroutine VOCEmission (bounds, num_soilp, filter_soilp, & end if associate( & - !dz => col%dz , & ! Input: [real(r8) (:,:) ] depth of layer (m) - !bsw => soilstate_inst%bsw_col , & ! Input: [real(r8) (:,:) ] Clapp and Hornberger "b" (nlevgrnd) - !clayfrac => soilstate_inst%clayfrac_col , & ! Input: [real(r8) (:) ] fraction of soil that is clay - !sandfrac => soilstate_inst%sandfrac_col , & ! Input: [real(r8) (:) ] fraction of soil that is sand - !watsat => soilstate_inst%watsat_col , & ! Input: [real(r8) (:,:) ] volumetric soil water at saturation (porosity) (nlevgrnd) - !sucsat => soilstate_inst%sucsat_col , & ! Input: [real(r8) (:,:) ] minimum soil suction (mm) (nlevgrnd) - !h2osoi_vol => waterstate_inst%h2osoi_vol_col , & ! Input: [real(r8) (:,:) ] volumetric soil water (m3/m3) - !h2osoi_ice => waterstate_inst%h2osoi_ice_col , & ! Input: [real(r8) (:,:) ] ice soil content (kg/m3) - btran => energyflux_inst%btran_patch , & ! Input: [real(r8) (:) ] transpiration wetness factor (0 to 1) + btran => energyflux_inst%btran_patch , & ! Input: [real(r8) (:) ] transpiration wetness factor (0 to 1) forc_solad => atm2lnd_inst%forc_solad_downscaled_col, & ! Input: [real(r8) (:,:) ] direct beam radiation (visible only) forc_solai => atm2lnd_inst%forc_solai_grc , & ! Input: [real(r8) (:,:) ] diffuse radiation (visible only) @@ -573,12 +564,8 @@ subroutine VOCEmission (bounds, num_soilp, filter_soilp, & ! Activity factor for LAI (Guenther et al., 2006): all species gamma_l = get_gamma_L(fsun240(p), elai(p)) - !gamma_sm = 1.0_r8 - !Impact of soil moisture on isoprene emission + ! Impact of soil moisture on isoprene emission gamma_sm = get_gamma_SM(btran(p)) - ! Activity factor for soil moisture: all species (commented out for now) - ! gamma_sm = get_gamma_SM(clayfrac(p), sandfrac(p), h2osoi_vol(c,:), h2osoi_ice(c,:), & - ! col%dz(c,:), soilstate_inst%bsw_col(c,:), watsat(c,:), sucsat(c,:), root_depth(patch%itype(p))) ! Loop through VOCs for light, temperature and leaf age activity factor & apply ! all final activity factors to baseline emission factors @@ -825,105 +812,38 @@ end function get_gamma_L !----------------------------------------------------------------------- function get_gamma_SM(btran_in) + !--------------------------------------- ! May 22, 2024 ! Activity factor for soil moisture of Isoprene (Wang et al., 2022, JAMES) ! It is based on eq. (11) in the paper. Because the temperature response - ! of isoprene has been explicitly included in CLM; + ! of isoprene has been explicitly included in CLM; + !ARGUMENTS: implicit none real(r8),intent(in) :: btran_in - + !!!------- the drought algorithm-------- - real(r8), parameter :: a1 = -7.4463 - real(r8), parameter :: b1 = 3.2552 + real(r8), parameter :: a1 = -7.4463_r8 + real(r8), parameter :: b1 = 3.2552_r8 real(r8) :: get_gamma_SM - if (btran_in >= 1.) then - get_gamma_SM = 1 - else - get_gamma_SM = 1/(1+b1*exp(a1*(btran_in-0.2))) - endif + !--------------------------------------- + + if (btran_in >= 1._r8) then + get_gamma_SM = 1._r8 + else + get_gamma_SM = 1._r8 / (1._r8 + b1 * exp(a1 * (btran_in - 0.2_r8))) + endif + end function get_gamma_SM - - !function get_gamma_SM(clayfrac_in, sandfrac_in, h2osoi_vol_in, h2osoi_ice_in, dz_in, & - ! bsw_in, watsat_in, sucsat_in, root_depth_in) - ! ! - ! ! Activity factor for soil moisture (Guenther et al., 2006): all species - ! !---------------------------------- - ! ! Calculate the mean scaling factor throughout the root depth. - ! ! wilting point potential is in units of matric potential (mm) - ! ! (1 J/Kg = 0.001 MPa, approx = 0.1 m) - ! ! convert to volumetric soil water using equation 7.118 of the CLM4 Technical Note - ! ! - ! ! !USES: - ! use clm_varcon , only : denice - ! use clm_varpar , only : nlevsoi - ! ! - ! ! !ARGUMENTS: - ! implicit none - ! real(r8),intent(in) :: clayfrac_in - ! real(r8),intent(in) :: sandfrac_in - ! real(r8),intent(in) :: h2osoi_vol_in(nlevsoi) - ! real(r8),intent(in) :: h2osoi_ice_in(nlevsoi) - ! real(r8),intent(in) :: dz_in(nlevsoi) - ! real(r8),intent(in) :: bsw_in(nlevsoi) - ! real(r8),intent(in) :: watsat_in(nlevsoi) - ! real(r8),intent(in) :: sucsat_in(nlevsoi) - ! real(r8),intent(in) :: root_depth_in - ! ! - ! ! !LOCAL VARIABLES: - ! real(r8) :: get_gamma_SM - ! integer :: j - ! real(r8) :: nl ! temporary number of soil levels - ! real(r8) :: theta_ice ! water content in ice in m3/m3 - ! real(r8) :: wilt ! wilting point in m3/m3 - ! real(r8) :: theta1 ! temporary - ! real(r8), parameter :: deltheta1=0.06_r8 ! empirical coefficient - ! real(r8), parameter :: smpmax = 2.57e5_r8 ! maximum soil matrix potential - ! !----------------------------------------------------------------------- - - ! if ((clayfrac_in > 0) .and. (sandfrac_in > 0)) then - ! get_gamma_SM = 0._r8 - ! nl=0._r8 - ! - ! do j = 1,nlevsoi - ! if (sum(dz_in(1:j)) < root_depth_in) then - ! theta_ice = h2osoi_ice_in(j)/(dz_in(j)*denice) - ! wilt = ((smpmax/sucsat_in(j))**(-1._r8/bsw_in(j))) * (watsat_in(j) - theta_ice) - ! theta1 = wilt + deltheta1 - ! if (h2osoi_vol_in(j) >= theta1) then - ! get_gamma_SM = get_gamma_SM + 1._r8 - ! else if ( (h2osoi_vol_in(j) > wilt) .and. (h2osoi_vol_in(j) < theta1) ) then - ! get_gamma_SM = get_gamma_SM + ( h2osoi_vol_in(j) - wilt ) / deltheta1 - ! else - ! get_gamma_SM = get_gamma_SM + 0._r8 - ! end if - ! nl=nl+1._r8 - ! end if - ! end do - ! - ! if (nl > 0._r8) then - ! get_gamma_SM = get_gamma_SM/nl - ! endif - - ! if (get_gamma_SM > 1.0_r8) then - ! write(iulog,*) 'healdSM > 1: gamma_SM, nl', get_gamma_SM, nl - ! get_gamma_SM=1.0_r8 - ! endif - - ! else - ! get_gamma_SM = 1.0_r8 - ! end if - - !end function get_gamma_SM - + !----------------------------------------------------------------------- function get_gamma_T(t_veg240_in, t_veg24_in,t_veg_in, ct1_in, ct2_in, betaT_in, LDF_in, Ceo_in, Eopt, topt, ivt_in) - ! Activity factor for temperature + ! Activity factor for temperature !-------------------------------- - ! May 24, 2024 Hui updated the temperature response curves of isoprene for - ! Boreal Broadleaf Deciduous Shrub and Arctic C3 grass based on + ! May 24, 2024 Hui updated the temperature response curves of isoprene for + ! Boreal Broadleaf Deciduous Shrub and Arctic C3 grass based on ! Wang et al., 2024 (GRL) and Wang et al., 2024 (Nature Communications) !-------------------------------- ! Calculate both a light-dependent fraction as in Guenther et al., 2006 for isoprene @@ -931,6 +851,9 @@ function get_gamma_T(t_veg240_in, t_veg24_in,t_veg_in, ct1_in, ct2_in, betaT_in, ! form of an exponential. Final activity factor depends on light dependent fraction ! of compound type. ! + ! !USES: + use clm_varcon, only: tfrz + ! !ARGUMENTS: implicit none integer,intent(in) :: ivt_in @@ -951,6 +874,7 @@ function get_gamma_T(t_veg240_in, t_veg24_in,t_veg_in, ct1_in, ct2_in, betaT_in, real(r8) :: gamma_t_LIF ! activity factor for temperature real(r8) :: x ! temporary i real(r8) :: bet_arc_c3 + real(r8), parameter :: bet_arc_c3_max = 300._r8 real(r8), parameter :: co1 = 313._r8 ! empirical coefficient real(r8), parameter :: co2 = 0.6_r8 ! empirical coefficient real(r8), parameter :: co4 = 0.05_r8 ! empirical coefficient @@ -968,11 +892,11 @@ function get_gamma_T(t_veg240_in, t_veg24_in,t_veg_in, ct1_in, ct2_in, betaT_in, topt = co1 + (co2 * (t_veg240_in-tstd0)) if ( (ivt_in == nbrdlf_dcd_brl_shrub) ) then ! boreal-deciduous-shrub ! coming from BEAR-oNS campaign willows results - Eopt = 7.9 * exp (0.217_r8 * (t_veg24_in-273.15_r8-24.0_r8)) + Eopt = 7.9_r8 * exp (0.217_r8 * (t_veg24_in - tfrz - 24.0_r8)) else if ( (ivt_in == nc3_arctic_grass ) ) then ! boreal-grass - Eopt = exp(0.12*(t_veg240_in-288.15_r8)) + Eopt = exp(0.12_r8 * (t_veg240_in - tfrz - 15._r8)) else - Eopt = Ceo_in * exp (co4 * (t_veg24_in-tstd0)) * exp(co4 * (t_veg240_in -tstd0)) + Eopt = Ceo_in * exp (co4 * (t_veg24_in - tstd0)) * exp(co4 * (t_veg240_in - tstd0)) endif else @@ -982,13 +906,11 @@ function get_gamma_T(t_veg240_in, t_veg24_in,t_veg_in, ct1_in, ct2_in, betaT_in, x = ( (1._r8/topt) - (1._r8/(t_veg_in)) ) / ct3 ! for the boreal grass from BEAR-oNS campaign if ( (ivt_in == nc3_arctic_grass ) ) then ! boreal-grass - bet_arc_c3 = 95+9.49*exp(0.53*(288.15_r8-t_veg240_in)) - if (bet_arc_c3 .gt. 300) then - bet_arc_c3 = 300 - endif - gamma_t_LDF = Eopt * exp(bet_arc_c3*((1/303.15_r8 - 1.0_r8/(t_veg_in))/ct3)) + bet_arc_c3 = 95._r8 + 9.49_r8 * exp(0.53_r8 * (tfrz + 15._r8 - t_veg240_in)) + bet_arc_c3 = min(bet_arc_c3, bet_arc_c3_max) + gamma_t_LDF = Eopt * exp(bet_arc_c3 * ((1._r8 / (tfrz + 30._r8) - 1._r8 / t_veg_in) / ct3)) else - gamma_t_LDF = Eopt * ( ct2_in * exp(ct1_in * x)/(ct2_in - ct1_in * (1._r8 - exp(ct2_in * x))) ) + gamma_t_LDF = Eopt * ( ct2_in * exp(ct1_in * x) / (ct2_in - ct1_in * (1._r8 - exp(ct2_in * x))) ) endif From f909d2da504328459dd8129df708e289dfe33de3 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 13:15:49 -0600 Subject: [PATCH 262/406] Fill all land cells with MISSING_RX_GDD_VAL. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 5d6809f7ff..c0c9adb30b 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -18,10 +18,9 @@ from ctsm.crop_calendars.import_ds import import_ds import ctsm.crop_calendars.cropcal_utils as utils from ctsm.crop_calendars.grid_one_variable import grid_one_variable +from ctsm.crop_calendars.cropcal_module import MISSING_RX_GDD_VAL GRIDDING_VAR_LIST = ["patches1d_ixy", "patches1d_jxy", "lat", "lon"] -MISSING_FILL = -1 # Something impossible to ensure that you can mark it as a missing value, to be -# bilinear-interpolated STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of # shr_strdata_init_from_inline() for sdat_cropcal_gdd20_baseline @@ -266,7 +265,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab # Set up a dummy DataArray to use for crops without an assigned GDDN variable dummy_da = xr.DataArray( - data=MISSING_FILL * np.ones_like(ds_in[var_list_in[0]].values), + data=np.full_like(ds_in[var_list_in[0]].values, MISSING_RX_GDD_VAL), dims=ds_in[var_list_in[0]].dims, coords=ds_in[var_list_in[0]].coords, ) @@ -284,10 +283,10 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab # Which GDDN history variable does this crop use? E.g., GDD0, GDD10 gddn, gddn_str = _get_gddn_for_cft(cft_str, variable) - # Fill any missing values with MISSING_FILL. This will mean that gddmaturity in these cells + # Fill any missing values with MISSING_RX_GDD_VAL. This will mean that gddmaturity there # never changes. if gddn_str is None: - # Crop not handled yet? Fill it entirely with missing value + # Crop not handled yet? It's already filled with missing value this_da = dummy_da print(" dummy GDD20") else: @@ -295,6 +294,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab this_da = ds_in[gddn_str] this_da = _add_time_axis(this_da) print(f" {gddn_str}") + this_da = this_da.fillna(MISSING_RX_GDD_VAL) # Add attributes of output file if (gddn is None) != (gddn_str is None): @@ -305,7 +305,6 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab long_name = f"GDD{gddn}20" this_da.attrs["long_name"] = long_name + f" baseline for {cft_str}" this_da.attrs["units"] = "°C days" - # this_da.attrs["_FillValue"] = MISSING_FILL # Copy that to ds_out var_out = _get_output_varname(cft_str) From 8fa8506ceb753e6e1571f4b93adb4c43ea51d8d1 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 13:39:15 -0600 Subject: [PATCH 263/406] Set different gdd20 baseline file if using default gdd20 baseline seasons. --- bld/namelist_files/namelist_defaults_ctsm.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 3f8bc1f382..70ff048aa7 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1711,7 +1711,8 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.tweaked_latlons.nc -lnd/clm2/cropdata/calendars/processed/gdd20bl.copied_from.gdds_20230829_161011.v2.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/20230714_cropcals_pr2_1deg.actually2deg.1980-2009.from_GDDB20.interpd_halfdeg.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/gdd20bl.copied_from.gdds_20230829_161011.v2.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/360x720_120830_ESMFmesh_c20210507_cdf5.tweaked_latlons.nc From 945c8bbd142d0c528cbddbafd529a790a88c2dd8 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 14:34:03 -0600 Subject: [PATCH 264/406] Add utility function to get list of managed crops WITH grasses. define_mgdcrop_list() had excluded foddergrass and switchgrass. New function define_mgdcrop_list_withgrasses includes them. Renamed the existing function to define_mgdcrop_list_nograsses for clarity. --- python/ctsm/crop_calendars/cropcal_module.py | 2 +- python/ctsm/crop_calendars/cropcal_utils.py | 11 ++++++++++- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 4 ++-- python/ctsm/crop_calendars/generate_gdds_functions.py | 4 ++-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/python/ctsm/crop_calendars/cropcal_module.py b/python/ctsm/crop_calendars/cropcal_module.py index b87d26816f..08754f8823 100644 --- a/python/ctsm/crop_calendars/cropcal_module.py +++ b/python/ctsm/crop_calendars/cropcal_module.py @@ -345,7 +345,7 @@ def import_output( my_vars, year_1=None, year_n=None, - my_vegtypes=utils.define_mgdcrop_list(), + my_vegtypes=utils.define_mgdcrop_list_nograsses(), sdates_rx_ds=None, gdds_rx_ds=None, verbose=False, diff --git a/python/ctsm/crop_calendars/cropcal_utils.py b/python/ctsm/crop_calendars/cropcal_utils.py index 00ed2413d2..2157dc87f4 100644 --- a/python/ctsm/crop_calendars/cropcal_utils.py +++ b/python/ctsm/crop_calendars/cropcal_utils.py @@ -207,7 +207,7 @@ def is_each_vegtype(this_vegtypelist, this_filter, this_method): return [is_this_vegtype(x, this_filter, this_method) for x in this_vegtypelist] -def define_mgdcrop_list(): +def define_mgdcrop_list_nograsses(): """ List (strings) of managed crops in CLM. """ @@ -216,6 +216,15 @@ def define_mgdcrop_list(): is_crop = is_each_vegtype(defined_pftlist, notcrop_list, "notok_contains") return [defined_pftlist[i] for i, x in enumerate(is_crop) if x] +def define_mgdcrop_list_withgrasses(): + """ + List (strings) of managed crops in CLM. + """ + notcrop_list = ["tree", "c3_arctic_grass", "c3_non-arctic_grass", "c4_grass", "shrub", "unmanaged", "not_vegetated"] + defined_pftlist = define_pftlist() + is_crop = is_each_vegtype(defined_pftlist, notcrop_list, "notok_contains") + return [defined_pftlist[i] for i, x in enumerate(is_crop) if x] + def vegtype_str2int(vegtype_str, vegtype_mainlist=None): """ diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index c0c9adb30b..64cfd804fa 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -144,7 +144,7 @@ def _get_cft_list(crop_list): "cotton", "irrigated_cotton"] """ - mgdcrop_list = utils.define_mgdcrop_list() + mgdcrop_list = utils.define_mgdcrop_list_nograsses() cft_str_list = [] for crop_str in crop_list: cft_str_list += [x for x in mgdcrop_list if crop_str in x] @@ -276,7 +276,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab # Process all crops encoding_dict = {} - for cft_str in utils.define_mgdcrop_list(): + for cft_str in utils.define_mgdcrop_list_nograsses(): cft_int = utils.vegtype_str2int(cft_str)[0] print(f"{cft_str} ({cft_int})") diff --git a/python/ctsm/crop_calendars/generate_gdds_functions.py b/python/ctsm/crop_calendars/generate_gdds_functions.py index 2658e1de87..83c167af00 100644 --- a/python/ctsm/crop_calendars/generate_gdds_functions.py +++ b/python/ctsm/crop_calendars/generate_gdds_functions.py @@ -282,9 +282,9 @@ def import_and_process_1yr( # Get list of crops to include if skip_crops is not None: - crops_to_read = [c for c in utils.define_mgdcrop_list() if c not in skip_crops] + crops_to_read = [c for c in utils.define_mgdcrop_list_nograsses() if c not in skip_crops] else: - crops_to_read = utils.define_mgdcrop_list() + crops_to_read = utils.define_mgdcrop_list_nograsses() print(h1_filelist) dates_ds = import_ds( From ec9f07f2a6cc5f79e6c207e8050db74ff2d857ea Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 14:36:41 -0600 Subject: [PATCH 265/406] generate_gdd20_baseline: Use MGDCROP_LIST constant. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 64cfd804fa..1de6b5a739 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -23,6 +23,7 @@ GRIDDING_VAR_LIST = ["patches1d_ixy", "patches1d_jxy", "lat", "lon"] STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of # shr_strdata_init_from_inline() for sdat_cropcal_gdd20_baseline +MGDCROP_LIST = utils.define_mgdcrop_list_nograsses() def _parse_args(): @@ -144,10 +145,9 @@ def _get_cft_list(crop_list): "cotton", "irrigated_cotton"] """ - mgdcrop_list = utils.define_mgdcrop_list_nograsses() cft_str_list = [] for crop_str in crop_list: - cft_str_list += [x for x in mgdcrop_list if crop_str in x] + cft_str_list += [x for x in MGDCROP_LIST if crop_str in x] return cft_str_list @@ -276,7 +276,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab # Process all crops encoding_dict = {} - for cft_str in utils.define_mgdcrop_list_nograsses(): + for cft_str in MGDCROP_LIST: cft_int = utils.vegtype_str2int(cft_str)[0] print(f"{cft_str} ({cft_int})") From 669f227921e1bdf59157cf1da1c9b4e5176851bc Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 14:37:05 -0600 Subject: [PATCH 266/406] generate_gdd20_baseline: Include mgd grasses. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 1de6b5a739..effc8e54ec 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -23,7 +23,7 @@ GRIDDING_VAR_LIST = ["patches1d_ixy", "patches1d_jxy", "lat", "lon"] STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of # shr_strdata_init_from_inline() for sdat_cropcal_gdd20_baseline -MGDCROP_LIST = utils.define_mgdcrop_list_nograsses() +MGDCROP_LIST = utils.define_mgdcrop_list_withgrasses() def _parse_args(): From a62460642403d827b58807cfe5961df0db026c71 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 14:41:46 -0600 Subject: [PATCH 267/406] generate_gdd20_baseline: Include unmanaged crops. --- python/ctsm/crop_calendars/cropcal_utils.py | 8 ++++++++ python/ctsm/crop_calendars/generate_gdd20_baseline.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/cropcal_utils.py b/python/ctsm/crop_calendars/cropcal_utils.py index 2157dc87f4..1a80448c1b 100644 --- a/python/ctsm/crop_calendars/cropcal_utils.py +++ b/python/ctsm/crop_calendars/cropcal_utils.py @@ -206,6 +206,14 @@ def is_each_vegtype(this_vegtypelist, this_filter, this_method): return [is_this_vegtype(x, this_filter, this_method) for x in this_vegtypelist] +def define_crop_list(): + """ + List (strings) of managed crops in CLM. + """ + notcrop_list = ["tree", "c3_arctic_grass", "c3_non-arctic_grass", "c4_grass", "shrub", "not_vegetated"] + defined_pftlist = define_pftlist() + is_crop = is_each_vegtype(defined_pftlist, notcrop_list, "notok_contains") + return [defined_pftlist[i] for i, x in enumerate(is_crop) if x] def define_mgdcrop_list_nograsses(): """ diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index effc8e54ec..3d082d0fde 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -23,7 +23,7 @@ GRIDDING_VAR_LIST = ["patches1d_ixy", "patches1d_jxy", "lat", "lon"] STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of # shr_strdata_init_from_inline() for sdat_cropcal_gdd20_baseline -MGDCROP_LIST = utils.define_mgdcrop_list_withgrasses() +MGDCROP_LIST = utils.define_crop_list() def _parse_args(): From 9ba2b5f164ad800e6c10f9e7b5f373c9f31ab504 Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Mon, 22 Jul 2024 10:51:05 -0600 Subject: [PATCH 268/406] rename vars, add arg to test and depth arg. --- src/biogeophys/TemperatureType.F90 | 16 ++++----- src/biogeophys/WaterStateBulkType.F90 | 8 ++--- src/biogeophys/WaterStateType.F90 | 28 +++++++-------- src/biogeophys/WaterType.F90 | 34 ++++++++++--------- src/main/clm_instMod.F90 | 15 ++++---- .../unittestWaterTypeFactory.F90 | 28 ++++++++++++++- 6 files changed, 79 insertions(+), 50 deletions(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 6084c7ee2c..a256e22eb7 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -120,8 +120,8 @@ module TemperatureType real(r8), pointer :: c_h2osfc_col (:) ! heat capacity of surface water ! Namelist parameters for initialization - real(r8), private :: excess_ice_coldstart_depth ! depth below which excess ice will be present - real(r8), private :: excess_ice_coldstart_temp ! coldstart temperature of layers with excess ice present + real(r8) :: excess_ice_coldstart_depth ! depth below which excess ice will be present + real(r8) :: excess_ice_coldstart_temp ! coldstart temperature of layers with excess ice present contains @@ -147,7 +147,7 @@ module TemperatureType !------------------------------------------------------------------------ subroutine Init(this, bounds, & em_roof_lun, em_wall_lun, em_improad_lun, em_perroad_lun, & - is_simple_buildtemp, is_prog_buildtemp, exice_init_stream_col, NLFileName) + is_simple_buildtemp, is_prog_buildtemp, exice_init_conc_col, NLFileName) ! ! !DESCRIPTION: ! @@ -162,7 +162,7 @@ subroutine Init(this, bounds, & real(r8) , intent(in) :: em_perroad_lun(bounds%begl:) logical , intent(in) :: is_simple_buildtemp ! Simple building temp is being used logical , intent(in) :: is_prog_buildtemp ! Prognostic building temp is being used - real(r8) , intent(in) :: exice_init_stream_col(bounds%begc:) ! initial excess ice concentration from the stream file + real(r8) , intent(in) :: exice_init_conc_col(bounds%begc:) ! initial coldstart excess ice concentration (from the stream file) character(len=*) , intent(in) :: NLFilename ! Namelist filename @@ -175,7 +175,7 @@ subroutine Init(this, bounds, & em_improad_lun(bounds%begl:bounds%endl), & em_perroad_lun(bounds%begl:bounds%endl), & is_simple_buildtemp, is_prog_buildtemp, & - exice_init_stream_col(bounds%begc:bounds%endc) ) + exice_init_conc_col(bounds%begc:bounds%endc) ) end subroutine Init @@ -650,7 +650,7 @@ end subroutine InitHistory !----------------------------------------------------------------------- subroutine InitCold(this, bounds, & em_roof_lun, em_wall_lun, em_improad_lun, em_perroad_lun, & - is_simple_buildtemp, is_prog_buildtemp, exice_init_stream_col) + is_simple_buildtemp, is_prog_buildtemp, exice_init_conc_col) ! ! !DESCRIPTION: ! Initialize cold start conditions for module variables @@ -675,7 +675,7 @@ subroutine InitCold(this, bounds, & real(r8) , intent(in) :: em_perroad_lun(bounds%begl:) logical , intent(in) :: is_simple_buildtemp ! Simple building temp is being used logical , intent(in) :: is_prog_buildtemp ! Prognostic building temp is being used - real(r8) , intent(in) :: exice_init_stream_col(bounds%begc:) ! initial ammount of excess ice from the stream file + real(r8) , intent(in) :: exice_init_conc_col(bounds%begc:) ! initial coldstart excess ice concentration (from the stream file) ! ! !LOCAL VARIABLES: integer :: j,l,c,p ! indices @@ -757,7 +757,7 @@ subroutine InitCold(this, bounds, & end if else this%t_soisno_col(c,1:nlevgrnd) = 272._r8 - if (use_excess_ice .and. exice_init_stream_col(c) > 0.0_r8) then + if (use_excess_ice .and. exice_init_conc_col(c) > 0.0_r8) then nexice_start = nlevsoi - 1 if (this%excess_ice_coldstart_depth <= 0.0_r8) then ! we double check this here, and when building namelists diff --git a/src/biogeophys/WaterStateBulkType.F90 b/src/biogeophys/WaterStateBulkType.F90 index 4f03ba177e..4cd425c976 100644 --- a/src/biogeophys/WaterStateBulkType.F90 +++ b/src/biogeophys/WaterStateBulkType.F90 @@ -47,7 +47,7 @@ module WaterStateBulkType !------------------------------------------------------------------------ subroutine InitBulk(this, bounds, info, vars, & - h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer, NLFilename, exice_init_stream_col) + h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer, exice_coldstart_depth, exice_init_conc_col) class(waterstatebulk_type), intent(inout) :: this type(bounds_type) , intent(in) :: bounds @@ -57,8 +57,8 @@ subroutine InitBulk(this, bounds, info, vars, & real(r8) , intent(in) :: watsat_col(bounds%begc:, 1:) ! volumetric soil water at saturation (porosity) real(r8) , intent(in) :: t_soisno_col(bounds%begc:, -nlevsno+1:) ! col soil temperature (Kelvin) logical , intent(in) :: use_aquifer_layer ! whether an aquifer layer is used in this run - character(len=*) , intent(in) :: NLFilename ! Namelist filename - real(r8) , intent(in) :: exice_init_stream_col(bounds%begc:) ! initial ammount of excess ice from stream + real(r8) , intent(in) :: exice_coldstart_depth ! depth below which excess ice will be present + real(r8) , intent(in) :: exice_init_conc_col(bounds%begc:) ! initial coldstart excess ice concentration (from the stream file) call this%Init(bounds = bounds, & info = info, & @@ -67,7 +67,7 @@ subroutine InitBulk(this, bounds, info, vars, & watsat_col = watsat_col, & t_soisno_col = t_soisno_col, & use_aquifer_layer = use_aquifer_layer, & - NLFilename = NLFilename, exice_init_stream_col = exice_init_stream_col(bounds%begc:bounds%endc)) + exice_coldstart_depth = exice_coldstart_depth, exice_init_conc_col = exice_init_conc_col(bounds%begc:bounds%endc)) call this%InitBulkAllocate(bounds) diff --git a/src/biogeophys/WaterStateType.F90 b/src/biogeophys/WaterStateType.F90 index 4c82e4efa6..8f0ba89104 100644 --- a/src/biogeophys/WaterStateType.F90 +++ b/src/biogeophys/WaterStateType.F90 @@ -80,7 +80,7 @@ module WaterStateType !------------------------------------------------------------------------ subroutine Init(this, bounds, info, tracer_vars, & - h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer, NLFilename, exice_init_stream_col) + h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer, exice_coldstart_depth, exice_init_conc_col) class(waterstate_type), intent(inout) :: this type(bounds_type) , intent(in) :: bounds @@ -90,8 +90,8 @@ subroutine Init(this, bounds, info, tracer_vars, & real(r8) , intent(in) :: watsat_col(bounds%begc:, 1:) ! volumetric soil water at saturation (porosity) real(r8) , intent(in) :: t_soisno_col(bounds%begc:, -nlevsno+1:) ! col soil temperature (Kelvin) logical , intent(in) :: use_aquifer_layer ! whether an aquifer layer is used in this run - character(len=*) , intent(in) :: NLFilename ! Namelist filename - real(r8) , intent(in) :: exice_init_stream_col(bounds%begc:bounds%endc) ! initial ammount of excess ice from stream + real(r8) , intent(in) :: exice_coldstart_depth ! depth below which excess ice will be present + real(r8) , intent(in) :: exice_init_conc_col(bounds%begc:bounds%endc) ! initial coldstart excess ice concentration (from the stream file) this%info => info @@ -103,7 +103,7 @@ subroutine Init(this, bounds, info, tracer_vars, & watsat_col = watsat_col, & t_soisno_col = t_soisno_col, & use_aquifer_layer = use_aquifer_layer, & - NLFilename = NLFilename, exice_init_stream_col = exice_init_stream_col) + exice_coldstart_depth = exice_coldstart_depth , exice_init_conc_col = exice_init_conc_col) end subroutine Init @@ -322,7 +322,7 @@ end subroutine InitHistory !----------------------------------------------------------------------- subroutine InitCold(this, bounds, & - h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer, NLFilename, exice_init_stream_col) + h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer, exice_coldstart_depth, exice_init_conc_col) ! ! !DESCRIPTION: ! Initialize time constant variables and cold start conditions @@ -342,12 +342,12 @@ subroutine InitCold(this, bounds, & real(r8) , intent(in) :: watsat_col(bounds%begc:, 1:) ! volumetric soil water at saturation (porosity) real(r8) , intent(in) :: t_soisno_col(bounds%begc:, -nlevsno+1:) ! col soil temperature (Kelvin) logical , intent(in) :: use_aquifer_layer ! whether an aquifer layer is used in this run - character(len=*) , intent(in) :: NLFilename ! Namelist filename - real(r8) , intent(in) :: exice_init_stream_col(bounds%begc:bounds%endc) ! initial ammount of excess ice from stream + real(r8) , intent(in) :: exice_coldstart_depth ! depth below which excess ice will be present + real(r8) , intent(in) :: exice_init_conc_col(bounds%begc:bounds%endc) ! initial coldstart excess ice concentration (from the stream file) ! ! !LOCAL VARIABLES: integer :: c,j,l,nlevs,g - integer :: nbedrock, n05m ! layer containing 0.5 m + integer :: nbedrock, nexice ! layer containing 0.5 m real(r8) :: ratio !----------------------------------------------------------------------- @@ -550,7 +550,7 @@ subroutine InitCold(this, bounds, & this%dynbal_baseline_ice_col(bounds%begc:bounds%endc) = 0._r8 !Initialize excess ice - this%exice_bulk_init(bounds%begc:bounds%endc) = exice_init_stream_col(bounds%begc:bounds%endc) + this%exice_bulk_init(bounds%begc:bounds%endc) = exice_init_conc_col(bounds%begc:bounds%endc) this%excess_ice_col(bounds%begc:bounds%endc,:) = 0.0_r8 if (use_excess_ice) then do c = bounds%begc,bounds%endc @@ -558,10 +558,10 @@ subroutine InitCold(this, bounds, & l = col%landunit(c) if (.not. lun%lakpoi(l)) then !not lake if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then - if (zisoi(nlevsoi) >= 0.5_r8) then - call find_soil_layer_containing_depth(0.5_r8,n05m) + if (zisoi(nlevsoi) >= exice_coldstart_depth) then + call find_soil_layer_containing_depth(exice_coldstart_depth,nexice) else - n05m=nlevsoi-1 + nexice=nlevsoi-1 endif if (use_bedrock .and. col%nbedrock(c) <=nlevsoi) then nbedrock = col%nbedrock(c) @@ -569,8 +569,8 @@ subroutine InitCold(this, bounds, & nbedrock = nlevsoi endif do j = 2, nlevmaxurbgrnd ! ignore first layer - if (n05m= n05m .and. j= nexice .and. j Date: Mon, 22 Jul 2024 10:55:49 -0600 Subject: [PATCH 269/406] add defaults for the stream test. --- .../testdefs/testmods_dirs/clm/ExcessIceStreams/user_nl_clm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cime_config/testdefs/testmods_dirs/clm/ExcessIceStreams/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/ExcessIceStreams/user_nl_clm index f61ca32a79..aa2c9efa5b 100644 --- a/cime_config/testdefs/testmods_dirs/clm/ExcessIceStreams/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/ExcessIceStreams/user_nl_clm @@ -1,2 +1,4 @@ use_excess_ice = .true. use_excess_ice_streams = .true. + excess_ice_coldstart_depth = 0.5 + excess_ice_coldstart_temp = -5.0 From dbf00993bfb1ff626f4f326e03b585293f9bc5da Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Mon, 22 Jul 2024 10:58:36 -0600 Subject: [PATCH 270/406] fix indent --- bld/CLMBuildNamelist.pm | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 498de96a4d..fa0d4a0382 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4589,13 +4589,13 @@ sub setup_logic_exice { my $finidat = $nl->get_value('finidat'); # If coldstart and use_excess_ice is on: if ( ( (not defined($use_exice_streams)) && value_is_true($use_exice) ) && string_is_undef_or_empty($finidat) ) { - $nl->set_variable_value('exice_streams', 'use_excess_ice_streams' , '.true.'); - $use_exice_streams = '.true.'; - # if excess ice is turned off - } elsif ( (not defined($use_exice_streams)) && (not value_is_true($use_exice)) ) { - $nl->set_variable_value('exice_streams', 'use_excess_ice_streams' , '.false.'); - $use_exice_streams = '.false.'; - } + $nl->set_variable_value('exice_streams', 'use_excess_ice_streams' , '.true.'); + $use_exice_streams = '.true.'; + # if excess ice is turned off + } elsif ( (not defined($use_exice_streams)) && (not value_is_true($use_exice)) ) { + $nl->set_variable_value('exice_streams', 'use_excess_ice_streams' , '.false.'); + $use_exice_streams = '.false.'; + } # If excess ice streams is on if (defined($use_exice_streams) && value_is_true($use_exice_streams)) { # Can only be true if excess ice is also on, otherwise fail From 753fda3ff0147837231a73c9c728dd9ce47b5997 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Tue, 23 Jul 2024 09:27:15 -0600 Subject: [PATCH 271/406] Format with black. --- python/ctsm/crop_calendars/cropcal_utils.py | 22 +++++++++++++++++-- .../crop_calendars/generate_gdd20_baseline.py | 12 +++------- .../crop_calendars/generate_gdds_functions.py | 17 ++++++++------ .../ctsm/crop_calendars/interpolate_gdds.py | 2 +- 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/python/ctsm/crop_calendars/cropcal_utils.py b/python/ctsm/crop_calendars/cropcal_utils.py index 1a80448c1b..584046edee 100644 --- a/python/ctsm/crop_calendars/cropcal_utils.py +++ b/python/ctsm/crop_calendars/cropcal_utils.py @@ -206,15 +206,24 @@ def is_each_vegtype(this_vegtypelist, this_filter, this_method): return [is_this_vegtype(x, this_filter, this_method) for x in this_vegtypelist] + def define_crop_list(): """ List (strings) of managed crops in CLM. """ - notcrop_list = ["tree", "c3_arctic_grass", "c3_non-arctic_grass", "c4_grass", "shrub", "not_vegetated"] + notcrop_list = [ + "tree", + "c3_arctic_grass", + "c3_non-arctic_grass", + "c4_grass", + "shrub", + "not_vegetated", + ] defined_pftlist = define_pftlist() is_crop = is_each_vegtype(defined_pftlist, notcrop_list, "notok_contains") return [defined_pftlist[i] for i, x in enumerate(is_crop) if x] + def define_mgdcrop_list_nograsses(): """ List (strings) of managed crops in CLM. @@ -224,11 +233,20 @@ def define_mgdcrop_list_nograsses(): is_crop = is_each_vegtype(defined_pftlist, notcrop_list, "notok_contains") return [defined_pftlist[i] for i, x in enumerate(is_crop) if x] + def define_mgdcrop_list_withgrasses(): """ List (strings) of managed crops in CLM. """ - notcrop_list = ["tree", "c3_arctic_grass", "c3_non-arctic_grass", "c4_grass", "shrub", "unmanaged", "not_vegetated"] + notcrop_list = [ + "tree", + "c3_arctic_grass", + "c3_non-arctic_grass", + "c4_grass", + "shrub", + "unmanaged", + "not_vegetated", + ] defined_pftlist = define_pftlist() is_crop = is_each_vegtype(defined_pftlist, notcrop_list, "notok_contains") return [defined_pftlist[i] for i, x in enumerate(is_crop) if x] diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 3d082d0fde..06d3a9cd21 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -72,27 +72,21 @@ def _parse_args(): parser.add_argument( "-y1", "--first-year", - help=( - "First calendar year to include" - ), + help=("First calendar year to include"), type=int, required=False, ) parser.add_argument( "-yN", "--last-year", - help=( - "Last calendar year to include" - ), + help=("Last calendar year to include"), type=int, required=False, ) parser.add_argument( "-v", "--variable", - help=( - "Which type of variable should be processed?" - ), + help=("Which type of variable should be processed?"), required=False, default="GDDBX", choices=["GDDBX", "GDDB20"], diff --git a/python/ctsm/crop_calendars/generate_gdds_functions.py b/python/ctsm/crop_calendars/generate_gdds_functions.py index 83c167af00..50e2ac3d00 100644 --- a/python/ctsm/crop_calendars/generate_gdds_functions.py +++ b/python/ctsm/crop_calendars/generate_gdds_functions.py @@ -651,10 +651,7 @@ def import_and_process_1yr( ) if save_figs and np.any(np.isnan(gddharv_atharv_p)): if np.all(np.isnan(gddharv_atharv_p)): - log( - logger, - " ❗ All GDDHARV are NaN; should only affect figure" - ) + log(logger, " ❗ All GDDHARV are NaN; should only affect figure") check_gddharv = False else: log( @@ -744,9 +741,15 @@ def import_and_process_1yr( ) else: error(logger, "Unexpected NaN for last season's GDD accumulation.") - if save_figs and check_gddharv and np.any( - np.isnan( - gddharv_yp_list[var][year_index - 1, active_this_year_where_gs_lastyr_indices] + if ( + save_figs + and check_gddharv + and np.any( + np.isnan( + gddharv_yp_list[var][ + year_index - 1, active_this_year_where_gs_lastyr_indices + ] + ) ) ): if incorrectly_daily: diff --git a/python/ctsm/crop_calendars/interpolate_gdds.py b/python/ctsm/crop_calendars/interpolate_gdds.py index 809be98826..830df18c73 100755 --- a/python/ctsm/crop_calendars/interpolate_gdds.py +++ b/python/ctsm/crop_calendars/interpolate_gdds.py @@ -70,7 +70,7 @@ def _setup_process_args(): help="Interpolate variables whose names start with this string", type=str, required=False, - default="gdd1_" + default="gdd1_", ) parser.add_argument( "--overwrite", From c0a3556886e31c5c4bfc55ceccec0925e407b1f4 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Tue, 23 Jul 2024 09:27:53 -0600 Subject: [PATCH 272/406] Add previous commit to .git-blame-ignore-revs. --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 44a697c343..31f63ef8ae 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -48,3 +48,4 @@ aa04d1f7d86cc2503b98b7e2b2d84dbfff6c316b 9660667b1267dcd4150889f5f39db540158be74a 665cf86102e09b4c4c5a140700676dca23bc55a9 045d90f1d80f713eb3ae0ac58f6c2352937f1eb0 +753fda3ff0147837231a73c9c728dd9ce47b5997 From 811659f6a6bd59a91bef8f10aa0d20b0fc268630 Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Tue, 23 Jul 2024 11:11:41 -0600 Subject: [PATCH 273/406] make unit-tests happy --- src/biogeophys/TemperatureType.F90 | 9 +++++++-- src/unit_test_shr/unittestDustEmisInputs.F90 | 6 +++++- src/unit_test_shr/unittestWaterTypeFactory.F90 | 6 +++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index a256e22eb7..3771735bb7 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -163,10 +163,15 @@ subroutine Init(this, bounds, & logical , intent(in) :: is_simple_buildtemp ! Simple building temp is being used logical , intent(in) :: is_prog_buildtemp ! Prognostic building temp is being used real(r8) , intent(in) :: exice_init_conc_col(bounds%begc:) ! initial coldstart excess ice concentration (from the stream file) - character(len=*) , intent(in) :: NLFilename ! Namelist filename + character(len=*) , intent(in), optional :: NLFilename ! Namelist filename - call this%ReadNL(NLFilename) + if ( present(NLFilename) ) then + call this%ReadNL(NLFilename) + else ! this is needed for testing + this%excess_ice_coldstart_depth = 0.5_r8 + this%excess_ice_coldstart_temp = -5.0_r8 + endif call this%InitAllocate ( bounds ) call this%InitHistory ( bounds, is_simple_buildtemp, is_prog_buildtemp ) call this%InitCold ( bounds, & diff --git a/src/unit_test_shr/unittestDustEmisInputs.F90 b/src/unit_test_shr/unittestDustEmisInputs.F90 index 6b15680ef7..af3ad63890 100644 --- a/src/unit_test_shr/unittestDustEmisInputs.F90 +++ b/src/unit_test_shr/unittestDustEmisInputs.F90 @@ -50,6 +50,7 @@ subroutine setUp(this) character(len=5) :: NLFilename = 'none' real(r8), allocatable :: urb_em(:) + real(r8), allocatable :: exice_init_conc_col(:) integer :: begl, endl, begc, endc integer :: c type(atm2lnd_params_type) :: atm2lnd_params @@ -91,12 +92,15 @@ subroutine setUp(this) call this%frictionvel_inst%InitForTesting(bounds) allocate( urb_em(begl:endl) ) urb_em(begl:endl) = 0.99_r8 ! Arbitrary won't matter here + allocate( exice_init_conc_col(begc:endc) ) + exice_init_conc_col(begc:endc) = 0.0_r8 ! zero, so it doesn't affect anything. call this%temperature_inst%Init(bounds, & em_roof_lun=urb_em(begl:endl), & em_wall_lun=urb_em(begl:endl), & em_improad_lun=urb_em(begl:endl), & em_perroad_lun=urb_em(begl:endl), & - is_simple_buildtemp=.true., is_prog_buildtemp=.false.) + is_simple_buildtemp=.true., is_prog_buildtemp=.false., & + exice_init_conc_col = exice_init_conc_col(begc:endc)) deallocate( urb_em ) end subroutine setUp diff --git a/src/unit_test_shr/unittestWaterTypeFactory.F90 b/src/unit_test_shr/unittestWaterTypeFactory.F90 index 06362ee1d0..2d83fd0058 100644 --- a/src/unit_test_shr/unittestWaterTypeFactory.F90 +++ b/src/unit_test_shr/unittestWaterTypeFactory.F90 @@ -140,7 +140,7 @@ subroutine create_water_type(this, water_inst, & end if if (present(exice_init_conc_col)) then - SHR_ASSERT_ALL_FL((ubound(exice_init_conc_col,1) == bounds%enc), sourcefile, __LINE__) + SHR_ASSERT_ALL_FL((ubound(exice_init_conc_col,1) == bounds%endc), sourcefile, __LINE__) endif if (present(enable_consistency_checks)) then @@ -177,9 +177,9 @@ subroutine create_water_type(this, water_inst, & l_exice_coldstart_depth = 0.5_r8 endif if (present(exice_init_conc_col)) then - l_exice_init_conc_col(:,:) = exice_init_conc_col + l_exice_init_conc_col(:) = exice_init_conc_col else - l_exice_init_conc_col(:,:) = 0.01_r8 + l_exice_init_conc_col(:) = 0.0_r8 endif call water_inst%InitForTesting(bounds, params, & From 88adda49954f51a4aa3fb1763e0c9d2e152f7c04 Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Tue, 23 Jul 2024 12:29:08 -0600 Subject: [PATCH 274/406] Make warning for non-equilibrium. --- src/biogeophys/WaterStateType.F90 | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/biogeophys/WaterStateType.F90 b/src/biogeophys/WaterStateType.F90 index 8f0ba89104..35441d65d9 100644 --- a/src/biogeophys/WaterStateType.F90 +++ b/src/biogeophys/WaterStateType.F90 @@ -724,6 +724,17 @@ subroutine Restart(this, bounds, ncid, flag, & ! Restart excess ice vars if (.not. use_excess_ice) then ! no need to even define the restart vars + call RestartExcessIceIssue( & + ncid = ncid, & + flag = flag, & + excess_ice_on_restart = excess_ice_on_restart) + if( excess_ice_on_restart ) then + if (masterproc) then + write(iulog,*) '--WARNING-- Starting from initial conditions with excess ice present.' + write(iulog,*) 'But use_excess_ice=.false.' + write(iulog,*) 'This will cause soil moisture and temperature not being in equilibrium' + endif + endif this%excess_ice_col(bounds%begc:bounds%endc,-nlevsno+1:nlevmaxurbgrnd)=0.0_r8 else call RestartExcessIceIssue( & From b11bc8923bbaa6edc6c60913c37c1ba7f906d2b6 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 24 Jul 2024 11:21:17 -0600 Subject: [PATCH 275/406] Update cism to work with this cmeps version --- .gitmodules | 2 +- components/cism | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 501708f587..810b43a266 100644 --- a/.gitmodules +++ b/.gitmodules @@ -36,7 +36,7 @@ fxDONOTUSEurl = https://github.com/NCAR/fates-release [submodule "cism"] path = components/cism url = https://github.com/ESCOMP/CISM-wrapper -fxtag = cismwrap_2_2_001 +fxtag = cismwrap_2_2_002 fxrequired = ToplevelRequired # Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed fxDONOTUSEurl = https://github.com/ESCOMP/CISM-wrapper diff --git a/components/cism b/components/cism index c05dd5c4fc..c84cc9f5b3 160000 --- a/components/cism +++ b/components/cism @@ -1 +1 @@ -Subproject commit c05dd5c4fc85327e76523aaea9cfe1e388748928 +Subproject commit c84cc9f5b3103766a35d0a7ddd5e9dbd7deae762 From 38656f9c9904a312b041afaf9d15ed2e95f3838a Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 24 Jul 2024 11:49:01 -0600 Subject: [PATCH 276/406] Get LILAC test working --- bld/env_run.xml | 1 + python/ctsm/lilac_make_runtime_inputs.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/bld/env_run.xml b/bld/env_run.xml index f3b7467168..cd865ad82c 100644 --- a/bld/env_run.xml +++ b/bld/env_run.xml @@ -9,6 +9,7 @@ Sample env_run.xml file that allows build-namelist to be run for testing in this --> + diff --git a/python/ctsm/lilac_make_runtime_inputs.py b/python/ctsm/lilac_make_runtime_inputs.py index 71f3c9bbe4..33b87c543e 100644 --- a/python/ctsm/lilac_make_runtime_inputs.py +++ b/python/ctsm/lilac_make_runtime_inputs.py @@ -47,6 +47,12 @@ TRUE,FALSE + + + logical + TRUE,FALSE + + """ From e438a907e1fa23934255a10dddcfee4601bcd20d Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 24 Jul 2024 12:14:39 -0600 Subject: [PATCH 277/406] Fix test number --- bld/unit_testers/build-namelist_test.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index 198df535ad..1533e00d73 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -163,7 +163,7 @@ sub cat_and_create_namelistinfile { # # Figure out number of tests that will run # -my $ntests = 3311; +my $ntests = 3317; if ( defined($opts{'compare'}) ) { $ntests += 2052; From 71ca96d2d8e14fbbe9bc7686c2d905907b4d43d5 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:29:29 -0600 Subject: [PATCH 278/406] Delete an unused ncd_log. --- src/biogeophys/TemperatureType.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index bb579b8031..5ba64f9b1e 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -897,7 +897,7 @@ subroutine Restart(this, bounds, ncid, flag, is_simple_buildtemp, is_prog_buildt use shr_log_mod , only : errMsg => shr_log_errMsg use spmdMod , only : masterproc use abortutils , only : endrun - use ncdio_pio , only : file_desc_t, ncd_double, ncd_int, ncd_log + use ncdio_pio , only : file_desc_t, ncd_double, ncd_int use restUtilMod ! ! !ARGUMENTS: From ad2c7f3af389ad2b2b6464987a06d1200049a813 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:30:31 -0600 Subject: [PATCH 279/406] Delete a troubleshooting log message. --- bld/CLMBuildNamelist.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index ad110b017e..5202717d74 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4200,7 +4200,6 @@ sub setup_logic_cropcal_streams { my $gdd20_season_end_file = $nl->get_value('stream_fldFileName_gdd20_season_end') ; if ( &string_is_undef_or_empty($gdd20_season_start_file) or &string_is_undef_or_empty($gdd20_season_end_file) ) { $log->message($gdd20_season_start_file); - $log->message('abcd'); $log->message($gdd20_season_end_file); $log->fatal_error("If stream_gdd20_seasons is true, gdd20 season start and end files must be provided." ); } From 592476f7c6ae5751b6360c372ed70ad4f94af0c7 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:34:29 -0600 Subject: [PATCH 280/406] Improve comments/error at "Handle invalid gdd20 season values." --- src/cpl/share_esmf/cropcalStreamMod.F90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index ef3bab03c5..d17ce1c259 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -793,9 +793,10 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Handle invalid gdd20 season values if (any(gdd20_season_starts(begp:endp) < 1._r8 .or. gdd20_season_ends(begp:endp) < 1._r8)) then - ! Fail if not allowing fallback to paramfile sowing windows + ! Fail if not allowing fallback to paramfile sowing windows. Only need to check for + ! values < 1 because values outside [1, 366] are set to -1 above. if ((.not. allow_invalid_gdd20_season_inputs) .and. any(gdd20_season_starts(begp:endp) < 1._r8 .and. patch%wtgcell(begp:endp) > 0._r8 .and. patch%itype(begp:endp) >= npcropmin)) then - write(iulog, *) 'At least one crop in one gridcell has invalid gdd20 season start date(s). To ignore and fall back to paramfile sowing windows, set allow_invalid_gdd20_season_inputs to .true.' + write(iulog, *) 'At least one crop in one gridcell has invalid gdd20 season start and/or end date(s). To ignore and fall back to paramfile sowing windows for such crop-gridcells, set allow_invalid_gdd20_season_inputs to .true.' write(iulog, *) 'Affected crops:' do ivt = npcropmin, mxpft do fp = 1, num_pcropp From 013028c4687f564953ef5443e159b02f00220297 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:35:49 -0600 Subject: [PATCH 281/406] Replace SSR with Sam Rabin. --- src/biogeochem/CNPhenologyMod.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index d1d3c85b21..159e688dbd 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2699,7 +2699,7 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & did_rx_gdds = .true. if (adapt_cropcal_rx_cultivar_gdds .and. crop_inst%gdd20_baseline_patch(p) > min_gdd20_baseline) then gddmaturity(p) = gddmaturity(p) * gdd20 / crop_inst%gdd20_baseline_patch(p) - !TODO SSR: Set maximum and minimum gddmaturity + !TODO Sam Rabin: Set maximum and minimum gddmaturity end if else if (ivt(p) == nwwheat .or. ivt(p) == nirrig_wwheat) then gddmaturity(p) = hybgdd(ivt(p)) @@ -2716,11 +2716,11 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & ivt(p) == nmiscanthus .or. ivt(p) == nirrig_miscanthus .or. & ivt(p) == nswitchgrass .or. ivt(p) == nirrig_switchgrass) then gddmaturity(p) = max(950._r8, min(gdd20*0.85_r8, hybgdd(ivt(p)))) - if (do_plant_normal) then ! TODO SSR: Add ".and. .not. do_plant_prescribed"? + if (do_plant_normal) then ! TODO Sam Rabin: Add ".and. .not. do_plant_prescribed"? gddmaturity(p) = max(950._r8, min(gddmaturity(p)+150._r8, 1850._r8)) end if else - ! TODO SSR: Add more descriptive error message + ! TODO Sam Rabin: Add more descriptive error message call endrun(msg="Stopping") end if From 96c7c2d96cf39347cd6e259366f5d321971e75eb Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:37:30 -0600 Subject: [PATCH 282/406] import_ds.py: Delete a commented-out line. --- python/ctsm/crop_calendars/import_ds.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/ctsm/crop_calendars/import_ds.py b/python/ctsm/crop_calendars/import_ds.py index 0526d3c720..486757492f 100644 --- a/python/ctsm/crop_calendars/import_ds.py +++ b/python/ctsm/crop_calendars/import_ds.py @@ -59,7 +59,6 @@ def manual_mfdataset(filelist, my_vars, my_vegtypes, time_slice): compat="override", coords="all", dim="time", - # combine="nested", ) return ds_out From 1d9e93989c656c91b44389a307bf07602727bcfd Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:38:29 -0600 Subject: [PATCH 283/406] Improve a comment in run_sys_tests.py. --- python/ctsm/run_sys_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctsm/run_sys_tests.py b/python/ctsm/run_sys_tests.py index 9961fd325d..b3a3b78379 100644 --- a/python/ctsm/run_sys_tests.py +++ b/python/ctsm/run_sys_tests.py @@ -736,7 +736,7 @@ def _check_py_env(test_attributes): # whether import is possible. # pylint: disable=import-error disable - # Check requirements for using modify_fsurdat, if needed + # Check requirements for using modify_fsurdat Python module, if needed modify_fsurdat_users = ["FSURDATMODIFYCTSM", "RXCROPMATURITY"] if any(any(u in t for u in modify_fsurdat_users) for t in test_attributes): try: From 78d9f98060c62ec486b32ef20dd2b14718d3c428 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:40:49 -0600 Subject: [PATCH 284/406] Improve error messages in PlantCrop(). --- src/biogeochem/CNPhenologyMod.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index 159e688dbd..855a1476f8 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2720,7 +2720,7 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & gddmaturity(p) = max(950._r8, min(gddmaturity(p)+150._r8, 1850._r8)) end if else - ! TODO Sam Rabin: Add more descriptive error message + write(iulog, *) 'ERROR: PlantCrop(): unrecognized ivt for GDD target: ',ivt(p) call endrun(msg="Stopping") end if @@ -2729,7 +2729,7 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & if (gddmaturity(p) < min_gddmaturity) then if (use_cropcal_rx_cultivar_gdds .or. generate_crop_gdds) then if (did_rx_gdds) then - write(iulog,*) 'Some patch with ivt ',ivt(p),' has rx gddmaturity',gddmaturity(p),'; using min_gddmaturity instead (',min_gddmaturity,')' + write(iulog,*) 'Some patch with ivt ',ivt(p),' has rx gddmaturity ',gddmaturity(p),'; using min_gddmaturity instead (',min_gddmaturity,')' end if gddmaturity(p) = min_gddmaturity else From d3e6cbf13c4347ad2e61bbd5841b1bb9a7363fb8 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:41:54 -0600 Subject: [PATCH 285/406] CropType: Fix a comment. --- src/biogeochem/CropType.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biogeochem/CropType.F90 b/src/biogeochem/CropType.F90 index 77da895135..0f650a4a9f 100644 --- a/src/biogeochem/CropType.F90 +++ b/src/biogeochem/CropType.F90 @@ -54,7 +54,7 @@ module CropType real(r8), pointer :: rx_cultivar_gdds_thisyr_patch (:,:) ! all cultivar GDD targets for this patch this year (ddays) [patch, mxsowings] real(r8), pointer :: gdd20_baseline_patch (:) ! GDD20 baseline for this patch (ddays) [patch] real(r8), pointer :: gdd20_season_start_patch(:) ! gdd20 season start date for this patch (day of year) [patch]. Real to enable history field. - real(r8), pointer :: gdd20_season_end_patch (:) ! gdd20 season end date for this patch (day of year) [patch]/ Real to enable history field. + real(r8), pointer :: gdd20_season_end_patch (:) ! gdd20 season end date for this patch (day of year) [patch]. Real to enable history field. real(r8), pointer :: sdates_thisyr_patch (:,:) ! all actual sowing dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: swindow_starts_thisyr_patch(:,:) ! all sowing window start dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: swindow_ends_thisyr_patch (:,:) ! all sowing window end dates for this patch this year (day of year) [patch, mxsowings] From 6546a681b92f736e460a106b33b4523b290f1cbc Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:42:57 -0600 Subject: [PATCH 286/406] TemperatureType Restart(): Delete unused p. --- src/biogeophys/TemperatureType.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 5ba64f9b1e..787efa81a0 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -909,7 +909,7 @@ subroutine Restart(this, bounds, ncid, flag, is_simple_buildtemp, is_prog_buildt logical , intent(in) :: is_prog_buildtemp ! Prognostic building temp is being used ! ! !LOCAL VARIABLES: - integer :: j,c,p ! indices + integer :: j,c ! indices logical :: readvar ! determine if variable is on initial file integer :: idata !----------------------------------------------------------------------- From c96ce350ed85600c8525b655f3acbfd2f6831059 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:49:19 -0600 Subject: [PATCH 287/406] Delete unused sowingWindows testmod. --- .../testmods_dirs/clm/sowingWindows/include_user_mods | 1 - .../testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm | 5 ----- 2 files changed, 6 deletions(-) delete mode 100644 cime_config/testdefs/testmods_dirs/clm/sowingWindows/include_user_mods delete mode 100644 cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm diff --git a/cime_config/testdefs/testmods_dirs/clm/sowingWindows/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/sowingWindows/include_user_mods deleted file mode 100644 index fe0e18cf88..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/sowingWindows/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../default diff --git a/cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm deleted file mode 100644 index 7024e99b96..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm +++ /dev/null @@ -1,5 +0,0 @@ -stream_fldFileName_swindow_start = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc' -stream_fldFileName_swindow_end = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc' -stream_meshfile_cropcal = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc' -stream_year_first_cropcal_swindows = 2000 -stream_year_last_cropcal_swindows = 2000 From b96ec9107fe8e5b02260689190cd79799f589938 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:52:49 -0600 Subject: [PATCH 288/406] check_rxboth_run.py: Improve error message. --- python/ctsm/crop_calendars/check_rxboth_run.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/check_rxboth_run.py b/python/ctsm/crop_calendars/check_rxboth_run.py index a1014b5e66..fa4affd220 100644 --- a/python/ctsm/crop_calendars/check_rxboth_run.py +++ b/python/ctsm/crop_calendars/check_rxboth_run.py @@ -156,7 +156,16 @@ def main(argv): any_bad = any_bad or gdds_not_obeyed if any_bad: - raise RuntimeError("Unexpected behavior in rxboth run") + msg = "\n ".join( + [ + "Unexpected behavior in rxboth run:", + f"any_bad_import_output: {any_bad_import_output}", + f"any_bad_check_const_vars: {any_bad_check_const_vars}", + f"sdate_not_obeyed: {sdate_not_obeyed}", + f"gdds_not_obeyed: {gdds_not_obeyed}", + ] + ) + raise RuntimeError(msg) if __name__ == "__main__": From 876e23bb4fe4c6274e5f328212b4377ae1dd9c1d Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:59:46 -0600 Subject: [PATCH 289/406] interpolate_gdds.py: Strict check for prefix match. --- python/ctsm/crop_calendars/interpolate_gdds.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/interpolate_gdds.py b/python/ctsm/crop_calendars/interpolate_gdds.py index 830df18c73..92bb112e3f 100755 --- a/python/ctsm/crop_calendars/interpolate_gdds.py +++ b/python/ctsm/crop_calendars/interpolate_gdds.py @@ -6,6 +6,7 @@ import sys import argparse import logging +import re import xarray as xr # -- add python/ctsm to path (needed if we want to run this stand-alone) @@ -121,7 +122,7 @@ def interpolate_gdds(args): if "lat" not in ds_in[var].dims and "lon" not in ds_in[var].dims: print(f"Skipping variable {var} with dimensions {ds_in[var].dims}") continue - elif args.variable_prefix not in var: + if not re.compile("^" + args.variable_prefix).match(var) print(f"Unexpected variable {var} on input file. Skipping.") continue if args.dry_run: From fecad299757de00f235217add67f70e60f7cda33 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:06:22 -0600 Subject: [PATCH 290/406] Delete unneeded cropAnnOutputMonthly testmod. --- cime_config/testdefs/testlist_clm.xml | 4 ++-- .../testmods_dirs/clm/cropAnnOutputMonthly/user_nl_clm | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) delete mode 100644 cime_config/testdefs/testmods_dirs/clm/cropAnnOutputMonthly/user_nl_clm diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index c2926a25e7..e5a1ae7ef4 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3794,7 +3794,7 @@ - + @@ -3803,7 +3803,7 @@ - + diff --git a/cime_config/testdefs/testmods_dirs/clm/cropAnnOutputMonthly/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/cropAnnOutputMonthly/user_nl_clm deleted file mode 100644 index 1c47a2ebd1..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/cropAnnOutputMonthly/user_nl_clm +++ /dev/null @@ -1,3 +0,0 @@ -! These variables SHOULD only be saved annually in real runs, but for testing purposes it's fine to have them monthly. -! Modifies h2 history file defined in crop testmod. -hist_nhtfrq(3) = 0 From c94aeeab68a6f98188f0f94127b3685d2c89dd49 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:09:35 -0600 Subject: [PATCH 291/406] Add READMEs to testmods decStart and midDecStart. --- cime_config/testdefs/testmods_dirs/clm/decStart/README | 1 + cime_config/testdefs/testmods_dirs/clm/midDecStart/README | 1 + 2 files changed, 2 insertions(+) create mode 100644 cime_config/testdefs/testmods_dirs/clm/decStart/README create mode 100644 cime_config/testdefs/testmods_dirs/clm/midDecStart/README diff --git a/cime_config/testdefs/testmods_dirs/clm/decStart/README b/cime_config/testdefs/testmods_dirs/clm/decStart/README new file mode 100644 index 0000000000..7cdab6abfd --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/decStart/README @@ -0,0 +1 @@ +Use midDecStart instead of decStart if you want ERP/ERS/etc. tests longer than 2 days to be able to have the split in December instead of January (i.e., before rather than after new year). \ No newline at end of file diff --git a/cime_config/testdefs/testmods_dirs/clm/midDecStart/README b/cime_config/testdefs/testmods_dirs/clm/midDecStart/README new file mode 100644 index 0000000000..7cdab6abfd --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/midDecStart/README @@ -0,0 +1 @@ +Use midDecStart instead of decStart if you want ERP/ERS/etc. tests longer than 2 days to be able to have the split in December instead of January (i.e., before rather than after new year). \ No newline at end of file From 833d4f67e888c747cfb2c35d8ae8354974987061 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:11:48 -0600 Subject: [PATCH 292/406] import_output(): Bugfix in call of check_v0_le_v1(). --- python/ctsm/crop_calendars/cropcal_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/cropcal_module.py b/python/ctsm/crop_calendars/cropcal_module.py index 08754f8823..927bdc9100 100644 --- a/python/ctsm/crop_calendars/cropcal_module.py +++ b/python/ctsm/crop_calendars/cropcal_module.py @@ -425,7 +425,7 @@ def import_output( # Check that e.g., GDDACCUM <= HUI for var_list in [["GDDACCUM", "HUI"], ["SYEARS", "HYEARS"]]: if all(v in this_ds_gs for v in var_list): - any_bad = check_v0_le_v1( + any_bad = any_bad or check_v0_le_v1( this_ds_gs, var_list, both_nan_ok=True, throw_error=throw_errors ) From 56523e4cbb4969e9648adb2601c40d847cb9d408 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:15:28 -0600 Subject: [PATCH 293/406] Remove 1-degree RXCROPMATURITY test from rxcropmaturity, crop_calendars suites. --- cime_config/testdefs/testlist_clm.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index e5a1ae7ef4..5d3fbeb11a 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3663,8 +3663,6 @@ - - From d06e8642a180190fea9f31c0a6af042758c31b81 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:18:38 -0600 Subject: [PATCH 294/406] Remove unneeded RxCropCalsAdaptFlush testmod. --- .../testmods_dirs/clm/RxCropCalsAdaptFlush/include_user_mods | 1 - .../testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/user_nl_clm | 2 -- 2 files changed, 3 deletions(-) delete mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/include_user_mods delete mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/user_nl_clm diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/include_user_mods deleted file mode 100644 index af5fe8591e..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../RxCropCalsAdapt diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/user_nl_clm deleted file mode 100644 index 4c6af0f6d2..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/user_nl_clm +++ /dev/null @@ -1,2 +0,0 @@ - -flush_gdd20 = .true. From a2bf12483a8fd1f33488b6df09ffe2b291aa2f43 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:21:18 -0600 Subject: [PATCH 295/406] generate_gdd20_baseline.py: Add a clarifying comment. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 06d3a9cd21..13668fc850 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -284,8 +284,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab this_da = dummy_da print(" dummy GDD20") else: - # this_da = ds_in[gddn].fillna(MISSING_FILL) - this_da = ds_in[gddn_str] + this_da = ds_in[gddn_str] # Already did ds_in.mean(dim="time") above this_da = _add_time_axis(this_da) print(f" {gddn_str}") this_da = this_da.fillna(MISSING_RX_GDD_VAL) From c9ff0ee33410b82462fbf4fc876b4130811e13cc Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:24:14 -0600 Subject: [PATCH 296/406] PlantCrop(): Resolve (dismiss) a TODO. --- src/biogeochem/CNPhenologyMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index 855a1476f8..1fb13d32eb 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2716,7 +2716,7 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & ivt(p) == nmiscanthus .or. ivt(p) == nirrig_miscanthus .or. & ivt(p) == nswitchgrass .or. ivt(p) == nirrig_switchgrass) then gddmaturity(p) = max(950._r8, min(gdd20*0.85_r8, hybgdd(ivt(p)))) - if (do_plant_normal) then ! TODO Sam Rabin: Add ".and. .not. do_plant_prescribed"? + if (do_plant_normal) then gddmaturity(p) = max(950._r8, min(gddmaturity(p)+150._r8, 1850._r8)) end if else From bc055919bd68719b53d6563d1f7a37976ec11c44 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:47:49 -0600 Subject: [PATCH 297/406] Improve flush_gdd20 restart logic. Previously, TemperatureType%flush_gdd20 would get set to true upon restart unless it was read and false. This change makes it so that: - If TemperatureType%flush_gdd20 isn't read from the restart file, it falls back to the flush_gdd20 from clm_varctl. - If TemperatureType%flush_gdd20 IS read from the restart file, it is set to true if it's true on the restart file OR if the flush_gdd20 from clm_varctl is true. --- src/biogeophys/TemperatureType.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 787efa81a0..98f6d0f638 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -1148,10 +1148,10 @@ subroutine Restart(this, bounds, ncid, flag, is_simple_buildtemp, is_prog_buildt long_name='Flag indicating that GDD20 values need to be flushed', & units='none', interpinic_flag='copy', readvar=readvar, data=idata) if (flag == 'read') then - if (readvar .and. idata == 0) then - this%flush_gdd20 = .false. + if (readvar) then + this%flush_gdd20 = flush_gdd20 .or. idata == 1 else - this%flush_gdd20 = .true. + this%flush_gdd20 = flush_gdd20 end if end if end if From d298aef9438d0b13bf37ccd162e1f5a303f6e4b4 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:53:54 -0600 Subject: [PATCH 298/406] Do not use flush_gdd20 from clm_varctl in flush decision. Not directly, at least. Instead, rely on TemperatureType%flush_gdd20 having been set correctly. --- src/biogeophys/TemperatureType.F90 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 98f6d0f638..4929b24307 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -169,6 +169,9 @@ subroutine Init(this, bounds, & em_perroad_lun(bounds%begl:bounds%endl), & is_simple_buildtemp, is_prog_buildtemp) + ! Finish up + this%flush_gdd20 = flush_gdd20 + end subroutine Init !------------------------------------------------------------------------ @@ -1693,13 +1696,13 @@ subroutine UpdateAccVars (this, bounds, crop_inst) ! Accumulate and extract running 20-year means if (is_end_curr_year()) then ! Flush, if needed - if (flush_gdd20 .or. this%flush_gdd20) then + if (this%flush_gdd20) then write(iulog, *) 'Flushing GDD20 variables' call markreset_accum_field('GDD020') call markreset_accum_field('GDD820') call markreset_accum_field('GDD1020') this%flush_gdd20 = .false. - flush_gdd20 = .false. + flush_gdd20 = .false. ! Shouldn't be necessary, because flush_gdd20 shouldn't be considered after Init and Restart. But just in case... end if call update_accum_field ('GDD020', this%gdd0_patch, nstep) call extract_accum_field ('GDD020', this%gdd020_patch, nstep) From 21ec2bae64581024dce26a7e6406852d71f34258 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 24 Jul 2024 17:31:58 -0600 Subject: [PATCH 299/406] Remove commented out lines make sure reproduces results for aux_clm on Derecho --- src/biogeochem/DUSTMod.F90 | 7 +------ src/biogeophys/SoilStateInitTimeConstMod.F90 | 9 ++++----- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index bf04311cbe..3831f4d430 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -714,9 +714,8 @@ subroutine DustEmission (bounds, & frc_thr_rghn_fct(p) = frc_thr_rgh_fct ! save and output hybrid drag partition factor else ! for lnd_frc_mbl=0, do not change friction velocity and assume drag partition factor = 0 - !wnd_frc_slt = fv(p) ! The value here is not important since once lnd_frc_mbl(p) <= 0.0_r8 there will be no emission. - frc_thr_rghn_fct(p) = 0.0_r8 ! When LAI > 1, the drag partition effect is zero. dmleung 16 Feb 2024. wnd_frc_slt = fv(p) * frc_thr_rghn_fct(p) ! The value here is not important since once lnd_frc_mbl(p) <= 0.0_r8 there will be no emission. dmleung added 5 Jul 2024 + frc_thr_rghn_fct(p) = 0.0_r8 ! When LAI > 1, the drag partition effect is zero. dmleung 16 Feb 2024. end if !########## end of drag partition effect ####################################################### @@ -741,13 +740,9 @@ subroutine DustEmission (bounds, & if (wnd_frc_slt > wnd_frc_thr_slt_it) then! if using Leung's scheme, use impact threshold for dust emission equation; if Zender, uses fluid threshold (wnd_frc_thr_slt) for dust emission equation !################### for Leung et al. (2023) ################################################ - !################ uncomment the below block if want to use Leung's scheme ################### - - !flx_mss_vrt_dst_ttl(p) = dst_emiss_coeff(p) * mss_frc_cly_vld(c) * forc_rho(c) * ((wnd_frc_slt**2.0_r8 - wnd_frc_thr_slt_it**2.0_r8) / wnd_frc_thr_slt_std) * (wnd_frc_slt / wnd_frc_thr_slt_it)**frag_expt ! Leung et al. (2022) uses Kok et al. (2014) dust emission euqation for emission flux ! dmleung: instead of using mss_frc_cly_vld(c) with a range of [0,0.2] , which makes dust too sensitive to input clay surface dataset), for now use 0.1 + mss_frc_cly_vld(c) * 0.1 / 0.20 with a range of [0.1,0.2]. So, instead of scaling dust emission to 1/20 times for El Djouf (western Sahara) because of its 1 % clay fraction, scale its emission to 1/2 times. This reduces the sensitivity of dust emission to the clay input dataset. In particular, because dust emission is a small-scale process and the grid-averaged clay from the new WISE surface dataset over El Djouf is 1 %, much lower than the former FAO estimation of 5 %, dmleung is not sure if the clay value of 1 % suppresses too much of El Djouf's small-scale dust emission process. Similar to Charlie Zender's feeling suspicious about the soil texture datasets (or the dust emission schemes' sandblasting process), dmleung feels the same and for now decides to still allow dust emission to weakly scale with clay fraction, however limiting the scaling factor to 0.1-0.2. See modification in SoilStateInitTimeConst.F90. dmleung 5 Jul 2024 flx_mss_vrt_dst_ttl(p) = dst_emiss_coeff(p) * mss_frc_cly_vld(c) * forc_rho(c) * ((wnd_frc_slt**2.0_r8 - wnd_frc_thr_slt_it**2.0_r8) / wnd_frc_thr_slt_it) * (wnd_frc_slt / wnd_frc_thr_slt_it)**frag_expt ! Leung et al. (2022) uses Kok et al. (2014) dust emission euqation for emission flux - !flx_mss_vrt_dst_ttl(p) = dst_emiss_coeff(p) * ( 0.1_r8 + mss_frc_cly_vld(c) * 0.1_r8 / 0.20_r8 ) * forc_rho(c) * ((wnd_frc_slt**2.0_r8 - wnd_frc_thr_slt_it**2.0_r8) / wnd_frc_thr_slt_it) * (wnd_frc_slt / wnd_frc_thr_slt_it)**frag_expt ! account for bare soil fraction, frozen soil fraction, and apply global tuning parameter (Kok et al. 2014) flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) * lnd_frc_mbl(p) * C_tune * liqfrac diff --git a/src/biogeophys/SoilStateInitTimeConstMod.F90 b/src/biogeophys/SoilStateInitTimeConstMod.F90 index b22ea2278a..74148d1b08 100644 --- a/src/biogeophys/SoilStateInitTimeConstMod.F90 +++ b/src/biogeophys/SoilStateInitTimeConstMod.F90 @@ -696,9 +696,9 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) end do ! -------------------------------------------------------------------- - ! Initialize threshold soil moisture, and mass fraction of clay as + ! Initialize threshold soil moisture, and mass fraction of clay as ! scaling coefficient of dust emission flux (kg/m2/s) in DUSTMod.F90 - ! dmleung modified 5 Jul 2024, reducing sensitivity of dust emission + ! dmleung modified 5 Jul 2024, reducing sensitivity of dust emission ! flux to clay fraction. ! Also, for threshold soil moisture, dmleung followed Zender (2003) ! DEAD scheme to again avoid dust flux being too sensitive to the choice @@ -713,10 +713,9 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) ! dmleung++ added 24 May 2024: The below calculates the threshold gravimetric water content for the dust emission calculation in DUSTMod.F90. The equation comes from Eq. 14 of Fecan et al. (1999; https://doi.org/10.1007/s00585-999-0149-7). - !gwc_thr_col = 0.17*clay3d + 0.0014*(clay3d**2), and we only concern the topmost soil layer. + !gwc_thr_col = 0.17*clay3d + 0.0014*(clay3d**2), and we only concern the topmost soil layer. ! Charlie Zender later on added a tuning factor (a) such that the equation becomes gwc_thr_col = a*[0.17*clay3d + 0.0014*(clay3d**2)]. (Zender et al., 2003a; https://doi.org/10.1029/2002JD002775) - ! There are different options: E.g., a = 1 gives the first line below. E.g., Kok et al. (2014a, b) chose to use a = 1. A second choice is proposed by Charlie Zender (2003a): a = 1/clay3d, which gives the second line below. - !soilstate_inst%gwc_thr_col(c) = 0.01_r8*(0.17_r8*clay3d(g,1) + 0.0014_r8*clay3d(g,1)*clay3d(g,1)) ! Jasper Kok et al. (2014) modified the equation of soil moisture effect for dust emissions using a tuning factor of a = 1. 0.01 is to convert the threshold gravimetric soil moisture from % to fraction. Danny M. Leung et al. (2023) followed K14 and used this equation for offline dust emission calculations using MERRA-2 met fields. Later, Leung et al. (2024) changed to a = 2 for CESM2 simulations. However, Danny Leung later (Dec 2023) decided to revert to Zender's choice and use a = 1 / (clay3d), which overall encourages more dust emissions from seriamid and more marginal dust sources. This note should probably be on the CLM tech note since dmleung has made a different decision for CESM than his own paper. dmleung added this detailed comment on 19 Feb 2024; edited 24 May 2024. + ! There are different options: E.g., a = 1 gives the first line below. E.g., Kok et al. (2014a, b) chose to use a = 1. A second choice is proposed by Charlie Zender (2003a): a = 1/clay3d, which gives the second line below. soilstate_inst%gwc_thr_col(c) = 0.17_r8 + 0.14_r8 * clay3d(g,1) * 0.01_r8 ! This was the original equation with a = 1 / (%clay) being the tuning factor for soil moisture effect in Zender's dust emission scheme. Danny M. Leung decided (Dec, 2023) that the Leung et al. (2023) dust emission scheme in the CESM will use Zender's tuning of a = 1 / (%clay), which overall encourages more dust emissions from seriamid and more marginal dust sources. Another advantage of using this tuning factor instead of a = 1 is that the dust emission threshold is linearly dependent on the clay fraction instead of parabolically dependent on clay fraction as in the above line. This means that dust emission becomes a little less sensitive to clay content (soil texture). 0.17 and 0.14 are fitting coefficients in Fecan et al. (1999), and 0.01 is used to convert surface clay fraction from percentage to fraction. dmleung added this detailed comment on 19 Feb 2024. ! dust emission scaling factor dependent on clay fraction From 827632e12b1f55253c8c75b999f4d8abd6f87b7a Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 18:08:09 -0600 Subject: [PATCH 300/406] Actually don't use TemperatureType%flush_gdd20 at all. Having that alongside flush_gdd20 from clm_varctl means that they can disagree when restarting, and it's not obvious which should win. Note that the user being able to request a flush at all is dangerous. If they start a run with it true, they might continue that run without setting it to false. Instead, the restart decision should be made by the model---if a gridcell has different prescribed gdd20 season from what its restart file says, then that gridcell should be flushed. --- src/biogeophys/TemperatureType.F90 | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 4929b24307..71722d84fb 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -95,7 +95,6 @@ module TemperatureType real(r8), pointer :: gdd020_patch (:) ! patch 20-year average of gdd0 (ddays) real(r8), pointer :: gdd820_patch (:) ! patch 20-year average of gdd8 (ddays) real(r8), pointer :: gdd1020_patch (:) ! patch 20-year average of gdd10 (ddays) - logical :: flush_gdd20 = .false. ! whether accumulated GDD20s need to be flushed ! Heat content real(r8), pointer :: beta_col (:) ! coefficient of convective velocity [-] @@ -169,9 +168,6 @@ subroutine Init(this, bounds, & em_perroad_lun(bounds%begl:bounds%endl), & is_simple_buildtemp, is_prog_buildtemp) - ! Finish up - this%flush_gdd20 = flush_gdd20 - end subroutine Init !------------------------------------------------------------------------ @@ -1139,26 +1135,6 @@ subroutine Restart(this, bounds, ncid, flag, is_simple_buildtemp, is_prog_buildt end if end if - if (use_crop) then - if (flag == 'write') then - if (this%flush_gdd20) then - idata = 1 - else - idata = 0 - end if - end if - call restartvar(ncid=ncid, flag=flag, varname='flush_gdd20', xtype=ncd_int, & - long_name='Flag indicating that GDD20 values need to be flushed', & - units='none', interpinic_flag='copy', readvar=readvar, data=idata) - if (flag == 'read') then - if (readvar) then - this%flush_gdd20 = flush_gdd20 .or. idata == 1 - else - this%flush_gdd20 = flush_gdd20 - end if - end if - end if - end subroutine Restart @@ -1696,13 +1672,12 @@ subroutine UpdateAccVars (this, bounds, crop_inst) ! Accumulate and extract running 20-year means if (is_end_curr_year()) then ! Flush, if needed - if (this%flush_gdd20) then + if (flush_gdd20) then write(iulog, *) 'Flushing GDD20 variables' call markreset_accum_field('GDD020') call markreset_accum_field('GDD820') call markreset_accum_field('GDD1020') - this%flush_gdd20 = .false. - flush_gdd20 = .false. ! Shouldn't be necessary, because flush_gdd20 shouldn't be considered after Init and Restart. But just in case... + flush_gdd20 = .false. end if call update_accum_field ('GDD020', this%gdd0_patch, nstep) call extract_accum_field ('GDD020', this%gdd020_patch, nstep) From 4ab30a5ddb9ac54cba659abc8682f1152e8eb844 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 18:20:21 -0600 Subject: [PATCH 301/406] crop_calendars Python stuff now includes crop grasses. --- python/ctsm/crop_calendars/cropcal_module.py | 2 +- python/ctsm/crop_calendars/generate_gdds_functions.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python/ctsm/crop_calendars/cropcal_module.py b/python/ctsm/crop_calendars/cropcal_module.py index 927bdc9100..719d352665 100644 --- a/python/ctsm/crop_calendars/cropcal_module.py +++ b/python/ctsm/crop_calendars/cropcal_module.py @@ -345,7 +345,7 @@ def import_output( my_vars, year_1=None, year_n=None, - my_vegtypes=utils.define_mgdcrop_list_nograsses(), + my_vegtypes=utils.define_mgdcrop_list_withgrasses(), sdates_rx_ds=None, gdds_rx_ds=None, verbose=False, diff --git a/python/ctsm/crop_calendars/generate_gdds_functions.py b/python/ctsm/crop_calendars/generate_gdds_functions.py index 50e2ac3d00..14bd6b2e40 100644 --- a/python/ctsm/crop_calendars/generate_gdds_functions.py +++ b/python/ctsm/crop_calendars/generate_gdds_functions.py @@ -282,9 +282,9 @@ def import_and_process_1yr( # Get list of crops to include if skip_crops is not None: - crops_to_read = [c for c in utils.define_mgdcrop_list_nograsses() if c not in skip_crops] + crops_to_read = [c for c in utils.define_mgdcrop_list_withgrasses() if c not in skip_crops] else: - crops_to_read = utils.define_mgdcrop_list_nograsses() + crops_to_read = utils.define_mgdcrop_list_withgrasses() print(h1_filelist) dates_ds = import_ds( From aeb4182302dc63bc88a3c8a5186233901f310f37 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 18:22:58 -0600 Subject: [PATCH 302/406] interpolate_gdds.py: Syntax fix. --- python/ctsm/crop_calendars/interpolate_gdds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/interpolate_gdds.py b/python/ctsm/crop_calendars/interpolate_gdds.py index 92bb112e3f..123d40af38 100755 --- a/python/ctsm/crop_calendars/interpolate_gdds.py +++ b/python/ctsm/crop_calendars/interpolate_gdds.py @@ -122,7 +122,7 @@ def interpolate_gdds(args): if "lat" not in ds_in[var].dims and "lon" not in ds_in[var].dims: print(f"Skipping variable {var} with dimensions {ds_in[var].dims}") continue - if not re.compile("^" + args.variable_prefix).match(var) + if not re.compile("^" + args.variable_prefix).match(var): print(f"Unexpected variable {var} on input file. Skipping.") continue if args.dry_run: From 929ec5d6c43a4234e35b060b02223888466554fc Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 23:08:27 -0600 Subject: [PATCH 303/406] Skip switchgrass in RXCROPMATURITY. --- cime_config/SystemTests/rxcropmaturity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cime_config/SystemTests/rxcropmaturity.py b/cime_config/SystemTests/rxcropmaturity.py index 46ed8f8be3..965398821f 100644 --- a/cime_config/SystemTests/rxcropmaturity.py +++ b/cime_config/SystemTests/rxcropmaturity.py @@ -438,7 +438,7 @@ def _run_generate_gdds(self, case_gddgen): f"--sdates-file {sdates_file}", f"--hdates-file {hdates_file}", f"--output-dir generate_gdds_out", - f"--skip-crops miscanthus,irrigated_miscanthus", + f"--skip-crops miscanthus,irrigated_miscanthus,switchgrass,irrigated_switchgrass", ] ) stu.run_python_script( From b70c156fed30f835a8b32cbca4d82dcbfe4888b2 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 23:16:00 -0600 Subject: [PATCH 304/406] Manually specify sowing date file for GddGen testmod. --- cime_config/testdefs/testmods_dirs/clm/GddGen/user_nl_clm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cime_config/testdefs/testmods_dirs/clm/GddGen/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/GddGen/user_nl_clm index 87cdd5d5b5..cfde517fd9 100644 --- a/cime_config/testdefs/testmods_dirs/clm/GddGen/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/GddGen/user_nl_clm @@ -1,4 +1,6 @@ cropcals_rx = .true. +stream_fldFileName_swindow_start = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.tweaked_latlons.nc' +stream_fldFileName_swindow_end = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.tweaked_latlons.nc' stream_fldFileName_cultivar_gdds = '' generate_crop_gdds = .true. use_mxmat = .false. From 7400d5bb153bd64be3eeb7f560e85128d736ed5c Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 05:14:01 -0600 Subject: [PATCH 305/406] Add izumi_nag.clm-RxCropCalsAdaptGGCMI test to expected fails. SMS_P128x1_Lm25.f10_f10_mg37.IHistClm60BgcCrop.izumi_nag.clm-RxCropCalsAdaptGGCMI --- cime_config/testdefs/ExpectedTestFails.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cime_config/testdefs/ExpectedTestFails.xml b/cime_config/testdefs/ExpectedTestFails.xml index 03eb6a157d..e5e1410d1e 100644 --- a/cime_config/testdefs/ExpectedTestFails.xml +++ b/cime_config/testdefs/ExpectedTestFails.xml @@ -170,6 +170,13 @@ + + + FAIL + #2659 + + + From 388c7ed45e9561206a88efeecdb6faec054522d1 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 05:55:45 -0600 Subject: [PATCH 306/406] Move stream_gdd20_seasons to cropCalStreamMod. --- bld/namelist_files/namelist_definition_ctsm.xml | 2 +- src/biogeophys/TemperatureType.F90 | 7 +++++-- src/cpl/share_esmf/cropcalStreamMod.F90 | 5 ++++- src/main/clm_varctl.F90 | 1 - src/main/controlMod.F90 | 4 +--- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 31ce220d9c..a24f59339a 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1839,7 +1839,7 @@ Filename of input stream data for baseline GDD20 values + group="cropcal_streams" valid_values="" > Set this to true to read gdd20 accumulation season start and end dates from stream files, rather than using hard-coded hemisphere-specific "warm seasons." diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 71722d84fb..707218cc27 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -8,7 +8,7 @@ module TemperatureType use decompMod , only : bounds_type use abortutils , only : endrun use clm_varctl , only : use_cndv, iulog, use_luna, use_crop, use_biomass_heat_storage - use clm_varctl , only : stream_gdd20_seasons, flush_gdd20 + use clm_varctl , only : flush_gdd20 use clm_varpar , only : nlevsno, nlevgrnd, nlevlak, nlevurb, nlevmaxurbgrnd use clm_varcon , only : spval, ispval use GridcellType , only : grc @@ -1401,6 +1401,7 @@ subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, d real(r8) :: lat ! latitude integer :: gdd20_season_start, gdd20_season_end integer :: jday ! Julian day of year (1, ..., 366) + logical :: stream_gdd20_seasons_tt ! Local derivation of this to avoid circular dependency associate( & gdd20_season_starts => crop_inst%gdd20_season_start_patch, & @@ -1432,6 +1433,8 @@ subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, d end if write(field_name, format_string) "GDD",basetemp_int + stream_gdd20_seasons_tt = any(gdd20_season_starts(begp:endp) > 0.5_r8) .and. any(gdd20_season_starts(begp:endp) < 366.5_r8) + do p = begp,endp ! Avoid unnecessary calculations over inactive points @@ -1447,7 +1450,7 @@ subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, d ((month > 9 .or. month < 4) .and. lat < 0._r8) ! Replace with read-in gdd20 accumulation season, if needed and valid ! (If these aren't being read in or they're invalid, they'll be -1) - if (stream_gdd20_seasons .and. patch%itype(p) >= npcropmin) then + if (stream_gdd20_seasons_tt .and. patch%itype(p) >= npcropmin) then gdd20_season_start = int(gdd20_season_starts(p)) gdd20_season_end = int(gdd20_season_ends(p)) if (gdd20_season_start >= 1 .and. gdd20_season_end >= 1) then diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index d17ce1c259..85218a0e1c 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -16,7 +16,6 @@ module cropcalStreamMod use clm_varctl , only : iulog use clm_varctl , only : use_cropcal_rx_swindows, use_cropcal_rx_cultivar_gdds, use_cropcal_streams use clm_varctl , only : adapt_cropcal_rx_cultivar_gdds - use clm_varctl , only : stream_gdd20_seasons use clm_varpar , only : mxpft use clm_varpar , only : mxsowings use perf_mod , only : t_startf, t_stopf @@ -53,6 +52,7 @@ module cropcalStreamMod character(len=CL) :: stream_fldFileName_gdd20_baseline ! GDD20 baseline stream filename to read logical :: cropcals_rx ! Used only for setting input files in namelist; does nothing in code, but needs to be here so namelist read doesn't crash logical :: cropcals_rx_adapt ! Used only for setting input files in namelist; does nothing in code, but needs to be here so namelist read doesn't crash + logical :: stream_gdd20_seasons ! Read start and end dates for gdd20 seasons from streams instead of using hemisphere-specific values logical :: allow_invalid_gdd20_season_inputs ! Fall back on hemisphere "warm periods" in cases of invalid values in stream_fldFileName_gdd20_season_start and _end? character(len=CL) :: stream_fldFileName_gdd20_season_start ! Stream filename to read for start of gdd20 season character(len=CL) :: stream_fldFileName_gdd20_season_end ! Stream filename to read for end of gdd20 season @@ -113,6 +113,7 @@ subroutine cropcal_init(bounds) stream_meshfile_cropcal, & cropcals_rx, & cropcals_rx_adapt, & + stream_gdd20_seasons, & allow_invalid_gdd20_season_inputs, & stream_fldFileName_gdd20_season_start, & stream_fldFileName_gdd20_season_end @@ -130,6 +131,7 @@ subroutine cropcal_init(bounds) stream_fldFileName_swindow_end = '' stream_fldFileName_cultivar_gdds = '' stream_fldFileName_gdd20_baseline = '' + stream_gdd20_seasons = .false. allow_invalid_gdd20_season_inputs = .false. stream_fldFileName_gdd20_season_start = '' stream_fldFileName_gdd20_season_end = '' @@ -173,6 +175,7 @@ subroutine cropcal_init(bounds) call shr_mpi_bcast(stream_fldFileName_cultivar_gdds, mpicom) call shr_mpi_bcast(stream_fldFileName_gdd20_baseline, mpicom) call shr_mpi_bcast(stream_meshfile_cropcal , mpicom) + call shr_mpi_bcast(stream_gdd20_seasons, mpicom) call shr_mpi_bcast(allow_invalid_gdd20_season_inputs, mpicom) call shr_mpi_bcast(stream_fldFileName_gdd20_season_start, mpicom) call shr_mpi_bcast(stream_fldFileName_gdd20_season_end, mpicom) diff --git a/src/main/clm_varctl.F90 b/src/main/clm_varctl.F90 index a1ca2fd3f7..678f386f23 100644 --- a/src/main/clm_varctl.F90 +++ b/src/main/clm_varctl.F90 @@ -392,7 +392,6 @@ module clm_varctl logical, public :: use_cropcal_rx_swindows = .false. logical, public :: use_cropcal_rx_cultivar_gdds = .false. logical, public :: adapt_cropcal_rx_cultivar_gdds = .false. - logical, public :: stream_gdd20_seasons = .false. logical, public :: flush_gdd20 = .false. !---------------------------------------------------------- diff --git a/src/main/controlMod.F90 b/src/main/controlMod.F90 index 98e0d1afac..634128798b 100644 --- a/src/main/controlMod.F90 +++ b/src/main/controlMod.F90 @@ -302,7 +302,7 @@ subroutine control_init(dtime) use_lch4, use_nitrif_denitrif, use_extralakelayers, & use_vichydro, use_cn, use_cndv, use_crop, use_fertilizer, & use_grainproduct, use_snicar_frc, use_vancouver, use_mexicocity, use_noio, & - use_nguardrail, crop_residue_removal_frac, stream_gdd20_seasons, flush_gdd20 + use_nguardrail, crop_residue_removal_frac, flush_gdd20 ! SNICAR namelist /clm_inparm/ & @@ -711,7 +711,6 @@ subroutine control_spmd() call mpi_bcast (use_cndv, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_nguardrail, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_crop, 1, MPI_LOGICAL, 0, mpicom, ier) - call mpi_bcast (stream_gdd20_seasons, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (flush_gdd20, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_fertilizer, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_grainproduct, 1, MPI_LOGICAL, 0, mpicom, ier) @@ -985,7 +984,6 @@ subroutine control_print () write(iulog,*) ' use_cn = ', use_cn write(iulog,*) ' use_cndv = ', use_cndv write(iulog,*) ' use_crop = ', use_crop - write(iulog,*) ' stream_gdd20_seasons = ', stream_gdd20_seasons write(iulog,*) ' flush_gdd20 = ', flush_gdd20 write(iulog,*) ' use_fertilizer = ', use_fertilizer write(iulog,*) ' use_grainproduct = ', use_grainproduct From 0d4a300112f16ee35332af5e72e8016ea1990c8a Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 14:21:40 -0600 Subject: [PATCH 307/406] Fix setting of stream_fldFileName_gdd20_baseline. --- bld/CLMBuildNamelist.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 232b15c3b1..c2d23ce962 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4247,7 +4247,7 @@ sub setup_logic_cropcal_streams { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_swindow_end'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldfilename_cultivar_gdds'); if ( &value_is_true($cropcals_rx_adapt) ) { - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_gdd20_baseline'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_gdd20_baseline', 'stream_gdd20_seasons'=>$stream_gdd20_seasons); } } From 649547c5015e59ab582436c8c1bcfc39e296dd8f Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 16:21:16 -0600 Subject: [PATCH 308/406] Don't specify PE layout for SMS crop_calendars tests. --- cime_config/testdefs/testlist_clm.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index b8e3dd2f31..ecdeeb659d 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3560,7 +3560,7 @@ - + @@ -3578,7 +3578,7 @@ - + From 26ce0bbb0ac7835f11251c0585eec778b4a79859 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 16:04:00 -0600 Subject: [PATCH 309/406] Add izumi versions of most derecho crop_calendars tests. --- cime_config/testdefs/testlist_clm.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index ecdeeb659d..18e66d4d64 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3563,6 +3563,7 @@ + @@ -3581,6 +3582,7 @@ + @@ -3593,6 +3595,7 @@ + @@ -3603,6 +3606,7 @@ + @@ -3613,6 +3617,7 @@ + @@ -3622,6 +3627,7 @@ + From ae4af6b53cf0e525cd125a1eea97472ad2c72799 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 16:26:14 -0600 Subject: [PATCH 310/406] Fix PE layouts of izumi crop_calendars tests. --- cime_config/testdefs/testlist_clm.xml | 30 +++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 18e66d4d64..edbfd3c4eb 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3579,6 +3579,15 @@ + + + + + + + + + @@ -3595,18 +3604,25 @@ - + + + + + + + + + - @@ -3614,6 +3630,16 @@ + + + + + + + + + + From e757d3df5154d3671129a26d9afab667f337591f Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 14:54:24 -0600 Subject: [PATCH 311/406] Add oldCropCals testmod and two aux_clm tests that use it. --- cime_config/testdefs/testlist_clm.xml | 27 +++++++++++++++++++ .../testmods_dirs/clm/oldCropCals/user_nl_clm | 3 +++ 2 files changed, 30 insertions(+) create mode 100644 cime_config/testdefs/testmods_dirs/clm/oldCropCals/user_nl_clm diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index edbfd3c4eb..ffaf4edc29 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3660,5 +3660,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cime_config/testdefs/testmods_dirs/clm/oldCropCals/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/oldCropCals/user_nl_clm new file mode 100644 index 0000000000..5885f6f2a0 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/oldCropCals/user_nl_clm @@ -0,0 +1,3 @@ +cropcals_rx = .false. +cropcals_rx_adapt = .false. +stream_gdd20_seasons = .false. \ No newline at end of file From 7070abcd9272cc4b0f55d086321ea6968f1d2e83 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 16:29:34 -0600 Subject: [PATCH 312/406] Add cropcals_rx(_adapt) in some testmods; rename RxCropCals to RxCropCalsNoAdapt. --- cime_config/testdefs/testlist_clm.xml | 2 +- cime_config/testdefs/testmods_dirs/clm/GddGen/include_user_mods | 1 + cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm | 2 -- .../testmods_dirs/clm/RxCropCalsAdapt/include_user_mods | 2 +- .../clm/{RxCropCals => RxCropCalsNoAdapt}/include_user_mods | 0 .../testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/user_nl_clm | 2 ++ 6 files changed, 5 insertions(+), 4 deletions(-) delete mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm rename cime_config/testdefs/testmods_dirs/clm/{RxCropCals => RxCropCalsNoAdapt}/include_user_mods (100%) create mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/user_nl_clm diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index ffaf4edc29..cca879604d 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3560,7 +3560,7 @@ - + diff --git a/cime_config/testdefs/testmods_dirs/clm/GddGen/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/GddGen/include_user_mods index 02ec13743f..4d75082583 100644 --- a/cime_config/testdefs/testmods_dirs/clm/GddGen/include_user_mods +++ b/cime_config/testdefs/testmods_dirs/clm/GddGen/include_user_mods @@ -1 +1,2 @@ ../cropMonthOutput +../oldCropCals \ No newline at end of file diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm deleted file mode 100644 index 8a0b4a91be..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm +++ /dev/null @@ -1,2 +0,0 @@ - -cropcals_rx = .true. diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods index 88c9694365..4f46c0ce95 100644 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods @@ -1 +1 @@ -../RxCropCals +../RxCropCalsNoAdapt diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCals/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/include_user_mods similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/RxCropCals/include_user_mods rename to cime_config/testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/include_user_mods diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/user_nl_clm new file mode 100644 index 0000000000..625960b389 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/user_nl_clm @@ -0,0 +1,2 @@ +cropcals_rx = .true. +cropcals_rx_adapt = .false. From f0a221f0a68c0aac10cf0c449406c1dcd27a6294 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 15:38:20 -0600 Subject: [PATCH 313/406] Rework RxCropCalsAdaptGGCMI to not require RxCropCalsAdapt. The latter will be deleted in the next commit. --- .../testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods | 2 +- .../testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods index af5fe8591e..23ea3745e6 100644 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods @@ -1 +1 @@ -../RxCropCalsAdapt +../crop diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm index dd4ac3117c..fdf5a86c26 100644 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm @@ -1,4 +1,5 @@ - +cropcals_rx = .false. +cropcals_rx_adapt = .true. stream_gdd20_seasons = .true. flush_gdd20 = .true. !TODO SSR: Try without this once you have half-degree inputs From 5dabe6287e9ad843003f08122cf58327aaf356b3 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 16:31:07 -0600 Subject: [PATCH 314/406] Delete RxCropCalsAdapt test. --- cime_config/testdefs/testlist_clm.xml | 18 ------------------ .../clm/RxCropCalsAdapt/include_user_mods | 1 - .../clm/RxCropCalsAdapt/user_nl_clm | 3 --- 3 files changed, 22 deletions(-) delete mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods delete mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index cca879604d..4e5574932e 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3570,24 +3570,6 @@ - - - - - - - - - - - - - - - - - - diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods deleted file mode 100644 index 4f46c0ce95..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../RxCropCalsNoAdapt diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm deleted file mode 100644 index 709c7221e0..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm +++ /dev/null @@ -1,3 +0,0 @@ - -cropcals_rx = .false. -cropcals_rx_adapt = .true. From ca6b608ce59eb9043297d4452a5d0107eb5b8e82 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 15:52:10 -0600 Subject: [PATCH 315/406] Add 'cropcals_rx_adapt = .false.' to RXCROPMATURITYSHARED. --- cime_config/SystemTests/rxcropmaturity.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cime_config/SystemTests/rxcropmaturity.py b/cime_config/SystemTests/rxcropmaturity.py index b1de55836b..fb254c408f 100644 --- a/cime_config/SystemTests/rxcropmaturity.py +++ b/cime_config/SystemTests/rxcropmaturity.py @@ -402,6 +402,7 @@ def _run_check_rxboth_run(self, skip_gen): def _modify_user_nl_allruns(self): nl_additions = [ "cropcals_rx = .true.", + "cropcals_rx_adapt = .false.", "stream_meshfile_cropcal = '{}'".format(self._case.get_value("LND_DOMAIN_MESH")), "stream_fldFileName_swindow_start = '{}'".format(self._sdatefile), "stream_fldFileName_swindow_end = '{}'".format(self._sdatefile), From 904e6b2f99164165a4a197436f4b583148bdc087 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 18:22:36 -0600 Subject: [PATCH 316/406] Change a test name in ExpectedTestFails.xml. --- cime_config/testdefs/ExpectedTestFails.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cime_config/testdefs/ExpectedTestFails.xml b/cime_config/testdefs/ExpectedTestFails.xml index 9b653d8e0b..d5e3e4e378 100644 --- a/cime_config/testdefs/ExpectedTestFails.xml +++ b/cime_config/testdefs/ExpectedTestFails.xml @@ -163,7 +163,7 @@ - + FAIL #2659 From a3d8158c65dcff4cabc1a9e3dd86fe762050f7f3 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 20:01:54 -0600 Subject: [PATCH 317/406] Turn on cropcals_rx_adapt by default for physics other than clm4_5, clm5_0, and clm5_1. --- bld/namelist_files/namelist_defaults_ctsm.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 706b4dd26d..5f336dabf8 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -2199,7 +2199,10 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c .false. -.false. +.true. +.false. +.false. +.false. .false. .false. 2000 From dcdd587848f18f2087543966856f6b0373a64adb Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 22:36:49 -0600 Subject: [PATCH 318/406] Remove most Izumi crop_calendars tests. --- cime_config/testdefs/testlist_clm.xml | 32 --------------------------- 1 file changed, 32 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 4e5574932e..efeb011844 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3563,7 +3563,6 @@ - @@ -3573,7 +3572,6 @@ - @@ -3592,15 +3590,6 @@ - - - - - - - - - @@ -3612,20 +3601,9 @@ - - - - - - - - - - - @@ -3635,7 +3613,6 @@ - @@ -3660,14 +3637,5 @@ - - - - - - - - - From af02bca9e98661c1f9450cf7602c96dc3aab2b15 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 23:17:56 -0700 Subject: [PATCH 319/406] Update ChangeLog and ChangeSum. --- doc/ChangeLog | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++ doc/ChangeSum | 1 + 2 files changed, 138 insertions(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index 506b3d5ad0..501a67720f 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,141 @@ =============================================================== +Tag name: ctsm5.2.016 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Thu Jul 25 22:39:39 MST 2024 +One-line Summary: Enable new crop calendars for clm60 compsets + +Purpose and description of changes +---------------------------------- + +This commit switches clm60 compsets (really, any compset other than clm45, clm50, and clm51) to use: +- Per-gridcell and -crop sowing windows derived from the GGCMI phase 3b group II static growing seasons +- Per-gridcell and -crop maturity requirements derived from those same growing seasons, over the 1980-2009 growing seasons +- Code to adjust those prescribed maturity requirements based on recent climate + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + +[X] clm6_0 + +[ ] clm5_1 + +[ ] clm5_0 + +[?] ctsm5_0-nwp: Probably? Haven't tested, but didn't exclude that setup. + +[ ] clm4_5 + + +Bugs fixed +---------- + +List of CTSM issues fixed (include CTSM Issue # and description): +- Fixes ESCOMP/CTSM#2584: Reset accumulators to initval instead of 0 (https://github.com/ESCOMP/CTSM/issues/2584) + +Notes of particular relevance for users +--------------------------------------- +[Remove any lines that don't apply. Remove entire section if nothing applies.] + +Caveats for users (e.g., need to interpolate initial conditions): + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): +- Add cropcals_rx, default false. +- Add cropcals_rx_adapt, default true. This is what changes default behavior. +- Rename stream_year_first_cropcal to stream_year_first_cropcal_swindows +- Rename stream_year_last_cropcal to stream_year_last_cropcal_swindows +- Rename model_year_align_cropcal to model_year_align_cropcal_swindows +- Add stream_year_first_cropcal_cultivar_gdds +- Add stream_year_last_cropcal_cultivar_gdds +- Add model_year_align_cropcal_cultivar_gdds +- Add stream_fldFileName_gdd20_baseline +- Add stream_gdd20_seasons (experimental) +- Add flush_gdd20 (experimental) +- Add stream_fldFileName_gdd20_season_start (experimental) +- Add stream_fldFileName_gdd20_season_end (experimental) +- Add allow_invalid_gdd20_season_inputs + +Changes to the datasets (e.g., parameter, surface or initial files): +- Many new default and optional stream files added. + +Changes to documentation: +- None yet. + +Notes of particular relevance for developers: +--------------------------------------------- + +Caveats for developers: +- tools/contrib/tweak_latlons.py: This is a script I made to avoid the "ambiguous nearest neighbor" issue described in the discussion at https://github.com/orgs/esmf-org/discussions/261. It's hopefully just a temporary thing as we wait for ESMF to fix that bug. If they end up deciding not to, then this should be moved into python/ctsm/ and fully tested. +- Unit and system testing needs to be added for generate_gdd20_baseline.py. +- RXCROPMATURITY SystemTest should be updated to also call generate_gdd20_baseline.py. + +Changes to tests or testing: +- Adds testmods: + - midDecStart: Like decStart, except starts Dec. 15 instead of 30. Useful for ERP/ERS tests where you want the split in December. + - GddGen + - oldCropCals: To recreate previous crop calendars + - RxCropCalsAdaptGGCMI (experimental) + - RxCropCalsNoAdapt + - StreamGDD20Seasons (experimental) +- Removes sowingWindows testmod +- Adds and changes various tests related to crop calendars + +Note that testmods marked "experimental" are fine to be marked as expected failures, if needed. + + +Testing summary: +---------------- + +[Remove any lines that don't apply.] + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + derecho - + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + (any machine) - + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- + izumi ------- + +Answer changes +-------------- + +Changes answers relative to baseline: + + Summarize any changes to answers, i.e., + - what code configurations: crop-enabled compsets with clm60 (or more specifically, other than clm45, clm50, clm51) + - what platforms/compilers: all + - nature of change (roundoff; larger than roundoff/same climate; new climate): new climate + + Probably? At least, there can be large regional shifts in crop growing season. Results are similar to those for the Prescribed Calendars setup in Rabin et al. (2023; https://gmd.copernicus.org/articles/16/7253/2023/gmd-16-7253-2023.html). + + If this tag changes climate describe the run(s) done to evaluate the new + climate (put details of the simulations in the experiment database) + - None + + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +- ESCOMP/CTSM#2560: Allow prescribed gddmaturity to vary based on recent climate (initial work, v2) (https://github.com/ESCOMP/CTSM/pull/2560) +- ESCOMP/CTSM#2585: Allow "manual" resets of all accumulator types (https://github.com/ESCOMP/CTSM/pull/2585) +- ESCOMP/CTSM#2593: Optionally base GDD20 on per-gridcell windows, not per-hemisphere (https://github.com/ESCOMP/CTSM/pull/2593) +- ESCOMP/CTSM#2661: Merge ctsm5.2.015 into scale-maturity-reqs (https://github.com/ESCOMP/CTSM/pull/2661) +- ESCOMP/CTSM#2664: Enable new crop calendars for clm60 compsets (https://github.com/ESCOMP/CTSM/pull/2664) +- ESCOMP/CTSM#2665: Merge new crop calendars into master (https://github.com/ESCOMP/CTSM/pull/2665) + +=============================================================== +=============================================================== Tag name: ctsm5.2.015 Originator(s): multiple (Samuel Levis,UCAR/TSS,303-665-1310, @mvertens, @jedwards4b, @billsacks, @Katetc) Date: Mon 22 Jul 2024 12:46:17 PM MDT diff --git a/doc/ChangeSum b/doc/ChangeSum index 9c6524b33d..f043ab8b34 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.2.016 samrabin 07/25/2024 Enable new crop calendars for clm60 compsets ctsm5.2.015 multiple 07/22/2024 Update submodule tags to pass runoff from cism to rof ctsm5.2.014 multiple 07/19/2024 use_matrixcn, use_soil_matrixcn come in as default .false. ctsm5.2.013 glemieux 07/18/2024 FATES Land Use V2 From 542f7080432e76073ea2734be0ed50393e9a6bb8 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 01:40:34 -0600 Subject: [PATCH 320/406] Wrap cropcal subroutine calls in use_crop. Avoids issue with undefined pointer to crop_inst%rx_swindow_starts_thisyr_patch and probably other arrays. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 1 + src/main/clm_driver.F90 | 4 ++-- src/main/clm_initializeMod.F90 | 26 +++++++++++++------------ 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 85218a0e1c..a39ba40754 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -14,6 +14,7 @@ module cropcalStreamMod use decompMod , only : bounds_type use abortutils , only : endrun use clm_varctl , only : iulog + use clm_varctl , only : use_crop use clm_varctl , only : use_cropcal_rx_swindows, use_cropcal_rx_cultivar_gdds, use_cropcal_streams use clm_varctl , only : adapt_cropcal_rx_cultivar_gdds use clm_varpar , only : mxpft diff --git a/src/main/clm_driver.F90 b/src/main/clm_driver.F90 index 01c0ec409e..53b1e55f7c 100644 --- a/src/main/clm_driver.F90 +++ b/src/main/clm_driver.F90 @@ -475,7 +475,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro ! When crop calendar streams are being used ! NOTE: This call needs to happen outside loops over nclumps (as streams are not threadsafe) - if (use_cropcal_streams .and. is_beg_curr_year()) then + if (use_crop .and. use_cropcal_streams .and. is_beg_curr_year()) then call cropcal_advance( bounds_proc ) end if @@ -1073,7 +1073,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro frictionvel_inst, photosyns_inst, drydepvel_inst) call t_stopf('depvel') - if (use_cropcal_streams .and. is_beg_curr_year()) then + if (use_crop .and. use_cropcal_streams .and. is_beg_curr_year()) then ! ============================================================================ ! Update crop calendars ! ============================================================================ diff --git a/src/main/clm_initializeMod.F90 b/src/main/clm_initializeMod.F90 index e8f70bdef8..0c0570c577 100644 --- a/src/main/clm_initializeMod.F90 +++ b/src/main/clm_initializeMod.F90 @@ -659,19 +659,21 @@ subroutine initialize2(ni,nj) end if ! Initialize crop calendars - call t_startf('init_cropcal') - call cropcal_init(bounds_proc) - if (use_cropcal_streams) then - call cropcal_advance( bounds_proc ) - !$OMP PARALLEL DO PRIVATE (nc, bounds_clump) - do nc = 1,nclumps - call get_clump_bounds(nc, bounds_clump) - call cropcal_interp(bounds_clump, filter_inactive_and_active(nc)%num_pcropp, & - filter_inactive_and_active(nc)%pcropp, .true., crop_inst) - end do - !$OMP END PARALLEL DO + if (use_crop) then + call t_startf('init_cropcal') + call cropcal_init(bounds_proc) + if (use_cropcal_streams) then + call cropcal_advance( bounds_proc ) + !$OMP PARALLEL DO PRIVATE (nc, bounds_clump) + do nc = 1,nclumps + call get_clump_bounds(nc, bounds_clump) + call cropcal_interp(bounds_clump, filter_inactive_and_active(nc)%num_pcropp, & + filter_inactive_and_active(nc)%pcropp, .true., crop_inst) + end do + !$OMP END PARALLEL DO + end if + call t_stopf('init_cropcal') end if - call t_stopf('init_cropcal') ! Initialize active history fields. ! This is only done if not a restart run. If a restart run, then this From 126ce292f1380e96ff22750ec4cd0dc1a518e8d6 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 07:49:53 -0600 Subject: [PATCH 321/406] Allocate first dim on dataptr2d_*_start/end arrays with begp:endp. Instead of just :. Hopefully will eliminate too-high index errors in threading tests. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index a39ba40754..3fdf1ba06c 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -545,9 +545,9 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Read prescribed sowing window start dates from input files allocate(dataptr2d_swindow_start(lsize, ncft)) - dataptr2d_swindow_start(:,:) = -1._r8 + dataptr2d_swindow_start(begp:endp,:) = -1._r8 allocate(dataptr2d_swindow_end (lsize, ncft)) - dataptr2d_swindow_end(:,:) = -1._r8 + dataptr2d_swindow_end(begp:endp,:) = -1._r8 if (use_cropcal_rx_swindows) then ! Starting with npcropmin will skip generic crops do n = 1, ncft @@ -742,9 +742,9 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Read prescribed gdd20 season start dates from input files allocate(dataptr2d_gdd20_season_start(lsize, ncft)) - dataptr2d_gdd20_season_start(:,:) = -1._r8 + dataptr2d_gdd20_season_start(begp:endp,:) = -1._r8 allocate(dataptr2d_gdd20_season_end (lsize, ncft)) - dataptr2d_gdd20_season_end(:,:) = -1._r8 + dataptr2d_gdd20_season_end(begp:endp,:) = -1._r8 if (stream_gdd20_seasons) then ! Starting with npcropmin will skip generic crops do n = 1, ncft From b7a8fb58e2186d9d6b9dcd20b8b6c57301bfcd8e Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 08:14:51 -0600 Subject: [PATCH 322/406] Allocate 2d arrays with begp:endp, not lsize. Hopefully will eliminate too-high index errors in threading tests. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 3fdf1ba06c..36c6c78f22 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -544,9 +544,9 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) dayspyr = get_curr_days_per_year() ! Read prescribed sowing window start dates from input files - allocate(dataptr2d_swindow_start(lsize, ncft)) + allocate(dataptr2d_swindow_start(begp:endp, ncft)) dataptr2d_swindow_start(begp:endp,:) = -1._r8 - allocate(dataptr2d_swindow_end (lsize, ncft)) + allocate(dataptr2d_swindow_end (begp:endp, ncft)) dataptr2d_swindow_end(begp:endp,:) = -1._r8 if (use_cropcal_rx_swindows) then ! Starting with npcropmin will skip generic crops @@ -631,7 +631,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) deallocate(dataptr2d_swindow_start) deallocate(dataptr2d_swindow_end) - allocate(dataptr2d_cultivar_gdds(lsize, ncft)) + allocate(dataptr2d_cultivar_gdds(begp:endp, ncft)) if (use_cropcal_rx_cultivar_gdds) then ! Read prescribed cultivar GDDs from input files ! Starting with npcropmin will skip generic crops @@ -688,7 +688,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) deallocate(dataptr2d_cultivar_gdds) - allocate(dataptr2d_gdd20_baseline(lsize, ncft)) + allocate(dataptr2d_gdd20_baseline(begp:endp, ncft)) if (adapt_cropcal_rx_cultivar_gdds) then ! Read GDD20 baselines from input files ! Starting with npcropmin will skip generic crops @@ -741,9 +741,9 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Read prescribed gdd20 season start dates from input files - allocate(dataptr2d_gdd20_season_start(lsize, ncft)) + allocate(dataptr2d_gdd20_season_start(begp:endp, ncft)) dataptr2d_gdd20_season_start(begp:endp,:) = -1._r8 - allocate(dataptr2d_gdd20_season_end (lsize, ncft)) + allocate(dataptr2d_gdd20_season_end (begp:endp, ncft)) dataptr2d_gdd20_season_end(begp:endp,:) = -1._r8 if (stream_gdd20_seasons) then ! Starting with npcropmin will skip generic crops From 63b66b66944e0bacf55da5e87f48a1a860ca9211 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 08:22:57 -0600 Subject: [PATCH 323/406] Add ERP_D_Ld10_P64x2.f10_f10_mg37.IHistClm60BgcCrop.derecho_intel.clm-default to crop_calendars suite. --- cime_config/testdefs/testlist_clm.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index efeb011844..bb421c8e9e 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -1016,6 +1016,7 @@ + From 7e04126d66429ee7419441538447bb3a3cc4b448 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 08:26:32 -0600 Subject: [PATCH 324/406] dataptr2d arrays' first dim is gridcell, not patch. Hopefully will eliminate too-high index errors in threading tests. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 36c6c78f22..d19c74ad37 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -544,10 +544,10 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) dayspyr = get_curr_days_per_year() ! Read prescribed sowing window start dates from input files - allocate(dataptr2d_swindow_start(begp:endp, ncft)) - dataptr2d_swindow_start(begp:endp,:) = -1._r8 - allocate(dataptr2d_swindow_end (begp:endp, ncft)) - dataptr2d_swindow_end(begp:endp,:) = -1._r8 + allocate(dataptr2d_swindow_start(bounds%begg:bounds%endg, ncft)) + dataptr2d_swindow_start(bounds%begg:bounds%endg,:) = -1._r8 + allocate(dataptr2d_swindow_end (bounds%begg:bounds%endg, ncft)) + dataptr2d_swindow_end(bounds%begg:bounds%endg,:) = -1._r8 if (use_cropcal_rx_swindows) then ! Starting with npcropmin will skip generic crops do n = 1, ncft @@ -563,7 +563,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) end if ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here - do g = 1,lsize + do g = bounds%begg, bounds%endg ! If read-in value is invalid, set to -1. Will be handled later in this subroutine. if (dataptr1d_swindow_start(g) <= 0 .or. dataptr1d_swindow_start(g) > dayspyr & @@ -631,7 +631,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) deallocate(dataptr2d_swindow_start) deallocate(dataptr2d_swindow_end) - allocate(dataptr2d_cultivar_gdds(begp:endp, ncft)) + allocate(dataptr2d_cultivar_gdds(bounds%begg:bounds%endg, ncft)) if (use_cropcal_rx_cultivar_gdds) then ! Read prescribed cultivar GDDs from input files ! Starting with npcropmin will skip generic crops @@ -688,7 +688,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) deallocate(dataptr2d_cultivar_gdds) - allocate(dataptr2d_gdd20_baseline(begp:endp, ncft)) + allocate(dataptr2d_gdd20_baseline(bounds%begg:bounds%endg, ncft)) if (adapt_cropcal_rx_cultivar_gdds) then ! Read GDD20 baselines from input files ! Starting with npcropmin will skip generic crops @@ -741,10 +741,10 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Read prescribed gdd20 season start dates from input files - allocate(dataptr2d_gdd20_season_start(begp:endp, ncft)) - dataptr2d_gdd20_season_start(begp:endp,:) = -1._r8 - allocate(dataptr2d_gdd20_season_end (begp:endp, ncft)) - dataptr2d_gdd20_season_end(begp:endp,:) = -1._r8 + allocate(dataptr2d_gdd20_season_start(bounds%begg:bounds%endg, ncft)) + dataptr2d_gdd20_season_start(bounds%begg:bounds%endg,:) = -1._r8 + allocate(dataptr2d_gdd20_season_end (bounds%begg:bounds%endg, ncft)) + dataptr2d_gdd20_season_end(bounds%begg:bounds%endg,:) = -1._r8 if (stream_gdd20_seasons) then ! Starting with npcropmin will skip generic crops do n = 1, ncft From d47b28a5e3e44b66a11fc8055d57f24c02d8bbbc Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 08:36:47 -0600 Subject: [PATCH 325/406] Use begg and endg instead of lsize. Hopefully will eliminate too-high index errors in threading tests. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index d19c74ad37..02cfc5e67a 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -507,7 +507,6 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) integer :: nc, fp integer :: dayspyr integer :: n, g - integer :: lsize integer :: rc integer :: begp, endp real(r8), pointer :: dataptr1d_swindow_start(:) @@ -536,7 +535,6 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Get pointer for stream data that is time and spatially interpolate to model time and grid ! Place all data from each type into a temporary 2d array - lsize = bounds%endg - bounds%begg + 1 begp = bounds%begp endp = bounds%endp @@ -644,7 +642,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here - do g = 1,lsize + do g = bounds%begg, bounds%endg ! If read-in value is invalid, have PlantCrop() set gddmaturity to PFT-default value. if (dataptr1d_cultivar_gdds(g) < 0 .or. dataptr1d_cultivar_gdds(g) > 1000000._r8) then @@ -672,8 +670,8 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! vegetated pft ig = g_to_ig(patch%gridcell(p)) - if (ig > lsize) then - write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') > lsize (',lsize,')' + if (ig < bounds%begg .or. ig > bounds%endg) then + write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') < begg (',bounds%begg,') or > endg (',bounds%endg,')' call ESMF_Finalize(endflag=ESMF_END_ABORT) end if @@ -701,7 +699,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here - do g = 1,lsize + do g = bounds%begg, bounds%endg dataptr2d_gdd20_baseline(g,n) = dataptr1d_gdd20_baseline(g) end do end do @@ -723,8 +721,8 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! vegetated pft ig = g_to_ig(patch%gridcell(p)) - if (ig > lsize) then - write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') > lsize (',lsize,')' + if (ig < bounds%begg .or. ig > bounds%endg) then + write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') < begg (',bounds%begg,') or > endg (',bounds%endg,')' call ESMF_Finalize(endflag=ESMF_END_ABORT) end if @@ -760,7 +758,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) end if ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here - do g = 1,lsize + do g = bounds%begg, bounds%endg ! If read-in value is invalid, set to -1. Will be handled later in this subroutine. if (dataptr1d_gdd20_season_start(g) <= 0 .or. dataptr1d_gdd20_season_start(g) > 366 & From 2156c7206c61a131d3a4a6da69940acafb3d1dcf Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 08:40:18 -0600 Subject: [PATCH 326/406] Use begg/endg instead of referring to bounds%. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 51 ++++++++++++++----------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 02cfc5e67a..5587641c21 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -422,6 +422,7 @@ subroutine cropcal_advance( bounds ) ! ! !LOCAL VARIABLES: integer :: g, ig ! Indices + integer :: begg, endg ! gridcell bounds integer :: year ! year (0, ...) for nstep+1 integer :: mon ! month (1, ..., 12) for nstep+1 integer :: day ! day of month (1, ..., 31) for nstep+1 @@ -430,6 +431,9 @@ subroutine cropcal_advance( bounds ) integer :: rc !----------------------------------------------------------------------- + begg = bounds%begg + endg = bounds%endg + call get_curr_date(year, mon, day, sec) mcdate = year*10000 + mon*100 + day if (use_cropcal_rx_swindows) then @@ -471,9 +475,9 @@ subroutine cropcal_advance( bounds ) end if if ( .not. allocated(g_to_ig) )then - allocate (g_to_ig(bounds%begg:bounds%endg) ) + allocate (g_to_ig(begg:endg) ) ig = 0 - do g = bounds%begg,bounds%endg + do g = begg,endg ig = ig+1 g_to_ig(g) = ig end do @@ -508,6 +512,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) integer :: dayspyr integer :: n, g integer :: rc + integer :: begg, endg integer :: begp, endp real(r8), pointer :: dataptr1d_swindow_start(:) real(r8), pointer :: dataptr1d_swindow_end (:) @@ -530,8 +535,10 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) gdd20_season_ends => crop_inst%gdd20_season_end_patch & ) - SHR_ASSERT_FL( (lbound(g_to_ig,1) <= bounds%begg ), sourcefile, __LINE__) - SHR_ASSERT_FL( (ubound(g_to_ig,1) >= bounds%endg ), sourcefile, __LINE__) + begg = bounds%begg + endg = bounds%endg + SHR_ASSERT_FL( (lbound(g_to_ig,1) <= begg ), sourcefile, __LINE__) + SHR_ASSERT_FL( (ubound(g_to_ig,1) >= endg ), sourcefile, __LINE__) ! Get pointer for stream data that is time and spatially interpolate to model time and grid ! Place all data from each type into a temporary 2d array @@ -542,10 +549,10 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) dayspyr = get_curr_days_per_year() ! Read prescribed sowing window start dates from input files - allocate(dataptr2d_swindow_start(bounds%begg:bounds%endg, ncft)) - dataptr2d_swindow_start(bounds%begg:bounds%endg,:) = -1._r8 - allocate(dataptr2d_swindow_end (bounds%begg:bounds%endg, ncft)) - dataptr2d_swindow_end(bounds%begg:bounds%endg,:) = -1._r8 + allocate(dataptr2d_swindow_start(begg:endg, ncft)) + dataptr2d_swindow_start(begg:endg,:) = -1._r8 + allocate(dataptr2d_swindow_end (begg:endg, ncft)) + dataptr2d_swindow_end(begg:endg,:) = -1._r8 if (use_cropcal_rx_swindows) then ! Starting with npcropmin will skip generic crops do n = 1, ncft @@ -561,7 +568,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) end if ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here - do g = bounds%begg, bounds%endg + do g = begg, endg ! If read-in value is invalid, set to -1. Will be handled later in this subroutine. if (dataptr1d_swindow_start(g) <= 0 .or. dataptr1d_swindow_start(g) > dayspyr & @@ -629,7 +636,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) deallocate(dataptr2d_swindow_start) deallocate(dataptr2d_swindow_end) - allocate(dataptr2d_cultivar_gdds(bounds%begg:bounds%endg, ncft)) + allocate(dataptr2d_cultivar_gdds(begg:endg, ncft)) if (use_cropcal_rx_cultivar_gdds) then ! Read prescribed cultivar GDDs from input files ! Starting with npcropmin will skip generic crops @@ -642,7 +649,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here - do g = bounds%begg, bounds%endg + do g = begg, endg ! If read-in value is invalid, have PlantCrop() set gddmaturity to PFT-default value. if (dataptr1d_cultivar_gdds(g) < 0 .or. dataptr1d_cultivar_gdds(g) > 1000000._r8) then @@ -670,8 +677,8 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! vegetated pft ig = g_to_ig(patch%gridcell(p)) - if (ig < bounds%begg .or. ig > bounds%endg) then - write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') < begg (',bounds%begg,') or > endg (',bounds%endg,')' + if (ig < begg .or. ig > endg) then + write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') < begg (',begg,') or > endg (',endg,')' call ESMF_Finalize(endflag=ESMF_END_ABORT) end if @@ -686,7 +693,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) deallocate(dataptr2d_cultivar_gdds) - allocate(dataptr2d_gdd20_baseline(bounds%begg:bounds%endg, ncft)) + allocate(dataptr2d_gdd20_baseline(begg:endg, ncft)) if (adapt_cropcal_rx_cultivar_gdds) then ! Read GDD20 baselines from input files ! Starting with npcropmin will skip generic crops @@ -699,7 +706,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here - do g = bounds%begg, bounds%endg + do g = begg, endg dataptr2d_gdd20_baseline(g,n) = dataptr1d_gdd20_baseline(g) end do end do @@ -721,8 +728,8 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! vegetated pft ig = g_to_ig(patch%gridcell(p)) - if (ig < bounds%begg .or. ig > bounds%endg) then - write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') < begg (',bounds%begg,') or > endg (',bounds%endg,')' + if (ig < begg .or. ig > endg) then + write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') < begg (',begg,') or > endg (',endg,')' call ESMF_Finalize(endflag=ESMF_END_ABORT) end if @@ -739,10 +746,10 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Read prescribed gdd20 season start dates from input files - allocate(dataptr2d_gdd20_season_start(bounds%begg:bounds%endg, ncft)) - dataptr2d_gdd20_season_start(bounds%begg:bounds%endg,:) = -1._r8 - allocate(dataptr2d_gdd20_season_end (bounds%begg:bounds%endg, ncft)) - dataptr2d_gdd20_season_end(bounds%begg:bounds%endg,:) = -1._r8 + allocate(dataptr2d_gdd20_season_start(begg:endg, ncft)) + dataptr2d_gdd20_season_start(begg:endg,:) = -1._r8 + allocate(dataptr2d_gdd20_season_end (begg:endg, ncft)) + dataptr2d_gdd20_season_end(begg:endg,:) = -1._r8 if (stream_gdd20_seasons) then ! Starting with npcropmin will skip generic crops do n = 1, ncft @@ -758,7 +765,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) end if ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here - do g = bounds%begg, bounds%endg + do g = begg, endg ! If read-in value is invalid, set to -1. Will be handled later in this subroutine. if (dataptr1d_gdd20_season_start(g) <= 0 .or. dataptr1d_gdd20_season_start(g) > 366 & From b1aed3f36e79cb8eaf278f1a72be77de88ad1c64 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 09:12:57 -0600 Subject: [PATCH 327/406] Update ChangeLog and ChangeSum. --- doc/ChangeLog | 13 ++++++------- doc/ChangeSum | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index 501a67720f..88c322e990 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,7 +1,7 @@ =============================================================== Tag name: ctsm5.2.016 Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) -Date: Thu Jul 25 22:39:39 MST 2024 +Date: Fri 26 Jul 2024 09:11:52 AM MDT One-line Summary: Enable new crop calendars for clm60 compsets Purpose and description of changes @@ -89,22 +89,21 @@ Note that testmods marked "experimental" are fine to be marked as expected failu Testing summary: ---------------- -[Remove any lines that don't apply.] - [PASS means all tests PASS; OK means tests PASS other than expected fails.] build-namelist tests (if CLMBuildNamelist.pm has changed): - derecho - + derecho - FAIL + 1x1_cidadinhoBR tests only. That's fine. Filed Issue #2666: 1x1_cidadinhoBR tests fail in build-namelist_test.pl (https://github.com/ESCOMP/CTSM/issues/2666). python testing (if python code has changed; see instructions in python/README.md; document testing done): - (any machine) - + derecho - PASS regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): - derecho ----- - izumi ------- + derecho ----- OK + izumi ------- OK Answer changes -------------- diff --git a/doc/ChangeSum b/doc/ChangeSum index f043ab8b34..edc9e4cada 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,6 +1,6 @@ Tag Who Date Summary ============================================================================================================================ - ctsm5.2.016 samrabin 07/25/2024 Enable new crop calendars for clm60 compsets + ctsm5.2.016 samrabin 07/26/2024 Enable new crop calendars for clm60 compsets ctsm5.2.015 multiple 07/22/2024 Update submodule tags to pass runoff from cism to rof ctsm5.2.014 multiple 07/19/2024 use_matrixcn, use_soil_matrixcn come in as default .false. ctsm5.2.013 glemieux 07/18/2024 FATES Land Use V2 From 66e28d184b6d68f55c8ac2bf3d26b5d46399e7bb Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 12:12:23 -0600 Subject: [PATCH 328/406] Changes one Izumi test from f19 to 10x15 resolution. - Was: ERS_D.f19_g17.I1850Clm50BgcCrop.izumi_nag.clm-ciso_monthly_matrixcn_spinup - Now: ERS_D.f10_f10_mg37.I1850Clm50BgcCrop.izumi_nag.clm-ciso_monthly_matrixcn_spinup Fixes ESCOMP/CTSM#2667: Change out a f19 resolution test on Izumi for f10 (https://github.com/ESCOMP/CTSM/issues/2667) --- cime_config/testdefs/testlist_clm.xml | 8 ++++++++ doc/ChangeLog | 9 +++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index bb421c8e9e..951e0d8d5b 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -863,6 +863,14 @@ + + + + + + + + diff --git a/doc/ChangeLog b/doc/ChangeLog index 88c322e990..0057567561 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,7 +1,7 @@ =============================================================== Tag name: ctsm5.2.016 Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) -Date: Fri 26 Jul 2024 09:11:52 AM MDT +Date: Fri Jul 26 12:20:46 MDT 2024 One-line Summary: Enable new crop calendars for clm60 compsets Purpose and description of changes @@ -35,12 +35,10 @@ Bugs fixed List of CTSM issues fixed (include CTSM Issue # and description): - Fixes ESCOMP/CTSM#2584: Reset accumulators to initval instead of 0 (https://github.com/ESCOMP/CTSM/issues/2584) +- Fixes ESCOMP/CTSM#2667: Change out a f19 resolution test on Izumi for f10 (https://github.com/ESCOMP/CTSM/issues/2667) Notes of particular relevance for users --------------------------------------- -[Remove any lines that don't apply. Remove entire section if nothing applies.] - -Caveats for users (e.g., need to interpolate initial conditions): Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): - Add cropcals_rx, default false. @@ -82,6 +80,9 @@ Changes to tests or testing: - StreamGDD20Seasons (experimental) - Removes sowingWindows testmod - Adds and changes various tests related to crop calendars +- Changes one Izumi test from f19 to 10x15 resolution: + - Was: ERS_D.f19_g17.I1850Clm50BgcCrop.izumi_nag.clm-ciso_monthly_matrixcn_spinup + - Now: ERS_D.f10_f10_mg37.I1850Clm50BgcCrop.izumi_nag.clm-ciso_monthly_matrixcn_spinup Note that testmods marked "experimental" are fine to be marked as expected failures, if needed. From 3ee733375e55e024054904244bf341526b70fc23 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 26 Jul 2024 13:31:35 -0600 Subject: [PATCH 329/406] Bring in changes from Danny into the soil state functions, and update dust unit tests to use the appropriate ones for Zender and Leung tests, add tests for the new MassFracClayLeung2023 function, get the unit tests all working again --- .../DustEmis_test/test_DustEmisLeung2023.pf | 4 +-- .../DustEmis_test/test_DustEmisZender2003.pf | 6 ++--- src/biogeophys/SoilStateInitTimeConstMod.F90 | 24 ++++++++++++++--- .../test_dust_soil_clay_functions.pf | 17 +++++++++++- src/unit_test_shr/unittestDustEmisInputs.F90 | 27 ++++++++++++++++--- 5 files changed, 64 insertions(+), 14 deletions(-) diff --git a/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf b/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf index 5fdf20b89d..2b87f4be0a 100644 --- a/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf +++ b/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf @@ -35,10 +35,10 @@ contains ! Allocate and initialize the test object for input objects dust-emission needs character(len=5) :: NLFilename = 'none' - call this%input%setUp() + dust_emis_method = 'Leung_2023' + call this%input%setUp( dust_emis_method=dust_emis_method ) ! Create the dust emission object last - dust_emis_method = 'Leung_2023' allocate(this%dust_emis, source = create_dust_emissions(bounds, NLFilename)) end subroutine setUp diff --git a/src/biogeochem/test/DustEmis_test/test_DustEmisZender2003.pf b/src/biogeochem/test/DustEmis_test/test_DustEmisZender2003.pf index 212da0af5a..db5b54aa08 100644 --- a/src/biogeochem/test/DustEmis_test/test_DustEmisZender2003.pf +++ b/src/biogeochem/test/DustEmis_test/test_DustEmisZender2003.pf @@ -107,7 +107,7 @@ contains call this%dust_emis%GetPatchVars( p, flx_mss_vrt_dst_tot=flx_mss_vrt_dst_tot, & vlc_trb_1=vlc_trb_1, vlc_trb_2=vlc_trb_2, vlc_trb_3=vlc_trb_3, & vlc_trb_4=vlc_trb_4) - @assertEqual( flx_mss_vrt_dst_tot, 1.208384635884010d-5, tolerance=tol ) + @assertEqual( flx_mss_vrt_dst_tot, 2.583480541056971d-6, tolerance=tol ) @assertEqual( vlc_trb_1, 3.407721147709135d-003, tolerance=tol ) @assertEqual( vlc_trb_2, 4.961153753164878d-003, tolerance=tol ) @assertEqual( vlc_trb_3, 4.980100969983446d-003, tolerance=tol ) @@ -270,7 +270,7 @@ contains call this%validate_patch(p) call this%dust_emis%GetPatchVars( p, flx_mss_vrt_dst_tot=flx_mss_vrt_dst_tot ) total_dust0 = flx_mss_vrt_dst_tot - @assertEqual( flx_mss_vrt_dst_tot, 1.063573374792962d-4, tolerance=tol ) + @assertEqual( flx_mss_vrt_dst_tot, 2.273879554711299d-5, tolerance=tol ) end do ! Double u10 and show result is higher call this%input%create_fv( u10=u10*2.0_r8, fv=fv) @@ -282,7 +282,7 @@ contains call this%validate_patch(p) call this%dust_emis%GetPatchVars( p, flx_mss_vrt_dst_tot=flx_mss_vrt_dst_tot ) total_dust_higher = flx_mss_vrt_dst_tot - @assertEqual( flx_mss_vrt_dst_tot, 1.774023264204829d-4, tolerance=tol ) + @assertEqual( flx_mss_vrt_dst_tot, 3.792794484764924d-5, tolerance=tol ) end do @assertGreaterThan( total_dust_higher, total_dust0 ) diff --git a/src/biogeophys/SoilStateInitTimeConstMod.F90 b/src/biogeophys/SoilStateInitTimeConstMod.F90 index ce051514bd..f03e1f83c7 100644 --- a/src/biogeophys/SoilStateInitTimeConstMod.F90 +++ b/src/biogeophys/SoilStateInitTimeConstMod.F90 @@ -10,6 +10,8 @@ module SoilStateInitTimeConstMod use LandunitType , only : lun use ColumnType , only : col use PatchType , only : patch + use abortUtils , only : endrun + use shr_infnan_mod, only : nan => shr_infnan_nan, assignment(=) ! implicit none private @@ -22,6 +24,7 @@ module SoilStateInitTimeConstMod public :: ThresholdSoilMoistZender2003 public :: ThresholdSoilMoistKok2014 public :: MassFracClay + public :: MassFracClayLeung2023 ! ! !PRIVATE MEMBER FUNCTIONS: private :: ReadNL @@ -177,6 +180,7 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) use clm_varcon , only : secspday, denh2o, denice, grlnd use clm_varctl , only : use_cn, use_lch4, use_fates use clm_varctl , only : iulog, fsurdat, paramfile, soil_layerstruct_predefined + use clm_varctl , only : dust_emis_method use landunit_varcon , only : istdlak, istwet, istsoil, istcrop, istice use column_varcon , only : icol_roof, icol_sunwall, icol_shadewall, icol_road_perv, icol_road_imperv use fileutils , only : getfil @@ -717,7 +721,11 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) g = col%gridcell(c) soilstate_inst%gwc_thr_col(c) = ThresholdSoilMoistZender2003( clay3d(g,1) ) - soilstate_inst%mss_frc_cly_vld_col(c) = MassFracClay( clay3d(g,1) ) + if ( trim(dust_emis_method) == "Leung_2023" ) then + soilstate_inst%mss_frc_cly_vld_col(c) = MassFracClayLeung2023( clay3d(g,1) ) + else + soilstate_inst%mss_frc_cly_vld_col(c) = MassFracClay( clay3d(g,1) ) + end if end do @@ -757,8 +765,6 @@ real(r8) function ThresholdSoilMoistZender2003( clay ) ! Notes from: dmleung 19 Feb 2024. ! !------------------------------------------------------------------------------ - use abortUtils , only : endrun - use shr_infnan_mod, only : nan => shr_infnan_nan, assignment(=) real(r8), intent(IN) :: clay ! Fraction of clay in the soil (%) if ( clay < 0.0_r8 .or. clay > 100.0_r8 )then @@ -800,9 +806,19 @@ real(r8) function MassFracClay( clay ) real(r8), intent(IN) :: clay ! Fraction of lay in the soil (%) MassFracClay = min(clay * 0.01_r8, 0.20_r8) - MassFracClay = 0.1_r8 + MassFracClay * 0.1_r8 / 0.20_r8 ! dmleung added this line to reduce the sensitivity of dust emission flux to clay fraction in DUSTMod. 5 Jul 2024 end function MassFracClay !------------------------------------------------------------------------------ + real(r8) function MassFracClayLeung2023( clay ) + ! Calculate the mass fraction of clay needed for dust emission, based on clay content + ! Based on the base Zender_2003 version, with a slight modification for Leung_2023 + real(r8), intent(IN) :: clay ! Fraction of lay in the soil (%) + + MassFracClayLeung2023 = MassFracClay( clay ) + MassFracClayLeung2023 = 0.1_r8 + MassFracClayLeung2023 * 0.1_r8 / 0.20_r8 ! dmleung added this line to reduce the sensitivity of dust emission flux to clay fraction in DUSTMod. 5 Jul 2024 + end function MassFracClayLeung2023 + + !------------------------------------------------------------------------------ + end module SoilStateInitTimeConstMod diff --git a/src/biogeophys/test/SoilStateInitTimeConst_test/test_dust_soil_clay_functions.pf b/src/biogeophys/test/SoilStateInitTimeConst_test/test_dust_soil_clay_functions.pf index 867e7ace6a..53a3d66987 100644 --- a/src/biogeophys/test/SoilStateInitTimeConst_test/test_dust_soil_clay_functions.pf +++ b/src/biogeophys/test/SoilStateInitTimeConst_test/test_dust_soil_clay_functions.pf @@ -70,7 +70,7 @@ contains real(r8) :: value value = MassFracClay( 0.0_r8 ) - @assertEqual( value, 0.1_r8, tolerance=tol ) + @assertEqual( value, 0.0_r8, tolerance=tol ) value = MassFracClay( 20.0_r8 ) @assertEqual( value, 0.20_r8, tolerance=tol ) value = MassFracClay( 25.0_r8 ) @@ -78,4 +78,19 @@ contains end subroutine TestClayMassFracValues + + @Test + subroutine TestClayMassFracValuesLeung2023(this) + class(TestDustEmisSoilFunctions), intent(inout) :: this + real(r8) :: value + + value = MassFracClayLeung2023( 0.0_r8 ) + @assertEqual( value, 0.1_r8, tolerance=tol ) + value = MassFracClayLeung2023( 20.0_r8 ) + @assertEqual( value, 0.20_r8, tolerance=tol ) + value = MassFracClayLeung2023( 25.0_r8 ) + @assertEqual( value, 0.20_r8, tolerance=tol ) + + end subroutine TestClayMassFracValuesLeung2023 + end module test_dust_soil_clay_functions diff --git a/src/unit_test_shr/unittestDustEmisInputs.F90 b/src/unit_test_shr/unittestDustEmisInputs.F90 index 6b15680ef7..d272002638 100644 --- a/src/unit_test_shr/unittestDustEmisInputs.F90 +++ b/src/unit_test_shr/unittestDustEmisInputs.F90 @@ -16,6 +16,8 @@ module unittestDustEmisInputs use FrictionVelocityMod, only : frictionvel_type use unittestWaterTypeFactory, only : unittest_water_type_factory_type use SoilStateInitTimeConstMod, only : ThresholdSoilMoistZender2003, MassFracClay + use SoilStateInitTimeConstMod, only : MassFracClayLeung2023 + use abortutils, only : endrun implicit none private @@ -43,9 +45,10 @@ module unittestDustEmisInputs !----------------------------------------------------------------------- - subroutine setUp(this) + subroutine setUp(this, dust_emis_method) use ColumnType, only : col class(unittest_dust_emis_input_type), intent(inout) :: this + character(len=*), intent(in), optional :: dust_emis_method ! Allocate and initiatlize the test object for input objects dust-emission needs character(len=5) :: NLFilename = 'none' @@ -54,6 +57,14 @@ subroutine setUp(this) integer :: c type(atm2lnd_params_type) :: atm2lnd_params integer, parameter :: snl = 3 + character(len=50) :: in_dust_emis_method + + !----------------------------------------------------------------------- + if ( present(dust_emis_method) )then + in_dust_emis_method = trim(dust_emis_method) + else + in_dust_emis_method = "Zender_2003" + end if ! Settings needed for clm_varpar soil_layerstruct_predefined = '20SL_8.5m' @@ -83,7 +94,7 @@ subroutine setUp(this) call this%atm2lnd_inst%InitForTesting(bounds, atm2lnd_params) ! Water and soil state -- after the subgrid setup call this%water_factory%setup_after_subgrid(snl = snl) - call this%setupSoilState( ) ! This needs to happen before the water_type object creation + call this%setupSoilState( in_dust_emis_method ) ! This needs to happen before the water_type object creation call this%water_factory%create_water_type(this%water_inst, watsat_col=this%soilstate_inst%watsat_col) ! Canopy state, friction velocity, and temperature state ojects call this%canopystate_inst%SetNMLForTesting() @@ -113,7 +124,7 @@ end subroutine tearDown !----------------------------------------------------------------------- - subroutine setupSoilState(this) + subroutine setupSoilState(this, dust_emis_method) ! ! !DESCRIPTION: ! Sets up the external environment used by Dust emissions - i.e., things accessed via @@ -125,6 +136,7 @@ subroutine setupSoilState(this) use ColumnType, only : col use GridcellType, only : grc class(unittest_dust_emis_input_type), intent(in) :: this + character(len=*), intent(in) :: dust_emis_method ! integer :: c,j real(r8), parameter :: clay = 10.0_r8 @@ -145,7 +157,14 @@ subroutine setupSoilState(this) ! These are needed for dust emissions initialization do c = bounds%begc, bounds%endc this%soilstate_inst%gwc_thr_col(c) = ThresholdSoilMoistZender2003( clay ) - this%soilstate_inst%mss_frc_cly_vld_col(c) = MassFracClay( clay ) + if ( trim(dust_emis_method) == "Zender_2003")then + this%soilstate_inst%mss_frc_cly_vld_col(c) = MassFracClay( clay ) + else if ( trim(dust_emis_method) == "Leung_2023")then + this%soilstate_inst%mss_frc_cly_vld_col(c) = MassFracClayLeung2023( clay ) + else + print *, 'dust_emis_method = ', trim(dust_emis_method) + call endrun("ERROR: do NOT know about this dust_emis_method") + end if end do end subroutine setupSoilState From 2422cef7f1e0ef1cc184eb51c0f973f2125396e9 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sat, 27 Jul 2024 17:15:10 -0600 Subject: [PATCH 330/406] Update date on Change files --- doc/ChangeLog | 2 +- doc/ChangeSum | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index 0057567561..79d640242d 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,7 +1,7 @@ =============================================================== Tag name: ctsm5.2.016 Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) -Date: Fri Jul 26 12:20:46 MDT 2024 +Date: Sat 27 Jul 2024 05:13:08 PM MDT One-line Summary: Enable new crop calendars for clm60 compsets Purpose and description of changes diff --git a/doc/ChangeSum b/doc/ChangeSum index edc9e4cada..382e5cf7cb 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,6 +1,6 @@ Tag Who Date Summary ============================================================================================================================ - ctsm5.2.016 samrabin 07/26/2024 Enable new crop calendars for clm60 compsets + ctsm5.2.016 samrabin 07/27/2024 Enable new crop calendars for clm60 compsets ctsm5.2.015 multiple 07/22/2024 Update submodule tags to pass runoff from cism to rof ctsm5.2.014 multiple 07/19/2024 use_matrixcn, use_soil_matrixcn come in as default .false. ctsm5.2.013 glemieux 07/18/2024 FATES Land Use V2 From ef8acabe22df4925f33b7e09172a2af5fb73c28a Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sun, 28 Jul 2024 12:49:55 -0600 Subject: [PATCH 331/406] Bring in actuall Leung_2023 DustEmissions subroutine, unit test fails because of change in answers --- src/biogeochem/DustEmisLeung2023.F90 | 331 +++++++++++++++++++++------ 1 file changed, 260 insertions(+), 71 deletions(-) diff --git a/src/biogeochem/DustEmisLeung2023.F90 b/src/biogeochem/DustEmisLeung2023.F90 index 0acd254c92..f6cd6bed89 100644 --- a/src/biogeochem/DustEmisLeung2023.F90 +++ b/src/biogeochem/DustEmisLeung2023.F90 @@ -24,8 +24,8 @@ module DustEmisLeung2023 use atm2lndType , only : atm2lnd_type use SoilStateType , only : soilstate_type use CanopyStateType , only : canopystate_type - use WaterStateBulkType , only : waterstatebulk_type - use WaterDiagnosticBulkType , only : waterdiagnosticbulk_type + use WaterStateBulkType , only : waterstatebulk_type + use WaterDiagnosticBulkType, only : waterdiagnosticbulk_type use FrictionVelocityMod , only : frictionvel_type use LandunitType , only : lun use PatchType , only : patch @@ -47,7 +47,7 @@ module DustEmisLeung2023 real(r8), pointer, private :: dst_emiss_coeff_patch (:) ! dust emission coefficient (unitless) real(r8), pointer, private :: wnd_frc_thr_patch (:) ! wet fluid threshold (m/s) real(r8), pointer, private :: wnd_frc_thr_dry_patch (:) ! dry fluid threshold (m/s) - real(r8), pointer, private :: wnd_frc_thr_it_patch (:) ! impact threshold (m/s) + real(r8), pointer, private :: wnd_frc_thr_it_patch (:) ! impact threshold (m/s) real(r8), pointer, private :: lnd_frc_mble_patch (:) ! land mobile fraction real(r8), pointer, private :: liq_frac_patch (:) ! liquid fraction of total water real(r8), pointer, private :: wnd_frc_soil_patch (:) ! soil wind friction velocity (m/s) @@ -62,7 +62,7 @@ module DustEmisLeung2023 real(r8), pointer, private :: prb_crs_fld_thr_patch (:) ! probability of wind speed crossing fluid threshold real(r8), pointer, private :: prb_crs_impct_thr_patch (:) ! probability of wind speed crossing impact threshold real(r8), pointer, private :: ssr_patch (:) ! [dimless] integrated shear stress ratiio, defined by Okin (2008) and then integrated by Caroline Pierre et al. (2014) - real(r8), pointer, private :: vai_Okin_patch (:) ! [m2 leaf /m2 land] LAI+SAI for calculating Okin drag partition + real(r8), pointer, private :: vai_Okin_patch (:) ! [m2 leaf /m2 land] LAI+SAI for calculating Okin drag partition real(r8), pointer, private :: frc_thr_rghn_fct_patch (:) ! [dimless] hybrid drag partition (or called roughness) factor real(r8), pointer, private :: wnd_frc_thr_std_patch (:) ! standardized fluid threshold friction velocity (m/s) type(prigent_roughness_stream_type), private :: prigent_roughness_stream ! Prigent roughness stream data @@ -316,7 +316,7 @@ subroutine InitCold(this, bounds) ! ! !LOCAL VARIABLES: !----------------------------------------------------------------------- - ! Caulculate Drag Partition factor (Marticorena and Bergametti 1995 formulation) + ! Calculate Drag Partition factor (Marticorena and Bergametti 1995 formulation) if ( this%prigent_roughness_stream%useStreams() )then !if usestreams == true, and it should be always true call this%CalcDragPartition( bounds ) else @@ -367,8 +367,6 @@ subroutine DustEmission (this, bounds, & real(r8) :: flx_mss_vrt_dst_ttl(bounds%begp:bounds%endp) real(r8) :: frc_thr_wet_fct real(r8) :: frc_thr_rgh_fct - real(r8) :: wnd_frc_thr_slt - real(r8) :: wnd_rfr_thr_slt real(r8) :: wnd_frc_slt real(r8) :: lnd_frc_mbl(bounds%begp:bounds%endp) real(r8) :: bd @@ -378,20 +376,48 @@ subroutine DustEmission (this, bounds, & real(r8) :: sumwt(bounds%begl:bounds%endl) ! sum of weights logical :: found ! temporary for error check integer :: index + + real(r8) :: tmp2 ! calculates the dry fluid threshold using Shao and Lu (2000) scheme; + ! replace the tmp1 (Iversen and White, 1982) that was passed from Dustini to DustEmission; + ! tmp2 will be calculated here + real(r8) :: wnd_frc_thr_slt_std ! [m/s] The soil threshold friction speed at standard air density (1.2250 kg/m3) + real(r8) :: frag_expt ! fragmentation exponent + real(r8) :: wnd_frc_thr_slt_it ! [m/s] created for impact threshold friction velocity + real(r8) :: wnd_frc_thr_slt ! [m/s] used for wet fluid threshold friction velocity + real(r8) :: K_length ! [dimless] normalized mean interobstacle distance, or called gap length (Okin, 2008) + real(r8) :: bare_frc ! LUH2 bare soil land cover fraction + real(r8) :: veg_frc ! LUH2 natural vegetation + crop land cover fraction ! ! constants ! real(r8), parameter :: cst_slt = 2.61_r8 ! [frc] Saltation constant real(r8), parameter :: flx_mss_fdg_fct = 5.0e-4_r8 ! [frc] Empir. mass flx tuning eflx_lh_vegt - real(r8), parameter :: vai_mbl_thr = 0.3_r8 ! [m2 m-2] VAI threshold quenching dust mobilization + real(r8), parameter :: vai_mbl_thr = 1.0_r8 ! [m2 m-2] new VAI threshold; Danny M. Leung suggests 1, and Zender's scheme uses 0.3 + + real(r8), parameter :: Cd0 = 4.4e-5_r8 ! [dimless] proportionality constant in calculation of dust emission coefficient + real(r8), parameter :: Ca = 2.7_r8 ! [dimless] proportionality constant in scaling of dust emission exponent + real(r8), parameter :: Ce = 2.0_r8 ! [dimless] proportionality constant scaling exponential dependence of dust emission coefficient on standardized soil threshold friction speed + real(r8), parameter :: C_tune = 0.05_r8 ! [dimless] global tuning constant for vertical dust flux; set to produce ~same global dust flux in control sim (I_2000) as old parameterization + real(r8), parameter :: wnd_frc_thr_slt_std_min = 0.16_r8 ! [m/s] minimum standardized soil threshold friction speed + real(r8), parameter :: forc_rho_std = 1.2250_r8 ! [kg/m3] density of air at standard pressure (101325) and temperature (293 K) + real(r8), parameter :: dns_slt = 2650.0_r8 ! [kg m-3] Density of optimal saltation particles + real(r8), parameter :: B_it = 0.82_r8 ! [dimless] ratio = u_star_it / u_star_ft0 + real(r8), parameter :: k = 0.4_r8 ! [dimless] von Karman constant + real(r8), parameter :: f_0 = 0.32_r8 ! [dimless] SSR in the immediate lee of a plant + real(r8), parameter :: c_e = 4.8_r8 ! [dimless] e-folding distance velocity recovery + real(r8), parameter :: D_p = 130e-6_r8 ! [m] Medium soil particle diameter, assuming a global constant of ~130 um following Leung et al. (2023). dmleung 16 Feb 2024 + real(r8), parameter :: gamma_Shao = 1.65e-4_r8 ! [kg s-2] interparticle cohesion: fitting parameter in Shao and Lu (2000) (S&L00). dmleung 16 Feb 2024 + real(r8), parameter :: A_Shao = 0.0123_r8 ! [dimless] coefficient for aerodynamic force: fitting parameter in Shao and Lu (2000). dmleung 16 Feb 2024 + real(r8), parameter :: frag_expt_thr = 5.0_r8 ! [dimless] threshold for fragmentation exponent defined in Leung et al. (2023), somewhere within 3 to 5. It is used to prevent a local AOD blowup (over Patagonia, Argentina), but one can test larger values and relax the threshold if wanted. dmleung 16 Feb 2024 + real(r8), parameter :: z0a_glob = 1e-4_r8 ! [m] assumed globally constant aeolian roughness length value in Leung et al. (2023), for the log law of the wall for Comola et al. (2019) intermittency scheme. dmleung 20 Feb 2024 + real(r8), parameter :: hgt_sal = 0.1_r8 ! [m] saltation height used by Comola et al. (2019) intermittency scheme for the log law of the wall. dmleung 20 Feb 2024 + real(r8), parameter :: vai0_Okin = 0.1_r8 ! [m2/m2] minimum VAI needed for Okin-Pierre's vegetation drag partition equation. lai=0 in the equation will lead to infinity, so a small value is added into this lai dmleung defined. + real(r8), parameter :: zii = 1000.0_r8 ! [m] convective boundary layer height added by dmleung 20 Feb 2024, following other CTSM modules (e.g., CanopyFluxesMod). Should we transfer PBL height (PBLH) from CAM? + real(r8) :: numer ! Numerator term for threshold crossing rate + real(r8) :: denom ! Denominator term for threshold crossing rate character(len=*),parameter :: subname = 'DUSTEmission' !------------------------------------------------------------------------ - write(iulog,*) - write(iulog,*) - write(iulog,*) subname//'::WARNING: CURRENTLY THIS IS JUST THE ZENDER 2003 VERIONS OF DUST EMISSIONS!' - write(iulog,*) - write(iulog,*) associate( & forc_rho => atm2lnd_inst%forc_rho_downscaled_col , & ! Input: [real(r8) (:) ] downscaled density (kg/m**3) @@ -402,16 +428,40 @@ subroutine DustEmission (this, bounds, & tlai => canopystate_inst%tlai_patch , & ! Input: [real(r8) (:) ] one-sided leaf area index, no burying by snow tsai => canopystate_inst%tsai_patch , & ! Input: [real(r8) (:) ] one-sided stem area index, no burying by snow - frac_sno => waterdiagnosticbulk_inst%frac_sno_col , & ! Input: [real(r8) (:) ] fraction of ground covered by snow (0 to 1) - h2osoi_vol => waterstatebulk_inst%h2osoi_vol_col , & ! Input: [real(r8) (:,:) ] volumetric soil water (0<=h2osoi_vol<=watsat) - h2osoi_liq => waterstatebulk_inst%h2osoi_liq_col , & ! Input: [real(r8) (:,:) ] liquid soil water (kg/m2) - h2osoi_ice => waterstatebulk_inst%h2osoi_ice_col , & ! Input: [real(r8) (:,:) ] frozen soil water (kg/m2) + frac_sno => waterdiagnosticbulk_inst%frac_sno_col, & ! Input: [real(r8) (:) ] fraction of ground covered by snow (0 to 1) + h2osoi_vol => waterstatebulk_inst%h2osoi_vol_col , & ! Input: [real(r8) (:,:) ] volumetric soil water (0<=h2osoi_vol<=watsat) + h2osoi_liq => waterstatebulk_inst%h2osoi_liq_col , & ! Input: [real(r8) (:,:) ] liquid soil water (kg/m2) + h2osoi_ice => waterstatebulk_inst%h2osoi_ice_col , & ! Input: [real(r8) (:,:) ] frozen soil water (kg/m2) fv => frictionvel_inst%fv_patch , & ! Input: [real(r8) (:) ] friction velocity (m/s) (for dust model) u10 => frictionvel_inst%u10_patch , & ! Input: [real(r8) (:) ] 10-m wind (m/s) (created for dust model) flx_mss_vrt_dst => this%flx_mss_vrt_dst_patch , & ! Output: [real(r8) (:,:) ] surface dust emission (kg/m**2/s) - flx_mss_vrt_dst_tot => this%flx_mss_vrt_dst_tot_patch & ! Output: [real(r8) (:) ] total dust flux back to atmosphere (pft) + flx_mss_vrt_dst_tot => this%flx_mss_vrt_dst_tot_patch , & ! Output: [real(r8) (:) ] total dust flux back to atmosphere (pft) + ! below variables are defined in Kok et al. (2014) or (mostly) Leung et al. (2023) dust emission scheme. dmleung 16 Feb 2024 + dst_emiss_coeff => this%dst_emiss_coeff_patch , & ! Output dust emission coefficient + wnd_frc_thr => this%wnd_frc_thr_patch , & ! output fluid threshold + wnd_frc_thr_dry => this%wnd_frc_thr_dry_patch , & ! output dry fluid threshold + wnd_frc_thr_it => this%wnd_frc_thr_it_patch , & ! output impact threshold + lnd_frc_mble => this%lnd_frc_mble_patch , & ! output bare land fraction + wnd_frc_soil => this%wnd_frc_soil_patch , & ! soil friction velocity u_*s = (u_*)(f_eff) + gwc => this%gwc_patch , & ! output gravimetric water content + liq_frac => this%liq_frac_patch , & ! output fraction of liquid moisture + intrmtncy_fct => this%intrmtncy_fct_patch , & ! output intermittency factor eta (fraction of time that dust emission is active within a timestep) + stblty => this%stblty_patch , & ! stability in similarity theory (no need to output) + u_mean_slt => this%u_mean_slt_patch , & ! output mean wind speed at 0.1 m height translated from friction velocity using the log law of the wall, assuming neutral condition + u_sd_slt => this%u_sd_slt_patch , & ! output standard deviation of wind speed from similarity theory + u_fld_thr => this%u_fld_thr_patch , & ! output fluid threshold wind speed at 0.1 m height translated from the log law of the wall + u_impct_thr => this%u_impct_thr_patch , & ! output impact threshold wind speed at 0.1 m height translated from the log law of the wall + thr_crs_rate => this%thr_crs_rate_patch , & ! output threshold crossing rate in Comola 2019 intermittency parameterization + prb_crs_fld_thr => this%prb_crs_fld_thr_patch , & ! output probability of instantaneous wind crossing fluid threshold in Comola 2019 intermittency parameterization + prb_crs_impct_thr => this%prb_crs_impct_thr_patch , & ! output probability of instantaneous wind crossing impact threshold in Comola 2019 intermittency parameterization + ssr => this%ssr_patch , & ! output vegetation drag partition factor in Okin 2008 vegetation roughness effect (called shear stress ratio, SSR in Okin 2008) + vai_Okin => this%vai_Okin_patch , & ! vegetation area index for calculating Okin-Pierre vegetation drag partitioning. vai=0 in the ssr equation will lead to infinity, so a small value is added into this vai dmleung defined. (no need to output) 16 Feb 2024 + frc_thr_rghn_fct => this%frc_thr_rghn_fct_patch , & ! output hybrid/total drag partition factor considering both rock and vegetation drag partition factors. + wnd_frc_thr_std => this%wnd_frc_thr_std_patch , & ! standardized dust emission threshold friction velocity defined in Jasper Kok et al. (2014). + dpfct_rock => this%dpfct_rock_patch , & ! output rock drag partition factor defined in Marticorena and Bergametti 1995. A fraction between 0 and 1. + obu => frictionvel_inst%obu_patch & ! Input: [real(r8) (:) ] Monin-Obukhov length from the friction Velocity module obu => frictionvel_inst%obu_patch & ! Input: [real(r8) (:) ] Monin-Obukhov length from the friction Velocity module ) ttlai(bounds%begp : bounds%endp) = 0._r8 @@ -486,11 +536,33 @@ subroutine DustEmission (this, bounds, & end if end do + ! dmleung add output for bare_frc and veg_frc here if wanted !!!!---------------------- + ! reset history output variables before next if-statement to avoid output = inf do fp = 1,num_nolakep p = filter_nolakep(fp) flx_mss_vrt_dst_tot(p) = 0.0_r8 + dst_emiss_coeff(p) = 0.0_r8 + wnd_frc_thr(p) = 0.0_r8 + wnd_frc_thr_dry(p) = 0.0_r8 + lnd_frc_mble(p) = 0.0_r8 + wnd_frc_soil(p) = 0.0_r8 + gwc(p) = 0.0_r8 + liq_frac(p) = 0.0_r8 + u_mean_slt(p) = 0.0_r8 + u_sd_slt(p) = 0.0_r8 + stblty(p) = 0.0_r8 + u_fld_thr(p) = 0.0_r8 + u_impct_thr(p) = 0.0_r8 + thr_crs_rate(p) = 0.0_r8 + prb_crs_fld_thr(p) = 0.0_r8 + prb_crs_impct_thr(p) = 0.0_r8 + intrmtncy_fct(p) = 0.0_r8 + ssr(p) = 0.0_r8 + vai_Okin(p) = 0.0_r8 + frc_thr_rghn_fct(p) = 0.0_r8 + wnd_frc_thr_std(p) = 0.0_r8 end do do n = 1, ndst do fp = 1,num_nolakep @@ -505,86 +577,203 @@ subroutine DustEmission (this, bounds, & l = patch%landunit(p) g = patch%gridcell(p) - ! only perform the following calculations if lnd_frc_mbl is non-zero - - if (lnd_frc_mbl(p) > 0.0_r8) then + !-------------------------------------------------------------------------------------------------- + ! put dust emission calculation here to output threshold friction velocity for the whole globe, + ! not just when lnd_frc_mbl = 0. Danny M. Leung 27 Nov 2021 + + !#################################################################################################### + ! calculate soil moisture effect for dust emission threshold + ! following Fecan, Marticorena et al. (1999) + ! also see Zender et al. (2003) for DUST emission scheme and Kok et al. (2014b) for K14 emission scheme in CESM + bd = (1._r8-watsat(c,1))*dns_slt ![kg m-3] Bulk density of dry surface soil (dmleung changed from 2700 to dns_slt, soil particle density, on 16 Feb 2024. Note that dn s_slt=2650 kg m-3 so the value is changed by a tiny bit from 2700 to 2650. dns_slt has been here for many years so dns_slt should be used here instead of explicitly typing the value out. dmleung 16 Feb 2024) + + ! use emission threshold to calculate standardized threshold and dust emission coefficient + + ! Here convert h2osoi_vol (H2OSOI) at the topmost CTSM soil layer from volumetric (m3 water / m3 soil) to gravimetric soil moisture (kg water / kg soil) + gwc_sfc = h2osoi_vol(c,1)*SHR_CONST_RHOFW/bd ![kg kg-1] Gravimetric H2O cont + if (gwc_sfc > gwc_thr(c)) then + frc_thr_wet_fct = sqrt(1.0_r8 + 1.21_r8 * (100.0_r8*(gwc_sfc - gwc_thr(c)))**0.68_r8) ! dmleung's comment: this is an empirical equation by Fecan, Marticorena et al. (1999) on relating the soil moisture factor on enhancing dust emission threshold to gravimetric soil moisture. 1.21 and 0.68 are fitting parameters in the regression done by Fecan; 100 is to convert gracimetric soil moisture from fraction (kg water / kg soil) to percentage. Note that gwc_thr was defined in SoilStateInitConst.F90 as a fraction. 1.0_r8 means there is no soil moisture effect on enhancing dust emission threhsold. dmleung 16 Feb 2024. + else + frc_thr_wet_fct = 1.0_r8 + end if - ! the following comes from subr. frc_thr_rgh_fct_get - ! purpose: compute factor by which surface roughness increases threshold - ! friction velocity (currently a constant) + ! output moisture variables + gwc(p) = gwc_sfc ! output surface gravimetric water content + + ! slevis: adding liqfrac here, because related to effects from soil water + liqfrac = max( 0.0_r8, min( 1.0_r8, h2osoi_liq(c,1) / (h2osoi_ice(c,1)+h2osoi_liq(c,1)+1.0e-6_r8) ) ) + ! dmleung: output liquid fraction + liq_frac(p) = liqfrac + + !####################################################################################################### + ! calculate Shao & Lu (2000) dust emission threshold scheme here + ! use tmp1 from DUSTini for Iversen and White I&W (1982) (~75 um is optimal); use tmp2 for S&L (2000) (~80 um is optimal) + ! see Danny M. Leung et al. (2023) + !####################################################################################################### + tmp2 = sqrt(A_Shao * (dns_slt*grav*D_p + gamma_Shao/D_p)) ! calculate S&L (2000) scheme here for threshold; gamma = 1.65e-4 following S&L00, D_p = 127 um ~ 130 um following Leung et al. (2023). dmleung use defined parameters instead of typing numerical values 16 Feb 2024 + wnd_frc_thr_dry(p) = tmp2 / sqrt(forc_rho(c)) ! dry fluid threshold + wnd_frc_thr_slt = tmp2 / sqrt(forc_rho(c)) * frc_thr_wet_fct !* frc_thr_rgh_fct ! fluid threshold. dmleung commented out frc_thr_rgh_fct since it is used to modify the wind, not the wind threshold. + wnd_frc_thr_slt_it = B_it * tmp2 / sqrt(forc_rho(c)) ! define impact threshold + + ! the above formula is true for Iversen and White (1982) and Shao and Lu (2000) scheme + wnd_frc_thr(p) = wnd_frc_thr_slt ! output fluid threshold + wnd_frc_thr_it(p) = wnd_frc_thr_slt_it ! output impact threshold + + !############################################################################################## + ! dmleung: here, calculate quantities relevant to the fluid threshold + ! standardized fluid threshold + wnd_frc_thr_slt_std = wnd_frc_thr_slt * sqrt(forc_rho(c) / forc_rho_std) ! standardized soil threshold friction speed (defined using fluid threshold) + wnd_frc_thr_std(p) = wnd_frc_thr_slt_std ! output standardized fluid threshold + ! dust emission coefficient or soil erodibility coefficient (this is analogous to the soil erodibility map or prefenertial source filter in Zender; see zendersoilerodstream) + dst_emiss_coeff(p) = Cd0 * exp(-Ce * (wnd_frc_thr_slt_std - wnd_frc_thr_slt_std_min) / wnd_frc_thr_slt_std_min) ! save dust emission coefficient here for all grids + + ! framentation exponent (dependent on fluid threshold) + frag_expt = (Ca * (wnd_frc_thr_slt_std - wnd_frc_thr_slt_std_min) / wnd_frc_thr_slt_std_min) ! fragmentation exponent, defined in Kok et al. (2014a) + if (frag_expt > frag_expt_thr) then ! set fragmentation exponent to be 3 or 5 at maximum, to avoid local AOD blowup + frag_expt = frag_expt_thr + end if - frc_thr_rgh_fct = 1.0_r8 + !############################################################################################## + !################ drag partition effect, and soil-surface friction velocity ################### + ! subsection on computing vegetation drag partition and hybrid drag partition factors + ! in Leung et al. (2023), drag partition effect is applied on the wind instead of the threshold + !############################################################################################## + ! the following comes from subr. frc_thr_rgh_fct_get + ! purpose: compute factor by which surface roughness increases threshold + ! friction velocity (currently a constant) + + if (lnd_frc_mbl(p) > 0.0_r8 .AND. tlai_lu(l)<=1_r8) then + !if (lnd_frc_mbl(p) > 0.0_r8 .AND. ttlai(p)<=1_r8) then + ! vegetation drag partition equation following Gregory Okin (2008) + Caroline Pierre et al. (2014) + !lai(p) = tlai_lu(l)+0.1_r8 ! LAI+SAI averaged to landunit level; the equation is undefined at lai=0, and LAI in CTSM has some zeros over deserts, so we add in a small number. + !lai(p) = ttlai(p) + lai0_Okin ! ttlai = tlai+tsai. Okin-Pierre's equation is undefined at lai=0, and LAI in CTSM has some zeros over deserts, so we add in a small number. On 26 Feb 2024, dmleung changed from tlai_lu(l) to ttlai(p) + !if (lai(p) > 1_r8) then + ! lai(p) = 1_r8 ! setting LAI = 1 to be a max value (since K_length goes to negative when LAI>1) + !end if ! + + !if (ttlai(p) + vai0_Okin <= 1_r8) then + ! vai_Okin(p) = ttlai(p) + vai0_Okin ! ttlai = vai = tlai+tsai. Okin-Pierre's equation is undefined at vai=0, and VAI in CTSM has some zeros over deserts, so we add in a small number. On 26 Feb 2024, dmleung changed from tlai_lu(l) to ttlai(p) + !end if ! In the Okin-Pierre formulation, VAI has to be 0 < VAI <= 1. + + vai_Okin(p) = tlai_lu(l)+vai0_Okin ! LAI+SAI averaged to landunit level; the equation is undefined at lai=0, and LAI in CTSM has some zeros over deserts, so we add in a small number. + if (vai_Okin(p) > 1_r8) then + vai_Okin(p) = 1_r8 ! setting LAI = 1 to be a max value (since K_length goes to negative when LAI>1) + end if - ! the following comes from subr. frc_thr_wet_fct_get - ! purpose: compute factor by which soil moisture increases threshold friction velocity - ! adjust threshold velocity for inhibition by moisture - ! modified 4/5/2002 (slevis) to use gravimetric instead of volumetric - ! water content - bd = (1._r8-watsat(c,1))*2.7e3_r8 ![kg m-3] Bulk density of dry surface soil - gwc_sfc = h2osoi_vol(c,1)*SHR_CONST_RHOFW/bd ![kg kg-1] Gravimetric H2O cont - if (gwc_sfc > gwc_thr(c)) then - frc_thr_wet_fct = sqrt(1.0_r8 + 1.21_r8 * (100.0_r8*(gwc_sfc - gwc_thr(c)))**0.68_r8) + ! calculate Okin's shear stress ratio (SSR, which is vegetation drag partition factor) using Pierre's equation + K_length = 2_r8 * (1_r8/vai_Okin(p) - 1_r8) ! Here LAI has to be non-zero to avoid blowup, and < 1 to avoid -ve K_length. See this equation in Leung et al. (2023). This line is Okin's formulation + ssr(p) = (K_length+f_0*c_e)/(K_length+c_e) ! see this equation in Caroline Pierre et al. (2014) or Leung et al. (2023). This line is Pierre's formulation. + + ! calculation of the hybrid/total drag partition effect considering both rock and vegetation drag partitioning using LUH2 bare and veg fractions within a grid + if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then + if (patch%itype(p) == noveg) then ! if bare, uses rock drag partition factor + if (dpfct_rock(p) /= dpfct_rock(p)) then ! dmleung added 24 May 2024: dpfct_rock(p) could be NaN; CLM could run when DEBUG=FALSE in env_build.xml but dies when DEBUG=TRUE (usually when checking if wnd_frc_slt > wnd_frc_thr_slt_it and if numer/denom < 30._r8 below) + !write(iulog,*) 'dpfct_rock(p) == NaN; dpfct_rock(p) = ', dpfct_rock(p) + frc_thr_rgh_fct = 0.001_r8 ! Set drag partition effect to be a very small value (or zero) such that there is no emission whenever dpfct_rock(p) = NaN; dmleung 24 May 2024 + else + !write(iulog,*) 'dpfct_rock(p) = ', dpfct_rock(p) + frc_thr_rgh_fct = dpfct_rock(p) + end if + !frc_thr_rgh_fct = dpfct_rock(p) ! This should be the original code when dpfct_rock(p) has values everywhere + else ! if vegetation, uses vegetation drag partition factor + frc_thr_rgh_fct = ssr(p) + end if else - frc_thr_wet_fct = 1.0_r8 + frc_thr_rgh_fct = 1.0_r8 end if - ! slevis: adding liqfrac here, because related to effects from soil water + wnd_frc_slt = fv(p) * frc_thr_rgh_fct ! wnd_frc_slt is the drag-parition-modified wind speed and will be used in the dust emission equation below + + frc_thr_rghn_fct(p) = frc_thr_rgh_fct ! save and output hybrid drag partition factor - liqfrac = max( 0.0_r8, min( 1.0_r8, h2osoi_liq(c,1) / (h2osoi_ice(c,1)+h2osoi_liq(c,1)+1.0e-6_r8) ) ) + else ! for lnd_frc_mbl=0, do not change friction velocity and assume drag partition factor = 0 + wnd_frc_slt = fv(p) * frc_thr_rghn_fct(p) ! The value here is not important since once lnd_frc_mbl(p) <= 0.0_r8 there will be no emission. dmleung added 5 Jul 2024 + frc_thr_rghn_fct(p) = 0.0_r8 ! When LAI > 1, the drag partition effect is zero. dmleung 16 Feb 2024. + end if + + !########## end of drag partition effect ####################################################### - ! the following lines come from subr. dst_mbl - ! purpose: adjust threshold friction velocity to acct for moisture and - ! roughness. The ratio saltation_factor / sqrt(forc_rho) comes from - ! subr. wnd_frc_thr_slt_get which computes dry threshold - ! friction velocity for saltation + ! save soil friction velocity and roughness effect before the if-statement + wnd_frc_soil(p) = wnd_frc_slt ! save soil friction velocity for CLM output, which has drag partition and Owen effect + ! 20 Feb 2024: dmleung notes that Leung does not consider the Owen's effect. This is Jasper Kok's decision. The Owen's effect should be in Zender's DUST emission scheme. - wnd_frc_thr_slt = this%saltation_factor / sqrt(forc_rho(c)) * frc_thr_wet_fct * frc_thr_rgh_fct + ! save land mobile fraction + lnd_frc_mble(p) = lnd_frc_mbl(p) ! save land mobile fraction first, before the if-statement + ! only perform the following calculations if lnd_frc_mbl is non-zero + + if (lnd_frc_mbl(p) > 0.0_r8) then ! if bare land fraction is larger than 0 then calculate the dust emission equation ! reset these variables which will be updated in the following if-block - wnd_frc_slt = fv(p) flx_mss_hrz_slt_ttl = 0.0_r8 flx_mss_vrt_dst_ttl(p) = 0.0_r8 - ! the following line comes from subr. dst_mbl - ! purpose: threshold saltation wind speed + ! the following comes from subr. flx_mss_hrz_slt_ttl_Whi79_get + ! purpose: compute vertically integrated streamwise mass flux of particles - wnd_rfr_thr_slt = u10(p) * wnd_frc_thr_slt / fv(p) + if (wnd_frc_slt > wnd_frc_thr_slt_it) then! if using Leung's scheme, use impact threshold for dust emission equation; if Zender, uses fluid threshold (wnd_frc_thr_slt) for dust emission equation - ! the following if-block comes from subr. wnd_frc_slt_get - ! purpose: compute the saltating friction velocity - ! theory: saltation roughens the boundary layer, AKA "Owen's effect" + !################### for Leung et al. (2023) ################################################ + ! dmleung: instead of using mss_frc_cly_vld(c) with a range of [0,0.2] , which makes dust too sensitive to input clay surface dataset), for now use 0.1 + mss_frc_cly_vld(c) * 0.1 / 0.20 with a range of [0.1,0.2]. So, instead of scaling dust emission to 1/20 times for El Djouf (western Sahara) because of its 1 % clay fraction, scale its emission to 1/2 times. This reduces the sensitivity of dust emission to the clay input dataset. In particular, because dust emission is a small-scale process and the grid-averaged clay from the new WISE surface dataset over El Djouf is 1 %, much lower than the former FAO estimation of 5 %, dmleung is not sure if the clay value of 1 % suppresses too much of El Djouf's small-scale dust emission process. Similar to Charlie Zender's feeling suspicious about the soil texture datasets (or the dust emission schemes' sandblasting process), dmleung feels the same and for now decides to still allow dust emission to weakly scale with clay fraction, however limiting the scaling factor to 0.1-0.2. See modification in SoilStateInitTimeConst.F90. dmleung 5 Jul 2024 + flx_mss_vrt_dst_ttl(p) = dst_emiss_coeff(p) * mss_frc_cly_vld(c) * forc_rho(c) * ((wnd_frc_slt**2.0_r8 - wnd_frc_thr_slt_it**2.0_r8) / wnd_frc_thr_slt_it) * (wnd_frc_slt / wnd_frc_thr_slt_it)**frag_expt ! Leung et al. (2022) uses Kok et al. (2014) dust emission euqation for emission flux - if (u10(p) >= wnd_rfr_thr_slt) then - wnd_rfr_dlt = u10(p) - wnd_rfr_thr_slt - wnd_frc_slt_dlt = 0.003_r8 * wnd_rfr_dlt * wnd_rfr_dlt - wnd_frc_slt = fv(p) + wnd_frc_slt_dlt + ! account for bare soil fraction, frozen soil fraction, and apply global tuning parameter (Kok et al. 2014) + flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) * lnd_frc_mbl(p) * C_tune * liqfrac + !######################################################################################## end if - ! the following comes from subr. flx_mss_hrz_slt_ttl_Whi79_get - ! purpose: compute vertically integrated streamwise mass flux of particles + !############## Danny M. Leung added the intermittency calculation ################################# + ! subsection for intermittency factor calculation (only used by Leung's scheme, not Zender's scheme) + ! Leung et al. (2023) uses the Comola et al. (2019) intermittency scheme for the calculation of intermittent dust emissions. + ! This part takes care of the sub-timestep, high-frequency (< 1 minute period) turblent wind fluctuations occuring at the planetary boundary layer (PBL) near surface. Subtimestep wind gusts and episodes are important for generating emissions in marginal dust source regions, such as semiarid areas and high-latitude polar deserts. + ! 2 Dec 2021: assume no buoyancy contribution to the wind fluctuation (u_sd_slt), so no obul(p) is needed. It is shown to be important for the wind fluctuations contribute little to the intermittency factor. We might add this back in the future revisions. + ! 20 Feb 2024: dmleung notes that dmleung may revise Comola's scheme in the future to improve Comola's formulation of the statistical parameterization. - if (wnd_frc_slt > wnd_frc_thr_slt) then - wnd_frc_rat = wnd_frc_thr_slt / wnd_frc_slt - flx_mss_hrz_slt_ttl = cst_slt * forc_rho(c) * (wnd_frc_slt**3.0_r8) * & - (1.0_r8 - wnd_frc_rat) * (1.0_r8 + wnd_frc_rat) * (1.0_r8 + wnd_frc_rat) / grav + ! mean lowpass-filtered wind speed at hgt_sal = 0.1 m saltation height (assuming aerodynamic roughness length z0a_glob = 1e-4 m globally for ease; also assuming neutral condition) + u_mean_slt(p) = (wnd_frc_slt/k) * log(hgt_sal / z0a_glob) ! translating from ustar (velocity scale) to actual wind - ! the following loop originates from subr. dst_mbl - ! purpose: apply land sfc and veg limitations and global tuning factor - ! slevis: multiply flx_mss_hrz_slt_ttl by liqfrac to incude the effect - ! of frozen soil + !stblty(p) = 0_r8 ! -dmleung 2 Dec 2021: use stability = 0 for now, assuming no buoyancy contribution. Might uncomment the above lines in future revisions. + stblty(p) = zii / obu(p) ! -dmleung 20 Feb 2024: use obu from CTSM and PBL height = zii (= 1000_r8) which is default in CTSM. Should we transfer PBL height from CAM? + if ((12_r8 - 0.5_r8 * stblty(p)) .GE. 0.001_r8) then ! should have used 0 theoretically; used 0.001 here to avoid undefined values + u_sd_slt(p) = wnd_frc_slt * (12_r8 - 0.5_r8 * stblty(p))**0.333_r8 + else + u_sd_slt(p) = wnd_frc_slt * (0.001_r8)**0.333_r8 ! should have used 0 theoretically; used 0.001 here to avoid undefined values + end if - flx_mss_hrz_slt_ttl = flx_mss_hrz_slt_ttl * lnd_frc_mbl(p) * & - flx_mss_fdg_fct * liqfrac + ! threshold velocities + ! Here wnd_frc_thr_slt is the fluid threshold; wnd_frc_thr_dry(p) is the dry fluid threshold; B_it*wnd_frc_thr_dry(p) is the impact threshold + ! fluid threshold wind at 0.1 m saltation height + u_fld_thr(p) = (wnd_frc_thr_slt/k) * log(hgt_sal / z0a_glob) ! assume a globally constant z0a value for the log law of the wall, but it can be z0m from CLM or, better, z0a from Prigent's roughness dataset. Danny M. Leung et al. (2023) chose to assume a global constant z0a = 1e-4 m. dmleung 20 Feb 2024 + ! impact threshold wind at 0.1 m saltation height + u_impct_thr(p) = (wnd_frc_thr_slt_it/k) * log(hgt_sal / z0a_glob) + + ! threshold crossing rate + numer = (u_fld_thr(p)**2.0_r8 - u_impct_thr(p)**2.0_r8 - 2.0_r8 * u_mean_slt(p) * (u_fld_thr(p) - u_impct_thr(p))) + denom = (2.0_r8 * u_sd_slt(p)**2.0_r8) ! note that u_sd_slt should be always positive + ! Truncate to zero if the expression inside exp is becoming too large + if ( numer/denom < 30._r8 ) then ! set numer/denom to be < 30 given exp(30) below is already very large; also denom itself should be non-zero and non-negative given the standard deviation (u_sd_slt) of the subtimestep wind fluctuation is non-negative. dmleung 28 May 2024 + thr_crs_rate(p) = (exp((u_fld_thr(p)**2.0_r8 - u_impct_thr(p)**2.0_r8 - 2.0_r8 * u_mean_slt(p) * (u_fld_thr(p) - u_impct_thr(p))) / (2.0_r8 * u_sd_slt(p)**2.0_r8)) + 1.0_r8)**(-1.0_r8) + else + thr_crs_rate(p) = 0.0_r8 end if - ! the following comes from subr. flx_mss_vrt_dst_ttl_MaB95_get - ! purpose: diagnose total vertical mass flux of dust from vertically - ! integrated streamwise mass flux + ! probability that lowpass-filtered wind speed does not exceed u_ft + prb_crs_fld_thr(p) = 0.5_r8 * (1.0_r8 + erf((u_fld_thr(p) - u_mean_slt(p)) / ( sqrt(2.0_r8) * u_sd_slt(p)))) + ! probability that lowpass-filtered wind speed does not exceed u_it + prb_crs_impct_thr(p) = 0.5_r8 * (1.0_r8 + erf((u_impct_thr(p) - u_mean_slt(p)) / ( sqrt(2.0_r8) * u_sd_slt(p)))) + + ! intermittency factor (eta; ranging from 0 to 1) + intrmtncy_fct(p) = 1.0_r8 - prb_crs_fld_thr(p) + thr_crs_rate(p) * (prb_crs_fld_thr(p) - prb_crs_impct_thr(p)) + + ! multiply dust emission flux by intermittency factor + if (intrmtncy_fct(p) /= intrmtncy_fct(p)) then ! if intrmtncy_fct(p) is not NaN then multiply by intermittency factor; this statement is needed because dust emission flx_mss_vrt_dst_ttl(p) has to be non NaN (at least zero) to be outputted + flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) + else + flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) * intrmtncy_fct(p) ! multiply dust flux by intermittency + end if - dst_slt_flx_rat_ttl = 100.0_r8 * exp( log(10.0_r8) * (13.4_r8 * mss_frc_cly_vld(c) - 6.0_r8) ) - flx_mss_vrt_dst_ttl(p) = flx_mss_hrz_slt_ttl * dst_slt_flx_rat_ttl + !############ end the intermittency subsection here; only use for Leung's scheme ########################## end if ! lnd_frc_mbl > 0.0 From 667003d404dd4b9ca63efce0c3a0a115c0b42651 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sun, 28 Jul 2024 16:59:51 -0600 Subject: [PATCH 332/406] Fix several of the Leung 2023 unit tests because of change in answers, still two fail --- src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf b/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf index 2b87f4be0a..e18e00e269 100644 --- a/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf +++ b/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf @@ -107,7 +107,7 @@ contains call this%dust_emis%GetPatchVars( p, flx_mss_vrt_dst_tot=flx_mss_vrt_dst_tot, & vlc_trb_1=vlc_trb_1, vlc_trb_2=vlc_trb_2, vlc_trb_3=vlc_trb_3, & vlc_trb_4=vlc_trb_4) - @assertEqual( flx_mss_vrt_dst_tot, 1.208384635884010d-5, tolerance=tol ) + @assertEqual( flx_mss_vrt_dst_tot, 1.305341724414137d-006, tolerance=tol ) @assertEqual( vlc_trb_1, 3.407721147709135d-003, tolerance=tol ) @assertEqual( vlc_trb_2, 4.961153753164878d-003, tolerance=tol ) @assertEqual( vlc_trb_3, 4.980100969983446d-003, tolerance=tol ) @@ -270,7 +270,7 @@ contains call this%validate_patch(p) call this%dust_emis%GetPatchVars( p, flx_mss_vrt_dst_tot=flx_mss_vrt_dst_tot ) total_dust0 = flx_mss_vrt_dst_tot - @assertEqual( flx_mss_vrt_dst_tot, 1.063573374792962d-4, tolerance=tol ) + @assertEqual( flx_mss_vrt_dst_tot, 1.064145669761461d-5, tolerance=tol ) end do ! Double u10 and show result is higher call this%input%create_fv( u10=u10*2.0_r8, fv=fv) @@ -282,7 +282,7 @@ contains call this%validate_patch(p) call this%dust_emis%GetPatchVars( p, flx_mss_vrt_dst_tot=flx_mss_vrt_dst_tot ) total_dust_higher = flx_mss_vrt_dst_tot - @assertEqual( flx_mss_vrt_dst_tot, 1.774023264204829d-4, tolerance=tol ) + @assertEqual( flx_mss_vrt_dst_tot, 1.064145669761461d-5, tolerance=tol ) end do @assertGreaterThan( total_dust_higher, total_dust0 ) From 0f9fee7ea3b3f6732e2f0dd7bf75f7f6536b3cfa Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 29 Jul 2024 11:42:44 -0600 Subject: [PATCH 333/406] LILAC needs to read and handle the drv_flds_in file as dust emission settings are required and it's namelist must be read --- cime_config/SystemTests/lilacsmoke.py | 2 +- python/ctsm/lilac_make_runtime_inputs.py | 1 - src/CMakeLists.txt | 1 + src/cpl/lilac/lnd_comp_esmf.F90 | 5 +++++ 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cime_config/SystemTests/lilacsmoke.py b/cime_config/SystemTests/lilacsmoke.py index 1287301ba2..5bdbb31ec1 100644 --- a/cime_config/SystemTests/lilacsmoke.py +++ b/cime_config/SystemTests/lilacsmoke.py @@ -38,7 +38,7 @@ logger = logging.getLogger(__name__) -_LILAC_RUNTIME_FILES = ["lnd_in", "lnd_modelio.nml", "lilac_in"] +_LILAC_RUNTIME_FILES = ["lnd_in", "lnd_modelio.nml", "drv_flds_in", "lilac_in"] class LILACSMOKE(SystemTestsCommon): diff --git a/python/ctsm/lilac_make_runtime_inputs.py b/python/ctsm/lilac_make_runtime_inputs.py index 33b87c543e..e751f6c931 100644 --- a/python/ctsm/lilac_make_runtime_inputs.py +++ b/python/ctsm/lilac_make_runtime_inputs.py @@ -307,7 +307,6 @@ def buildnml(cime_path, rundir): # remove temporary files in rundir os.remove(os.path.join(rundir, "config_cache.xml")) os.remove(os.path.join(rundir, "env_lilac.xml")) - os.remove(os.path.join(rundir, "drv_flds_in")) os.remove(infile) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9388e65bc2..5b0f6c9b1b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -107,5 +107,6 @@ add_subdirectory(${CLM_ROOT}/src/init_interp/test clm_init_interp_test) add_subdirectory(${CLM_ROOT}/src/self_tests/test clm_self_tests_test) # Add driver unit test directories +# (these should be moved to the appropriate submodule) add_subdirectory(${CLM_ROOT}/src/drv_test drv_test) diff --git a/src/cpl/lilac/lnd_comp_esmf.F90 b/src/cpl/lilac/lnd_comp_esmf.F90 index 298aa730c0..6c8bb2a491 100644 --- a/src/cpl/lilac/lnd_comp_esmf.F90 +++ b/src/cpl/lilac/lnd_comp_esmf.F90 @@ -114,6 +114,8 @@ subroutine lnd_init(comp, import_state, export_state, clock, rc) use ESMF , only : ESMF_StateAdd use ESMF , only : operator(==) + use shr_dust_emis_mod , only : shr_dust_emis_readnl + ! input/output variables type(ESMF_GridComp) :: comp ! CLM gridded component type(ESMF_State) :: import_state ! CLM import state @@ -270,6 +272,9 @@ subroutine lnd_init(comp, import_state, export_state, clock, rc) ! Fill in the value for model_meshfile in lnd_comp_shr used by the stream routines in share_esmf/ model_meshfile = trim(lnd_mesh_filename) + ! Reading in the drv_flds_in namelist is required for dust emissions + call shr_dust_emis_readnl( mpicom, "drv_flds_in") + !---------------------- ! Obtain caseid and start type from attributes in import state !---------------------- From 6d07a80bb6a4d0238863907c92a8f984480e72ad Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Mon, 29 Jul 2024 11:50:48 -0600 Subject: [PATCH 334/406] pass physics and proper defaults. --- bld/CLMBuildNamelist.pm | 15 +++++++++------ bld/namelist_files/namelist_defaults_ctsm.xml | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index fa0d4a0382..77fb8a679f 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -1815,7 +1815,7 @@ sub process_namelist_inline_logic { ################################# # namelist group: exice_streams # ################################# - setup_logic_exice($opts, $nl_flags, $definition, $defaults, $nl); + setup_logic_exice($opts, $nl_flags, $definition, $defaults, $nl, $physv); ########################################## # namelist group: clm_temperature_inparm # @@ -2322,7 +2322,6 @@ sub setup_logic_soilstate { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'organic_frac_squared' ); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_bedrock', 'use_fates'=>$nl_flags->{'use_fates'}, 'vichydro'=>$nl_flags->{'vichydro'} ); - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_excess_ice'); # excess ice flag should be read before stream vars my $var1 = "soil_layerstruct_predefined"; my $var2 = "soil_layerstruct_userdefined"; @@ -4583,7 +4582,8 @@ sub setup_logic_exice { # # excess ice streams # - my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; + my ($opts, $nl_flags, $definition, $defaults, $nl, $physv) = @_; + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_excess_ice', 'phys'=>$physv->as_string()); my $use_exice = $nl->get_value( 'use_excess_ice' ); my $use_exice_streams = $nl->get_value( 'use_excess_ice_streams' ); my $finidat = $nl->get_value('finidat'); @@ -4642,10 +4642,13 @@ sub setup_logic_coldstart_temp { # set initial temperatures for excess ice gridcells: - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'excess_ice_coldstart_temp'); - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'excess_ice_coldstart_depth'); - my $use_exice = $nl->get_value( 'use_excess_ice' ); + + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'excess_ice_coldstart_temp', + 'use_excess_ice'=>$use_exice); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'excess_ice_coldstart_depth', + 'use_excess_ice'=>$use_exice); + my $use_exice_streams = $nl->get_value( 'use_excess_ice_streams' ); my $exice_cs_temp = $nl->get_value( 'excess_ice_coldstart_temp' ); my $exice_cs_depth = $nl->get_value( 'excess_ice_coldstart_depth' ); diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 47f9802aff..7ce2235a84 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -2058,8 +2058,8 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c .false. -0.0 -1.0 +-1.0 +0.5 -3.15 0.5 lnd/clm2/paramdata/exice_init_0.125x0.125_c20220516.nc From 7208b6671a7dde13624ecca56bc0e165a111ac9b Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 29 Jul 2024 16:36:29 -0600 Subject: [PATCH 335/406] Fix grammar --- bld/namelist_files/namelist_definition_ctsm.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 575d72856f..a987fc1300 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -2826,12 +2826,12 @@ If TRUE turn on the excess ice physics, (Lee et al., 2014; Cai et al., 2020) -Initial soil temperature to use for gridcells with excess ice present during a run starting with coldstart (deg C). Value is only apply if use_excess_ice is true. +Initial soil temperature to use for gridcells with excess ice present during a run starting with coldstart (deg C). Value only applys if use_excess_ice is true. -Soil depth below which initial excess ice concentration will be applied during a run starting with coldstart (m). Value is only apply if use_excess_ice is true. +Soil depth below which initial excess ice concentration will be applied during a run starting with coldstart (m). Value only applys if use_excess_ice is true. If this is set below depth of the soil depth, only the last soil layer will get excess ice. From 17c748bad564c560cf305549b744c7c82c13b5c5 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 29 Jul 2024 17:06:26 -0600 Subject: [PATCH 336/406] Fix 1x1_cidadinhoBR tests by specifying 2000_control for it and 1850_control for smallville --- bld/unit_testers/build-namelist_test.pl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index c92f428d82..d6291c94de 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -1532,7 +1532,11 @@ sub cat_and_create_namelistinfile { # Check for crop resolutions my @crop1850_res = ( "1x1_smallvilleIA", "1x1_cidadinhoBR" ); foreach my $res ( @crop1850_res ) { - $options = "-bgc bgc -crop -res $res -use_case 1850_control -envxml_dir ."; + my $use_case = "1850_control"; + if ( $res =~ /1x1_cidadinhoBR/ ) { + $use_case = "2000_control"; + } + $options = "-bgc bgc -crop -res $res -use_case $use_case -envxml_dir ."; &make_env_run(); eval{ system( "$bldnml $options > $tempfile 2>&1 " ); }; is( $@, '', "$options" ); From 0903d70234eab143bdb5f48959fed725e6766422 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 29 Jul 2024 18:00:13 -0600 Subject: [PATCH 337/406] This looks wrong, but is actually correct in order to get this to work through initialization, otherwise it fails thinking stremas are off --- src/cpl/share_esmf/PrigentRoughnessStreamType.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 index 564891bcac..2e78704614 100644 --- a/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 +++ b/src/cpl/share_esmf/PrigentRoughnessStreamType.F90 @@ -184,7 +184,7 @@ logical function UseStreams(this) UseStreams = .true. end if else - UseStreams = .false. ! Prigent streams are off without a filename given + UseStreams = .true. end if end function UseStreams From a58ad0fba8030617211b3e7d36167cc0a70631d6 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 29 Jul 2024 18:01:35 -0600 Subject: [PATCH 338/406] Get unit tests all working by adjusting the Leung 2023 test by what's doing on inside the Leung 2023 code --- .../test/DustEmis_test/test_DustEmisLeung2023.pf | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf b/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf index e18e00e269..e6634d9ad7 100644 --- a/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf +++ b/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf @@ -234,7 +234,7 @@ contains patch%itype(bounds%begp:bounds%endp) = 1 call this%input%create_atm2lnd() call this%input%create_fv( ) - this%input%canopystate_inst%tlai_patch(:) = 0.3_r8 + this%input%canopystate_inst%tlai_patch(:) = 1.0_r8 call this%dust_emis%DustEmission(bounds, this%input%num_nolakep, this%input%filter_nolakep, this%input%atm2lnd_inst, & this%input%soilstate_inst, this%input%canopystate_inst, this%input%water_inst%waterstatebulk_inst, & this%input%water_inst%waterdiagnosticbulk_inst, this%input%frictionvel_inst) @@ -250,8 +250,8 @@ contains !----------------------------------------------------------------------- @Test - subroutine check_dust_emis_increasing_wind(this) - ! Check dust emissions with increasing wind + subroutine check_dust_emis_increasing_fv(this) + ! Check dust emissions with increasing friction velocity class(TestDustEmisLeung2023), intent(inout) :: this integer :: p, c real(r8) :: flx_mss_vrt_dst_tot @@ -259,7 +259,7 @@ contains real(r8) :: u10 = 10._r8 real(r8) :: total_dust0, total_dust_higher - ! Run baseline u10 + ! Run baseline fv call this%input%create_atm2lnd() call this%input%create_fv( u10=u10, fv=fv ) call this%dust_emis%DustEmission(bounds, this%input%num_nolakep, this%input%filter_nolakep, this%input%atm2lnd_inst, & @@ -272,8 +272,8 @@ contains total_dust0 = flx_mss_vrt_dst_tot @assertEqual( flx_mss_vrt_dst_tot, 1.064145669761461d-5, tolerance=tol ) end do - ! Double u10 and show result is higher - call this%input%create_fv( u10=u10*2.0_r8, fv=fv) + ! Double fv and show result is higher + call this%input%create_fv( u10=u10, fv=fv*2.0_r8) call this%dust_emis%DustEmission(bounds, this%input%num_nolakep, this%input%filter_nolakep, this%input%atm2lnd_inst, & this%input%soilstate_inst, this%input%canopystate_inst, this%input%water_inst%waterstatebulk_inst, & this%input%water_inst%waterdiagnosticbulk_inst, this%input%frictionvel_inst) @@ -282,11 +282,11 @@ contains call this%validate_patch(p) call this%dust_emis%GetPatchVars( p, flx_mss_vrt_dst_tot=flx_mss_vrt_dst_tot ) total_dust_higher = flx_mss_vrt_dst_tot - @assertEqual( flx_mss_vrt_dst_tot, 1.064145669761461d-5, tolerance=tol ) + @assertEqual( flx_mss_vrt_dst_tot, 8.309603073613188d-5, tolerance=tol ) end do @assertGreaterThan( total_dust_higher, total_dust0 ) - end subroutine check_dust_emis_increasing_wind + end subroutine check_dust_emis_increasing_fv !----------------------------------------------------------------------- From a8ff15afcd6e4c7f813c4cd9b93605b8ddc7f216 Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Tue, 30 Jul 2024 08:01:23 -0600 Subject: [PATCH 339/406] add error when user sets streams to false and exice to true on coldstart --- bld/CLMBuildNamelist.pm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 77fb8a679f..ca99c131b0 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4595,6 +4595,11 @@ sub setup_logic_exice { } elsif ( (not defined($use_exice_streams)) && (not value_is_true($use_exice)) ) { $nl->set_variable_value('exice_streams', 'use_excess_ice_streams' , '.false.'); $use_exice_streams = '.false.'; + # Checking for cold clm_start_type and not finidat here since finidat can be not set set in branch/hybrid runs and + # These cases are handled in the restart routines in the model + } elsif ( defined($use_exice_streams) && (not value_is_true($use_exice_streams)) && value_is_true($use_exice) && + ( $nl_flags->{'clm_start_type'} eq "'cold'" || $nl_flags->{'clm_start_type'} eq "'arb_ic'" )) { + $log->fatal_error("use_excess_ice_streams can NOT be FALSE when use_excess_ice is TRUE on the cold start" ); } # If excess ice streams is on if (defined($use_exice_streams) && value_is_true($use_exice_streams)) { From 4d4fe83b759c972c9f4e8d0ad9422d1cd36c7693 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 30 Jul 2024 08:38:44 -0600 Subject: [PATCH 340/406] Fix test numbers --- bld/unit_testers/build-namelist_test.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index d6291c94de..5bccb3b77c 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -163,10 +163,10 @@ sub cat_and_create_namelistinfile { # # Figure out number of tests that will run # -my $ntests = 3317; +my $ntests = 3329; if ( defined($opts{'compare'}) ) { - $ntests += 2052; + $ntests += 1999; } plan( tests=>$ntests ); From cdb36a0f681d02a2c5720940c169a559783a3e9b Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 30 Jul 2024 08:39:36 -0600 Subject: [PATCH 341/406] Update change files --- doc/ChangeLog | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++ doc/ChangeSum | 1 + 2 files changed, 116 insertions(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index 79d640242d..8c01174329 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,119 @@ =============================================================== +Tag name: ctsm5.2.017 +Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) +Date: Tue 30 Jul 2024 08:39:20 AM MDT +One-line Summary: Dust emissions control moved to cmeps + +Purpose and description of changes +---------------------------------- + +Remove the dust emissions namelist items from CTSM and use the namelist in the drv_flds_in for CMEPS. + +This updates CTSM to use the namelist control in CMEPS (in ESCOMP/CMEPS#429). So the CMEPS external needs to be updated, and the +namelist control in CTSM changed to use CMEPS rather than the internal CTSM control settings and the CTSM ones removed. + +The new XML variable: + + LND_SETS_DUST_EMIS_DRV_FLDS + +controls whether dust emission settings are set by CTSM or by CAM. Only one or the other can set them, and it's required so when CAM +and CTSM are running together they need to know which one will select. + +This required some changes for LILAC. The drv_flds_in namelist file is now required for LILAC, and read for dust emissions +(and dust emissions only) at the LILAC lnd_comp_esmf.F90 level. + +Add a unit test for the CMEPS code to make sure it's working correctly. This validates the code both for CAM and CTSM. + +Fix the cidinahoBR test in the build-namelist unit tester. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm6_0 + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- + +List of CTSM issues fixed (include CTSM Issue # and description) [one per line]: + + Fixes #2376 -- Update CTSM with CMEPS that controls dust emission options + Fixes #2150 -- Ability to flip between different dust emission methods + Fixes #2524 -- Add option to NOT set dust emission settings when coupled to CAM + Fixes #2666 -- cidinahoBR test in build-namelist + +Notes of particular relevance for users +--------------------------------------- + +Caveats for users (e.g., need to interpolate initial conditions): + dust_emis_method can NOT be set to Leung_2023 -- yet. + See below about LND_SETS_DUST_EMIS_DRV_FLDS + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): + CTSM namelist items moved to the dust_emis_inparm namelist in drv_flds_in: + zender_soil_erod_source and dust_emis_method + + New logical XML variable: + LND_SETS_DUST_EMIS_DRV_FLDS + If TRUE CTSM sets the dust emission namelist, otherwise CAM will (when coupled to CAM) + If you are NOT coupled to CAM and LND_SETS_DUST_EMIS_DRV_FLDS==FALSE, the model will abort + trying to read the dust_emis_inparm namelist in drv_flds_in. + +Notes of particular relevance for developers: +--------------------------------------------- + +Caveats for developers (e.g., code that is duplicated that requires double maintenance): + Added a PF unit test for CMEPS dust emission code. This should be moved to CMEPS, but still run here. + Control design in DustEmisFactory, uses a Functional Programming design pattern with pure functions + from CMEPS for CTSM to know the drv_flds_in settings. Design notes were added in that regard. + +Testing summary: Regular +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + derecho - PASS (1006 are different because of the namelist changes) + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + derecho - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + + +Answer changes +-------------- + +Changes answers relative to baseline: No bit-for-bit + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +(https://github.com/ESCOMP/ctsm/pull) + #2545 -- Dust emission control moved to CMEPS + +=============================================================== +=============================================================== Tag name: ctsm5.2.016 Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) Date: Sat 27 Jul 2024 05:13:08 PM MDT diff --git a/doc/ChangeSum b/doc/ChangeSum index 382e5cf7cb..15a67bcf04 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.2.017 erik 07/30/2024 Dust emissions control moved to cmeps ctsm5.2.016 samrabin 07/27/2024 Enable new crop calendars for clm60 compsets ctsm5.2.015 multiple 07/22/2024 Update submodule tags to pass runoff from cism to rof ctsm5.2.014 multiple 07/19/2024 use_matrixcn, use_soil_matrixcn come in as default .false. From f28ecdb5cbd3ab3ab28ffd149ef54761055e926e Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 30 Jul 2024 08:44:59 -0600 Subject: [PATCH 342/406] Add note about the fails compared to baselines --- doc/ChangeLog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index 8c01174329..aeb7b2972f 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -105,6 +105,12 @@ Answer changes Changes answers relative to baseline: No bit-for-bit + The following tests compared different to baseline because of reproducability issues: + ERP_P64x2_Lm13.f10_f10_mg37.IHistClm60Bgc.derecho_intel.clm-monthly--clm-matrixcnOn_ignore_warnings + SMS_D.1x1_brazil.I2000Clm60FatesSpCruRsGs.derecho_gnu.clm-FatesColdDryDepSatPhen + SMS_D.1x1_brazil.I2000Clm60FatesSpCruRsGs.derecho_gnu.clm-FatesColdMeganSatPhen + The CN-MATRIX issue is because of #2619 and FATES because of #2656 + Other details ------------- From e5656454d59db3b2f414993bfb41d18ca54ba1b3 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Tue, 30 Jul 2024 21:26:45 -0600 Subject: [PATCH 343/406] Add more excess ice tests for fail conditions, the last two don't pass as they aren't setup yet --- bld/unit_testers/build-namelist_test.pl | 31 ++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index 53c71ae0e9..b9d20cbba9 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -163,10 +163,10 @@ sub cat_and_create_namelistinfile { # # Figure out number of tests that will run # -my $ntests = 3254; +my $ntests = 3263; if ( defined($opts{'compare'}) ) { - $ntests += 2001; + $ntests += 1965; } plan( tests=>$ntests ); @@ -322,7 +322,7 @@ sub cat_and_create_namelistinfile { "-res 0.9x1.25 -namelist '&a irrigate=.true./'", "-res 0.9x1.25 -verbose", "-res 0.9x1.25 -ssp_rcp SSP2-4.5", "-res 0.9x1.25 -test", "-res 0.9x1.25 -sim_year 1850", "-res 0.9x1.25 -namelist '&a use_lai_streams=.true.,use_soil_moisture_streams=.true./'", "-res 0.9x1.25 -namelist '&a use_excess_ice=.true. use_excess_ice_streams=.true./'", - "-res 0.9x1.25 -namelist '&a use_excess_ice=.true. use_excess_ice_streams=.false./'", + "-res 0.9x1.25 --clm_start_type cold -namelist '&a use_excess_ice=.true. use_excess_ice_streams=.true./'", "-res 0.9x1.25 -use_case 1850_control", "-res 1x1pt_US-UMB -clm_usr_name 1x1pt_US-UMB -namelist '&a fsurdat=\"/dev/null\"/'", "-res 1x1_brazil", @@ -542,6 +542,21 @@ sub cat_and_create_namelistinfile { GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, + "coldstart exice on wo stream"=>{ options=>"-res 0.9x1.25 -envxml_dir . --clm_start_type cold", + namelst=>"use_excess_ice=.true., use_excess_ice_streams = .false.", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm6_0", + }, + "coldstart exice on bad temp" =>{ options=>"-res 0.9x1.25 -envxml_dir . --clm_start_type cold", + namelst=>"use_excess_ice=.true., use_excess_ice_streams = .true., excess_ice_coldstart_temp=0.0", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm6_0", + }, + "coldstart exice on bad depth" =>{ options=>"-res 0.9x1.25 -envxml_dir . --clm_start_type cold", + namelst=>"use_excess_ice=.true., use_excess_ice_streams = .true., excess_ice_coldstart_depth=0.0", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm6_0", + }, "clm50CNDVwtransient" =>{ options=>" -envxml_dir . -use_case 20thC_transient -dynamic_vegetation -res 10x15 -ignore_warnings", namelst=>"", GLC_TWO_WAY_COUPLING=>"FALSE", @@ -1295,6 +1310,16 @@ sub cat_and_create_namelistinfile { GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, + "Set coldtemp wo coldstart" =>{ options=>"-envxml_dir . --clm_start_type startup", + namelst=>"use_excess_ice=.true.,excess_ice_coldstart_temp=-10.", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm6_0", + }, + "Set colddepth wo coldstart" =>{ options=>"-envxml_dir . --clm_start_type startup", + namelst=>"use_excess_ice=.true.,excess_ice_coldstart_depth=0.5", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm6_0", + }, "NotNEONbutNEONlightres" =>{ options=>"--res CLM_USRDAT --clm_usr_name regional --envxml_dir . --bgc bgc --light_res 106x174", namelst=>"fsurdat='build-namelist_test.pl'", GLC_TWO_WAY_COUPLING=>"FALSE", From e3605abe8fb9e614873fe273967ab6554209b27c Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 31 Jul 2024 14:28:18 -0600 Subject: [PATCH 344/406] Change the checks a bit, the coldstart temps always need to be set even if excess ice is off --- bld/CLMBuildNamelist.pm | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index ca99c131b0..ee82afdb17 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4645,9 +4645,22 @@ sub setup_logic_coldstart_temp { my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; - # set initial temperatures for excess ice gridcells: + # set initial temperatures for excess ice gridcells: needs to be set whether excess ice is on or not my $use_exice = $nl->get_value( 'use_excess_ice' ); + my $finidat = $nl->get_value('finidat'); + + my @list = ( "excess_ice_coldstart_temp", "excess_ice_coldstart_depth" ); + + # Only needs to be set if it's a coldstart + if ( ! string_is_undef_or_empty($finidat) ) { + foreach my $var ( @list ) { + my $val = $nl->get_value( $var ); + if ( defined($val) ) { + $log->warning("$var only needs to be set if this is a cold-start, although InitCold is always called"); + } + } + } add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'excess_ice_coldstart_temp', 'use_excess_ice'=>$use_exice); @@ -4658,15 +4671,12 @@ sub setup_logic_coldstart_temp { my $exice_cs_temp = $nl->get_value( 'excess_ice_coldstart_temp' ); my $exice_cs_depth = $nl->get_value( 'excess_ice_coldstart_depth' ); - if (defined($use_exice) && value_is_true($use_exice)) { - # Checking this setting only needed IF excess ice streams are on get the stream defaults - if (defined($use_exice_streams) && value_is_true($use_exice_streams)) { - if (defined($exice_cs_depth) && $exice_cs_depth <= 0.0 ) { - $log->fatal_error("excess_ice_coldstart_depth is <= 0.0" ); - } - if (defined($exice_cs_temp) && $exice_cs_temp >= 0.0 ) { - $log->fatal_error("excess_ice_coldstart_temp is >= 0.0, no excess ice will be present in this run" ); - } + if (defined($use_exice_streams) && value_is_true($use_exice_streams)) { + if (defined($exice_cs_depth) && $exice_cs_depth <= 0.0 ) { + $log->fatal_error("excess_ice_coldstart_depth is <= 0.0" ); + } + if (defined($exice_cs_temp) && $exice_cs_temp >= 0.0 ) { + $log->fatal_error("excess_ice_coldstart_temp is >= 0.0, no excess ice will be present in this run" ); } } } From 18df0661ea867a1b2e8cc75027be5ddaf0ea5753 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 31 Jul 2024 14:59:17 -0600 Subject: [PATCH 345/406] Save use_excice_streams to nl_flags so it doesn't have to be set to false when not needed --- bld/CLMBuildNamelist.pm | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index ee82afdb17..ab70d698f4 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4593,7 +4593,6 @@ sub setup_logic_exice { $use_exice_streams = '.true.'; # if excess ice is turned off } elsif ( (not defined($use_exice_streams)) && (not value_is_true($use_exice)) ) { - $nl->set_variable_value('exice_streams', 'use_excess_ice_streams' , '.false.'); $use_exice_streams = '.false.'; # Checking for cold clm_start_type and not finidat here since finidat can be not set set in branch/hybrid runs and # These cases are handled in the restart routines in the model @@ -4601,6 +4600,9 @@ sub setup_logic_exice { ( $nl_flags->{'clm_start_type'} eq "'cold'" || $nl_flags->{'clm_start_type'} eq "'arb_ic'" )) { $log->fatal_error("use_excess_ice_streams can NOT be FALSE when use_excess_ice is TRUE on the cold start" ); } + + # Put use_exice_streams into nl_flags so can be referenced later + $nl_flags->{'use_excice_streams'} = $use_exice_streams; # If excess ice streams is on if (defined($use_exice_streams) && value_is_true($use_exice_streams)) { # Can only be true if excess ice is also on, otherwise fail @@ -4652,7 +4654,7 @@ sub setup_logic_coldstart_temp { my @list = ( "excess_ice_coldstart_temp", "excess_ice_coldstart_depth" ); - # Only needs to be set if it's a coldstart + # Only needs to be set by the user if it's a coldstart if ( ! string_is_undef_or_empty($finidat) ) { foreach my $var ( @list ) { my $val = $nl->get_value( $var ); @@ -4667,7 +4669,7 @@ sub setup_logic_coldstart_temp { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'excess_ice_coldstart_depth', 'use_excess_ice'=>$use_exice); - my $use_exice_streams = $nl->get_value( 'use_excess_ice_streams' ); + my $use_exice_streams = $nl_flags->{'use_excice_streams'}; my $exice_cs_temp = $nl->get_value( 'excess_ice_coldstart_temp' ); my $exice_cs_depth = $nl->get_value( 'excess_ice_coldstart_depth' ); From 797444f1a5878c138ac6e198000f5ed292a992cf Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 31 Jul 2024 16:51:57 -0600 Subject: [PATCH 346/406] Start the ChangeLog --- doc/ChangeLog | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++ doc/ChangeSum | 1 + 2 files changed, 105 insertions(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index aeb7b2972f..f3bf5c91c1 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,108 @@ =============================================================== +Tag name: ctsm5.2.018 +Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) +Date: Wed 31 Jul 2024 04:38:36 PM MDT +One-line Summary: Fix/excess ice cold start + +Purpose and description of changes +---------------------------------- + +Changed the way soil temperature is initialized when excess ice is on and the model starts from cold. + +Specific notes + +Added 2 parameters: excess_ice_coldstart_depth and excess_ice_coldstart_temp which control top depth and soil temperature for soil layers in columns where excess ice is present. Other columns get their default soil temperature. + +excessicestream_type has been taken out of waterstate_type and its routines are called directly in clm_inst%Init. Checks for UseExcessIceStreams() are still in place in WaterStateType.F90 for double-checking. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm6_0 + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- + +List of CTSM issues fixed (include CTSM Issue # and description) [one per line]: + Fixes #2384 -- Cold start temperature init when excess ice is on + Fixes #2373 -- SMS_Lm3_D_Mmpi-serial.1x1_brazil.I2000Clm50FatesCruRsGs.izumi_intel.clm-FatesColdHydro fails + +Notes of particular relevance for users +--------------------------------------- + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): + Added: excess_ice_coldstart_temp and excess_ice_coldstart_depth + +Changes made to namelist defaults (e.g., changed parameter values): + Set new namelist items differently when excess ice is on or off + +Notes of particular relevance for developers: +--------------------------------------------- + +Caveats for developers (e.g., code that is duplicated that requires double maintenance): + Note that the coldstart variables are always used even without excess ice or with an finidat file. + InitCold is always called so the variables are always set. + +Changes to tests or testing: + New tests for build-namelist unit tester + +Testing summary: regular +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + derecho - PASS (998 are different from baseline) + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + derecho - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- + izumi ------- + + any other testing (give details below): + + SMS_Lm12.f09_f09_mg17.I1850Clm60Sp.derecho_intel.clm-ExcessIceStartup_output_sp_exice + derecho ---- PASS + +Answer changes +-------------- + +Changes answers relative to baseline: + + Summarize any changes to answers, i.e., + - what code configurations: + - what platforms/compilers: + - nature of change (roundoff; larger than roundoff/same climate; new climate): + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +(https://github.com/ESCOMP/ctsm/pull) + #2465 -- fix excess ice cold starts + +=============================================================== +=============================================================== Tag name: ctsm5.2.017 Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) Date: Tue 30 Jul 2024 08:39:20 AM MDT diff --git a/doc/ChangeSum b/doc/ChangeSum index 15a67bcf04..c70d3a372b 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.2.018 erik 07/31/2024 Fix/excess ice cold start ctsm5.2.017 erik 07/30/2024 Dust emissions control moved to cmeps ctsm5.2.016 samrabin 07/27/2024 Enable new crop calendars for clm60 compsets ctsm5.2.015 multiple 07/22/2024 Update submodule tags to pass runoff from cism to rof From 574380df9b4a465cfdb76d861d16b3f0ea8ec667 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 31 Jul 2024 16:54:27 -0600 Subject: [PATCH 347/406] Fix author --- doc/ChangeLog | 2 +- doc/ChangeSum | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index f3bf5c91c1..049a075ad6 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,6 +1,6 @@ =============================================================== Tag name: ctsm5.2.018 -Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) +Originator(s): mvdebolskiy Date: Wed 31 Jul 2024 04:38:36 PM MDT One-line Summary: Fix/excess ice cold start diff --git a/doc/ChangeSum b/doc/ChangeSum index c70d3a372b..d631d2d7dc 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,6 +1,6 @@ Tag Who Date Summary ============================================================================================================================ - ctsm5.2.018 erik 07/31/2024 Fix/excess ice cold start + ctsm5.2.018 mvdebols 07/31/2024 Fix/excess ice cold start ctsm5.2.017 erik 07/30/2024 Dust emissions control moved to cmeps ctsm5.2.016 samrabin 07/27/2024 Enable new crop calendars for clm60 compsets ctsm5.2.015 multiple 07/22/2024 Update submodule tags to pass runoff from cism to rof From fb4941b301bead3f5cd42d5147a47a0efa07ac75 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 1 Aug 2024 00:59:41 -0600 Subject: [PATCH 348/406] Don't need to set coldstart temp or depth as these tests are starting up from finidat files --- .../testdefs/testmods_dirs/clm/ExcessIceStreams/user_nl_clm | 2 -- 1 file changed, 2 deletions(-) diff --git a/cime_config/testdefs/testmods_dirs/clm/ExcessIceStreams/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/ExcessIceStreams/user_nl_clm index aa2c9efa5b..f61ca32a79 100644 --- a/cime_config/testdefs/testmods_dirs/clm/ExcessIceStreams/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/ExcessIceStreams/user_nl_clm @@ -1,4 +1,2 @@ use_excess_ice = .true. use_excess_ice_streams = .true. - excess_ice_coldstart_depth = 0.5 - excess_ice_coldstart_temp = -5.0 From 3ed874dd619f2d2af32e4292ae12a37fb765084f Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Thu, 1 Aug 2024 05:40:03 -0600 Subject: [PATCH 349/406] add more to notes. --- doc/ChangeLog | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index 049a075ad6..9bd2f3e86f 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -11,9 +11,13 @@ Changed the way soil temperature is initialized when excess ice is on and the mo Specific notes -Added 2 parameters: excess_ice_coldstart_depth and excess_ice_coldstart_temp which control top depth and soil temperature for soil layers in columns where excess ice is present. Other columns get their default soil temperature. +TemperatureType now has a private ReadNL subroutine that reads two new namelist options: excess_ice_coldstart_depth and excess_ice_coldstart_temp which control top depth (higher layers get default initial temperature) and soil temperature for soil layers ONLY in columns where excess ice is present. Other columns get their default soil temperature. + +New namelist options belong to a new group "clm_temperature_inparm" with its own logic routine. This is done so in the future hardcoded cold start temperatures can be moved to the namelists. Two namelist tests have been added to check for invalid values logic as well as a test for use_excess_ice_streams logic. use_excess_ice_streams has no default value (due to options for restart) and default value set in the CLMBuildNamelist.pm. + +excessicestream_type has been taken out of waterstate_type and its routines are called directly in clm_inst%Init. Checks for UseExcessIceStreams() are still in place in WaterStateType.F90 for double-checking. clm_inst%Init now has a new local variable with excess ice concentration that are read from streams (or zeros if use_excess_ice is false). Arguments for temperature_type%Init and water_type%Init (and children types) have been changed to include this new variable. Fortran unit tests are also updated to account for these new arguments. + -excessicestream_type has been taken out of waterstate_type and its routines are called directly in clm_inst%Init. Checks for UseExcessIceStreams() are still in place in WaterStateType.F90 for double-checking. Significant changes to scientifically-supported configurations From 6012fc622a7f5867f6ed087841a0dd00fa6ea4c6 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 1 Aug 2024 14:59:21 -0600 Subject: [PATCH 350/406] Update changelog with more info on the expected answer changes --- doc/ChangeLog | 21 ++++++++++++++------- doc/ChangeSum | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index 049a075ad6..55e0bb1329 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,7 +1,7 @@ =============================================================== Tag name: ctsm5.2.018 Originator(s): mvdebolskiy -Date: Wed 31 Jul 2024 04:38:36 PM MDT +Date: Thu 01 Aug 2024 02:53:41 PM MDT One-line Summary: Fix/excess ice cold start Purpose and description of changes @@ -76,13 +76,13 @@ Testing summary: regular regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): - derecho ----- - izumi ------- + derecho ----- OK + izumi ------- OK any other testing (give details below): SMS_Lm12.f09_f09_mg17.I1850Clm60Sp.derecho_intel.clm-ExcessIceStartup_output_sp_exice - derecho ---- PASS + derecho ---- PASS (but different from baseline) Answer changes -------------- @@ -90,9 +90,16 @@ Answer changes Changes answers relative to baseline: Summarize any changes to answers, i.e., - - what code configurations: - - what platforms/compilers: - - nature of change (roundoff; larger than roundoff/same climate; new climate): + - what code configurations: excess ice on + - what platforms/compilers: all + - nature of change: different at startup + Different for cold-start and can be different for a few points where the cold-start + values are still used on interpoaltion of an existing finidat file + + Tests that compare different to baseline: + ERS_D.f10_f10_mg37.I1850Clm60Sp.izumi_nag.clm-ExcessIceStreams + SMS_Lm12.f09_f09_mg17.I1850Clm60Sp.derecho_intel.clm-ExcessIceStartup_output_sp_exice + failed baseline comparison (though answers for default physics configurations have not changed). Other details ------------- diff --git a/doc/ChangeSum b/doc/ChangeSum index d631d2d7dc..7766c18851 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,6 +1,6 @@ Tag Who Date Summary ============================================================================================================================ - ctsm5.2.018 mvdebols 07/31/2024 Fix/excess ice cold start + ctsm5.2.018 mvdebols 08/01/2024 Fix/excess ice cold start ctsm5.2.017 erik 07/30/2024 Dust emissions control moved to cmeps ctsm5.2.016 samrabin 07/27/2024 Enable new crop calendars for clm60 compsets ctsm5.2.015 multiple 07/22/2024 Update submodule tags to pass runoff from cism to rof From 0f147f87c63d7cec705fb86f704a5c41ea73cebb Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Thu, 1 Aug 2024 16:43:46 -0600 Subject: [PATCH 351/406] Name hardwired coefficients into named params --- src/biogeochem/VOCEmissionMod.F90 | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/biogeochem/VOCEmissionMod.F90 b/src/biogeochem/VOCEmissionMod.F90 index b84d6a4ae8..3a6976a9ba 100644 --- a/src/biogeochem/VOCEmissionMod.F90 +++ b/src/biogeochem/VOCEmissionMod.F90 @@ -826,13 +826,14 @@ function get_gamma_SM(btran_in) !!!------- the drought algorithm-------- real(r8), parameter :: a1 = -7.4463_r8 real(r8), parameter :: b1 = 3.2552_r8 + real(r8), parameter :: btran_threshold = 0.2_r8 real(r8) :: get_gamma_SM !--------------------------------------- if (btran_in >= 1._r8) then get_gamma_SM = 1._r8 else - get_gamma_SM = 1._r8 / (1._r8 + b1 * exp(a1 * (btran_in - 0.2_r8))) + get_gamma_SM = 1._r8 / (1._r8 + b1 * exp(a1 * (btran_in - btran_threshold))) endif end function get_gamma_SM @@ -884,6 +885,12 @@ function get_gamma_T(t_veg240_in, t_veg24_in,t_veg_in, ct1_in, ct2_in, betaT_in, real(r8), parameter :: ct3 = 0.00831_r8 ! empirical coefficient (0.0083 in User's Guide) real(r8), parameter :: tstd = 303.15_r8 ! std temperature [K] real(r8), parameter :: bet = 0.09_r8 ! beta empirical coefficient [K-1] + real(r8), parameter :: std_act_energy_isopr = 95._r8 ! standard activation energy for isoprene + real(r8), parameter :: empirical_param_1 = 9.49_r8 ! empirical param for the activation energy in response to 10-day temperature change + real(r8), parameter :: empirical_param_2 = 0.53_r8 ! empirical param for the activation energy in response to 10-day temperature change + real(r8), parameter :: empirical_param_3 = 0.12_r8 ! empirical param for the emission factors of arctic C3 grass in response to 10-day temperature change + real(r8), parameter :: empirical_param_4 = 7.9_r8 ! empirical param for the emission factors of broadleaf deciduous boreal shrubs in response to 10-day temperature change + real(r8), parameter :: empirical_param_5 = 0.217_r8 ! empirical param for the emission factors of broadleaf deciduous boreal shrubs in response to 10-day temperature change !----------------------------------------------------------------------- ! Light dependent fraction (Guenther et al., 2006) @@ -892,11 +899,11 @@ function get_gamma_T(t_veg240_in, t_veg24_in,t_veg_in, ct1_in, ct2_in, betaT_in, topt = co1 + (co2 * (t_veg240_in-tstd0)) if ( (ivt_in == nbrdlf_dcd_brl_shrub) ) then ! boreal-deciduous-shrub ! coming from BEAR-oNS campaign willows results - Eopt = 7.9_r8 * exp (0.217_r8 * (t_veg24_in - tfrz - 24.0_r8)) + Eopt = empirical_param_4 * exp (empirical_param_5 * (t_veg24_in - tfrz - 24.0_r8)) else if ( (ivt_in == nc3_arctic_grass ) ) then ! boreal-grass - Eopt = exp(0.12_r8 * (t_veg240_in - tfrz - 15._r8)) + Eopt = exp(empirical_param_3 * (t_veg240_in - tfrz - 15._r8)) else - Eopt = Ceo_in * exp (co4 * (t_veg24_in - tstd0)) * exp(co4 * (t_veg240_in - tstd0)) + Eopt = Ceo_in * exp (co4 * (t_veg24_in - tstd0)) * exp(co4 * (t_veg240_in - tstd0)) endif else @@ -906,7 +913,7 @@ function get_gamma_T(t_veg240_in, t_veg24_in,t_veg_in, ct1_in, ct2_in, betaT_in, x = ( (1._r8/topt) - (1._r8/(t_veg_in)) ) / ct3 ! for the boreal grass from BEAR-oNS campaign if ( (ivt_in == nc3_arctic_grass ) ) then ! boreal-grass - bet_arc_c3 = 95._r8 + 9.49_r8 * exp(0.53_r8 * (tfrz + 15._r8 - t_veg240_in)) + bet_arc_c3 = std_act_energy_isopr + empirical_param_1 * exp(empirical_param_2 * (tfrz + 15._r8 - t_veg240_in)) bet_arc_c3 = min(bet_arc_c3, bet_arc_c3_max) gamma_t_LDF = Eopt * exp(bet_arc_c3 * ((1._r8 / (tfrz + 30._r8) - 1._r8 / t_veg_in) / ct3)) else From c5fb0bbb9a4cacb7e285f5169c5d5c39c25216e3 Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Thu, 1 Aug 2024 17:45:40 -0600 Subject: [PATCH 352/406] Documentation for two variables --- src/biogeochem/VOCEmissionMod.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/biogeochem/VOCEmissionMod.F90 b/src/biogeochem/VOCEmissionMod.F90 index 3a6976a9ba..533c7d892e 100644 --- a/src/biogeochem/VOCEmissionMod.F90 +++ b/src/biogeochem/VOCEmissionMod.F90 @@ -874,8 +874,8 @@ function get_gamma_T(t_veg240_in, t_veg24_in,t_veg_in, ct1_in, ct2_in, betaT_in, real(r8) :: gamma_t_LDF ! activity factor for temperature real(r8) :: gamma_t_LIF ! activity factor for temperature real(r8) :: x ! temporary i - real(r8) :: bet_arc_c3 - real(r8), parameter :: bet_arc_c3_max = 300._r8 + real(r8) :: bet_arc_c3 ! activity factor for temperature for arctic C3 grass + real(r8), parameter :: bet_arc_c3_max = 300._r8 ! max value, activity factor for temperature for arctic C3 graass real(r8), parameter :: co1 = 313._r8 ! empirical coefficient real(r8), parameter :: co2 = 0.6_r8 ! empirical coefficient real(r8), parameter :: co4 = 0.05_r8 ! empirical coefficient From 9dfc55a14af2ab918372e3323434c98a6ec78207 Mon Sep 17 00:00:00 2001 From: Samuel Levis Date: Thu, 1 Aug 2024 17:47:20 -0600 Subject: [PATCH 353/406] Draft ChangeLog/ChangeSum --- doc/ChangeLog | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++ doc/ChangeSum | 1 + 2 files changed, 71 insertions(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index aeb7b2972f..03a3c8999b 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,74 @@ =============================================================== +Tag name: ctsm5.2.0?? +Originator(s): HuiWangWanderInGitHub (Hui Wang,huiw16@uci.edu) +Date: Thu 01 Aug 2024 05:09:46 PM MDT +One-line Summary: MEGAN updates (MEGAN-CLM6) + +Purpose and description of changes +---------------------------------- + + We add new features to MEGANv2.1 for simulating isoprene emissions based on three recent studies conducted at the BAI lab in the University of California, Irvine. The first one is about the drought impact on isoprene (Wang et al., 2022). The second study investigates the effect of temperature history on the emission factors of boreal broadleaf deciduous shrubs (Wang et al., 2024a). The third study explores a different temperature response curve for C3 Arctic grass (Wang et al., 2024b, under press). These changes improved the model's representation of isoprene emissions during drought and in high-latitude ecosystems. + + This work relates to issue #1323 and is based on the old MEGANv2.1 framework, but incorporates new scientific insights. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm6_0 + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Notes of particular relevance for users +--------------------------------------- + +Changes to documentation: + Hui Wang has offered to provide the modifications to the documentation that we need. + The PR https://github.com/ESCOMP/CTSM/pull/2588 includes a list of references. + +Substantial timing or memory changes: +[e.g., check PFS test in the test suite and look at timings, if you +expect possible significant timing changes] + + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + +Answer changes +-------------- + +Changes answers relative to baseline: Yes + + Summarize any changes to answers, i.e., + - what code configurations: All that calculate BVOCs (so not FATES) + - what platforms/compilers: All + - nature of change: Larger than roundoff, only in BVOC output and specifically limited to MEG_ and _voc variables. + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/CTSM/pull/2588 + +=============================================================== +=============================================================== Tag name: ctsm5.2.017 Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) Date: Tue 30 Jul 2024 08:39:20 AM MDT diff --git a/doc/ChangeSum b/doc/ChangeSum index 15a67bcf04..315a5cb470 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.2.0?? slevis 08/01/2024 MEGAN updates (MEGAN-CLM6) ctsm5.2.017 erik 07/30/2024 Dust emissions control moved to cmeps ctsm5.2.016 samrabin 07/27/2024 Enable new crop calendars for clm60 compsets ctsm5.2.015 multiple 07/22/2024 Update submodule tags to pass runoff from cism to rof From a8371f7c1d76ea7b0ccb75ebe9ca63ae090473b2 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 2 Aug 2024 09:27:21 -0600 Subject: [PATCH 354/406] Update change files --- doc/ChangeLog | 7 +++---- doc/ChangeSum | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index c5c7c3b9f7..f5cf28096c 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,7 +1,7 @@ =============================================================== Tag name: ctsm5.2.018 Originator(s): mvdebolskiy -Date: Thu 01 Aug 2024 02:53:41 PM MDT +Date: Fri 02 Aug 2024 09:26:33 AM MDT One-line Summary: Fix/excess ice cold start Purpose and description of changes @@ -86,7 +86,7 @@ Testing summary: regular any other testing (give details below): SMS_Lm12.f09_f09_mg17.I1850Clm60Sp.derecho_intel.clm-ExcessIceStartup_output_sp_exice - derecho ---- PASS (but different from baseline) + derecho ---- PASS Answer changes -------------- @@ -94,7 +94,7 @@ Answer changes Changes answers relative to baseline: Summarize any changes to answers, i.e., - - what code configurations: excess ice on + - what code configurations: excess ice on, with coldstart or with finidat file interpolation - what platforms/compilers: all - nature of change: different at startup Different for cold-start and can be different for a few points where the cold-start @@ -102,7 +102,6 @@ Changes answers relative to baseline: Tests that compare different to baseline: ERS_D.f10_f10_mg37.I1850Clm60Sp.izumi_nag.clm-ExcessIceStreams - SMS_Lm12.f09_f09_mg17.I1850Clm60Sp.derecho_intel.clm-ExcessIceStartup_output_sp_exice failed baseline comparison (though answers for default physics configurations have not changed). Other details diff --git a/doc/ChangeSum b/doc/ChangeSum index 7766c18851..3d9d70bc54 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,6 +1,6 @@ Tag Who Date Summary ============================================================================================================================ - ctsm5.2.018 mvdebols 08/01/2024 Fix/excess ice cold start + ctsm5.2.018 mvdebols 08/02/2024 Fix/excess ice cold start ctsm5.2.017 erik 07/30/2024 Dust emissions control moved to cmeps ctsm5.2.016 samrabin 07/27/2024 Enable new crop calendars for clm60 compsets ctsm5.2.015 multiple 07/22/2024 Update submodule tags to pass runoff from cism to rof From b09ef392ba4026ac80f4416c6c9b88bf780d34d8 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 5 Aug 2024 15:46:52 -0600 Subject: [PATCH 355/406] mpi bcast dust_emis_method so can go between Leung and Zender methods --- src/main/controlMod.F90 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/controlMod.F90 b/src/main/controlMod.F90 index 46d9e9958a..ef4ead3709 100644 --- a/src/main/controlMod.F90 +++ b/src/main/controlMod.F90 @@ -871,6 +871,9 @@ subroutine control_spmd() call mpi_bcast (anoxia, 1, MPI_LOGICAL, 0, mpicom, ier) end if + ! dust emissions + call mpi_bcast (dust_emis_method, len(dust_emis_method), MPI_CHARACTER, 0, mpicom, ier) + ! lakes call mpi_bcast (deepmixing_depthcrit, 1, MPI_REAL8, 0, mpicom, ier) call mpi_bcast (deepmixing_mixfact, 1, MPI_REAL8, 0, mpicom, ier) From 90b534af01502b20e7bbd3ceb8250746b4eadfd5 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 5 Aug 2024 17:56:19 -0600 Subject: [PATCH 356/406] Change Izumi nag RxCropCalsAdaptGGCMI to debug. Previously: SMS_P128x1_Lm25.f10_f10_mg37.IHistClm60BgcCrop.izumi_nag.clm-RxCropCalsAdaptGGCMI Now: SMS_D_P128x1_Lm25.f10_f10_mg37.IHistClm60BgcCrop.izumi_nag.clm-RxCropCalsAdaptGGCMI --- cime_config/testdefs/testlist_clm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 951e0d8d5b..ce2b6de435 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3578,7 +3578,7 @@ - + From 7387303e8f5c1f40a60604dbf170dea12a877039 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 5 Aug 2024 17:59:53 -0600 Subject: [PATCH 357/406] Change 25-month crop_calendars tests to use cropMonthOutput. --- cime_config/testdefs/testlist_clm.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index ce2b6de435..d6ea6b08da 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3569,7 +3569,7 @@ - + @@ -3578,7 +3578,7 @@ - + From d3de08bfff1ec10ba088442d39f0da59ee106378 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 5 Aug 2024 18:01:36 -0600 Subject: [PATCH 358/406] Change 25-month crop_calendars tests to be 733 days instead. --- cime_config/testdefs/testlist_clm.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index d6ea6b08da..3ab3f86436 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3569,7 +3569,7 @@ - + @@ -3578,7 +3578,7 @@ - + From f40bdbb139656885a37d6199f9d4c37eda45069a Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 5 Aug 2024 18:04:01 -0600 Subject: [PATCH 359/406] Add temporary test suite fix_nag_bld_2659. --- cime_config/testdefs/testlist_clm.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 3ab3f86436..ea02ac9375 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3572,6 +3572,7 @@ + @@ -3583,6 +3584,7 @@ + From e72354571a2186c14400791490cc967887565de5 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Mon, 5 Aug 2024 22:25:03 -0600 Subject: [PATCH 360/406] Add a subroutine to set drag partition for testing, label input/output variables for DustEmission, remove begc/endc --- src/biogeochem/DustEmisLeung2023.F90 | 81 ++++++++++++++++++---------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/src/biogeochem/DustEmisLeung2023.F90 b/src/biogeochem/DustEmisLeung2023.F90 index f6cd6bed89..6cc86e4eb5 100644 --- a/src/biogeochem/DustEmisLeung2023.F90 +++ b/src/biogeochem/DustEmisLeung2023.F90 @@ -77,6 +77,8 @@ module DustEmisLeung2023 procedure , private :: InitHistory ! History initialization procedure , private :: InitCold procedure , private :: CalcDragPartition ! Calculate drag partitioning based on Prigent roughness stream + ! Public for unit testing + procedure , public :: SetDragPartition ! Set drag partitioning for testing end type dust_emis_leung2023_type @@ -126,11 +128,9 @@ subroutine InitAllocate(this, bounds) type(bounds_type), intent(in) :: bounds ! ! !LOCAL VARIABLES: - integer :: begc,endc integer :: begp,endp !------------------------------------------------------------------------ - begc = bounds%begc ; endc = bounds%endc begp = bounds%begp ; endp = bounds%endp allocate(this%dst_emiss_coeff_patch (begp:endp)) ; this%dst_emiss_coeff_patch (:) = nan allocate(this%wnd_frc_thr_patch (begp:endp)) ; this%wnd_frc_thr_patch (:) = nan @@ -208,11 +208,9 @@ subroutine InitHistory(this, bounds) type(bounds_type), intent(in) :: bounds ! ! !LOCAL VARIABLES: - integer :: begc,endc integer :: begp,endp !------------------------------------------------------------------------ - begc = bounds%begc; endc = bounds%endc begp = bounds%begp; endp = bounds%endp this%dst_emiss_coeff_patch(begp:endp) = spval call hist_addfld1d (fname='DUST_EMIS_COEFF', units='dimensionless', & @@ -434,34 +432,34 @@ subroutine DustEmission (this, bounds, & h2osoi_ice => waterstatebulk_inst%h2osoi_ice_col , & ! Input: [real(r8) (:,:) ] frozen soil water (kg/m2) fv => frictionvel_inst%fv_patch , & ! Input: [real(r8) (:) ] friction velocity (m/s) (for dust model) - u10 => frictionvel_inst%u10_patch , & ! Input: [real(r8) (:) ] 10-m wind (m/s) (created for dust model) + obu => frictionvel_inst%obu_patch , & ! Input: [real(r8) (:) ] Monin-Obukhov length from the friction Velocity module obu => frictionvel_inst%obu_patch & ! Input: [real(r8) (:) ] Monin-Obukhov length from the friction Velocity module + + dpfct_rock => this%dpfct_rock_patch , & ! Input: rock drag partition factor defined in Marticorena and Bergametti 1995. A fraction between 0 and 1. flx_mss_vrt_dst => this%flx_mss_vrt_dst_patch , & ! Output: [real(r8) (:,:) ] surface dust emission (kg/m**2/s) flx_mss_vrt_dst_tot => this%flx_mss_vrt_dst_tot_patch , & ! Output: [real(r8) (:) ] total dust flux back to atmosphere (pft) ! below variables are defined in Kok et al. (2014) or (mostly) Leung et al. (2023) dust emission scheme. dmleung 16 Feb 2024 - dst_emiss_coeff => this%dst_emiss_coeff_patch , & ! Output dust emission coefficient - wnd_frc_thr => this%wnd_frc_thr_patch , & ! output fluid threshold - wnd_frc_thr_dry => this%wnd_frc_thr_dry_patch , & ! output dry fluid threshold - wnd_frc_thr_it => this%wnd_frc_thr_it_patch , & ! output impact threshold - lnd_frc_mble => this%lnd_frc_mble_patch , & ! output bare land fraction - wnd_frc_soil => this%wnd_frc_soil_patch , & ! soil friction velocity u_*s = (u_*)(f_eff) - gwc => this%gwc_patch , & ! output gravimetric water content - liq_frac => this%liq_frac_patch , & ! output fraction of liquid moisture - intrmtncy_fct => this%intrmtncy_fct_patch , & ! output intermittency factor eta (fraction of time that dust emission is active within a timestep) - stblty => this%stblty_patch , & ! stability in similarity theory (no need to output) - u_mean_slt => this%u_mean_slt_patch , & ! output mean wind speed at 0.1 m height translated from friction velocity using the log law of the wall, assuming neutral condition - u_sd_slt => this%u_sd_slt_patch , & ! output standard deviation of wind speed from similarity theory - u_fld_thr => this%u_fld_thr_patch , & ! output fluid threshold wind speed at 0.1 m height translated from the log law of the wall - u_impct_thr => this%u_impct_thr_patch , & ! output impact threshold wind speed at 0.1 m height translated from the log law of the wall - thr_crs_rate => this%thr_crs_rate_patch , & ! output threshold crossing rate in Comola 2019 intermittency parameterization - prb_crs_fld_thr => this%prb_crs_fld_thr_patch , & ! output probability of instantaneous wind crossing fluid threshold in Comola 2019 intermittency parameterization - prb_crs_impct_thr => this%prb_crs_impct_thr_patch , & ! output probability of instantaneous wind crossing impact threshold in Comola 2019 intermittency parameterization - ssr => this%ssr_patch , & ! output vegetation drag partition factor in Okin 2008 vegetation roughness effect (called shear stress ratio, SSR in Okin 2008) - vai_Okin => this%vai_Okin_patch , & ! vegetation area index for calculating Okin-Pierre vegetation drag partitioning. vai=0 in the ssr equation will lead to infinity, so a small value is added into this vai dmleung defined. (no need to output) 16 Feb 2024 - frc_thr_rghn_fct => this%frc_thr_rghn_fct_patch , & ! output hybrid/total drag partition factor considering both rock and vegetation drag partition factors. - wnd_frc_thr_std => this%wnd_frc_thr_std_patch , & ! standardized dust emission threshold friction velocity defined in Jasper Kok et al. (2014). - dpfct_rock => this%dpfct_rock_patch , & ! output rock drag partition factor defined in Marticorena and Bergametti 1995. A fraction between 0 and 1. - obu => frictionvel_inst%obu_patch & ! Input: [real(r8) (:) ] Monin-Obukhov length from the friction Velocity module obu => frictionvel_inst%obu_patch & ! Input: [real(r8) (:) ] Monin-Obukhov length from the friction Velocity module + dst_emiss_coeff => this%dst_emiss_coeff_patch , & ! Output: dust emission coefficient + wnd_frc_thr => this%wnd_frc_thr_patch , & ! Output: fluid threshold + wnd_frc_thr_dry => this%wnd_frc_thr_dry_patch , & ! Output: dry fluid threshold + wnd_frc_thr_it => this%wnd_frc_thr_it_patch , & ! Output: impact threshold + lnd_frc_mble => this%lnd_frc_mble_patch , & ! Output: bare land fraction + wnd_frc_soil => this%wnd_frc_soil_patch , & ! Output: soil friction velocity u_*s = (u_*)(f_eff) + gwc => this%gwc_patch , & ! output: gravimetric water content + liq_frac => this%liq_frac_patch , & ! Output: fraction of liquid moisture + intrmtncy_fct => this%intrmtncy_fct_patch , & ! Output: intermittency factor eta (fraction of time that dust emission is active within a timestep) + stblty => this%stblty_patch , & ! Output: stability in similarity theory (no need to output) + u_mean_slt => this%u_mean_slt_patch , & ! Output: mean wind speed at 0.1 m height translated from friction velocity using the log law of the wall, assuming neutral condition + u_sd_slt => this%u_sd_slt_patch , & ! Output: standard deviation of wind speed from similarity theory + u_fld_thr => this%u_fld_thr_patch , & ! Output: fluid threshold wind speed at 0.1 m height translated from the log law of the wall + u_impct_thr => this%u_impct_thr_patch , & ! Output: impact threshold wind speed at 0.1 m height translated from the log law of the wall + thr_crs_rate => this%thr_crs_rate_patch , & ! Output: threshold crossing rate in Comola 2019 intermittency parameterization + prb_crs_fld_thr => this%prb_crs_fld_thr_patch , & ! Output: probability of instantaneous wind crossing fluid threshold in Comola 2019 intermittency parameterization + prb_crs_impct_thr => this%prb_crs_impct_thr_patch , & ! Output: probability of instantaneous wind crossing impact threshold in Comola 2019 intermittency parameterization + ssr => this%ssr_patch , & ! Output: vegetation drag partition factor in Okin 2008 vegetation roughness effect (called shear stress ratio, SSR in Okin 2008) + vai_Okin => this%vai_Okin_patch , & ! Output: vegetation area index for calculating Okin-Pierre vegetation drag partitioning. vai=0 in the ssr equation will lead to infinity, so a small value is added into this vai dmleung defined. (no need to output) 16 Feb 2024 + frc_thr_rghn_fct => this%frc_thr_rghn_fct_patch , & ! Output: hybrid/total drag partition factor considering both rock and vegetation drag partition factors. + wnd_frc_thr_std => this%wnd_frc_thr_std_patch & ! Output: standardized dust emission threshold friction velocity defined in Jasper Kok et al. (2014). ) ttlai(bounds%begp : bounds%endp) = 0._r8 @@ -869,6 +867,33 @@ subroutine CalcDragPartition(this, bounds) end subroutine CalcDragPartition + + !------------------------------------------------------------------------ + + subroutine SetDragPartition(this, bounds, drag_partition) + ! + ! !DESCRIPTION: + ! Set the drag partition for testing + ! + ! !USES: + ! + ! !ARGUMENTS: + implicit none + class (dust_emis_leung2023_type) :: this + type(bounds_type), intent(in) :: bounds + real(r8), intent(in) :: drag_partition + ! + ! !LOCAL VARIABLES: + integer :: p ! Indices + + !--------------------------------------------------------------------- + + do p = bounds%begp,bounds%endp + this%dpfct_rock_patch(p) = drag_partition + end do + + end subroutine SetDragPartition + !============================================================================== end module DustEmisLeung2023 \ No newline at end of file From 064c37cc02a207002e49cb7ab5e13195ffb0d669 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 7 Aug 2024 09:32:06 -0600 Subject: [PATCH 361/406] By default prigent streams are off for Zender dust emissions --- bld/CLMBuildNamelist.pm | 11 ++++++++--- bld/namelist_files/namelist_defaults_ctsm.xml | 3 ++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index f449dcc6e3..f1e5935196 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -1601,7 +1601,6 @@ sub process_namelist_inline_logic { setup_logic_fates($opts, $nl_flags, $definition, $defaults, $nl); setup_logic_z0param($opts, $nl_flags, $definition, $defaults, $nl); setup_logic_misc($opts, $nl_flags, $definition, $defaults, $nl); - setup_logic_prigent_roughness($opts, $nl_flags, $definition, $defaults, $nl); ######################################### # namelist group: atm2lnd_inparm @@ -1703,6 +1702,7 @@ sub process_namelist_inline_logic { # namelist options for dust emissions ###################################### setup_logic_dust_emis($opts, $nl_flags, $definition, $defaults, $nl); + setup_logic_prigent_roughness($opts, $nl_flags, $definition, $defaults, $nl); ################################# # namelist group: megan_emis_nl # @@ -4671,12 +4671,17 @@ sub setup_logic_prigent_roughness { # my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; my $var = "use_prigent_roughness"; - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var ); + my $dust_emis_method = remove_leading_and_trailing_quotes( $nl->get_value('dust_emis_method') ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var, + 'dust_emis_method'=>$dust_emis_method ); my $use_prigent = $nl->get_value($var); if ( &value_is_true($use_prigent) ) { + if ( $dust_emis_method eq "Leung_2023" ) { + $log->warning( "$var does NOT need to be set without dust_emis_method being Leung_2023" ); + } add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldfilename_prigentroughness' ); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_meshfile_prigentroughness' ); - } else { + } elsif ( $dust_emis_method eq "Leung_2023" ) { $log->fatal_error("variable \"$var\" MUST be true when Leung_2023 dust emission method is being used" ); } } diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 9a56c1f1b0..ea82141663 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -2001,7 +2001,8 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c -.true. +.true. +.false. lnd/clm2/dustemisdata/Prigent_2005_roughness_0.25x0.25_cdf5_c240127.nc Date: Wed, 7 Aug 2024 11:43:29 -0600 Subject: [PATCH 362/406] Remove include_user_mods from RxCropCals* and midDecStart testmods. --- cime_config/testdefs/testlist_clm.xml | 8 +++++--- .../clm/RxCropCalsAdaptGGCMI/include_user_mods | 1 - .../testmods_dirs/clm/RxCropCalsNoAdapt/include_user_mods | 1 - .../testmods_dirs/clm/midDecStart/include_user_mods | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods delete mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/include_user_mods delete mode 100644 cime_config/testdefs/testmods_dirs/clm/midDecStart/include_user_mods diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index ea02ac9375..1e687522e4 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3587,24 +3587,26 @@ - + - + + - + + diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods deleted file mode 100644 index 23ea3745e6..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../crop diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/include_user_mods deleted file mode 100644 index 23ea3745e6..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../crop diff --git a/cime_config/testdefs/testmods_dirs/clm/midDecStart/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/midDecStart/include_user_mods deleted file mode 100644 index fe0e18cf88..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/midDecStart/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../default From 38926beaa6623270bf54ae9a2efdaa19305366a1 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 7 Aug 2024 12:40:02 -0600 Subject: [PATCH 363/406] Copy CNFireLi2021Mod.F90 to new CNFireLi2024Mod.F90. Includes new fire_method li2024gswpfrc. --- bld/namelist_files/namelist_defaults_ctsm.xml | 15 + .../namelist_definition_ctsm.xml | 3 +- src/biogeochem/CNFireFactoryMod.F90 | 3 + src/biogeochem/CNFireLi2024Mod.F90 | 658 ++++++++++++++++++ 4 files changed, 678 insertions(+), 1 deletion(-) create mode 100644 src/biogeochem/CNFireLi2024Mod.F90 diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 655e97c47c..a7e59935a4 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -319,6 +319,21 @@ attributes from the config_cache.xml file (with keys converted to upper-case). 0.5d00 0.28d00 +30.0d00 +80.0d00 +0.85d00 +0.98d00 +0.025d00 +0.09d-4 +0.010d00 +0.17d-3 +1.6d-4 +0.33d00 +75.d00 +1050.d00 +0.5d00 +0.28d00 + .false. .true. diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 37c457141c..6b70c117e5 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -252,13 +252,14 @@ formulation (1). + group="cnfire_inparm" valid_values="nofire,li2014qianfrc,li2016crufrc,li2021gswpfrc,li2024gswpfrc" > The method type to use for CNFire nofire: Turn fire effects off li2014qianfrc: Reference paper Li, et. al.(2014) tuned with QIAN atmospheric forcing li2016crufrc: Reference paper Li, et. al.(2016) tuned with CRU-NCEP atmospheric forcing li2021gswpfrc: Reference paper Li, et. al.(2021?) tuned with GSWP3 atmospheric forcing +li2024gswpfrc: No reference paper yet shr_kind_r8, CL => shr_kind_CL + use shr_const_mod , only : SHR_CONST_PI,SHR_CONST_TKFRZ + use shr_infnan_mod , only : shr_infnan_isnan + use clm_varctl , only : iulog + use clm_varpar , only : nlevdecomp, ndecomp_pools, nlevdecomp_full, nlevgrnd + use clm_varcon , only : dzsoi_decomp + use pftconMod , only : noveg, pftcon + use abortutils , only : endrun + use decompMod , only : bounds_type + use subgridAveMod , only : p2c + use atm2lndType , only : atm2lnd_type + use CNDVType , only : dgvs_type + use CNVegStateType , only : cnveg_state_type + use CNVegCarbonStateType , only : cnveg_carbonstate_type, spinup_factor_deadwood + use CNVegCarbonFluxType , only : cnveg_carbonflux_type + use CNVegNitrogenStateType , only : cnveg_nitrogenstate_type + use CNVegNitrogenFluxType , only : cnveg_nitrogenflux_type + use SoilBiogeochemDecompCascadeConType , only : decomp_cascade_con + use EnergyFluxType , only : energyflux_type + use SaturatedExcessRunoffMod , only : saturated_excess_runoff_type + use WaterDiagnosticBulkType , only : waterdiagnosticbulk_type + use Wateratm2lndBulkType , only : wateratm2lndbulk_type + use WaterStateBulkType , only : waterstatebulk_type + use SoilStateType , only : soilstate_type + use SoilWaterRetentionCurveMod , only : soil_water_retention_curve_type + use GridcellType , only : grc + use ColumnType , only : col + use PatchType , only : patch + use SoilBiogeochemStateType , only : get_spinup_latitude_term + use FireMethodType , only : fire_method_type + use CNFireBaseMod , only : cnfire_base_type, cnfire_const, cnfire_params + ! + implicit none + private + ! + ! !PUBLIC TYPES: + public :: cnfire_li2024_type + ! + type, extends(cnfire_base_type) :: cnfire_li2024_type + private + contains + ! + ! !PUBLIC MEMBER FUNCTIONS: + procedure, public :: need_lightning_and_popdens + procedure, public :: CNFireArea ! Calculate fire area + end type cnfire_li2024_type + + ! + ! !PRIVATE MEMBER DATA: + !----------------------------------------------------------------------- + + character(len=*), parameter, private :: sourcefile = & + __FILE__ + +contains + + !----------------------------------------------------------------------- + function need_lightning_and_popdens(this) + ! !ARGUMENTS: + class(cnfire_li2024_type), intent(in) :: this + logical :: need_lightning_and_popdens ! function result + ! + ! !LOCAL VARIABLES: + + character(len=*), parameter :: subname = 'need_lightning_and_popdens' + !----------------------------------------------------------------------- + + need_lightning_and_popdens = .true. + end function need_lightning_and_popdens + + !----------------------------------------------------------------------- + subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_soilp, & + num_exposedvegp, filter_exposedvegp, num_noexposedvegp, filter_noexposedvegp, & + atm2lnd_inst, energyflux_inst, saturated_excess_runoff_inst, waterdiagnosticbulk_inst, & + wateratm2lndbulk_inst, waterstatebulk_inst, soilstate_inst, soil_water_retention_curve, & + cnveg_state_inst, cnveg_carbonstate_inst, totlitc_col, decomp_cpools_vr_col, t_soi17cm_col) + ! + ! !DESCRIPTION: + ! Computes column-level burned area + ! + ! !USES: + use clm_time_manager , only: get_step_size_real, get_curr_days_per_year, get_curr_date, get_nstep + use clm_varcon , only: secspday, secsphr + use clm_varctl , only: spinup_state + use pftconMod , only: nc4_grass, nc3crop, ndllf_evr_tmp_tree + use pftconMod , only: nbrdlf_evr_trp_tree, nbrdlf_dcd_trp_tree, nbrdlf_evr_shrub + use dynSubgridControlMod , only : run_has_transient_landcover + ! + ! !ARGUMENTS: + class(cnfire_li2024_type) :: this + type(bounds_type) , intent(in) :: bounds + integer , intent(in) :: num_soilc ! number of soil columns in filter + integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_soilp ! number of soil patches in filter + integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_exposedvegp ! number of points in filter_exposedvegp + integer , intent(in) :: filter_exposedvegp(:) ! patch filter for non-snow-covered veg + integer , intent(in) :: num_noexposedvegp ! number of points in filter_noexposedvegp + integer , intent(in) :: filter_noexposedvegp(:) ! patch filter where frac_veg_nosno is 0 + type(atm2lnd_type) , intent(in) :: atm2lnd_inst + type(energyflux_type) , intent(in) :: energyflux_inst + type(saturated_excess_runoff_type) , intent(in) :: saturated_excess_runoff_inst + type(waterdiagnosticbulk_type) , intent(in) :: waterdiagnosticbulk_inst + type(wateratm2lndbulk_type) , intent(in) :: wateratm2lndbulk_inst + type(waterstatebulk_type) , intent(in) :: waterstatebulk_inst + type(soilstate_type) , intent(in) :: soilstate_inst + class(soil_water_retention_curve_type), intent(in) :: soil_water_retention_curve + type(cnveg_state_type) , intent(inout) :: cnveg_state_inst + type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst + real(r8) , intent(in) :: totlitc_col(bounds%begc:) + real(r8) , intent(in) :: decomp_cpools_vr_col(bounds%begc:,1:,1:) + real(r8) , intent(in) :: t_soi17cm_col(bounds%begc:) + ! + ! !LOCAL VARIABLES: + ! + integer :: g,l,c,p,pi,j,fc,fp,kyr, kmo, kda, mcsec ! index variables + real(r8) :: dt ! time step variable (s) + real(r8) :: dayspyr ! days per year + real(r8) :: cli ! effect of climate on deforestation fires (0-1) + real(r8) :: cri ! thresholds used for cli, (mm/d), see Eq.(7) in Li et al.(2013) + real(r8) :: fb ! availability of fuel for regs A and C + real(r8) :: fhd ! impact of hd on agricultural fire + real(r8) :: fgdp ! impact of gdp on agricultural fire + real(r8) :: fire_m ! combustability of fuel for fire occurrence + real(r8) :: spread_m ! combustability of fuel for fire spread + real(r8) :: Lb_lf ! length-to-breadth ratio added by Lifang + integer :: i_cwd ! cwd pool + real(r8) :: lh ! anthro. ignitions (count/km2/hr) + real(r8) :: fs ! hd-dependent fires suppression (0-1) + real(r8) :: ig ! total ignitions (count/km2/hr) + real(r8) :: hdmlf ! human density + real(r8) :: arh, arh30 !combustability of fuel related to RH and RH30 + real(r8) :: afuel !weight for arh and arh30 + real(r8) :: btran_col(bounds%begc:bounds%endc) + logical :: transient_landcover ! whether this run has any prescribed transient landcover + real(r8), target :: prec60_col_target(bounds%begc:bounds%endc) + real(r8), target :: prec10_col_target(bounds%begc:bounds%endc) + real(r8), target :: rh30_col_target(bounds%begc:bounds%endc) + real(r8), pointer :: prec60_col(:) + real(r8), pointer :: prec10_col(:) + real(r8), pointer :: rh30_col(:) + !----------------------------------------------------------------------- + + SHR_ASSERT_ALL_FL((ubound(totlitc_col) == (/bounds%endc/)), sourcefile, __LINE__) + SHR_ASSERT_ALL_FL((ubound(decomp_cpools_vr_col) == (/bounds%endc,nlevdecomp_full,ndecomp_pools/)), sourcefile, __LINE__) + SHR_ASSERT_ALL_FL((ubound(t_soi17cm_col) == (/bounds%endc/)), sourcefile, __LINE__) + + associate( & + totlitc => totlitc_col , & ! Input: [real(r8) (:) ] (gC/m2) total lit C (column-level mean) + decomp_cpools_vr => decomp_cpools_vr_col , & ! Input: [real(r8) (:,:,:) ] (gC/m3) VR decomp. (litter, cwd, soil) + tsoi17 => t_soi17cm_col , & ! Input: [real(r8) (:) ] (K) soil T for top 0.17 m + + lfuel => cnfire_const%lfuel , & ! Input: [real(r8) ] (gC/m2) Lower threshold of fuel mass + ufuel => cnfire_const%ufuel , & ! Input: [real(r8) ] (gC/m2) Upper threshold of fuel mass + rh_hgh => cnfire_const%rh_hgh , & ! Input: [real(r8) ] (%) High relative humidity + rh_low => cnfire_const%rh_low , & ! Input: [real(r8) ] (%) Low relative humidity + cli_scale => cnfire_const%cli_scale , & ! Input: [real(r8) ] (/d) global constant for deforestation fires + cropfire_a1 => cnfire_const%cropfire_a1 , & ! Input: [real(r8) ] (/hr) a1 parameter for cropland fire + non_boreal_peatfire_c => cnfire_const%non_boreal_peatfire_c , & ! Input: [real(r8) ] (/hr) c parameter for non-boreal peatland fire + pot_hmn_ign_counts_alpha => cnfire_const%pot_hmn_ign_counts_alpha , & ! Input: [real(r8) ] (/person/month) Potential human ignition counts + boreal_peatfire_c => cnfire_const%boreal_peatfire_c , & ! Input: [real(r8) ] (/hr) c parameter for boreal peatland fire + + fsr_pft => pftcon%fsr_pft , & ! Input: + fd_pft => pftcon%fd_pft , & ! Input: + rswf_min => pftcon%rswf_min , & ! Input: + rswf_max => pftcon%rswf_max , & ! Input: + btran2 => this%cnfire_base_type%btran2_patch , & ! Input: [real(r8) (:) ] root zone soil wetness + fsat => saturated_excess_runoff_inst%fsat_col , & ! Input: [real(r8) (:) ] fractional area with water table at surface + wf2 => waterdiagnosticbulk_inst%wf2_col , & ! Input: [real(r8) (:) ] soil water as frac. of whc for top 0.17 m + + is_cwd => decomp_cascade_con%is_cwd , & ! Input: [logical (:) ] TRUE => pool is a cwd pool + spinup_factor => decomp_cascade_con%spinup_factor , & ! Input: [real(r8) (:) ] factor for AD spinup associated with each pool + + forc_rh => wateratm2lndbulk_inst%forc_rh_grc , & ! Input: [real(r8) (:) ] relative humidity + forc_wind => atm2lnd_inst%forc_wind_grc , & ! Input: [real(r8) (:) ] atmospheric wind speed (m/s) + forc_t => atm2lnd_inst%forc_t_downscaled_col , & ! Input: [real(r8) (:) ] downscaled atmospheric temperature (Kelvin) + forc_rain => wateratm2lndbulk_inst%forc_rain_downscaled_col , & ! Input: [real(r8) (:) ] downscaled rain + forc_snow => wateratm2lndbulk_inst%forc_snow_downscaled_col , & ! Input: [real(r8) (:) ] downscaled snow + prec60 => wateratm2lndbulk_inst%prec60_patch , & ! Input: [real(r8) (:) ] 60-day running mean of tot. precipitation + prec10 => wateratm2lndbulk_inst%prec10_patch , & ! Input: [real(r8) (:) ] 10-day running mean of tot. precipitation + rh30 => wateratm2lndbulk_inst%rh30_patch , & ! Input: [real(r8) (:) ] 10-day running mean of tot. precipitation + dwt_smoothed => cnveg_state_inst%dwt_smoothed_patch , & ! Input: [real(r8) (:) ] change in patch weight (-1 to 1) on the gridcell, smoothed over the year + cropf_col => cnveg_state_inst%cropf_col , & ! Input: [real(r8) (:) ] cropland fraction in veg column + gdp_lf => this%gdp_lf_col , & ! Input: [real(r8) (:) ] gdp data + peatf_lf => this%peatf_lf_col , & ! Input: [real(r8) (:) ] peatland fraction data + abm_lf => this%abm_lf_col , & ! Input: [integer (:) ] prescribed crop fire time + baf_crop => cnveg_state_inst%baf_crop_col , & ! Output: [real(r8) (:) ] burned area fraction for cropland (/sec) + baf_peatf => cnveg_state_inst%baf_peatf_col , & ! Output: [real(r8) (:) ] burned area fraction for peatland (/sec) + burndate => cnveg_state_inst%burndate_patch , & ! Output: [integer (:) ] burn date for crop + fbac => cnveg_state_inst%fbac_col , & ! Output: [real(r8) (:) ] total burned area out of conversion (/sec) + fbac1 => cnveg_state_inst%fbac1_col , & ! Output: [real(r8) (:) ] burned area out of conversion region due to land use fire + farea_burned => cnveg_state_inst%farea_burned_col , & ! Output: [real(r8) (:) ] total fractional area burned (/sec) + nfire => cnveg_state_inst%nfire_col , & ! Output: [real(r8) (:) ] fire counts (count/km2/sec), valid only in Reg. C + fsr_col => cnveg_state_inst%fsr_col , & ! Output: [real(r8) (:) ] fire spread rate at column level + fd_col => cnveg_state_inst%fd_col , & ! Output: [real(r8) (:) ] fire duration rate at column level + lgdp_col => cnveg_state_inst%lgdp_col , & ! Output: [real(r8) (:) ] gdp limitation factor for nfire + lgdp1_col => cnveg_state_inst%lgdp1_col , & ! Output: [real(r8) (:) ] gdp limitation factor for baf per fire + lpop_col => cnveg_state_inst%lpop_col , & ! Output: [real(r8) (:) ] pop limitation factor for baf per fire + lfwt => cnveg_state_inst%lfwt_col , & ! Output: [real(r8) (:) ] fractional coverage of non-crop and non-bare-soil Patches + trotr1_col => cnveg_state_inst%trotr1_col , & ! Output: [real(r8) (:) ] patch weight of BET on the column (0-1) + trotr2_col => cnveg_state_inst%trotr2_col , & ! Output: [real(r8) (:) ] patch weight of BDT on the column (0-1) + dtrotr_col => cnveg_state_inst%dtrotr_col , & ! Output: [real(r8) (:) ] decreased frac. coverage of BET+BDT on grid for dt + lfc => cnveg_state_inst%lfc_col , & ! Output: [real(r8) (:) ] conversion area frac. of BET+BDT that haven't burned before + wtlf => cnveg_state_inst%wtlf_col , & ! Output: [real(r8) (:) ] fractional coverage of non-crop Patches + + totvegc => cnveg_carbonstate_inst%totvegc_col , & ! Input: [real(r8) (:) ] totvegc at column level + deadcrootc => cnveg_carbonstate_inst%deadcrootc_patch , & ! Input: [real(r8) (:) ] (gC/m2) dead coarse root C + deadcrootc_storage => cnveg_carbonstate_inst%deadcrootc_storage_patch , & ! Input: [real(r8) (:) ] (gC/m2) dead coarse root C storage + deadcrootc_xfer => cnveg_carbonstate_inst%deadcrootc_xfer_patch , & ! Input: [real(r8) (:) ] (gC/m2) dead coarse root C transfer + deadstemc => cnveg_carbonstate_inst%deadstemc_patch , & ! Input: [real(r8) (:) ] (gC/m2) dead stem root C + frootc => cnveg_carbonstate_inst%frootc_patch , & ! Input: [real(r8) (:) ] (gC/m2) fine root C + frootc_storage => cnveg_carbonstate_inst%frootc_storage_patch , & ! Input: [real(r8) (:) ] (gC/m2) fine root C storage + frootc_xfer => cnveg_carbonstate_inst%frootc_xfer_patch , & ! Input: [real(r8) (:) ] (gC/m2) fine root C transfer + livecrootc => cnveg_carbonstate_inst%livecrootc_patch , & ! Input: [real(r8) (:) ] (gC/m2) live coarse root C + livecrootc_storage => cnveg_carbonstate_inst%livecrootc_storage_patch , & ! Input: [real(r8) (:) ] (gC/m2) live coarse root C storage + livecrootc_xfer => cnveg_carbonstate_inst%livecrootc_xfer_patch , & ! Input: [real(r8) (:) ] (gC/m2) live coarse root C transfer + leafc => cnveg_carbonstate_inst%leafc_patch , & ! Input: [real(r8) (:) ] (gC/m2) leaf C + leafc_storage => cnveg_carbonstate_inst%leafc_storage_patch , & ! Input: [real(r8) (:) ] (gC/m2) leaf C storage + leafc_xfer => cnveg_carbonstate_inst%leafc_xfer_patch , & ! Input: [real(r8) (:) ] (gC/m2) leaf C transfer + rootc_col => cnveg_carbonstate_inst%rootc_col , & ! Output: [real(r8) (:) ] root carbon + leafc_col => cnveg_carbonstate_inst%leafc_col , & ! Output: [real(r8) (:) ] leaf carbon at column level + deadstemc_col => cnveg_carbonstate_inst%deadstemc_col , & ! Output: [real(r8) (:) ] deadstem carbon at column level + fuelc => cnveg_carbonstate_inst%fuelc_col , & ! Output: [real(r8) (:) ] fuel load coutside cropland + fuelc_crop => cnveg_carbonstate_inst%fuelc_crop_col & ! Output: [real(r8) (:) ] fuel load for cropland + ) + + transient_landcover = run_has_transient_landcover() + + !pft to column average + prec10_col =>prec10_col_target + call p2c(bounds, num_soilc, filter_soilc, & + prec10(bounds%begp:bounds%endp), & + prec10_col(bounds%begc:bounds%endc)) + + prec60_col =>prec60_col_target + call p2c(bounds, num_soilc, filter_soilc, & + prec60(bounds%begp:bounds%endp), & + prec60_col(bounds%begc:bounds%endc)) + + rh30_col =>rh30_col_target + call p2c(bounds, num_soilc, filter_soilc, & + rh30(bounds%begp:bounds%endp), & + rh30_col(bounds%begc:bounds%endc)) + + call p2c(bounds, num_soilc, filter_soilc, & + leafc(bounds%begp:bounds%endp), & + leafc_col(bounds%begc:bounds%endc)) + + call p2c(bounds, num_soilc, filter_soilc, & + deadstemc(bounds%begp:bounds%endp), & + deadstemc_col(bounds%begc:bounds%endc)) + + call get_curr_date (kyr, kmo, kda, mcsec) + dayspyr = get_curr_days_per_year() + ! Get model step size + dt = get_step_size_real() + ! + ! On first time-step, just set area burned to zero and exit + ! + if ( get_nstep() == 0 )then + do fc = 1,num_soilc + c = filter_soilc(fc) + farea_burned(c) = 0._r8 + baf_crop(c) = 0._r8 + baf_peatf(c) = 0._r8 + fbac(c) = 0._r8 + fbac1(c) = 0._r8 + cropf_col(c) = 0._r8 + end do + return + end if + ! + ! Calculate fraction of crop (cropf_col) and non-crop and non-bare-soil + ! vegetation (lfwt) in vegetated column + ! + do fc = 1,num_soilc + c = filter_soilc(fc) + cropf_col(c) = 0._r8 + lfwt(c) = 0._r8 + end do + do fp = 1, num_soilp + p = filter_soilp(fp) + c = patch%column(p) + ! For crop veg types + if( patch%itype(p) > nc4_grass )then + cropf_col(c) = cropf_col(c) + patch%wtcol(p) + end if + ! For natural vegetation (non-crop and non-bare-soil) + if( patch%itype(p) >= ndllf_evr_tmp_tree .and. patch%itype(p) <= nc4_grass )then + lfwt(c) = lfwt(c) + patch%wtcol(p) + end if + end do + ! + ! Calculate crop fuel + ! + do fc = 1,num_soilc + c = filter_soilc(fc) + fuelc_crop(c)=0._r8 + end do + do fp = 1, num_soilp + p = filter_soilp(fp) + c = patch%column(p) + ! For crop PFTs, fuel load includes leaf and litter; only + ! column-level litter carbon + ! is available, so we use leaf carbon to estimate the + ! litter carbon for crop PFTs + if( patch%itype(p) > nc4_grass .and. patch%wtcol(p) > 0._r8 .and. leafc_col(c) > 0._r8 )then + fuelc_crop(c)=fuelc_crop(c) + (leafc(p) + leafc_storage(p) + & + leafc_xfer(p))*patch%wtcol(p)/cropf_col(c) + & + totlitc(c)*leafc(p)/leafc_col(c)*patch%wtcol(p)/cropf_col(c) + end if + end do + ! + ! Calculate noncrop column variables + ! + do fc = 1,num_soilc + c = filter_soilc(fc) + fsr_col(c) = 0._r8 + fd_col(c) = 0._r8 + rootc_col(c) = 0._r8 + lgdp_col(c) = 0._r8 + lgdp1_col(c) = 0._r8 + lpop_col(c) = 0._r8 + btran_col(c) = 0._r8 + wtlf(c) = 0._r8 + trotr1_col(c)= 0._r8 + trotr2_col(c)= 0._r8 + if (transient_landcover) then + dtrotr_col(c)=0._r8 + end if + end do + + ! This subroutine calculates btran2 + call this%CNFire_calc_fire_root_wetness_Li2021(bounds, & + num_exposedvegp, filter_exposedvegp, num_noexposedvegp, filter_noexposedvegp, & + waterstatebulk_inst, soilstate_inst, soil_water_retention_curve) + do fp = 1, num_exposedvegp + p = filter_exposedvegp(fp) + c = patch%column(p) + ! For non-crop -- natural vegetation and bare-soil + if( patch%itype(p) < nc3crop .and. cropf_col(c) < 1.0_r8 )then + btran_col(c) = btran_col(c)+max(0._r8, min(1._r8, & + (btran2(p)-rswf_min(patch%itype(p)))/(rswf_max(patch%itype(p)) & + -rswf_min(patch%itype(p)))))*patch%wtcol(p) + wtlf(c) = wtlf(c)+patch%wtcol(p) + end if + end do + + do fp = 1, num_soilp + p = filter_soilp(fp) + c = patch%column(p) + g = col%gridcell(c) + + ! For non-crop -- natural vegetation and bare-soil + if( patch%itype(p) < nc3crop .and. cropf_col(c) < 1.0_r8 )then + + ! NOTE(wjs, 2016-12-15) These calculations of the fraction of evergreen + ! and deciduous tropical trees (used to determine if a column is + ! tropical closed forest) use the current fractions. However, I think + ! they are used in code that applies to land cover change. Note that + ! land cover change is currently generated on the first time step of the + ! year (even though the fire code sees the annually-smoothed dwt). Thus, + ! I think that, for this to be totally consistent, this code should + ! consider the fractional coverage of each PFT prior to the relevant + ! land cover change event. (These fractions could be computed in the + ! code that handles land cover change, so that the fire code remains + ! agnostic to exactly how and when land cover change happens.) + ! + ! For example, if a year started with fractional coverages of + ! nbrdlf_evr_trp_tree = 0.35 and nbrdlf_dcd_trp_tree = 0.35, but then + ! the start-of-year land cover change reduced both of these to 0.2: The + ! current code would consider the column to NOT be tropical closed + ! forest (because nbrdlf_evr_trp_tree+nbrdlf_dcd_trp_tree < 0.6), + ! whereas in fact the land cover change occurred when the column *was* + ! tropical closed forest. + if( patch%itype(p) == nbrdlf_evr_trp_tree .and. patch%wtcol(p) > 0._r8 )then + trotr1_col(c)=trotr1_col(c)+patch%wtcol(p) + end if + if( patch%itype(p) == nbrdlf_dcd_trp_tree .and. patch%wtcol(p) > 0._r8 )then + trotr2_col(c)=trotr2_col(c)+patch%wtcol(p) + end if + + if (transient_landcover) then + if( patch%itype(p) == nbrdlf_evr_trp_tree .or. patch%itype(p) == nbrdlf_dcd_trp_tree )then + if(dwt_smoothed(p) < 0._r8)then + ! Land cover change in CLM happens all at once on the first time + ! step of the year. However, the fire code needs deforestation + ! rates throughout the year, in order to combine these + ! deforestation rates with the current season's climate. So we + ! use a smoothed version of dwt. + ! + ! This isn't ideal, because the carbon stocks that the fire code + ! is operating on will have decreased by the full annual amount + ! before the fire code does anything. But the biggest effect of + ! these deforestation fires is as a trigger for other fires, and + ! the C fluxes are merely diagnostic so don't need to be + ! conservative, so this isn't a big issue. + ! + ! (Actually, it would be even better if the fire code had a + ! realistic breakdown of annual deforestation into the + ! different seasons. But having deforestation spread evenly + ! throughout the year is much better than having it all + ! concentrated on January 1.) + dtrotr_col(c)=dtrotr_col(c)-dwt_smoothed(p) + end if + end if + end if + rootc_col(c) = rootc_col(c) + (frootc(p) + frootc_storage(p) + & + frootc_xfer(p) + deadcrootc(p) * spinup_factor_deadwood + & + deadcrootc_storage(p) + deadcrootc_xfer(p) + & + livecrootc(p)+livecrootc_storage(p) + & + livecrootc_xfer(p))*patch%wtcol(p) + + fsr_col(c) = fsr_col(c) + fsr_pft(patch%itype(p))*patch%wtcol(p)/(1.0_r8-cropf_col(c)) + + hdmlf=this%forc_hdm(g) + + ! all these constants are in Li et al. BG (2012a,b;2013) + + if( hdmlf > 0.1_r8 )then + ! For NOT bare-soil + if( patch%itype(p) /= noveg )then + ! For shrub and grass (crop already excluded above) + if( patch%itype(p) >= nbrdlf_evr_shrub )then !for shurb and grass + lgdp_col(c) = lgdp_col(c) + (0.1_r8 + 0.9_r8* & + exp(-1._r8*SHR_CONST_PI* & + (gdp_lf(c)/8._r8)**0.5_r8))*patch%wtcol(p) & + /(1.0_r8 - cropf_col(c)) + lgdp1_col(c) = lgdp1_col(c) + (0.2_r8 + 0.8_r8* & + exp(-1._r8*SHR_CONST_PI* & + (gdp_lf(c)/7._r8)))*patch%wtcol(p)/(1._r8 - cropf_col(c)) + lpop_col(c) = lpop_col(c) + (0.2_r8 + 0.8_r8* & + exp(-1._r8*SHR_CONST_PI* & + (hdmlf/450._r8)**0.5_r8))*patch%wtcol(p)/(1._r8 - cropf_col(c)) + else ! for trees + if( gdp_lf(c) > 20._r8 )then + lgdp_col(c) =lgdp_col(c)+cnfire_const%occur_hi_gdp_tree*patch%wtcol(p)/(1._r8 - cropf_col(c)) + lgdp1_col(c) =lgdp1_col(c)+0.62_r8*patch%wtcol(p)/(1._r8 - cropf_col(c)) + else + if( gdp_lf(c) > 8._r8 )then + lgdp_col(c)=lgdp_col(c)+0.79_r8*patch%wtcol(p)/(1._r8 - cropf_col(c)) + lgdp1_col(c)=lgdp1_col(c)+0.83_r8*patch%wtcol(p)/(1._r8 - cropf_col(c)) + else + lgdp_col(c) = lgdp_col(c)+patch%wtcol(p)/(1._r8 - cropf_col(c)) + lgdp1_col(c)=lgdp1_col(c)+patch%wtcol(p)/(1._r8 - cropf_col(c)) + end if + end if + lpop_col(c) = lpop_col(c) + (0.4_r8 + 0.6_r8* & + exp(-1._r8*SHR_CONST_PI* & + (hdmlf/125._r8)))*patch%wtcol(p)/(1._r8 -cropf_col(c)) + end if + end if + else + lgdp_col(c) = lgdp_col(c)+patch%wtcol(p)/(1.0_r8 - cropf_col(c)) + lgdp1_col(c) = lgdp1_col(c)+patch%wtcol(p)/(1.0_r8 -cropf_col(c)) + lpop_col(c) = lpop_col(c)+patch%wtcol(p)/(1.0_r8 -cropf_col(c)) + end if + + fd_col(c) = fd_col(c) + fd_pft(patch%itype(p)) * patch%wtcol(p) * secsphr / (1.0_r8-cropf_col(c)) + end if + end do + + ! estimate annual decreased fractional coverage of BET+BDT + ! land cover conversion in CLM4.5 is the same for each timestep except for the beginning + + if (transient_landcover) then + do fc = 1,num_soilc + c = filter_soilc(fc) + if( dtrotr_col(c) > 0._r8 )then + if( kmo == 1 .and. kda == 1 .and. mcsec == 0)then + lfc(c) = 0._r8 + end if + if( kmo == 1 .and. kda == 1 .and. mcsec == dt)then + lfc(c) = dtrotr_col(c)*dayspyr*secspday/dt + end if + else + lfc(c)=0._r8 + end if + end do + end if + ! + ! calculate burned area fraction in cropland + ! + do fc = 1,num_soilc + c = filter_soilc(fc) + baf_crop(c)=0._r8 + end do + + do fp = 1,num_soilp + p = filter_soilp(fp) + if( kmo == 1 .and. kda == 1 .and. mcsec == 0 )then + burndate(p) = 10000 ! init. value; actual range [0 365] + end if + end do + + do fp = 1, num_soilp + p = filter_soilp(fp) + c = patch%column(p) + g = col%gridcell(c) + + ! For crop + if( forc_t(c) >= SHR_CONST_TKFRZ .and. patch%itype(p) > nc4_grass .and. & + kmo == abm_lf(c) .and. & + burndate(p) >= 999 .and. patch%wtcol(p) > 0._r8 )then ! catch crop burn time + + hdmlf = this%forc_hdm(g) + + ! calculate human density impact on ag. fire + fhd = 0.04_r8+0.96_r8*exp(-1._r8*SHR_CONST_PI*(hdmlf/350._r8)**0.5_r8) + + ! calculate impact of GDP on ag. fire + fgdp = 0.01_r8+0.99_r8*exp(-1._r8*SHR_CONST_PI*(gdp_lf(c)/10._r8)) + + ! calculate burned area + fb = max(0.0_r8,min(1.0_r8,(fuelc_crop(c)-lfuel)/(ufuel-lfuel))) + + ! crop fire only for generic crop types at this time + ! managed crops are treated as grasses if crop model is turned on + baf_crop(c) = baf_crop(c) + cropfire_a1/secsphr*fhd*fgdp*patch%wtcol(p) + if( fb*fhd*fgdp*patch%wtcol(p) > 0._r8)then + burndate(p)=kda + end if + end if + end do + ! + ! calculate peatland fire + ! + do fc = 1, num_soilc + c = filter_soilc(fc) + g= col%gridcell(c) + if(grc%latdeg(g) < cnfire_const%borealat )then + baf_peatf(c) = non_boreal_peatfire_c/secsphr*max(0._r8, & + min(1._r8,(4.0_r8-prec60_col(c)*secspday)/ & + 4.0_r8))**2*peatf_lf(c)*(1._r8-fsat(c)) + else + baf_peatf(c) = boreal_peatfire_c/secsphr*exp(-SHR_CONST_PI*(max(wf2(c),0._r8)/0.3_r8))* & + max(0._r8,min(1._r8,(tsoi17(c)-SHR_CONST_TKFRZ)/10._r8))*peatf_lf(c)* & + (1._r8-fsat(c)) + end if + end do + ! + ! calculate other fires + ! + + ! Set the number of timesteps for e-folding. + ! When the simulation has run fewer than this number of steps, + ! re-scale the e-folding time to get a stable early estimate. + + ! find which pool is the cwd pool + i_cwd = 0 + do l = 1, ndecomp_pools + if ( is_cwd(l) ) then + i_cwd = l + endif + end do + + ! + ! begin column loop to calculate fractional area affected by fire + ! + do fc = 1, num_soilc + c = filter_soilc(fc) + g = col%gridcell(c) + hdmlf=this%forc_hdm(g) + nfire(c) = 0._r8 + if( cropf_col(c) < 1._r8 )then + fuelc(c) = totlitc(c)+totvegc(c)-rootc_col(c)-fuelc_crop(c)*cropf_col(c) + if (spinup_state == 2) then + fuelc(c) = fuelc(c) + ((spinup_factor_deadwood - 1._r8)*deadstemc_col(c)) + do j = 1, nlevdecomp + fuelc(c) = fuelc(c)+decomp_cpools_vr(c,j,i_cwd) * dzsoi_decomp(j) * spinup_factor(i_cwd) & + * get_spinup_latitude_term(grc%latdeg(col%gridcell(c))) + end do + else + do j = 1, nlevdecomp + fuelc(c) = fuelc(c)+decomp_cpools_vr(c,j,i_cwd) * dzsoi_decomp(j) + end do + end if + fuelc(c) = fuelc(c)/(1._r8-cropf_col(c)) + fb = max(0.0_r8,min(1.0_r8,(fuelc(c)-lfuel)/(ufuel-lfuel))) + if (trotr1_col(c)+trotr2_col(c)<=0.6_r8) then + afuel =min(1._r8,max(0._r8,(fuelc(c)-2500._r8)/(5000._r8-2500._r8))) + arh=1._r8-max(0._r8, min(1._r8,(forc_rh(g)-rh_low)/(rh_hgh-rh_low))) + arh30=1._r8-max(cnfire_params%prh30, min(1._r8,rh30_col(c)/90._r8)) + if (forc_rh(g) < rh_hgh.and. wtlf(c) > 0._r8 .and. tsoi17(c)> SHR_CONST_TKFRZ)then + fire_m = ((afuel*arh30+(1._r8-afuel)*arh)**1.5_r8) & + *((1._r8-btran_col(c)/wtlf(c))**0.5_r8) + else + fire_m = 0._r8 + end if + lh = pot_hmn_ign_counts_alpha*6.8_r8*hdmlf**(0.43_r8)/30._r8/24._r8 + fs = 1._r8-(0.01_r8+0.98_r8*exp(-0.025_r8*hdmlf)) + ig = (lh+this%forc_lnfm(g)/(5.16_r8+2.16_r8* & + cos(SHR_CONST_PI/180._r8*3*min(60._r8,abs(grc%latdeg(g)))))* & + cnfire_params%ignition_efficiency)*(1._r8-fs)*(1._r8-cropf_col(c)) + nfire(c) = ig/secsphr*fb*fire_m*lgdp_col(c) !fire counts/km2/sec + Lb_lf = 1._r8+10._r8*(1._r8-EXP(-0.06_r8*forc_wind(g))) + spread_m = fire_m**0.5_r8 + farea_burned(c) = min(1._r8,(cnfire_const%g0*spread_m*fsr_col(c)* & + fd_col(c)/1000._r8)**2*lgdp1_col(c)* & + lpop_col(c)*nfire(c)*SHR_CONST_PI*Lb_lf+ & + baf_crop(c)+baf_peatf(c)) ! fraction (0-1) per sec + else + farea_burned(c)=min(1._r8,baf_crop(c)+baf_peatf(c)) + end if + ! + ! if landuse change data is used, calculate deforestation fires and + ! add it in the total of burned area fraction + ! + if (transient_landcover) then + if( trotr1_col(c)+trotr2_col(c) > 0.6_r8 )then + if(( kmo == 1 .and. kda == 1 .and. mcsec == 0) .or. & + dtrotr_col(c) <=0._r8 )then + fbac1(c) = 0._r8 + farea_burned(c) = baf_crop(c)+baf_peatf(c) + else + cri = (4.0_r8*trotr1_col(c)+1.8_r8*trotr2_col(c))/(trotr1_col(c)+trotr2_col(c)) + cli = (max(0._r8,min(1._r8,(cri-prec60_col(c)*secspday)/cri))**0.5)* & + (max(0._r8,min(1._r8,(cri-prec10_col(c)*secspday)/cri))**0.5)* & + (15._r8*min(0.0016_r8,dtrotr_col(c)/dt*dayspyr*secspday)+0.009_r8)* & + max(0._r8,min(1._r8,(0.25_r8-(forc_rain(c)+forc_snow(c))*secsphr)/0.25_r8)) + farea_burned(c) = fb*cli*(cli_scale/secspday)+baf_crop(c)+baf_peatf(c) + ! burned area out of conversion region due to land use fire + fbac1(c) = max(0._r8,fb*cli*(cli_scale/secspday) - 2.0_r8*lfc(c)/dt) + end if + ! total burned area out of conversion + fbac(c) = fbac1(c)+baf_crop(c)+baf_peatf(c) + else + fbac(c) = farea_burned(c) + end if + end if + + else + farea_burned(c) = min(1._r8,baf_crop(c)+baf_peatf(c)) + end if + + end do ! end of column loop + + end associate + + end subroutine CNFireArea + +end module CNFireLi2024Mod From 5b95a3e6a658e20e80b3869df823c81636067304 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 7 Aug 2024 13:06:36 -0600 Subject: [PATCH 364/406] Fix bugs and develop crop fire modeling In the CNFireLi2024Mod.F90, (1) fixed the bug described in https://github.com/ESCOMP/CTSM/issues/2566 to avoid incorrect accumulation of baf_crop and ensure that crop fires do not occur during the crop growing season; (2) confined crop fires to periods after harvest and before planting when crop module is active; (3) removed the dependency of baf_crop on fuel availability; (4) improved the modeling of the influence of socio-economic factors on crop burned area; (5) recalibrated the cropfire_a1 constant based on GFED5 crop burned area; (6) modify the declaration of CNFireArea in these F90 files to include the variable crop_inst, declare the variable crop_inst, and import and utilize crop_type from the module CropType. In addition, the modules CNDriverMod.F90, CNFireLi2014Mod.F90, CNFireLi2016Mod.F90, CNFireLi2021Mod.F90, CNFireNoFireMod.F90, FATESFireBase.F90, and FireMethodType.F90 include the subroutine CNFireArea. (6) is implemented in these modules. (Original commit by Fang Li, lifang@mail.iap.ac.cn.) --- src/biogeochem/CNDriverMod.F90 | 2 +- src/biogeochem/CNFireLi2014Mod.F90 | 4 +++- src/biogeochem/CNFireLi2016Mod.F90 | 4 +++- src/biogeochem/CNFireLi2021Mod.F90 | 4 +++- src/biogeochem/CNFireLi2024Mod.F90 | 26 +++++++++++++------------- src/biogeochem/CNFireNoFireMod.F90 | 4 +++- src/biogeochem/FATESFireBase.F90 | 5 ++++- src/main/FireMethodType.F90 | 4 +++- 8 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/biogeochem/CNDriverMod.F90 b/src/biogeochem/CNDriverMod.F90 index b23019eb23..ee97bcc360 100644 --- a/src/biogeochem/CNDriverMod.F90 +++ b/src/biogeochem/CNDriverMod.F90 @@ -936,7 +936,7 @@ subroutine CNDriverNoLeaching(bounds, num_exposedvegp, filter_exposedvegp, num_noexposedvegp, filter_noexposedvegp, & atm2lnd_inst, energyflux_inst, saturated_excess_runoff_inst, waterdiagnosticbulk_inst, wateratm2lndbulk_inst, & waterstatebulk_inst, soilstate_inst, soil_water_retention_curve, & - cnveg_state_inst, cnveg_carbonstate_inst, & + crop_inst, cnveg_state_inst, cnveg_carbonstate_inst, & totlitc_col=soilbiogeochem_carbonstate_inst%totlitc_col(begc:endc), & decomp_cpools_vr_col=soilbiogeochem_carbonstate_inst%decomp_cpools_vr_col(begc:endc,1:nlevdecomp_full,1:ndecomp_pools), & t_soi17cm_col=temperature_inst%t_soi17cm_col(begc:endc)) diff --git a/src/biogeochem/CNFireLi2014Mod.F90 b/src/biogeochem/CNFireLi2014Mod.F90 index 6ea21a530b..cb41230a10 100644 --- a/src/biogeochem/CNFireLi2014Mod.F90 +++ b/src/biogeochem/CNFireLi2014Mod.F90 @@ -87,7 +87,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ num_exposedvegp, filter_exposedvegp, num_noexposedvegp, filter_noexposedvegp, & atm2lnd_inst, energyflux_inst, saturated_excess_runoff_inst, waterdiagnosticbulk_inst, & wateratm2lndbulk_inst, waterstatebulk_inst, soilstate_inst, soil_water_retention_curve, & - cnveg_state_inst, cnveg_carbonstate_inst, totlitc_col, decomp_cpools_vr_col, t_soi17cm_col) + crop_inst, cnveg_state_inst, cnveg_carbonstate_inst, totlitc_col, decomp_cpools_vr_col, t_soi17cm_col) ! ! !DESCRIPTION: ! Computes column-level burned area @@ -98,6 +98,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ use pftconMod , only: nc4_grass, nc3crop, ndllf_evr_tmp_tree use pftconMod , only: nbrdlf_evr_trp_tree, nbrdlf_dcd_trp_tree, nbrdlf_evr_shrub use dynSubgridControlMod , only: run_has_transient_landcover + use CropType , only: crop_type ! ! !ARGUMENTS: class(cnfire_li2014_type) :: this @@ -120,6 +121,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ class(soil_water_retention_curve_type), intent(in) :: soil_water_retention_curve type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst + type(crop_type) , intent(in) :: crop_inst real(r8) , intent(in) :: totlitc_col(bounds%begc:) real(r8) , intent(in) :: decomp_cpools_vr_col(bounds%begc:,1:,1:) real(r8) , intent(in) :: t_soi17cm_col(bounds%begc:) diff --git a/src/biogeochem/CNFireLi2016Mod.F90 b/src/biogeochem/CNFireLi2016Mod.F90 index 7bbb183860..6021901a28 100644 --- a/src/biogeochem/CNFireLi2016Mod.F90 +++ b/src/biogeochem/CNFireLi2016Mod.F90 @@ -89,7 +89,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ num_exposedvegp, filter_exposedvegp, num_noexposedvegp, filter_noexposedvegp, & atm2lnd_inst, energyflux_inst, saturated_excess_runoff_inst, waterdiagnosticbulk_inst, & wateratm2lndbulk_inst, waterstatebulk_inst, soilstate_inst, soil_water_retention_curve, & - cnveg_state_inst, cnveg_carbonstate_inst, totlitc_col, decomp_cpools_vr_col, t_soi17cm_col) + crop_inst, cnveg_state_inst, cnveg_carbonstate_inst, totlitc_col, decomp_cpools_vr_col, t_soi17cm_col) ! ! !DESCRIPTION: ! Computes column-level burned area @@ -101,6 +101,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ use pftconMod , only: nc4_grass, nc3crop, ndllf_evr_tmp_tree use pftconMod , only: nbrdlf_evr_trp_tree, nbrdlf_dcd_trp_tree, nbrdlf_evr_shrub use dynSubgridControlMod , only : run_has_transient_landcover + use CropType , only: crop_type ! ! !ARGUMENTS: class(cnfire_li2016_type) :: this @@ -123,6 +124,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ class(soil_water_retention_curve_type), intent(in) :: soil_water_retention_curve type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst + type(crop_type) , intent(in) :: crop_inst real(r8) , intent(in) :: totlitc_col(bounds%begc:) real(r8) , intent(in) :: decomp_cpools_vr_col(bounds%begc:,1:,1:) real(r8) , intent(in) :: t_soi17cm_col(bounds%begc:) diff --git a/src/biogeochem/CNFireLi2021Mod.F90 b/src/biogeochem/CNFireLi2021Mod.F90 index 86d5cc235d..74f553eb5c 100644 --- a/src/biogeochem/CNFireLi2021Mod.F90 +++ b/src/biogeochem/CNFireLi2021Mod.F90 @@ -89,7 +89,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ num_exposedvegp, filter_exposedvegp, num_noexposedvegp, filter_noexposedvegp, & atm2lnd_inst, energyflux_inst, saturated_excess_runoff_inst, waterdiagnosticbulk_inst, & wateratm2lndbulk_inst, waterstatebulk_inst, soilstate_inst, soil_water_retention_curve, & - cnveg_state_inst, cnveg_carbonstate_inst, totlitc_col, decomp_cpools_vr_col, t_soi17cm_col) + crop_inst, cnveg_state_inst, cnveg_carbonstate_inst, totlitc_col, decomp_cpools_vr_col, t_soi17cm_col) ! ! !DESCRIPTION: ! Computes column-level burned area @@ -101,6 +101,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ use pftconMod , only: nc4_grass, nc3crop, ndllf_evr_tmp_tree use pftconMod , only: nbrdlf_evr_trp_tree, nbrdlf_dcd_trp_tree, nbrdlf_evr_shrub use dynSubgridControlMod , only : run_has_transient_landcover + use CropType , only: crop_type ! ! !ARGUMENTS: class(cnfire_li2021_type) :: this @@ -123,6 +124,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ class(soil_water_retention_curve_type), intent(in) :: soil_water_retention_curve type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst + type(crop_type) , intent(in) :: crop_inst real(r8) , intent(in) :: totlitc_col(bounds%begc:) real(r8) , intent(in) :: decomp_cpools_vr_col(bounds%begc:,1:,1:) real(r8) , intent(in) :: t_soi17cm_col(bounds%begc:) diff --git a/src/biogeochem/CNFireLi2024Mod.F90 b/src/biogeochem/CNFireLi2024Mod.F90 index 9908732018..6d3bf6657a 100644 --- a/src/biogeochem/CNFireLi2024Mod.F90 +++ b/src/biogeochem/CNFireLi2024Mod.F90 @@ -89,7 +89,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ num_exposedvegp, filter_exposedvegp, num_noexposedvegp, filter_noexposedvegp, & atm2lnd_inst, energyflux_inst, saturated_excess_runoff_inst, waterdiagnosticbulk_inst, & wateratm2lndbulk_inst, waterstatebulk_inst, soilstate_inst, soil_water_retention_curve, & - cnveg_state_inst, cnveg_carbonstate_inst, totlitc_col, decomp_cpools_vr_col, t_soi17cm_col) + crop_inst, cnveg_state_inst, cnveg_carbonstate_inst, totlitc_col, decomp_cpools_vr_col, t_soi17cm_col) ! ! !DESCRIPTION: ! Computes column-level burned area @@ -97,10 +97,11 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ ! !USES: use clm_time_manager , only: get_step_size_real, get_curr_days_per_year, get_curr_date, get_nstep use clm_varcon , only: secspday, secsphr - use clm_varctl , only: spinup_state + use clm_varctl , only: spinup_state, use_crop use pftconMod , only: nc4_grass, nc3crop, ndllf_evr_tmp_tree use pftconMod , only: nbrdlf_evr_trp_tree, nbrdlf_dcd_trp_tree, nbrdlf_evr_shrub use dynSubgridControlMod , only : run_has_transient_landcover + use CropType , only: crop_type ! ! !ARGUMENTS: class(cnfire_li2024_type) :: this @@ -123,6 +124,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ class(soil_water_retention_curve_type), intent(in) :: soil_water_retention_curve type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst + type(crop_type) , intent(in) :: crop_inst real(r8) , intent(in) :: totlitc_col(bounds%begc:) real(r8) , intent(in) :: decomp_cpools_vr_col(bounds%begc:,1:,1:) real(r8) , intent(in) :: t_soi17cm_col(bounds%begc:) @@ -237,7 +239,8 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ leafc_col => cnveg_carbonstate_inst%leafc_col , & ! Output: [real(r8) (:) ] leaf carbon at column level deadstemc_col => cnveg_carbonstate_inst%deadstemc_col , & ! Output: [real(r8) (:) ] deadstem carbon at column level fuelc => cnveg_carbonstate_inst%fuelc_col , & ! Output: [real(r8) (:) ] fuel load coutside cropland - fuelc_crop => cnveg_carbonstate_inst%fuelc_crop_col & ! Output: [real(r8) (:) ] fuel load for cropland + fuelc_crop => cnveg_carbonstate_inst%fuelc_crop_col , & ! Output: [real(r8) (:) ] fuel load for cropland + croplive => crop_inst%croplive_patch & ! Input: [logical (:) ] flag, true if planted, not harvested ) transient_landcover = run_has_transient_landcover() @@ -522,21 +525,18 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ hdmlf = this%forc_hdm(g) ! calculate human density impact on ag. fire - fhd = 0.04_r8+0.96_r8*exp(-1._r8*SHR_CONST_PI*(hdmlf/350._r8)**0.5_r8) + fhd = 0.2_r8+0.8_r8*exp(-1._r8*SHR_CONST_PI*(hdmlf/400._r8)) ! calculate impact of GDP on ag. fire - fgdp = 0.01_r8+0.99_r8*exp(-1._r8*SHR_CONST_PI*(gdp_lf(c)/10._r8)) + fgdp = 0.05_r8+0.95_r8*exp(-1._r8*SHR_CONST_PI*(gdp_lf(c)/20._r8)) ! calculate burned area - fb = max(0.0_r8,min(1.0_r8,(fuelc_crop(c)-lfuel)/(ufuel-lfuel))) - - ! crop fire only for generic crop types at this time - ! managed crops are treated as grasses if crop model is turned on - baf_crop(c) = baf_crop(c) + cropfire_a1/secsphr*fhd*fgdp*patch%wtcol(p) - if( fb*fhd*fgdp*patch%wtcol(p) > 0._r8)then - burndate(p)=kda + if((use_crop .and. (.not. croplive(p))) & + .or. (.not. use_crop)) then + burndate(p) = kda + baf_crop(c) = baf_crop(c)+0.21_r8 / secsphr * fhd * fgdp * patch%wtcol(p) end if - end if + end if end do ! ! calculate peatland fire diff --git a/src/biogeochem/CNFireNoFireMod.F90 b/src/biogeochem/CNFireNoFireMod.F90 index 0dc1ee39d1..6785faa851 100644 --- a/src/biogeochem/CNFireNoFireMod.F90 +++ b/src/biogeochem/CNFireNoFireMod.F90 @@ -62,13 +62,14 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ atm2lnd_inst, energyflux_inst, saturated_excess_runoff_inst, & waterdiagnosticbulk_inst, wateratm2lndbulk_inst, & waterstatebulk_inst, soilstate_inst, soil_water_retention_curve, & - cnveg_state_inst, cnveg_carbonstate_inst, totlitc_col, decomp_cpools_vr_col, t_soi17cm_col) + crop_inst, cnveg_state_inst, cnveg_carbonstate_inst, totlitc_col, decomp_cpools_vr_col, t_soi17cm_col) ! ! !DESCRIPTION: ! Computes column-level burned area ! ! !USES: use subgridAveMod , only : p2c + use CropType , only : crop_type ! ! !ARGUMENTS: class(cnfire_nofire_type) :: this @@ -91,6 +92,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ class(soil_water_retention_curve_type), intent(in) :: soil_water_retention_curve type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst + type(crop_type) , intent(in) :: crop_inst real(r8) , intent(in) :: totlitc_col(bounds%begc:) real(r8) , intent(in) :: decomp_cpools_vr_col(bounds%begc:,1:,1:) real(r8) , intent(in) :: t_soi17cm_col(bounds%begc:) diff --git a/src/biogeochem/FATESFireBase.F90 b/src/biogeochem/FATESFireBase.F90 index a47ff2e8c4..59279d7625 100644 --- a/src/biogeochem/FATESFireBase.F90 +++ b/src/biogeochem/FATESFireBase.F90 @@ -206,7 +206,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ atm2lnd_inst, energyflux_inst, saturated_excess_runoff_inst, & waterdiagnosticbulk_inst, wateratm2lndbulk_inst, & waterstatebulk_inst, soilstate_inst, soil_water_retention_curve, & - cnveg_state_inst, cnveg_carbonstate_inst, totlitc_col, decomp_cpools_vr_col, t_soi17cm_col) + crop_inst, cnveg_state_inst, cnveg_carbonstate_inst, totlitc_col, decomp_cpools_vr_col, t_soi17cm_col) ! ! !DESCRIPTION: ! Computes column-level burned area (NOT USED FOR FATES) @@ -220,6 +220,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ use SoilStateType , only : soilstate_type use SoilWaterRetentionCurveMod , only : soil_water_retention_curve_type use atm2lndType , only : atm2lnd_type + use CropType , only: crop_type ! ! !ARGUMENTS: class(fates_fire_base_type) :: this @@ -242,6 +243,8 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ class(soil_water_retention_curve_type), intent(in) :: soil_water_retention_curve type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst + type(crop_type) , intent(in) :: crop_inst + real(r8) , intent(in) :: totlitc_col(bounds%begc:) real(r8) , intent(in) :: decomp_cpools_vr_col(bounds%begc:,1:,1:) real(r8) , intent(in) :: t_soi17cm_col(bounds%begc:) diff --git a/src/main/FireMethodType.F90 b/src/main/FireMethodType.F90 index 63c05821b7..978450e65f 100644 --- a/src/main/FireMethodType.F90 +++ b/src/main/FireMethodType.F90 @@ -119,7 +119,7 @@ subroutine CNFireArea_interface (this, bounds, num_soilc, filter_soilc, num_soil atm2lnd_inst, energyflux_inst, saturated_excess_runoff_inst, & waterdiagnosticbulk_inst, wateratm2lndbulk_inst, & waterstatebulk_inst, soilstate_inst, soil_water_retention_curve, & - cnveg_state_inst, cnveg_carbonstate_inst, totlitc_col, decomp_cpools_vr_col, t_soi17cm_col) + crop_inst, cnveg_state_inst, cnveg_carbonstate_inst, totlitc_col, decomp_cpools_vr_col, t_soi17cm_col) ! ! !DESCRIPTION: ! Computes column-level burned area @@ -137,6 +137,7 @@ subroutine CNFireArea_interface (this, bounds, num_soilc, filter_soilc, num_soil use SoilWaterRetentionCurveMod , only : soil_water_retention_curve_type use CNVegStateType , only : cnveg_state_type use CNVegCarbonStateType , only : cnveg_carbonstate_type + use CropType , only : crop_type import :: fire_method_type ! ! !ARGUMENTS: @@ -160,6 +161,7 @@ subroutine CNFireArea_interface (this, bounds, num_soilc, filter_soilc, num_soil class(soil_water_retention_curve_type), intent(in) :: soil_water_retention_curve type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst + type(crop_type) , intent(in) :: crop_inst real(r8) , intent(in) :: totlitc_col(bounds%begc:) real(r8) , intent(in) :: decomp_cpools_vr_col(bounds%begc:,1:,1:) real(r8) , intent(in) :: t_soi17cm_col(bounds%begc:) From 412bb3e1bb1df35fb2503acf4a86f0256fbaa77e Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 7 Aug 2024 13:16:09 -0600 Subject: [PATCH 365/406] change the cropfire_a1 to 0.21 in namelist_defaults. (Original commit by Fang Li, lifang@mail.iap.ac.cn.) --- bld/namelist_files/namelist_defaults_ctsm.xml | 2 +- src/biogeochem/CNFireLi2024Mod.F90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index a7e59935a4..b301bcfb28 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -327,7 +327,7 @@ attributes from the config_cache.xml file (with keys converted to upper-case). 0.09d-4 0.010d00 0.17d-3 -1.6d-4 +0.21d00 0.33d00 75.d00 1050.d00 diff --git a/src/biogeochem/CNFireLi2024Mod.F90 b/src/biogeochem/CNFireLi2024Mod.F90 index 6d3bf6657a..503ed28488 100644 --- a/src/biogeochem/CNFireLi2024Mod.F90 +++ b/src/biogeochem/CNFireLi2024Mod.F90 @@ -534,7 +534,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ if((use_crop .and. (.not. croplive(p))) & .or. (.not. use_crop)) then burndate(p) = kda - baf_crop(c) = baf_crop(c)+0.21_r8 / secsphr * fhd * fgdp * patch%wtcol(p) + baf_crop(c) = baf_crop(c)+cropfire_a1 / secsphr * fhd * fgdp * patch%wtcol(p) end if end if end do From be82840310150f694824ce7c2504c6049362497e Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 7 Aug 2024 15:17:47 -0600 Subject: [PATCH 366/406] cropMonthOutput h2 file is now finally monthly. Specifying hist_nhtfrq(3) seems to override later specification of h2 in "hist_nhtfrq = 0,-240,0" --- cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm index aea8e39e6c..8ad588381e 100644 --- a/cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm @@ -14,7 +14,7 @@ hist_fincl2 += 'DYN_COL_SOIL_ADJUSTMENTS_C' ! That's needed for the mxsowings and mxharvests axes to make sense. ! However, for testing purposes, it makes sense to save more frequently. hist_fincl3 = 'SDATES', 'SDATES_PERHARV', 'SYEARS_PERHARV', 'HDATES', 'GRAINC_TO_FOOD_PERHARV', 'GRAINC_TO_FOOD_ANN', 'GRAINN_TO_FOOD_PERHARV', 'GRAINN_TO_FOOD_ANN', 'GRAINC_TO_SEED_PERHARV', 'GRAINC_TO_SEED_ANN', 'GRAINN_TO_SEED_PERHARV', 'GRAINN_TO_SEED_ANN', 'HDATES', 'GDDHARV_PERHARV', 'GDDACCUM_PERHARV', 'HUI_PERHARV', 'SOWING_REASON_PERHARV', 'HARVEST_REASON_PERHARV', 'SWINDOW_STARTS', 'SWINDOW_ENDS', 'GDD20_BASELINE', 'GDD20_SEASON_START', 'GDD20_SEASON_END' -hist_nhtfrq(3) = -24 -hist_mfilt(3) = 1 +hist_nhtfrq = -24,-8,-24 +hist_mfilt = 1,1,1 hist_type1d_pertape(3) = 'PFTS' -hist_dov2xy(3) = .false. +hist_dov2xy = .true.,.false.,.false. From 2e4ceb8bee60884c0ea9de05577228803a8e3a8d Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Wed, 7 Aug 2024 17:15:14 -0600 Subject: [PATCH 367/406] Fix the error check --- bld/CLMBuildNamelist.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index f1e5935196..ff796efca9 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4676,7 +4676,7 @@ sub setup_logic_prigent_roughness { 'dust_emis_method'=>$dust_emis_method ); my $use_prigent = $nl->get_value($var); if ( &value_is_true($use_prigent) ) { - if ( $dust_emis_method eq "Leung_2023" ) { + if ( $dust_emis_method ne "Leung_2023" ) { $log->warning( "$var does NOT need to be set without dust_emis_method being Leung_2023" ); } add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldfilename_prigentroughness' ); From 00cb8a1a0766711bcaf0ea51323b65a8bdd71ec0 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 7 Aug 2024 17:15:55 -0600 Subject: [PATCH 368/406] Reduce Izumi cropMonthOutput-RxCropCalsAdaptGGCMI test to 65 days. --- cime_config/testdefs/testlist_clm.xml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 1e687522e4..3e47ca2d95 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3582,12 +3582,20 @@ + + + + + + + + - + From 31f4c03f5cf14e7eb7559e2b9ac5f238abe1ae36 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 7 Aug 2024 17:50:03 -0600 Subject: [PATCH 369/406] Remove fix_nag_bld_2659 test suite. --- cime_config/testdefs/testlist_clm.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 3e47ca2d95..3f6fe51ea0 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3572,7 +3572,6 @@ - @@ -3592,7 +3591,6 @@ - @@ -3603,7 +3601,6 @@ - @@ -3614,7 +3611,6 @@ - From a63a4786f9d26ccdc02fea3d8561c1a5fab19db0 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 7 Aug 2024 17:51:45 -0600 Subject: [PATCH 370/406] Remove expected fail that's now fixed. SMS_Lm25.f10_f10_mg37.IHistClm60BgcCrop.izumi_nag.clm-RxCropCalsAdaptGGCMI --- cime_config/testdefs/ExpectedTestFails.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cime_config/testdefs/ExpectedTestFails.xml b/cime_config/testdefs/ExpectedTestFails.xml index d5e3e4e378..d1e0a1a8b8 100644 --- a/cime_config/testdefs/ExpectedTestFails.xml +++ b/cime_config/testdefs/ExpectedTestFails.xml @@ -163,13 +163,6 @@ - - - FAIL - #2659 - - - From b89a8292bb60cac454cbc4a89615c0f3a3094e96 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Thu, 8 Aug 2024 00:04:00 -0600 Subject: [PATCH 371/406] Add test for prigent on without Leung --- bld/unit_testers/build-namelist_test.pl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index 5c70aa613a..0e230aecd8 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -163,10 +163,10 @@ sub cat_and_create_namelistinfile { # # Figure out number of tests that will run # -my $ntests = 3254; +my $ntests = 3255; if ( defined($opts{'compare'}) ) { - $ntests += 2001; + $ntests += 1965; } plan( tests=>$ntests ); @@ -1295,6 +1295,11 @@ sub cat_and_create_namelistinfile { GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, + "PrigentOnWOLeung" =>{ options=>"-envxml_dir . -bgc sp", + namelst=>"use_prigent_roughness=.true.,dust_emis_method='Zender_2003'", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm6_0", + }, "NotNEONbutNEONlightres" =>{ options=>"--res CLM_USRDAT --clm_usr_name regional --envxml_dir . --bgc bgc --light_res 106x174", namelst=>"fsurdat='build-namelist_test.pl'", GLC_TWO_WAY_COUPLING=>"FALSE", From 1041a7c93b27dc45a4c1587b02da5c1dac04b7cd Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 8 Aug 2024 11:09:21 -0600 Subject: [PATCH 372/406] Add testmods and tests for all fire methods. --- cime_config/testdefs/testlist_clm.xml | 40 +++++++++++++++++++ .../clm/FireLi2014Qian/include_user_mods | 1 + .../clm/FireLi2014Qian/user_nl_clm | 1 + .../clm/FireLi2016Cru/include_user_mods | 1 + .../clm/FireLi2016Cru/user_nl_clm | 1 + .../clm/FireLi2021GSWP/include_user_mods | 1 + .../clm/FireLi2021GSWP/user_nl_clm | 1 + .../clm/FireLi2024GSWP/include_user_mods | 1 + .../clm/FireLi2024GSWP/user_nl_clm | 1 + 9 files changed, 48 insertions(+) create mode 100644 cime_config/testdefs/testmods_dirs/clm/FireLi2014Qian/include_user_mods create mode 100644 cime_config/testdefs/testmods_dirs/clm/FireLi2014Qian/user_nl_clm create mode 100644 cime_config/testdefs/testmods_dirs/clm/FireLi2016Cru/include_user_mods create mode 100644 cime_config/testdefs/testmods_dirs/clm/FireLi2016Cru/user_nl_clm create mode 100644 cime_config/testdefs/testmods_dirs/clm/FireLi2021GSWP/include_user_mods create mode 100644 cime_config/testdefs/testmods_dirs/clm/FireLi2021GSWP/user_nl_clm create mode 100644 cime_config/testdefs/testmods_dirs/clm/FireLi2024GSWP/include_user_mods create mode 100644 cime_config/testdefs/testmods_dirs/clm/FireLi2024GSWP/user_nl_clm diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 1ac2334ba8..36da30043c 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3070,5 +3070,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cime_config/testdefs/testmods_dirs/clm/FireLi2014Qian/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FireLi2014Qian/include_user_mods new file mode 100644 index 0000000000..fe0e18cf88 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FireLi2014Qian/include_user_mods @@ -0,0 +1 @@ +../default diff --git a/cime_config/testdefs/testmods_dirs/clm/FireLi2014Qian/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FireLi2014Qian/user_nl_clm new file mode 100644 index 0000000000..3e05de8cad --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FireLi2014Qian/user_nl_clm @@ -0,0 +1 @@ +fire_method = 'li2014qianfrc' diff --git a/cime_config/testdefs/testmods_dirs/clm/FireLi2016Cru/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FireLi2016Cru/include_user_mods new file mode 100644 index 0000000000..fe0e18cf88 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FireLi2016Cru/include_user_mods @@ -0,0 +1 @@ +../default diff --git a/cime_config/testdefs/testmods_dirs/clm/FireLi2016Cru/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FireLi2016Cru/user_nl_clm new file mode 100644 index 0000000000..eecbd5b2d9 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FireLi2016Cru/user_nl_clm @@ -0,0 +1 @@ +fire_method = 'li2016crufrc' diff --git a/cime_config/testdefs/testmods_dirs/clm/FireLi2021GSWP/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FireLi2021GSWP/include_user_mods new file mode 100644 index 0000000000..fe0e18cf88 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FireLi2021GSWP/include_user_mods @@ -0,0 +1 @@ +../default diff --git a/cime_config/testdefs/testmods_dirs/clm/FireLi2021GSWP/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FireLi2021GSWP/user_nl_clm new file mode 100644 index 0000000000..8703daf6f8 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FireLi2021GSWP/user_nl_clm @@ -0,0 +1 @@ +fire_method = 'li2021gswpfrc' diff --git a/cime_config/testdefs/testmods_dirs/clm/FireLi2024GSWP/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FireLi2024GSWP/include_user_mods new file mode 100644 index 0000000000..fe0e18cf88 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FireLi2024GSWP/include_user_mods @@ -0,0 +1 @@ +../default diff --git a/cime_config/testdefs/testmods_dirs/clm/FireLi2024GSWP/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FireLi2024GSWP/user_nl_clm new file mode 100644 index 0000000000..7926de5891 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FireLi2024GSWP/user_nl_clm @@ -0,0 +1 @@ +fire_method = 'li2024gswpfrc' From d0f7fc2b56d0094b0bbb5e6ecc1ef580a55c8431 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Aug 2024 15:54:51 -0600 Subject: [PATCH 373/406] Update cam and cice to version in cesm3_0_beta02, there is a gitfleximod problem in updating to this CAM version --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 07a4f7e3e6..bc8db311ec 100644 --- a/.gitmodules +++ b/.gitmodules @@ -40,14 +40,14 @@ fxDONOTUSEurl = https://github.com/NCAR/fates-release [submodule "cice"] path = components/cice url = https://github.com/ESCOMP/CESM_CICE -fxtag = cesm_cice6_5_0_9 +fxtag = cesm_cice6_5_0_10 fxrequired = ToplevelRequired fxDONOTUSEurl = https://github.com/ESCOMP/CESM_CICE [submodule "cam"] path = components/cam url = https://github.com/ESCOMP/CAM -fxtag = cam6_3_162 +fxtag = cam6_4_016 fxrequired = ToplevelRequired fxDONOTUSEurl = https://github.com/ESCOMP/CAM # From 5084ea66320b447af6a600cf90dd9d580366e5d6 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Aug 2024 16:19:00 -0600 Subject: [PATCH 374/406] Remove CAM and CICE from the submodules for coming into CTSM master --- .gitmodules | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/.gitmodules b/.gitmodules index bc8db311ec..19bc024208 100644 --- a/.gitmodules +++ b/.gitmodules @@ -33,27 +33,6 @@ fxrequired = AlwaysRequired # Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed fxDONOTUSEurl = https://github.com/NCAR/fates-release -# -# CAM and CICE in order to do F compsets along this brunch -# (Remove before going to master) -# -[submodule "cice"] - path = components/cice - url = https://github.com/ESCOMP/CESM_CICE -fxtag = cesm_cice6_5_0_10 -fxrequired = ToplevelRequired -fxDONOTUSEurl = https://github.com/ESCOMP/CESM_CICE - -[submodule "cam"] -path = components/cam -url = https://github.com/ESCOMP/CAM -fxtag = cam6_4_016 -fxrequired = ToplevelRequired -fxDONOTUSEurl = https://github.com/ESCOMP/CAM -# -# End -# - [submodule "cism"] path = components/cism url = https://github.com/ESCOMP/CISM-wrapper From 870588a1a03edb35591f9495adec140bca20acdb Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Aug 2024 16:44:26 -0600 Subject: [PATCH 375/406] Add a LDust testmod and tests for it Add a testmod for running Leung_2023 dust emission explicitly. And change some existing aux_clm tests over to it (but not all). --- cime_config/testdefs/testlist_clm.xml | 12 ++++++------ .../clm60cam7LndTuningModeLDust/include_user_mods | 1 + .../clm/clm60cam7LndTuningModeLDust/user_nl_clm | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 cime_config/testdefs/testmods_dirs/clm/clm60cam7LndTuningModeLDust/include_user_mods create mode 100644 cime_config/testdefs/testmods_dirs/clm/clm60cam7LndTuningModeLDust/user_nl_clm diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 951e0d8d5b..f4484c7410 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -100,7 +100,7 @@ - + @@ -109,7 +109,7 @@ - + @@ -129,7 +129,7 @@ - + @@ -138,7 +138,7 @@ - + @@ -156,7 +156,7 @@ - + @@ -247,7 +247,7 @@ - + diff --git a/cime_config/testdefs/testmods_dirs/clm/clm60cam7LndTuningModeLDust/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/clm60cam7LndTuningModeLDust/include_user_mods new file mode 100644 index 0000000000..ef8619d930 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/clm60cam7LndTuningModeLDust/include_user_mods @@ -0,0 +1 @@ +../clm60cam7LndTuningMode diff --git a/cime_config/testdefs/testmods_dirs/clm/clm60cam7LndTuningModeLDust/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/clm60cam7LndTuningModeLDust/user_nl_clm new file mode 100644 index 0000000000..54057b1893 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/clm60cam7LndTuningModeLDust/user_nl_clm @@ -0,0 +1 @@ +dust_emis_method = 'Leung_2023' From b669df61dccd3831c6b9851de0aff41c4cd8c64e Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Aug 2024 17:28:26 -0600 Subject: [PATCH 376/406] Add some comments about how this can be removed --- .../share_esmf/PrigentRoughnessStreamType.F90 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/unit_test_stubs/share_esmf/PrigentRoughnessStreamType.F90 b/src/unit_test_stubs/share_esmf/PrigentRoughnessStreamType.F90 index 71fb04f942..d9ded09c30 100644 --- a/src/unit_test_stubs/share_esmf/PrigentRoughnessStreamType.F90 +++ b/src/unit_test_stubs/share_esmf/PrigentRoughnessStreamType.F90 @@ -3,6 +3,12 @@ module PrigentRoughnessStreamType !----------------------------------------------------------------------- ! !DESCRIPTION: ! UNIT-TEST STUB for Prigent Roughtness Stream + ! This just allows the Leung dust emission code to be tested without + ! reading in the streams data, by faking it and setting it to a + ! constant value. + ! NOTE: THIS SHOULD BE REMOVED WHEN THE MAIN VERSION ALLOWS IT TO BE OFF. + ! https://github.com/ESCOMP/CTSM/issues/2381 + ! Removing this version will remove testing code duplication. ! !USES use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_cl use shr_log_mod , only : errMsg => shr_log_errMsg @@ -50,7 +56,7 @@ subroutine Init(this, bounds, NLFilename) ! local variables !----------------------------------------------------------------------- allocate(this%prigent_rghn(bounds%begg:bounds%endg)) - this%prigent_rghn(:) = 1.0_r8 + this%prigent_rghn(:) = 1.0_r8 ! This should likely be a smaller value like 0.1 based on the Prigent dataset average values InitDone = .true. end subroutine Init From 2d889afb1b94a4c25143a9f8003395188fe81110 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Aug 2024 17:37:19 -0600 Subject: [PATCH 377/406] Add some comments and revise the warning as per code review --- bld/CLMBuildNamelist.pm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 463bd9c429..b99be288e7 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -5091,11 +5091,15 @@ sub setup_logic_prigent_roughness { my $use_prigent = $nl->get_value($var); if ( &value_is_true($use_prigent) ) { if ( $dust_emis_method ne "Leung_2023" ) { - $log->warning( "$var does NOT need to be set without dust_emis_method being Leung_2023" ); + # The Prigent dataset could be used for other purposes + # (such as roughness as in https://github.com/ESCOMP/CTSM/issues/2349) + $log->warning( "$var does NOT need to on without dust_emis_method being Leung_2023, it simply won't be used" ); } add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldfilename_prigentroughness' ); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_meshfile_prigentroughness' ); } elsif ( $dust_emis_method eq "Leung_2023" ) { + # In the future we WILL allow it to be turned off for testing and Paleo work + # see: https://github.com/ESCOMP/CTSM/issues/2381) $log->fatal_error("variable \"$var\" MUST be true when Leung_2023 dust emission method is being used" ); } } From e76b9d1092cb8b5ea6ceb82bda5016a5b336f608 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Aug 2024 17:38:36 -0600 Subject: [PATCH 378/406] REmove all of the settings of GLC_TWO_WAY_COUPLING set to FALSE since that's the default and it's not needed --- bld/unit_testers/build-namelist_test.pl | 7 ------- 1 file changed, 7 deletions(-) diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index 1fe98b392e..d7c94b8538 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -519,7 +519,6 @@ sub cat_and_create_namelistinfile { }, "LeungDust_WO_Prigent" =>{ options=>" -envxml_dir . -bgc sp", namelst=>"use_prigent_roughness=.false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_1", }, "soilm_stream off w file" =>{ options=>"-res 0.9x1.25 -envxml_dir .", @@ -544,17 +543,14 @@ sub cat_and_create_namelistinfile { }, "coldstart exice on wo stream"=>{ options=>"-res 0.9x1.25 -envxml_dir . --clm_start_type cold", namelst=>"use_excess_ice=.true., use_excess_ice_streams = .false.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "coldstart exice on bad temp" =>{ options=>"-res 0.9x1.25 -envxml_dir . --clm_start_type cold", namelst=>"use_excess_ice=.true., use_excess_ice_streams = .true., excess_ice_coldstart_temp=0.0", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "coldstart exice on bad depth" =>{ options=>"-res 0.9x1.25 -envxml_dir . --clm_start_type cold", namelst=>"use_excess_ice=.true., use_excess_ice_streams = .true., excess_ice_coldstart_depth=0.0", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "clm50CNDVwtransient" =>{ options=>" -envxml_dir . -use_case 20thC_transient -dynamic_vegetation -res 10x15 -ignore_warnings", @@ -1274,17 +1270,14 @@ sub cat_and_create_namelistinfile { }, "Set coldtemp wo coldstart" =>{ options=>"-envxml_dir . --clm_start_type startup", namelst=>"use_excess_ice=.true.,excess_ice_coldstart_temp=-10.", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "Set colddepth wo coldstart" =>{ options=>"-envxml_dir . --clm_start_type startup", namelst=>"use_excess_ice=.true.,excess_ice_coldstart_depth=0.5", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "PrigentOnWOLeung" =>{ options=>"-envxml_dir . -bgc sp", namelst=>"use_prigent_roughness=.true.,dust_emis_method='Zender_2003'", - GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm6_0", }, "NotNEONbutNEONlightres" =>{ options=>"--res CLM_USRDAT --clm_usr_name regional --envxml_dir . --bgc bgc --light_res 106x174", From 00537d1e28e787c80c1769a6c458197ca7d2a5ca Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Aug 2024 18:21:48 -0600 Subject: [PATCH 379/406] Add dust fields including FV to output history files for the Zender and Leung dust tests --- .../clm/clm45cam4LndTuningModeZDustSoilErod/user_nl_clm | 1 + .../clm/clm50cam5LndTuningModeZDustSoilErod/user_nl_clm | 1 + .../clm/clm51cam6LndTuningModeZDustSoilErod/user_nl_clm | 1 + .../testmods_dirs/clm/clm60cam7LndTuningModeLDust/user_nl_clm | 4 ++++ 4 files changed, 7 insertions(+) diff --git a/cime_config/testdefs/testmods_dirs/clm/clm45cam4LndTuningModeZDustSoilErod/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/clm45cam4LndTuningModeZDustSoilErod/user_nl_clm index 93b7ee2e48..d97fbbac57 100644 --- a/cime_config/testdefs/testmods_dirs/clm/clm45cam4LndTuningModeZDustSoilErod/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/clm45cam4LndTuningModeZDustSoilErod/user_nl_clm @@ -1,3 +1,4 @@ ! Turn on using the soil eroditability file in CTSM dust_emis_method = 'Zender_2003' zender_soil_erod_source = 'lnd' +hist_fincl1 += 'FV' diff --git a/cime_config/testdefs/testmods_dirs/clm/clm50cam5LndTuningModeZDustSoilErod/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/clm50cam5LndTuningModeZDustSoilErod/user_nl_clm index 93b7ee2e48..d97fbbac57 100644 --- a/cime_config/testdefs/testmods_dirs/clm/clm50cam5LndTuningModeZDustSoilErod/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/clm50cam5LndTuningModeZDustSoilErod/user_nl_clm @@ -1,3 +1,4 @@ ! Turn on using the soil eroditability file in CTSM dust_emis_method = 'Zender_2003' zender_soil_erod_source = 'lnd' +hist_fincl1 += 'FV' diff --git a/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningModeZDustSoilErod/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningModeZDustSoilErod/user_nl_clm index 93b7ee2e48..d97fbbac57 100644 --- a/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningModeZDustSoilErod/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningModeZDustSoilErod/user_nl_clm @@ -1,3 +1,4 @@ ! Turn on using the soil eroditability file in CTSM dust_emis_method = 'Zender_2003' zender_soil_erod_source = 'lnd' +hist_fincl1 += 'FV' diff --git a/cime_config/testdefs/testmods_dirs/clm/clm60cam7LndTuningModeLDust/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/clm60cam7LndTuningModeLDust/user_nl_clm index 54057b1893..e6da29bbe9 100644 --- a/cime_config/testdefs/testmods_dirs/clm/clm60cam7LndTuningModeLDust/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/clm60cam7LndTuningModeLDust/user_nl_clm @@ -1 +1,5 @@ dust_emis_method = 'Leung_2023' +! Add all of the Leung optional history fields +hist_fincl1 += 'FV', 'DUST_EMIS_COEFF', 'WND_FRC_FT', 'WND_FRC_FT_DRY', 'WND_FRC_IT', 'WND_FRC_SOIL', 'LND_FRC_MBLE', 'GWC', 'LIQ_FRAC', + 'U_S_MEAN', 'U_S_MEAN', 'ZETAOBU', 'U_FT', 'U_IT', 'ALPHA_TC_RATE', 'P_IT'. 'ETA', 'SSR', 'VAI_OKIN', + 'FRC_THR_RGHN_FCT', 'WND_FRC_FT_STD', 'DPFCT_ROCK' From f484425c53260f1294faf5e7c3f410bc69659ac8 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Aug 2024 18:34:20 -0600 Subject: [PATCH 380/406] Modify some of the comments based on code review, and simplify the MassFracClayLeung2023 function --- src/biogeophys/SoilStateInitTimeConstMod.F90 | 27 ++++++++++---------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/biogeophys/SoilStateInitTimeConstMod.F90 b/src/biogeophys/SoilStateInitTimeConstMod.F90 index edcc14430b..e6fcca0f27 100644 --- a/src/biogeophys/SoilStateInitTimeConstMod.F90 +++ b/src/biogeophys/SoilStateInitTimeConstMod.F90 @@ -706,15 +706,8 @@ subroutine SoilStateInitTimeConst(bounds, soilstate_inst, nlfilename) ! -------------------------------------------------------------------- ! Initialize threshold soil moisture, and mass fraction of clay as - ! scaling coefficient of dust emission flux (kg/m2/s) in DUSTMod.F90 - ! dmleung modified 5 Jul 2024, reducing sensitivity of dust emission - ! flux to clay fraction. - ! Also, for threshold soil moisture, dmleung followed Zender (2003) - ! DEAD scheme to again avoid dust flux being too sensitive to the choice - ! of clay dataset. This is different from what Leung et al. (2023) - ! intended to do. - ! Both decisions are based on tuning preference and could subject to - ! future changes. dmleung 5 Jul 2024 + ! scaling coefficient of dust emission flux (kg/m2/s) in each DustEmisType + ! module. See the comments in each function. ! -------------------------------------------------------------------- do c = begc,endc @@ -751,6 +744,7 @@ real(r8) function ThresholdSoilMoistZender2003( clay ) ! convert surface clay fraction from percentage to fraction. ! The equation comes from Eq. 14 of Fecan et al. (1999; https://doi.org/10.1007/s00585-999-0149-7). ! + ! NOTE: dmleung 19 Feb 2024. !------------------------------------------------------------------------------ ! For future developments Danny M. Leung decided (Dec, 2023) that the Leung et al. (2023) o ! dust emission scheme in the CESM will use Zender's tuning as well, which overall @@ -762,7 +756,11 @@ real(r8) function ThresholdSoilMoistZender2003( clay ) ! ! Also see the notes below for ThresholdSoilMoistKok2014. ! - ! Notes from: dmleung 19 Feb 2024. + ! NOTE on Leung dust emissions: dmleung Jul 5 2024: + ! + ! dmleung followed Zender (2003) DUST scheme to again avoid dust flux being too sensitive to the choice + ! of clay dataset. This is different from what Leung et al. (2023) intended to do. + ! NOTE: This might need to be adjusted for tuning in the future. ! !------------------------------------------------------------------------------ real(r8), intent(IN) :: clay ! Fraction of clay in the soil (%) @@ -781,6 +779,8 @@ real(r8) function ThresholdSoilMoistKok2014( clay ) !------------------------------------------------------------------------------ ! Calculate the threshold soil moisture needed for dust emission, based on clay content ! + ! NOTE: dmleung 24 May 2024. + ! ! The below calculates the threshold gravimetric water content for the dust emission ! calculation in DustEmis. The equation comes from Eq. 14 of Fecan et al. ! (1999; https://doi.org/10.1007/s00585-999-0149-7). @@ -792,7 +792,6 @@ real(r8) function ThresholdSoilMoistKok2014( clay ) ! Charlie Zender (2003a) chose: a = 1/clay3d, which gives the ThresholdSoilMoistZender2003 ! function above. ! - ! Notes from: dmleung 24 May 2024. !------------------------------------------------------------------------------ real(r8), intent(IN) :: clay ! Fraction of clay in the soil (%) @@ -813,10 +812,12 @@ end function MassFracClay real(r8) function MassFracClayLeung2023( clay ) ! Calculate the mass fraction of clay needed for dust emission, based on clay content ! Based on the base Zender_2003 version, with a slight modification for Leung_2023 + ! dmleung modified 5 Jul 2024, reducing sensitivity of dust emission + ! flux to clay fraction. + ! NOTE: This might need to be adjusted for tuning in the future. real(r8), intent(IN) :: clay ! Fraction of lay in the soil (%) - MassFracClayLeung2023 = MassFracClay( clay ) - MassFracClayLeung2023 = 0.1_r8 + MassFracClayLeung2023 * 0.1_r8 / 0.20_r8 ! dmleung added this line to reduce the sensitivity of dust emission flux to clay fraction in DUSTMod. 5 Jul 2024 + MassFracClayLeung2023 = 0.1_r8 + MassFracClay( clay ) * 0.1_r8 / 0.20_r8 ! dmleung added this line to reduce the sensitivity of dust emission flux to clay fraction in DUSTMod. 5 Jul 2024 end function MassFracClayLeung2023 !------------------------------------------------------------------------------ From d7e5a6ddf2668c1379a85fa7fadd5b259ca6540c Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Aug 2024 18:43:51 -0600 Subject: [PATCH 381/406] Add back the comment that got accidentally removed --- src/unit_test_shr/unittestDustEmisInputs.F90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/unit_test_shr/unittestDustEmisInputs.F90 b/src/unit_test_shr/unittestDustEmisInputs.F90 index a8473460b9..6f1a7b09f7 100644 --- a/src/unit_test_shr/unittestDustEmisInputs.F90 +++ b/src/unit_test_shr/unittestDustEmisInputs.F90 @@ -84,10 +84,11 @@ subroutine setUp(this) glcmec_downscale_longwave = .false., & lapse_rate = 0.01_r8 & ! arbitrary (this is unused for these tests) ) + call this%atm2lnd_inst%InitForTesting(bounds, atm2lnd_params) ! Water and soil state -- after the subgrid setup call this%water_factory%setup_after_subgrid(snl = snl) - call this%setupSoilState( ) + call this%setupSoilState( ) ! This needs to happen before the water_type object creation call this%water_factory%create_water_type(this%water_inst, watsat_col=this%soilstate_inst%watsat_col) ! Canopy state, friction velocity, and temperature state ojects call this%canopystate_inst%SetNMLForTesting() From e83caa8e79bec1c25bd7d353838606b36efbc42d Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Aug 2024 18:47:43 -0600 Subject: [PATCH 382/406] Remove if for use_cn around adding FV to the history file and make it default inactive --- src/biogeophys/FrictionVelocityMod.F90 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/biogeophys/FrictionVelocityMod.F90 b/src/biogeophys/FrictionVelocityMod.F90 index 8e06e66e32..44e1bf6294 100644 --- a/src/biogeophys/FrictionVelocityMod.F90 +++ b/src/biogeophys/FrictionVelocityMod.F90 @@ -262,12 +262,10 @@ subroutine InitHistory(this, bounds) ptr_patch=this%ram1_patch, default='inactive') end if - !if (use_cn) then this%fv_patch(begp:endp) = spval call hist_addfld1d (fname='FV', units='m/s', & avgflag='A', long_name='friction velocity', & - ptr_patch=this%fv_patch) - !end if + ptr_patch=this%fv_patch, default='inactive') call hist_addfld1d (fname='RAH1', units='s/m', & avgflag='A', long_name='aerodynamical resistance ', & From 4daf9246649eb0ba77800706614ff039c36d747b Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Aug 2024 18:59:46 -0600 Subject: [PATCH 383/406] Code review noticed a couple places where a 1_8 really should be vai_mbl_thr --- src/biogeochem/DustEmisLeung2023.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/biogeochem/DustEmisLeung2023.F90 b/src/biogeochem/DustEmisLeung2023.F90 index 6cc86e4eb5..c96853b710 100644 --- a/src/biogeochem/DustEmisLeung2023.F90 +++ b/src/biogeochem/DustEmisLeung2023.F90 @@ -640,7 +640,7 @@ subroutine DustEmission (this, bounds, & ! purpose: compute factor by which surface roughness increases threshold ! friction velocity (currently a constant) - if (lnd_frc_mbl(p) > 0.0_r8 .AND. tlai_lu(l)<=1_r8) then + if (lnd_frc_mbl(p) > 0.0_r8 .AND. tlai_lu(l)<= vai_mbl_thr) then !if (lnd_frc_mbl(p) > 0.0_r8 .AND. ttlai(p)<=1_r8) then ! vegetation drag partition equation following Gregory Okin (2008) + Caroline Pierre et al. (2014) !lai(p) = tlai_lu(l)+0.1_r8 ! LAI+SAI averaged to landunit level; the equation is undefined at lai=0, and LAI in CTSM has some zeros over deserts, so we add in a small number. @@ -687,7 +687,7 @@ subroutine DustEmission (this, bounds, & else ! for lnd_frc_mbl=0, do not change friction velocity and assume drag partition factor = 0 wnd_frc_slt = fv(p) * frc_thr_rghn_fct(p) ! The value here is not important since once lnd_frc_mbl(p) <= 0.0_r8 there will be no emission. dmleung added 5 Jul 2024 - frc_thr_rghn_fct(p) = 0.0_r8 ! When LAI > 1, the drag partition effect is zero. dmleung 16 Feb 2024. + frc_thr_rghn_fct(p) = 0.0_r8 ! When LAI > vai_mbl_thr, the drag partition effect is zero. dmleung 16 Feb 2024. end if !########## end of drag partition effect ####################################################### From 5007a1a8850c1635f13d9e21ef8a07a31fe2db23 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Aug 2024 19:10:09 -0600 Subject: [PATCH 384/406] Remove cam and cice as submodules --- components/cam | 1 - components/cice | 1 - 2 files changed, 2 deletions(-) delete mode 160000 components/cam delete mode 160000 components/cice diff --git a/components/cam b/components/cam deleted file mode 160000 index ab476f9b73..0000000000 --- a/components/cam +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ab476f9b7345cbefdc4cf67ff17f0fe85d8c7387 diff --git a/components/cice b/components/cice deleted file mode 160000 index bdf6ea04d6..0000000000 --- a/components/cice +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bdf6ea04d6133434fcaa4de5336de106f01290d0 From 63a2e73deb72088d84160e020b8ae2b254edfa88 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Aug 2024 19:47:32 -0600 Subject: [PATCH 385/406] Remove commented out code for clarity --- src/biogeochem/DustEmisLeung2023.F90 | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/biogeochem/DustEmisLeung2023.F90 b/src/biogeochem/DustEmisLeung2023.F90 index c96853b710..1746056fbf 100644 --- a/src/biogeochem/DustEmisLeung2023.F90 +++ b/src/biogeochem/DustEmisLeung2023.F90 @@ -641,17 +641,6 @@ subroutine DustEmission (this, bounds, & ! friction velocity (currently a constant) if (lnd_frc_mbl(p) > 0.0_r8 .AND. tlai_lu(l)<= vai_mbl_thr) then - !if (lnd_frc_mbl(p) > 0.0_r8 .AND. ttlai(p)<=1_r8) then - ! vegetation drag partition equation following Gregory Okin (2008) + Caroline Pierre et al. (2014) - !lai(p) = tlai_lu(l)+0.1_r8 ! LAI+SAI averaged to landunit level; the equation is undefined at lai=0, and LAI in CTSM has some zeros over deserts, so we add in a small number. - !lai(p) = ttlai(p) + lai0_Okin ! ttlai = tlai+tsai. Okin-Pierre's equation is undefined at lai=0, and LAI in CTSM has some zeros over deserts, so we add in a small number. On 26 Feb 2024, dmleung changed from tlai_lu(l) to ttlai(p) - !if (lai(p) > 1_r8) then - ! lai(p) = 1_r8 ! setting LAI = 1 to be a max value (since K_length goes to negative when LAI>1) - !end if ! - - !if (ttlai(p) + vai0_Okin <= 1_r8) then - ! vai_Okin(p) = ttlai(p) + vai0_Okin ! ttlai = vai = tlai+tsai. Okin-Pierre's equation is undefined at vai=0, and VAI in CTSM has some zeros over deserts, so we add in a small number. On 26 Feb 2024, dmleung changed from tlai_lu(l) to ttlai(p) - !end if ! In the Okin-Pierre formulation, VAI has to be 0 < VAI <= 1. vai_Okin(p) = tlai_lu(l)+vai0_Okin ! LAI+SAI averaged to landunit level; the equation is undefined at lai=0, and LAI in CTSM has some zeros over deserts, so we add in a small number. if (vai_Okin(p) > 1_r8) then @@ -667,13 +656,10 @@ subroutine DustEmission (this, bounds, & if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then if (patch%itype(p) == noveg) then ! if bare, uses rock drag partition factor if (dpfct_rock(p) /= dpfct_rock(p)) then ! dmleung added 24 May 2024: dpfct_rock(p) could be NaN; CLM could run when DEBUG=FALSE in env_build.xml but dies when DEBUG=TRUE (usually when checking if wnd_frc_slt > wnd_frc_thr_slt_it and if numer/denom < 30._r8 below) - !write(iulog,*) 'dpfct_rock(p) == NaN; dpfct_rock(p) = ', dpfct_rock(p) frc_thr_rgh_fct = 0.001_r8 ! Set drag partition effect to be a very small value (or zero) such that there is no emission whenever dpfct_rock(p) = NaN; dmleung 24 May 2024 else - !write(iulog,*) 'dpfct_rock(p) = ', dpfct_rock(p) frc_thr_rgh_fct = dpfct_rock(p) end if - !frc_thr_rgh_fct = dpfct_rock(p) ! This should be the original code when dpfct_rock(p) has values everywhere else ! if vegetation, uses vegetation drag partition factor frc_thr_rgh_fct = ssr(p) end if @@ -731,7 +717,6 @@ subroutine DustEmission (this, bounds, & ! mean lowpass-filtered wind speed at hgt_sal = 0.1 m saltation height (assuming aerodynamic roughness length z0a_glob = 1e-4 m globally for ease; also assuming neutral condition) u_mean_slt(p) = (wnd_frc_slt/k) * log(hgt_sal / z0a_glob) ! translating from ustar (velocity scale) to actual wind - !stblty(p) = 0_r8 ! -dmleung 2 Dec 2021: use stability = 0 for now, assuming no buoyancy contribution. Might uncomment the above lines in future revisions. stblty(p) = zii / obu(p) ! -dmleung 20 Feb 2024: use obu from CTSM and PBL height = zii (= 1000_r8) which is default in CTSM. Should we transfer PBL height from CAM? if ((12_r8 - 0.5_r8 * stblty(p)) .GE. 0.001_r8) then ! should have used 0 theoretically; used 0.001 here to avoid undefined values u_sd_slt(p) = wnd_frc_slt * (12_r8 - 0.5_r8 * stblty(p))**0.333_r8 From 5a2c873d89b0001baac42480182b115fbec19a98 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Aug 2024 21:38:49 -0600 Subject: [PATCH 386/406] Clarify the error message that the prigent streams file is required for Leung 2023 dust emissions --- src/biogeochem/DustEmisLeung2023.F90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/biogeochem/DustEmisLeung2023.F90 b/src/biogeochem/DustEmisLeung2023.F90 index 1746056fbf..5f92d58425 100644 --- a/src/biogeochem/DustEmisLeung2023.F90 +++ b/src/biogeochem/DustEmisLeung2023.F90 @@ -319,7 +319,8 @@ subroutine InitCold(this, bounds) call this%CalcDragPartition( bounds ) else - call endrun( "ERROR:: Drag partitioning MUST now use a streams file of aeolian roughness length to calculate, it can no longer read from the fsurdat file" ) + call endrun( "ERROR:: dus_emis_Leung_2023 requires requires a streams file of aeolian roughness length to calculate drag partitioning" ) + end if end subroutine InitCold From 96d2de57dd024b712035d5cca54dbe7d3cec65e5 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Fri, 9 Aug 2024 22:59:31 -0600 Subject: [PATCH 387/406] Fix a syntax topo, so will work --- .../testmods_dirs/clm/clm60cam7LndTuningModeLDust/user_nl_clm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cime_config/testdefs/testmods_dirs/clm/clm60cam7LndTuningModeLDust/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/clm60cam7LndTuningModeLDust/user_nl_clm index e6da29bbe9..09887c81b8 100644 --- a/cime_config/testdefs/testmods_dirs/clm/clm60cam7LndTuningModeLDust/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/clm60cam7LndTuningModeLDust/user_nl_clm @@ -1,5 +1,5 @@ dust_emis_method = 'Leung_2023' ! Add all of the Leung optional history fields hist_fincl1 += 'FV', 'DUST_EMIS_COEFF', 'WND_FRC_FT', 'WND_FRC_FT_DRY', 'WND_FRC_IT', 'WND_FRC_SOIL', 'LND_FRC_MBLE', 'GWC', 'LIQ_FRAC', - 'U_S_MEAN', 'U_S_MEAN', 'ZETAOBU', 'U_FT', 'U_IT', 'ALPHA_TC_RATE', 'P_IT'. 'ETA', 'SSR', 'VAI_OKIN', + 'U_S_MEAN', 'U_S_MEAN', 'ZETAOBU', 'U_FT', 'U_IT', 'ALPHA_TC_RATE', 'P_IT', 'ETA', 'SSR', 'VAI_OKIN', 'FRC_THR_RGHN_FCT', 'WND_FRC_FT_STD', 'DPFCT_ROCK' From 38c42ae7b0ec946ee95b56b3840e0ba4c6d6515f Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sat, 10 Aug 2024 14:13:06 -0600 Subject: [PATCH 388/406] Add a unit test to check if dpfct_rock can be nan, currently fails as the nan handling inside of DustEmissions for Leung is not sufficient --- src/biogeochem/DustEmisBase.F90 | 19 ++++++++ .../DustEmis_test/test_DustEmisLeung2023.pf | 47 +++++++++++++++++-- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/biogeochem/DustEmisBase.F90 b/src/biogeochem/DustEmisBase.F90 index d13df40992..84467c701d 100644 --- a/src/biogeochem/DustEmisBase.F90 +++ b/src/biogeochem/DustEmisBase.F90 @@ -69,6 +69,8 @@ module DustEmisBase procedure , public :: GetConstVars ! Get important constant variables procedure , public :: CleanBase ! Base object deallocation (allows extension) procedure , public :: Clean => CleanBase ! Deallocation used by callers + procedure , public :: SetDragPartitionBase ! Base SetDragPartition method that just aborts + procedure , public :: SetDragPartition => SetDragPartitionBase ! SetDrgPartiotion used by callers procedure , private :: InitAllocateBase procedure , private :: InitHistoryBase procedure , private :: InitDustVars ! Initialize variables used in DustEmission method @@ -237,6 +239,23 @@ subroutine InitHistoryBase(this, bounds) end subroutine InitHistoryBase + !------------------------------------------------------------------------ + + subroutine SetDragPartitionBase(this, bounds, drag_partition) + ! + ! !DESCRIPTION: + ! Set the drag partition for testing -- only aborts as only used by the Leung instance + ! + ! !USES: + ! !ARGUMENTS: + class(dust_emis_base_type) :: this + type(bounds_type), intent(in) :: bounds + real(r8), intent(in) :: drag_partition + + call endrun(msg="SetDragPartition is NOT allowed for this dust emission class type") + + end subroutine SetDragPartitionBase + !----------------------------------------------------------------------- subroutine WritePatchToLog(this, p) diff --git a/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf b/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf index 4947a76d60..7aac658d33 100644 --- a/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf +++ b/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf @@ -7,7 +7,9 @@ module test_DustEmisLeung2023 use unittestSubgridMod, only : bounds use DustEmisBase use DustEmisLeung2023 + use abortutils, only : endrun use shr_kind_mod , only : r8 => shr_kind_r8 + use shr_infnan_mod, only : nan => shr_infnan_nan, assignment(=) use DustEmisFactory, only : create_dust_emissions use shr_dust_emis_mod, only : dust_emis_set_options @@ -37,7 +39,7 @@ contains call dust_emis_set_options( 'Leung_2023', 'none') call this%input%setUp( ) - + ! Create the dust emission object last allocate(this%dust_emis, source = create_dust_emissions(bounds, NLFilename)) end subroutine setUp @@ -72,7 +74,7 @@ contains call this%dust_emis%WritePatchToLog( p ) end do end subroutine print_values - + !----------------------------------------------------------------------- subroutine validate_patch(this, p) @@ -203,7 +205,7 @@ contains class(TestDustEmisLeung2023), intent(inout) :: this real(r8) :: flx_mss_vrt_dst_tot character(100) :: expected_msg - + call this%input%create_atm2lnd() call this%input%create_fv( ) ! Dust should abort with an error on dust mobility when snow fraction greater than 1 @@ -215,7 +217,7 @@ contains @assertExceptionRaised(expected_msg) end subroutine aborts_on_bad_dust_mobility - + !----------------------------------------------------------------------- @Test @@ -248,7 +250,44 @@ contains end subroutine dust_zero_when_tlai_high !----------------------------------------------------------------------- + @Test + subroutine dust_handles_dpfct_rock_nan(this) + use PatchType, only : patch + ! Check dust emissions can handle dpftc_rock being set to NaN + class(TestDustEmisLeung2023), intent(inout) :: this + integer :: p, i + real(r8) :: flx_mss_vrt_dst_tot, drag_partition, previous_dst_tot + call this%input%create_atm2lnd() + call this%input%create_fv( ) + ! Loop over twice first with nan and then with the tiny value that nan should be changed to + do i = 1, 2 + if ( i == 1 )then + drag_partition = nan + else if ( i == 2 ) then + drag_partition = 0.001 + else + call endrun( "Looping over too many iterations should just be 2" ) + end if + call this%dust_emis%SetDragPartition(bounds, drag_partition) + call this%dust_emis%DustEmission(bounds, this%input%num_nolakep, this%input%filter_nolakep, this%input%atm2lnd_inst, & + this%input%soilstate_inst, this%input%canopystate_inst, this%input%water_inst%waterstatebulk_inst, & + this%input%water_inst%waterdiagnosticbulk_inst, this%input%frictionvel_inst) + do p = bounds%begp, bounds%endp + call this%dust_emis%GetPatchVars( p, flx_mss_vrt_dst_tot=flx_mss_vrt_dst_tot ) + @assertEqual( flx_mss_vrt_dst_tot, 0.0_r8 ) + if ( i == 1 )then + previous_dst_tot = flx_mss_vrt_dst_tot + else + ! Verify that the result with nan is equal to the result with the tiny value as expected + @assertEqual( flx_mss_vrt_dst_tot, previous_dst_tot ) + end if + end do + end do + + end subroutine dust_handles_dpfct_rock_nan + + !----------------------------------------------------------------------- @Test subroutine check_dust_emis_increasing_fv(this) ! Check dust emissions with increasing friction velocity From 1fe61e623a20fe94e9221d2e405d8fcc3d7263b4 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sat, 10 Aug 2024 14:19:33 -0600 Subject: [PATCH 389/406] Now passes the unit-test --- src/biogeochem/DustEmisLeung2023.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/biogeochem/DustEmisLeung2023.F90 b/src/biogeochem/DustEmisLeung2023.F90 index 5f92d58425..bab1eba4ed 100644 --- a/src/biogeochem/DustEmisLeung2023.F90 +++ b/src/biogeochem/DustEmisLeung2023.F90 @@ -14,7 +14,7 @@ module DustEmisLeung2023 ! !USES: use shr_kind_mod , only : r8 => shr_kind_r8 use shr_log_mod , only : errMsg => shr_log_errMsg - use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=) + use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=), shr_infnan_isnan use clm_varpar , only : dst_src_nbr, ndst use clm_varcon , only : grav, spval use landunit_varcon , only : istcrop, istsoil @@ -656,7 +656,7 @@ subroutine DustEmission (this, bounds, & ! calculation of the hybrid/total drag partition effect considering both rock and vegetation drag partitioning using LUH2 bare and veg fractions within a grid if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then if (patch%itype(p) == noveg) then ! if bare, uses rock drag partition factor - if (dpfct_rock(p) /= dpfct_rock(p)) then ! dmleung added 24 May 2024: dpfct_rock(p) could be NaN; CLM could run when DEBUG=FALSE in env_build.xml but dies when DEBUG=TRUE (usually when checking if wnd_frc_slt > wnd_frc_thr_slt_it and if numer/denom < 30._r8 below) + if (shr_infnan_isnan(dpfct_rock(p)) ) then ! dmleung added 24 May 2024: dpfct_rock(p) could be NaN; CLM could run when DEBUG=FALSE in env_build.xml but dies when DEBUG=TRUE (usually when checking if wnd_frc_slt > wnd_frc_thr_slt_it and if numer/denom < 30._r8 below) frc_thr_rgh_fct = 0.001_r8 ! Set drag partition effect to be a very small value (or zero) such that there is no emission whenever dpfct_rock(p) = NaN; dmleung 24 May 2024 else frc_thr_rgh_fct = dpfct_rock(p) From 1895cba46af487a6dcd84c80add4d97559929efe Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sat, 10 Aug 2024 15:40:58 -0600 Subject: [PATCH 390/406] Add obu to create_fv, and add test for obu of zero, and error handling for it (since downstream values can be corrupted if it's NaN or zero), also use shr_infnan_isnan for intrmtncy_fct --- src/biogeochem/DustEmisLeung2023.F90 | 6 ++++- .../DustEmis_test/test_DustEmisLeung2023.pf | 23 ++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/biogeochem/DustEmisLeung2023.F90 b/src/biogeochem/DustEmisLeung2023.F90 index bab1eba4ed..4c80c3b8ca 100644 --- a/src/biogeochem/DustEmisLeung2023.F90 +++ b/src/biogeochem/DustEmisLeung2023.F90 @@ -718,6 +718,10 @@ subroutine DustEmission (this, bounds, & ! mean lowpass-filtered wind speed at hgt_sal = 0.1 m saltation height (assuming aerodynamic roughness length z0a_glob = 1e-4 m globally for ease; also assuming neutral condition) u_mean_slt(p) = (wnd_frc_slt/k) * log(hgt_sal / z0a_glob) ! translating from ustar (velocity scale) to actual wind + if ( obu(p) == 0.0_r8 )then + call endrun(msg='Input obu is zero and can NOT be' ) + return + end if stblty(p) = zii / obu(p) ! -dmleung 20 Feb 2024: use obu from CTSM and PBL height = zii (= 1000_r8) which is default in CTSM. Should we transfer PBL height from CAM? if ((12_r8 - 0.5_r8 * stblty(p)) .GE. 0.001_r8) then ! should have used 0 theoretically; used 0.001 here to avoid undefined values u_sd_slt(p) = wnd_frc_slt * (12_r8 - 0.5_r8 * stblty(p))**0.333_r8 @@ -751,7 +755,7 @@ subroutine DustEmission (this, bounds, & intrmtncy_fct(p) = 1.0_r8 - prb_crs_fld_thr(p) + thr_crs_rate(p) * (prb_crs_fld_thr(p) - prb_crs_impct_thr(p)) ! multiply dust emission flux by intermittency factor - if (intrmtncy_fct(p) /= intrmtncy_fct(p)) then ! if intrmtncy_fct(p) is not NaN then multiply by intermittency factor; this statement is needed because dust emission flx_mss_vrt_dst_ttl(p) has to be non NaN (at least zero) to be outputted + if ( shr_infnan_isnan(intrmtncy_fct(p)) ) then ! if intrmtncy_fct(p) is not NaN then multiply by intermittency factor; this statement is needed because dust emission flx_mss_vrt_dst_ttl(p) has to be non NaN (at least zero) to be outputted flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) else flx_mss_vrt_dst_ttl(p) = flx_mss_vrt_dst_ttl(p) * intrmtncy_fct(p) ! multiply dust flux by intermittency diff --git a/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf b/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf index 7aac658d33..66f0191c06 100644 --- a/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf +++ b/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf @@ -109,7 +109,7 @@ contains call this%dust_emis%GetPatchVars( p, flx_mss_vrt_dst_tot=flx_mss_vrt_dst_tot, & vlc_trb_1=vlc_trb_1, vlc_trb_2=vlc_trb_2, vlc_trb_3=vlc_trb_3, & vlc_trb_4=vlc_trb_4) - @assertEqual( flx_mss_vrt_dst_tot, 1.305341724414137d-006, tolerance=tol ) + @assertEqual( flx_mss_vrt_dst_tot, 1.305340725852736d-006, tolerance=tol ) @assertEqual( vlc_trb_1, 3.407721147709135d-003, tolerance=tol ) @assertEqual( vlc_trb_2, 4.961153753164878d-003, tolerance=tol ) @assertEqual( vlc_trb_3, 4.980100969983446d-003, tolerance=tol ) @@ -287,6 +287,23 @@ contains end subroutine dust_handles_dpfct_rock_nan + !----------------------------------------------------------------------- + @Test + subroutine dust_aborts_on_zero_obu(this) + use PatchType, only : patch + ! Check dust emissions can handle dpftc_rock being set to NaN + class(TestDustEmisLeung2023), intent(inout) :: this + character(100) :: expected_msg + + call this%input%create_atm2lnd() + call this%input%create_fv( obu=0.0_r8 ) + call this%dust_emis%DustEmission(bounds, this%input%num_nolakep, this%input%filter_nolakep, this%input%atm2lnd_inst, & + this%input%soilstate_inst, this%input%canopystate_inst, this%input%water_inst%waterstatebulk_inst, & + this%input%water_inst%waterdiagnosticbulk_inst, this%input%frictionvel_inst) + expected_msg = "ABORTED: Input obu is zero and can NOT be" + @assertExceptionRaised(expected_msg) + + end subroutine dust_aborts_on_zero_obu !----------------------------------------------------------------------- @Test subroutine check_dust_emis_increasing_fv(this) @@ -309,7 +326,7 @@ contains call this%validate_patch(p) call this%dust_emis%GetPatchVars( p, flx_mss_vrt_dst_tot=flx_mss_vrt_dst_tot ) total_dust0 = flx_mss_vrt_dst_tot - @assertEqual( flx_mss_vrt_dst_tot, 1.064145669761461d-5, tolerance=tol ) + @assertEqual( flx_mss_vrt_dst_tot, 1.064145664977026d-5, tolerance=tol ) end do ! Double fv and show result is higher call this%input%create_fv( u10=u10, fv=fv*2.0_r8) @@ -321,7 +338,7 @@ contains call this%validate_patch(p) call this%dust_emis%GetPatchVars( p, flx_mss_vrt_dst_tot=flx_mss_vrt_dst_tot ) total_dust_higher = flx_mss_vrt_dst_tot - @assertEqual( flx_mss_vrt_dst_tot, 8.309603073613188d-5, tolerance=tol ) + @assertEqual( flx_mss_vrt_dst_tot, 8.309603071671919d-5, tolerance=tol ) end do @assertGreaterThan( total_dust_higher, total_dust0 ) From d42679740bbcec5dfd3c29721b5b359197508cd2 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sat, 10 Aug 2024 18:53:07 -0600 Subject: [PATCH 391/406] Set obu in create_fv since it's needed for the Leung method --- src/unit_test_shr/unittestDustEmisInputs.F90 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/unit_test_shr/unittestDustEmisInputs.F90 b/src/unit_test_shr/unittestDustEmisInputs.F90 index 6f1a7b09f7..771eb410f7 100644 --- a/src/unit_test_shr/unittestDustEmisInputs.F90 +++ b/src/unit_test_shr/unittestDustEmisInputs.F90 @@ -202,17 +202,19 @@ end subroutine create_atm2lnd !----------------------------------------------------------------------- - subroutine create_fv(this, fv, u10, ram1) + subroutine create_fv(this, fv, u10, ram1, obu) ! Initializes some fields needed for dust emissions in this%frictionvel_inst, and sets ! fields based on inputs. Excluded inputs are given a default value class(unittest_dust_emis_input_type), intent(inout) :: this real(r8), intent(in), optional :: fv real(r8), intent(in), optional :: u10 real(r8), intent(in), optional :: ram1 + real(r8), intent(in), optional :: obu real(r8), parameter :: fv_default = 2.0_r8 real(r8), parameter :: u10_default = 4._r8 real(r8), parameter :: ram1_default = 200._r8 + real(r8), parameter :: obu_default = -100._r8 ! ------------------------------------------------------------------------ if (present(fv)) then @@ -233,6 +235,12 @@ subroutine create_fv(this, fv, u10, ram1) this%frictionvel_inst%ram1_patch(bounds%begp:bounds%endp) = ram1_default end if + if (present(obu)) then + this%frictionvel_inst%obu_patch(bounds%begp:bounds%endp) = obu + else + this%frictionvel_inst%obu_patch(bounds%begp:bounds%endp) = obu_default + end if + end subroutine create_fv !----------------------------------------------------------------------- From f29bfb42ed99ccbbf3d9d5955721af21c2787436 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sat, 10 Aug 2024 18:54:42 -0600 Subject: [PATCH 392/406] Add a couple more asserts for MassFracClayLeung which helps clarify it's behavior --- .../test_dust_soil_clay_functions.pf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/biogeophys/test/SoilStateInitTimeConst_test/test_dust_soil_clay_functions.pf b/src/biogeophys/test/SoilStateInitTimeConst_test/test_dust_soil_clay_functions.pf index 53a3d66987..d2d458994c 100644 --- a/src/biogeophys/test/SoilStateInitTimeConst_test/test_dust_soil_clay_functions.pf +++ b/src/biogeophys/test/SoilStateInitTimeConst_test/test_dust_soil_clay_functions.pf @@ -71,8 +71,11 @@ contains value = MassFracClay( 0.0_r8 ) @assertEqual( value, 0.0_r8, tolerance=tol ) + value = MassFracClay( 10.0_r8 ) + @assertEqual( value, 0.10_r8, tolerance=tol ) value = MassFracClay( 20.0_r8 ) @assertEqual( value, 0.20_r8, tolerance=tol ) + ! value after 20% clay should stay at 0.2 value = MassFracClay( 25.0_r8 ) @assertEqual( value, 0.20_r8, tolerance=tol ) @@ -86,8 +89,11 @@ contains value = MassFracClayLeung2023( 0.0_r8 ) @assertEqual( value, 0.1_r8, tolerance=tol ) + value = MassFracClayLeung2023( 10.0_r8 ) + @assertEqual( value, 0.15_r8, tolerance=tol ) value = MassFracClayLeung2023( 20.0_r8 ) @assertEqual( value, 0.20_r8, tolerance=tol ) + ! value after 20% clay should stay at 0.2 value = MassFracClayLeung2023( 25.0_r8 ) @assertEqual( value, 0.20_r8, tolerance=tol ) From 601b090980fc1bd59f00f06f190a65b6041c00b6 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sat, 10 Aug 2024 23:50:53 -0600 Subject: [PATCH 393/406] Add a test that low stabiility is constant dust emission output, change tolerance to accomidate that, use asserts rather than endrun in the test code, increase fv and obu for one of the tests --- .../DustEmis_test/test_DustEmisLeung2023.pf | 50 +++++++++++++++++-- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf b/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf index 66f0191c06..31adea76e9 100644 --- a/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf +++ b/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf @@ -7,7 +7,6 @@ module test_DustEmisLeung2023 use unittestSubgridMod, only : bounds use DustEmisBase use DustEmisLeung2023 - use abortutils, only : endrun use shr_kind_mod , only : r8 => shr_kind_r8 use shr_infnan_mod, only : nan => shr_infnan_nan, assignment(=) use DustEmisFactory, only : create_dust_emissions @@ -15,7 +14,7 @@ module test_DustEmisLeung2023 implicit none - real(r8), parameter :: tol = 1.e-18_r8 + real(r8), parameter :: tol = 1.e-16_r8 @TestCase type, extends(TestCase) :: TestDustEmisLeung2023 @@ -259,7 +258,7 @@ contains real(r8) :: flx_mss_vrt_dst_tot, drag_partition, previous_dst_tot call this%input%create_atm2lnd() - call this%input%create_fv( ) + call this%input%create_fv( obu = -500.0_r8, fv=15.0_r8 ) ! Loop over twice first with nan and then with the tiny value that nan should be changed to do i = 1, 2 if ( i == 1 )then @@ -267,7 +266,7 @@ contains else if ( i == 2 ) then drag_partition = 0.001 else - call endrun( "Looping over too many iterations should just be 2" ) + @assertEqual( i, 1, "Iteration too high, should just be up to 2" ) end if call this%dust_emis%SetDragPartition(bounds, drag_partition) call this%dust_emis%DustEmission(bounds, this%input%num_nolakep, this%input%filter_nolakep, this%input%atm2lnd_inst, & @@ -287,7 +286,48 @@ contains end subroutine dust_handles_dpfct_rock_nan - !----------------------------------------------------------------------- + !----------------------------------------------------------------------- + + @Test + subroutine dust_constant_low_stability(this) + use PatchType, only : patch + ! Check that dust emissions stay constant until stability high enough + class(TestDustEmisLeung2023), intent(inout) :: this + integer :: p, i + real(r8) :: flx_mss_vrt_dst_tot, obu, previous_dst_tot + + call this%input%create_atm2lnd() + do i = 1, 3 + if ( i == 1 )then + obu = 100._r8 + else if ( i == 2 ) then + obu = 50._r8 + else if ( i == 3 ) then + obu = 41.67013917826486_r8 + else + @assertEqual( i, 1, "Iteration too high, should just be up to 3" ) + end if + call this%input%create_fv( obu=obu ) + call this%dust_emis%DustEmission(bounds, this%input%num_nolakep, this%input%filter_nolakep, this%input%atm2lnd_inst, & + this%input%soilstate_inst, this%input%canopystate_inst, this%input%water_inst%waterstatebulk_inst, & + this%input%water_inst%waterdiagnosticbulk_inst, this%input%frictionvel_inst) + do p = bounds%begp, bounds%endp + call this%dust_emis%GetPatchVars( p, flx_mss_vrt_dst_tot=flx_mss_vrt_dst_tot ) + print *, ' obu = ', obu, ' dust_tot = ', flx_mss_vrt_dst_tot + @assertEqual( flx_mss_vrt_dst_tot, 1.305341766366198d-006, tolerance=tol ) + if ( i == 1 )then + previous_dst_tot = flx_mss_vrt_dst_tot + else + ! Verify that the previous result is identical to the others + @assertEqual( flx_mss_vrt_dst_tot, previous_dst_tot, tolerance=tol ) + end if + end do + end do + + end subroutine dust_constant_low_stability + + !----------------------------------------------------------------------- + @Test subroutine dust_aborts_on_zero_obu(this) use PatchType, only : patch From 02b4f9ab051efdbaab30644d1799724b1574c233 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sun, 11 Aug 2024 00:17:51 -0600 Subject: [PATCH 394/406] Change constants so they are more recognizable as floating point with a decimal, and remove subname in favor of giving line and filename with errmsg --- src/biogeochem/DustEmisLeung2023.F90 | 86 +++++++++---------- .../DustEmis_test/test_DustEmisLeung2023.pf | 10 +-- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/biogeochem/DustEmisLeung2023.F90 b/src/biogeochem/DustEmisLeung2023.F90 index 4c80c3b8ca..869c18c0dc 100644 --- a/src/biogeochem/DustEmisLeung2023.F90 +++ b/src/biogeochem/DustEmisLeung2023.F90 @@ -215,87 +215,87 @@ subroutine InitHistory(this, bounds) this%dst_emiss_coeff_patch(begp:endp) = spval call hist_addfld1d (fname='DUST_EMIS_COEFF', units='dimensionless', & avgflag='A', long_name='soil erodibility or dust emission coefficient for Kok emission scheme', & - ptr_patch=this%dst_emiss_coeff_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%dst_emiss_coeff_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%wnd_frc_thr_patch(begp:endp) = spval call hist_addfld1d (fname='WND_FRC_FT', units='m/s', & avgflag='A', long_name='fluid threshold friction velocity', & - ptr_patch=this%wnd_frc_thr_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%wnd_frc_thr_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%wnd_frc_thr_dry_patch(begp:endp) = spval call hist_addfld1d (fname='WND_FRC_FT_DRY', units='m/s', & avgflag='A', long_name='dry fluid threshold friction velocity', & - ptr_patch=this%wnd_frc_thr_dry_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%wnd_frc_thr_dry_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%wnd_frc_thr_it_patch(begp:endp) = spval call hist_addfld1d (fname='WND_FRC_IT', units='m/s', & avgflag='A', long_name='impact threshold friction velocity', & - ptr_patch=this%wnd_frc_thr_it_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%wnd_frc_thr_it_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%wnd_frc_soil_patch(begp:endp) = spval call hist_addfld1d (fname='WND_FRC_SOIL', units='m/s', & avgflag='A', long_name='soil surface wind friction velocity', & - ptr_patch=this%wnd_frc_soil_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%wnd_frc_soil_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%lnd_frc_mble_patch(begp:endp) = spval call hist_addfld1d (fname='LND_FRC_MBLE', units='dimensionless', & avgflag='A', long_name='land mobile fraction', & - ptr_patch=this%lnd_frc_mble_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%lnd_frc_mble_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%gwc_patch(begp:endp) = spval call hist_addfld1d (fname='GWC', units='kg/kg', & avgflag='A', long_name='gravimetric soil moisture at the topmost soil layer', & - ptr_patch=this%gwc_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%gwc_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%liq_frac_patch(begp:endp) = spval call hist_addfld1d (fname='LIQ_FRAC', units='dimensionless', & avgflag='A', long_name='fraction of total water that is liquid', & - ptr_patch=this%liq_frac_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%liq_frac_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%u_mean_slt_patch(begp:endp) = spval call hist_addfld1d (fname='U_S_MEAN', units='m/s', & avgflag='A', long_name='mean wind velocity at saltation level', & - ptr_patch=this%u_mean_slt_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%u_mean_slt_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%u_sd_slt_patch(begp:endp) = spval call hist_addfld1d (fname='U_S_SIGMA', units='m/s', & avgflag='A', long_name='sd of wind velocity at saltation level', & - ptr_patch=this%u_sd_slt_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%u_sd_slt_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%stblty_patch(begp:endp) = spval call hist_addfld1d (fname='ZETAOBU', units='', & avgflag='A', long_name='stability parameter', & - ptr_patch=this%stblty_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%stblty_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%u_fld_thr_patch(begp:endp) = spval call hist_addfld1d (fname='U_FT', units='m/s', & avgflag='A', long_name='fluid threshold velocity at saltation level', & - ptr_patch=this%u_fld_thr_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%u_fld_thr_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%u_impct_thr_patch(begp:endp) = spval call hist_addfld1d (fname='U_IT', units='m/s', & avgflag='A', long_name='impact threshold velocity at saltation level', & - ptr_patch=this%u_impct_thr_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%u_impct_thr_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%thr_crs_rate_patch(begp:endp) = spval call hist_addfld1d (fname='ALPHA_TC_RATE', units='', & avgflag='A', long_name='threshold crossing rate', & - ptr_patch=this%thr_crs_rate_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%thr_crs_rate_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%prb_crs_fld_thr_patch(begp:endp) = spval call hist_addfld1d (fname='P_FT', units='', & avgflag='A', long_name='probability of winds crossing fluid threshold', & - ptr_patch=this%prb_crs_fld_thr_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%prb_crs_fld_thr_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%prb_crs_impct_thr_patch(begp:endp) = spval call hist_addfld1d (fname='P_IT', units='', & avgflag='A', long_name='probability of winds crossing impact threshold', & - ptr_patch=this%prb_crs_impct_thr_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%prb_crs_impct_thr_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%intrmtncy_fct_patch(begp:endp) = spval call hist_addfld1d (fname='ETA', units='', & avgflag='A', long_name='intermittency factor', & - ptr_patch=this%intrmtncy_fct_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%intrmtncy_fct_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%ssr_patch(begp:endp) = spval call hist_addfld1d (fname='SSR', units='m/s', & avgflag='A', long_name='Okin-Pierre vegetation shear stress ratio (drag partition factor)', & - ptr_patch=this%ssr_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%ssr_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%vai_Okin_patch(begp:endp) = spval call hist_addfld1d (fname='VAI_OKIN', units='m/s', & avgflag='A', long_name='vegetation area index used in the Okin-Pierre plant drag partition scheme', & - ptr_patch=this%vai_Okin_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%vai_Okin_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%frc_thr_rghn_fct_patch(begp:endp) = spval call hist_addfld1d (fname='FRC_THR_RGHN_FCT', units='dimensionless', & avgflag='A', long_name='hybrid drag partition (or roughness) factor', & - ptr_patch=this%frc_thr_rghn_fct_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%frc_thr_rghn_fct_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%wnd_frc_thr_std_patch(begp:endp) = spval call hist_addfld1d (fname='WND_FRC_FT_STD', units='m/s', & avgflag='A', long_name='standardized fluid threshold friction velocity', & - ptr_patch=this%wnd_frc_thr_std_patch, set_lake=0._r8, set_urb=0._r8) + ptr_patch=this%wnd_frc_thr_std_patch, set_lake=0.0_r8, set_urb=0.0_r8) this%dpfct_rock_patch(begp:endp) = spval call hist_addfld1d (fname='DPFCT_ROCK', units='m/s', & avgflag='A', long_name='rock drag partition factor', & @@ -414,7 +414,6 @@ subroutine DustEmission (this, bounds, & real(r8), parameter :: zii = 1000.0_r8 ! [m] convective boundary layer height added by dmleung 20 Feb 2024, following other CTSM modules (e.g., CanopyFluxesMod). Should we transfer PBL height (PBLH) from CAM? real(r8) :: numer ! Numerator term for threshold crossing rate real(r8) :: denom ! Denominator term for threshold crossing rate - character(len=*),parameter :: subname = 'DUSTEmission' !------------------------------------------------------------------------ associate( & @@ -463,7 +462,7 @@ subroutine DustEmission (this, bounds, & wnd_frc_thr_std => this%wnd_frc_thr_std_patch & ! Output: standardized dust emission threshold friction velocity defined in Jasper Kok et al. (2014). ) - ttlai(bounds%begp : bounds%endp) = 0._r8 + ttlai(bounds%begp : bounds%endp) = 0.0_r8 ! make lai average at landunit level do fp = 1,num_nolakep p = filter_nolakep(fp) @@ -471,12 +470,12 @@ subroutine DustEmission (this, bounds, & enddo tlai_lu(bounds%begl : bounds%endl) = spval - sumwt(bounds%begl : bounds%endl) = 0._r8 + sumwt(bounds%begl : bounds%endl) = 0.0_r8 do p = bounds%begp,bounds%endp - if (ttlai(p) /= spval .and. patch%active(p) .and. patch%wtlunit(p) /= 0._r8) then + if (ttlai(p) /= spval .and. patch%active(p) .and. patch%wtlunit(p) /= 0.0_r8) then c = patch%column(p) l = patch%landunit(p) - if (sumwt(l) == 0._r8) tlai_lu(l) = 0._r8 + if (sumwt(l) == 0.0_r8) tlai_lu(l) = 0.0_r8 tlai_lu(l) = tlai_lu(l) + ttlai(p) * patch%wtlunit(p) sumwt(l) = sumwt(l) + patch%wtlunit(p) end if @@ -487,19 +486,20 @@ subroutine DustEmission (this, bounds, & found = .true. index = l exit - else if (sumwt(l) /= 0._r8) then + else if (sumwt(l) /= 0.0_r8) then tlai_lu(l) = tlai_lu(l)/sumwt(l) end if end do if (found) then - write(iulog,*) subname//':: error: sumwt is greater than 1.0 at l= ',index + write(iulog,*) 'error: sumwt is greater than 1.0 at l= ',index call endrun(subgrid_index=index, subgrid_level=subgrid_level_landunit, msg=errMsg(sourcefile, __LINE__)) + return end if ! Loop through patches ! initialize variables which get passed to the atmosphere - flx_mss_vrt_dst(bounds%begp:bounds%endp,:)=0._r8 + flx_mss_vrt_dst(bounds%begp:bounds%endp,:)=0.0_r8 do fp = 1,num_nolakep p = filter_nolakep(fp) @@ -584,7 +584,7 @@ subroutine DustEmission (this, bounds, & ! calculate soil moisture effect for dust emission threshold ! following Fecan, Marticorena et al. (1999) ! also see Zender et al. (2003) for DUST emission scheme and Kok et al. (2014b) for K14 emission scheme in CESM - bd = (1._r8-watsat(c,1))*dns_slt ![kg m-3] Bulk density of dry surface soil (dmleung changed from 2700 to dns_slt, soil particle density, on 16 Feb 2024. Note that dn s_slt=2650 kg m-3 so the value is changed by a tiny bit from 2700 to 2650. dns_slt has been here for many years so dns_slt should be used here instead of explicitly typing the value out. dmleung 16 Feb 2024) + bd = (1.0_r8-watsat(c,1))*dns_slt ![kg m-3] Bulk density of dry surface soil (dmleung changed from 2700 to dns_slt, soil particle density, on 16 Feb 2024. Note that dn s_slt=2650 kg m-3 so the value is changed by a tiny bit from 2700 to 2650. dns_slt has been here for many years so dns_slt should be used here instead of explicitly typing the value out. dmleung 16 Feb 2024) ! use emission threshold to calculate standardized threshold and dust emission coefficient @@ -644,19 +644,19 @@ subroutine DustEmission (this, bounds, & if (lnd_frc_mbl(p) > 0.0_r8 .AND. tlai_lu(l)<= vai_mbl_thr) then vai_Okin(p) = tlai_lu(l)+vai0_Okin ! LAI+SAI averaged to landunit level; the equation is undefined at lai=0, and LAI in CTSM has some zeros over deserts, so we add in a small number. - if (vai_Okin(p) > 1_r8) then - vai_Okin(p) = 1_r8 ! setting LAI = 1 to be a max value (since K_length goes to negative when LAI>1) + if (vai_Okin(p) > 1.0_r8) then + vai_Okin(p) = 1.0_r8 ! setting LAI = 1 to be a max value (since K_length goes to negative when LAI>1) end if ! calculate Okin's shear stress ratio (SSR, which is vegetation drag partition factor) using Pierre's equation - K_length = 2_r8 * (1_r8/vai_Okin(p) - 1_r8) ! Here LAI has to be non-zero to avoid blowup, and < 1 to avoid -ve K_length. See this equation in Leung et al. (2023). This line is Okin's formulation + K_length = 2.0_r8 * (1.0_r8/vai_Okin(p) - 1.0_r8) ! Here LAI has to be non-zero to avoid blowup, and < 1 to avoid -ve K_length. See this equation in Leung et al. (2023). This line is Okin's formulation ssr(p) = (K_length+f_0*c_e)/(K_length+c_e) ! see this equation in Caroline Pierre et al. (2014) or Leung et al. (2023). This line is Pierre's formulation. ! calculation of the hybrid/total drag partition effect considering both rock and vegetation drag partitioning using LUH2 bare and veg fractions within a grid if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then if (patch%itype(p) == noveg) then ! if bare, uses rock drag partition factor - if (shr_infnan_isnan(dpfct_rock(p)) ) then ! dmleung added 24 May 2024: dpfct_rock(p) could be NaN; CLM could run when DEBUG=FALSE in env_build.xml but dies when DEBUG=TRUE (usually when checking if wnd_frc_slt > wnd_frc_thr_slt_it and if numer/denom < 30._r8 below) + if (shr_infnan_isnan(dpfct_rock(p)) ) then ! dmleung added 24 May 2024: dpfct_rock(p) could be NaN; CLM could run when DEBUG=FALSE in env_build.xml but dies when DEBUG=TRUE (usually when checking if wnd_frc_slt > wnd_frc_thr_slt_it and if numer/denom < 30 below) frc_thr_rgh_fct = 0.001_r8 ! Set drag partition effect to be a very small value (or zero) such that there is no emission whenever dpfct_rock(p) = NaN; dmleung 24 May 2024 else frc_thr_rgh_fct = dpfct_rock(p) @@ -723,8 +723,8 @@ subroutine DustEmission (this, bounds, & return end if stblty(p) = zii / obu(p) ! -dmleung 20 Feb 2024: use obu from CTSM and PBL height = zii (= 1000_r8) which is default in CTSM. Should we transfer PBL height from CAM? - if ((12_r8 - 0.5_r8 * stblty(p)) .GE. 0.001_r8) then ! should have used 0 theoretically; used 0.001 here to avoid undefined values - u_sd_slt(p) = wnd_frc_slt * (12_r8 - 0.5_r8 * stblty(p))**0.333_r8 + if ((12.0_r8 - 0.5_r8 * stblty(p)) .GE. 0.001_r8) then ! should have used 0 theoretically; used 0.001 here to avoid undefined values + u_sd_slt(p) = wnd_frc_slt * (12.0_r8 - 0.5_r8 * stblty(p))**0.333_r8 else u_sd_slt(p) = wnd_frc_slt * (0.001_r8)**0.333_r8 ! should have used 0 theoretically; used 0.001 here to avoid undefined values end if @@ -740,7 +740,7 @@ subroutine DustEmission (this, bounds, & numer = (u_fld_thr(p)**2.0_r8 - u_impct_thr(p)**2.0_r8 - 2.0_r8 * u_mean_slt(p) * (u_fld_thr(p) - u_impct_thr(p))) denom = (2.0_r8 * u_sd_slt(p)**2.0_r8) ! note that u_sd_slt should be always positive ! Truncate to zero if the expression inside exp is becoming too large - if ( numer/denom < 30._r8 ) then ! set numer/denom to be < 30 given exp(30) below is already very large; also denom itself should be non-zero and non-negative given the standard deviation (u_sd_slt) of the subtimestep wind fluctuation is non-negative. dmleung 28 May 2024 + if ( numer/denom < 30.0_r8 ) then ! set numer/denom to be < 30 given exp(30) below is already very large; also denom itself should be non-zero and non-negative given the standard deviation (u_sd_slt) of the subtimestep wind fluctuation is non-negative. dmleung 28 May 2024 thr_crs_rate(p) = (exp((u_fld_thr(p)**2.0_r8 - u_impct_thr(p)**2.0_r8 - 2.0_r8 * u_mean_slt(p) * (u_fld_thr(p) - u_impct_thr(p))) / (2.0_r8 * u_sd_slt(p)**2.0_r8)) + 1.0_r8)**(-1.0_r8) else thr_crs_rate(p) = 0.0_r8 @@ -829,20 +829,20 @@ subroutine CalcDragPartition(this, bounds) ! estimating roughness effect at a distance of 10 m following Leung et al. (2023) real(r8), parameter :: b1 = 0.7_r8 ! [dimless] first fitting coefficient for the drag partition equation by Marticorena and Bergametti (1995), later modified by Darmenova et al. (2009). real(r8), parameter :: b2 = 0.8_r8 ! [dimless] second fitting coefficient for the drag partition equation by Marticorena and Bergametti (1995), later modified by Darmenova et al. (2009). - character(len=*), parameter :: subname = 'PrigentRoughnessStream::CalcDragPartition' !--------------------------------------------------------------------- ! Make sure we've initialized the Prigent roughness streams if ( .not. this%prigent_roughness_stream%IsStreamInit() )then - call endrun(msg=subname//' ERROR: Streams have not been initialized, make sure Init is called first' & - //', and streams are on') + write(iulog,*)'Error : Prigent roughness stream is NOT on: ', errMsg(sourcefile, __LINE__) + call endrun(msg=' ERROR: Streams have not been initialized, make sure Init is called first' & + //', and streams are on') + return end if ! dmleung: this loop calculates the drag partition effect (or roughness effect) of rocks. ! We save the drag partition factor as a patch level quantity. ! TODO: EBK 02/13/2024: Several magic numbers here that should become parameters so the meaning is preserved - !z0s = 2_r8/30_r8 * D_p ! equation for smooth roughness length for soil grain. See Danny M. Leung et al. (2023) and Martina Klose et al. (2021) for instance. 1/15 is a coefficient that relates roughness to soil particle diameter D_p. - z0s = 2_r8 * D_p / 30_r8 ! equation for smooth roughness length for soil grain. See Danny M. Leung et al. (2023) and Martina Klose et al. (2021) for instance. 1/15 is a coefficient that relates roughness to soil particle diameter D_p. + z0s = 2.0_r8 * D_p / 30.0_r8 ! equation for smooth roughness length for soil grain. See Danny M. Leung et al. (2023) and Martina Klose et al. (2021) for instance. 1/15 is a coefficient that relates roughness to soil particle diameter D_p. ! Here we assume soil medium size is a global constant, and so is smooth roughness length. do p = bounds%begp,bounds%endp g = patch%gridcell(p) @@ -850,7 +850,7 @@ subroutine CalcDragPartition(this, bounds) if (lun%itype(l) /= istdlak) then ! Calculating rock drag partition factor using the Marticorena and Bergametti (1995) formulation. ! 0.01 is used to convert Prigent's roughness length dataset from centimeter to meter. - this%dpfct_rock_patch(p) = 1._r8 - ( log(this%prigent_roughness_stream%prigent_rghn(g)*0.01_r8/z0s) & + this%dpfct_rock_patch(p) = 1.0_r8 - ( log(this%prigent_roughness_stream%prigent_rghn(g)*0.01_r8/z0s) & / log(b1 * (X/z0s)**b2 ) ) end if end do diff --git a/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf b/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf index 31adea76e9..77e0cba4c0 100644 --- a/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf +++ b/src/biogeochem/test/DustEmis_test/test_DustEmisLeung2023.pf @@ -121,7 +121,7 @@ contains @Test subroutine dust_zero_for_fixed_ratio(this) - ! Check dust emissions are zero for a no wind + ! Check dust emissions are zero for a fixed ratio class(TestDustEmisLeung2023), intent(inout) :: this integer :: p real(r8) :: flx_mss_vrt_dst_tot @@ -130,7 +130,7 @@ contains call this%input%create_atm2lnd() call this%dust_emis%GetConstVars( SaltationFactor ) - ! Figure out what fv needs to be so that the wind threshold will be u10*(1/(1-eps)) + ! Figure out what fv needs to be so that the wind threshold will result in zero dust emission fv = ( SaltationFactor / sqrt( this%input%atm2lnd_inst%forc_rho_downscaled_col(bounds%begc)) ) - 1.d-15 call this%input%create_fv( fv=fv ) call this%dust_emis%DustEmission(bounds, this%input%num_nolakep, this%input%filter_nolakep, this%input%atm2lnd_inst, & @@ -274,6 +274,7 @@ contains this%input%water_inst%waterdiagnosticbulk_inst, this%input%frictionvel_inst) do p = bounds%begp, bounds%endp call this%dust_emis%GetPatchVars( p, flx_mss_vrt_dst_tot=flx_mss_vrt_dst_tot ) + ! TODO: To have a more robust test the input should be adjusted so that there is dust emissions rather than zero @assertEqual( flx_mss_vrt_dst_tot, 0.0_r8 ) if ( i == 1 )then previous_dst_tot = flx_mss_vrt_dst_tot @@ -352,12 +353,11 @@ contains integer :: p, c real(r8) :: flx_mss_vrt_dst_tot real(r8) :: fv = 4.0_r8 - real(r8) :: u10 = 10._r8 real(r8) :: total_dust0, total_dust_higher ! Run baseline fv call this%input%create_atm2lnd() - call this%input%create_fv( u10=u10, fv=fv ) + call this%input%create_fv( fv=fv ) call this%dust_emis%DustEmission(bounds, this%input%num_nolakep, this%input%filter_nolakep, this%input%atm2lnd_inst, & this%input%soilstate_inst, this%input%canopystate_inst, this%input%water_inst%waterstatebulk_inst, & this%input%water_inst%waterdiagnosticbulk_inst, this%input%frictionvel_inst) @@ -369,7 +369,7 @@ contains @assertEqual( flx_mss_vrt_dst_tot, 1.064145664977026d-5, tolerance=tol ) end do ! Double fv and show result is higher - call this%input%create_fv( u10=u10, fv=fv*2.0_r8) + call this%input%create_fv( fv=fv*2.0_r8) call this%dust_emis%DustEmission(bounds, this%input%num_nolakep, this%input%filter_nolakep, this%input%atm2lnd_inst, & this%input%soilstate_inst, this%input%canopystate_inst, this%input%water_inst%waterstatebulk_inst, & this%input%water_inst%waterdiagnosticbulk_inst, this%input%frictionvel_inst) From 07a62b62f60c3f326c750cf477d2487d7832f04c Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sun, 11 Aug 2024 01:50:33 -0600 Subject: [PATCH 395/406] Update change files --- doc/ChangeLog | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++ doc/ChangeSum | 1 + 2 files changed, 113 insertions(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index f5cf28096c..e166e65dad 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,116 @@ =============================================================== +Tag name: ctsm5.2.019 +Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) +Date: Sun 11 Aug 2024 12:31:34 AM MDT +One-line Summary: Add in an additional dust emission method Leung_2023, by default off + +Purpose and description of changes +---------------------------------- + +Author: Danny Leung +This is a scheme that builds upon a switch of CESM2's default dust emission scheme (Zender et al., 2003) to +@jfkok's more physical and less empirical one (Kok et al., 2014). This brings in additional modifications and add new aeolian +physics to the Kok's scheme, most notably, by adding the roughness effect (or called drag partition effect) which discounts surface +soil erosion by winds due to the presence of local-scale land-surface roughness elements (mostly plants and rocks). We use a hybrid +approach to account for both roughness from rocks (with a 2-D time-invariant dataset provided by Prigent et al., 2005; 2012) and +roughness from plants (time-varying, as a function of CLM's LAI). We further include the dust emission intermittency effects due to +boundary-layer turbulence. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm6_0 + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- + +List of CTSM issues fixed (include CTSM Issue # and description) [one per line]: + #1604 -- A new physically based dust emission scheme with more aeolian physics (updated) + +Notes of particular relevance for users +--------------------------------------- + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): New namelist items + dust_emis_method can now be set to Leung_2023 + + use_prigent_roughness ---------------- Logical to use the Prigent dataset (default on when Leung_2023 is used) + stream_fldfilename_prigentroughness -- Prigent streams dataset + stream_meshile_prigentroughness ------ Mesh file for the dataset + + History field FV can now be output for any compset, but it's default off + +Changes to the datasets (e.g., parameter, surface or initial files): + New Prigent streams dataset with aeolian roughness length + +Notes of particular relevance for developers: +--------------------------------------------- + +Caveats for developers (e.g., code that is duplicated that requires double maintenance): + Some issues will be worked on later + + #2568 -- Turn on Leung by default for clm6_0 physics + + #2381 -- Allow Prigent streams to be off in Leung_2023 dust emissions for Paleo work + (Fixing this will allow a unit-test module to be removed) + + #2680 -- prigentroughnessmapalgo is hardcoded to bilinear add it to the namelist so it can change (change name to snake-case) + + #2681 -- Add global dust variables to the DustEmisBase class that apply to both Zender and Leung dust emission methods bfb enhancement next + + #2682 -- Small function and saved constants for convective velocity code cleanup enhancement priority + +Changes to tests or testing: + New test mod that explicitly turns on Leung_2023 dust emissions: clm60cam7LndTuningModeLDust + Some of the tests with clm60cam7LndTuningMode were converted to this test mod + PF unit tester was added for Leung_2023 + + +Testing summary: regular +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + derecho - PASS + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + derecho - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + +Answer changes +-------------- + +Changes answers relative to baseline: no bit-for-bit (unless you turn the new dust scheme on) + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +(https://github.com/ESCOMP/ctsm/pull) + #1897 -- A new physically based dust emission scheme with more aeolian physics (updated) + +=============================================================== +=============================================================== Tag name: ctsm5.2.018 Originator(s): mvdebolskiy Date: Fri 02 Aug 2024 09:26:33 AM MDT diff --git a/doc/ChangeSum b/doc/ChangeSum index 3d9d70bc54..207dd158d6 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.2.019 erik 08/11/2024 Add in an additional dust emission method Leung_2023, by default off ctsm5.2.018 mvdebols 08/02/2024 Fix/excess ice cold start ctsm5.2.017 erik 07/30/2024 Dust emissions control moved to cmeps ctsm5.2.016 samrabin 07/27/2024 Enable new crop calendars for clm60 compsets From 0d17de5aeda75717a9ac969823c29e9051a49a56 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sun, 11 Aug 2024 03:04:52 -0600 Subject: [PATCH 396/406] Update test number --- bld/unit_testers/build-namelist_test.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index d7c94b8538..c51b0b0cb3 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -166,7 +166,7 @@ sub cat_and_create_namelistinfile { my $ntests = 3339; if ( defined($opts{'compare'}) ) { - $ntests += 1963; + $ntests += 1999; } plan( tests=>$ntests ); From 39ecaf0ff822f7ccbd1502a5040ff0becc0e8c5b Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sun, 11 Aug 2024 03:05:17 -0600 Subject: [PATCH 397/406] Update Change files --- doc/ChangeLog | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index e166e65dad..09c71a47df 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,7 +1,7 @@ =============================================================== Tag name: ctsm5.2.019 Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) -Date: Sun 11 Aug 2024 12:31:34 AM MDT +Date: Sun 11 Aug 2024 03:05:01 AM MDT One-line Summary: Add in an additional dust emission method Leung_2023, by default off Purpose and description of changes @@ -77,7 +77,7 @@ Changes to tests or testing: New test mod that explicitly turns on Leung_2023 dust emissions: clm60cam7LndTuningModeLDust Some of the tests with clm60cam7LndTuningMode were converted to this test mod PF unit tester was added for Leung_2023 - + More work could be done in the unit-testers to exercise more of the code Testing summary: regular ---------------- @@ -86,7 +86,7 @@ Testing summary: regular build-namelist tests (if CLMBuildNamelist.pm has changed): - derecho - PASS + derecho - PASS (1002 are different) python testing (if python code has changed; see instructions in python/README.md; document testing done): From 9b1ff2430442cb153aeafc285a75fd0da042deb9 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sun, 11 Aug 2024 03:13:38 -0600 Subject: [PATCH 398/406] Change default back to Zender --- bld/namelist_files/namelist_defaults_dust_emis.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bld/namelist_files/namelist_defaults_dust_emis.xml b/bld/namelist_files/namelist_defaults_dust_emis.xml index 319110cc55..40bf3d9078 100644 --- a/bld/namelist_files/namelist_defaults_dust_emis.xml +++ b/bld/namelist_files/namelist_defaults_dust_emis.xml @@ -14,7 +14,7 @@ attributes from the config_cache.xml file (with keys converted to upper-case). --> -Leung_2023 +Zender_2003 atm From b45a7fafd79b886a2582249d95f747a5998f7d78 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sun, 11 Aug 2024 08:59:33 -0600 Subject: [PATCH 399/406] Remove the PEM test with CISM for LDust since it changes answers --- cime_config/testdefs/testlist_clm.xml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index f4484c7410..cd4c41f23c 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -138,15 +138,6 @@ - - - - - - - - - From 503c47f084319f216dca499f0a5a93fd44a50c1c Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sun, 11 Aug 2024 09:02:29 -0600 Subject: [PATCH 400/406] Update change files --- doc/ChangeLog | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index 09c71a47df..92f4657f9d 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,7 +1,7 @@ =============================================================== Tag name: ctsm5.2.019 Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) -Date: Sun 11 Aug 2024 03:05:01 AM MDT +Date: Sun 11 Aug 2024 09:00:43 AM MDT One-line Summary: Add in an additional dust emission method Leung_2023, by default off Purpose and description of changes @@ -101,6 +101,7 @@ Answer changes -------------- Changes answers relative to baseline: no bit-for-bit (unless you turn the new dust scheme on) + Tests with BGC change answers because fieldlists are different (FV is no longer on history files by default) Other details ------------- From 2797829a586b13ba9db4f7266d33e034d60c7976 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 12 Aug 2024 11:12:21 -0600 Subject: [PATCH 401/406] Add comment explaining dummy use of crop_inst. --- src/biogeochem/CNFireLi2014Mod.F90 | 2 +- src/biogeochem/CNFireLi2016Mod.F90 | 2 +- src/biogeochem/CNFireLi2021Mod.F90 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/biogeochem/CNFireLi2014Mod.F90 b/src/biogeochem/CNFireLi2014Mod.F90 index a11e9d5edc..fcd27a7951 100644 --- a/src/biogeochem/CNFireLi2014Mod.F90 +++ b/src/biogeochem/CNFireLi2014Mod.F90 @@ -122,7 +122,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ class(soil_water_retention_curve_type), intent(in) :: soil_water_retention_curve type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst - type(crop_type) , intent(in) :: crop_inst + type(crop_type) , intent(in) :: crop_inst ! Dummy argument; not used in this version of CNFireArea real(r8) , intent(in) :: totlitc_col(bounds%begc:) real(r8) , intent(in) :: decomp_cpools_vr_col(bounds%begc:,1:,1:) real(r8) , intent(in) :: t_soi17cm_col(bounds%begc:) diff --git a/src/biogeochem/CNFireLi2016Mod.F90 b/src/biogeochem/CNFireLi2016Mod.F90 index 6021901a28..955d7fe398 100644 --- a/src/biogeochem/CNFireLi2016Mod.F90 +++ b/src/biogeochem/CNFireLi2016Mod.F90 @@ -124,7 +124,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ class(soil_water_retention_curve_type), intent(in) :: soil_water_retention_curve type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst - type(crop_type) , intent(in) :: crop_inst + type(crop_type) , intent(in) :: crop_inst ! Dummy argument; not used in this version of CNFireArea real(r8) , intent(in) :: totlitc_col(bounds%begc:) real(r8) , intent(in) :: decomp_cpools_vr_col(bounds%begc:,1:,1:) real(r8) , intent(in) :: t_soi17cm_col(bounds%begc:) diff --git a/src/biogeochem/CNFireLi2021Mod.F90 b/src/biogeochem/CNFireLi2021Mod.F90 index 74f553eb5c..6d504a1e26 100644 --- a/src/biogeochem/CNFireLi2021Mod.F90 +++ b/src/biogeochem/CNFireLi2021Mod.F90 @@ -124,7 +124,7 @@ subroutine CNFireArea (this, bounds, num_soilc, filter_soilc, num_soilp, filter_ class(soil_water_retention_curve_type), intent(in) :: soil_water_retention_curve type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst - type(crop_type) , intent(in) :: crop_inst + type(crop_type) , intent(in) :: crop_inst ! Dummy argument; not used in this version of CNFireArea real(r8) , intent(in) :: totlitc_col(bounds%begc:) real(r8) , intent(in) :: decomp_cpools_vr_col(bounds%begc:,1:,1:) real(r8) , intent(in) :: t_soi17cm_col(bounds%begc:) From 197ac0d5759343fdc53bd9699a30d4f0cec8983d Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Tue, 13 Aug 2024 08:10:09 -0600 Subject: [PATCH 402/406] update the FATES tag --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 19bc024208..4e9f5b51fb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,7 +28,7 @@ [submodule "fates"] path = src/fates url = https://github.com/NGEET/fates -fxtag = sci.1.77.1_api.36.0.0 +fxtag = sci.1.77.2_api.36.0.0 fxrequired = AlwaysRequired # Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed fxDONOTUSEurl = https://github.com/NCAR/fates-release From 13839f783221c6705a0ceadf3a5d382cf66d56d2 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Tue, 13 Aug 2024 15:18:37 -0600 Subject: [PATCH 403/406] Updating the fates pointer to sci.1.77.2_api.36.0.0 --- src/fates | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fates b/src/fates index 1982b0032c..e372f0b6cd 160000 --- a/src/fates +++ b/src/fates @@ -1 +1 @@ -Subproject commit 1982b0032c3cab6278892eccb85f643114ffb1af +Subproject commit e372f0b6cdf46f76f888ee6ac92d9a3572464e8b From ac85dbc04426b2649387bd19fc8de56ce8565a6d Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Tue, 13 Aug 2024 15:34:13 -0600 Subject: [PATCH 404/406] updated changlog --- doc/ChangeLog | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++ doc/ChangeSum | 1 + 2 files changed, 69 insertions(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index c1f5dfaa5a..af975c9975 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,72 @@ =============================================================== +Tag name: ctsm5.2.021 +Originator(s): rgknox (Ryan Knox,LAWRENCE BERKELEY NATIONAL LABORATORY,510-495-2153) +Date: Tue 13 Aug 2024 03:22:34 PM MDT +One-line Summary: Adding on-the-fly parameter settings for prescribed N and P in FATES + +Purpose and description of changes +---------------------------------- + +This set of changes makes it so that the FATES PRT2 test is forced to have prescribed N and P uptake in FATES. + +This allows the default to be coupled N and P, which is less confusing to new FATES-CNP users. + +The FATES tag is also updated to sci.1.77.2_api.36.0.0 which has a bug-fix for the output variable FATES_TRANSITION_MATRIX_LULU. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? NO + +Bugs fixed +---------- + + +List of CTSM issues fixed (include CTSM Issue # and description) [one per line]: + + +Notes of particular relevance for users +--------------------------------------- + +CLM-FATES with N and cycling is not fully coupled yet. Therefor, these changes are only relevant to the testing infrastructure. +Users are not encouraged to use fates_parteh_mode = 2 + +Notes of particular relevance for developers: +--------------------------------------------- + +Changes to tests or testing: Existing scripting that FATES has used in other tests to update parameters in certain tests, is now +being called for FatesColdPRT2 tests. This forces parameters fates_cnp_prescribed_nuptake and fates_cnp_prescribed_puptake to be 1. + + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + +Answer changes +-------------- + +Changes answers relative to baseline: NONE, except: + +FATES_TRANSITION_MATRIX_LULU reflects a BASELINE DIFF from a bug fix in two FATES tests. + +All other tests are B4B. + + +Other details +------------- + +FATES submodule was updated, pointing to tag sci.1.77.2_api.36.0.0 + +Pull Requests that document the changes (include PR ids): https://github.com/ESCOMP/CTSM/pull/2624 + + +=============================================================== +=============================================================== Tag name: ctsm5.2.020 Originator(s): HuiWangWanderInGitHub (Hui Wang,huiw16@uci.edu) Date: Mon 12 Aug 2024 10:51:04 AM MDT diff --git a/doc/ChangeSum b/doc/ChangeSum index d035fe4861..852ec9e838 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.2.021 rgknox 08/13/2024 Adding on-the-fly parameter settings for prescribed N and P in FATES ctsm5.2.020 slevis 08/12/2024 MEGAN updates (MEGAN-CLM6) ctsm5.2.019 erik 08/11/2024 Add in an additional dust emission method Leung_2023, by default off ctsm5.2.018 mvdebols 08/02/2024 Fix/excess ice cold start From 02885d4457a2f49e1742e6a46bc6276794ff085d Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 14 Aug 2024 12:31:40 -0600 Subject: [PATCH 405/406] Update ChangeLog and ChangeSum. --- doc/ChangeLog | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ doc/ChangeSum | 1 + 2 files changed, 72 insertions(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index af975c9975..5850069532 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,75 @@ =============================================================== +Tag name: ctsm5.2.022 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Wed Aug 14 12:24:27 MDT 2024 +One-line Summary: Rework crop_calendars suite and cropMonthOutput + +Purpose and description of changes +---------------------------------- + +- Changes cropMonthOutput testmod so that h2 files are actually monthly instead of daily +- Changes a nag test to use debug mode (fixing a build error) and run for shorter (fixing cputime exceedance) +- Reworks RxCropCals* testmods to not include crop testmod + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + +[ ] clm6_0 + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- +[Remove any lines that don't apply. Remove entire section if nothing applies.] + +List of CTSM issues fixed (include CTSM Issue # and description): +- Fixes ESCOMP/CTSM#2659: Test build failure: SMS_P128x1_Lm25.f10_f10_mg37.IHistClm60BgcCrop.izumi_nag.clm-RxCropCalsAdaptGGCMI (https://github.com/ESCOMP/CTSM/issues/2659) + + +Notes of particular relevance for developers: +--------------------------------------------- +NOTE: Be sure to review the steps in README.CHECKLIST.master_tags as well as the coding style in the Developers Guide + +Changes to tests or testing: +- Changes answers to all cropMonthOutput tests due to changed h2 file frequency. Also affects frequency of one other test. + + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + + +Answer changes +-------------- + +Changes answers relative to baseline: + - Only in testmods, not production runs + + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +- ESCOMP/CTSM#2676: Rework crop_calendars suite and cropMonthOutput (https://github.com/ESCOMP/CTSM/pull/2676) + +=============================================================== +=============================================================== Tag name: ctsm5.2.021 Originator(s): rgknox (Ryan Knox,LAWRENCE BERKELEY NATIONAL LABORATORY,510-495-2153) Date: Tue 13 Aug 2024 03:22:34 PM MDT diff --git a/doc/ChangeSum b/doc/ChangeSum index 852ec9e838..61bf4985de 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.2.022 samrabin 08/14/2024 Rework crop_calendars suite and cropMonthOutput ctsm5.2.021 rgknox 08/13/2024 Adding on-the-fly parameter settings for prescribed N and P in FATES ctsm5.2.020 slevis 08/12/2024 MEGAN updates (MEGAN-CLM6) ctsm5.2.019 erik 08/11/2024 Add in an additional dust emission method Leung_2023, by default off From 4f56ffcbbdd1d841de9edf5f67e625428282073d Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 16 Aug 2024 11:09:17 -0600 Subject: [PATCH 406/406] Update ChangeLog and ChangeSum. --- doc/ChangeLog | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++ doc/ChangeSum | 1 + 2 files changed, 76 insertions(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index 5850069532..7a6d764c14 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,79 @@ =============================================================== +Tag name: ctsm5.2.023 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Fri Aug 16 11:00:32 MDT 2024 +One-line Summary: Merge b4b-dev + +Purpose and description of changes +---------------------------------- + +Brings in 3 PRs from b4b-dev to master: +- Always check finidat_interp_dest.status (ESCOMP/CTSM#2679; Sam Rabin) +- Remove broken assign-to-project Github workflow (ESCOMP/CTSM#2683; Sam Rabin) +- [Fang summer '24 PR 1.1] Fix bugs and develop crop fire modeling (ESCOMP/CTSM#2684; Fang Li and Sam Rabin) + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + +[ ] clm6_0 + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- + +List of CTSM issues fixed: +- Resolves ESCOMP/CTSM#2562: Fix or disable "Assign to High Priority project" workflow +- Resolves ESCOMP/CTSM#2566: bug in crop fire modeling +- Resolves ESCOMP/CTSM#2596: Check of finidat_interp_dest.status isn't reached + +Notes of particular relevance for users +--------------------------------------- + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): +- Adds new fire_method option li2024gswpfrc; default hasn't changed. + + +Notes of particular relevance for developers: +--------------------------------------------- +NOTE: Be sure to review the steps in README.CHECKLIST.master_tags as well as the coding style in the Developers Guide + +Changes to tests or testing: Adds 4 tests: +- SMS_D_Ld65.f10_f10_mg37.I2000Clm50BgcCru.derecho_gnu.clm-FireLi2016Cru +- SMS_D_Ld65.f10_f10_mg37.I2000Clm45BgcCropQianRs.derecho_intel.clm-FireLi2014Qian +- SMS_D_Ld65.f10_f10_mg37.I2000Clm50BgcCrop.izumi_intel.clm-FireLi2021GSWP +- SMS_D_Ld65.f10_f10_mg37.I2000Clm60BgcCrop.izumi_nag.clm-FireLi2024GSWP + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + +Other details +------------- + +Pull Requests that document the changes: +- Always check finidat_interp_dest.status (ESCOMP/CTSM#2679; Sam Rabin): https://github.com/ESCOMP/CTSM/pull/2679 +- Remove broken assign-to-project Github workflow (ESCOMP/CTSM#2683; Sam Rabin): https://github.com/ESCOMP/CTSM/pull/2683 +- [Fang summer '24 PR 1.1] Fix bugs and develop crop fire modeling (ESCOMP/CTSM#2684; Fang Li and Sam Rabin): https://github.com/ESCOMP/CTSM/pull/2684 + + +=============================================================== +=============================================================== Tag name: ctsm5.2.022 Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) Date: Wed Aug 14 12:24:27 MDT 2024 diff --git a/doc/ChangeSum b/doc/ChangeSum index 61bf4985de..176ac066d2 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.2.023 samrabin 08/16/2024 Merge b4b-dev ctsm5.2.022 samrabin 08/14/2024 Rework crop_calendars suite and cropMonthOutput ctsm5.2.021 rgknox 08/13/2024 Adding on-the-fly parameter settings for prescribed N and P in FATES ctsm5.2.020 slevis 08/12/2024 MEGAN updates (MEGAN-CLM6)