diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 9f4ce73ed9..953747ddbf 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -1202,6 +1202,15 @@ + + + + + + + + + diff --git a/cime_config/testdefs/testmods_dirs/clm/ciso_cwd_hr/README b/cime_config/testdefs/testmods_dirs/clm/ciso_cwd_hr/README index 39ac318b86..d0dab73f44 100644 --- a/cime_config/testdefs/testmods_dirs/clm/ciso_cwd_hr/README +++ b/cime_config/testdefs/testmods_dirs/clm/ciso_cwd_hr/README @@ -1,8 +1,12 @@ This points to an alternate params file with -rf_cwdl2 = 0.5 -rf_cwdl3_bgc = 0.5 -while by default these parameters equal zero. +ceta = 450 (default = 358) +The README used to say that the alternate paramfile contained +rf_cwdl2 = 0.5 (default 0) +rf_cwdl3_bgc = 0.5 (default 0) +but now 0.5 is the default. + +The README also used to say: The test outputs inactive history fields that would contain zeros when running with the default params file and should be greater than zero when running with the alternate params file. diff --git a/cime_config/testdefs/testmods_dirs/clm/leafcn_t_evolving/README b/cime_config/testdefs/testmods_dirs/clm/leafcn_t_evolving/README new file mode 100644 index 0000000000..3a21ac1a4e --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/leafcn_t_evolving/README @@ -0,0 +1,6 @@ +This points to an alternate params file with +leafcn_co2_slope = 30 (default = 0) +to confirm that the leafcn_t_evolving calculation doesn't fail. + +When we added this test, slevis and ekluzek agreed to consolidate our custom paramfiles into one. +The first of the consolidated paramfiles is named ctsm60_ceta450_cn30_co2_slope30.c250326.nc. diff --git a/cime_config/testdefs/testmods_dirs/clm/leafcn_t_evolving/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/leafcn_t_evolving/include_user_mods new file mode 100644 index 0000000000..fe0e18cf88 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/leafcn_t_evolving/include_user_mods @@ -0,0 +1 @@ +../default diff --git a/cime_config/testdefs/testmods_dirs/clm/leafcn_t_evolving/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/leafcn_t_evolving/user_nl_clm new file mode 100644 index 0000000000..443e9ac1e5 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/leafcn_t_evolving/user_nl_clm @@ -0,0 +1 @@ +paramfile = '$DIN_LOC_ROOT/lnd/clm2/paramdata/ctsm60_ceta450_cn30_co2_slope30.c250326.nc' diff --git a/doc/ChangeLog b/doc/ChangeLog index e594c688a0..ab165f8136 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,64 @@ =============================================================== +Tag name: ctsm5.3.044 +Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310) +Date: Wed 14 May 2025 11:42:41 AM MDT +One-line Summary: Introduce time-evolving LEAFCN_TARGET as function of leafcn param + +Purpose and description of changes +---------------------------------- + + Motivated by a phd student's project (Emma Hauser, now Professor at UW-Madison) we have introduced time-evolving leafcn as a possible template for other time-evolving parameters. + +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_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- +List of CTSM issues fixed (include CTSM Issue # and description) [one per line]: + Resolves #1646 Time evolving parameterization for leaf properties + +Notes of particular relevance for developers: +--------------------------------------------- +Changes to tests or testing: + New test in aux_clm that points to an alternate params file with leafcn_co2_slope = 30 (default = 0) to confirm that the leafcn_t_evolving calculation doesn't fail + +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: No + but field lists differ due to new history field LEAFCN_TARGET + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/1654 + +=============================================================== +=============================================================== Tag name: ctsm5.3.043 Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310) Date: Fri 09 May 2025 12:28:17 PM MDT diff --git a/doc/ChangeSum b/doc/ChangeSum index e805a39512..85192e7d5a 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.3.044 slevis 05/14/2025 Introduce time-evolving LEAFCN_TARGET as function of leafcn param ctsm5.3.043 slevis 05/09/2025 Merge b4b-dev ctsm5.3.042 glemieux 05/08/2025 Update FATES tag, tests and address namelist option bug ctsm5.3.041 samrabin 04/25/2025 Merge b4b-dev to master diff --git a/src/biogeochem/CNAllocationMod.F90 b/src/biogeochem/CNAllocationMod.F90 index 6376279a61..254e951cfe 100644 --- a/src/biogeochem/CNAllocationMod.F90 +++ b/src/biogeochem/CNAllocationMod.F90 @@ -23,6 +23,7 @@ module CNAllocationMod use CNVegCarbonStateType , only : cnveg_carbonstate_type use CNVegCarbonFluxType , only : cnveg_carbonflux_type use CNVegStateType , only : cnveg_state_type + use CNVegNitrogenStateType, only: cnveg_nitrogenstate_type use CropReprPoolsMod , only : nrepr use CNPhenologyMod , only : CropPhase use CNSharedParamsMod , only : use_fun @@ -433,7 +434,7 @@ end subroutine calc_crop_allocation_fractions !----------------------------------------------------------------------- subroutine calc_allometry(num_soilp, filter_soilp, & - cnveg_carbonflux_inst, cnveg_state_inst) + cnveg_carbonflux_inst, cnveg_state_inst, cnveg_nitrogenstate_inst) ! ! !DESCRIPTION: ! Calculate c_allometry and n_allometry terms based on allocation fractions @@ -442,6 +443,7 @@ subroutine calc_allometry(num_soilp, filter_soilp, & integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! filter for soil patches type(cnveg_carbonflux_type) , intent(in) :: cnveg_carbonflux_inst + type(cnveg_nitrogenstate_type) , intent(in) :: cnveg_nitrogenstate_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst ! ! !LOCAL VARIABLES: @@ -464,7 +466,7 @@ subroutine calc_allometry(num_soilp, filter_soilp, & croot_stem => pftcon%croot_stem , & ! Input: allocation parameter: new coarse root C per new stem C (gC/gC) stem_leaf => pftcon%stem_leaf , & ! Input: allocation parameter: new stem c per new leaf C (gC/gC) flivewd => pftcon%flivewd , & ! Input: allocation parameter: fraction of new wood that is live (phloem and ray parenchyma) (no units) - leafcn => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) + leafcn_t_evolving => cnveg_nitrogenstate_inst%leafcn_t_evolving_patch, & ! Input: leaf C:N (gC/gN) frootcn => pftcon%frootcn , & ! Input: fine root C:N (gC/gN) livewdcn => pftcon%livewdcn , & ! Input: live wood (phloem and ray parenchyma) C:N (gC/gN) deadwdcn => pftcon%deadwdcn , & ! Input: dead wood (xylem and heartwood) C:N (gC/gN) @@ -503,7 +505,7 @@ subroutine calc_allometry(num_soilp, filter_soilp, & else g1 = grperc(ivt(p)) end if - cnl = leafcn(ivt(p)) + cnl = leafcn_t_evolving(p) cnfr = frootcn(ivt(p)) cnlw = livewdcn(ivt(p)) cndw = deadwdcn(ivt(p)) diff --git a/src/biogeochem/CNDriverMod.F90 b/src/biogeochem/CNDriverMod.F90 index 0274fcc87f..b407e07ad5 100644 --- a/src/biogeochem/CNDriverMod.F90 +++ b/src/biogeochem/CNDriverMod.F90 @@ -429,7 +429,7 @@ subroutine CNDriverNoLeaching(bounds, end if call calc_allometry(num_bgc_vegp, filter_bgc_vegp, & - cnveg_carbonflux_inst, cnveg_state_inst) + cnveg_carbonflux_inst, cnveg_state_inst, cnveg_nitrogenstate_inst) call t_stopf('cnalloc') end if diff --git a/src/biogeochem/CNFUNMod.F90 b/src/biogeochem/CNFUNMod.F90 index 0af5efe580..cdbe7ba71b 100644 --- a/src/biogeochem/CNFUNMod.F90 +++ b/src/biogeochem/CNFUNMod.F90 @@ -216,7 +216,7 @@ subroutine CNFUNInit (bounds,cnveg_state_inst,cnveg_carbonstate_inst,cnveg_nitro ! Set local pointers associate(ivt => patch%itype , & ! Input: [integer (:) ] p - leafcn => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) + leafcn_t_evolving => cnveg_nitrogenstate_inst%leafcn_t_evolving_patch , & ! Input: leaf C:N (gC/gN) leafcn_offset => cnveg_state_inst%leafcn_offset_patch , & ! Output: ! [real(r8) (:) ] Leaf C:N used by FUN leafc_storage_xfer_acc => cnveg_carbonstate_inst%leafc_storage_xfer_acc_patch , & ! Output: [real(r8) (:) @@ -249,7 +249,7 @@ subroutine CNFUNInit (bounds,cnveg_state_inst,cnveg_carbonstate_inst,cnveg_nitro !--- numofyear = nstep/nstep_fun if (mod(nstep,nstep_fun) == 0) then - leafcn_offset(bounds%begp:bounds%endp) = leafcn(ivt(bounds%begp:bounds%endp)) + leafcn_offset(bounds%begp:bounds%endp) = leafcn_t_evolving(bounds%begp:bounds%endp) storage_cdemand(bounds%begp:bounds%endp) = 0._r8 storage_ndemand(bounds%begp:bounds%endp) = 0._r8 leafn_storage_xfer_acc(bounds%begp:bounds%endp) = 0._r8 @@ -573,7 +573,7 @@ subroutine CNFUN(bounds,num_soilc, filter_soilc,num_soilp& !-------------------------------------------------------------------- !--------------------------------- associate(ivt => patch%itype , & ! Input: [integer (:) ] p - leafcn => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) + leafcn_t_evolving => cnveg_nitrogenstate_inst%leafcn_t_evolving_patch , & ! Input: leaf C:N (gC/gN) season_decid => pftcon%season_decid , & ! Input: binary flag for seasonal ! -deciduous leaf habit (0 or 1) stress_decid => pftcon%stress_decid , & ! Input: binary flag for stress @@ -1276,7 +1276,7 @@ subroutine CNFUN(bounds,num_soilc, filter_soilc,num_soilp& litterfall_c_step(p,istp)* fixerfrac,& litterfall_n_step(p,istp)* fixerfrac,& total_n_resistance, total_c_spent_retrans,total_c_accounted_retrans, & - free_n_retrans,paid_for_n_retrans, leafcn(ivt(p)), & + free_n_retrans,paid_for_n_retrans, leafcn_t_evolving(p), & grperc(ivt(p)), plantCN(p)) else @@ -1307,7 +1307,7 @@ subroutine CNFUN(bounds,num_soilc, filter_soilc,num_soilp& if (leafn(p) == 0.0_r8) then ! to avoid division by zero delta_CN = fun_cn_flex_c(ivt(p)) ! Max CN ratio over standard else - delta_CN = (leafc(p)+leafc_storage(p))/(leafn(p)+leafn_storage(p)) - leafcn(ivt(p)) ! leaf CN ratio + delta_CN = (leafc(p)+leafc_storage(p))/(leafn(p)+leafn_storage(p)) - leafcn_t_evolving(p) ! leaf CN ratio end if ! C used for uptake is reduced if the cost of N is very high frac_ideal_C_use = max(0.0_r8,1.0_r8 - (total_N_resistance-fun_cn_flex_a(ivt(p)))/fun_cn_flex_b(ivt(p)) ) diff --git a/src/biogeochem/CNGRespMod.F90 b/src/biogeochem/CNGRespMod.F90 index c152c19c2c..de8b145615 100644 --- a/src/biogeochem/CNGRespMod.F90 +++ b/src/biogeochem/CNGRespMod.F90 @@ -60,7 +60,6 @@ subroutine CNGResp(num_soilp, filter_soilp, cnveg_carbonflux_inst, canopystate_i woody => pftcon%woody , & ! Input: binary flag for woody lifeform (1=woody, 0=not woody) grperc => pftcon%grperc , & ! Input: growth respiration parameter grpnow => pftcon%grpnow , & ! Input: growth respiration parameter - leafcn => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) livewdcn => pftcon%livewdcn , & ! Input: live wood (phloem and ray parenchyma) C:N (gC/gN) laisun => canopystate_inst%laisun_patch , & ! Input: [real(r8) (:)] sunlit projected leaf area index diff --git a/src/biogeochem/CNGapMortalityMod.F90 b/src/biogeochem/CNGapMortalityMod.F90 index 73ff0bc911..24f0f6b145 100644 --- a/src/biogeochem/CNGapMortalityMod.F90 +++ b/src/biogeochem/CNGapMortalityMod.F90 @@ -163,7 +163,6 @@ subroutine CNGapMortality (bounds, num_soilp, filter_soilp, & greffic => dgvs_inst%greffic_patch , & ! Input: [real(r8) (:) ] heatstress => dgvs_inst%heatstress_patch , & ! Input: [real(r8) (:) ] - leafcn => pftcon%leafcn , & ! Input: [real(r8) (:)] leaf C:N (gC/gN) livewdcn => pftcon%livewdcn , & ! Input: [real(r8) (:)] live wood (phloem and ray parenchyma) C:N (gC/gN) laisun => canopystate_inst%laisun_patch , & ! Input: [real(r8) (:) ] sunlit projected leaf area index laisha => canopystate_inst%laisha_patch , & ! Input: [real(r8) (:) ] shaded projected leaf area index diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index 5ad4fb1157..ff1e5b1b74 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -1387,8 +1387,6 @@ subroutine CNStressDecidPhenology (num_soilp, filter_soilp , & leaf_long => pftcon%leaf_long , & ! Input: leaf longevity (yrs) woody => pftcon%woody , & ! Input: binary flag for woody lifeform (1=woody, 0=not woody) stress_decid => pftcon%stress_decid , & ! Input: binary flag for stress-deciduous leaf habit (0 or 1) - leafcn => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) - frootcn => pftcon%frootcn , & ! Input: fine root C:N (gC/gN) crit_onset_gdd_sf => pftcon%crit_onset_gdd_sf , & ! Input: scale factor for crit_onset_gdd (unitless) ndays_on => pftcon%ndays_on , & ! Input: number of days to complete leaf onset (days) @@ -2076,8 +2074,8 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & associate( & ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type - leaf_long => pftcon%leaf_long , & ! Input: leaf longevity (yrs) - leafcn => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) + leaf_long => pftcon%leaf_long , & ! Input: leaf longevity (yrs) + leafcn_t_evolving => cnveg_nitrogenstate_inst%leafcn_t_evolving_patch, & ! Input: leaf C:N (gC/gN) manunitro => pftcon%manunitro , & ! Input: max manure to be applied in total (kgN/m2) minplanttemp => pftcon%minplanttemp , & ! Input: planttemp => pftcon%planttemp , & ! Input: @@ -2303,7 +2301,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & vf(p) = 0._r8 end if - call PlantCrop(p, leafcn(ivt(p)), jday, kyr, do_plant_normal, & + call PlantCrop(p, leafcn_t_evolving(p), jday, kyr, do_plant_normal, & do_plant_lastchance, do_plant_prescribed, & temperature_inst, crop_inst, cnveg_state_inst, & cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, & @@ -2564,8 +2562,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & crop_seedc_to_leaf(p) = crop_seedc_to_leaf(p) - leafc_xfer(p)/dt crop_seedn_to_leaf(p) = crop_seedn_to_leaf(p) - leafn_xfer(p)/dt leafc_xfer(p) = 0._r8 - - leafn_xfer(p) = leafc_xfer(p) / leafcn(ivt(p)) + leafn_xfer(p) = leafc_xfer(p) / leafcn_t_evolving(p) if (use_c13) then c13_cnveg_carbonstate_inst%leafc_xfer_patch(p) = 0._r8 endif @@ -2605,7 +2602,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & crop_seedn_to_leaf(p) = crop_seedn_to_leaf(p) - leafn_xfer(p)/dt onset_counter(p) = 0._r8 leafc_xfer(p) = 0._r8 - leafn_xfer(p) = leafc_xfer(p) / leafcn(ivt(p)) + leafn_xfer(p) = leafc_xfer(p) / leafcn_t_evolving(p) if (use_c13) then c13_cnveg_carbonstate_inst%leafc_xfer_patch(p) = 0._r8 endif @@ -2780,7 +2777,7 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & ! ! !ARGUMENTS: integer , intent(in) :: p ! PATCH index running over - real(r8) , intent(in) :: leafcn_in ! leaf C:N (gC/gN) of this patch's vegetation type (pftcon%leafcn(ivt(p))) + real(r8) , intent(in) :: leafcn_in ! leaf C:N (gC/gN) of this patch integer , intent(in) :: jday ! julian day of the year integer , intent(in) :: kyr ! current year logical , intent(in) :: do_plant_normal ! Are all the normal requirements for planting met? @@ -3391,7 +3388,7 @@ subroutine CNOffsetLitterfall (num_soilp, filter_soilp, & associate( & ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type - leafcn => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) + leafcn_t_evolving => cnveg_nitrogenstate_inst%leafcn_t_evolving_patch , & ! Input: leaf C:N (gC/gN) biofuel_harvfrac => pftcon%biofuel_harvfrac , & ! Input: cut a fraction of leaf & stem for biofuel (-) repr_structure_harvfrac => pftcon%repr_structure_harvfrac , & ! Input: fraction of each reproductive structure component that is harvested and sent to the crop products pool @@ -3743,7 +3740,7 @@ subroutine CNOffsetLitterfall (num_soilp, filter_soilp, & leafcn_offset(p) = leafc(p)/leafn(p) end if else - leafcn_offset(p) = leafcn(ivt(p)) + leafcn_offset(p) = leafcn_t_evolving(p) end if leafn_to_litter(p) = leafc_to_litter(p)/leafcn_offset(p) - leafn_to_retransn(p) leafn_to_litter(p) = max(leafn_to_litter(p),0._r8) @@ -3775,7 +3772,7 @@ subroutine CNOffsetLitterfall (num_soilp, filter_soilp, & end if ! calculate the leaf N litterfall and retranslocation leafn_to_litter(p) = leafc_to_litter(p) / lflitcn(ivt(p)) - leafn_to_retransn(p) = (leafc_to_litter(p) / leafcn(ivt(p))) - leafn_to_litter(p) + leafn_to_retransn(p) = (leafc_to_litter(p) / leafcn_t_evolving(p)) - leafn_to_litter(p) if (use_matrixcn) then if(leafn(p) .gt. 0)then @@ -3891,7 +3888,7 @@ subroutine CNBackgroundLitterfall (num_soilp, filter_soilp, & associate( & ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type - leafcn => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) + leafcn_t_evolving => cnveg_nitrogenstate_inst%leafcn_t_evolving_patch, & ! Input: leaf C:N (gC/gN) lflitcn => pftcon%lflitcn , & ! Input: leaf litter C:N (gC/gN) frootcn => pftcon%frootcn , & ! Input: fine root C:N (gC/gN) @@ -3977,7 +3974,7 @@ subroutine CNBackgroundLitterfall (num_soilp, filter_soilp, & leafcn_offset(p) = leafc(p)/leafn(p) end if else - leafcn_offset(p) = leafcn(ivt(p)) + leafcn_offset(p) = leafcn_t_evolving(p) end if leafn_to_litter(p) = leafc_to_litter(p)/leafcn_offset(p) - leafn_to_retransn(p) leafn_to_litter(p) = max(leafn_to_litter(p),0._r8) @@ -4006,7 +4003,7 @@ subroutine CNBackgroundLitterfall (num_soilp, filter_soilp, & end if ! calculate the leaf N litterfall and retranslocation leafn_to_litter(p) = leafc_to_litter(p) / lflitcn(ivt(p)) - leafn_to_retransn(p) = (leafc_to_litter(p) / leafcn(ivt(p))) - leafn_to_litter(p) + leafn_to_retransn(p) = (leafc_to_litter(p) / leafcn_t_evolving(p)) - leafn_to_litter(p) if (use_matrixcn) then if(leafn(p) .ne. 0)then diff --git a/src/biogeochem/CNVegCarbonStateType.F90 b/src/biogeochem/CNVegCarbonStateType.F90 index aee328a45e..142578e656 100644 --- a/src/biogeochem/CNVegCarbonStateType.F90 +++ b/src/biogeochem/CNVegCarbonStateType.F90 @@ -1794,7 +1794,6 @@ subroutine Restart ( this, bounds, ncid, flag, carbon_type, reseed_dead_plants, use shr_infnan_mod , only : isnan => shr_infnan_isnan, nan => shr_infnan_nan, assignment(=) use clm_varcon , only : c13ratio, c14ratio use clm_varctl , only : spinup_state, use_cndv, MM_Nuptake_opt - use clm_varctl , only : spinup_state, use_cndv, MM_Nuptake_opt use clm_time_manager , only : is_restart use landunit_varcon , only : istsoil, istcrop use spmdMod , only : mpicom @@ -4637,6 +4636,7 @@ subroutine DynamicPatchAdjustments(this, bounds, & num_soilp_with_inactive, filter_soilp_with_inactive, & patch_state_updater, & leafc_seed, deadstemc_seed, & + leafcn_t_evolving, & conv_cflux, wood_product_cflux, crop_product_cflux, & dwt_frootc_to_litter, & dwt_livecrootc_to_litter, & @@ -4658,6 +4658,7 @@ subroutine DynamicPatchAdjustments(this, bounds, & type(patch_state_updater_type) , intent(in) :: patch_state_updater real(r8) , intent(in) :: leafc_seed ! seed amount for leaf C real(r8) , intent(in) :: deadstemc_seed ! seed amount for deadstem C + real(r8) , intent(in) :: leafcn_t_evolving( bounds%begp: ) ! current leaf C:N (gC/gN) real(r8) , intent(inout) :: conv_cflux( bounds%begp: ) ! patch-level conversion C flux to atm (expressed per unit GRIDCELL area) real(r8) , intent(inout) :: wood_product_cflux( bounds%begp: ) ! patch-level product C flux (expressed per unit GRIDCELL area) real(r8) , intent(inout) :: crop_product_cflux( bounds%begp: ) ! patch-level crop product C flux (expressed per unit GRIDCELL area) @@ -4700,6 +4701,7 @@ subroutine DynamicPatchAdjustments(this, bounds, & call ComputeSeedAmounts(bounds, & num_soilp_with_inactive, filter_soilp_with_inactive, & + leafcn_t_evolving = leafcn_t_evolving(begp:endp), & species = this%species, & leafc_seed = leafc_seed, & deadstemc_seed = deadstemc_seed, & diff --git a/src/biogeochem/CNVegComputeSeedMod.F90 b/src/biogeochem/CNVegComputeSeedMod.F90 index 2581d5ba1e..760c2fc767 100644 --- a/src/biogeochem/CNVegComputeSeedMod.F90 +++ b/src/biogeochem/CNVegComputeSeedMod.F90 @@ -39,6 +39,7 @@ module CNVegComputeSeedMod !----------------------------------------------------------------------- subroutine ComputeSeedAmounts(bounds, & num_soilp_with_inactive, filter_soilp_with_inactive, & + leafcn_t_evolving, & species, & leafc_seed, deadstemc_seed, & leaf_patch, leaf_storage_patch, leaf_xfer_patch, & @@ -66,6 +67,7 @@ subroutine ComputeSeedAmounts(bounds, & integer , intent(in) :: species ! which C/N species we're operating on; should be one of the values in CNSpeciesMod real(r8) , intent(in) :: leafc_seed ! seed amount for leaf C real(r8) , intent(in) :: deadstemc_seed ! seed amount for deadstem C + real(r8) , intent(in) :: leafcn_t_evolving( bounds%begp: ) ! current leaf C:N (gC/gN) real(r8) , intent(in) :: leaf_patch( bounds%begp: ) ! current leaf C or N content (g/m2) real(r8) , intent(in) :: leaf_storage_patch( bounds%begp: ) ! current leaf C or N storage content (g/m2) real(r8) , intent(in) :: leaf_xfer_patch( bounds%begp: ) ! current leaf C or N xfer content (g/m2) @@ -88,6 +90,7 @@ subroutine ComputeSeedAmounts(bounds, & real(r8) :: my_leaf_seed real(r8) :: my_deadstem_seed integer :: pft_type + real(r8) :: leafcn real(r8) :: pleaf real(r8) :: pstor real(r8) :: pxfer @@ -118,6 +121,7 @@ subroutine ComputeSeedAmounts(bounds, & my_deadstem_seed = 0._r8 pft_type = patch%itype(p) + leafcn = leafcn_t_evolving(p) call LeafProportions( & ignore_current_state = ignore_current_state_patch(p), & @@ -131,10 +135,10 @@ subroutine ComputeSeedAmounts(bounds, & if (pft_type /= noveg) then my_leaf_seed = leafc_seed * & - SpeciesTypeMultiplier(species, pft_type, COMPONENT_LEAF) + SpeciesTypeMultiplier(species, pft_type, leafcn, COMPONENT_LEAF) if (pftcon%woody(pft_type) == 1._r8) then my_deadstem_seed = deadstemc_seed * & - SpeciesTypeMultiplier(species, pft_type, COMPONENT_DEADWOOD) + SpeciesTypeMultiplier(species, pft_type, leafcn, COMPONENT_DEADWOOD) end if end if @@ -150,7 +154,7 @@ end subroutine ComputeSeedAmounts !----------------------------------------------------------------------- - function SpeciesTypeMultiplier(species, pft_type, component) result(multiplier) + function SpeciesTypeMultiplier(species, pft_type, leafcn, component) result(multiplier) ! ! !DESCRIPTION: ! Returns a multiplier based on the species type. This multiplier is @@ -163,6 +167,7 @@ function SpeciesTypeMultiplier(species, pft_type, component) result(multiplier) real(r8) :: multiplier ! function result integer, intent(in) :: species ! which C/N species we're operating on; should be one of the values in CNSpeciesMod integer, intent(in) :: pft_type + real(r8), intent(in) :: leafcn integer, intent(in) :: component ! which plant component; should be one of the COMPONENT_* parameters defined in this module ! ! !LOCAL VARIABLES: @@ -188,7 +193,7 @@ function SpeciesTypeMultiplier(species, pft_type, component) result(multiplier) case (CN_SPECIES_N) select case (component) case (COMPONENT_LEAF) - multiplier = 1._r8 / pftcon%leafcn(pft_type) + multiplier = 1._r8 / leafcn case (COMPONENT_DEADWOOD) multiplier = 1._r8 / pftcon%deadwdcn(pft_type) case default diff --git a/src/biogeochem/CNVegNitrogenStateType.F90 b/src/biogeochem/CNVegNitrogenStateType.F90 index 61bb2d5d16..7ff8e77df9 100644 --- a/src/biogeochem/CNVegNitrogenStateType.F90 +++ b/src/biogeochem/CNVegNitrogenStateType.F90 @@ -17,19 +17,27 @@ module CNVegNitrogenStateType use ColumnType , only : col use PatchType , only : patch use dynPatchStateUpdaterMod, only : patch_state_updater_type + use atm2lndType , only : atm2lnd_type use CNSpeciesMod , only : CN_SPECIES_N use CNVegComputeSeedMod, only : ComputeSeedAmounts use CropReprPoolsMod , only : nrepr, get_repr_hist_fname, get_repr_rest_fname, get_repr_longname + use ncdio_pio ! ! !PUBLIC TYPES: implicit none private - + type :: cnveg_nstate_params_type + real(r8) :: leafcn_co2_base ! ppm CO2 for target leaf CN scaling + real(r8) :: leafcn_co2_slope ! CO2 effects on target leaf CN scaling (unitless) + end type cnveg_nstate_params_type ! + type(cnveg_nstate_params_type), public, protected :: params_inst ! params_inst is populated in readParams + type, public :: cnveg_nitrogenstate_type + real(r8), pointer :: leafcn_t_evolving_patch (:) ! (gC/gN) time evolving leafcn real(r8), pointer :: reproductiven_patch (:,:) ! (gN/m2) reproductive (e.g., grain) N (crop) real(r8), pointer :: reproductiven_storage_patch (:,:) ! (gN/m2) reproductive (e.g., grain) N storage (crop) real(r8), pointer :: reproductiven_xfer_patch (:,:) ! (gN/m2) reproductive (e.g., grain) N transfer (crop) @@ -218,6 +226,8 @@ module CNVegNitrogenStateType procedure , private :: InitAllocate procedure , private :: InitHistory procedure , private :: InitCold + procedure , private :: ReadParams + procedure , public :: time_evolv_leafcn ! updates time-evolving leafcn end type cnveg_nitrogenstate_type !------------------------------------------------------------------------ @@ -231,10 +241,11 @@ module CNVegNitrogenStateType !------------------------------------------------------------------------ subroutine Init(this, bounds, & leafc_patch, leafc_storage_patch, frootc_patch, frootc_storage_patch, & - deadstemc_patch, alloc_full_veg) + deadstemc_patch, alloc_full_veg, params_ncid) class(cnveg_nitrogenstate_type) :: this type(bounds_type) , intent(in) :: bounds + type(file_desc_t) , intent(inout) :: params_ncid ! pio netCDF file id real(r8) , intent(in) :: leafc_patch (:) !(begp:) real(r8) , intent(in) :: leafc_storage_patch (:) !(begp:) real(r8) , intent(in) :: frootc_patch (:) !(begp:) @@ -248,6 +259,7 @@ subroutine Init(this, bounds, & call this%InitCold ( bounds, & leafc_patch, leafc_storage_patch, frootc_patch, frootc_storage_patch, deadstemc_patch) end if + call this%ReadParams( params_ncid ) end subroutine Init !------------------------------------------------------------------------ @@ -337,8 +349,8 @@ subroutine InitAllocate(this, bounds, alloc_full_veg) allocate(this%seedn_grc (begg:endg)) ; this%seedn_grc (:) = nan allocate(this%totvegn_col (begc:endc)) ; this%totvegn_col (:) = nan allocate(this%totn_p2c_col (begc:endc)) ; this%totn_p2c_col (:) = nan - + allocate(this%leafcn_t_evolving_patch (begp:endp)) ; this%leafcn_t_evolving_patch (:) = nan if(use_matrixcn)then allocate(this%leafn0_patch (begp:endp)) ; this%leafn0_patch (:) = nan allocate(this%leafn0_storage_patch (begp:endp)) ; this%leafn0_storage_patch (:) = nan @@ -492,6 +504,11 @@ subroutine InitHistory(this, bounds) !------------------------------- ! patch state variables !------------------------------- + + this%leafcn_t_evolving_patch(begp:endp) = spval + call hist_addfld1d (fname='LEAFCN_TARGET', units='gC/gN', & + avgflag='A', long_name='Target leaf C:N; compare against leafC/leafN', & + ptr_patch=this%leafcn_t_evolving_patch) if (use_crop) then this%reproductiven_patch(begp:endp,:) = spval @@ -828,6 +845,8 @@ subroutine InitCold(this, bounds, & do p = bounds%begp,bounds%endp + this%leafcn_t_evolving_patch(p) = pftcon%leafcn(patch%itype(p)) + l = patch%landunit(p) if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then @@ -847,11 +866,11 @@ subroutine InitCold(this, bounds, & end if end if else - this%leafn_patch(p) = leafc_patch(p) / pftcon%leafcn(patch%itype(p)) - this%leafn_storage_patch(p) = leafc_storage_patch(p) / pftcon%leafcn(patch%itype(p)) + this%leafn_patch(p) = leafc_patch(p) / this%leafcn_t_evolving_patch(p) + this%leafn_storage_patch(p) = leafc_storage_patch(p) / this%leafcn_t_evolving_patch(p) if(use_matrixcn)then - this%matrix_cap_leafn_patch(p) = leafc_patch(p) / pftcon%leafcn(patch%itype(p)) - this%matrix_cap_leafn_storage_patch(p) = leafc_storage_patch(p) / pftcon%leafcn(patch%itype(p)) + this%matrix_cap_leafn_patch(p) = leafc_patch(p) / this%leafcn_t_evolving_patch(p) + this%matrix_cap_leafn_storage_patch(p) = leafc_storage_patch(p) / this%leafcn_t_evolving_patch(p) end if if (MM_Nuptake_opt .eqv. .true.) then this%frootn_patch(p) = frootc_patch(p) / pftcon%frootcn(patch%itype(p)) @@ -1108,7 +1127,6 @@ subroutine Restart ( this, bounds, ncid, flag, leafc_patch, & ! ! !USES: use restUtilMod - use ncdio_pio use clm_varctl , only : spinup_state, use_cndv use clm_time_manager , only : get_nstep, is_restart use clm_varctl , only : MM_Nuptake_opt @@ -1815,11 +1833,11 @@ subroutine Restart ( this, bounds, ncid, flag, leafc_patch, & end if end if else - this%leafn_patch(p) = leafc_patch(p) / pftcon%leafcn(patch%itype(p)) - this%leafn_storage_patch(p) = leafc_storage_patch(p) / pftcon%leafcn(patch%itype(p)) + this%leafn_patch(p) = leafc_patch(p) / this%leafcn_t_evolving_patch(p) + this%leafn_storage_patch(p) = leafc_storage_patch(p) / this%leafcn_t_evolving_patch(p) if(use_matrixcn)then - this%matrix_cap_leafn_patch(p) = leafc_patch(p) / pftcon%leafcn(patch%itype(p)) - this%matrix_cap_leafn_storage_patch(p) = leafc_storage_patch(p) / pftcon%leafcn(patch%itype(p)) + this%matrix_cap_leafn_patch(p) = leafc_patch(p) / this%leafcn_t_evolving_patch(p) + this%matrix_cap_leafn_storage_patch(p) = leafc_storage_patch(p) / this%leafcn_t_evolving_patch(p) end if if (MM_Nuptake_opt .eqv. .true.) then this%frootn_patch(p) = frootc_patch(p) / pftcon%frootcn(patch%itype(p)) @@ -1976,6 +1994,7 @@ subroutine SetValues ( this, & do fi = 1,num_patch i = filter_patch(fi) + this%leafcn_t_evolving_patch(i) = value_patch this%leafn_patch(i) = value_patch this%leafn_storage_patch(i) = value_patch this%leafn_xfer_patch(i) = value_patch @@ -2276,6 +2295,73 @@ subroutine Summary_nitrogenstate(this, bounds, num_soilc, filter_soilc, num_soil end subroutine Summary_nitrogenstate + !----------------------------------------------------------------------- + subroutine ReadParams ( this, ncid ) + ! + ! !USES: + use paramUtilMod, only: readNcdioScalar + + implicit none + + ! !ARGUMENTS: + class(cnveg_nitrogenstate_type) :: this + type(file_desc_t), intent(inout) :: ncid ! pio netCDF file id + ! + ! !LOCAL VARIABLES: + !----------------------------------------------------------------------- + + ! read in parameters + + ! read in the scalar parameters + + ! ppm CO2 for target leaf CN scaling (ppmv) + call readNcdioScalar(ncid, 'leafcn_co2_base', sourcefile, params_inst%leafcn_co2_base) + ! CO2 effects on target leaf CN scaling (unitless) + call readNcdioScalar(ncid, 'leafcn_co2_slope', sourcefile, params_inst%leafcn_co2_slope) + + end subroutine ReadParams + + !----------------------------------------------------------------------- + subroutine time_evolv_leafcn(this, bounds, num_bgc_vegp, filter_bgc_vegp, atm2lnd_inst) + ! + ! AUTHOR: slevis + ! + ! !DESCRIPTION: + ! Update time-evolving parameters + ! + ! Calculate time evolving leafcn from co2_ppmv and three paramfile + ! parameters. Of the three parameters, leafcn_co2_slope is set to 0 + ! (as of ctsm5.4) so leafcn_t_evolving_patch defaults to the values of + ! paramfile parameter leafcn + ! !USES: + use pftconMod, only : pftcon + use clm_time_manager, only : is_first_step + ! + ! !ARGUMENTS: + class(cnveg_nitrogenstate_type) :: this + type(bounds_type), intent(in) :: bounds + integer, intent(in) :: num_bgc_vegp ! number of veg patches in filter + integer, intent(in) :: filter_bgc_vegp(:) ! filter for veg patches + type(atm2lnd_type), intent(in) :: atm2lnd_inst + ! + ! !LOCAL VARIABLES: + integer :: g, c, p, fp ! indices + real(r8) :: co2_ppmv ! atm co2 concentration + !----------------------------------------------------------------------- + + ! Loop to get leafcn_col + do fp = 1, num_bgc_vegp + p = filter_bgc_vegp(fp) + c = patch%column(p) + g = patch%gridcell(p) + co2_ppmv = 1.e6_r8 * atm2lnd_inst%forc_pco2_grc(g) / & + atm2lnd_inst%forc_pbot_not_downscaled_grc(g) + this%leafcn_t_evolving_patch(p) = pftcon%leafcn(patch%itype(p)) + & + max(params_inst%leafcn_co2_slope * log(co2_ppmv / params_inst%leafcn_co2_base), 0._r8) + end do + + end subroutine time_evolv_leafcn + !----------------------------------------------------------------------- subroutine DynamicPatchAdjustments(this, bounds, & num_soilp_with_inactive, filter_soilp_with_inactive, & @@ -2323,8 +2409,6 @@ subroutine DynamicPatchAdjustments(this, bounds, & real(r8) :: seed_leafn_storage_patch(bounds%begp:bounds%endp) real(r8) :: seed_leafn_xfer_patch(bounds%begp:bounds%endp) real(r8) :: seed_deadstemn_patch(bounds%begp:bounds%endp) - - character(len=*), parameter :: subname = 'DynamicPatchAdjustments' !----------------------------------------------------------------------- begp = bounds%begp @@ -2344,6 +2428,7 @@ subroutine DynamicPatchAdjustments(this, bounds, & call ComputeSeedAmounts(bounds, & num_soilp_with_inactive, filter_soilp_with_inactive, & + leafcn_t_evolving = this%leafcn_t_evolving_patch(begp:endp), & species = CN_SPECIES_N, & leafc_seed = leafc_seed, & deadstemc_seed = deadstemc_seed, & diff --git a/src/biogeochem/CNVegetationFacade.F90 b/src/biogeochem/CNVegetationFacade.F90 index dd20ce50a2..47099708f4 100644 --- a/src/biogeochem/CNVegetationFacade.F90 +++ b/src/biogeochem/CNVegetationFacade.F90 @@ -282,7 +282,7 @@ subroutine Init(this, bounds, NLFilename, nskip_steps, params_ncid) this%cnveg_carbonstate_inst%frootc_patch(begp:endp), & this%cnveg_carbonstate_inst%frootc_storage_patch(begp:endp), & this%cnveg_carbonstate_inst%deadstemc_patch(begp:endp), & - alloc_full_veg=alloc_full_veg) + alloc_full_veg=alloc_full_veg, params_ncid=params_ncid) call this%cnveg_nitrogenflux_inst%Init(bounds,alloc_full_veg=alloc_full_veg) call this%c_products_inst%Init(bounds, species_non_isotope_type('C')) diff --git a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 index 0980ff5378..82dceef664 100644 --- a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 +++ b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 @@ -16,6 +16,7 @@ module NutrientCompetitionCLM45defaultMod use LandunitType , only : lun use ColumnType , only : col use PatchType , only : patch + use CNVegNitrogenStateType, only : cnveg_nitrogenstate_type use NutrientCompetitionMethodMod, only : nutrient_competition_method_type use CropReprPoolsMod , only : nrepr use CNPhenologyMod , only : CropPhase @@ -89,7 +90,6 @@ subroutine calc_plant_nutrient_competition (this, & use CanopyStateType , only : canopystate_type use CNVegCarbonStateType , only : cnveg_carbonstate_type use CNVegCarbonFluxType , only : cnveg_carbonflux_type - use CNVegNitrogenStateType, only : cnveg_nitrogenstate_type use CNVegNitrogenFluxType , only : cnveg_nitrogenflux_type use SoilBiogeochemNitrogenStateType, only : soilbiogeochem_nitrogenstate_type ! @@ -113,7 +113,7 @@ subroutine calc_plant_nutrient_competition (this, & call this%calc_plant_cn_alloc (bounds, num_soilp, filter_soilp, & cnveg_state_inst, crop_inst, canopystate_inst, & cnveg_carbonstate_inst, cnveg_carbonflux_inst, c13_cnveg_carbonflux_inst, & - c14_cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, & + c14_cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, cnveg_nitrogenstate_inst, & fpg_col=fpg_col(bounds%begc:bounds%endc)) end subroutine calc_plant_nutrient_competition @@ -122,7 +122,7 @@ end subroutine calc_plant_nutrient_competition subroutine calc_plant_cn_alloc (this, bounds, num_soilp, filter_soilp, & cnveg_state_inst, crop_inst, canopystate_inst, & cnveg_carbonstate_inst, cnveg_carbonflux_inst, c13_cnveg_carbonflux_inst, & - c14_cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, fpg_col) + c14_cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, cnveg_nitrogenstate_inst, fpg_col) ! ! !USES: use pftconMod , only : pftcon, npcropmin @@ -150,6 +150,7 @@ subroutine calc_plant_cn_alloc (this, bounds, num_soilp, filter_soilp, & type(cnveg_carbonflux_type) , intent(inout) :: c13_cnveg_carbonflux_inst type(cnveg_carbonflux_type) , intent(inout) :: c14_cnveg_carbonflux_inst type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst + type(cnveg_nitrogenstate_type) , intent(in) :: cnveg_nitrogenstate_inst real(r8) , intent(in) :: fpg_col(bounds%begc:) ! ! !LOCAL VARIABLES: @@ -177,7 +178,7 @@ subroutine calc_plant_cn_alloc (this, bounds, num_soilp, filter_soilp, & croot_stem => pftcon%croot_stem , & ! Input: allocation parameter: new coarse root C per new stem C (gC/gC) stem_leaf => pftcon%stem_leaf , & ! Input: allocation parameter: new stem c per new leaf C (gC/gC) flivewd => pftcon%flivewd , & ! Input: allocation parameter: fraction of new wood that is live (phloem and ray parenchyma) (no units) - leafcn => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) + leafcn_t_evolving => cnveg_nitrogenstate_inst%leafcn_t_evolving_patch , & ! Input: leaf C:N (gC/gN) frootcn => pftcon%frootcn , & ! Input: fine root C:N (gC/gN) livewdcn => pftcon%livewdcn , & ! Input: live wood (phloem and ray parenchyma) C:N (gC/gN) deadwdcn => pftcon%deadwdcn , & ! Input: dead wood (xylem and heartwood) C:N (gC/gN) @@ -274,7 +275,7 @@ subroutine calc_plant_cn_alloc (this, bounds, num_soilp, filter_soilp, & f4 = flivewd(ivt(p)) g1 = grperc(ivt(p)) g2 = grpnow(ivt(p)) - cnl = leafcn(ivt(p)) + cnl = leafcn_t_evolving(p) cnfr = frootcn(ivt(p)) cnlw = livewdcn(ivt(p)) cndw = deadwdcn(ivt(p)) @@ -447,7 +448,6 @@ subroutine calc_plant_nutrient_demand(this, bounds, & use CropType , only : crop_type use CNVegStateType , only : cnveg_state_type use CNVegCarbonStateType , only : cnveg_carbonstate_type - use CNVegNitrogenStateType , only : cnveg_nitrogenstate_type use CNVegCarbonFluxType , only : cnveg_carbonflux_type use CNVegNitrogenFluxType , only : cnveg_nitrogenflux_type use SoilBiogeochemCarbonFluxType, only : soilbiogeochem_carbonflux_type @@ -512,7 +512,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, & use CropType , only : crop_type use CNVegStateType , only : cnveg_state_type use CNVegCarbonStateType , only : cnveg_carbonstate_type - use CNVegNitrogenStateType , only : cnveg_nitrogenstate_type use CNVegCarbonFluxType , only : cnveg_carbonflux_type use CNVegNitrogenFluxType , only : cnveg_nitrogenflux_type use CNSharedParamsMod , only : use_fun @@ -552,7 +551,7 @@ subroutine calc_plant_nitrogen_demand(this, bounds, & associate( & ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type - leafcn => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) + leafcn_t_evolving => cnveg_nitrogenstate_inst%leafcn_t_evolving_patch , & ! Input: leaf C:N (gC/gN) frootcn => pftcon%frootcn , & ! Input: fine root C:N (gC/gN) livewdcn => pftcon%livewdcn , & ! Input: live wood (phloem and ray parenchyma) C:N (gC/gN) graincn => pftcon%graincn , & ! Input: grain C:N (gC/gN) @@ -645,7 +644,7 @@ subroutine calc_plant_nitrogen_demand(this, bounds, & if (grain_flag(p) == 0._r8)then if(.not.use_fun) then t1 = 1 / dt - leafn_to_retransn(p) = t1 * ((leafc(p) / leafcn(ivt(p))) - (leafc(p) / & + leafn_to_retransn(p) = t1 * ((leafc(p) / leafcn_t_evolving(p)) - (leafc(p) / & fleafcn(ivt(p)))) livestemn_to_retransn(p) = t1 * ((livestemc(p) / livewdcn(ivt(p))) - (livestemc(p) / & fstemcn(ivt(p)))) diff --git a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 index 993c0a2e9a..00e8a00b77 100644 --- a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 +++ b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 @@ -275,7 +275,7 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & croot_stem => pftcon%croot_stem , & ! Input: allocation parameter: new coarse root C per new stem C (gC/gC) stem_leaf => pftcon%stem_leaf , & ! Input: allocation parameter: new stem c per new leaf C (gC/gC) flivewd => pftcon%flivewd , & ! Input: allocation parameter: fraction of new wood that is live (phloem and ray parenchyma) (no units) - leafcn => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) + leafcn_t_evolving => cnveg_nitrogenstate_inst%leafcn_t_evolving_patch , & ! Input: leaf C:N (gC/gN) frootcn => pftcon%frootcn , & ! Input: fine root C:N (gC/gN) livewdcn => pftcon%livewdcn , & ! Input: live wood (phloem and ray parenchyma) C:N (gC/gN) fcur2 => pftcon%fcur , & ! Input: allocation parameter: fraction of allocation that goes to currently displayed growth, remainder to storage @@ -635,6 +635,7 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & f3 = f3, & f4 = f4, & f5 = f5, & + leafcn_t_evolving = leafcn_t_evolving(p), & ! Outputs npool_to_leafn = npool_to_leafn(p), & @@ -728,7 +729,7 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & end if end if - leafcn_max = leafcn(ivt(p)) + 15.0_r8 + leafcn_max = leafcn_t_evolving(p) + 15.0_r8 frootcn_max = frootcn(ivt(p)) + 15.0_r8 ! Note that for high CN ratio stress the plant part does not retranslocate nitrogen as the plant part will need the N @@ -930,7 +931,7 @@ end subroutine calc_plant_cn_alloc !----------------------------------------------------------------------- subroutine calc_npool_to_components_flexiblecn( & - npool, ivt, nlc, fcur, f1, f2, f3, f4, f5, & + npool, ivt, nlc, fcur, f1, f2, f3, f4, f5, leafcn_t_evolving, & npool_to_leafn, npool_to_leafn_storage, & npool_to_frootn, npool_to_frootn_storage, & npool_to_livestemn, npool_to_livestemn_storage, & @@ -952,6 +953,7 @@ subroutine calc_npool_to_components_flexiblecn( & real(r8), intent(in) :: f3 ! C allocation parameter - stem:leaf ratio real(r8), intent(in) :: f4 ! C allocation parameter - fraction of new wood that is live real(r8), intent(in) :: f5(:) ! C allocation parameter - repr:leaf ratio for each crop reproductive pool + real(r8), intent(in) :: leafcn_t_evolving ! leaf C:N (gC/gN) ! Each of the following output variables is in units of gN/m2/s; they are ! intent(inout) because some may remain unchanged in some circumstances. @@ -1018,7 +1020,6 @@ subroutine calc_npool_to_components_flexiblecn( & associate( & woody => pftcon%woody , & ! Input: binary flag for woody lifeform (1=woody, 0=not woody) - leafcn => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) frootcn => pftcon%frootcn , & ! Input: fine root C:N (gC/gN) livewdcn => pftcon%livewdcn , & ! Input: live wood (phloem and ray parenchyma) C:N (gC/gN) deadwdcn => pftcon%deadwdcn , & ! Input: dead wood (xylem and heartwood) C:N (gC/gN) @@ -1027,7 +1028,7 @@ subroutine calc_npool_to_components_flexiblecn( & dt = get_step_size_real() - cnl = leafcn(ivt) + cnl = leafcn_t_evolving cnfr = frootcn(ivt) cnlw = livewdcn(ivt) cndw = deadwdcn(ivt) @@ -1455,7 +1456,7 @@ subroutine calc_plant_nitrogen_demand(this, bounds, & associate( & ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type - leafcn => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) + leafcn_t_evolving => cnveg_nitrogenstate_inst%leafcn_t_evolving_patch , & ! Input: leaf C:N (gC/gN) fleafcn => pftcon%fleafcn , & ! Input: leaf c:n during organ fill ffrootcn => pftcon%ffrootcn , & ! Input: froot c:n during organ fill fstemcn => pftcon%fstemcn , & ! Input: stem c:n during organ fill @@ -1520,8 +1521,8 @@ subroutine calc_plant_nitrogen_demand(this, bounds, & this%actual_leafcn(p) = leafc(p) / leafn(p) end if - leafcn_min = leafcn(ivt(p)) - 10.0_r8 - leafcn_max = leafcn(ivt(p)) + 10.0_r8 + leafcn_min = leafcn_t_evolving(p) - 10.0_r8 + leafcn_max = leafcn_t_evolving(p) + 10.0_r8 this%actual_leafcn(p) = max( this%actual_leafcn(p), leafcn_min-0.0001_r8 ) this%actual_leafcn(p) = min( this%actual_leafcn(p), leafcn_max ) @@ -1563,7 +1564,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, & ! retranslocated N deployment depends on seasonal cycle of potential GPP ! (requires one year run to accumulate demand) - tempsum_potential_gpp(p) = tempsum_potential_gpp(p) + gpp(p) ! Adding the following line to carry max retransn info to CN Annual Update diff --git a/src/biogeochem/dynConsBiogeochemMod.F90 b/src/biogeochem/dynConsBiogeochemMod.F90 index f8cff29911..445d1a7bda 100644 --- a/src/biogeochem/dynConsBiogeochemMod.F90 +++ b/src/biogeochem/dynConsBiogeochemMod.F90 @@ -445,6 +445,7 @@ subroutine dyn_cnbal_patch(bounds, & patch_state_updater, & leafc_seed = leafc_seed, & deadstemc_seed = deadstemc_seed, & + leafcn_t_evolving = cnveg_nitrogenstate_inst%leafcn_t_evolving_patch(begp:endp), & conv_cflux = conv_cflux(begp:endp), & wood_product_cflux = wood_product_cflux(begp:endp), & crop_product_cflux = crop_product_cflux(begp:endp), & @@ -468,6 +469,7 @@ subroutine dyn_cnbal_patch(bounds, & patch_state_updater, & leafc_seed = leafc_seed, & deadstemc_seed = deadstemc_seed, & + leafcn_t_evolving = cnveg_nitrogenstate_inst%leafcn_t_evolving_patch(begp:endp), & conv_cflux = conv_c13flux(begp:endp), & wood_product_cflux = wood_product_c13flux(begp:endp), & crop_product_cflux = crop_product_c13flux(begp:endp), & @@ -493,6 +495,7 @@ subroutine dyn_cnbal_patch(bounds, & patch_state_updater, & leafc_seed = leafc_seed, & deadstemc_seed = deadstemc_seed, & + leafcn_t_evolving = cnveg_nitrogenstate_inst%leafcn_t_evolving_patch(begp:endp), & conv_cflux = conv_c14flux(begp:endp), & wood_product_cflux = wood_product_c14flux(begp:endp), & crop_product_cflux = crop_product_c14flux(begp:endp), & diff --git a/src/biogeochem/test/CNVegComputeSeed_test/test_ComputeSeedAmounts.pf b/src/biogeochem/test/CNVegComputeSeed_test/test_ComputeSeedAmounts.pf index be87b4c989..c0911fb9ee 100644 --- a/src/biogeochem/test/CNVegComputeSeed_test/test_ComputeSeedAmounts.pf +++ b/src/biogeochem/test/CNVegComputeSeed_test/test_ComputeSeedAmounts.pf @@ -92,6 +92,7 @@ contains real(r8), intent(out), optional :: seed_leaf_xfer real(r8), intent(out), optional :: seed_deadstem + real(r8) :: leafcn_t_evolving_patch(bounds%begp:bounds%endp) real(r8) :: leaf_patch(bounds%begp:bounds%endp) real(r8) :: leaf_storage_patch(bounds%begp:bounds%endp) real(r8) :: leaf_xfer_patch(bounds%begp:bounds%endp) @@ -102,6 +103,7 @@ contains real(r8) :: seed_leaf_xfer_patch(bounds%begp:bounds%endp) real(r8) :: seed_deadstem_patch(bounds%begp:bounds%endp) + leafcn_t_evolving_patch(:) = 10._r8 leaf_patch(:) = 0._r8 leaf_patch(POI) = LEAF_POI leaf_storage_patch(:) = 0._r8 @@ -117,6 +119,7 @@ contains call ComputeSeedAmounts(bounds, & this%numf, this%filter, & + leafcn_t_evolving = leafcn_t_evolving_patch, & species = species, & leafc_seed = LEAFC_SEED, & deadstemc_seed = DEADSTEMC_SEED, & diff --git a/src/biogeophys/CanopyFluxesMod.F90 b/src/biogeophys/CanopyFluxesMod.F90 index 0c5431728d..afe665e6d1 100644 --- a/src/biogeophys/CanopyFluxesMod.F90 +++ b/src/biogeophys/CanopyFluxesMod.F90 @@ -41,6 +41,7 @@ module CanopyFluxesMod use HumanIndexMod , only : humanindex_type use ch4Mod , only : ch4_type use PhotosynthesisMod , only : photosyns_type + use CNVegnitrogenstateType, only : cnveg_nitrogenstate_type use GridcellType , only : grc use ColumnType , only : col use PatchType , only : patch @@ -192,7 +193,7 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, energyflux_inst, frictionvel_inst, soilstate_inst, solarabs_inst, surfalb_inst, & temperature_inst, waterfluxbulk_inst, waterstatebulk_inst, & waterdiagnosticbulk_inst, wateratm2lndbulk_inst, ch4_inst, ozone_inst, & - photosyns_inst, & + photosyns_inst, cnveg_nitrogenstate_inst, & humanindex_inst, soil_water_retention_curve, & downreg_patch, leafn_patch, froot_carbon, croot_carbon) ! @@ -264,6 +265,7 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, type(ch4_type) , intent(inout) :: ch4_inst class(ozone_base_type) , intent(inout) :: ozone_inst type(photosyns_type) , intent(inout) :: photosyns_inst + type(cnveg_nitrogenstate_type) , intent(in) :: cnveg_nitrogenstate_inst type(humanindex_type) , intent(inout) :: humanindex_inst class(soil_water_retention_curve_type) , intent(in) :: soil_water_retention_curve real(r8), intent(in) :: downreg_patch(bounds%begp:) ! fractional reduction in GPP due to N limitation (dimensionless) @@ -1126,6 +1128,7 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, svpts(begp:endp), eah(begp:endp), o2(begp:endp), co2(begp:endp), rb(begp:endp), bsun(begp:endp), & bsha(begp:endp), btran(begp:endp), dayl_factor(begp:endp), leafn_patch(begp:endp), & qsatl(begp:endp), qaf(begp:endp), & + cnveg_nitrogenstate_inst, & atm2lnd_inst, temperature_inst, soilstate_inst, waterdiagnosticbulk_inst, surfalb_inst, solarabs_inst, & canopystate_inst, ozone_inst, photosyns_inst, waterfluxbulk_inst, & froot_carbon(begp:endp), croot_carbon(begp:endp)) @@ -1133,6 +1136,7 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, call Photosynthesis (bounds, fn, filterp, & svpts(begp:endp), eah(begp:endp), o2(begp:endp), co2(begp:endp), rb(begp:endp), btran(begp:endp), & dayl_factor(begp:endp), leafn_patch(begp:endp), & + cnveg_nitrogenstate_inst, & atm2lnd_inst, temperature_inst, surfalb_inst, solarabs_inst, & canopystate_inst, ozone_inst, photosyns_inst, phase='sun') endif @@ -1147,6 +1151,7 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, call Photosynthesis (bounds, fn, filterp, & svpts(begp:endp), eah(begp:endp), o2(begp:endp), co2(begp:endp), rb(begp:endp), btran(begp:endp), & dayl_factor(begp:endp), leafn_patch(begp:endp), & + cnveg_nitrogenstate_inst, & atm2lnd_inst, temperature_inst, surfalb_inst, solarabs_inst, & canopystate_inst, ozone_inst, photosyns_inst, phase='sha') end if diff --git a/src/biogeophys/LunaMod.F90 b/src/biogeophys/LunaMod.F90 index 5641846309..950fefbc32 100644 --- a/src/biogeophys/LunaMod.F90 +++ b/src/biogeophys/LunaMod.F90 @@ -291,7 +291,6 @@ subroutine Update_Photosynthesis_Capacity(bounds, fn, filterp, & associate( & c3psn => pftcon%c3psn , & ! photosynthetic pathway: 0. = c4, 1. = c3 slatop => pftcon%slatop , & ! specific leaf area at top of canopy, projected area basis [m^2/gC] - leafcn => pftcon%leafcn , & ! leaf C:N (gC/gN) forc_pbot10 => atm2lnd_inst%forc_pbot240_downscaled_patch , & ! Input: [real(r8) (:) ] 10 day mean atmospheric pressure(Pa) CO2_p240 => atm2lnd_inst%forc_pco2_240_patch , & ! Input: [real(r8) (:) ] 10-day mean CO2 partial pressure (Pa) O2_p240 => atm2lnd_inst%forc_po2_240_patch , & ! Input: [real(r8) (:) ] 10-day mean O2 partial pressure (Pa) diff --git a/src/biogeophys/PhotosynthesisMod.F90 b/src/biogeophys/PhotosynthesisMod.F90 index 2a0ce2d57c..b8fd577382 100644 --- a/src/biogeophys/PhotosynthesisMod.F90 +++ b/src/biogeophys/PhotosynthesisMod.F90 @@ -24,6 +24,7 @@ module PhotosynthesisMod use CIsoAtmTimeseriesMod, only : C14BombSpike, use_c14_bombspike, C13TimeSeries, use_c13_timeseries, nsectors_c14 use atm2lndType , only : atm2lnd_type use CanopyStateType , only : canopystate_type + use CNVegnitrogenstateType, only : cnveg_nitrogenstate_type use WaterDiagnosticBulkType , only : waterdiagnosticbulk_type use WaterFluxBulkType , only : waterfluxbulk_type use SoilStateType , only : soilstate_type @@ -1220,6 +1221,7 @@ end subroutine NewPatchInit subroutine Photosynthesis ( bounds, fn, filterp, & esat_tv, eair, oair, cair, rb, btran, & dayl_factor, leafn, & + cnveg_nitrogenstate_inst, & atm2lnd_inst, temperature_inst, surfalb_inst, solarabs_inst, & canopystate_inst, ozone_inst, photosyns_inst, phase) ! @@ -1249,6 +1251,7 @@ subroutine Photosynthesis ( bounds, fn, filterp, & real(r8) , intent(in) :: btran( bounds%begp: ) ! transpiration wetness factor (0 to 1) [pft] real(r8) , intent(in) :: dayl_factor( bounds%begp: ) ! scalar (0-1) for daylength real(r8) , intent(in) :: leafn( bounds%begp: ) ! leaf N (gN/m2) + type(cnveg_nitrogenstate_type), intent(in) :: cnveg_nitrogenstate_inst type(atm2lnd_type) , intent(in) :: atm2lnd_inst type(temperature_type) , intent(in) :: temperature_inst type(surfalb_type) , intent(in) :: surfalb_inst @@ -1352,6 +1355,7 @@ subroutine Photosynthesis ( bounds, fn, filterp, & real(r8) :: sum_nscaler real(r8) :: total_lai + real(r8) :: leafcn_local integer :: nptreemax real(r8) :: dtime ! land model time step (sec) @@ -1544,13 +1548,17 @@ subroutine Photosynthesis ( bounds, fn, filterp, & do f = 1, fn p = filterp(f) - if (lnc_opt .eqv. .false.) then + if (.not. lnc_opt) then ! Leaf nitrogen concentration at the top of the canopy (g N leaf / m**2 leaf) - - if ( (slatop(patch%itype(p)) *leafcn(patch%itype(p))) .le. 0.0_r8)then + if (use_cn) then ! use the leafcn calculated in subroutine time_evolv_leafcn + leafcn_local = cnveg_nitrogenstate_inst%leafcn_t_evolving_patch(p) + else ! use the leafcn prescribed in the paramfile + leafcn_local = leafcn(ivt(p)) + end if + if ( (slatop(ivt(p)) * leafcn_local) <= 0.0_r8)then call endrun(subgrid_index=p, subgrid_level=subgrid_level_patch, msg="ERROR: slatop or leafcn is zero") end if - lnc(p) = 1._r8 / (slatop(patch%itype(p)) * leafcn(patch%itype(p))) + lnc(p) = 1._r8 / (slatop(ivt(p)) * leafcn_local) end if ! Using the actual nitrogen allocated to the leaf after @@ -2699,6 +2707,7 @@ end subroutine ci_func subroutine PhotosynthesisHydraulicStress ( bounds, fn, filterp, & esat_tv, eair, oair, cair, rb, bsun, bsha, btran, dayl_factor, leafn, & qsatl, qaf, & + cnveg_nitrogenstate_inst, & atm2lnd_inst, temperature_inst, soilstate_inst, waterdiagnosticbulk_inst, & surfalb_inst, solarabs_inst, canopystate_inst, ozone_inst, & photosyns_inst, waterfluxbulk_inst, froot_carbon, croot_carbon) @@ -2741,6 +2750,7 @@ subroutine PhotosynthesisHydraulicStress ( bounds, fn, filterp, & real(r8) , intent(in) :: froot_carbon( bounds%begp: ) ! fine root carbon (gC/m2) [pft] real(r8) , intent(in) :: croot_carbon( bounds%begp: ) ! live coarse root carbon (gC/m2) [pft] + type(cnveg_nitrogenstate_type), intent(in) :: cnveg_nitrogenstate_inst type(atm2lnd_type) , intent(in) :: atm2lnd_inst type(temperature_type) , intent(in) :: temperature_inst type(surfalb_type) , intent(in) :: surfalb_inst @@ -2881,6 +2891,7 @@ subroutine PhotosynthesisHydraulicStress ( bounds, fn, filterp, & real(r8) , pointer :: o3coefg_sha (:) ! o3 coefficient used in rs calculation, shaded real(r8) :: sum_nscaler real(r8) :: total_lai + real(r8) :: leafcn_local integer :: nptreemax real(r8) :: dtime ! land model time step (sec) integer :: j,g ! index @@ -3162,9 +3173,14 @@ subroutine PhotosynthesisHydraulicStress ( bounds, fn, filterp, & do f = 1, fn p = filterp(f) - if (lnc_opt .eqv. .false.) then + if (.not. lnc_opt) then + if (use_cn) then ! use the leafcn calculated in subroutine time_evolv_leafcn + leafcn_local = cnveg_nitrogenstate_inst%leafcn_t_evolving_patch(p) + else ! use the leafcn prescribed in the paramfile + leafcn_local = leafcn(ivt(p)) + end if ! Leaf nitrogen concentration at the top of the canopy (g N leaf / m**2 leaf) - lnc(p) = 1._r8 / (slatop(patch%itype(p)) * leafcn(patch%itype(p))) + lnc(p) = 1._r8 / (slatop(ivt(p)) * leafcn_local) end if ! Using the actual nitrogen allocated to the leaf after diff --git a/src/main/clm_driver.F90 b/src/main/clm_driver.F90 index dcdb5558e6..9969ff4a69 100644 --- a/src/main/clm_driver.F90 +++ b/src/main/clm_driver.F90 @@ -170,6 +170,28 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro call get_proc_bounds(bounds_proc) nclumps = get_proc_clumps() + ! ------------------------------------------------- + ! Obtain updated values of time-evolving parameters + ! + ! As of ctsm5.4 + ! - We have this capability for leafcn by calculating leafcn_t_evolving_patch in subr. time_evolv_leafcn + ! - leafcn_t_evolving_patch's calculation defaults to leafcn's paramfile values + ! - This needs to be called before calling dynSubgrid_driver + ! ------------------------------------------------- + if (use_cn) then + !$OMP PARALLEL DO PRIVATE (nc, bounds_clump) + do nc = 1, nclumps + call get_clump_bounds(nc, bounds_clump) + + call bgc_vegetation_inst%cnveg_nitrogenstate_inst%time_evolv_leafcn( & + bounds_clump, & + filter_inactive_and_active(nc)%num_bgc_vegp, & + filter_inactive_and_active(nc)%bgc_vegp, & + atm2lnd_inst) + end do + !$OMP END PARALLEL DO + end if + ! ======================================================================== ! In the first time step of a startup or hybrid run, we want to update CLM's glacier ! areas to match those given by GLC. This is because, in initialization, we do not yet @@ -723,6 +745,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro temperature_inst, water_inst%waterfluxbulk_inst, water_inst%waterstatebulk_inst, & water_inst%waterdiagnosticbulk_inst, water_inst%wateratm2lndbulk_inst, & ch4_inst, ozone_inst, photosyns_inst, & + bgc_vegetation_inst%cnveg_nitrogenstate_inst, & humanindex_inst, soil_water_retention_curve, & downreg_patch = downreg_patch(bounds_clump%begp:bounds_clump%endp), & leafn_patch = leafn_patch(bounds_clump%begp:bounds_clump%endp), &