From 1e6ba7a8bd5b88a2125dc52546c6b6a146ea7110 Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Wed, 23 Mar 2022 22:06:35 -0600 Subject: [PATCH 01/17] Consolidate some duplicated NutrientCompetition code into a new subr The two NutrientCompetition methods did exactly the same thing for the start of calc_plant_nitrogen_demand, for the calculation of gpp, some MR terms and availc. I have consolidated this duplicated code into a new subroutine, calc_gpp_mr_availc. --- src/biogeochem/CNAllocationMod.F90 | 246 ++++++++++++++++++ src/biogeochem/CNDriverMod.F90 | 11 +- .../NutrientCompetitionCLM45defaultMod.F90 | 155 +---------- .../NutrientCompetitionFlexibleCNMod.F90 | 141 +--------- .../NutrientCompetitionMethodMod.F90 | 49 +--- src/main/clm_initializeMod.F90 | 2 +- src/main/readParamsMod.F90 | 7 +- 7 files changed, 275 insertions(+), 336 deletions(-) create mode 100644 src/biogeochem/CNAllocationMod.F90 diff --git a/src/biogeochem/CNAllocationMod.F90 b/src/biogeochem/CNAllocationMod.F90 new file mode 100644 index 0000000000..089c3b8cbf --- /dev/null +++ b/src/biogeochem/CNAllocationMod.F90 @@ -0,0 +1,246 @@ +module CNAllocationMod + + !----------------------------------------------------------------------- + ! !DESCRIPTION: + ! This module contains subroutines to calculate allocation of C and N to different + ! plant components. It also contains subroutines to calculate gpp and maintenance + ! respiration. + ! + ! !USES: +#include "shr_assert.h" + use shr_kind_mod , only : r8 => shr_kind_r8 + use shr_log_mod , only : errMsg => shr_log_errMsg + use abortutils , only : endrun + use decompMod , only : bounds_type + use clm_varcon , only : secspday + use clm_varctl , only : use_c13, use_c14 + use PatchType , only : patch + use pftconMod , only : pftcon, npcropmin + use CropType , only : crop_type + use PhotosynthesisMod , only : photosyns_type + use CanopyStateType , only : canopystate_type + use CNVegCarbonStateType , only : cnveg_carbonstate_type + use CNVegCarbonFluxType , only : cnveg_carbonflux_type + use CropReprPoolsMod , only : nrepr + ! + implicit none + private + ! + ! !PUBLIC MEMBER FUNCTIONS: + public :: readParams ! Read in parameters from file + public :: calc_gpp_mr_availc + + ! !PRIVATE MEMBER VARIABLES: + type, private :: params_type + real(r8) :: dayscrecover ! number of days to recover negative cpool + end type params_type + + type(params_type), private :: params_inst + + character(len=*), parameter, private :: sourcefile = & + __FILE__ + +contains + + !----------------------------------------------------------------------- + subroutine readParams (ncid) + ! + ! !USES: + use ncdio_pio , only : file_desc_t,ncd_io + ! + ! !ARGUMENTS: + type(file_desc_t),intent(inout) :: ncid ! pio netCDF file id + ! + ! !LOCAL VARIABLES: + character(len=32) :: subname = 'CNAllocParamsType' + character(len=100) :: errCode = '-Error reading in parameters file:' + logical :: readv ! has variable been read in or not + real(r8) :: tempr ! temporary to read in parameter + character(len=100) :: tString ! temp. var for reading + !----------------------------------------------------------------------- + + ! read in parameters + + tString='dayscrecover' + call ncd_io(varname=trim(tString),data=tempr, flag='read', ncid=ncid, readvar=readv) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + params_inst%dayscrecover=tempr + + end subroutine readParams + + !----------------------------------------------------------------------- + subroutine calc_gpp_mr_availc(bounds, num_soilp, filter_soilp, & + crop_inst, photosyns_inst, canopystate_inst, & + cnveg_carbonstate_inst, cnveg_carbonflux_inst, & + c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst) + ! + ! !DESCRIPTION: + ! Calculate total GPP, various maintenance respiration terms, and total available C + ! for allocation + ! + ! !ARGUMENTS: + type(bounds_type) , intent(in) :: bounds + integer , intent(in) :: num_soilp ! number of soil patches in filter + integer , intent(in) :: filter_soilp(:) ! filter for soil patches + type(crop_type) , intent(in) :: crop_inst + type(photosyns_type) , intent(in) :: photosyns_inst + type(canopystate_type) , intent(in) :: canopystate_inst + type(cnveg_carbonstate_type) , intent(in) :: cnveg_carbonstate_inst + type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst + type(cnveg_carbonflux_type) , intent(inout) :: c13_cnveg_carbonflux_inst + type(cnveg_carbonflux_type) , intent(inout) :: c14_cnveg_carbonflux_inst + ! + ! !LOCAL VARIABLES: + integer :: p, k ! indices + integer :: fp ! filter patch index + real(r8) :: dayscrecover ! number of days to recover negative cpool + real(r8) :: mr ! maintenance respiration (gC/m2/s) + real(r8) :: reproductive_mr_tot ! total maintenance respiration from grain components (gC/m2/s) + real(r8) :: curmr, curmr_ratio ! xsmrpool temporary variables + + character(len=*), parameter :: subname = 'calc_gpp_mr_availc' + !----------------------------------------------------------------------- + + ! set number of days to recover negative cpool + dayscrecover = params_inst%dayscrecover + + associate ( & + ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type + woody => pftcon%woody , & ! Input: binary flag for woody lifeform (1=woody, 0=not woody) + croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] flag, true if planted, not harvested + psnsun => photosyns_inst%psnsun_patch , & ! Input: [real(r8) (:) ] sunlit leaf-level photosynthesis (umol CO2 /m**2/ s) + psnsha => photosyns_inst%psnsha_patch , & ! Input: [real(r8) (:) ] shaded leaf-level photosynthesis (umol CO2 /m**2/ s) + c13_psnsun => photosyns_inst%c13_psnsun_patch , & ! Input: [real(r8) (:) ] sunlit leaf-level photosynthesis (umol CO2 /m**2/ s) + c13_psnsha => photosyns_inst%c13_psnsha_patch , & ! Input: [real(r8) (:) ] shaded leaf-level photosynthesis (umol CO2 /m**2/ s) + c14_psnsun => photosyns_inst%c14_psnsun_patch , & ! Input: [real(r8) (:) ] sunlit leaf-level photosynthesis (umol CO2 /m**2/ s) + c14_psnsha => photosyns_inst%c14_psnsha_patch , & ! Input: [real(r8) (:) ] shaded leaf-level photosynthesis (umol CO2 /m**2/ s) + 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 + xsmrpool => cnveg_carbonstate_inst%xsmrpool_patch , & ! Input: [real(r8) (:) ] (gC/m2) temporary photosynthate C pool + leaf_mr => cnveg_carbonflux_inst%leaf_mr_patch , & ! Input: [real(r8) (:) ] + froot_mr => cnveg_carbonflux_inst%froot_mr_patch , & ! Input: [real(r8) (:) ] + livestem_mr => cnveg_carbonflux_inst%livestem_mr_patch , & ! Input: [real(r8) (:) ] + livecroot_mr => cnveg_carbonflux_inst%livecroot_mr_patch , & ! Input: [real(r8) (:) ] + reproductive_mr => cnveg_carbonflux_inst%reproductive_mr_patch , & ! Input: [real(r8) (:,:) ] + psnsun_to_cpool => cnveg_carbonflux_inst%psnsun_to_cpool_patch , & ! Output: [real(r8) (:) ] + psnshade_to_cpool => cnveg_carbonflux_inst%psnshade_to_cpool_patch , & ! Output: [real(r8) (:) ] + gpp => cnveg_carbonflux_inst%gpp_before_downreg_patch , & ! Output: [real(r8) (:) ] GPP flux before downregulation (gC/m2/s) + availc => cnveg_carbonflux_inst%availc_patch , & ! Output: [real(r8) (:) ] C flux available for allocation (gC/m2/s) + leaf_curmr => cnveg_carbonflux_inst%leaf_curmr_patch , & ! Output: [real(r8) (:) ] + froot_curmr => cnveg_carbonflux_inst%froot_curmr_patch , & ! Output: [real(r8) (:) ] + livestem_curmr => cnveg_carbonflux_inst%livestem_curmr_patch , & ! Output: [real(r8) (:) ] + livecroot_curmr => cnveg_carbonflux_inst%livecroot_curmr_patch , & ! Output: [real(r8) (:) ] + reproductive_curmr => cnveg_carbonflux_inst%reproductive_curmr_patch , & ! Output: [real(r8) (:,:) ] + leaf_xsmr => cnveg_carbonflux_inst%leaf_xsmr_patch , & ! Output: [real(r8) (:) ] + froot_xsmr => cnveg_carbonflux_inst%froot_xsmr_patch , & ! Output: [real(r8) (:) ] + livestem_xsmr => cnveg_carbonflux_inst%livestem_xsmr_patch , & ! Output: [real(r8) (:) ] + livecroot_xsmr => cnveg_carbonflux_inst%livecroot_xsmr_patch , & ! Output: [real(r8) (:) ] + reproductive_xsmr => cnveg_carbonflux_inst%reproductive_xsmr_patch , & ! Output: [real(r8) (:,:) ] + cpool_to_xsmrpool => cnveg_carbonflux_inst%cpool_to_xsmrpool_patch , & ! Output: [real(r8) (:) ] + xsmrpool_recover => cnveg_carbonflux_inst%xsmrpool_recover_patch & ! Output: [real(r8) (:) ] C flux assigned to recovery of negative cpool (gC/m2/s) + ) + + do fp = 1,num_soilp + p = filter_soilp(fp) + + ! get the time step total gross photosynthesis + ! this is coming from the canopy fluxes code, and is the + ! gpp that is used to control stomatal conductance. + ! For the nitrogen downregulation code, this is assumed + ! to be the potential gpp, and the actual gpp will be + ! reduced due to N limitation. + + ! Convert psn from umol/m2/s -> gC/m2/s + + ! The input psn (psnsun and psnsha) are expressed per unit LAI + ! in the sunlit and shaded canopy, respectively. These need to be + ! scaled by laisun and laisha to get the total gpp for allocation + + ! Note that no associate statement is used for the isotope carbon fluxes below + ! since they are not always allocated AND nag compiler will complain if you try to + ! to have an associate statement with unallocated memory + + psnsun_to_cpool(p) = psnsun(p) * laisun(p) * 12.011e-6_r8 + psnshade_to_cpool(p) = psnsha(p) * laisha(p) * 12.011e-6_r8 + + if ( use_c13 ) then + c13_cnveg_carbonflux_inst%psnsun_to_cpool_patch(p) = c13_psnsun(p) * laisun(p) * 12.011e-6_r8 + c13_cnveg_carbonflux_inst%psnshade_to_cpool_patch(p) = c13_psnsha(p) * laisha(p) * 12.011e-6_r8 + end if + + if ( use_c14 ) then + c14_cnveg_carbonflux_inst%psnsun_to_cpool_patch(p) = c14_psnsun(p) * laisun(p) * 12.011e-6_r8 + c14_cnveg_carbonflux_inst%psnshade_to_cpool_patch(p) = c14_psnsha(p) * laisha(p) * 12.011e-6_r8 + end if + + gpp(p) = psnsun_to_cpool(p) + psnshade_to_cpool(p) + + ! get the time step total maintenance respiration + ! These fluxes should already be in gC/m2/s + + mr = leaf_mr(p) + froot_mr(p) + if (woody(ivt(p)) == 1.0_r8) then + mr = mr + livestem_mr(p) + livecroot_mr(p) + else if (ivt(p) >= npcropmin) then + if (croplive(p)) then + reproductive_mr_tot = 0._r8 + do k = 1, nrepr + reproductive_mr_tot = reproductive_mr_tot + reproductive_mr(p,k) + end do + mr = mr + livestem_mr(p) + reproductive_mr_tot + end if + end if + + ! carbon flux available for allocation + availc(p) = gpp(p) - mr + + ! new code added for isotope calculations, 7/1/05, PET + ! If mr > gpp, then some mr comes from gpp, the rest comes from + ! cpool (xsmr) + if (mr > 0._r8 .and. availc(p) < 0._r8) then + curmr = gpp(p) + curmr_ratio = curmr / mr + else + curmr_ratio = 1._r8 + end if + leaf_curmr(p) = leaf_mr(p) * curmr_ratio + leaf_xsmr(p) = leaf_mr(p) - leaf_curmr(p) + froot_curmr(p) = froot_mr(p) * curmr_ratio + froot_xsmr(p) = froot_mr(p) - froot_curmr(p) + livestem_curmr(p) = livestem_mr(p) * curmr_ratio + livestem_xsmr(p) = livestem_mr(p) - livestem_curmr(p) + livecroot_curmr(p) = livecroot_mr(p) * curmr_ratio + livecroot_xsmr(p) = livecroot_mr(p) - livecroot_curmr(p) + do k = 1, nrepr + reproductive_curmr(p,k) = reproductive_mr(p,k) * curmr_ratio + reproductive_xsmr(p,k) = reproductive_mr(p,k) - reproductive_curmr(p,k) + end do + + ! no allocation when available c is negative + availc(p) = max(availc(p),0.0_r8) + + ! test for an xsmrpool deficit + if (xsmrpool(p) < 0.0_r8) then + ! Running a deficit in the xsmrpool, so the first priority is to let + ! some availc from this timestep accumulate in xsmrpool. + ! Determine rate of recovery for xsmrpool deficit + + xsmrpool_recover(p) = -xsmrpool(p)/(dayscrecover*secspday) + if (xsmrpool_recover(p) < availc(p)) then + ! available carbon reduced by amount for xsmrpool recovery + availc(p) = availc(p) - xsmrpool_recover(p) + else + ! all of the available carbon goes to xsmrpool recovery + xsmrpool_recover(p) = availc(p) + availc(p) = 0.0_r8 + end if + cpool_to_xsmrpool(p) = xsmrpool_recover(p) + end if + + end do + + end associate + + end subroutine calc_gpp_mr_availc + +end module CNAllocationMod diff --git a/src/biogeochem/CNDriverMod.F90 b/src/biogeochem/CNDriverMod.F90 index 7441a9fa07..cfe00401b8 100644 --- a/src/biogeochem/CNDriverMod.F90 +++ b/src/biogeochem/CNDriverMod.F90 @@ -114,6 +114,7 @@ subroutine CNDriverNoLeaching(bounds, use clm_varpar , only: nlevdecomp, ndecomp_cascade_transitions, ndecomp_pools use subgridAveMod , only: p2c use CropType , only: crop_type + use CNAllocationMod , only: calc_gpp_mr_availc use CNNDynamicsMod , only: CNNDeposition,CNNFixation, CNNFert, CNSoyfix,CNFreeLivingFixation use CNMRespMod , only: CNMResp use CNFUNMod , only: CNFUNInit !, CNFUN @@ -386,10 +387,18 @@ subroutine CNDriverNoLeaching(bounds, end if + call t_startf('cnalloc') + call calc_gpp_mr_availc( & + bounds, num_soilp, filter_soilp, & + crop_inst, photosyns_inst, canopystate_inst, & + cnveg_carbonstate_inst, cnveg_carbonflux_inst, & + c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst) + call t_stopf('cnalloc') + call t_startf('calc_plant_nutrient_demand') call nutrient_competition_method%calc_plant_nutrient_demand ( & bounds, num_soilp, filter_soilp, & - photosyns_inst, crop_inst, canopystate_inst, & + crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & diff --git a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 index 9853c1ef8e..9f251faa24 100644 --- a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 +++ b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 @@ -16,7 +16,6 @@ module NutrientCompetitionCLM45defaultMod use ColumnType , only : col use PatchType , only : patch use NutrientCompetitionMethodMod, only : nutrient_competition_method_type - use NutrientCompetitionMethodMod, only : params_inst use CropReprPoolsMod , only : nrepr !use clm_varctl , only : iulog ! @@ -443,7 +442,7 @@ end subroutine calc_plant_cn_alloc !----------------------------------------------------------------------- subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& - photosyns_inst, crop_inst, canopystate_inst, & + crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & @@ -453,7 +452,6 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& ! ! !USES: use CanopyStateType , only : canopystate_type - use PhotosynthesisMod , only : photosyns_type use CropType , only : crop_type use CNVegStateType , only : cnveg_state_type use CNVegCarbonStateType , only : cnveg_carbonstate_type @@ -470,9 +468,8 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! filter for soil patches - type(photosyns_type) , intent(in) :: photosyns_inst type(crop_type) , intent(in) :: crop_inst - type(canopystate_type) , intent(in) :: canopystate_inst + type(canopystate_type) , intent(in) :: canopystate_inst ! unused in this version type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst @@ -488,7 +485,7 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& !----------------------------------------------------------------------- call this%calc_plant_nitrogen_demand(bounds, num_soilp, filter_soilp, & - photosyns_inst, crop_inst, canopystate_inst, & + crop_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & @@ -499,7 +496,7 @@ end subroutine calc_plant_nutrient_demand !----------------------------------------------------------------------- subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & - photosyns_inst, crop_inst, canopystate_inst, & + crop_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & @@ -509,11 +506,7 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & use pftconMod , only : npcropmin, pftcon use pftconMod , only : ntmp_soybean, nirrig_tmp_soybean use pftconMod , only : ntrp_soybean, nirrig_trp_soybean - use clm_varcon , only : secspday - use clm_varctl , only : use_c13, use_c14 use clm_time_manager , only : get_step_size_real - use CanopyStateType , only : canopystate_type - use PhotosynthesisMod , only : photosyns_type use CropType , only : crop_type use CNVegStateType , only : cnveg_state_type use CNVegCarbonStateType , only : cnveg_carbonstate_type @@ -527,9 +520,7 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! filter for soil patches - type(photosyns_type) , intent(in) :: photosyns_inst type(crop_type) , intent(in) :: crop_inst - type(canopystate_type) , intent(in) :: canopystate_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst @@ -541,20 +532,16 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & real(r8) , intent(out) :: arepr(bounds%begp:,:) ! ! !LOCAL VARIABLES: - integer :: c,p,l,j,k ! indices + integer :: p,l,j,k ! indices integer :: fp ! lake filter patch index - real(r8):: mr ! maintenance respiration (gC/m2/s) - real(r8):: reproductive_mr_tot ! total maintenance respiration from grain components (gC/m2/s) real(r8):: f1,f2,f3,f4,g1,g2 ! allocation parameters real(r8):: g1a ! g1 included in allocation/allometry real(r8):: cnl,cnfr,cnlw,cndw ! C:N ratios for leaf, fine root, and wood - real(r8):: curmr, curmr_ratio ! xsmrpool temporary variables real(r8):: f5(nrepr) ! reproductive allocation parameters real(r8):: cng ! C:N ratio for grain (= cnlw for now; slevis) real(r8):: fleaf ! fraction allocated to leaf real(r8):: t1 ! temporary variable real(r8):: dt ! model time step - real(r8):: dayscrecover ! number of days to recover negative cpool real(r8):: f5_tot ! sum of f5 terms real(r8):: f5_n_tot ! sum of f5 terms converted from C to N @@ -592,15 +579,7 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & declfact => pftcon%declfact , & ! Input: 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-deciduous leaf habit (0 or 1) - psnsun => photosyns_inst%psnsun_patch , & ! Input: [real(r8) (:) ] sunlit leaf-level photosynthesis (umol CO2 /m**2/ s) - psnsha => photosyns_inst%psnsha_patch , & ! Input: [real(r8) (:) ] shaded leaf-level photosynthesis (umol CO2 /m**2/ s) - c13_psnsun => photosyns_inst%c13_psnsun_patch , & ! Input: [real(r8) (:) ] sunlit leaf-level photosynthesis (umol CO2 /m**2/ s) - c13_psnsha => photosyns_inst%c13_psnsha_patch , & ! Input: [real(r8) (:) ] shaded leaf-level photosynthesis (umol CO2 /m**2/ s) - c14_psnsun => photosyns_inst%c14_psnsun_patch , & ! Input: [real(r8) (:) ] sunlit leaf-level photosynthesis (umol CO2 /m**2/ s) - c14_psnsha => photosyns_inst%c14_psnsha_patch , & ! Input: [real(r8) (:) ] shaded leaf-level photosynthesis (umol CO2 /m**2/ s) - - 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 + hui => crop_inst%gddplant_patch , & ! Input: [real(r8) (:) ] =gdd since planting (gddplant) leafout => crop_inst%gddtsoi_patch , & ! Input: [real(r8) (:) ] =gdd from top soil layer temperature @@ -622,36 +601,16 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & annsum_potential_gpp => cnveg_state_inst%annsum_potential_gpp_patch , & ! Output: [real(r8) (:) ] annual sum of potential GPP annmax_retransn => cnveg_state_inst%annmax_retransn_patch , & ! Output: [real(r8) (:) ] annual max of retranslocated N pool - xsmrpool => cnveg_carbonstate_inst%xsmrpool_patch , & ! Input: [real(r8) (:) ] (gC/m2) temporary photosynthate C pool - leafc => cnveg_carbonstate_inst%leafc_patch , & ! Input: [real(r8) (:) ] + leafc => cnveg_carbonstate_inst%leafc_patch , & ! Input: [real(r8) (:) ] frootc => cnveg_carbonstate_inst%frootc_patch , & ! Input: [real(r8) (:) ] livestemc => cnveg_carbonstate_inst%livestemc_patch , & ! Input: [real(r8) (:) ] retransn => cnveg_nitrogenstate_inst%retransn_patch , & ! Input: [real(r8) (:) ] (gN/m2) plant pool of retranslocated N annsum_npp => cnveg_carbonflux_inst%annsum_npp_patch , & ! Input: [real(r8) (:) ] annual sum of NPP, for wood allocation - leaf_mr => cnveg_carbonflux_inst%leaf_mr_patch , & ! Input: [real(r8) (:) ] - froot_mr => cnveg_carbonflux_inst%froot_mr_patch , & ! Input: [real(r8) (:) ] - livestem_mr => cnveg_carbonflux_inst%livestem_mr_patch , & ! Input: [real(r8) (:) ] - livecroot_mr => cnveg_carbonflux_inst%livecroot_mr_patch , & ! Input: [real(r8) (:) ] - reproductive_mr => cnveg_carbonflux_inst%reproductive_mr_patch , & ! Input: [real(r8) (:,:) ] gpp => cnveg_carbonflux_inst%gpp_before_downreg_patch , & ! Output: [real(r8) (:) ] GPP flux before downregulation (gC/m2/s) availc => cnveg_carbonflux_inst%availc_patch , & ! Output: [real(r8) (:) ] C flux available for allocation (gC/m2/s) - xsmrpool_recover => cnveg_carbonflux_inst%xsmrpool_recover_patch , & ! Output: [real(r8) (:) ] C flux assigned to recovery of negative cpool (gC/m2/s) - psnsun_to_cpool => cnveg_carbonflux_inst%psnsun_to_cpool_patch , & ! Output: [real(r8) (:) ] - psnshade_to_cpool => cnveg_carbonflux_inst%psnshade_to_cpool_patch , & ! Output: [real(r8) (:) ] - leaf_curmr => cnveg_carbonflux_inst%leaf_curmr_patch , & ! Output: [real(r8) (:) ] - froot_curmr => cnveg_carbonflux_inst%froot_curmr_patch , & ! Output: [real(r8) (:) ] - livestem_curmr => cnveg_carbonflux_inst%livestem_curmr_patch , & ! Output: [real(r8) (:) ] - livecroot_curmr => cnveg_carbonflux_inst%livecroot_curmr_patch , & ! Output: [real(r8) (:) ] - reproductive_curmr => cnveg_carbonflux_inst%reproductive_curmr_patch , & ! Output: [real(r8) (:,:) ] - leaf_xsmr => cnveg_carbonflux_inst%leaf_xsmr_patch , & ! Output: [real(r8) (:) ] - froot_xsmr => cnveg_carbonflux_inst%froot_xsmr_patch , & ! Output: [real(r8) (:) ] - livestem_xsmr => cnveg_carbonflux_inst%livestem_xsmr_patch , & ! Output: [real(r8) (:) ] - livecroot_xsmr => cnveg_carbonflux_inst%livecroot_xsmr_patch , & ! Output: [real(r8) (:) ] - reproductive_xsmr => cnveg_carbonflux_inst%reproductive_xsmr_patch , & ! Output: [real(r8) (:,:) ] - cpool_to_xsmrpool => cnveg_carbonflux_inst%cpool_to_xsmrpool_patch , & ! Output: [real(r8) (:) ] - + plant_ndemand => cnveg_nitrogenflux_inst%plant_ndemand_patch , & ! Output: [real(r8) (:) ] N flux required to support initial GPP (gN/m2/s) avail_retransn => cnveg_nitrogenflux_inst%avail_retransn_patch , & ! Output: [real(r8) (:) ] N flux available from retranslocation pool (gN/m2/s) retransn_to_npool => cnveg_nitrogenflux_inst%retransn_to_npool_patch , & ! Output: [real(r8) (:) ] deployment of retranslocated N (gN/m2/s) @@ -664,108 +623,10 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! set time steps dt = get_step_size_real() - ! set number of days to recover negative cpool - dayscrecover = params_inst%dayscrecover - ! loop over patches to assess the total plant N demand do fp = 1,num_soilp p = filter_soilp(fp) - c = patch%column(p) - - ! get the time step total gross photosynthesis - ! this is coming from the canopy fluxes code, and is the - ! gpp that is used to control stomatal conductance. - ! For the nitrogen downregulation code, this is assumed - ! to be the potential gpp, and the actual gpp will be - ! reduced due to N limitation. - - ! Convert psn from umol/m2/s -> gC/m2/s - - ! The input psn (psnsun and psnsha) are expressed per unit LAI - ! in the sunlit and shaded canopy, respectively. These need to be - ! scaled by laisun and laisha to get the total gpp for allocation - - ! Note that no associate statement is used for the isotope carbon fluxes below - ! since they are not always allocated AND nag compiler will complain if you try to - ! to have an associate statement with unallocated memory - - psnsun_to_cpool(p) = psnsun(p) * laisun(p) * 12.011e-6_r8 - psnshade_to_cpool(p) = psnsha(p) * laisha(p) * 12.011e-6_r8 - - if ( use_c13 ) then - c13_cnveg_carbonflux_inst%psnsun_to_cpool_patch(p) = c13_psnsun(p) * laisun(p) * 12.011e-6_r8 - c13_cnveg_carbonflux_inst%psnshade_to_cpool_patch(p) = c13_psnsha(p) * laisha(p) * 12.011e-6_r8 - endif - - if ( use_c14 ) then - c14_cnveg_carbonflux_inst%psnsun_to_cpool_patch(p) = c14_psnsun(p) * laisun(p) * 12.011e-6_r8 - c14_cnveg_carbonflux_inst%psnshade_to_cpool_patch(p) = c14_psnsha(p) * laisha(p) * 12.011e-6_r8 - endif - - gpp(p) = psnsun_to_cpool(p) + psnshade_to_cpool(p) - ! get the time step total maintenance respiration - ! These fluxes should already be in gC/m2/s - - mr = leaf_mr(p) + froot_mr(p) - if (woody(ivt(p)) == 1.0_r8) then - mr = mr + livestem_mr(p) + livecroot_mr(p) - else if (ivt(p) >= npcropmin) then - if (croplive(p)) then - reproductive_mr_tot = 0._r8 - do k = 1, nrepr - reproductive_mr_tot = reproductive_mr_tot + reproductive_mr(p,k) - end do - mr = mr + livestem_mr(p) + reproductive_mr_tot - end if - end if - - ! carbon flux available for allocation - availc(p) = gpp(p) - mr - - ! new code added for isotope calculations, 7/1/05, PET - ! If mr > gpp, then some mr comes from gpp, the rest comes from - ! cpool (xsmr) - if (mr > 0._r8 .and. availc(p) < 0._r8) then - curmr = gpp(p) - curmr_ratio = curmr / mr - else - curmr_ratio = 1._r8 - end if - leaf_curmr(p) = leaf_mr(p) * curmr_ratio - leaf_xsmr(p) = leaf_mr(p) - leaf_curmr(p) - froot_curmr(p) = froot_mr(p) * curmr_ratio - froot_xsmr(p) = froot_mr(p) - froot_curmr(p) - livestem_curmr(p) = livestem_mr(p) * curmr_ratio - livestem_xsmr(p) = livestem_mr(p) - livestem_curmr(p) - livecroot_curmr(p) = livecroot_mr(p) * curmr_ratio - livecroot_xsmr(p) = livecroot_mr(p) - livecroot_curmr(p) - do k = 1, nrepr - reproductive_curmr(p,k) = reproductive_mr(p,k) * curmr_ratio - reproductive_xsmr(p,k) = reproductive_mr(p,k) - reproductive_curmr(p,k) - end do - - ! no allocation when available c is negative - availc(p) = max(availc(p),0.0_r8) - - ! test for an xsmrpool deficit - if (xsmrpool(p) < 0.0_r8) then - ! Running a deficit in the xsmrpool, so the first priority is to let - ! some availc from this timestep accumulate in xsmrpool. - ! Determine rate of recovery for xsmrpool deficit - - xsmrpool_recover(p) = -xsmrpool(p)/(dayscrecover*secspday) - if (xsmrpool_recover(p) < availc(p)) then - ! available carbon reduced by amount for xsmrpool recovery - availc(p) = availc(p) - xsmrpool_recover(p) - else - ! all of the available carbon goes to xsmrpool recovery - xsmrpool_recover(p) = availc(p) - availc(p) = 0.0_r8 - end if - cpool_to_xsmrpool(p) = xsmrpool_recover(p) - end if - f1 = froot_leaf(ivt(p)) f2 = croot_stem(ivt(p)) diff --git a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 index 15ae445c1a..f85cf46743 100644 --- a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 +++ b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 @@ -23,7 +23,6 @@ module NutrientCompetitionFlexibleCNMod use ColumnType , only : col use PatchType , only : patch use NutrientCompetitionMethodMod, only : nutrient_competition_method_type - use NutrientCompetitionMethodMod, only : params_inst use CropReprPoolsMod , only : nrepr use clm_varctl , only : iulog ! @@ -895,7 +894,7 @@ end subroutine calc_plant_cn_alloc ! ----------------------------------------------------------------------- subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& - photosyns_inst, crop_inst, canopystate_inst, & + crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & @@ -905,7 +904,6 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& ! ! !USES: use CanopyStateType , only : canopystate_type - use PhotosynthesisMod , only : photosyns_type use CropType , only : crop_type use CNVegStateType , only : cnveg_state_type use CNVegCarbonStateType , only : cnveg_carbonstate_type @@ -920,7 +918,6 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! filter for soil patches - type(photosyns_type) , intent(in) :: photosyns_inst type(crop_type) , intent(in) :: crop_inst type(canopystate_type) , intent(in) :: canopystate_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst @@ -938,7 +935,7 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& !----------------------------------------------------------------------- call this%calc_plant_nitrogen_demand(bounds, num_soilp, filter_soilp, & - photosyns_inst, crop_inst, canopystate_inst, & + crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & @@ -951,7 +948,7 @@ end subroutine calc_plant_nutrient_demand !----------------------------------------------------------------------- subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & - photosyns_inst, crop_inst, canopystate_inst, & + crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & @@ -963,12 +960,10 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & use pftconMod , only : npcropmin, pftcon use pftconMod , only : ntmp_soybean, nirrig_tmp_soybean use pftconMod , only : ntrp_soybean, nirrig_trp_soybean - use clm_varcon , only : secspday, dzsoi_decomp - use clm_varctl , only : use_c13, use_c14 + use clm_varcon , only : dzsoi_decomp use clm_varpar , only : nlevdecomp use clm_time_manager , only : get_step_size_real use CanopyStateType , only : canopystate_type - use PhotosynthesisMod , only : photosyns_type use CropType , only : crop_type use CNVegStateType , only : cnveg_state_type use CNVegCarbonStateType , only : cnveg_carbonstate_type @@ -986,7 +981,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! filter for soil patches - type(photosyns_type) , intent(in) :: photosyns_inst type(crop_type) , intent(in) :: crop_inst type(canopystate_type) , intent(in) :: canopystate_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst @@ -1005,18 +999,14 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! !LOCAL VARIABLES: integer :: c, p, j, k ! indices integer :: fp ! lake filter patch index - real(r8) :: mr ! maintenance respiration (gC/m2/s) - real(r8) :: reproductive_mr_tot ! total maintenance respiration from grain components (gC/m2/s) real(r8) :: f1, f2, f3, f4, g1, g2 ! allocation parameters real(r8) :: g1a ! g1 included in allocation/allometry real(r8) :: cnl, cnfr, cnlw, cndw ! C:N ratios for leaf, fine root, and wood - real(r8) :: curmr, curmr_ratio ! xsmrpool temporary variables real(r8) :: f5(nrepr) ! reproductive allocation parameters real(r8) :: cng ! C:N ratio for grain (= cnlw for now; slevis) real(r8) :: fleaf ! fraction allocated to leaf real(r8) :: t1 ! temporary variable real(r8) :: dt ! model time step - real(r8) :: dayscrecover ! number of days to recover negative cpool real(r8) :: f5_tot ! sum of f5 terms real(r8) :: f5_n_tot ! sum of f5 terms converted from C to N real(r8) :: f_N (bounds%begp:bounds%endp) @@ -1068,12 +1058,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & declfact => pftcon%declfact , & ! Input: 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-deciduous leaf habit (0 or 1) - psnsun => photosyns_inst%psnsun_patch , & ! Input: [real(r8) (:) ] sunlit leaf-level photosynthesis (umol CO2 /m**2/ s) - psnsha => photosyns_inst%psnsha_patch , & ! Input: [real(r8) (:) ] shaded leaf-level photosynthesis (umol CO2 /m**2/ s) - c13_psnsun => photosyns_inst%c13_psnsun_patch , & ! Input: [real(r8) (:) ] sunlit leaf-level photosynthesis (umol CO2 /m**2/ s) - c13_psnsha => photosyns_inst%c13_psnsha_patch , & ! Input: [real(r8) (:) ] shaded leaf-level photosynthesis (umol CO2 /m**2/ s) - c14_psnsun => photosyns_inst%c14_psnsun_patch , & ! Input: [real(r8) (:) ] sunlit leaf-level photosynthesis (umol CO2 /m**2/ s) - c14_psnsha => photosyns_inst%c14_psnsha_patch , & ! Input: [real(r8) (:) ] shaded leaf-level photosynthesis (umol CO2 /m**2/ s) 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 @@ -1098,7 +1082,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & annsum_potential_gpp => cnveg_state_inst%annsum_potential_gpp_patch , & ! Output: [real(r8) (:) ] annual sum of potential GPP annmax_retransn => cnveg_state_inst%annmax_retransn_patch , & ! Output: [real(r8) (:) ] annual max of retranslocated N pool - xsmrpool => cnveg_carbonstate_inst%xsmrpool_patch , & ! Input: [real(r8) (:) ] (gC/m2) temporary photosynthate C pool leafc => cnveg_carbonstate_inst%leafc_patch , & ! Input: [real(r8) (:) ] frootc => cnveg_carbonstate_inst%frootc_patch , & ! Input: [real(r8) (:) ] livestemc => cnveg_carbonstate_inst%livestemc_patch , & ! Input: [real(r8) (:) ] @@ -1106,27 +1089,8 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & retransn => cnveg_nitrogenstate_inst%retransn_patch , & ! Input: [real(r8) (:) ] (gN/m2) plant pool of retranslocated N annsum_npp => cnveg_carbonflux_inst%annsum_npp_patch , & ! Input: [real(r8) (:) ] annual sum of NPP, for wood allocation - leaf_mr => cnveg_carbonflux_inst%leaf_mr_patch , & ! Input: [real(r8) (:) ] - froot_mr => cnveg_carbonflux_inst%froot_mr_patch , & ! Input: [real(r8) (:) ] - livestem_mr => cnveg_carbonflux_inst%livestem_mr_patch , & ! Input: [real(r8) (:) ] - livecroot_mr => cnveg_carbonflux_inst%livecroot_mr_patch , & ! Input: [real(r8) (:) ] - reproductive_mr => cnveg_carbonflux_inst%reproductive_mr_patch , & ! Input: [real(r8) (:,:) ] gpp => cnveg_carbonflux_inst%gpp_before_downreg_patch , & ! Output: [real(r8) (:) ] GPP flux before downregulation (gC/m2/s) availc => cnveg_carbonflux_inst%availc_patch , & ! Output: [real(r8) (:) ] C flux available for allocation (gC/m2/s) - xsmrpool_recover => cnveg_carbonflux_inst%xsmrpool_recover_patch , & ! Output: [real(r8) (:) ] C flux assigned to recovery of negative cpool (gC/m2/s) - psnsun_to_cpool => cnveg_carbonflux_inst%psnsun_to_cpool_patch , & ! Output: [real(r8) (:) ] - psnshade_to_cpool => cnveg_carbonflux_inst%psnshade_to_cpool_patch , & ! Output: [real(r8) (:) ] - leaf_curmr => cnveg_carbonflux_inst%leaf_curmr_patch , & ! Output: [real(r8) (:) ] - froot_curmr => cnveg_carbonflux_inst%froot_curmr_patch , & ! Output: [real(r8) (:) ] - livestem_curmr => cnveg_carbonflux_inst%livestem_curmr_patch , & ! Output: [real(r8) (:) ] - livecroot_curmr => cnveg_carbonflux_inst%livecroot_curmr_patch , & ! Output: [real(r8) (:) ] - reproductive_curmr => cnveg_carbonflux_inst%reproductive_curmr_patch , & ! Output: [real(r8) (:,:) ] - leaf_xsmr => cnveg_carbonflux_inst%leaf_xsmr_patch , & ! Output: [real(r8) (:) ] - froot_xsmr => cnveg_carbonflux_inst%froot_xsmr_patch , & ! Output: [real(r8) (:) ] - livestem_xsmr => cnveg_carbonflux_inst%livestem_xsmr_patch , & ! Output: [real(r8) (:) ] - livecroot_xsmr => cnveg_carbonflux_inst%livecroot_xsmr_patch , & ! Output: [real(r8) (:) ] - reproductive_xsmr => cnveg_carbonflux_inst%reproductive_xsmr_patch , & ! Output: [real(r8) (:,:) ] - cpool_to_xsmrpool => cnveg_carbonflux_inst%cpool_to_xsmrpool_patch , & ! Output: [real(r8) (:) ] leafn => cnveg_nitrogenstate_inst%leafn_patch , & ! Input: [real(r8) (:) ] (gN/m2) leaf N plant_ndemand => cnveg_nitrogenflux_inst%plant_ndemand_patch , & ! Output: [real(r8) (:) ] N flux required to support initial GPP (gN/m2/s) @@ -1147,105 +1111,10 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! set time steps dt = get_step_size_real() - ! set number of days to recover negative cpool - dayscrecover = params_inst%dayscrecover ! loop over patches to assess the total plant N demand + ! loop over patches to assess the total plant N demand do fp = 1,num_soilp p = filter_soilp(fp) - ! get the time step total gross photosynthesis - ! this is coming from the canopy fluxes code, and is the - ! gpp that is used to control stomatal conductance. - ! For the nitrogen downregulation code, this is assumed - ! to be the potential gpp, and the actual gpp will be - ! reduced due to N limitation. - - ! Convert psn from umol/m2/s -> gC/m2/s - - ! The input psn (psnsun and psnsha) are expressed per unit LAI - ! in the sunlit and shaded canopy, respectively. These need to be - ! scaled by laisun and laisha to get the total gpp for allocation - - ! Note that no associate statement is used for the isotope carbon fluxes below - ! since they are not always allocated AND nag compiler will complain if you try to - ! to have an associate statement with unallocated memory - - psnsun_to_cpool(p) = psnsun(p) * laisun(p) * 12.011e-6_r8 - psnshade_to_cpool(p) = psnsha(p) * laisha(p) * 12.011e-6_r8 - - if ( use_c13 ) then - c13_cnveg_carbonflux_inst%psnsun_to_cpool_patch(p) = c13_psnsun(p) * laisun(p) * 12.011e-6_r8 - c13_cnveg_carbonflux_inst%psnshade_to_cpool_patch(p) = c13_psnsha(p) * laisha(p) * 12.011e-6_r8 - endif - - if ( use_c14 ) then - c14_cnveg_carbonflux_inst%psnsun_to_cpool_patch(p) = c14_psnsun(p) * laisun(p) * 12.011e-6_r8 - c14_cnveg_carbonflux_inst%psnshade_to_cpool_patch(p) = c14_psnsha(p) * laisha(p) * 12.011e-6_r8 - endif - - gpp(p) = psnsun_to_cpool(p) + psnshade_to_cpool(p) - - ! get the time step total maintenance respiration - ! These fluxes should already be in gC/m2/s - - mr = leaf_mr(p) + froot_mr(p) - if (woody(ivt(p)) == 1.0_r8) then - mr = mr + livestem_mr(p) + livecroot_mr(p) - else if (ivt(p) >= npcropmin) then - if (croplive(p)) then - reproductive_mr_tot = 0._r8 - do k = 1, nrepr - reproductive_mr_tot = reproductive_mr_tot + reproductive_mr(p,k) - end do - mr = mr + livestem_mr(p) + reproductive_mr_tot - end if - end if - - ! carbon flux available for allocation - availc(p) = gpp(p) - mr - - ! new code added for isotope calculations, 7/1/05, PET - ! If mr > gpp, then some mr comes from gpp, the rest comes from - ! cpool (xsmr) - if (mr > 0._r8 .and. availc(p) < 0._r8) then - curmr = gpp(p) - curmr_ratio = curmr / mr - else - curmr_ratio = 1._r8 - end if - leaf_curmr(p) = leaf_mr(p) * curmr_ratio - leaf_xsmr(p) = leaf_mr(p) - leaf_curmr(p) - froot_curmr(p) = froot_mr(p) * curmr_ratio - froot_xsmr(p) = froot_mr(p) - froot_curmr(p) - livestem_curmr(p) = livestem_mr(p) * curmr_ratio - livestem_xsmr(p) = livestem_mr(p) - livestem_curmr(p) - livecroot_curmr(p) = livecroot_mr(p) * curmr_ratio - livecroot_xsmr(p) = livecroot_mr(p) - livecroot_curmr(p) - do k = 1, nrepr - reproductive_curmr(p,k) = reproductive_mr(p,k) * curmr_ratio - reproductive_xsmr(p,k) = reproductive_mr(p,k) - reproductive_curmr(p,k) - end do - - ! no allocation when available c is negative - availc(p) = max(availc(p),0.0_r8) - - ! test for an xsmrpool deficit - if (xsmrpool(p) < 0.0_r8) then - ! Running a deficit in the xsmrpool, so the first priority is to let - ! some availc from this timestep accumulate in xsmrpool. - ! Determine rate of recovery for xsmrpool deficit - - xsmrpool_recover(p) = -xsmrpool(p)/(dayscrecover*secspday) - if (xsmrpool_recover(p) < availc(p)) then - ! available carbon reduced by amount for xsmrpool recovery - availc(p) = availc(p) - xsmrpool_recover(p) - else - ! all of the available carbon goes to xsmrpool recovery - xsmrpool_recover(p) = availc(p) - availc(p) = 0.0_r8 - end if - cpool_to_xsmrpool(p) = xsmrpool_recover(p) - end if - f1 = froot_leaf(ivt(p)) f2 = croot_stem(ivt(p)) diff --git a/src/biogeochem/NutrientCompetitionMethodMod.F90 b/src/biogeochem/NutrientCompetitionMethodMod.F90 index e306544423..230c3b07de 100644 --- a/src/biogeochem/NutrientCompetitionMethodMod.F90 +++ b/src/biogeochem/NutrientCompetitionMethodMod.F90 @@ -4,7 +4,7 @@ module NutrientCompetitionMethodMod ! !DESCRIPTION: ! Abstract base class for functions to calculate nutrient competition ! - ! Created by Jinyun Tang, following Bill Sack's implementation of polymorphism + ! Created by Jinyun Tang, following Bill Sacks's implementation of polymorphism ! !USES: use shr_kind_mod , only : r8 => shr_kind_r8 implicit none @@ -20,9 +20,6 @@ module NutrientCompetitionMethodMod ! initialization procedure(init_interface), public, deferred :: init - ! Read in parameters - procedure, public :: readParams - ! compute plant nutrient demand procedure(calc_plant_nutrient_demand_interface), public, deferred :: calc_plant_nutrient_demand @@ -31,12 +28,6 @@ module NutrientCompetitionMethodMod end type nutrient_competition_method_type - type, public :: params_type - real(r8) :: dayscrecover ! number of days to recover negative cpool - end type params_type - ! - type(params_type), public, protected :: params_inst ! params_inst is populated in readParamsMod - abstract interface ! Note: The following code is adapted based on what Bill Scaks has done for soil water retention curve @@ -69,7 +60,7 @@ end subroutine init_interface !--------------------------------------------------------------------------- subroutine calc_plant_nutrient_demand_interface (this, bounds, num_soilp, filter_soilp, & - photosyns_inst, crop_inst, canopystate_inst, & + crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & @@ -83,7 +74,6 @@ subroutine calc_plant_nutrient_demand_interface (this, bounds, num_soilp, filter ! USES use shr_kind_mod , only : r8 => shr_kind_r8 use decompMod , only : bounds_type - use PhotosynthesisMod , only : photosyns_type use CropType , only : crop_type use CanopyStateType , only : canopystate_type use CNVegStateType , only : cnveg_state_type @@ -101,7 +91,6 @@ subroutine calc_plant_nutrient_demand_interface (this, bounds, num_soilp, filter type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! filter for soil patches - type(photosyns_type) , intent(in) :: photosyns_inst type(crop_type) , intent(in) :: crop_inst type(canopystate_type) , intent(in) :: canopystate_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst @@ -165,38 +154,4 @@ end subroutine calc_plant_nutrient_competition_interface end interface - character(len=*), parameter, private :: sourcefile = & - __FILE__ - -contains - - !----------------------------------------------------------------------- - subroutine readParams (this, ncid ) - ! - ! !USES: - use shr_log_mod , only : errMsg => shr_log_errMsg - use ncdio_pio , only : file_desc_t,ncd_io - use abortutils , only : endrun - ! - ! !ARGUMENTS: - class(nutrient_competition_method_type), intent(in) :: this - type(file_desc_t),intent(inout) :: ncid ! pio netCDF file id - ! - ! !LOCAL VARIABLES: - character(len=32) :: subname = 'CNAllocParamsType' - character(len=100) :: errCode = '-Error reading in parameters file:' - logical :: readv ! has variable been read in or not - real(r8) :: tempr ! temporary to read in parameter - character(len=100) :: tString ! temp. var for reading - !----------------------------------------------------------------------- - - ! read in parameters - - tString='dayscrecover' - call ncd_io(varname=trim(tString),data=tempr, flag='read', ncid=ncid, readvar=readv) - if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) - params_inst%dayscrecover=tempr - - end subroutine readParams - end module NutrientCompetitionMethodMod diff --git a/src/main/clm_initializeMod.F90 b/src/main/clm_initializeMod.F90 index 73ec4ffd58..8a3d608237 100644 --- a/src/main/clm_initializeMod.F90 +++ b/src/main/clm_initializeMod.F90 @@ -303,7 +303,7 @@ subroutine initialize2(ni,nj) call clm_instReadNML( NLFilename ) allocate(nutrient_competition_method, & source=create_nutrient_competition_method(bounds_proc)) - call readParameters(nutrient_competition_method, photosyns_inst) + call readParameters(photosyns_inst) ! Initialize time manager if (nsrest == nsrStartup) then diff --git a/src/main/readParamsMod.F90 b/src/main/readParamsMod.F90 index 31a116ab28..2d60b29be5 100644 --- a/src/main/readParamsMod.F90 +++ b/src/main/readParamsMod.F90 @@ -24,10 +24,11 @@ module readParamsMod contains !----------------------------------------------------------------------- - subroutine readParameters (nutrient_competition_method, photosyns_inst) + subroutine readParameters (photosyns_inst) ! ! ! USES: use CNSharedParamsMod , only : CNParamsReadShared + use CNAllocationMod , only : readCNAllocParams => readParams use CNGapMortalityMod , only : readCNGapMortParams => readParams use CNMRespMod , only : readCNMRespParams => readParams use CNFUNMod , only : readCNFUNParams => readParams @@ -59,13 +60,11 @@ subroutine readParameters (nutrient_competition_method, photosyns_inst) use initVerticalMod , only : readParams_initVertical => readParams use SurfaceWaterMod , only : readParams_SurfaceWater => readParams use SoilHydrologyInitTimeConstMod , only : readParams_SoilHydrologyInitTimeConst => readParams - use NutrientCompetitionMethodMod , only : nutrient_competition_method_type use clm_varctl, only : NLFilename_in use PhotosynthesisMod , only : photosyns_type ! ! !ARGUMENTS: type(photosyns_type) , intent(in) :: photosyns_inst - class(nutrient_competition_method_type), intent(in) :: nutrient_competition_method ! ! !LOCAL VARIABLES: character(len=256) :: locfn ! local file name @@ -88,7 +87,7 @@ subroutine readParameters (nutrient_competition_method, photosyns_inst) ! Above ground biogeochemistry... ! if (use_cn) then - call nutrient_competition_method%readParams(ncid) + call readCNAllocParams(ncid) call readCNGapMortParams(ncid) call readCNMRespParams(ncid) call readCNFUNParams(ncid) From df8d0c6798c16454e18313b8b4d54079d1a339ed Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Mon, 28 Mar 2022 16:11:59 -0600 Subject: [PATCH 02/17] Move aroot and arepr into CNVegStateType This will support the incorporation of AgSys --- src/biogeochem/CNDriverMod.F90 | 8 +--- src/biogeochem/CNVegStateType.F90 | 5 +++ .../NutrientCompetitionCLM45defaultMod.F90 | 39 ++++++------------- .../NutrientCompetitionFlexibleCNMod.F90 | 38 ++++++------------ .../NutrientCompetitionMethodMod.F90 | 10 +---- 5 files changed, 30 insertions(+), 70 deletions(-) diff --git a/src/biogeochem/CNDriverMod.F90 b/src/biogeochem/CNDriverMod.F90 index cfe00401b8..af63801e8f 100644 --- a/src/biogeochem/CNDriverMod.F90 +++ b/src/biogeochem/CNDriverMod.F90 @@ -42,7 +42,6 @@ module CNDriverMod use SaturatedExcessRunoffMod , only : saturated_excess_runoff_type use ActiveLayerMod , only : active_layer_type use SoilWaterRetentionCurveMod , only : soil_water_retention_curve_type - use CropReprPoolsMod , only : nrepr ! ! !PUBLIC TYPES: implicit none @@ -204,8 +203,6 @@ subroutine CNDriverNoLeaching(bounds, real(r8):: pmnf_decomp_cascade(bounds%begc:bounds%endc,1:nlevdecomp,1:ndecomp_cascade_transitions) !potential mineral N flux, from one pool to another real(r8):: p_decomp_npool_to_din(bounds%begc:bounds%endc,1:nlevdecomp,1:ndecomp_cascade_transitions) ! potential flux to dissolved inorganic N real(r8):: p_decomp_cn_gain(bounds%begc:bounds%endc,1:nlevdecomp,1:ndecomp_pools) ! C:N ratio of the flux gained by the receiver pool - real(r8):: arepr(bounds%begp:bounds%endp,nrepr) ! reproductive allocation coefficient(s) (only used for use_crop) - real(r8):: aroot(bounds%begp:bounds%endp) ! root allocation coefficient (only used for use_crop) integer :: begp,endp integer :: begc,endc @@ -403,8 +400,7 @@ subroutine CNDriverNoLeaching(bounds, c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, & - energyflux_inst, & - aroot=aroot(begp:endp), arepr=arepr(begp:endp,:)) + energyflux_inst) ! get the column-averaged plant_ndemand (needed for following call to SoilBiogeochemCompetition) @@ -438,8 +434,6 @@ subroutine CNDriverNoLeaching(bounds, c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & soilbiogeochem_nitrogenstate_inst, & - aroot=aroot(begp:endp), & - arepr=arepr(begp:endp,:), & fpg_col=soilbiogeochem_state_inst%fpg_col(begc:endc)) call t_stopf('calc_plant_nutrient_competition') diff --git a/src/biogeochem/CNVegStateType.F90 b/src/biogeochem/CNVegStateType.F90 index e79172c22e..e39a334ab8 100644 --- a/src/biogeochem/CNVegStateType.F90 +++ b/src/biogeochem/CNVegStateType.F90 @@ -15,6 +15,7 @@ module CNVegStateType use PatchType , only : patch use AnnualFluxDribbler, only : annual_flux_dribbler_type, annual_flux_dribbler_patch use dynSubgridControlMod, only : get_for_testing_allow_non_annual_changes + use CropReprPoolsMod, only : nrepr ! ! !PUBLIC TYPES: implicit none @@ -40,6 +41,8 @@ module CNVegStateType real(r8) , pointer :: astemi_patch (:) ! patch saved stem allocation coefficient from phase 2 real(r8) , pointer :: aleaf_patch (:) ! patch leaf allocation coefficient real(r8) , pointer :: astem_patch (:) ! patch stem allocation coefficient + real(r8) , pointer :: aroot_patch (:) ! patch root allocation coefficient + real(r8) , pointer :: arepr_patch (:,:) ! patch reproductive allocation coefficient(s) real(r8) , pointer :: htmx_patch (:) ! patch max hgt attained by a crop during yr (m) integer , pointer :: peaklai_patch (:) ! patch 1: max allowed lai; 0: not at max @@ -201,6 +204,8 @@ subroutine InitAllocate(this, bounds) allocate(this%astemi_patch (begp:endp)) ; this%astemi_patch (:) = nan allocate(this%aleaf_patch (begp:endp)) ; this%aleaf_patch (:) = nan allocate(this%astem_patch (begp:endp)) ; this%astem_patch (:) = nan + allocate(this%aroot_patch (begp:endp)) ; this%aroot_patch (:) = nan + allocate(this%arepr_patch (begp:endp, nrepr)) ; this%arepr_patch (:,:) = nan allocate(this%htmx_patch (begp:endp)) ; this%htmx_patch (:) = 0.0_r8 allocate(this%peaklai_patch (begp:endp)) ; this%peaklai_patch (:) = 0 diff --git a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 index 9f251faa24..4d8af78fd7 100644 --- a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 +++ b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 @@ -77,8 +77,7 @@ subroutine calc_plant_nutrient_competition (this, & cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & - soilbiogeochem_nitrogenstate_inst, & - aroot, arepr, fpg_col) + soilbiogeochem_nitrogenstate_inst, fpg_col) ! ! !USES: use CNVegStateType , only : cnveg_state_type @@ -106,16 +105,12 @@ subroutine calc_plant_nutrient_competition (this, & type(cnveg_nitrogenstate_type) , intent(inout) :: cnveg_nitrogenstate_inst type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst type(soilbiogeochem_nitrogenstate_type), intent(in) :: soilbiogeochem_nitrogenstate_inst - real(r8) , intent(in) :: aroot(bounds%begp:) - real(r8) , intent(in) :: arepr(bounds%begp:,:) real(r8) , intent(in) :: fpg_col(bounds%begc:) 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, & - aroot=aroot(bounds%begp:bounds%endp), & - arepr=arepr(bounds%begp:bounds%endp,:), & fpg_col=fpg_col(bounds%begc:bounds%endc)) end subroutine calc_plant_nutrient_competition @@ -124,8 +119,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, & - aroot, arepr, fpg_col) + c14_cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, fpg_col) ! ! !USES: use pftconMod , only : pftcon, npcropmin @@ -153,8 +147,6 @@ 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 - real(r8) , intent(in) :: aroot(bounds%begp:) - real(r8) , intent(in) :: arepr(bounds%begp:,:) real(r8) , intent(in) :: fpg_col(bounds%begc:) ! ! !LOCAL VARIABLES: @@ -170,8 +162,6 @@ subroutine calc_plant_cn_alloc (this, bounds, num_soilp, filter_soilp, & real(r8):: fsmn(bounds%begp:bounds%endp) ! A emperate variable for adjusting FUN uptakes !----------------------------------------------------------------------- - SHR_ASSERT_ALL_FL((ubound(aroot) == (/bounds%endp/)), sourcefile, __LINE__) - SHR_ASSERT_ALL_FL((ubound(arepr) == (/bounds%endp, nrepr/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(fpg_col) == (/bounds%endc/)), sourcefile, __LINE__) associate( & @@ -196,8 +186,10 @@ subroutine calc_plant_cn_alloc (this, bounds, num_soilp, filter_soilp, & croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] flag, true if planted, not harvested peaklai => cnveg_state_inst%peaklai_patch , & ! Input: [integer (:) ] 1: max allowed lai; 0: not at max - aleaf => cnveg_state_inst%aleaf_patch , & ! Output: [real(r8) (:) ] leaf allocation coefficient - astem => cnveg_state_inst%astem_patch , & ! Output: [real(r8) (:) ] stem allocation coefficient + aleaf => cnveg_state_inst%aleaf_patch , & ! Input: [real(r8) (:) ] leaf allocation coefficient + astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient + aroot => cnveg_state_inst%aroot_patch , & ! Input: [real(r8) (:) ] root allocation coefficient + arepr => cnveg_state_inst%arepr_patch , & ! Input: [real(r8) (:,:) ] reproductive allocation coefficient(s) c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) n_allometry => cnveg_state_inst%n_allometry_patch , & ! Output: [real(r8) (:) ] N allocation index (DIM) downreg => cnveg_state_inst%downreg_patch , & ! Output: [real(r8) (:) ] fractional reduction in GPP due to N limitation (DIM) @@ -447,8 +439,7 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, & - energyflux_inst, & - aroot, arepr) + energyflux_inst) ! ! !USES: use CanopyStateType , only : canopystate_type @@ -480,17 +471,13 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& type(soilbiogeochem_carbonflux_type) , intent(in) :: soilbiogeochem_carbonflux_inst type(soilbiogeochem_nitrogenstate_type), intent(in) :: soilbiogeochem_nitrogenstate_inst type(energyflux_type) , intent(in) :: energyflux_inst - real(r8) , intent(out) :: aroot(bounds%begp:) - real(r8) , intent(out) :: arepr(bounds%begp:,:) !----------------------------------------------------------------------- call this%calc_plant_nitrogen_demand(bounds, num_soilp, filter_soilp, & crop_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & - cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & - aroot=aroot(bounds%begp:bounds%endp), & - arepr=arepr(bounds%begp:bounds%endp,:)) + cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst) end subroutine calc_plant_nutrient_demand @@ -499,8 +486,7 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & crop_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & - cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & - aroot, arepr) + cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst) ! ! !USES: use pftconMod , only : npcropmin, pftcon @@ -528,8 +514,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & type(cnveg_carbonflux_type) , intent(inout) :: c14_cnveg_carbonflux_inst type(cnveg_nitrogenstate_type) , intent(in) :: cnveg_nitrogenstate_inst type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst - real(r8) , intent(out) :: aroot(bounds%begp:) - real(r8) , intent(out) :: arepr(bounds%begp:,:) ! ! !LOCAL VARIABLES: integer :: p,l,j,k ! indices @@ -547,9 +531,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & !----------------------------------------------------------------------- - SHR_ASSERT_ALL_FL((ubound(aroot) == (/bounds%endp/)), sourcefile, __LINE__) - SHR_ASSERT_ALL_FL((ubound(arepr) == (/bounds%endp, nrepr/)), sourcefile, __LINE__) - associate( & ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type @@ -593,6 +574,8 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & astemi => cnveg_state_inst%astemi_patch , & ! Output: [real(r8) (:) ] saved allocation coefficient from phase 2 aleaf => cnveg_state_inst%aleaf_patch , & ! Output: [real(r8) (:) ] leaf allocation coefficient astem => cnveg_state_inst%astem_patch , & ! Output: [real(r8) (:) ] stem allocation coefficient + aroot => cnveg_state_inst%aroot_patch , & ! Output: [real(r8) (:) ] root allocation coefficient + arepr => cnveg_state_inst%arepr_patch , & ! Output: [real(r8) (:,:) ] reproductive allocation coefficient(s) grain_flag => cnveg_state_inst%grain_flag_patch , & ! Output: [real(r8) (:) ] 1: grain fill stage; 0: not c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) n_allometry => cnveg_state_inst%n_allometry_patch , & ! Output: [real(r8) (:) ] N allocation index (DIM) diff --git a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 index f85cf46743..9ae7776e68 100644 --- a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 +++ b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 @@ -138,8 +138,7 @@ subroutine calc_plant_nutrient_competition (this, & cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & - soilbiogeochem_nitrogenstate_inst, & - aroot, arepr, fpg_col) + soilbiogeochem_nitrogenstate_inst, fpg_col) ! ! !USES: use CNVegStateType , only : cnveg_state_type @@ -166,8 +165,6 @@ subroutine calc_plant_nutrient_competition (this, & type(cnveg_nitrogenstate_type) , intent(inout) :: cnveg_nitrogenstate_inst type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst type(soilbiogeochem_nitrogenstate_type), intent(in) :: soilbiogeochem_nitrogenstate_inst - real(r8), intent(in) :: aroot (bounds%begp:) - real(r8), intent(in) :: arepr(bounds%begp:,:) real(r8), intent(in) :: fpg_col (bounds%begc:) call this%calc_plant_cn_alloc(bounds, num_soilp, filter_soilp, & @@ -175,8 +172,6 @@ subroutine calc_plant_nutrient_competition (this, & cnveg_carbonstate_inst, cnveg_carbonflux_inst, c13_cnveg_carbonflux_inst, & c14_cnveg_carbonflux_inst, cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & soilbiogeochem_nitrogenstate_inst, & - aroot=aroot(bounds%begp:bounds%endp), & - arepr=arepr(bounds%begp:bounds%endp,:), & fpg_col=fpg_col(bounds%begc:bounds%endc)) end subroutine calc_plant_nutrient_competition @@ -186,8 +181,7 @@ 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_nitrogenstate_inst, cnveg_nitrogenflux_inst, & - soilbiogeochem_nitrogenstate_inst, & - aroot, arepr, fpg_col) + soilbiogeochem_nitrogenstate_inst, fpg_col) ! ! !USES: use pftconMod , only : pftcon, npcropmin @@ -221,8 +215,6 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst type(cnveg_nitrogenstate_type) , intent(inout) :: cnveg_nitrogenstate_inst type(soilbiogeochem_nitrogenstate_type), intent(in) :: soilbiogeochem_nitrogenstate_inst - real(r8) , intent(in) :: aroot(bounds%begp:) - real(r8) , intent(in) :: arepr(bounds%begp:,:) real(r8) , intent(in) :: fpg_col(bounds%begc:) ! ! !LOCAL VARIABLES: @@ -282,8 +274,6 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & ! ----------------------------------------------------------------------- - SHR_ASSERT_ALL_FL((ubound(aroot) == (/bounds%endp/)) , sourcefile, __LINE__) - SHR_ASSERT_ALL_FL((ubound(arepr) == (/bounds%endp, nrepr/)) , sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(fpg_col) == (/bounds%endc/)) , sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(this%actual_storage_leafcn) >= (/bounds%endp/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((lbound(this%actual_storage_leafcn) <= (/bounds%begp/)), sourcefile, __LINE__) @@ -311,8 +301,10 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] flag, true if planted, not harvested peaklai => cnveg_state_inst%peaklai_patch , & ! Input: [integer (:) ] 1: max allowed lai; 0: not at max - aleaf => cnveg_state_inst%aleaf_patch , & ! Output: [real(r8) (:) ] leaf allocation coefficient - astem => cnveg_state_inst%astem_patch , & ! Output: [real(r8) (:) ] stem allocation coefficient + aleaf => cnveg_state_inst%aleaf_patch , & ! Input: [real(r8) (:) ] leaf allocation coefficient + astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient + aroot => cnveg_state_inst%aroot_patch , & ! Input: [real(r8) (:) ] root allocation coefficient + arepr => cnveg_state_inst%arepr_patch , & ! Input: [real(r8) (:,:) ] reproductive allocation coefficient(s) c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) annsum_npp => cnveg_carbonflux_inst%annsum_npp_patch , & ! Input: [real(r8) (:) ] annual sum of NPP, for wood allocation @@ -899,8 +891,7 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, & - energyflux_inst, & - aroot, arepr) + energyflux_inst) ! ! !USES: use CanopyStateType , only : canopystate_type @@ -930,8 +921,6 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& type(soilbiogeochem_carbonflux_type) , intent(in) :: soilbiogeochem_carbonflux_inst type(soilbiogeochem_nitrogenstate_type), intent(in) :: soilbiogeochem_nitrogenstate_inst type(energyflux_type) , intent(in) :: energyflux_inst - real(r8) , intent(out) :: aroot(bounds%begp:) - real(r8) , intent(out) :: arepr(bounds%begp:,:) !----------------------------------------------------------------------- call this%calc_plant_nitrogen_demand(bounds, num_soilp, filter_soilp, & @@ -940,9 +929,7 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, & - energyflux_inst, & - aroot=aroot(bounds%begp:bounds%endp), & - arepr=arepr(bounds%begp:bounds%endp,:)) + energyflux_inst) end subroutine calc_plant_nutrient_demand @@ -953,8 +940,7 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, & - energyflux_inst, & - aroot, arepr) + energyflux_inst) ! ! !USES: use pftconMod , only : npcropmin, pftcon @@ -993,8 +979,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & type(soilbiogeochem_carbonflux_type) , intent(in) :: soilbiogeochem_carbonflux_inst type(soilbiogeochem_nitrogenstate_type), intent(in) :: soilbiogeochem_nitrogenstate_inst type(energyflux_type) , intent(in) :: energyflux_inst - real(r8) , intent(out) :: aroot(bounds%begp:) - real(r8) , intent(out) :: arepr(bounds%begp:,:) ! ! !LOCAL VARIABLES: integer :: c, p, j, k ! indices @@ -1024,8 +1008,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! ----------------------------------------------------------------------- - SHR_ASSERT_ALL_FL((ubound(aroot) == (/bounds%endp/)), sourcefile, __LINE__) - SHR_ASSERT_ALL_FL((ubound(arepr) == (/bounds%endp, nrepr/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(this%actual_leafcn) >= (/bounds%endp/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((lbound(this%actual_leafcn) <= (/bounds%begp/)), sourcefile, __LINE__) @@ -1074,6 +1056,8 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & astemi => cnveg_state_inst%astemi_patch , & ! Output: [real(r8) (:) ] saved allocation coefficient from phase 2 aleaf => cnveg_state_inst%aleaf_patch , & ! Output: [real(r8) (:) ] leaf allocation coefficient astem => cnveg_state_inst%astem_patch , & ! Output: [real(r8) (:) ] stem allocation coefficient + aroot => cnveg_state_inst%aroot_patch , & ! Output: [real(r8) (:) ] root allocation coefficient + arepr => cnveg_state_inst%arepr_patch , & ! Output: [real(r8) (:,:) ] reproductive allocation coefficient(s) grain_flag => cnveg_state_inst%grain_flag_patch , & ! Output: [real(r8) (:) ] 1: grain fill stage; 0: not c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) n_allometry => cnveg_state_inst%n_allometry_patch , & ! Output: [real(r8) (:) ] N allocation index (DIM) diff --git a/src/biogeochem/NutrientCompetitionMethodMod.F90 b/src/biogeochem/NutrientCompetitionMethodMod.F90 index 230c3b07de..041d285421 100644 --- a/src/biogeochem/NutrientCompetitionMethodMod.F90 +++ b/src/biogeochem/NutrientCompetitionMethodMod.F90 @@ -65,8 +65,7 @@ subroutine calc_plant_nutrient_demand_interface (this, bounds, num_soilp, filter c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, & - energyflux_inst, & - aroot, arepr) + energyflux_inst) ! ! DESCRIPTION ! calculate nutrient yield after considering competition between different components @@ -103,8 +102,6 @@ subroutine calc_plant_nutrient_demand_interface (this, bounds, num_soilp, filter type(soilbiogeochem_carbonflux_type), intent(in) :: soilbiogeochem_carbonflux_inst type(soilbiogeochem_nitrogenstate_type), intent(in) :: soilbiogeochem_nitrogenstate_inst type(energyflux_type) , intent(in) :: energyflux_inst - real(r8) , intent(out) :: aroot(bounds%begp:) - real(r8) , intent(out) :: arepr(bounds%begp:,:) end subroutine calc_plant_nutrient_demand_interface @@ -115,8 +112,7 @@ subroutine calc_plant_nutrient_competition_interface (this, & cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & - soilbiogeochem_nitrogenstate_inst, & - aroot, arepr, fpg_col) + soilbiogeochem_nitrogenstate_inst, fpg_col) ! ! !USES: use shr_kind_mod , only : r8 => shr_kind_r8 @@ -146,8 +142,6 @@ subroutine calc_plant_nutrient_competition_interface (this, & type(cnveg_nitrogenstate_type) , intent(inout) :: cnveg_nitrogenstate_inst type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst type(soilbiogeochem_nitrogenstate_type), intent(in) :: soilbiogeochem_nitrogenstate_inst - real(r8) , intent(in) :: aroot(bounds%begp:) - real(r8) , intent(in) :: arepr(bounds%begp:,:) real(r8) , intent(in) :: fpg_col(bounds%begc:) end subroutine calc_plant_nutrient_competition_interface From 17cb29dcf629fde596944406bdd312e7f1de6f94 Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Tue, 29 Mar 2022 23:01:31 -0600 Subject: [PATCH 03/17] Add a CropPhase subroutine and use it I this this will become more useful as I do some further refactoring --- src/biogeochem/CNDriverMod.F90 | 1 + src/biogeochem/CNPhenologyMod.F90 | 78 ++++++++++++++++++- src/biogeochem/CropType.F90 | 12 ++- .../NutrientCompetitionCLM45defaultMod.F90 | 35 +++++++-- .../NutrientCompetitionFlexibleCNMod.F90 | 33 ++++++-- .../NutrientCompetitionMethodMod.F90 | 3 + 6 files changed, 141 insertions(+), 21 deletions(-) diff --git a/src/biogeochem/CNDriverMod.F90 b/src/biogeochem/CNDriverMod.F90 index af63801e8f..94b9563be8 100644 --- a/src/biogeochem/CNDriverMod.F90 +++ b/src/biogeochem/CNDriverMod.F90 @@ -395,6 +395,7 @@ subroutine CNDriverNoLeaching(bounds, call t_startf('calc_plant_nutrient_demand') call nutrient_competition_method%calc_plant_nutrient_demand ( & bounds, num_soilp, filter_soilp, & + num_pcropp, filter_pcropp, & crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index 07f993546e..f54c2a6d56 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -28,6 +28,8 @@ module CNPhenologyMod use CNVegnitrogenstateType , only : cnveg_nitrogenstate_type use CNVegnitrogenfluxType , only : cnveg_nitrogenflux_type use CropType , only : crop_type + use CropType , only : cphase_planted, cphase_leafemerge + use CropType , only : cphase_grainfill, cphase_harvest use pftconMod , only : pftcon use SoilStateType , only : soilstate_type use TemperatureType , only : temperature_type @@ -48,6 +50,7 @@ module CNPhenologyMod public :: CNPhenologyreadNML ! Read namelist public :: CNPhenologyInit ! Initialization public :: CNPhenology ! Update + public :: CropPhase ! Get the current phase of each crop patch ! !PUBLIC for unit testing public :: CNPhenologySetNML ! Set the namelist setttings explicitly for unit tests @@ -2012,7 +2015,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & offset_flag(p) = 0._r8 ! carbon and nitrogen transfers if (croplive(p)) then - cphase(p) = 1._r8 + cphase(p) = cphase_planted ! call vernalization if winter temperate cereal planted, living, and the ! vernalization factor is not 1; @@ -2045,8 +2048,14 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & hui(p) = max(hui(p),huigrain(p)) endif + ! The following conditionals are similar to those in CropPhase. However, they + ! differ slightly because here we are potentially setting a new crop phase, + ! whereas CropPhase is just designed to get the current, already-determined + ! phase. However, despite these differences: if you make changes to the + ! following conditionals, you should also check to see if you should make + ! similar changes in CropPhase. if (leafout(p) >= huileaf(p) .and. hui(p) < huigrain(p) .and. idpp < mxmat(ivt(p))) then - cphase(p) = 2._r8 + cphase(p) = cphase_leafemerge if (abs(onset_counter(p)) > 1.e-6_r8) then onset_flag(p) = 1._r8 onset_counter(p) = dt @@ -2076,7 +2085,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & harvest_count(p) = harvest_count(p) + 1 crop_inst%hdates_thisyr(p, harvest_count(p)) = real(jday, r8) croplive(p) = .false. ! no re-entry in greater if-block - cphase(p) = 4._r8 + cphase(p) = cphase_harvest if (tlai(p) > 0._r8) then ! plant had emerged before harvest offset_flag(p) = 1._r8 offset_counter(p) = dt @@ -2106,7 +2115,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & ! Use CN's simple formula at least as a place holder (slevis) else if (hui(p) >= huigrain(p)) then - cphase(p) = 3._r8 + cphase(p) = cphase_grainfill bglfr(p) = 1._r8/(leaf_long(ivt(p))*avg_dayspyr*secspday) end if @@ -2144,6 +2153,67 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & end subroutine CropPhenology + !----------------------------------------------------------------------- + subroutine CropPhase(bounds, num_pcropp, filter_pcropp, & + crop_inst, cnveg_state_inst, crop_phase) + ! + ! !DESCRIPTION: + ! Get the current phase of each crop patch. + ! + ! The returned values (in crop_phase) are from the set of cphase_* values defined in + ! CropType. The returned values in crop_phase are only valid for patches where + ! croplive is true; the values are undefined where croplive is false and should not be + ! used there! + ! + ! This has logic similar to that in CropPhenology. If you make changes here, you + ! should also check if similar changes need to be made in CropPhenology. + ! + ! !ARGUMENTS: + 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 + type(crop_type) , intent(in) :: crop_inst + type(cnveg_state_type) , intent(in) :: cnveg_state_inst + real(r8) , intent(inout) :: crop_phase(bounds%begp:) + ! + ! !LOCAL VARIABLES: + integer :: p, fp + + character(len=*), parameter :: subname = 'CropPhase' + !----------------------------------------------------------------------- + SHR_ASSERT_ALL_FL((ubound(crop_phase) == [bounds%endp]), sourcefile, __LINE__) + + associate( & + croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] Flag, true if planted, not harvested + hui => crop_inst%gddplant_patch , & ! Input: [real(r8) (:) ] gdd since planting (gddplant) + leafout => crop_inst%gddtsoi_patch , & ! Input: [real(r8) (:) ] gdd from top soil layer temperature + huileaf => cnveg_state_inst%huileaf_patch , & ! Input: [real(r8) (:) ] heat unit index needed from planting to leaf emergence + huigrain => cnveg_state_inst%huigrain_patch & ! Input: [real(r8) (:) ] same to reach vegetative maturity + ) + + do fp = 1, num_pcropp + p = filter_pcropp(fp) + + if (croplive(p)) then + ! Start with cphase_planted, but this might get changed in the later + ! conditional blocks. + crop_phase(p) = cphase_planted + if (leafout(p) >= huileaf(p) .and. hui(p) < huigrain(p)) then + crop_phase(p) = cphase_leafemerge + else if (hui(p) >= huigrain(p)) then + ! Since we know croplive is true, any hui greater than huigrain implies that + ! we're in the grainfill stage: if we were passt gddmaturity then croplive + ! would be false. + crop_phase(p) = cphase_grainfill + end if + end if + end do + + end associate + + end subroutine CropPhase + + !----------------------------------------------------------------------- subroutine CropPhenologyInit(bounds) ! diff --git a/src/biogeochem/CropType.F90 b/src/biogeochem/CropType.F90 index efc919a21c..634b04e8b7 100644 --- a/src/biogeochem/CropType.F90 +++ b/src/biogeochem/CropType.F90 @@ -24,6 +24,14 @@ module CropType ! ! !PUBLIC DATA TYPES: ! + + ! Possible values of cphase + real(r8), parameter, public :: cphase_not_planted = 0._r8 + real(r8), parameter, public :: cphase_planted = 1._r8 + real(r8), parameter, public :: cphase_leafemerge = 2._r8 + real(r8), parameter, public :: cphase_grainfill = 3._r8 + real(r8), parameter, public :: cphase_harvest = 4._r8 + ! Crop state variables structure type, public :: crop_type @@ -34,7 +42,7 @@ module CropType real(r8), pointer :: gddplant_patch (:) ! patch accum gdd past planting date for crop (ddays) real(r8), pointer :: gddtsoi_patch (:) ! patch growing degree-days from planting (top two soil layers) (ddays) real(r8), pointer :: vf_patch (:) ! patch vernalization factor for cereal - real(r8), pointer :: cphase_patch (:) ! phenology phase + real(r8), pointer :: cphase_patch (:) ! phenology phase (see cphase_* constants above for possible values) real(r8), pointer :: latbaset_patch (:) ! Latitude vary baset for gddplant (degree C) character(len=20) :: baset_mapping real(r8) :: baset_latvary_intercept @@ -200,7 +208,7 @@ subroutine InitAllocate(this, bounds) allocate(this%gddplant_patch (begp:endp)) ; this%gddplant_patch (:) = spval allocate(this%gddtsoi_patch (begp:endp)) ; this%gddtsoi_patch (:) = spval allocate(this%vf_patch (begp:endp)) ; this%vf_patch (:) = 0.0_r8 - allocate(this%cphase_patch (begp:endp)) ; this%cphase_patch (:) = 0.0_r8 + allocate(this%cphase_patch (begp:endp)) ; this%cphase_patch (:) = cphase_not_planted allocate(this%latbaset_patch (begp:endp)) ; this%latbaset_patch (:) = spval allocate(this%sdates_thisyr(begp:endp,1:mxsowings)) ; this%sdates_thisyr(:,:) = spval allocate(this%hdates_thisyr(begp:endp,1:mxharvests)) ; this%hdates_thisyr(:,:) = spval diff --git a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 index 4d8af78fd7..81ace923c0 100644 --- a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 +++ b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 @@ -11,13 +11,17 @@ module NutrientCompetitionCLM45defaultMod ! ! !USES: use shr_kind_mod , only : r8 => shr_kind_r8 + use shr_log_mod , only : errMsg => shr_log_errMsg use decompMod , only : bounds_type use LandunitType , only : lun use ColumnType , only : col use PatchType , only : patch use NutrientCompetitionMethodMod, only : nutrient_competition_method_type use CropReprPoolsMod , only : nrepr - !use clm_varctl , only : iulog + use CNPhenologyMod , only : CropPhase + use CropType , only : cphase_planted, cphase_leafemerge, cphase_grainfill + use clm_varctl , only : iulog + use abortutils , only : endrun ! implicit none private @@ -434,6 +438,7 @@ end subroutine calc_plant_cn_alloc !----------------------------------------------------------------------- subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& + num_pcropp, filter_pcropp, & crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & @@ -459,6 +464,8 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_pcropp ! number of prog crop patches in filter + integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches type(crop_type) , intent(in) :: crop_inst type(canopystate_type) , intent(in) :: canopystate_inst ! unused in this version type(cnveg_state_type) , intent(inout) :: cnveg_state_inst @@ -474,6 +481,7 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& !----------------------------------------------------------------------- call this%calc_plant_nitrogen_demand(bounds, num_soilp, filter_soilp, & + num_pcropp, filter_pcropp, & crop_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & @@ -483,6 +491,7 @@ end subroutine calc_plant_nutrient_demand !----------------------------------------------------------------------- subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & + num_pcropp, filter_pcropp, & crop_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & @@ -506,6 +515,8 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_pcropp ! number of prog crop patches in filter + integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches type(crop_type) , intent(in) :: crop_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst @@ -528,7 +539,9 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & real(r8):: dt ! model time step real(r8):: f5_tot ! sum of f5 terms real(r8):: f5_n_tot ! sum of f5 terms converted from C to N + real(r8):: crop_phase(bounds%begp:bounds%endp) + character(len=*), parameter :: subname = "calc_plant_nitrogen_demand" !----------------------------------------------------------------------- associate( & @@ -563,12 +576,10 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & hui => crop_inst%gddplant_patch , & ! Input: [real(r8) (:) ] =gdd since planting (gddplant) - leafout => crop_inst%gddtsoi_patch , & ! Input: [real(r8) (:) ] =gdd from top soil layer temperature - croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] flag, true if planted, not harvested + croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] flag, true if planted, not harvested gddmaturity => cnveg_state_inst%gddmaturity_patch , & ! Input: [real(r8) (:) ] gdd needed to harvest - huileaf => cnveg_state_inst%huileaf_patch , & ! Input: [real(r8) (:) ] heat unit index needed from planting to leaf emergence - huigrain => cnveg_state_inst%huigrain_patch , & ! Input: [real(r8) (:) ] same to reach vegetative maturity + huigrain => cnveg_state_inst%huigrain_patch , & ! Input: [real(r8) (:) ] same to reach vegetative maturity peaklai => cnveg_state_inst%peaklai_patch , & ! Input: [integer (:) ] 1: max allowed lai; 0: not at max aleafi => cnveg_state_inst%aleafi_patch , & ! Output: [real(r8) (:) ] saved allocation coefficient from phase 2 astemi => cnveg_state_inst%astemi_patch , & ! Output: [real(r8) (:) ] saved allocation coefficient from phase 2 @@ -606,6 +617,9 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! set time steps dt = get_step_size_real() + call CropPhase(bounds, num_pcropp, filter_pcropp, crop_inst, cnveg_state_inst, & + crop_phase = crop_phase(bounds%begp:bounds%endp)) + ! loop over patches to assess the total plant N demand do fp = 1,num_soilp p = filter_soilp(fp) @@ -651,7 +665,7 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! and carbon assimilation ! Next phase: leaf emergence to start of leaf decline - if (leafout(p) >= huileaf(p) .and. hui(p) < huigrain(p)) then + if (crop_phase(p) == cphase_leafemerge) then ! allocation rules for crops based on maturity and linear decrease ! of amount allocated to roots over course of the growing season @@ -688,7 +702,7 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! shift allocation either when enough gdd are accumulated or maximum number ! of days has elapsed since planting - else if (hui(p) >= huigrain(p)) then + else if (crop_phase(p) == cphase_grainfill) then aroot(p) = max(0._r8, min(1._r8, arooti(ivt(p)) - & (arooti(ivt(p)) - arootf(ivt(p))) * min(1._r8, hui(p)/gddmaturity(p)))) @@ -756,7 +770,8 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & end do arepr(p,nrepr) = 1._r8 - aroot(p) - astem(p) - aleaf(p) - else ! pre emergence + else if (crop_phase(p) == cphase_planted) then + ! pre emergence ! allocation coefficients should be irrelevant because crops have no ! live carbon pools; this applies to this "else" and to the "else" a few ! lines down @@ -766,6 +781,10 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & do k = 1, nrepr arepr(p,k) = 0._r8 end do + + else + write(iulog,*) "ERROR in " // subname // ": unexpected crop_phase: ", crop_phase(p) + call endrun(msg="ERROR: unexpected crop_phase "//errmsg(sourcefile, __LINE__)) end if f1 = aroot(p) / aleaf(p) diff --git a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 index 9ae7776e68..ece26bd53f 100644 --- a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 +++ b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 @@ -18,13 +18,17 @@ module NutrientCompetitionFlexibleCNMod ! ! !USES: use shr_kind_mod , only : r8 => shr_kind_r8 + use shr_log_mod , only : errMsg => shr_log_errMsg use decompMod , only : bounds_type use LandunitType , only : lun use ColumnType , only : col use PatchType , only : patch use NutrientCompetitionMethodMod, only : nutrient_competition_method_type - use CropReprPoolsMod , only : nrepr + use CropReprPoolsMod , only : nrepr + use CNPhenologyMod , only : CropPhase + use CropType , only : cphase_planted, cphase_leafemerge, cphase_grainfill use clm_varctl , only : iulog + use abortutils , only : endrun ! implicit none private @@ -886,6 +890,7 @@ end subroutine calc_plant_cn_alloc ! ----------------------------------------------------------------------- subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& + num_pcropp, filter_pcropp, & crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & @@ -909,6 +914,8 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_pcropp ! number of prog crop patches in filter + integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches type(crop_type) , intent(in) :: crop_inst type(canopystate_type) , intent(in) :: canopystate_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst @@ -924,6 +931,7 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& !----------------------------------------------------------------------- call this%calc_plant_nitrogen_demand(bounds, num_soilp, filter_soilp, & + num_pcropp, filter_pcropp, & crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & @@ -935,6 +943,7 @@ end subroutine calc_plant_nutrient_demand !----------------------------------------------------------------------- subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & + num_pcropp, filter_pcropp, & crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & @@ -967,6 +976,8 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_pcropp ! number of prog crop patches in filter + integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches type(crop_type) , intent(in) :: crop_inst type(canopystate_type) , intent(in) :: canopystate_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst @@ -1002,10 +1013,12 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & real(r8) :: substrate_term real(r8) :: temp_scalar real(r8) :: Vmax_N + real(r8) :: crop_phase (bounds%begp:bounds%endp) real(r8) :: allocation_leaf (bounds%begp:bounds%endp) real(r8) :: allocation_stem (bounds%begp:bounds%endp) real(r8) :: allocation_froot (bounds%begp:bounds%endp) + character(len=*), parameter :: subname = "calc_plant_nitrogen_demand" ! ----------------------------------------------------------------------- SHR_ASSERT_ALL_FL((ubound(this%actual_leafcn) >= (/bounds%endp/)), sourcefile, __LINE__) @@ -1045,11 +1058,9 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & laisha => canopystate_inst%laisha_patch , & ! Input: [real(r8) (:) ] shaded projected leaf area index hui => crop_inst%gddplant_patch , & ! Input: [real(r8) (:) ] =gdd since planting (gddplant) - leafout => crop_inst%gddtsoi_patch , & ! Input: [real(r8) (:) ] =gdd from top soil layer temperature croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] flag, true if planted, not harvested gddmaturity => cnveg_state_inst%gddmaturity_patch , & ! Input: [real(r8) (:) ] gdd needed to harvest - huileaf => cnveg_state_inst%huileaf_patch , & ! Input: [real(r8) (:) ] heat unit index needed from planting to leaf emergence huigrain => cnveg_state_inst%huigrain_patch , & ! Input: [real(r8) (:) ] same to reach vegetative maturity peaklai => cnveg_state_inst%peaklai_patch , & ! Input: [integer (:) ] 1: max allowed lai; 0: not at max aleafi => cnveg_state_inst%aleafi_patch , & ! Output: [real(r8) (:) ] saved allocation coefficient from phase 2 @@ -1095,6 +1106,9 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! set time steps dt = get_step_size_real() + call CropPhase(bounds, num_pcropp, filter_pcropp, crop_inst, cnveg_state_inst, & + crop_phase = crop_phase(bounds%begp:bounds%endp)) + ! loop over patches to assess the total plant N demand do fp = 1,num_soilp p = filter_soilp(fp) @@ -1141,7 +1155,7 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! and carbon assimilation ! Next phase: leaf emergence to start of leaf decline - if (leafout(p) >= huileaf(p) .and. hui(p) < huigrain(p)) then + if (crop_phase(p) == cphase_leafemerge) then ! allocation rules for crops based on maturity and linear decrease ! of amount allocated to roots over course of the growing season @@ -1178,7 +1192,7 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! shift allocation either when enough gdd are accumulated or maximum number ! of days has elapsed since planting - else if (hui(p) >= huigrain(p)) then + else if (crop_phase(p) == cphase_grainfill) then aroot(p) = max(0._r8, min(1._r8, arooti(ivt(p)) - & (arooti(ivt(p)) - arootf(ivt(p))) * min(1._r8, hui(p)/gddmaturity(p)))) if (astemi(p) > astemf(ivt(p))) then @@ -1218,7 +1232,7 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & if (astem(p) == astemf(ivt(p)) .or. & (ivt(p) /= ntmp_soybean .and. ivt(p) /= nirrig_tmp_soybean .and.& - ivt(p) /= ntrp_soybean .and. ivt(p) /= nirrig_trp_soybean)) then + ivt(p) /= ntrp_soybean .and. ivt(p) /= nirrig_trp_soybean)) then if (grain_flag(p) == 0._r8) then t1 = 1 / dt leafn_to_retransn(p) = t1 * max(leafn(p)- (leafc(p) / fleafcn(ivt(p))),0._r8) @@ -1241,7 +1255,8 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & end do arepr(p,nrepr) = 1._r8 - aroot(p) - astem(p) - aleaf(p) - else ! pre emergence + else if (crop_phase(p) == cphase_planted) then + ! pre emergence ! allocation coefficients should be irrelevant because crops have no ! live carbon pools; this applies to this "else" and to the "else" a few ! lines down @@ -1251,6 +1266,10 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & do k = 1, nrepr arepr(p,k) = 0._r8 end do + + else + write(iulog,*) "ERROR in " // subname // ": unexpected crop_phase: ", crop_phase(p) + call endrun(msg="ERROR: unexpected crop_phase "//errmsg(sourcefile, __LINE__)) end if f1 = aroot(p) / aleaf(p) diff --git a/src/biogeochem/NutrientCompetitionMethodMod.F90 b/src/biogeochem/NutrientCompetitionMethodMod.F90 index 041d285421..7e74775b7a 100644 --- a/src/biogeochem/NutrientCompetitionMethodMod.F90 +++ b/src/biogeochem/NutrientCompetitionMethodMod.F90 @@ -60,6 +60,7 @@ end subroutine init_interface !--------------------------------------------------------------------------- subroutine calc_plant_nutrient_demand_interface (this, bounds, num_soilp, filter_soilp, & + num_pcropp, filter_pcropp, & crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & @@ -90,6 +91,8 @@ subroutine calc_plant_nutrient_demand_interface (this, bounds, num_soilp, filter type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_pcropp ! number of prog crop patches in filter + integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches type(crop_type) , intent(in) :: crop_inst type(canopystate_type) , intent(in) :: canopystate_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst From 6cf95aaf0f64ec15c78d8f8cbedc72a769336575 Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Wed, 30 Mar 2022 12:14:08 -0600 Subject: [PATCH 04/17] Consolidate retranslocation code This will facilitate splitting out different pieces into their own subroutines. --- .../NutrientCompetitionCLM45defaultMod.F90 | 88 ++++++++++--------- .../NutrientCompetitionFlexibleCNMod.F90 | 72 ++++++++------- 2 files changed, 86 insertions(+), 74 deletions(-) diff --git a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 index 81ace923c0..0d245ce402 100644 --- a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 +++ b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 @@ -719,47 +719,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & huigrain(p)),1._r8)**allconsl(ivt(p)) ))) end if - !Beth's retranslocation of leafn, stemn, rootn to organ - !Filter excess plant N to retransn pool for organ N - !Only do one time then hold grain_flag till onset next season - - ! slevis: Will astem ever = astemf exactly? - ! Beth's response: ...looks like astem can equal astemf under the right circumstances. - !It might be worth a rewrite to capture what I was trying to do, but the retranslocation for - !corn and wheat begins at the beginning of the grain fill stage, but for soybean I was holding it - !until after the leaf and stem decline were complete. Looking at how astem is calculated, once the - !stem decline is near complete, astem should (usually) be set to astemf. The reason for holding off - !on soybean is that the retranslocation scheme begins at the beginning of the grain phase, when the - !leaf and stem are still growing, but declining. Since carbon is still getting allocated and now - !there is more nitrogen available, the nitrogen can be diverted from grain. For corn and wheat - !the impact was probably enough to boost productivity, but for soybean the nitrogen was better off - !fulfilling the grain fill. It seems that if the peak lai is reached for soybean though that this - !would be bypassed altogether, not the intended outcome. I checked several of my output files and - !they all seemed to be going through the retranslocation loop for soybean - good news. - - if (astem(p) == astemf(ivt(p)) .or. & - (ivt(p) /= ntmp_soybean .and. ivt(p) /= nirrig_tmp_soybean .and.& - ivt(p) /= ntrp_soybean .and. ivt(p) /= nirrig_trp_soybean)) then - 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) / & - fleafcn(ivt(p)))) - livestemn_to_retransn(p) = t1 * ((livestemc(p) / livewdcn(ivt(p))) - (livestemc(p) / & - fstemcn(ivt(p)))) - frootn_to_retransn(p) = 0._r8 - if (ffrootcn(ivt(p)) > 0._r8) then - frootn_to_retransn(p) = t1 * ((frootc(p) / frootcn(ivt(p))) - (frootc(p) / & - ffrootcn(ivt(p)))) - end if - else !leafn retrans flux is handled in phenology - frootn_to_retransn(p) = 0._r8 - livestemn_to_retransn(p)=0.0_r8 - end if !fun - grain_flag(p) = 1._r8 - end if - end if - ! For AgroIBIS-based crop model, all repr allocation is assumed to go ! into the last reproductive pool. In practice there is only a single ! reproductive pool with the AgroIBIS-based crop model, but for @@ -846,6 +805,53 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! Adding the following line to carry max retransn info to CN Annual Update tempmax_retransn(p) = max(tempmax_retransn(p),retransn(p)) + if (ivt(p) >= npcropmin) then + if (croplive(p)) then + if (crop_phase(p) == cphase_grainfill) then + !Beth's retranslocation of leafn, stemn, rootn to organ + !Filter excess plant N to retransn pool for organ N + !Only do one time then hold grain_flag till onset next season + + ! slevis: Will astem ever = astemf exactly? + ! Beth's response: ...looks like astem can equal astemf under the right circumstances. + !It might be worth a rewrite to capture what I was trying to do, but the retranslocation for + !corn and wheat begins at the beginning of the grain fill stage, but for soybean I was holding it + !until after the leaf and stem decline were complete. Looking at how astem is calculated, once the + !stem decline is near complete, astem should (usually) be set to astemf. The reason for holding off + !on soybean is that the retranslocation scheme begins at the beginning of the grain phase, when the + !leaf and stem are still growing, but declining. Since carbon is still getting allocated and now + !there is more nitrogen available, the nitrogen can be diverted from grain. For corn and wheat + !the impact was probably enough to boost productivity, but for soybean the nitrogen was better off + !fulfilling the grain fill. It seems that if the peak lai is reached for soybean though that this + !would be bypassed altogether, not the intended outcome. I checked several of my output files and + !they all seemed to be going through the retranslocation loop for soybean - good news. + + if (astem(p) == astemf(ivt(p)) .or. & + (ivt(p) /= ntmp_soybean .and. ivt(p) /= nirrig_tmp_soybean .and.& + ivt(p) /= ntrp_soybean .and. ivt(p) /= nirrig_trp_soybean)) then + 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) / & + fleafcn(ivt(p)))) + livestemn_to_retransn(p) = t1 * ((livestemc(p) / livewdcn(ivt(p))) - (livestemc(p) / & + fstemcn(ivt(p)))) + frootn_to_retransn(p) = 0._r8 + if (ffrootcn(ivt(p)) > 0._r8) then + frootn_to_retransn(p) = t1 * ((frootc(p) / frootcn(ivt(p))) - (frootc(p) / & + ffrootcn(ivt(p)))) + end if + else !leafn retrans flux is handled in phenology + frootn_to_retransn(p) = 0._r8 + livestemn_to_retransn(p)=0.0_r8 + end if !fun + grain_flag(p) = 1._r8 + end if + end if + end if + end if + end if + ! Beth's code: crops pull from retransn pool only during grain fill; ! retransn pool has N from leaves, stems, and roots for ! retranslocation diff --git a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 index ece26bd53f..a5b7e3d3d1 100644 --- a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 +++ b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 @@ -1212,39 +1212,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & huigrain(p)),1._r8)**allconsl(ivt(p)) ))) end if - !Beth's retranslocation of leafn, stemn, rootn to organ - !Filter excess plant N to retransn pool for organ N - !Only do one time then hold grain_flag till onset next season - - ! slevis: Will astem ever = astemf exactly? - ! Beth's response: ...looks like astem can equal astemf under the right circumstances. - !It might be worth a rewrite to capture what I was trying to do, but the retranslocation for - !corn and wheat begins at the beginning of the grain fill stage, but for soybean I was holding it - !until after the leaf and stem decline were complete. Looking at how astem is calculated, once the - !stem decline is near complete, astem should (usually) be set to astemf. The reason for holding off - !on soybean is that the retranslocation scheme begins at the beginning of the grain phase, when the - !leaf and stem are still growing, but declining. Since carbon is still getting allocated and now - !there is more nitrogen available, the nitrogen can be diverted from grain. For corn and wheat - !the impact was probably enough to boost productivity, but for soybean the nitrogen was better off - !fulfilling the grain fill. It seems that if the peak lai is reached for soybean though that this - !would be bypassed altogether, not the intended outcome. I checked several of my output files and - !they all seemed to be going through the retranslocation loop for soybean - good news. - - if (astem(p) == astemf(ivt(p)) .or. & - (ivt(p) /= ntmp_soybean .and. ivt(p) /= nirrig_tmp_soybean .and.& - ivt(p) /= ntrp_soybean .and. ivt(p) /= nirrig_trp_soybean)) then - if (grain_flag(p) == 0._r8) then - t1 = 1 / dt - leafn_to_retransn(p) = t1 * max(leafn(p)- (leafc(p) / fleafcn(ivt(p))),0._r8) - livestemn_to_retransn(p) = t1 * max(livestemn(p) - (livestemc(p) / fstemcn(ivt(p))),0._r8) - frootn_to_retransn(p) = 0._r8 - if (ffrootcn(ivt(p)) > 0._r8) then - frootn_to_retransn(p) = t1 * max(frootn(p) - (frootc(p) / ffrootcn(ivt(p))),0._r8) - end if - grain_flag(p) = 1._r8 - end if - end if - ! For AgroIBIS-based crop model, all repr allocation is assumed to go ! into the last reproductive pool. In practice there is only a single ! reproductive pool with the AgroIBIS-based crop model, but for @@ -1379,6 +1346,45 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! Adding the following line to carry max retransn info to CN Annual Update tempmax_retransn(p) = max(tempmax_retransn(p),retransn(p)) + if (ivt(p) >= npcropmin) then + if (croplive(p)) then + if (crop_phase(p) == cphase_grainfill) then + !Beth's retranslocation of leafn, stemn, rootn to organ + !Filter excess plant N to retransn pool for organ N + !Only do one time then hold grain_flag till onset next season + + ! slevis: Will astem ever = astemf exactly? + ! Beth's response: ...looks like astem can equal astemf under the right circumstances. + !It might be worth a rewrite to capture what I was trying to do, but the retranslocation for + !corn and wheat begins at the beginning of the grain fill stage, but for soybean I was holding it + !until after the leaf and stem decline were complete. Looking at how astem is calculated, once the + !stem decline is near complete, astem should (usually) be set to astemf. The reason for holding off + !on soybean is that the retranslocation scheme begins at the beginning of the grain phase, when the + !leaf and stem are still growing, but declining. Since carbon is still getting allocated and now + !there is more nitrogen available, the nitrogen can be diverted from grain. For corn and wheat + !the impact was probably enough to boost productivity, but for soybean the nitrogen was better off + !fulfilling the grain fill. It seems that if the peak lai is reached for soybean though that this + !would be bypassed altogether, not the intended outcome. I checked several of my output files and + !they all seemed to be going through the retranslocation loop for soybean - good news. + + if (astem(p) == astemf(ivt(p)) .or. & + (ivt(p) /= ntmp_soybean .and. ivt(p) /= nirrig_tmp_soybean .and.& + ivt(p) /= ntrp_soybean .and. ivt(p) /= nirrig_trp_soybean)) then + if (grain_flag(p) == 0._r8) then + t1 = 1 / dt + leafn_to_retransn(p) = t1 * max(leafn(p)- (leafc(p) / fleafcn(ivt(p))),0._r8) + livestemn_to_retransn(p) = t1 * max(livestemn(p) - (livestemc(p) / fstemcn(ivt(p))),0._r8) + frootn_to_retransn(p) = 0._r8 + if (ffrootcn(ivt(p)) > 0._r8) then + frootn_to_retransn(p) = t1 * max(frootn(p) - (frootc(p) / ffrootcn(ivt(p))),0._r8) + end if + grain_flag(p) = 1._r8 + end if + end if + end if + end if + end if + ! Beth's code: crops pull from retransn pool only during grain fill; ! retransn pool has N from leaves, stems, and roots for ! retranslocation From fc989d0f64768b1f6dc3f89c906993d7a5cdc1c1 Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Wed, 30 Mar 2022 12:26:52 -0600 Subject: [PATCH 05/17] Add placeholder flag telling us whether we're running with AgSys --- src/main/clm_varctl.F90 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/clm_varctl.F90 b/src/main/clm_varctl.F90 index fc1a464dea..7b3eaedcae 100644 --- a/src/main/clm_varctl.F90 +++ b/src/main/clm_varctl.F90 @@ -145,6 +145,10 @@ module clm_varctl ! If prognostic crops are turned on logical, public :: use_crop = .false. + ! Whether we're using the AgSys crop model + ! TODO(wjs, 2022-03-30) Add namelist control of this variable + logical, public :: use_crop_agsys = .false. + ! true => separate crop landunit is not created by default logical, public :: create_crop_landunit = .false. From f0ffff94ba37c0d19ea9c8fb35729d2a5d3ade42 Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Thu, 31 Mar 2022 12:24:02 -0600 Subject: [PATCH 06/17] Make use_crop_agsys a parameter for now --- src/main/clm_varctl.F90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/clm_varctl.F90 b/src/main/clm_varctl.F90 index 7b3eaedcae..40135e8575 100644 --- a/src/main/clm_varctl.F90 +++ b/src/main/clm_varctl.F90 @@ -146,8 +146,9 @@ module clm_varctl logical, public :: use_crop = .false. ! Whether we're using the AgSys crop model - ! TODO(wjs, 2022-03-30) Add namelist control of this variable - logical, public :: use_crop_agsys = .false. + ! TODO(wjs, 2022-03-30) Add namelist control of this variable (at which point we'll + ! need to remove the 'parameter' attribute) + logical, public, parameter :: use_crop_agsys = .false. ! true => separate crop landunit is not created by default logical, public :: create_crop_landunit = .false. From e24b6287ce41e5db7c5571a7a6c1faf797c8968e Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Thu, 31 Mar 2022 15:36:20 -0600 Subject: [PATCH 07/17] Move setting of grain_flag to where it belongs --- src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 | 5 +++-- src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 index 4d993f3aa7..d81dbb0cbd 100644 --- a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 +++ b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 @@ -695,7 +695,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & astemi(p) = astem(p) ! save for use by equations after shift aleafi(p) = aleaf(p) ! to reproductive phenology stage begins - grain_flag(p) = 0._r8 ! setting to 0 while in phase 2 ! Phase 2 completed: ! ================== @@ -811,7 +810,9 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & if (ivt(p) >= npcropmin) then if (croplive(p)) then - if (crop_phase(p) == cphase_grainfill) then + if (crop_phase(p) == cphase_leafemerge) then + grain_flag(p) = 0._r8 ! setting to 0 while in phase 2 + else if (crop_phase(p) == cphase_grainfill) then !Beth's retranslocation of leafn, stemn, rootn to organ !Filter excess plant N to retransn pool for organ N !Only do one time then hold grain_flag till onset next season diff --git a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 index ef9f395325..d944ddd3a7 100644 --- a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 +++ b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 @@ -1185,7 +1185,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & astemi(p) = astem(p) ! save for use by equations after shift aleafi(p) = aleaf(p) ! to reproductive phenology stage begins - grain_flag(p) = 0._r8 ! setting to 0 while in phase 2 ! Phase 2 completed: ! ================== @@ -1348,7 +1347,9 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & if (ivt(p) >= npcropmin) then if (croplive(p)) then - if (crop_phase(p) == cphase_grainfill) then + if (crop_phase(p) == cphase_leafemerge) then + grain_flag(p) = 0._r8 ! setting to 0 while in phase 2 + else if (crop_phase(p) == cphase_grainfill) then !Beth's retranslocation of leafn, stemn, rootn to organ !Filter excess plant N to retransn pool for organ N !Only do one time then hold grain_flag till onset next season From d133823b229bfaa464ce15653dcbcd9c7dd42d01 Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Thu, 31 Mar 2022 22:22:26 -0600 Subject: [PATCH 08/17] Extract the calculation of crop allocation fractions into its own subr This way we can share this code between the different nutrient competition methods rather than having it duplicated. It also gives us a path towards skipping these calculations when running with AgSys. One change I made in doing this refactor was changing allocation fractions to always sum to 1, even in pre-emergence and when not croplive... this shouldn't make any difference outside of the allocation fractions themselves (and note that those aren't output to history files, so I don't think this should lead to any answer changes). --- src/biogeochem/CNAllocationMod.F90 | 181 +++++++++++++++++- src/biogeochem/CNDriverMod.F90 | 9 +- src/biogeochem/CNPhenologyMod.F90 | 2 +- .../NutrientCompetitionCLM45defaultMod.F90 | 155 ++------------- .../NutrientCompetitionFlexibleCNMod.F90 | 156 ++------------- 5 files changed, 217 insertions(+), 286 deletions(-) diff --git a/src/biogeochem/CNAllocationMod.F90 b/src/biogeochem/CNAllocationMod.F90 index 089c3b8cbf..7401d26b7c 100644 --- a/src/biogeochem/CNAllocationMod.F90 +++ b/src/biogeochem/CNAllocationMod.F90 @@ -13,22 +13,26 @@ module CNAllocationMod use abortutils , only : endrun use decompMod , only : bounds_type use clm_varcon , only : secspday - use clm_varctl , only : use_c13, use_c14 + use clm_varctl , only : use_c13, use_c14, iulog use PatchType , only : patch use pftconMod , only : pftcon, npcropmin use CropType , only : crop_type + use CropType , only : cphase_planted, cphase_leafemerge, cphase_grainfill use PhotosynthesisMod , only : photosyns_type use CanopyStateType , only : canopystate_type use CNVegCarbonStateType , only : cnveg_carbonstate_type use CNVegCarbonFluxType , only : cnveg_carbonflux_type + use CNVegStateType , only : cnveg_state_type use CropReprPoolsMod , only : nrepr + use CNPhenologyMod , only : CropPhase ! implicit none private ! ! !PUBLIC MEMBER FUNCTIONS: public :: readParams ! Read in parameters from file - public :: calc_gpp_mr_availc + public :: calc_gpp_mr_availc ! Calculate total GPP, various maintenance respiration terms, and total available C for allocation + public :: calc_crop_allocation_fractions ! Calculate crop allocation fractions to leaf, stem, root and repr ! !PRIVATE MEMBER VARIABLES: type, private :: params_type @@ -243,4 +247,177 @@ subroutine calc_gpp_mr_availc(bounds, num_soilp, filter_soilp, & end subroutine calc_gpp_mr_availc + !----------------------------------------------------------------------- + subroutine calc_crop_allocation_fractions(bounds, num_pcropp, filter_pcropp, & + crop_inst, cnveg_state_inst) + ! + ! !DESCRIPTION: + ! Calculate crop allocation fractions to leaf, stem, root and repr, following + ! AgroIBIS subroutine phenocrop + ! + ! This sets the following variables in cnveg_state_inst for all patches in the pcrop + ! filter: + ! - aleaf + ! - astem + ! - aroot + ! - arepr + ! + ! And under some conditions it updates the following variables: + ! - astemi + ! - aleafi + ! + ! !ARGUMENTS: + 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 + type(crop_type) , intent(in) :: crop_inst + type(cnveg_state_type) , intent(inout) :: cnveg_state_inst + ! + ! !LOCAL VARIABLES: + integer :: p, fp, k + real(r8) :: fleaf ! fraction allocated to leaf + real(r8) :: crop_phase(bounds%begp:bounds%endp) + + character(len=*), parameter :: subname = 'calc_crop_allocation_fractions' + !----------------------------------------------------------------------- + + associate( & + ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type + arooti => pftcon%arooti , & ! Input: parameter used below + arootf => pftcon%arootf , & ! Input: parameter used below + bfact => pftcon%bfact , & ! Input: parameter used below + fleafi => pftcon%fleafi , & ! Input: parameter used below + aleaff => pftcon%aleaff , & ! Input: parameter used below + astemf => pftcon%astemf , & ! Input: parameter used below + allconss => pftcon%allconss , & ! Input: parameter used below + allconsl => pftcon%allconsl , & ! Input: parameter used below + declfact => pftcon%declfact , & ! Input: parameter used below + croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] flag, true if planted, not harvested + hui => crop_inst%hui_patch , & ! Input: [real(r8) (:) ] crop patch heat unit index (growing degree-days); set to 0 at sowing and accumulated until harvest + peaklai => cnveg_state_inst%peaklai_patch , & ! Input: [integer (:) ] 1: max allowed lai; 0: not at max + gddmaturity => cnveg_state_inst%gddmaturity_patch , & ! Input: [real(r8) (:) ] gdd needed to harvest + huigrain => cnveg_state_inst%huigrain_patch , & ! Input: [real(r8) (:) ] same to reach vegetative maturity + aleafi => cnveg_state_inst%aleafi_patch , & ! Output: [real(r8) (:) ] saved allocation coefficient from phase 2 + astemi => cnveg_state_inst%astemi_patch , & ! Output: [real(r8) (:) ] saved allocation coefficient from phase 2 + aleaf => cnveg_state_inst%aleaf_patch , & ! Output: [real(r8) (:) ] leaf allocation coefficient + astem => cnveg_state_inst%astem_patch , & ! Output: [real(r8) (:) ] stem allocation coefficient + aroot => cnveg_state_inst%aroot_patch , & ! Output: [real(r8) (:) ] root allocation coefficient + arepr => cnveg_state_inst%arepr_patch & ! Output: [real(r8) (:,:) ] reproductive allocation coefficient(s) + ) + + call CropPhase(bounds, num_pcropp, filter_pcropp, crop_inst, cnveg_state_inst, & + crop_phase = crop_phase(bounds%begp:bounds%endp)) + + do fp = 1, num_pcropp + p = filter_pcropp(fp) + + if (croplive(p)) then + ! same phases appear in subroutine CropPhenology + + ! Phase 1 completed: + ! ================== + ! if hui is less than the number of gdd needed for filling of grain + ! leaf emergence also has to have taken place for lai changes to occur + ! and carbon assimilation + ! Next phase: leaf emergence to start of leaf decline + + if (crop_phase(p) == cphase_leafemerge) then + + ! allocation rules for crops based on maturity and linear decrease + ! of amount allocated to roots over course of the growing season + + do k = 1, nrepr + arepr(p,k) = 0._r8 + end do + if (peaklai(p) == 1) then ! lai at maximum allowed + aleaf(p) = 1.e-5_r8 + astem(p) = 0._r8 + aroot(p) = 1._r8 - aleaf(p) + else + aroot(p) = max(0._r8, min(1._r8, arooti(ivt(p)) - & + (arooti(ivt(p)) - arootf(ivt(p))) * & + min(1._r8, hui(p)/gddmaturity(p)))) + fleaf = fleafi(ivt(p)) * (exp(-bfact(ivt(p))) - & + exp(-bfact(ivt(p))*hui(p)/huigrain(p))) / & + (exp(-bfact(ivt(p)))-1) ! fraction alloc to leaf (from J Norman alloc curve) + aleaf(p) = max(1.e-5_r8, (1._r8 - aroot(p)) * fleaf) + astem(p) = 1._r8 - aleaf(p) - aroot(p) + end if + + ! AgroIBIS included here an immediate adjustment to aleaf & astem if the + ! predicted lai from the above allocation coefficients exceeded laimx. + ! We have decided to live with lais slightly higher than laimx by + ! enforcing the cap in the following tstep through the peaklai logic above. + + astemi(p) = astem(p) ! save for use by equations after shift + aleafi(p) = aleaf(p) ! to reproductive phenology stage begins + + ! Phase 2 completed: + ! ================== + ! shift allocation either when enough gdd are accumulated or maximum number + ! of days has elapsed since planting + + else if (crop_phase(p) == cphase_grainfill) then + aroot(p) = max(0._r8, min(1._r8, arooti(ivt(p)) - & + (arooti(ivt(p)) - arootf(ivt(p))) * min(1._r8, hui(p)/gddmaturity(p)))) + if (astemi(p) > astemf(ivt(p))) then + astem(p) = max(0._r8, max(astemf(ivt(p)), astem(p) * & + (1._r8 - min((hui(p)- & + huigrain(p))/((gddmaturity(p)*declfact(ivt(p)))- & + huigrain(p)),1._r8)**allconss(ivt(p)) ))) + end if + + ! If crops have hit peaklai, then set leaf allocation to small value + if (peaklai(p) == 1) then + aleaf(p) = 1.e-5_r8 + else if (aleafi(p) > aleaff(ivt(p))) then + aleaf(p) = max(1.e-5_r8, max(aleaff(ivt(p)), aleaf(p) * & + (1._r8 - min((hui(p)- & + huigrain(p))/((gddmaturity(p)*declfact(ivt(p)))- & + huigrain(p)),1._r8)**allconsl(ivt(p)) ))) + end if + + ! For AgroIBIS-based crop model, all repr allocation is assumed to go + ! into the last reproductive pool. In practice there is only a single + ! reproductive pool with the AgroIBIS-based crop model, but for + ! software testing we can have multiple, in which situation we want the + ! active pool to be the last one. + do k = 1, nrepr-1 + arepr(p,k) = 0._r8 + end do + arepr(p,nrepr) = 1._r8 - aroot(p) - astem(p) - aleaf(p) + + else if (crop_phase(p) == cphase_planted) then + ! pre emergence + ! allocation coefficients should be irrelevant because crops have no + ! live carbon pools + aleaf(p) = 1._r8 + astem(p) = 0._r8 + aroot(p) = 0._r8 + do k = 1, nrepr + arepr(p,k) = 0._r8 + end do + + else + write(iulog,*) "ERROR in " // subname // ": unexpected crop_phase: ", crop_phase(p) + call endrun(msg="ERROR: unexpected crop_phase "//errmsg(sourcefile, __LINE__)) + end if + + else ! .not croplive + ! allocation coefficients should be irrelevant because crops have no + ! live carbon pools + aleaf(p) = 1._r8 + astem(p) = 0._r8 + aroot(p) = 0._r8 + do k = 1, nrepr + arepr(p,k) = 0._r8 + end do + end if + + end do + + end associate + + end subroutine calc_crop_allocation_fractions + end module CNAllocationMod diff --git a/src/biogeochem/CNDriverMod.F90 b/src/biogeochem/CNDriverMod.F90 index 94b9563be8..21a62b2b60 100644 --- a/src/biogeochem/CNDriverMod.F90 +++ b/src/biogeochem/CNDriverMod.F90 @@ -11,7 +11,7 @@ module CNDriverMod use decompMod , only : bounds_type use perf_mod , only : t_startf, t_stopf use clm_varctl , only : use_nitrif_denitrif, use_nguardrail - use clm_varctl , only : iulog, use_crop + use clm_varctl , only : iulog, use_crop, use_crop_agsys use SoilBiogeochemDecompCascadeConType, only : mimics_decomp, century_decomp, decomp_method use CNSharedParamsMod , only : use_fun use CNVegStateType , only : cnveg_state_type @@ -113,7 +113,7 @@ subroutine CNDriverNoLeaching(bounds, use clm_varpar , only: nlevdecomp, ndecomp_cascade_transitions, ndecomp_pools use subgridAveMod , only: p2c use CropType , only: crop_type - use CNAllocationMod , only: calc_gpp_mr_availc + use CNAllocationMod , only: calc_gpp_mr_availc, calc_crop_allocation_fractions use CNNDynamicsMod , only: CNNDeposition,CNNFixation, CNNFert, CNSoyfix,CNFreeLivingFixation use CNMRespMod , only: CNMResp use CNFUNMod , only: CNFUNInit !, CNFUN @@ -390,6 +390,11 @@ subroutine CNDriverNoLeaching(bounds, crop_inst, photosyns_inst, canopystate_inst, & cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst) + + if (.not. use_crop_agsys) then + call calc_crop_allocation_fractions(bounds, num_pcropp, filter_pcropp, & + crop_inst, cnveg_state_inst) + end if call t_stopf('cnalloc') call t_startf('calc_plant_nutrient_demand') diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index 4cbb5555e2..f87a59ebaf 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2185,7 +2185,7 @@ subroutine CropPhase(bounds, num_pcropp, filter_pcropp, & associate( & croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] Flag, true if planted, not harvested - hui => crop_inst%gddplant_patch , & ! Input: [real(r8) (:) ] gdd since planting (gddplant) + hui => crop_inst%hui_patch , & ! Input: [real(r8) (:) ] gdd since planting (gddplant) leafout => crop_inst%gddtsoi_patch , & ! Input: [real(r8) (:) ] gdd from top soil layer temperature huileaf => cnveg_state_inst%huileaf_patch , & ! Input: [real(r8) (:) ] heat unit index needed from planting to leaf emergence huigrain => cnveg_state_inst%huigrain_patch & ! Input: [real(r8) (:) ] same to reach vegetative maturity diff --git a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 index d81dbb0cbd..10e601e8e8 100644 --- a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 +++ b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 @@ -19,7 +19,7 @@ module NutrientCompetitionCLM45defaultMod use NutrientCompetitionMethodMod, only : nutrient_competition_method_type use CropReprPoolsMod , only : nrepr use CNPhenologyMod , only : CropPhase - use CropType , only : cphase_planted, cphase_leafemerge, cphase_grainfill + use CropType , only : cphase_leafemerge, cphase_grainfill use clm_varctl , only : iulog use abortutils , only : endrun ! @@ -534,7 +534,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & real(r8):: cnl,cnfr,cnlw,cndw ! C:N ratios for leaf, fine root, and wood real(r8):: f5(nrepr) ! reproductive allocation parameters real(r8):: cng ! C:N ratio for grain (= cnlw for now; slevis) - real(r8):: fleaf ! fraction allocated to leaf real(r8):: t1 ! temporary variable real(r8):: dt ! model time step real(r8):: f5_tot ! sum of f5 terms @@ -560,33 +559,19 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & 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 - bfact => pftcon%bfact , & ! Input: parameter used below - aleaff => pftcon%aleaff , & ! Input: parameter used below - arootf => pftcon%arootf , & ! Input: parameter used below astemf => pftcon%astemf , & ! Input: parameter used below - arooti => pftcon%arooti , & ! Input: parameter used below - fleafi => pftcon%fleafi , & ! Input: parameter used below - allconsl => pftcon%allconsl , & ! Input: parameter used below - allconss => pftcon%allconss , & ! Input: parameter used below grperc => pftcon%grperc , & ! Input: parameter used below grpnow => pftcon%grpnow , & ! Input: parameter used below - declfact => pftcon%declfact , & ! Input: 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-deciduous leaf habit (0 or 1) - hui => crop_inst%hui_patch , & ! Input: [real(r8) (:) ] crop patch heat unit index (growing degree-days); set to 0 at sowing and accumulated until harvest croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] flag, true if planted, not harvested - gddmaturity => cnveg_state_inst%gddmaturity_patch , & ! Input: [real(r8) (:) ] gdd needed to harvest - huigrain => cnveg_state_inst%huigrain_patch , & ! Input: [real(r8) (:) ] same to reach vegetative maturity - peaklai => cnveg_state_inst%peaklai_patch , & ! Input: [integer (:) ] 1: max allowed lai; 0: not at max - aleafi => cnveg_state_inst%aleafi_patch , & ! Output: [real(r8) (:) ] saved allocation coefficient from phase 2 - astemi => cnveg_state_inst%astemi_patch , & ! Output: [real(r8) (:) ] saved allocation coefficient from phase 2 - aleaf => cnveg_state_inst%aleaf_patch , & ! Output: [real(r8) (:) ] leaf allocation coefficient - astem => cnveg_state_inst%astem_patch , & ! Output: [real(r8) (:) ] stem allocation coefficient - aroot => cnveg_state_inst%aroot_patch , & ! Output: [real(r8) (:) ] root allocation coefficient - arepr => cnveg_state_inst%arepr_patch , & ! Output: [real(r8) (:,:) ] reproductive allocation coefficient(s) + aleaf => cnveg_state_inst%aleaf_patch , & ! Input: [real(r8) (:) ] leaf allocation coefficient + astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient + aroot => cnveg_state_inst%aroot_patch , & ! Input: [real(r8) (:) ] root allocation coefficient + arepr => cnveg_state_inst%arepr_patch , & ! Input: [real(r8) (:,:) ] reproductive allocation coefficient(s) grain_flag => cnveg_state_inst%grain_flag_patch , & ! Output: [real(r8) (:) ] 1: grain fill stage; 0: not c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) n_allometry => cnveg_state_inst%n_allometry_patch , & ! Output: [real(r8) (:) ] N allocation index (DIM) @@ -640,132 +625,17 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & end if f4 = flivewd(ivt(p)) - g1 = grperc(ivt(p)) + if (ivt(p) >= npcropmin) then + g1 = 0.25_r8 + else + g1 = grperc(ivt(p)) + end if g2 = grpnow(ivt(p)) cnl = leafcn(ivt(p)) cnfr = frootcn(ivt(p)) cnlw = livewdcn(ivt(p)) cndw = deadwdcn(ivt(p)) - ! calculate f1 to f5 for prog crops following AgroIBIS subr phenocrop - - do k = 1, nrepr - f5(k) = 0._r8 ! continued intializations from above - end do - - if (ivt(p) >= npcropmin) then ! skip 2 generic crops - - if (croplive(p)) then - ! same phases appear in subroutine CropPhenology - - ! Phase 1 completed: - ! ================== - ! if hui is less than the number of gdd needed for filling of grain - ! leaf emergence also has to have taken place for lai changes to occur - ! and carbon assimilation - ! Next phase: leaf emergence to start of leaf decline - - if (crop_phase(p) == cphase_leafemerge) then - - ! allocation rules for crops based on maturity and linear decrease - ! of amount allocated to roots over course of the growing season - - do k = 1, nrepr - arepr(p,k) = 0._r8 - end do - if (peaklai(p) == 1) then ! lai at maximum allowed - aleaf(p) = 1.e-5_r8 - astem(p) = 0._r8 - aroot(p) = 1._r8 - aleaf(p) - else - aroot(p) = max(0._r8, min(1._r8, arooti(ivt(p)) - & - (arooti(ivt(p)) - arootf(ivt(p))) * & - min(1._r8, hui(p)/gddmaturity(p)))) - fleaf = fleafi(ivt(p)) * (exp(-bfact(ivt(p))) - & - exp(-bfact(ivt(p))*hui(p)/huigrain(p))) / & - (exp(-bfact(ivt(p)))-1) ! fraction alloc to leaf (from J Norman alloc curve) - aleaf(p) = max(1.e-5_r8, (1._r8 - aroot(p)) * fleaf) - astem(p) = 1._r8 - aleaf(p) - aroot(p) - end if - - ! AgroIBIS included here an immediate adjustment to aleaf & astem if the - ! predicted lai from the above allocation coefficients exceeded laimx. - ! We have decided to live with lais slightly higher than laimx by - ! enforcing the cap in the following tstep through the peaklai logic above. - - astemi(p) = astem(p) ! save for use by equations after shift - aleafi(p) = aleaf(p) ! to reproductive phenology stage begins - - ! Phase 2 completed: - ! ================== - ! shift allocation either when enough gdd are accumulated or maximum number - ! of days has elapsed since planting - - else if (crop_phase(p) == cphase_grainfill) then - - aroot(p) = max(0._r8, min(1._r8, arooti(ivt(p)) - & - (arooti(ivt(p)) - arootf(ivt(p))) * min(1._r8, hui(p)/gddmaturity(p)))) - if (astemi(p) > astemf(ivt(p))) then - astem(p) = max(0._r8, max(astemf(ivt(p)), astem(p) * & - (1._r8 - min((hui(p)- & - huigrain(p))/((gddmaturity(p)*declfact(ivt(p)))- & - huigrain(p)),1._r8)**allconss(ivt(p)) ))) - end if - - ! If crops have hit peaklai, then set leaf allocation to small value - if (peaklai(p) == 1) then - aleaf(p) = 1.e-5_r8 - else if (aleafi(p) > aleaff(ivt(p))) then - aleaf(p) = max(1.e-5_r8, max(aleaff(ivt(p)), aleaf(p) * & - (1._r8 - min((hui(p)- & - huigrain(p))/((gddmaturity(p)*declfact(ivt(p)))- & - huigrain(p)),1._r8)**allconsl(ivt(p)) ))) - end if - - ! For AgroIBIS-based crop model, all repr allocation is assumed to go - ! into the last reproductive pool. In practice there is only a single - ! reproductive pool with the AgroIBIS-based crop model, but for - ! software testing we can have multiple, in which situation we want the - ! active pool to be the last one. - do k = 1, nrepr-1 - arepr(p,k) = 0._r8 - end do - arepr(p,nrepr) = 1._r8 - aroot(p) - astem(p) - aleaf(p) - - else if (crop_phase(p) == cphase_planted) then - ! pre emergence - ! allocation coefficients should be irrelevant because crops have no - ! live carbon pools; this applies to this "else" and to the "else" a few - ! lines down - aleaf(p) = 1.e-5_r8 - astem(p) = 0._r8 - aroot(p) = 0._r8 - do k = 1, nrepr - arepr(p,k) = 0._r8 - end do - - else - write(iulog,*) "ERROR in " // subname // ": unexpected crop_phase: ", crop_phase(p) - call endrun(msg="ERROR: unexpected crop_phase "//errmsg(sourcefile, __LINE__)) - end if - - f1 = aroot(p) / aleaf(p) - f3 = astem(p) / aleaf(p) - do k = 1, nrepr - f5(k) = arepr(p,k) / aleaf(p) - end do - g1 = 0.25_r8 - - else ! .not croplive - f1 = 0._r8 - f3 = 0._r8 - do k = 1, nrepr - f5(k) = 0._r8 - end do - g1 = 0.25_r8 - end if - end if - ! based on available C, use constant allometric relationships to ! determine N requirements @@ -783,6 +653,11 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & (f3*(1._r8-f4)*(1._r8+f2))/cndw else if (ivt(p) >= npcropmin) then ! skip generic crops cng = graincn(ivt(p)) + f1 = aroot(p) / aleaf(p) + f3 = astem(p) / aleaf(p) + do k = 1, nrepr + f5(k) = arepr(p,k) / aleaf(p) + end do f5_tot = 0._r8 f5_n_tot = 0._r8 do k = 1, nrepr diff --git a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 index d944ddd3a7..1c1287130c 100644 --- a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 +++ b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 @@ -26,7 +26,7 @@ module NutrientCompetitionFlexibleCNMod use NutrientCompetitionMethodMod, only : nutrient_competition_method_type use CropReprPoolsMod , only : nrepr use CNPhenologyMod , only : CropPhase - use CropType , only : cphase_planted, cphase_leafemerge, cphase_grainfill + use CropType , only : cphase_leafemerge, cphase_grainfill use clm_varctl , only : iulog use abortutils , only : endrun ! @@ -999,7 +999,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & real(r8) :: cnl, cnfr, cnlw, cndw ! C:N ratios for leaf, fine root, and wood real(r8) :: f5(nrepr) ! reproductive allocation parameters real(r8) :: cng ! C:N ratio for grain (= cnlw for now; slevis) - real(r8) :: fleaf ! fraction allocated to leaf real(r8) :: t1 ! temporary variable real(r8) :: dt ! model time step real(r8) :: f5_tot ! sum of f5 terms @@ -1040,35 +1039,21 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & 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 - bfact => pftcon%bfact , & ! Input: parameter used below - aleaff => pftcon%aleaff , & ! Input: parameter used below - arootf => pftcon%arootf , & ! Input: parameter used below astemf => pftcon%astemf , & ! Input: parameter used below - arooti => pftcon%arooti , & ! Input: parameter used below - fleafi => pftcon%fleafi , & ! Input: parameter used below - allconsl => pftcon%allconsl , & ! Input: parameter used below - allconss => pftcon%allconss , & ! Input: parameter used below grperc => pftcon%grperc , & ! Input: parameter used below grpnow => pftcon%grpnow , & ! Input: parameter used below - declfact => pftcon%declfact , & ! Input: 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-deciduous leaf habit (0 or 1) 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 - hui => crop_inst%hui_patch , & ! Input: [real(r8) (:) ] crop patch heat unit index (growing degree-days); set to 0 at sowing and accumulated until harvest croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] flag, true if planted, not harvested - gddmaturity => cnveg_state_inst%gddmaturity_patch , & ! Input: [real(r8) (:) ] gdd needed to harvest - huigrain => cnveg_state_inst%huigrain_patch , & ! Input: [real(r8) (:) ] same to reach vegetative maturity - peaklai => cnveg_state_inst%peaklai_patch , & ! Input: [integer (:) ] 1: max allowed lai; 0: not at max - aleafi => cnveg_state_inst%aleafi_patch , & ! Output: [real(r8) (:) ] saved allocation coefficient from phase 2 - astemi => cnveg_state_inst%astemi_patch , & ! Output: [real(r8) (:) ] saved allocation coefficient from phase 2 - aleaf => cnveg_state_inst%aleaf_patch , & ! Output: [real(r8) (:) ] leaf allocation coefficient - astem => cnveg_state_inst%astem_patch , & ! Output: [real(r8) (:) ] stem allocation coefficient - aroot => cnveg_state_inst%aroot_patch , & ! Output: [real(r8) (:) ] root allocation coefficient - arepr => cnveg_state_inst%arepr_patch , & ! Output: [real(r8) (:,:) ] reproductive allocation coefficient(s) + aleaf => cnveg_state_inst%aleaf_patch , & ! Input: [real(r8) (:) ] leaf allocation coefficient + astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient + aroot => cnveg_state_inst%aroot_patch , & ! Input: [real(r8) (:) ] root allocation coefficient + arepr => cnveg_state_inst%arepr_patch , & ! Input: [real(r8) (:,:) ] reproductive allocation coefficient(s) grain_flag => cnveg_state_inst%grain_flag_patch , & ! Output: [real(r8) (:) ] 1: grain fill stage; 0: not c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) n_allometry => cnveg_state_inst%n_allometry_patch , & ! Output: [real(r8) (:) ] N allocation index (DIM) @@ -1129,133 +1114,17 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & end if f4 = flivewd(ivt(p)) - g1 = grperc(ivt(p)) + if (ivt(p) >= npcropmin) then + g1 = 0.25_r8 + else + g1 = grperc(ivt(p)) + end if g2 = grpnow(ivt(p)) cnl = leafcn(ivt(p)) cnfr = frootcn(ivt(p)) cnlw = livewdcn(ivt(p)) cndw = deadwdcn(ivt(p)) - - ! calculate f1 to f5 for prog crops following AgroIBIS subr phenocrop - - do k = 1, nrepr - f5(k) = 0._r8 ! continued intializations from above - end do - - if (ivt(p) >= npcropmin) then ! skip 2 generic crops - - if (croplive(p)) then - ! same phases appear in subroutine CropPhenology - - ! Phase 1 completed: - ! ================== - ! if hui is less than the number of gdd needed for filling of grain - ! leaf emergence also has to have taken place for lai changes to occur - ! and carbon assimilation - ! Next phase: leaf emergence to start of leaf decline - - if (crop_phase(p) == cphase_leafemerge) then - - ! allocation rules for crops based on maturity and linear decrease - ! of amount allocated to roots over course of the growing season - - do k = 1, nrepr - arepr(p,k) = 0._r8 - end do - if (peaklai(p) == 1) then ! lai at maximum allowed - aleaf(p) = 1.e-5_r8 - astem(p) = 0._r8 - aroot(p) = 1._r8 - aleaf(p) - else - aroot(p) = max(0._r8, min(1._r8, arooti(ivt(p)) - & - (arooti(ivt(p)) - arootf(ivt(p))) * & - min(1._r8, hui(p)/gddmaturity(p)))) - fleaf = fleafi(ivt(p)) * (exp(-bfact(ivt(p))) - & - exp(-bfact(ivt(p))*hui(p)/huigrain(p))) / & - (exp(-bfact(ivt(p)))-1) ! fraction alloc to leaf (from J Norman alloc curve) - aleaf(p) = max(1.e-5_r8, (1._r8 - aroot(p)) * fleaf) - astem(p) = 1._r8 - aleaf(p) - aroot(p) - end if - - ! AgroIBIS included here an immediate adjustment to aleaf & astem if the - ! predicted lai from the above allocation coefficients exceeded laimx. - ! We have decided to live with lais slightly higher than laimx by - ! enforcing the cap in the following tstep through the peaklai logic above. - - astemi(p) = astem(p) ! save for use by equations after shift - aleafi(p) = aleaf(p) ! to reproductive phenology stage begins - - ! Phase 2 completed: - ! ================== - ! shift allocation either when enough gdd are accumulated or maximum number - ! of days has elapsed since planting - - else if (crop_phase(p) == cphase_grainfill) then - aroot(p) = max(0._r8, min(1._r8, arooti(ivt(p)) - & - (arooti(ivt(p)) - arootf(ivt(p))) * min(1._r8, hui(p)/gddmaturity(p)))) - if (astemi(p) > astemf(ivt(p))) then - astem(p) = max(0._r8, max(astemf(ivt(p)), astem(p) * & - (1._r8 - min((hui(p)- & - huigrain(p))/((gddmaturity(p)*declfact(ivt(p)))- & - huigrain(p)),1._r8)**allconss(ivt(p)) ))) - end if - - ! If crops have hit peaklai, then set leaf allocation to small value - if (peaklai(p) == 1) then - aleaf(p) = 1.e-5_r8 - else if (aleafi(p) > aleaff(ivt(p))) then - aleaf(p) = max(1.e-5_r8, max(aleaff(ivt(p)), aleaf(p) * & - (1._r8 - min((hui(p)- & - huigrain(p))/((gddmaturity(p)*declfact(ivt(p)))- & - huigrain(p)),1._r8)**allconsl(ivt(p)) ))) - end if - - ! For AgroIBIS-based crop model, all repr allocation is assumed to go - ! into the last reproductive pool. In practice there is only a single - ! reproductive pool with the AgroIBIS-based crop model, but for - ! software testing we can have multiple, in which situation we want the - ! active pool to be the last one. - do k = 1, nrepr-1 - arepr(p,k) = 0._r8 - end do - arepr(p,nrepr) = 1._r8 - aroot(p) - astem(p) - aleaf(p) - - else if (crop_phase(p) == cphase_planted) then - ! pre emergence - ! allocation coefficients should be irrelevant because crops have no - ! live carbon pools; this applies to this "else" and to the "else" a few - ! lines down - aleaf(p) = 1.e-5_r8 - astem(p) = 0._r8 - aroot(p) = 0._r8 - do k = 1, nrepr - arepr(p,k) = 0._r8 - end do - - else - write(iulog,*) "ERROR in " // subname // ": unexpected crop_phase: ", crop_phase(p) - call endrun(msg="ERROR: unexpected crop_phase "//errmsg(sourcefile, __LINE__)) - end if - - f1 = aroot(p) / aleaf(p) - f3 = astem(p) / aleaf(p) - do k = 1, nrepr - f5(k) = arepr(p,k) / aleaf(p) - end do - g1 = 0.25_r8 - - - else ! .not croplive - f1 = 0._r8 - f3 = 0._r8 - do k = 1, nrepr - f5(k) = 0._r8 - end do - g1 = 0.25_r8 - end if - end if - ! based on available C, use constant allometric relationships to ! determine N requirements if (.not. use_fun) then @@ -1269,6 +1138,11 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & (f3*(1._r8-f4)*(1._r8+f2))/cndw else if (ivt(p) >= npcropmin) then ! skip generic crops cng = graincn(ivt(p)) + f1 = aroot(p) / aleaf(p) + f3 = astem(p) / aleaf(p) + do k = 1, nrepr + f5(k) = arepr(p,k) / aleaf(p) + end do f5_tot = 0._r8 f5_n_tot = 0._r8 do k = 1, nrepr From dc14d4d9ad854839d18e14b52bd7b42abd373bdd Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Tue, 5 Apr 2022 21:51:40 -0600 Subject: [PATCH 09/17] Extract the calculation of allometry into its own subroutine This allows the duplicate code for the calculation of allometry (in both nutrient competition methods) to be consolidated into a single subroutine. --- src/biogeochem/CNAllocationMod.F90 | 123 +++++++++++++++++- src/biogeochem/CNDriverMod.F90 | 4 + .../NutrientCompetitionCLM45defaultMod.F90 | 90 +------------ .../NutrientCompetitionFlexibleCNMod.F90 | 84 +----------- 4 files changed, 129 insertions(+), 172 deletions(-) diff --git a/src/biogeochem/CNAllocationMod.F90 b/src/biogeochem/CNAllocationMod.F90 index 7401d26b7c..9241e9af0c 100644 --- a/src/biogeochem/CNAllocationMod.F90 +++ b/src/biogeochem/CNAllocationMod.F90 @@ -25,14 +25,16 @@ module CNAllocationMod use CNVegStateType , only : cnveg_state_type use CropReprPoolsMod , only : nrepr use CNPhenologyMod , only : CropPhase + use CNSharedParamsMod , only : use_fun ! implicit none private ! ! !PUBLIC MEMBER FUNCTIONS: - public :: readParams ! Read in parameters from file - public :: calc_gpp_mr_availc ! Calculate total GPP, various maintenance respiration terms, and total available C for allocation + public :: readParams ! Read in parameters from file + public :: calc_gpp_mr_availc ! Calculate total GPP, various maintenance respiration terms, and total available C for allocation public :: calc_crop_allocation_fractions ! Calculate crop allocation fractions to leaf, stem, root and repr + public :: calc_allometry ! Calculate c_allometry and n_allometry terms based on allocation fractions ! !PRIVATE MEMBER VARIABLES: type, private :: params_type @@ -420,4 +422,121 @@ subroutine calc_crop_allocation_fractions(bounds, num_pcropp, filter_pcropp, & end subroutine calc_crop_allocation_fractions + !----------------------------------------------------------------------- + subroutine calc_allometry(num_soilp, filter_soilp, & + cnveg_carbonflux_inst, cnveg_state_inst) + ! + ! !DESCRIPTION: + ! Calculate c_allometry and n_allometry terms based on allocation fractions + ! + ! !ARGUMENTS: + 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_state_type) , intent(inout) :: cnveg_state_inst + ! + ! !LOCAL VARIABLES: + integer :: p, fp, k + real(r8):: f1,f2,f3,f4,g1 ! allocation parameters + real(r8):: g1a ! g1 included in allocation/allometry + real(r8):: cnl,cnfr,cnlw,cndw ! C:N ratios for leaf, fine root, and wood + real(r8):: f5(nrepr) ! reproductive allocation parameters + real(r8):: cng ! C:N ratio for grain (= cnlw for now; slevis) + real(r8):: f5_tot ! sum of f5 terms + real(r8):: f5_n_tot ! sum of f5 terms converted from C to N + + character(len=*), parameter :: subname = 'calc_allometry' + !----------------------------------------------------------------------- + + associate( & + ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type + woody => pftcon%woody , & ! Input: binary flag for woody lifeform (1=woody, 0=not woody) + froot_leaf => pftcon%froot_leaf , & ! Input: allocation parameter: new fine root C per new leaf C (gC/gC) + 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) + 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) + graincn => pftcon%graincn , & ! Input: grain C:N (gC/gN) + grperc => pftcon%grperc , & ! Input: parameter used below + annsum_npp => cnveg_carbonflux_inst%annsum_npp_patch , & ! Input: [real(r8) (:) ] annual sum of NPP, for wood allocation + aleaf => cnveg_state_inst%aleaf_patch , & ! Input: [real(r8) (:) ] leaf allocation coefficient + astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient + aroot => cnveg_state_inst%aroot_patch , & ! Input: [real(r8) (:) ] root allocation coefficient + arepr => cnveg_state_inst%arepr_patch , & ! Input: [real(r8) (:,:) ] reproductive allocation coefficient(s) + c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) + n_allometry => cnveg_state_inst%n_allometry_patch & ! Output: [real(r8) (:) ] N allocation index (DIM) + ) + + do fp = 1 , num_soilp + p = filter_soilp(fp) + + f1 = froot_leaf(ivt(p)) + f2 = croot_stem(ivt(p)) + + ! modified wood allocation to be 2.2 at npp=800 gC/m2/yr, 0.2 at npp=0, + ! constrained so that it does not go lower than 0.2 (under negative annsum_npp) + ! This variable allocation is only for trees. Shrubs have a constant + ! allocation as specified in the pft-physiology file. The value is also used + ! as a trigger here: -1.0 means to use the dynamic allocation (trees). + + if (stem_leaf(ivt(p)) == -1._r8) then + f3 = (2.7_r8/(1.0_r8+exp(-0.004_r8*(annsum_npp(p) - 300.0_r8)))) - 0.4_r8 + else + f3 = stem_leaf(ivt(p)) + end if + + f4 = flivewd(ivt(p)) + if (ivt(p) >= npcropmin) then + g1 = 0.25_r8 + else + g1 = grperc(ivt(p)) + end if + cnl = leafcn(ivt(p)) + cnfr = frootcn(ivt(p)) + cnlw = livewdcn(ivt(p)) + cndw = deadwdcn(ivt(p)) + + ! based on available C, use constant allometric relationships to + ! determine N requirements + if (.not. use_fun) then + g1a = g1 + else + g1a = 0._r8 + end if + if (woody(ivt(p)) == 1.0_r8) then + c_allometry(p) = (1._r8+g1a)*(1._r8+f1+f3*(1._r8+f2)) + n_allometry(p) = 1._r8/cnl + f1/cnfr + (f3*f4*(1._r8+f2))/cnlw + & + (f3*(1._r8-f4)*(1._r8+f2))/cndw + else if (ivt(p) >= npcropmin) then ! skip generic crops + cng = graincn(ivt(p)) + f1 = aroot(p) / aleaf(p) + f3 = astem(p) / aleaf(p) + do k = 1, nrepr + f5(k) = arepr(p,k) / aleaf(p) + end do + f5_tot = 0._r8 + f5_n_tot = 0._r8 + do k = 1, nrepr + f5_tot = f5_tot + f5(k) + ! Note that currently we use the same C/N ratio for all grain components: + f5_n_tot = f5_n_tot + f5(k)/cng + end do + c_allometry(p) = (1._r8+g1a)*(1._r8+f1+f5_tot+f3*(1._r8+f2)) + n_allometry(p) = 1._r8/cnl + f1/cnfr + f5_n_tot + (f3*f4*(1._r8+f2))/cnlw + & + (f3*(1._r8-f4)*(1._r8+f2))/cndw + else + c_allometry(p) = 1._r8+g1a+f1+f1*g1a + n_allometry(p) = 1._r8/cnl + f1/cnfr + end if + + end do + + end associate + + end subroutine calc_allometry + + end module CNAllocationMod diff --git a/src/biogeochem/CNDriverMod.F90 b/src/biogeochem/CNDriverMod.F90 index 21a62b2b60..08158fca17 100644 --- a/src/biogeochem/CNDriverMod.F90 +++ b/src/biogeochem/CNDriverMod.F90 @@ -114,6 +114,7 @@ subroutine CNDriverNoLeaching(bounds, use subgridAveMod , only: p2c use CropType , only: crop_type use CNAllocationMod , only: calc_gpp_mr_availc, calc_crop_allocation_fractions + use CNAllocationMod , only: calc_allometry use CNNDynamicsMod , only: CNNDeposition,CNNFixation, CNNFert, CNSoyfix,CNFreeLivingFixation use CNMRespMod , only: CNMResp use CNFUNMod , only: CNFUNInit !, CNFUN @@ -395,6 +396,9 @@ subroutine CNDriverNoLeaching(bounds, call calc_crop_allocation_fractions(bounds, num_pcropp, filter_pcropp, & crop_inst, cnveg_state_inst) end if + + call calc_allometry(num_soilp, filter_soilp, & + cnveg_carbonflux_inst, cnveg_state_inst) call t_stopf('cnalloc') call t_startf('calc_plant_nutrient_demand') diff --git a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 index 10e601e8e8..ba29efbc08 100644 --- a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 +++ b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 @@ -255,7 +255,6 @@ subroutine calc_plant_cn_alloc (this, bounds, num_soilp, filter_soilp, & p = filter_soilp(fp) c = patch%column(p) - ! set some local allocation variables f1 = froot_leaf(ivt(p)) f2 = croot_stem(ivt(p)) @@ -527,17 +526,10 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst ! ! !LOCAL VARIABLES: - integer :: p,l,j,k ! indices + integer :: p,l,j ! indices integer :: fp ! lake filter patch index - real(r8):: f1,f2,f3,f4,g1,g2 ! allocation parameters - real(r8):: g1a ! g1 included in allocation/allometry - real(r8):: cnl,cnfr,cnlw,cndw ! C:N ratios for leaf, fine root, and wood - real(r8):: f5(nrepr) ! reproductive allocation parameters - real(r8):: cng ! C:N ratio for grain (= cnlw for now; slevis) real(r8):: t1 ! temporary variable real(r8):: dt ! model time step - real(r8):: f5_tot ! sum of f5 terms - real(r8):: f5_n_tot ! sum of f5 terms converted from C to N real(r8):: crop_phase(bounds%begp:bounds%endp) character(len=*), parameter :: subname = "calc_plant_nitrogen_demand" @@ -546,32 +538,21 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & associate( & ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type - woody => pftcon%woody , & ! Input: binary flag for woody lifeform (1=woody, 0=not woody) - froot_leaf => pftcon%froot_leaf , & ! Input: allocation parameter: new fine root C per new leaf C (gC/gC) - 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) - 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) + 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) 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 astemf => pftcon%astemf , & ! Input: parameter used below - grperc => pftcon%grperc , & ! Input: parameter used below - grpnow => pftcon%grpnow , & ! Input: parameter used below 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-deciduous leaf habit (0 or 1) croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] flag, true if planted, not harvested - aleaf => cnveg_state_inst%aleaf_patch , & ! Input: [real(r8) (:) ] leaf allocation coefficient astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient - aroot => cnveg_state_inst%aroot_patch , & ! Input: [real(r8) (:) ] root allocation coefficient - arepr => cnveg_state_inst%arepr_patch , & ! Input: [real(r8) (:,:) ] reproductive allocation coefficient(s) grain_flag => cnveg_state_inst%grain_flag_patch , & ! Output: [real(r8) (:) ] 1: grain fill stage; 0: not c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) n_allometry => cnveg_state_inst%n_allometry_patch , & ! Output: [real(r8) (:) ] N allocation index (DIM) @@ -586,7 +567,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & retransn => cnveg_nitrogenstate_inst%retransn_patch , & ! Input: [real(r8) (:) ] (gN/m2) plant pool of retranslocated N - annsum_npp => cnveg_carbonflux_inst%annsum_npp_patch , & ! Input: [real(r8) (:) ] annual sum of NPP, for wood allocation gpp => cnveg_carbonflux_inst%gpp_before_downreg_patch , & ! Output: [real(r8) (:) ] GPP flux before downregulation (gC/m2/s) availc => cnveg_carbonflux_inst%availc_patch , & ! Output: [real(r8) (:) ] C flux available for allocation (gC/m2/s) @@ -609,70 +589,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & do fp = 1,num_soilp p = filter_soilp(fp) - f1 = froot_leaf(ivt(p)) - f2 = croot_stem(ivt(p)) - - ! modified wood allocation to be 2.2 at npp=800 gC/m2/yr, 0.2 at npp=0, - ! constrained so that it does not go lower than 0.2 (under negative annsum_npp) - ! This variable allocation is only for trees. Shrubs have a constant - ! allocation as specified in the pft-physiologfy file. The value is also used - ! as a trigger here: -1.0 means to use the dynamic allocation (trees). - - if (stem_leaf(ivt(p)) == -1._r8) then - f3 = (2.7_r8/(1.0_r8+exp(-0.004_r8*(annsum_npp(p) - 300.0_r8)))) - 0.4_r8 - else - f3 = stem_leaf(ivt(p)) - end if - - f4 = flivewd(ivt(p)) - if (ivt(p) >= npcropmin) then - g1 = 0.25_r8 - else - g1 = grperc(ivt(p)) - end if - g2 = grpnow(ivt(p)) - cnl = leafcn(ivt(p)) - cnfr = frootcn(ivt(p)) - cnlw = livewdcn(ivt(p)) - cndw = deadwdcn(ivt(p)) - - ! based on available C, use constant allometric relationships to - ! determine N requirements - - !RF. I removed the growth respiration from this, because it is used to calculate - !plantCN for N uptake AND c_allometry for allocation. If we add gresp to the - !allometry calculation then we allocate too much carbon since gresp is not allocated here. - if (.not. use_fun) then - g1a = g1 - else - g1a = 0._r8 - end if - if (woody(ivt(p)) == 1.0_r8) then - c_allometry(p) = (1._r8+g1a)*(1._r8+f1+f3*(1._r8+f2)) - n_allometry(p) = 1._r8/cnl + f1/cnfr + (f3*f4*(1._r8+f2))/cnlw + & - (f3*(1._r8-f4)*(1._r8+f2))/cndw - else if (ivt(p) >= npcropmin) then ! skip generic crops - cng = graincn(ivt(p)) - f1 = aroot(p) / aleaf(p) - f3 = astem(p) / aleaf(p) - do k = 1, nrepr - f5(k) = arepr(p,k) / aleaf(p) - end do - f5_tot = 0._r8 - f5_n_tot = 0._r8 - do k = 1, nrepr - f5_tot = f5_tot + f5(k) - ! Note that currently we use the same C/N ratio for all grain components: - f5_n_tot = f5_n_tot + f5(k)/cng - end do - c_allometry(p) = (1._r8+g1a)*(1._r8+f1+f5_tot+f3*(1._r8+f2)) - n_allometry(p) = 1._r8/cnl + f1/cnfr + f5_n_tot + (f3*f4*(1._r8+f2))/cnlw + & - (f3*(1._r8-f4)*(1._r8+f2))/cndw - else - c_allometry(p) = 1._r8+g1a+f1+f1*g1a - n_allometry(p) = 1._r8/cnl + f1/cnfr - end if - plant_ndemand(p) = availc(p)*(n_allometry(p)/c_allometry(p)) ! retranslocated N deployment depends on seasonal cycle of potential GPP diff --git a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 index 1c1287130c..4c573926f6 100644 --- a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 +++ b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 @@ -992,17 +992,10 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & type(energyflux_type) , intent(in) :: energyflux_inst ! ! !LOCAL VARIABLES: - integer :: c, p, j, k ! indices + integer :: c, p, j ! indices integer :: fp ! lake filter patch index - real(r8) :: f1, f2, f3, f4, g1, g2 ! allocation parameters - real(r8) :: g1a ! g1 included in allocation/allometry - real(r8) :: cnl, cnfr, cnlw, cndw ! C:N ratios for leaf, fine root, and wood - real(r8) :: f5(nrepr) ! reproductive allocation parameters - real(r8) :: cng ! C:N ratio for grain (= cnlw for now; slevis) real(r8) :: t1 ! temporary variable real(r8) :: dt ! model time step - real(r8) :: f5_tot ! sum of f5 terms - real(r8) :: f5_n_tot ! sum of f5 terms converted from C to N real(r8) :: f_N (bounds%begp:bounds%endp) real(r8) :: Kmin real(r8) :: leafcn_max @@ -1026,22 +1019,11 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & associate( & ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type - woody => pftcon%woody , & ! Input: binary flag for woody lifeform (1=woody, 0=not woody) - froot_leaf => pftcon%froot_leaf , & ! Input: allocation parameter: new fine root C per new leaf C (gC/gC) - 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) - 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) - graincn => pftcon%graincn , & ! Input: grain 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 astemf => pftcon%astemf , & ! Input: parameter used below - grperc => pftcon%grperc , & ! Input: parameter used below - grpnow => pftcon%grpnow , & ! Input: parameter used below 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-deciduous leaf habit (0 or 1) @@ -1050,10 +1032,7 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] flag, true if planted, not harvested - aleaf => cnveg_state_inst%aleaf_patch , & ! Input: [real(r8) (:) ] leaf allocation coefficient astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient - aroot => cnveg_state_inst%aroot_patch , & ! Input: [real(r8) (:) ] root allocation coefficient - arepr => cnveg_state_inst%arepr_patch , & ! Input: [real(r8) (:,:) ] reproductive allocation coefficient(s) grain_flag => cnveg_state_inst%grain_flag_patch , & ! Output: [real(r8) (:) ] 1: grain fill stage; 0: not c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) n_allometry => cnveg_state_inst%n_allometry_patch , & ! Output: [real(r8) (:) ] N allocation index (DIM) @@ -1068,7 +1047,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & livecrootc => cnveg_carbonstate_inst%livecrootc_patch , & ! Input: [real(r8) (:) ] retransn => cnveg_nitrogenstate_inst%retransn_patch , & ! Input: [real(r8) (:) ] (gN/m2) plant pool of retranslocated N - annsum_npp => cnveg_carbonflux_inst%annsum_npp_patch , & ! Input: [real(r8) (:) ] annual sum of NPP, for wood allocation gpp => cnveg_carbonflux_inst%gpp_before_downreg_patch , & ! Output: [real(r8) (:) ] GPP flux before downregulation (gC/m2/s) availc => cnveg_carbonflux_inst%availc_patch , & ! Output: [real(r8) (:) ] C flux available for allocation (gC/m2/s) @@ -1098,66 +1076,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & do fp = 1,num_soilp p = filter_soilp(fp) - f1 = froot_leaf(ivt(p)) - f2 = croot_stem(ivt(p)) - - ! modified wood allocation to be 2.2 at npp=800 gC/m2/yr, 0.2 at npp=0, - ! constrained so that it does not go lower than 0.2 (under negative annsum_npp) - ! This variable allocation is only for trees. Shrubs have a constant - ! allocation as specified in the pft-physiology file. The value is also used - ! as a trigger here: -1.0 means to use the dynamic allocation (trees). - - if (stem_leaf(ivt(p)) == -1._r8) then - f3 = (2.7_r8/(1.0_r8+exp(-0.004_r8*(annsum_npp(p) - 300.0_r8)))) - 0.4_r8 - else - f3 = stem_leaf(ivt(p)) - end if - - f4 = flivewd(ivt(p)) - if (ivt(p) >= npcropmin) then - g1 = 0.25_r8 - else - g1 = grperc(ivt(p)) - end if - g2 = grpnow(ivt(p)) - cnl = leafcn(ivt(p)) - cnfr = frootcn(ivt(p)) - cnlw = livewdcn(ivt(p)) - cndw = deadwdcn(ivt(p)) - - ! based on available C, use constant allometric relationships to - ! determine N requirements - if (.not. use_fun) then - g1a = g1 - else - g1a = 0._r8 - end if - if (woody(ivt(p)) == 1.0_r8) then - c_allometry(p) = (1._r8+g1a)*(1._r8+f1+f3*(1._r8+f2)) - n_allometry(p) = 1._r8/cnl + f1/cnfr + (f3*f4*(1._r8+f2))/cnlw + & - (f3*(1._r8-f4)*(1._r8+f2))/cndw - else if (ivt(p) >= npcropmin) then ! skip generic crops - cng = graincn(ivt(p)) - f1 = aroot(p) / aleaf(p) - f3 = astem(p) / aleaf(p) - do k = 1, nrepr - f5(k) = arepr(p,k) / aleaf(p) - end do - f5_tot = 0._r8 - f5_n_tot = 0._r8 - do k = 1, nrepr - f5_tot = f5_tot + f5(k) - ! Note that currently we use the same C/N ratio for all grain components: - f5_n_tot = f5_n_tot + f5(k)/cng - end do - c_allometry(p) = (1._r8+g1a)*(1._r8+f1+f5_tot+f3*(1._r8+f2)) - n_allometry(p) = 1._r8/cnl + f1/cnfr + f5_n_tot + (f3*f4*(1._r8+f2))/cnlw + & - (f3*(1._r8-f4)*(1._r8+f2))/cndw - else - c_allometry(p) = 1._r8+g1a+f1+f1*g1a - n_allometry(p) = 1._r8/cnl + f1/cnfr - end if - ! when we have "if (leafn(p) == 0.0_r8)" below then we ! have floating overflow (out of floating point range) ! error in "actual_leafcn(p) = leafc(p) / leafn(p)" From 51241e8c3e5236ba0c0cb6adde78398ae4cd8dd2 Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Wed, 6 Apr 2022 12:55:11 -0600 Subject: [PATCH 10/17] Allow time-varying C:N ratios AgSys will specify time-varying C:N ratios for each plant component. This commit provides placeholders for these, and changes the allocation and nutrient competition code to use these potentially time-varying C:N ratios rather than the constant parameters. I will probably change some other uses of the C:N parameters as well, but plan to do that in a follow-up commit. --- src/biogeochem/CNAllocationMod.F90 | 26 +++++------- src/biogeochem/CNVegStateType.F90 | 38 ++++++++++++++++- .../NutrientCompetitionCLM45defaultMod.F90 | 37 ++++++++--------- .../NutrientCompetitionFlexibleCNMod.F90 | 41 ++++++++++--------- 4 files changed, 85 insertions(+), 57 deletions(-) diff --git a/src/biogeochem/CNAllocationMod.F90 b/src/biogeochem/CNAllocationMod.F90 index 9241e9af0c..067821e330 100644 --- a/src/biogeochem/CNAllocationMod.F90 +++ b/src/biogeochem/CNAllocationMod.F90 @@ -441,7 +441,6 @@ subroutine calc_allometry(num_soilp, filter_soilp, & real(r8):: g1a ! g1 included in allocation/allometry real(r8):: cnl,cnfr,cnlw,cndw ! C:N ratios for leaf, fine root, and wood real(r8):: f5(nrepr) ! reproductive allocation parameters - real(r8):: cng ! C:N ratio for grain (= cnlw for now; slevis) real(r8):: f5_tot ! sum of f5 terms real(r8):: f5_n_tot ! sum of f5 terms converted from C to N @@ -455,22 +454,22 @@ 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) - 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) - graincn => pftcon%graincn , & ! Input: grain C:N (gC/gN) grperc => pftcon%grperc , & ! Input: parameter used below annsum_npp => cnveg_carbonflux_inst%annsum_npp_patch , & ! Input: [real(r8) (:) ] annual sum of NPP, for wood allocation aleaf => cnveg_state_inst%aleaf_patch , & ! Input: [real(r8) (:) ] leaf allocation coefficient astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient aroot => cnveg_state_inst%aroot_patch , & ! Input: [real(r8) (:) ] root allocation coefficient arepr => cnveg_state_inst%arepr_patch , & ! Input: [real(r8) (:,:) ] reproductive allocation coefficient(s) + leafcn => cnveg_state_inst%leafcn_patch , & ! Input: [real(r8) (:) ] current leaf C:N ratio + frootcn => cnveg_state_inst%frootcn_patch , & ! Input: [real(r8) (:) ] current fine root C:N ratio + livewdcn => cnveg_state_inst%livewdcn_patch , & ! Input: [real(r8) (:) ] current live wood C:N ratio + deadwdcn => cnveg_state_inst%deadwdcn_patch , & ! Input: [real(r8) (:) ] current dead wood C:N ratio + reproductivecn => cnveg_state_inst%reproductivecn_patch,& ! Input: [real(r8) (:,:) ] current crop reproductive (e.g., grain) C:N ratios c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) n_allometry => cnveg_state_inst%n_allometry_patch & ! Output: [real(r8) (:) ] N allocation index (DIM) ) - do fp = 1 , num_soilp + do fp = 1, num_soilp p = filter_soilp(fp) f1 = froot_leaf(ivt(p)) @@ -494,10 +493,10 @@ subroutine calc_allometry(num_soilp, filter_soilp, & else g1 = grperc(ivt(p)) end if - cnl = leafcn(ivt(p)) - cnfr = frootcn(ivt(p)) - cnlw = livewdcn(ivt(p)) - cndw = deadwdcn(ivt(p)) + cnl = leafcn(p) + cnfr = frootcn(p) + cnlw = livewdcn(p) + cndw = deadwdcn(p) ! based on available C, use constant allometric relationships to ! determine N requirements @@ -511,7 +510,6 @@ subroutine calc_allometry(num_soilp, filter_soilp, & n_allometry(p) = 1._r8/cnl + f1/cnfr + (f3*f4*(1._r8+f2))/cnlw + & (f3*(1._r8-f4)*(1._r8+f2))/cndw else if (ivt(p) >= npcropmin) then ! skip generic crops - cng = graincn(ivt(p)) f1 = aroot(p) / aleaf(p) f3 = astem(p) / aleaf(p) do k = 1, nrepr @@ -521,8 +519,7 @@ subroutine calc_allometry(num_soilp, filter_soilp, & f5_n_tot = 0._r8 do k = 1, nrepr f5_tot = f5_tot + f5(k) - ! Note that currently we use the same C/N ratio for all grain components: - f5_n_tot = f5_n_tot + f5(k)/cng + f5_n_tot = f5_n_tot + f5(k)/reproductivecn(p,k) end do c_allometry(p) = (1._r8+g1a)*(1._r8+f1+f5_tot+f3*(1._r8+f2)) n_allometry(p) = 1._r8/cnl + f1/cnfr + f5_n_tot + (f3*f4*(1._r8+f2))/cnlw + & @@ -538,5 +535,4 @@ subroutine calc_allometry(num_soilp, filter_soilp, & end subroutine calc_allometry - end module CNAllocationMod diff --git a/src/biogeochem/CNVegStateType.F90 b/src/biogeochem/CNVegStateType.F90 index e39a334ab8..3b665b5927 100644 --- a/src/biogeochem/CNVegStateType.F90 +++ b/src/biogeochem/CNVegStateType.F90 @@ -7,12 +7,13 @@ module CNVegStateType use abortutils , only : endrun use spmdMod , only : masterproc use clm_varpar , only : nlevsno, nlevgrnd, nlevlak, nlevsoi - use clm_varctl , only : use_cn, iulog, fsurdat, use_crop, use_cndv + use clm_varctl , only : use_cn, iulog, fsurdat, use_crop, use_cndv, use_crop_agsys use clm_varcon , only : spval, ispval, grlnd use landunit_varcon, only : istsoil, istcrop use LandunitType , only : lun use ColumnType , only : col use PatchType , only : patch + use pftconMod , only : pftcon, npcropmin use AnnualFluxDribbler, only : annual_flux_dribbler_type, annual_flux_dribbler_patch use dynSubgridControlMod, only : get_for_testing_allow_non_annual_changes use CropReprPoolsMod, only : nrepr @@ -99,6 +100,11 @@ module CNVegStateType real(r8), pointer :: tempmax_retransn_patch (:) ! patch temporary annual max of retranslocated N pool (gN/m2) real(r8), pointer :: annmax_retransn_patch (:) ! patch annual max of retranslocated N pool (gN/m2) real(r8), pointer :: downreg_patch (:) ! patch fractional reduction in GPP due to N limitation (DIM) + real(r8), pointer :: leafcn_patch (:) ! patch current leaf C:N ratio + real(r8), pointer :: frootcn_patch (:) ! patch current fine root C:N ratio + real(r8), pointer :: livewdcn_patch (:) ! patch current live wood C:N ratio + real(r8), pointer :: deadwdcn_patch (:) ! patch current dead wood C:N ratio + real(r8), pointer :: reproductivecn_patch (:,:) ! patch crop reproductive (e.g., grain) C:N ratios real(r8), pointer :: leafcn_offset_patch (:) ! patch leaf C:N used by FUN real(r8), pointer :: plantCN_patch (:) ! patch plant C:N used by FUN @@ -260,6 +266,11 @@ subroutine InitAllocate(this, bounds) allocate(this%tempmax_retransn_patch (begp:endp)) ; this%tempmax_retransn_patch (:) = nan allocate(this%annmax_retransn_patch (begp:endp)) ; this%annmax_retransn_patch (:) = nan allocate(this%downreg_patch (begp:endp)) ; this%downreg_patch (:) = nan + allocate(this%leafcn_patch (begp:endp)) ; this%leafcn_patch (:) = nan + allocate(this%frootcn_patch (begp:endp)) ; this%frootcn_patch (:) = nan + allocate(this%livewdcn_patch (begp:endp)) ; this%livewdcn_patch (:) = nan + allocate(this%deadwdcn_patch (begp:endp)) ; this%deadwdcn_patch (:) = nan + allocate(this%reproductivecn_patch (begp:endp, nrepr)) ; this%reproductivecn_patch (:,:) = nan allocate(this%leafcn_offset_patch (begp:endp)) ; this%leafcn_offset_patch (:) = nan allocate(this%plantCN_patch (begp:endp)) ; this%plantCN_patch (:) = nan @@ -476,6 +487,10 @@ subroutine initCold(this, bounds) integer :: g,l,c,p ! dices !----------------------------------------------------------------------- + associate( & + ivt => patch%itype & + ) + ! -------------------------------------------------------------------- ! Initialize terms needed for dust model ! TODO - move these terms to DUSTMod module variables @@ -576,6 +591,25 @@ subroutine initCold(this, bounds) this%plantCN_patch(p) = spval end if + if (lun%itype(l) == istsoil .or. & + (lun%itype(l) == istcrop .and. .not. use_crop_agsys)) then + ! When using AgSys these variables will be set dynamically. Otherwise, use the + ! parameters in pftcon + ! + ! NOTE(wjs, 2022-04-06) It may be that we still want to initialize these as + ! below even when running with AgSys. But until we figure this out, I'm keeping + ! this NOT done with AgSys as a reminder that we need to determine how we'll do + ! this. + this%leafcn_patch(p) = pftcon%leafcn(ivt(p)) + this%frootcn_patch(p) = pftcon%frootcn(ivt(p)) + this%livewdcn_patch(p) = pftcon%livewdcn(ivt(p)) + this%deadwdcn_patch(p) = pftcon%deadwdcn(ivt(p)) + if (ivt(p) >= npcropmin) then + ! Note that we use the same grain c:n ratio for all reproductive components + this%reproductivecn_patch(p,:) = pftcon%graincn(ivt(p)) + end if + end if + end do ! fire variables @@ -584,6 +618,7 @@ subroutine initCold(this, bounds) this%lfc2_col(c) = 0._r8 end do + end associate end subroutine initCold !------------------------------------------------------------------------ @@ -598,7 +633,6 @@ subroutine Restart(this, bounds, ncid, flag, cnveg_carbonstate, & use CNVegCarbonStateType , only: cnveg_carbonstate_type use restUtilMod use ncdio_pio - use pftconMod , only : pftcon ! ! !ARGUMENTS: class(cnveg_state_type) :: this diff --git a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 index ba29efbc08..f1993f982f 100644 --- a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 +++ b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 @@ -162,7 +162,6 @@ subroutine calc_plant_cn_alloc (this, bounds, num_soilp, filter_soilp, & real(r8):: gresp_storage ! temporary variable for growth resp to storage real(r8):: nlc ! temporary variable for total new leaf carbon allocation real(r8):: f5(nrepr) ! reproductive allocation parameters - real(r8):: cng ! C:N ratio for grain (= cnlw for now; slevis) real(r8):: fsmn(bounds%begp:bounds%endp) ! A emperate variable for adjusting FUN uptakes !----------------------------------------------------------------------- @@ -178,12 +177,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) - 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) fcur2 => pftcon%fcur , & ! Input: allocation parameter: fraction of allocation that goes to currently displayed growth, remainder to storage - graincn => pftcon%graincn , & ! Input: grain C:N (gC/gN) grperc => pftcon%grperc , & ! Input: growth respiration parameter grpnow => pftcon%grpnow , & ! Input: growth respiration parameter @@ -194,6 +188,11 @@ subroutine calc_plant_cn_alloc (this, bounds, num_soilp, filter_soilp, & astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient aroot => cnveg_state_inst%aroot_patch , & ! Input: [real(r8) (:) ] root allocation coefficient arepr => cnveg_state_inst%arepr_patch , & ! Input: [real(r8) (:,:) ] reproductive allocation coefficient(s) + leafcn => cnveg_state_inst%leafcn_patch , & ! Input: [real(r8) (:) ] current leaf C:N ratio + frootcn => cnveg_state_inst%frootcn_patch , & ! Input: [real(r8) (:) ] current fine root C:N ratio + livewdcn => cnveg_state_inst%livewdcn_patch , & ! Input: [real(r8) (:) ] current live wood C:N ratio + deadwdcn => cnveg_state_inst%deadwdcn_patch , & ! Input: [real(r8) (:) ] current dead wood C:N ratio + reproductivecn => cnveg_state_inst%reproductivecn_patch , & ! Input: [real(r8) (:,:) ] current crop reproductive (e.g., grain) C:N ratios c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) n_allometry => cnveg_state_inst%n_allometry_patch , & ! Output: [real(r8) (:) ] N allocation index (DIM) downreg => cnveg_state_inst%downreg_patch , & ! Output: [real(r8) (:) ] fractional reduction in GPP due to N limitation (DIM) @@ -275,10 +274,10 @@ 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)) - cnfr = frootcn(ivt(p)) - cnlw = livewdcn(ivt(p)) - cndw = deadwdcn(ivt(p)) + cnl = leafcn(p) + cnfr = frootcn(p) + cnlw = livewdcn(p) + cndw = deadwdcn(p) fcur = fcur2(ivt(p)) if (ivt(p) >= npcropmin) then ! skip 2 generic crops @@ -389,7 +388,6 @@ subroutine calc_plant_cn_alloc (this, bounds, num_soilp, filter_soilp, & npool_to_deadcrootn_storage(p) = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * (1._r8 - fcur) end if if (ivt(p) >= npcropmin) then ! skip 2 generic crops - cng = graincn(ivt(p)) npool_to_livestemn(p) = (nlc * f3 * f4 / cnlw) * fcur npool_to_livestemn_storage(p) = (nlc * f3 * f4 / cnlw) * (1._r8 - fcur) npool_to_deadstemn(p) = (nlc * f3 * (1._r8 - f4) / cndw) * fcur @@ -399,8 +397,8 @@ subroutine calc_plant_cn_alloc (this, bounds, num_soilp, filter_soilp, & npool_to_deadcrootn(p) = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * fcur npool_to_deadcrootn_storage(p) = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * (1._r8 - fcur) do k = 1, nrepr - npool_to_reproductiven(p,k) = (nlc * f5(k) / cng) * fcur - npool_to_reproductiven_storage(p,k) = (nlc * f5(k) / cng) * (1._r8 -fcur) + npool_to_reproductiven(p,k) = (nlc * f5(k) / reproductivecn(p,k)) * fcur + npool_to_reproductiven_storage(p,k) = (nlc * f5(k) / reproductivecn(p,k)) * (1._r8 -fcur) end do end if @@ -538,10 +536,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & associate( & ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type - 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) - graincn => pftcon%graincn , & ! Input: grain 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 @@ -553,6 +547,9 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] flag, true if planted, not harvested astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient + leafcn => cnveg_state_inst%leafcn_patch , & ! Input: [real(r8) (:) ] current leaf C:N ratio + frootcn => cnveg_state_inst%frootcn_patch , & ! Input: [real(r8) (:) ] current fine root C:N ratio + livewdcn => cnveg_state_inst%livewdcn_patch , & ! Input: [real(r8) (:) ] current live wood C:N ratio grain_flag => cnveg_state_inst%grain_flag_patch , & ! Output: [real(r8) (:) ] 1: grain fill stage; 0: not c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) n_allometry => cnveg_state_inst%n_allometry_patch , & ! Output: [real(r8) (:) ] N allocation index (DIM) @@ -628,13 +625,13 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & 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(p)) - (leafc(p) / & fleafcn(ivt(p)))) - livestemn_to_retransn(p) = t1 * ((livestemc(p) / livewdcn(ivt(p))) - (livestemc(p) / & + livestemn_to_retransn(p) = t1 * ((livestemc(p) / livewdcn(p)) - (livestemc(p) / & fstemcn(ivt(p)))) frootn_to_retransn(p) = 0._r8 if (ffrootcn(ivt(p)) > 0._r8) then - frootn_to_retransn(p) = t1 * ((frootc(p) / frootcn(ivt(p))) - (frootc(p) / & + frootn_to_retransn(p) = t1 * ((frootc(p) / frootcn(p)) - (frootc(p) / & ffrootcn(ivt(p)))) end if else !leafn retrans flux is handled in phenology diff --git a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 index 4c573926f6..e0f7a51d93 100644 --- a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 +++ b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 @@ -230,7 +230,6 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & real(r8) :: gresp_storage ! temporary variable for growth resp to storage real(r8) :: nlc ! temporary variable for total new leaf carbon allocation real(r8) :: f5(nrepr) ! reproductive allocation parameters - real(r8) :: cng ! C:N ratio for grain (= cnlw for now; slevis) real(r8) :: dt ! model time step real(r8):: fsmn(bounds%begp:bounds%endp) ! A emperate variable for adjusting FUN uptakes @@ -292,12 +291,10 @@ 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) - 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) + leafcn_const => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) + frootcn_const => pftcon%frootcn , & ! Input: fine root C:N (gC/gN) + livewdcn_const => 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 - graincn => pftcon%graincn , & ! Input: grain C:N (gC/gN) grperc => pftcon%grperc , & ! Input: growth respiration parameter grpnow => pftcon%grpnow , & ! Input: growth respiration parameter evergreen => pftcon%evergreen , & ! Input: binary flag for evergreen leaf habit (0 or 1) @@ -309,6 +306,11 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient aroot => cnveg_state_inst%aroot_patch , & ! Input: [real(r8) (:) ] root allocation coefficient arepr => cnveg_state_inst%arepr_patch , & ! Input: [real(r8) (:,:) ] reproductive allocation coefficient(s) + leafcn => cnveg_state_inst%leafcn_patch , & ! Input: [real(r8) (:) ] current leaf C:N ratio + frootcn => cnveg_state_inst%frootcn_patch , & ! Input: [real(r8) (:) ] current fine root C:N ratio + livewdcn => cnveg_state_inst%livewdcn_patch , & ! Input: [real(r8) (:) ] current live wood C:N ratio + deadwdcn => cnveg_state_inst%deadwdcn_patch , & ! Input: [real(r8) (:) ] current dead wood C:N ratio + reproductivecn => cnveg_state_inst%reproductivecn_patch , & ! Input: [real(r8) (:,:) ] current crop reproductive (e.g., grain) C:N ratios c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) annsum_npp => cnveg_carbonflux_inst%annsum_npp_patch , & ! Input: [real(r8) (:) ] annual sum of NPP, for wood allocation @@ -404,10 +406,10 @@ 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)) - cnfr = frootcn(ivt(p)) - cnlw = livewdcn(ivt(p)) - cndw = deadwdcn(ivt(p)) + cnl = leafcn(p) + cnfr = frootcn(p) + cnlw = livewdcn(p) + cndw = deadwdcn(p) fcur = fcur2(ivt(p)) if (evergreen(ivt(p)) == 1._r8) then @@ -537,7 +539,6 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & end if if (ivt(p) >= npcropmin) then ! skip 2 generic crops - cng = graincn(ivt(p)) npool_to_livestemn_demand(p) = (nlc * f3 * f4 / cnlw) * fcur npool_to_livestemn_storage_demand(p) = (nlc * f3 * f4 / cnlw) * (1._r8 - fcur) npool_to_deadstemn_demand(p) = (nlc * f3 * (1._r8 - f4) / cndw) * fcur @@ -547,8 +548,8 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & npool_to_deadcrootn_demand(p) = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * fcur npool_to_deadcrootn_storage_demand(p) = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * (1._r8 - fcur) do k = 1, nrepr - npool_to_reproductiven_demand(p,k) = (nlc * f5(k) / cng) * fcur - npool_to_reproductiven_storage_demand(p,k) = (nlc * f5(k) / cng) * (1._r8 -fcur) + npool_to_reproductiven_demand(p,k) = (nlc * f5(k) / reproductivecn(p,k)) * fcur + npool_to_reproductiven_storage_demand(p,k) = (nlc * f5(k) / reproductivecn(p,k)) * (1._r8 -fcur) end do end if @@ -770,8 +771,8 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & end if end if - leafcn_max = leafcn(ivt(p)) + 15.0_r8 - frootcn_max = frootcn(ivt(p)) + 15.0_r8 + leafcn_max = leafcn_const(ivt(p)) + 15.0_r8 + frootcn_max = frootcn_const(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 ! if high leaf CN ratio (i.e., high leaf C compared to N) then turnover extra C @@ -804,7 +805,7 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & if (woody(ivt(p)) == 1._r8) then - livewdcn_max = livewdcn(ivt(p)) + 15.0_r8 + livewdcn_max = livewdcn_const(ivt(p)) + 15.0_r8 ! if high coarse root CN ratio (i.e., high coarse root C compared to N) then turnover extra C if (livecrootcn_actual > livewdcn_max) then @@ -838,7 +839,7 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & if (ivt(p) >= npcropmin) then ! skip 2 generic crops - livewdcn_max = livewdcn(ivt(p)) + 15.0_r8 + livewdcn_max = livewdcn_const(ivt(p)) + 15.0_r8 ! if high coarse root CN ratio (i.e., high coarse root C compared to N) then turnover extra C if (livecrootcn_actual > livewdcn_max) then @@ -1019,7 +1020,7 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & associate( & ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type - leafcn => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) + leafcn_const => pftcon%leafcn , & ! 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 @@ -1088,8 +1089,8 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & end if - leafcn_min = leafcn(ivt(p)) - 10.0_r8 - leafcn_max = leafcn(ivt(p)) + 10.0_r8 + leafcn_min = leafcn_const(ivt(p)) - 10.0_r8 + leafcn_max = leafcn_const(ivt(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 ) From 08727ed0bc34d21b76be08384b947f16d91b5041 Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Tue, 12 Apr 2022 11:43:31 -0600 Subject: [PATCH 11/17] Revert "Allow time-varying C:N ratios" This reverts commit 51241e8c3e5236ba0c0cb6adde78398ae4cd8dd2. After further discussion, it isn't clear if we really want to get time-varying C:N ratios from AgSys, and if we do, it isn't clear what those should be and in which places we should use the time-varying vs. time-constant C:N ratios. So for now we're sticking with the use of pft-constant C:N ratios; we may revisit this later. --- src/biogeochem/CNAllocationMod.F90 | 26 +++++++----- src/biogeochem/CNVegStateType.F90 | 38 +---------------- .../NutrientCompetitionCLM45defaultMod.F90 | 37 +++++++++-------- .../NutrientCompetitionFlexibleCNMod.F90 | 41 +++++++++---------- 4 files changed, 57 insertions(+), 85 deletions(-) diff --git a/src/biogeochem/CNAllocationMod.F90 b/src/biogeochem/CNAllocationMod.F90 index 067821e330..9241e9af0c 100644 --- a/src/biogeochem/CNAllocationMod.F90 +++ b/src/biogeochem/CNAllocationMod.F90 @@ -441,6 +441,7 @@ subroutine calc_allometry(num_soilp, filter_soilp, & real(r8):: g1a ! g1 included in allocation/allometry real(r8):: cnl,cnfr,cnlw,cndw ! C:N ratios for leaf, fine root, and wood real(r8):: f5(nrepr) ! reproductive allocation parameters + real(r8):: cng ! C:N ratio for grain (= cnlw for now; slevis) real(r8):: f5_tot ! sum of f5 terms real(r8):: f5_n_tot ! sum of f5 terms converted from C to N @@ -454,22 +455,22 @@ 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) + 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) + graincn => pftcon%graincn , & ! Input: grain C:N (gC/gN) grperc => pftcon%grperc , & ! Input: parameter used below annsum_npp => cnveg_carbonflux_inst%annsum_npp_patch , & ! Input: [real(r8) (:) ] annual sum of NPP, for wood allocation aleaf => cnveg_state_inst%aleaf_patch , & ! Input: [real(r8) (:) ] leaf allocation coefficient astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient aroot => cnveg_state_inst%aroot_patch , & ! Input: [real(r8) (:) ] root allocation coefficient arepr => cnveg_state_inst%arepr_patch , & ! Input: [real(r8) (:,:) ] reproductive allocation coefficient(s) - leafcn => cnveg_state_inst%leafcn_patch , & ! Input: [real(r8) (:) ] current leaf C:N ratio - frootcn => cnveg_state_inst%frootcn_patch , & ! Input: [real(r8) (:) ] current fine root C:N ratio - livewdcn => cnveg_state_inst%livewdcn_patch , & ! Input: [real(r8) (:) ] current live wood C:N ratio - deadwdcn => cnveg_state_inst%deadwdcn_patch , & ! Input: [real(r8) (:) ] current dead wood C:N ratio - reproductivecn => cnveg_state_inst%reproductivecn_patch,& ! Input: [real(r8) (:,:) ] current crop reproductive (e.g., grain) C:N ratios c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) n_allometry => cnveg_state_inst%n_allometry_patch & ! Output: [real(r8) (:) ] N allocation index (DIM) ) - do fp = 1, num_soilp + do fp = 1 , num_soilp p = filter_soilp(fp) f1 = froot_leaf(ivt(p)) @@ -493,10 +494,10 @@ subroutine calc_allometry(num_soilp, filter_soilp, & else g1 = grperc(ivt(p)) end if - cnl = leafcn(p) - cnfr = frootcn(p) - cnlw = livewdcn(p) - cndw = deadwdcn(p) + cnl = leafcn(ivt(p)) + cnfr = frootcn(ivt(p)) + cnlw = livewdcn(ivt(p)) + cndw = deadwdcn(ivt(p)) ! based on available C, use constant allometric relationships to ! determine N requirements @@ -510,6 +511,7 @@ subroutine calc_allometry(num_soilp, filter_soilp, & n_allometry(p) = 1._r8/cnl + f1/cnfr + (f3*f4*(1._r8+f2))/cnlw + & (f3*(1._r8-f4)*(1._r8+f2))/cndw else if (ivt(p) >= npcropmin) then ! skip generic crops + cng = graincn(ivt(p)) f1 = aroot(p) / aleaf(p) f3 = astem(p) / aleaf(p) do k = 1, nrepr @@ -519,7 +521,8 @@ subroutine calc_allometry(num_soilp, filter_soilp, & f5_n_tot = 0._r8 do k = 1, nrepr f5_tot = f5_tot + f5(k) - f5_n_tot = f5_n_tot + f5(k)/reproductivecn(p,k) + ! Note that currently we use the same C/N ratio for all grain components: + f5_n_tot = f5_n_tot + f5(k)/cng end do c_allometry(p) = (1._r8+g1a)*(1._r8+f1+f5_tot+f3*(1._r8+f2)) n_allometry(p) = 1._r8/cnl + f1/cnfr + f5_n_tot + (f3*f4*(1._r8+f2))/cnlw + & @@ -535,4 +538,5 @@ subroutine calc_allometry(num_soilp, filter_soilp, & end subroutine calc_allometry + end module CNAllocationMod diff --git a/src/biogeochem/CNVegStateType.F90 b/src/biogeochem/CNVegStateType.F90 index 3b665b5927..e39a334ab8 100644 --- a/src/biogeochem/CNVegStateType.F90 +++ b/src/biogeochem/CNVegStateType.F90 @@ -7,13 +7,12 @@ module CNVegStateType use abortutils , only : endrun use spmdMod , only : masterproc use clm_varpar , only : nlevsno, nlevgrnd, nlevlak, nlevsoi - use clm_varctl , only : use_cn, iulog, fsurdat, use_crop, use_cndv, use_crop_agsys + use clm_varctl , only : use_cn, iulog, fsurdat, use_crop, use_cndv use clm_varcon , only : spval, ispval, grlnd use landunit_varcon, only : istsoil, istcrop use LandunitType , only : lun use ColumnType , only : col use PatchType , only : patch - use pftconMod , only : pftcon, npcropmin use AnnualFluxDribbler, only : annual_flux_dribbler_type, annual_flux_dribbler_patch use dynSubgridControlMod, only : get_for_testing_allow_non_annual_changes use CropReprPoolsMod, only : nrepr @@ -100,11 +99,6 @@ module CNVegStateType real(r8), pointer :: tempmax_retransn_patch (:) ! patch temporary annual max of retranslocated N pool (gN/m2) real(r8), pointer :: annmax_retransn_patch (:) ! patch annual max of retranslocated N pool (gN/m2) real(r8), pointer :: downreg_patch (:) ! patch fractional reduction in GPP due to N limitation (DIM) - real(r8), pointer :: leafcn_patch (:) ! patch current leaf C:N ratio - real(r8), pointer :: frootcn_patch (:) ! patch current fine root C:N ratio - real(r8), pointer :: livewdcn_patch (:) ! patch current live wood C:N ratio - real(r8), pointer :: deadwdcn_patch (:) ! patch current dead wood C:N ratio - real(r8), pointer :: reproductivecn_patch (:,:) ! patch crop reproductive (e.g., grain) C:N ratios real(r8), pointer :: leafcn_offset_patch (:) ! patch leaf C:N used by FUN real(r8), pointer :: plantCN_patch (:) ! patch plant C:N used by FUN @@ -266,11 +260,6 @@ subroutine InitAllocate(this, bounds) allocate(this%tempmax_retransn_patch (begp:endp)) ; this%tempmax_retransn_patch (:) = nan allocate(this%annmax_retransn_patch (begp:endp)) ; this%annmax_retransn_patch (:) = nan allocate(this%downreg_patch (begp:endp)) ; this%downreg_patch (:) = nan - allocate(this%leafcn_patch (begp:endp)) ; this%leafcn_patch (:) = nan - allocate(this%frootcn_patch (begp:endp)) ; this%frootcn_patch (:) = nan - allocate(this%livewdcn_patch (begp:endp)) ; this%livewdcn_patch (:) = nan - allocate(this%deadwdcn_patch (begp:endp)) ; this%deadwdcn_patch (:) = nan - allocate(this%reproductivecn_patch (begp:endp, nrepr)) ; this%reproductivecn_patch (:,:) = nan allocate(this%leafcn_offset_patch (begp:endp)) ; this%leafcn_offset_patch (:) = nan allocate(this%plantCN_patch (begp:endp)) ; this%plantCN_patch (:) = nan @@ -487,10 +476,6 @@ subroutine initCold(this, bounds) integer :: g,l,c,p ! dices !----------------------------------------------------------------------- - associate( & - ivt => patch%itype & - ) - ! -------------------------------------------------------------------- ! Initialize terms needed for dust model ! TODO - move these terms to DUSTMod module variables @@ -591,25 +576,6 @@ subroutine initCold(this, bounds) this%plantCN_patch(p) = spval end if - if (lun%itype(l) == istsoil .or. & - (lun%itype(l) == istcrop .and. .not. use_crop_agsys)) then - ! When using AgSys these variables will be set dynamically. Otherwise, use the - ! parameters in pftcon - ! - ! NOTE(wjs, 2022-04-06) It may be that we still want to initialize these as - ! below even when running with AgSys. But until we figure this out, I'm keeping - ! this NOT done with AgSys as a reminder that we need to determine how we'll do - ! this. - this%leafcn_patch(p) = pftcon%leafcn(ivt(p)) - this%frootcn_patch(p) = pftcon%frootcn(ivt(p)) - this%livewdcn_patch(p) = pftcon%livewdcn(ivt(p)) - this%deadwdcn_patch(p) = pftcon%deadwdcn(ivt(p)) - if (ivt(p) >= npcropmin) then - ! Note that we use the same grain c:n ratio for all reproductive components - this%reproductivecn_patch(p,:) = pftcon%graincn(ivt(p)) - end if - end if - end do ! fire variables @@ -618,7 +584,6 @@ subroutine initCold(this, bounds) this%lfc2_col(c) = 0._r8 end do - end associate end subroutine initCold !------------------------------------------------------------------------ @@ -633,6 +598,7 @@ subroutine Restart(this, bounds, ncid, flag, cnveg_carbonstate, & use CNVegCarbonStateType , only: cnveg_carbonstate_type use restUtilMod use ncdio_pio + use pftconMod , only : pftcon ! ! !ARGUMENTS: class(cnveg_state_type) :: this diff --git a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 index f1993f982f..ba29efbc08 100644 --- a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 +++ b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 @@ -162,6 +162,7 @@ subroutine calc_plant_cn_alloc (this, bounds, num_soilp, filter_soilp, & real(r8):: gresp_storage ! temporary variable for growth resp to storage real(r8):: nlc ! temporary variable for total new leaf carbon allocation real(r8):: f5(nrepr) ! reproductive allocation parameters + real(r8):: cng ! C:N ratio for grain (= cnlw for now; slevis) real(r8):: fsmn(bounds%begp:bounds%endp) ! A emperate variable for adjusting FUN uptakes !----------------------------------------------------------------------- @@ -177,7 +178,12 @@ 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) + 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) fcur2 => pftcon%fcur , & ! Input: allocation parameter: fraction of allocation that goes to currently displayed growth, remainder to storage + graincn => pftcon%graincn , & ! Input: grain C:N (gC/gN) grperc => pftcon%grperc , & ! Input: growth respiration parameter grpnow => pftcon%grpnow , & ! Input: growth respiration parameter @@ -188,11 +194,6 @@ subroutine calc_plant_cn_alloc (this, bounds, num_soilp, filter_soilp, & astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient aroot => cnveg_state_inst%aroot_patch , & ! Input: [real(r8) (:) ] root allocation coefficient arepr => cnveg_state_inst%arepr_patch , & ! Input: [real(r8) (:,:) ] reproductive allocation coefficient(s) - leafcn => cnveg_state_inst%leafcn_patch , & ! Input: [real(r8) (:) ] current leaf C:N ratio - frootcn => cnveg_state_inst%frootcn_patch , & ! Input: [real(r8) (:) ] current fine root C:N ratio - livewdcn => cnveg_state_inst%livewdcn_patch , & ! Input: [real(r8) (:) ] current live wood C:N ratio - deadwdcn => cnveg_state_inst%deadwdcn_patch , & ! Input: [real(r8) (:) ] current dead wood C:N ratio - reproductivecn => cnveg_state_inst%reproductivecn_patch , & ! Input: [real(r8) (:,:) ] current crop reproductive (e.g., grain) C:N ratios c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) n_allometry => cnveg_state_inst%n_allometry_patch , & ! Output: [real(r8) (:) ] N allocation index (DIM) downreg => cnveg_state_inst%downreg_patch , & ! Output: [real(r8) (:) ] fractional reduction in GPP due to N limitation (DIM) @@ -274,10 +275,10 @@ 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(p) - cnfr = frootcn(p) - cnlw = livewdcn(p) - cndw = deadwdcn(p) + cnl = leafcn(ivt(p)) + cnfr = frootcn(ivt(p)) + cnlw = livewdcn(ivt(p)) + cndw = deadwdcn(ivt(p)) fcur = fcur2(ivt(p)) if (ivt(p) >= npcropmin) then ! skip 2 generic crops @@ -388,6 +389,7 @@ subroutine calc_plant_cn_alloc (this, bounds, num_soilp, filter_soilp, & npool_to_deadcrootn_storage(p) = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * (1._r8 - fcur) end if if (ivt(p) >= npcropmin) then ! skip 2 generic crops + cng = graincn(ivt(p)) npool_to_livestemn(p) = (nlc * f3 * f4 / cnlw) * fcur npool_to_livestemn_storage(p) = (nlc * f3 * f4 / cnlw) * (1._r8 - fcur) npool_to_deadstemn(p) = (nlc * f3 * (1._r8 - f4) / cndw) * fcur @@ -397,8 +399,8 @@ subroutine calc_plant_cn_alloc (this, bounds, num_soilp, filter_soilp, & npool_to_deadcrootn(p) = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * fcur npool_to_deadcrootn_storage(p) = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * (1._r8 - fcur) do k = 1, nrepr - npool_to_reproductiven(p,k) = (nlc * f5(k) / reproductivecn(p,k)) * fcur - npool_to_reproductiven_storage(p,k) = (nlc * f5(k) / reproductivecn(p,k)) * (1._r8 -fcur) + npool_to_reproductiven(p,k) = (nlc * f5(k) / cng) * fcur + npool_to_reproductiven_storage(p,k) = (nlc * f5(k) / cng) * (1._r8 -fcur) end do end if @@ -536,6 +538,10 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & associate( & ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type + 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) + graincn => pftcon%graincn , & ! Input: grain 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 @@ -547,9 +553,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] flag, true if planted, not harvested astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient - leafcn => cnveg_state_inst%leafcn_patch , & ! Input: [real(r8) (:) ] current leaf C:N ratio - frootcn => cnveg_state_inst%frootcn_patch , & ! Input: [real(r8) (:) ] current fine root C:N ratio - livewdcn => cnveg_state_inst%livewdcn_patch , & ! Input: [real(r8) (:) ] current live wood C:N ratio grain_flag => cnveg_state_inst%grain_flag_patch , & ! Output: [real(r8) (:) ] 1: grain fill stage; 0: not c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) n_allometry => cnveg_state_inst%n_allometry_patch , & ! Output: [real(r8) (:) ] N allocation index (DIM) @@ -625,13 +628,13 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & if (grain_flag(p) == 0._r8)then if(.not.use_fun) then t1 = 1 / dt - leafn_to_retransn(p) = t1 * ((leafc(p) / leafcn(p)) - (leafc(p) / & + leafn_to_retransn(p) = t1 * ((leafc(p) / leafcn(ivt(p))) - (leafc(p) / & fleafcn(ivt(p)))) - livestemn_to_retransn(p) = t1 * ((livestemc(p) / livewdcn(p)) - (livestemc(p) / & + livestemn_to_retransn(p) = t1 * ((livestemc(p) / livewdcn(ivt(p))) - (livestemc(p) / & fstemcn(ivt(p)))) frootn_to_retransn(p) = 0._r8 if (ffrootcn(ivt(p)) > 0._r8) then - frootn_to_retransn(p) = t1 * ((frootc(p) / frootcn(p)) - (frootc(p) / & + frootn_to_retransn(p) = t1 * ((frootc(p) / frootcn(ivt(p))) - (frootc(p) / & ffrootcn(ivt(p)))) end if else !leafn retrans flux is handled in phenology diff --git a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 index e0f7a51d93..4c573926f6 100644 --- a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 +++ b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 @@ -230,6 +230,7 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & real(r8) :: gresp_storage ! temporary variable for growth resp to storage real(r8) :: nlc ! temporary variable for total new leaf carbon allocation real(r8) :: f5(nrepr) ! reproductive allocation parameters + real(r8) :: cng ! C:N ratio for grain (= cnlw for now; slevis) real(r8) :: dt ! model time step real(r8):: fsmn(bounds%begp:bounds%endp) ! A emperate variable for adjusting FUN uptakes @@ -291,10 +292,12 @@ 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_const => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) - frootcn_const => pftcon%frootcn , & ! Input: fine root C:N (gC/gN) - livewdcn_const => pftcon%livewdcn , & ! Input: live wood (phloem and ray parenchyma) C:N (gC/gN) + 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) fcur2 => pftcon%fcur , & ! Input: allocation parameter: fraction of allocation that goes to currently displayed growth, remainder to storage + graincn => pftcon%graincn , & ! Input: grain C:N (gC/gN) grperc => pftcon%grperc , & ! Input: growth respiration parameter grpnow => pftcon%grpnow , & ! Input: growth respiration parameter evergreen => pftcon%evergreen , & ! Input: binary flag for evergreen leaf habit (0 or 1) @@ -306,11 +309,6 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient aroot => cnveg_state_inst%aroot_patch , & ! Input: [real(r8) (:) ] root allocation coefficient arepr => cnveg_state_inst%arepr_patch , & ! Input: [real(r8) (:,:) ] reproductive allocation coefficient(s) - leafcn => cnveg_state_inst%leafcn_patch , & ! Input: [real(r8) (:) ] current leaf C:N ratio - frootcn => cnveg_state_inst%frootcn_patch , & ! Input: [real(r8) (:) ] current fine root C:N ratio - livewdcn => cnveg_state_inst%livewdcn_patch , & ! Input: [real(r8) (:) ] current live wood C:N ratio - deadwdcn => cnveg_state_inst%deadwdcn_patch , & ! Input: [real(r8) (:) ] current dead wood C:N ratio - reproductivecn => cnveg_state_inst%reproductivecn_patch , & ! Input: [real(r8) (:,:) ] current crop reproductive (e.g., grain) C:N ratios c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) annsum_npp => cnveg_carbonflux_inst%annsum_npp_patch , & ! Input: [real(r8) (:) ] annual sum of NPP, for wood allocation @@ -406,10 +404,10 @@ 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(p) - cnfr = frootcn(p) - cnlw = livewdcn(p) - cndw = deadwdcn(p) + cnl = leafcn(ivt(p)) + cnfr = frootcn(ivt(p)) + cnlw = livewdcn(ivt(p)) + cndw = deadwdcn(ivt(p)) fcur = fcur2(ivt(p)) if (evergreen(ivt(p)) == 1._r8) then @@ -539,6 +537,7 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & end if if (ivt(p) >= npcropmin) then ! skip 2 generic crops + cng = graincn(ivt(p)) npool_to_livestemn_demand(p) = (nlc * f3 * f4 / cnlw) * fcur npool_to_livestemn_storage_demand(p) = (nlc * f3 * f4 / cnlw) * (1._r8 - fcur) npool_to_deadstemn_demand(p) = (nlc * f3 * (1._r8 - f4) / cndw) * fcur @@ -548,8 +547,8 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & npool_to_deadcrootn_demand(p) = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * fcur npool_to_deadcrootn_storage_demand(p) = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * (1._r8 - fcur) do k = 1, nrepr - npool_to_reproductiven_demand(p,k) = (nlc * f5(k) / reproductivecn(p,k)) * fcur - npool_to_reproductiven_storage_demand(p,k) = (nlc * f5(k) / reproductivecn(p,k)) * (1._r8 -fcur) + npool_to_reproductiven_demand(p,k) = (nlc * f5(k) / cng) * fcur + npool_to_reproductiven_storage_demand(p,k) = (nlc * f5(k) / cng) * (1._r8 -fcur) end do end if @@ -771,8 +770,8 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & end if end if - leafcn_max = leafcn_const(ivt(p)) + 15.0_r8 - frootcn_max = frootcn_const(ivt(p)) + 15.0_r8 + leafcn_max = leafcn(ivt(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 ! if high leaf CN ratio (i.e., high leaf C compared to N) then turnover extra C @@ -805,7 +804,7 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & if (woody(ivt(p)) == 1._r8) then - livewdcn_max = livewdcn_const(ivt(p)) + 15.0_r8 + livewdcn_max = livewdcn(ivt(p)) + 15.0_r8 ! if high coarse root CN ratio (i.e., high coarse root C compared to N) then turnover extra C if (livecrootcn_actual > livewdcn_max) then @@ -839,7 +838,7 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & if (ivt(p) >= npcropmin) then ! skip 2 generic crops - livewdcn_max = livewdcn_const(ivt(p)) + 15.0_r8 + livewdcn_max = livewdcn(ivt(p)) + 15.0_r8 ! if high coarse root CN ratio (i.e., high coarse root C compared to N) then turnover extra C if (livecrootcn_actual > livewdcn_max) then @@ -1020,7 +1019,7 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & associate( & ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type - leafcn_const => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) + leafcn => pftcon%leafcn , & ! 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 @@ -1089,8 +1088,8 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & end if - leafcn_min = leafcn_const(ivt(p)) - 10.0_r8 - leafcn_max = leafcn_const(ivt(p)) + 10.0_r8 + leafcn_min = leafcn(ivt(p)) - 10.0_r8 + leafcn_max = leafcn(ivt(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 ) From f3ec663d5db1b4d931d8d12d98a3bc59ff69b0c5 Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Tue, 12 Apr 2022 11:45:26 -0600 Subject: [PATCH 12/17] Fix spacing --- src/biogeochem/CNAllocationMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biogeochem/CNAllocationMod.F90 b/src/biogeochem/CNAllocationMod.F90 index 9241e9af0c..ef646511f1 100644 --- a/src/biogeochem/CNAllocationMod.F90 +++ b/src/biogeochem/CNAllocationMod.F90 @@ -470,7 +470,7 @@ subroutine calc_allometry(num_soilp, filter_soilp, & n_allometry => cnveg_state_inst%n_allometry_patch & ! Output: [real(r8) (:) ] N allocation index (DIM) ) - do fp = 1 , num_soilp + do fp = 1, num_soilp p = filter_soilp(fp) f1 = froot_leaf(ivt(p)) From d82a64d0e0e1a1600a69d3406728ae810bec5e50 Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Wed, 13 Apr 2022 12:55:21 -0600 Subject: [PATCH 13/17] Fix / add documentation of subroutine outputs --- src/biogeochem/CNDriverMod.F90 | 1 - .../NutrientCompetitionCLM45defaultMod.F90 | 36 +++++++++---------- .../NutrientCompetitionFlexibleCNMod.F90 | 36 +++++++++---------- .../NutrientCompetitionMethodMod.F90 | 7 ++-- 4 files changed, 38 insertions(+), 42 deletions(-) diff --git a/src/biogeochem/CNDriverMod.F90 b/src/biogeochem/CNDriverMod.F90 index 08158fca17..7d947ba6d9 100644 --- a/src/biogeochem/CNDriverMod.F90 +++ b/src/biogeochem/CNDriverMod.F90 @@ -407,7 +407,6 @@ subroutine CNDriverNoLeaching(bounds, num_pcropp, filter_pcropp, & crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & - c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, & energyflux_inst) diff --git a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 index ba29efbc08..ea7d3f607c 100644 --- a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 +++ b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 @@ -440,7 +440,6 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& num_pcropp, filter_pcropp, & crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & - c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, & energyflux_inst) @@ -468,10 +467,8 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& type(crop_type) , intent(in) :: crop_inst type(canopystate_type) , intent(in) :: canopystate_inst ! unused in this version type(cnveg_state_type) , intent(inout) :: cnveg_state_inst - type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst - type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst - type(cnveg_carbonflux_type) , intent(inout) :: c13_cnveg_carbonflux_inst - type(cnveg_carbonflux_type) , intent(inout) :: c14_cnveg_carbonflux_inst + type(cnveg_carbonstate_type) , intent(in) :: cnveg_carbonstate_inst + type(cnveg_carbonflux_type) , intent(in) :: cnveg_carbonflux_inst type(cnveg_nitrogenstate_type) , intent(in) :: cnveg_nitrogenstate_inst type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst type(soilbiogeochem_carbonflux_type) , intent(in) :: soilbiogeochem_carbonflux_inst @@ -483,7 +480,6 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& num_pcropp, filter_pcropp, & crop_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & - c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst) end subroutine calc_plant_nutrient_demand @@ -493,9 +489,16 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & num_pcropp, filter_pcropp, & crop_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & - c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst) ! + ! !DESCRIPTION: + ! Sets the following output variables that are used elsewhere: + ! - plant_ndemand + ! - retransn_to_npool + ! - leafn_to_retransn + ! - frootn_to_retransn + ! - livestemn_to_retransn + ! ! !USES: use pftconMod , only : npcropmin, pftcon use pftconMod , only : ntmp_soybean, nirrig_tmp_soybean @@ -518,10 +521,8 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches type(crop_type) , intent(in) :: crop_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst - type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst - type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst - type(cnveg_carbonflux_type) , intent(inout) :: c13_cnveg_carbonflux_inst - type(cnveg_carbonflux_type) , intent(inout) :: c14_cnveg_carbonflux_inst + type(cnveg_carbonstate_type) , intent(in) :: cnveg_carbonstate_inst + type(cnveg_carbonflux_type) , intent(in) :: cnveg_carbonflux_inst type(cnveg_nitrogenstate_type) , intent(in) :: cnveg_nitrogenstate_inst type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst ! @@ -553,13 +554,13 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] flag, true if planted, not harvested astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient + c_allometry => cnveg_state_inst%c_allometry_patch , & ! Input: [real(r8) (:) ] C allocation index (DIM) + n_allometry => cnveg_state_inst%n_allometry_patch , & ! Input: [real(r8) (:) ] N allocation index (DIM) + annsum_potential_gpp => cnveg_state_inst%annsum_potential_gpp_patch , & ! Input: [real(r8) (:) ] annual sum of potential GPP + annmax_retransn => cnveg_state_inst%annmax_retransn_patch , & ! Input: [real(r8) (:) ] annual max of retranslocated N pool grain_flag => cnveg_state_inst%grain_flag_patch , & ! Output: [real(r8) (:) ] 1: grain fill stage; 0: not - c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) - n_allometry => cnveg_state_inst%n_allometry_patch , & ! Output: [real(r8) (:) ] N allocation index (DIM) tempsum_potential_gpp => cnveg_state_inst%tempsum_potential_gpp_patch , & ! Output: [real(r8) (:) ] temporary annual sum of potential GPP tempmax_retransn => cnveg_state_inst%tempmax_retransn_patch , & ! Output: [real(r8) (:) ] temporary annual max of retranslocated N pool (gN/m2) - annsum_potential_gpp => cnveg_state_inst%annsum_potential_gpp_patch , & ! Output: [real(r8) (:) ] annual sum of potential GPP - annmax_retransn => cnveg_state_inst%annmax_retransn_patch , & ! Output: [real(r8) (:) ] annual max of retranslocated N pool leafc => cnveg_carbonstate_inst%leafc_patch , & ! Input: [real(r8) (:) ] frootc => cnveg_carbonstate_inst%frootc_patch , & ! Input: [real(r8) (:) ] @@ -567,13 +568,12 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & retransn => cnveg_nitrogenstate_inst%retransn_patch , & ! Input: [real(r8) (:) ] (gN/m2) plant pool of retranslocated N - gpp => cnveg_carbonflux_inst%gpp_before_downreg_patch , & ! Output: [real(r8) (:) ] GPP flux before downregulation (gC/m2/s) - availc => cnveg_carbonflux_inst%availc_patch , & ! Output: [real(r8) (:) ] C flux available for allocation (gC/m2/s) + gpp => cnveg_carbonflux_inst%gpp_before_downreg_patch , & ! Input: [real(r8) (:) ] GPP flux before downregulation (gC/m2/s) + availc => cnveg_carbonflux_inst%availc_patch , & ! Input: [real(r8) (:) ] C flux available for allocation (gC/m2/s) plant_ndemand => cnveg_nitrogenflux_inst%plant_ndemand_patch , & ! Output: [real(r8) (:) ] N flux required to support initial GPP (gN/m2/s) avail_retransn => cnveg_nitrogenflux_inst%avail_retransn_patch , & ! Output: [real(r8) (:) ] N flux available from retranslocation pool (gN/m2/s) retransn_to_npool => cnveg_nitrogenflux_inst%retransn_to_npool_patch , & ! Output: [real(r8) (:) ] deployment of retranslocated N (gN/m2/s) - sminn_to_npool => cnveg_nitrogenflux_inst%sminn_to_npool_patch , & ! Output: [real(r8) (:) ] deployment of soil mineral N uptake (gN/m2/s) leafn_to_retransn => cnveg_nitrogenflux_inst%leafn_to_retransn_patch , & ! Output: [real(r8) (:) ] frootn_to_retransn => cnveg_nitrogenflux_inst%frootn_to_retransn_patch , & ! Output: [real(r8) (:) ] livestemn_to_retransn => cnveg_nitrogenflux_inst%livestemn_to_retransn_patch & ! Output: [real(r8) (:) ] diff --git a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 index 4c573926f6..8900ba4140 100644 --- a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 +++ b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 @@ -893,7 +893,6 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& num_pcropp, filter_pcropp, & crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & - c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, & energyflux_inst) @@ -919,10 +918,8 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& type(crop_type) , intent(in) :: crop_inst type(canopystate_type) , intent(in) :: canopystate_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst - type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst - type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst - type(cnveg_carbonflux_type) , intent(inout) :: c13_cnveg_carbonflux_inst - type(cnveg_carbonflux_type) , intent(inout) :: c14_cnveg_carbonflux_inst + type(cnveg_carbonstate_type) , intent(in) :: cnveg_carbonstate_inst + type(cnveg_carbonflux_type) , intent(in) :: cnveg_carbonflux_inst type(cnveg_nitrogenstate_type) , intent(in) :: cnveg_nitrogenstate_inst type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst type(soilbiogeochem_carbonflux_type) , intent(in) :: soilbiogeochem_carbonflux_inst @@ -934,7 +931,6 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& num_pcropp, filter_pcropp, & crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & - c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, & energyflux_inst) @@ -946,11 +942,18 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & num_pcropp, filter_pcropp, & crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & - c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, & energyflux_inst) ! + ! !DESCRIPTION: + ! Sets the following output variables that are used elsewhere: + ! - plant_ndemand + ! - retransn_to_npool + ! - leafn_to_retransn + ! - frootn_to_retransn + ! - livestemn_to_retransn + ! ! !USES: use pftconMod , only : npcropmin, pftcon use pftconMod , only : ntmp_soybean, nirrig_tmp_soybean @@ -981,10 +984,8 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & type(crop_type) , intent(in) :: crop_inst type(canopystate_type) , intent(in) :: canopystate_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst - type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst - type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst - type(cnveg_carbonflux_type) , intent(inout) :: c13_cnveg_carbonflux_inst - type(cnveg_carbonflux_type) , intent(inout) :: c14_cnveg_carbonflux_inst + type(cnveg_carbonstate_type) , intent(in) :: cnveg_carbonstate_inst + type(cnveg_carbonflux_type) , intent(in) :: cnveg_carbonflux_inst type(cnveg_nitrogenstate_type) , intent(in) :: cnveg_nitrogenstate_inst type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst type(soilbiogeochem_carbonflux_type) , intent(in) :: soilbiogeochem_carbonflux_inst @@ -1033,13 +1034,13 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & croplive => crop_inst%croplive_patch , & ! Input: [logical (:) ] flag, true if planted, not harvested astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient + c_allometry => cnveg_state_inst%c_allometry_patch , & ! Input: [real(r8) (:) ] C allocation index (DIM) + n_allometry => cnveg_state_inst%n_allometry_patch , & ! Input: [real(r8) (:) ] N allocation index (DIM) + annsum_potential_gpp => cnveg_state_inst%annsum_potential_gpp_patch , & ! Input: [real(r8) (:) ] annual sum of potential GPP + annmax_retransn => cnveg_state_inst%annmax_retransn_patch , & ! Input: [real(r8) (:) ] annual max of retranslocated N pool grain_flag => cnveg_state_inst%grain_flag_patch , & ! Output: [real(r8) (:) ] 1: grain fill stage; 0: not - c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) - n_allometry => cnveg_state_inst%n_allometry_patch , & ! Output: [real(r8) (:) ] N allocation index (DIM) tempsum_potential_gpp => cnveg_state_inst%tempsum_potential_gpp_patch , & ! Output: [real(r8) (:) ] temporary annual sum of potential GPP tempmax_retransn => cnveg_state_inst%tempmax_retransn_patch , & ! Output: [real(r8) (:) ] temporary annual max of retranslocated N pool (gN/m2) - annsum_potential_gpp => cnveg_state_inst%annsum_potential_gpp_patch , & ! Output: [real(r8) (:) ] annual sum of potential GPP - annmax_retransn => cnveg_state_inst%annmax_retransn_patch , & ! Output: [real(r8) (:) ] annual max of retranslocated N pool leafc => cnveg_carbonstate_inst%leafc_patch , & ! Input: [real(r8) (:) ] frootc => cnveg_carbonstate_inst%frootc_patch , & ! Input: [real(r8) (:) ] @@ -1047,14 +1048,13 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & livecrootc => cnveg_carbonstate_inst%livecrootc_patch , & ! Input: [real(r8) (:) ] retransn => cnveg_nitrogenstate_inst%retransn_patch , & ! Input: [real(r8) (:) ] (gN/m2) plant pool of retranslocated N - gpp => cnveg_carbonflux_inst%gpp_before_downreg_patch , & ! Output: [real(r8) (:) ] GPP flux before downregulation (gC/m2/s) - availc => cnveg_carbonflux_inst%availc_patch , & ! Output: [real(r8) (:) ] C flux available for allocation (gC/m2/s) + gpp => cnveg_carbonflux_inst%gpp_before_downreg_patch , & ! Input: [real(r8) (:) ] GPP flux before downregulation (gC/m2/s) + availc => cnveg_carbonflux_inst%availc_patch , & ! Input: [real(r8) (:) ] C flux available for allocation (gC/m2/s) leafn => cnveg_nitrogenstate_inst%leafn_patch , & ! Input: [real(r8) (:) ] (gN/m2) leaf N plant_ndemand => cnveg_nitrogenflux_inst%plant_ndemand_patch , & ! Output: [real(r8) (:) ] N flux required to support initial GPP (gN/m2/s) avail_retransn => cnveg_nitrogenflux_inst%avail_retransn_patch , & ! Output: [real(r8) (:) ] N flux available from retranslocation pool (gN/m2/s) retransn_to_npool => cnveg_nitrogenflux_inst%retransn_to_npool_patch , & ! Output: [real(r8) (:) ] deployment of retranslocated N (gN/m2/s) - sminn_to_npool => cnveg_nitrogenflux_inst%sminn_to_npool_patch , & ! Output: [real(r8) (:) ] deployment of soil mineral N uptake (gN/m2/s) leafn_to_retransn => cnveg_nitrogenflux_inst%leafn_to_retransn_patch , & ! Output: [real(r8) (:) ] frootn_to_retransn => cnveg_nitrogenflux_inst%frootn_to_retransn_patch , & ! Output: [real(r8) (:) ] livestemn_to_retransn => cnveg_nitrogenflux_inst%livestemn_to_retransn_patch,& ! Output: [real(r8) (:) ] diff --git a/src/biogeochem/NutrientCompetitionMethodMod.F90 b/src/biogeochem/NutrientCompetitionMethodMod.F90 index 7e74775b7a..2ce1db26a5 100644 --- a/src/biogeochem/NutrientCompetitionMethodMod.F90 +++ b/src/biogeochem/NutrientCompetitionMethodMod.F90 @@ -63,7 +63,6 @@ subroutine calc_plant_nutrient_demand_interface (this, bounds, num_soilp, filter num_pcropp, filter_pcropp, & crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & - c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, & energyflux_inst) @@ -96,10 +95,8 @@ subroutine calc_plant_nutrient_demand_interface (this, bounds, num_soilp, filter type(crop_type) , intent(in) :: crop_inst type(canopystate_type) , intent(in) :: canopystate_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst - type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst - type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst - type(cnveg_carbonflux_type) , intent(inout) :: c13_cnveg_carbonflux_inst - type(cnveg_carbonflux_type) , intent(inout) :: c14_cnveg_carbonflux_inst + type(cnveg_carbonstate_type) , intent(in) :: cnveg_carbonstate_inst + type(cnveg_carbonflux_type) , intent(in) :: cnveg_carbonflux_inst type(cnveg_nitrogenstate_type) , intent(in) :: cnveg_nitrogenstate_inst type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst type(soilbiogeochem_carbonflux_type), intent(in) :: soilbiogeochem_carbonflux_inst From f3c8669fc676e2764d650e85b97bd7c43a8add07 Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Wed, 13 Apr 2022 16:12:42 -0600 Subject: [PATCH 14/17] Call calc_plant_nitrogen_demand separately for non-crop and crop patches This lets us skip this call when running with AgSys - since the relevant output variables will be calculated differently by AgSys. --- src/biogeochem/CNDriverMod.F90 | 32 +++- src/biogeochem/CNVegetationFacade.F90 | 4 + .../NutrientCompetitionCLM45defaultMod.F90 | 147 +++++++++++------- .../NutrientCompetitionFlexibleCNMod.F90 | 101 ++++++++---- .../NutrientCompetitionMethodMod.F90 | 20 ++- src/main/clm_driver.F90 | 1 + 6 files changed, 203 insertions(+), 102 deletions(-) diff --git a/src/biogeochem/CNDriverMod.F90 b/src/biogeochem/CNDriverMod.F90 index 7d947ba6d9..a782cc1986 100644 --- a/src/biogeochem/CNDriverMod.F90 +++ b/src/biogeochem/CNDriverMod.F90 @@ -82,7 +82,8 @@ end subroutine CNDriverInit !----------------------------------------------------------------------- subroutine CNDriverNoLeaching(bounds, & - num_soilc, filter_soilc, num_soilp, filter_soilp, num_pcropp, filter_pcropp, & + num_soilc, filter_soilc, num_soilp, filter_soilp, & + num_pcropp, filter_pcropp, num_soilnopcropp, filter_soilnopcropp, & num_exposedvegp, filter_exposedvegp, num_noexposedvegp, filter_noexposedvegp, & cnveg_state_inst, & cnveg_carbonflux_inst, cnveg_carbonstate_inst, & @@ -152,6 +153,8 @@ subroutine CNDriverNoLeaching(bounds, integer , intent(in) :: filter_soilp(:) ! filter for soil patches integer , intent(in) :: num_pcropp ! number of prog. crop patches in filter integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches + integer , intent(in) :: num_soilnopcropp ! number of non-prog. crop soil patches in filter + integer , intent(in) :: filter_soilnopcropp(:) ! filter for non-prog. crop 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 @@ -402,14 +405,27 @@ subroutine CNDriverNoLeaching(bounds, call t_stopf('cnalloc') call t_startf('calc_plant_nutrient_demand') + ! We always call calc_plant_nutrient_demand for natural veg patches, but only call + ! it for crop patches if NOT running with AgSys (since AgSys calculates the relevant + ! output variables in its own way). call nutrient_competition_method%calc_plant_nutrient_demand ( & - bounds, num_soilp, filter_soilp, & - num_pcropp, filter_pcropp, & - crop_inst, canopystate_inst, & - cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & - cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & - soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, & - energyflux_inst) + bounds, & + num_soilnopcropp, filter_soilnopcropp, .false., & + crop_inst, canopystate_inst, & + cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & + cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & + soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, & + energyflux_inst) + if (.not. use_crop_agsys) then + call nutrient_competition_method%calc_plant_nutrient_demand ( & + bounds, & + num_pcropp, filter_pcropp, .true., & + crop_inst, canopystate_inst, & + cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & + cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & + soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, & + energyflux_inst) + end if ! get the column-averaged plant_ndemand (needed for following call to SoilBiogeochemCompetition) diff --git a/src/biogeochem/CNVegetationFacade.F90 b/src/biogeochem/CNVegetationFacade.F90 index a03eee2841..384e4a4966 100644 --- a/src/biogeochem/CNVegetationFacade.F90 +++ b/src/biogeochem/CNVegetationFacade.F90 @@ -879,6 +879,7 @@ subroutine EcosystemDynamicsPreDrainage(this, bounds, & num_soilc, filter_soilc, & num_soilp, filter_soilp, & num_pcropp, filter_pcropp, & + num_soilnopcropp, filter_soilnopcropp, & num_exposedvegp, filter_exposedvegp, & num_noexposedvegp, filter_noexposedvegp, & soilbiogeochem_carbonflux_inst, soilbiogeochem_carbonstate_inst, & @@ -910,6 +911,8 @@ subroutine EcosystemDynamicsPreDrainage(this, bounds, & integer , intent(in) :: filter_soilp(:) ! filter for soil patches integer , intent(in) :: num_pcropp ! number of prog. crop patches in filter integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches + integer , intent(in) :: num_soilnopcropp ! number of non-prog. crop soil patches in filter + integer , intent(in) :: filter_soilnopcropp(:) ! filter for non-prog. crop 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 @@ -952,6 +955,7 @@ subroutine EcosystemDynamicsPreDrainage(this, bounds, & num_soilc, filter_soilc, & num_soilp, filter_soilp, & num_pcropp, filter_pcropp, & + num_soilnopcropp, filter_soilnopcropp, & num_exposedvegp, filter_exposedvegp, & num_noexposedvegp, filter_noexposedvegp, & this%cnveg_state_inst, & diff --git a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 index ea7d3f607c..9b1f8f9d09 100644 --- a/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 +++ b/src/biogeochem/NutrientCompetitionCLM45defaultMod.F90 @@ -436,8 +436,8 @@ subroutine calc_plant_cn_alloc (this, bounds, num_soilp, filter_soilp, & end subroutine calc_plant_cn_alloc !----------------------------------------------------------------------- - subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& - num_pcropp, filter_pcropp, & + subroutine calc_plant_nutrient_demand(this, bounds, & + num_p, filter_p, call_is_for_pcrop, & crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & @@ -460,10 +460,18 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& ! !ARGUMENTS: class(nutrient_competition_clm45default_type), intent(inout) :: this type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches - integer , intent(in) :: num_pcropp ! number of prog crop patches in filter - integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches + + ! This subroutine is meant to be called separately for non-prognostic-crop points and + ! prognostic-crop points. (The reason for this is so that the call for prognostic-crop + ! points can be skipped when a separate crop model is calculating these variables.) In + ! the call for non-prognostic-crop points, this filter should be the soilnopcropp + ! filter and call_is_for_pcrop should be false; in the call for prognostic-crop + ! points, this filter should be the pcropp filter and call_is_for_pcrop should be + ! true. + integer , intent(in) :: num_p ! number of patches in filter + integer , intent(in) :: filter_p(:) ! patch filter + logical , intent(in) :: call_is_for_pcrop + type(crop_type) , intent(in) :: crop_inst type(canopystate_type) , intent(in) :: canopystate_inst ! unused in this version type(cnveg_state_type) , intent(inout) :: cnveg_state_inst @@ -476,8 +484,8 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& type(energyflux_type) , intent(in) :: energyflux_inst !----------------------------------------------------------------------- - call this%calc_plant_nitrogen_demand(bounds, num_soilp, filter_soilp, & - num_pcropp, filter_pcropp, & + call this%calc_plant_nitrogen_demand(bounds, & + num_p, filter_p, call_is_for_pcrop, & crop_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst) @@ -485,8 +493,8 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& end subroutine calc_plant_nutrient_demand !----------------------------------------------------------------------- - subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & - num_pcropp, filter_pcropp, & + subroutine calc_plant_nitrogen_demand(this, bounds, & + num_p, filter_p, call_is_for_pcrop, & crop_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst) @@ -515,10 +523,18 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! !ARGUMENTS: class(nutrient_competition_clm45default_type), intent(in) :: this type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches - integer , intent(in) :: num_pcropp ! number of prog crop patches in filter - integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches + + ! This subroutine is meant to be called separately for non-prognostic-crop points and + ! prognostic-crop points. (The reason for this is so that the call for prognostic-crop + ! points can be skipped when a separate crop model is calculating these variables.) In + ! the call for non-prognostic-crop points, this filter should be the soilnopcropp + ! filter and call_is_for_pcrop should be false; in the call for prognostic-crop + ! points, this filter should be the pcropp filter and call_is_for_pcrop should be + ! true. + integer , intent(in) :: num_p ! number of patches in filter + integer , intent(in) :: filter_p(:) ! patch filter + logical , intent(in) :: call_is_for_pcrop + type(crop_type) , intent(in) :: crop_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(cnveg_carbonstate_type) , intent(in) :: cnveg_carbonstate_inst @@ -582,12 +598,9 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! set time steps dt = get_step_size_real() - call CropPhase(bounds, num_pcropp, filter_pcropp, crop_inst, cnveg_state_inst, & - crop_phase = crop_phase(bounds%begp:bounds%endp)) - ! loop over patches to assess the total plant N demand - do fp = 1,num_soilp - p = filter_soilp(fp) + do fp = 1, num_p + p = filter_p(fp) plant_ndemand(p) = availc(p)*(n_allometry(p)/c_allometry(p)) @@ -598,8 +611,15 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! Adding the following line to carry max retransn info to CN Annual Update tempmax_retransn(p) = max(tempmax_retransn(p),retransn(p)) + end do + + if (call_is_for_pcrop) then + call CropPhase(bounds, num_p, filter_p, crop_inst, cnveg_state_inst, & + crop_phase = crop_phase(bounds%begp:bounds%endp)) + + do fp = 1, num_p + p = filter_p(fp) - if (ivt(p) >= npcropmin) then if (croplive(p)) then if (crop_phase(p) == cphase_leafemerge) then grain_flag(p) = 0._r8 ! setting to 0 while in phase 2 @@ -646,50 +666,65 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & end if end if end if - end if - - ! Beth's code: crops pull from retransn pool only during grain fill; - ! retransn pool has N from leaves, stems, and roots for - ! retranslocation - - if(.not.use_fun)then + end do + end if + + ! Beth's code: crops pull from retransn pool only during grain fill; + ! retransn pool has N from leaves, stems, and roots for + ! retranslocation + if (.not. use_fun) then + if (call_is_for_pcrop) then + do fp = 1, num_p + p = filter_p(fp) + + if (grain_flag(p) == 1._r8) then + avail_retransn(p) = plant_ndemand(p) + else + avail_retransn(p) = 0.0_r8 + end if + end do + else + do fp = 1, num_p + p = filter_p(fp) - if (ivt(p) >= npcropmin .and. grain_flag(p) == 1._r8) then - avail_retransn(p) = plant_ndemand(p) - else if (ivt(p) < npcropmin .and. annsum_potential_gpp(p) > 0._r8) then - avail_retransn(p) = (annmax_retransn(p)/2._r8)*(gpp(p)/annsum_potential_gpp(p))/dt - else - avail_retransn(p) = 0.0_r8 - end if + if (annsum_potential_gpp(p) > 0._r8) then + avail_retransn(p) = (annmax_retransn(p)/2._r8)*(gpp(p)/annsum_potential_gpp(p))/dt + else + avail_retransn(p) = 0.0_r8 + end if + end do + end if - ! make sure available retrans N doesn't exceed storage - avail_retransn(p) = min(avail_retransn(p), retransn(p)/dt) + do fp = 1, num_p + p = filter_p(fp) - ! modify plant N demand according to the availability of - ! retranslocated N - ! take from retransn pool at most the flux required to meet - ! plant ndemand + ! make sure available retrans N doesn't exceed storage + avail_retransn(p) = min(avail_retransn(p), retransn(p)/dt) - if (plant_ndemand(p) > avail_retransn(p)) then - retransn_to_npool(p) = avail_retransn(p) - else - retransn_to_npool(p) = plant_ndemand(p) - end if + ! modify plant N demand according to the availability of + ! retranslocated N + ! take from retransn pool at most the flux required to meet + ! plant ndemand - if ( .not. use_fun ) then - plant_ndemand(p) = plant_ndemand(p) - retransn_to_npool(p) - else - if (season_decid(ivt(p)) == 1._r8.or.stress_decid(ivt(p))==1._r8) then - plant_ndemand(p) = plant_ndemand(p) - retransn_to_npool(p) - end if - end if + if (plant_ndemand(p) > avail_retransn(p)) then + retransn_to_npool(p) = avail_retransn(p) + else + retransn_to_npool(p) = plant_ndemand(p) + end if - end if !use_fun + if ( .not. use_fun ) then + plant_ndemand(p) = plant_ndemand(p) - retransn_to_npool(p) + else + if (season_decid(ivt(p)) == 1._r8.or.stress_decid(ivt(p))==1._r8) then + plant_ndemand(p) = plant_ndemand(p) - retransn_to_npool(p) + end if + end if + end do - end do ! end patch loop + end if !use_fun - end associate + end associate - end subroutine calc_plant_nitrogen_demand + end subroutine calc_plant_nitrogen_demand end module NutrientCompetitionCLM45defaultMod diff --git a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 index 8900ba4140..d51327dcdd 100644 --- a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 +++ b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 @@ -889,8 +889,8 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & end subroutine calc_plant_cn_alloc ! ----------------------------------------------------------------------- - subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& - num_pcropp, filter_pcropp, & + subroutine calc_plant_nutrient_demand(this, bounds, & + num_p, filter_p, call_is_for_pcrop, & crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & @@ -911,10 +911,18 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& ! !ARGUMENTS: class(nutrient_competition_FlexibleCN_type), intent(inout) :: this type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches - integer , intent(in) :: num_pcropp ! number of prog crop patches in filter - integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches + + ! This subroutine is meant to be called separately for non-prognostic-crop points and + ! prognostic-crop points. (The reason for this is so that the call for prognostic-crop + ! points can be skipped when a separate crop model is calculating these variables.) In + ! the call for non-prognostic-crop points, this filter should be the soilnopcropp + ! filter and call_is_for_pcrop should be false; in the call for prognostic-crop + ! points, this filter should be the pcropp filter and call_is_for_pcrop should be + ! true. + integer , intent(in) :: num_p ! number of patches in filter + integer , intent(in) :: filter_p(:) ! patch filter + logical , intent(in) :: call_is_for_pcrop + type(crop_type) , intent(in) :: crop_inst type(canopystate_type) , intent(in) :: canopystate_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst @@ -927,8 +935,8 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& type(energyflux_type) , intent(in) :: energyflux_inst !----------------------------------------------------------------------- - call this%calc_plant_nitrogen_demand(bounds, num_soilp, filter_soilp, & - num_pcropp, filter_pcropp, & + call this%calc_plant_nitrogen_demand(bounds, & + num_p, filter_p, call_is_for_pcrop, & crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & @@ -938,8 +946,8 @@ subroutine calc_plant_nutrient_demand(this, bounds, num_soilp, filter_soilp,& end subroutine calc_plant_nutrient_demand !----------------------------------------------------------------------- - subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & - num_pcropp, filter_pcropp, & + subroutine calc_plant_nitrogen_demand(this, bounds, & + num_p, filter_p, call_is_for_pcrop, & crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & @@ -977,10 +985,18 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! !ARGUMENTS: class(nutrient_competition_FlexibleCN_type), intent(inout) :: this type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches - integer , intent(in) :: num_pcropp ! number of prog crop patches in filter - integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches + + ! This subroutine is meant to be called separately for non-prognostic-crop points and + ! prognostic-crop points. (The reason for this is so that the call for prognostic-crop + ! points can be skipped when a separate crop model is calculating these variables.) In + ! the call for non-prognostic-crop points, this filter should be the soilnopcropp + ! filter and call_is_for_pcrop should be false; in the call for prognostic-crop + ! points, this filter should be the pcropp filter and call_is_for_pcrop should be + ! true. + integer , intent(in) :: num_p ! number of patches in filter + integer , intent(in) :: filter_p(:) ! patch filter + logical , intent(in) :: call_is_for_pcrop + type(crop_type) , intent(in) :: crop_inst type(canopystate_type) , intent(in) :: canopystate_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst @@ -1069,12 +1085,9 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! set time steps dt = get_step_size_real() - call CropPhase(bounds, num_pcropp, filter_pcropp, crop_inst, cnveg_state_inst, & - crop_phase = crop_phase(bounds%begp:bounds%endp)) - ! loop over patches to assess the total plant N demand - do fp = 1,num_soilp - p = filter_soilp(fp) + do fp = 1, num_p + p = filter_p(fp) ! when we have "if (leafn(p) == 0.0_r8)" below then we ! have floating overflow (out of floating point range) @@ -1136,8 +1149,15 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & ! Adding the following line to carry max retransn info to CN Annual Update tempmax_retransn(p) = max(tempmax_retransn(p),retransn(p)) + end do + + if (call_is_for_pcrop) then + call CropPhase(bounds, num_p, filter_p, crop_inst, cnveg_state_inst, & + crop_phase = crop_phase(bounds%begp:bounds%endp)) + + do fp = 1, num_p + p = filter_p(fp) - if (ivt(p) >= npcropmin) then if (croplive(p)) then if (crop_phase(p) == cphase_leafemerge) then grain_flag(p) = 0._r8 ! setting to 0 while in phase 2 @@ -1176,19 +1196,36 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & end if end if end if - end if + end do + end if - ! Beth's code: crops pull from retransn pool only during grain fill; - ! retransn pool has N from leaves, stems, and roots for - ! retranslocation + ! Beth's code: crops pull from retransn pool only during grain fill; + ! retransn pool has N from leaves, stems, and roots for + ! retranslocation + if (call_is_for_pcrop) then + do fp = 1, num_p + p = filter_p(fp) - if (ivt(p) >= npcropmin .and. grain_flag(p) == 1._r8) then - avail_retransn(p) = plant_ndemand(p) - else if (ivt(p) < npcropmin .and. annsum_potential_gpp(p) > 0._r8) then - avail_retransn(p) = (annmax_retransn(p)/2._r8)*(gpp(p)/annsum_potential_gpp(p))/dt - else - avail_retransn(p) = 0.0_r8 - end if + if (grain_flag(p) == 1._r8) then + avail_retransn(p) = plant_ndemand(p) + else + avail_retransn(p) = 0.0_r8 + end if + end do + else + do fp = 1, num_p + p = filter_p(fp) + + if (annsum_potential_gpp(p) > 0._r8) then + avail_retransn(p) = (annmax_retransn(p)/2._r8)*(gpp(p)/annsum_potential_gpp(p))/dt + else + avail_retransn(p) = 0.0_r8 + end if + end do + end if + + do fp = 1, num_p + p = filter_p(fp) ! make sure available retrans N doesn't exceed storage avail_retransn(p) = min(avail_retransn(p), retransn(p)/dt) @@ -1208,7 +1245,7 @@ subroutine calc_plant_nitrogen_demand(this, bounds, num_soilp, filter_soilp, & plant_ndemand(p) = plant_ndemand(p) - retransn_to_npool(p) end if - end do ! end patch loop + end do end associate diff --git a/src/biogeochem/NutrientCompetitionMethodMod.F90 b/src/biogeochem/NutrientCompetitionMethodMod.F90 index 2ce1db26a5..a9cb295de4 100644 --- a/src/biogeochem/NutrientCompetitionMethodMod.F90 +++ b/src/biogeochem/NutrientCompetitionMethodMod.F90 @@ -59,8 +59,8 @@ subroutine init_interface(this, bounds) end subroutine init_interface !--------------------------------------------------------------------------- - subroutine calc_plant_nutrient_demand_interface (this, bounds, num_soilp, filter_soilp, & - num_pcropp, filter_pcropp, & + subroutine calc_plant_nutrient_demand_interface (this, bounds, & + num_p, filter_p, call_is_for_pcrop, & crop_inst, canopystate_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & @@ -88,10 +88,18 @@ subroutine calc_plant_nutrient_demand_interface (this, bounds, num_soilp, filter ! !ARGUMENTS: class(nutrient_competition_method_type) , intent(inout) :: this type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches - integer , intent(in) :: num_pcropp ! number of prog crop patches in filter - integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches + + ! This subroutine is meant to be called separately for non-prognostic-crop points and + ! prognostic-crop points. (The reason for this is so that the call for prognostic-crop + ! points can be skipped when a separate crop model is calculating these variables.) In + ! the call for non-prognostic-crop points, this filter should be the soilnopcropp + ! filter and call_is_for_pcrop should be false; in the call for prognostic-crop + ! points, this filter should be the pcropp filter and call_is_for_pcrop should be + ! true. + integer , intent(in) :: num_p ! number of patches in filter + integer , intent(in) :: filter_p(:) ! patch filter + logical , intent(in) :: call_is_for_pcrop + type(crop_type) , intent(in) :: crop_inst type(canopystate_type) , intent(in) :: canopystate_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst diff --git a/src/main/clm_driver.F90 b/src/main/clm_driver.F90 index 03f906e604..f62d8070ba 100644 --- a/src/main/clm_driver.F90 +++ b/src/main/clm_driver.F90 @@ -984,6 +984,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro filter(nc)%num_soilc, filter(nc)%soilc, & filter(nc)%num_soilp, filter(nc)%soilp, & filter(nc)%num_pcropp, filter(nc)%pcropp, & + filter(nc)%num_soilnopcropp, filter(nc)%soilnopcropp, & filter(nc)%num_exposedvegp, filter(nc)%exposedvegp, & filter(nc)%num_noexposedvegp, filter(nc)%noexposedvegp, & soilbiogeochem_carbonflux_inst, soilbiogeochem_carbonstate_inst, & From 046c0001d7c695a93da557dbf7f9443482b5cd8f Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Wed, 13 Apr 2022 20:54:54 -0600 Subject: [PATCH 15/17] Extract single-point subroutine for calculating npool_to_* components This will make the code clearer when I introduce a similar subroutine for use with AgSys. --- .../NutrientCompetitionFlexibleCNMod.F90 | 542 +++++++++++------- 1 file changed, 320 insertions(+), 222 deletions(-) diff --git a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 index d51327dcdd..e9df168bcf 100644 --- a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 +++ b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 @@ -19,15 +19,17 @@ module NutrientCompetitionFlexibleCNMod ! !USES: use shr_kind_mod , only : r8 => shr_kind_r8 use shr_log_mod , only : errMsg => shr_log_errMsg + use clm_time_manager , only : get_step_size_real use decompMod , only : bounds_type use LandunitType , only : lun use ColumnType , only : col use PatchType , only : patch + use pftconMod , only : pftcon, npcropmin use NutrientCompetitionMethodMod, only : nutrient_competition_method_type use CropReprPoolsMod , only : nrepr use CNPhenologyMod , only : CropPhase use CropType , only : cphase_leafemerge, cphase_grainfill - use clm_varctl , only : iulog + use clm_varctl , only : iulog, use_crop_agsys use abortutils , only : endrun ! implicit none @@ -59,6 +61,9 @@ module NutrientCompetitionFlexibleCNMod end interface nutrient_competition_FlexibleCN_type ! + ! !PRIVATE MEMBER FUNCTIONS: + private :: calc_npool_to_components_flexiblecn ! Calculate npool_to_* terms for a single patch using the FlexibleCN approach + character(len=*), parameter, private :: sourcefile = & __FILE__ !------------------------------------------------------------------------ @@ -188,9 +193,7 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & soilbiogeochem_nitrogenstate_inst, fpg_col) ! ! !USES: - use pftconMod , only : pftcon, npcropmin use clm_varctl , only : use_c13, use_c14, carbon_resp_opt - use clm_time_manager , only : get_step_size_real use CNVegStateType , only : cnveg_state_type use CropType , only : crop_type use CanopyStateType , only : canopystate_type @@ -225,14 +228,10 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & integer :: c,p,k ! indices integer :: fp ! lake filter patch index real(r8) :: f1,f2,f3,f4,g1,g2 ! allocation parameters - real(r8) :: cnl,cnfr,cnlw,cndw ! C:N ratios for leaf, fine root, and wood real(r8) :: fcur ! fraction of current psn displayed as growth real(r8) :: gresp_storage ! temporary variable for growth resp to storage real(r8) :: nlc ! temporary variable for total new leaf carbon allocation real(r8) :: f5(nrepr) ! reproductive allocation parameters - real(r8) :: cng ! C:N ratio for grain (= cnlw for now; slevis) - real(r8) :: dt ! model time step - real(r8):: fsmn(bounds%begp:bounds%endp) ! A emperate variable for adjusting FUN uptakes real(r8):: frootcn_storage_actual real(r8):: frootcn_actual @@ -244,37 +243,6 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & real(r8):: frootcn_max real(r8):: livewdcn_max real(r8):: frac_resp - real(r8):: npool_to_reproductiven_demand_tot - real(r8):: npool_to_reproductiven_storage_demand_tot - real(r8) :: npool_to_leafn_demand (bounds%begp:bounds%endp) - real(r8) :: npool_to_leafn_storage_demand (bounds%begp:bounds%endp) - real(r8) :: npool_to_frootn_demand (bounds%begp:bounds%endp) - real(r8) :: npool_to_frootn_storage_demand (bounds%begp:bounds%endp) - real(r8) :: npool_to_livestemn_demand (bounds%begp:bounds%endp) - real(r8) :: npool_to_livestemn_storage_demand (bounds%begp:bounds%endp) - real(r8) :: npool_to_livecrootn_demand (bounds%begp:bounds%endp) - real(r8) :: npool_to_livecrootn_storage_demand (bounds%begp:bounds%endp) - real(r8) :: npool_to_deadstemn_demand (bounds%begp:bounds%endp) - real(r8) :: npool_to_deadstemn_storage_demand (bounds%begp:bounds%endp) - real(r8) :: npool_to_deadcrootn_demand (bounds%begp:bounds%endp) - real(r8) :: npool_to_deadcrootn_storage_demand (bounds%begp:bounds%endp) - real(r8) :: npool_to_reproductiven_demand (bounds%begp:bounds%endp, nrepr) - real(r8) :: npool_to_reproductiven_storage_demand (bounds%begp:bounds%endp, nrepr) - real(r8) :: total_plant_Ndemand (bounds%begp:bounds%endp) - real(r8) :: frNdemand_npool_to_leafn (bounds%begp:bounds%endp) - real(r8) :: frNdemand_npool_to_leafn_storage (bounds%begp:bounds%endp) - real(r8) :: frNdemand_npool_to_frootn (bounds%begp:bounds%endp) - real(r8) :: frNdemand_npool_to_frootn_storage (bounds%begp:bounds%endp) - real(r8) :: frNdemand_npool_to_livestemn (bounds%begp:bounds%endp) - real(r8) :: frNdemand_npool_to_livestemn_storage (bounds%begp:bounds%endp) - real(r8) :: frNdemand_npool_to_deadstemn (bounds%begp:bounds%endp) - real(r8) :: frNdemand_npool_to_deadstemn_storage (bounds%begp:bounds%endp) - real(r8) :: frNdemand_npool_to_livecrootn (bounds%begp:bounds%endp) - real(r8) :: frNdemand_npool_to_livecrootn_storage (bounds%begp:bounds%endp) - real(r8) :: frNdemand_npool_to_deadcrootn (bounds%begp:bounds%endp) - real(r8) :: frNdemand_npool_to_deadcrootn_storage (bounds%begp:bounds%endp) - real(r8) :: frNdemand_npool_to_reproductiven (bounds%begp:bounds%endp, nrepr) - real(r8) :: frNdemand_npool_to_reproductiven_storage(bounds%begp:bounds%endp, nrepr) ! ----------------------------------------------------------------------- @@ -295,9 +263,7 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & 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) fcur2 => pftcon%fcur , & ! Input: allocation parameter: fraction of allocation that goes to currently displayed growth, remainder to storage - graincn => pftcon%graincn , & ! Input: grain C:N (gC/gN) grperc => pftcon%grperc , & ! Input: growth respiration parameter grpnow => pftcon%grpnow , & ! Input: growth respiration parameter evergreen => pftcon%evergreen , & ! Input: binary flag for evergreen leaf habit (0 or 1) @@ -374,9 +340,6 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & ) - ! set time steps - dt = get_step_size_real() - ! patch loop to distribute the available N between the competing patches ! on the basis of relative demand, and allocate C and N to new growth and storage @@ -404,10 +367,6 @@ 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)) - cnfr = frootcn(ivt(p)) - cnlw = livewdcn(ivt(p)) - cndw = deadwdcn(ivt(p)) fcur = fcur2(ivt(p)) if (evergreen(ivt(p)) == 1._r8) then @@ -516,184 +475,38 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & end if cpool_to_gresp_storage(p) = gresp_storage * g1 * (1._r8 - g2) - - ! computing 1.) fractional N demand and 2.) N allocation after uptake for different plant parts - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - ! computing nitrogen demand for different pools based on carbon allocated and CN ratio - npool_to_leafn_demand(p) = (nlc / cnl) * fcur - npool_to_leafn_storage_demand(p) = (nlc / cnl) * (1._r8 - fcur) - npool_to_frootn_demand(p) = (nlc * f1 / cnfr) * fcur - npool_to_frootn_storage_demand(p) = (nlc * f1 / cnfr) * (1._r8 - fcur) - if (woody(ivt(p)) == 1._r8) then - - npool_to_livestemn_demand(p) = (nlc * f3 * f4 / cnlw) * fcur - npool_to_livestemn_storage_demand(p) = (nlc * f3 * f4 / cnlw) * (1._r8 - fcur) - npool_to_deadstemn_demand(p) = (nlc * f3 * (1._r8 - f4) / cndw) * fcur - npool_to_deadstemn_storage_demand(p) = (nlc * f3 * (1._r8 - f4) / cndw) * (1._r8 - fcur) - npool_to_livecrootn_demand(p) = (nlc * f2 * f3 * f4 / cnlw) * fcur - npool_to_livecrootn_storage_demand(p) = (nlc * f2 * f3 * f4 / cnlw) * (1._r8 - fcur) - npool_to_deadcrootn_demand(p) = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * fcur - npool_to_deadcrootn_storage_demand(p) = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * (1._r8 - fcur) - end if - if (ivt(p) >= npcropmin) then ! skip 2 generic crops - - cng = graincn(ivt(p)) - npool_to_livestemn_demand(p) = (nlc * f3 * f4 / cnlw) * fcur - npool_to_livestemn_storage_demand(p) = (nlc * f3 * f4 / cnlw) * (1._r8 - fcur) - npool_to_deadstemn_demand(p) = (nlc * f3 * (1._r8 - f4) / cndw) * fcur - npool_to_deadstemn_storage_demand(p) = (nlc * f3 * (1._r8 - f4) / cndw) * (1._r8 - fcur) - npool_to_livecrootn_demand(p) = (nlc * f2 * f3 * f4 / cnlw) * fcur - npool_to_livecrootn_storage_demand(p) = (nlc * f2 * f3 * f4 / cnlw) * (1._r8 - fcur) - npool_to_deadcrootn_demand(p) = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * fcur - npool_to_deadcrootn_storage_demand(p) = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * (1._r8 - fcur) - do k = 1, nrepr - npool_to_reproductiven_demand(p,k) = (nlc * f5(k) / cng) * fcur - npool_to_reproductiven_storage_demand(p,k) = (nlc * f5(k) / cng) * (1._r8 -fcur) - end do - end if - - - ! computing 1.) fractional N demand for different plant parts -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - total_plant_Ndemand(p) = npool_to_leafn_demand(p) + npool_to_leafn_storage_demand(p) + & - npool_to_frootn_demand(p) + npool_to_frootn_storage_demand(p) - - if (woody(ivt(p)) == 1._r8) then - - total_plant_Ndemand(p) = npool_to_leafn_demand(p) + npool_to_leafn_storage_demand(p) + & - npool_to_frootn_demand(p) + npool_to_frootn_storage_demand(p) + & - npool_to_livestemn_demand(p) + npool_to_livestemn_storage_demand(p) + npool_to_deadstemn_demand(p) + & - npool_to_deadstemn_storage_demand(p) + & - npool_to_livecrootn_demand(p) + npool_to_livecrootn_storage_demand(p) + npool_to_deadcrootn_demand(p) + & - npool_to_deadcrootn_storage_demand(p) - - end if - if (ivt(p) >= npcropmin) then ! skip 2 generic crops - - npool_to_reproductiven_demand_tot = 0._r8 - npool_to_reproductiven_storage_demand_tot = 0._r8 - do k = 1, nrepr - npool_to_reproductiven_demand_tot = npool_to_reproductiven_demand_tot + & - npool_to_reproductiven_demand(p,k) - npool_to_reproductiven_storage_demand_tot = npool_to_reproductiven_storage_demand_tot + & - npool_to_reproductiven_storage_demand(p,k) - end do - - total_plant_Ndemand(p) = npool_to_leafn_demand(p) + npool_to_leafn_storage_demand(p) + & - npool_to_frootn_demand(p) + npool_to_frootn_storage_demand(p) + & - npool_to_livestemn_demand(p) + npool_to_livestemn_storage_demand(p) + npool_to_deadstemn_demand(p) + & - npool_to_deadstemn_storage_demand(p) + & - npool_to_livecrootn_demand(p) + npool_to_livecrootn_storage_demand(p) + npool_to_deadcrootn_demand(p) + & - npool_to_deadcrootn_storage_demand(p) + & - npool_to_reproductiven_demand_tot + npool_to_reproductiven_storage_demand_tot - - end if - - if (total_plant_Ndemand(p) == 0.0_r8) then ! removing division by zero - - frNdemand_npool_to_leafn(p) = 0.0_r8 - frNdemand_npool_to_leafn_storage(p) = 0.0_r8 - frNdemand_npool_to_frootn(p) = 0.0_r8 - frNdemand_npool_to_frootn_storage(p) = 0.0_r8 - if (woody(ivt(p)) == 1._r8) then - - frNdemand_npool_to_livestemn(p) = 0.0_r8 - frNdemand_npool_to_livestemn_storage(p) = 0.0_r8 - frNdemand_npool_to_deadstemn(p) = 0.0_r8 - frNdemand_npool_to_deadstemn_storage(p) = 0.0_r8 - frNdemand_npool_to_livecrootn(p) = 0.0_r8 - frNdemand_npool_to_livecrootn_storage(p) = 0.0_r8 - frNdemand_npool_to_deadcrootn(p) = 0.0_r8 - frNdemand_npool_to_deadcrootn_storage(p) = 0.0_r8 - end if - if (ivt(p) >= npcropmin) then ! skip 2 generic crops - - frNdemand_npool_to_livestemn(p) = 0.0_r8 - frNdemand_npool_to_livestemn_storage(p) = 0.0_r8 - frNdemand_npool_to_deadstemn(p) = 0.0_r8 - frNdemand_npool_to_deadstemn_storage(p) = 0.0_r8 - frNdemand_npool_to_livecrootn(p) = 0.0_r8 - frNdemand_npool_to_livecrootn_storage(p) = 0.0_r8 - frNdemand_npool_to_deadcrootn(p) = 0.0_r8 - frNdemand_npool_to_deadcrootn_storage(p) = 0.0_r8 - do k = 1, nrepr - frNdemand_npool_to_reproductiven(p,k) = 0.0_r8 - frNdemand_npool_to_reproductiven_storage(p,k) = 0.0_r8 - end do - end if + if (use_crop_agsys .and. ivt(p) >= npcropmin) then + ! FIXME(wjs, 2022-04-13) use AgSys-based method else - - frNdemand_npool_to_leafn(p) = npool_to_leafn_demand(p) / total_plant_Ndemand(p) - frNdemand_npool_to_leafn_storage(p) = npool_to_leafn_storage_demand(p) / total_plant_Ndemand(p) - frNdemand_npool_to_frootn(p) = npool_to_frootn_demand(p) / total_plant_Ndemand(p) - frNdemand_npool_to_frootn_storage(p) = npool_to_frootn_storage_demand(p) / total_plant_Ndemand(p) - if (woody(ivt(p)) == 1._r8) then - - frNdemand_npool_to_livestemn(p) = npool_to_livestemn_demand(p) / total_plant_Ndemand(p) - frNdemand_npool_to_livestemn_storage(p) = npool_to_livestemn_storage_demand(p) / total_plant_Ndemand(p) - frNdemand_npool_to_deadstemn(p) = npool_to_deadstemn_demand(p) / total_plant_Ndemand(p) - frNdemand_npool_to_deadstemn_storage(p) = npool_to_deadstemn_storage_demand(p) / total_plant_Ndemand(p) - frNdemand_npool_to_livecrootn(p) = npool_to_livecrootn_demand(p) / total_plant_Ndemand(p) - frNdemand_npool_to_livecrootn_storage(p) = npool_to_livecrootn_storage_demand(p) / total_plant_Ndemand(p) - frNdemand_npool_to_deadcrootn(p) = npool_to_deadcrootn_demand(p) / total_plant_Ndemand(p) - frNdemand_npool_to_deadcrootn_storage(p) = npool_to_deadcrootn_storage_demand(p) / total_plant_Ndemand(p) - end if - if (ivt(p) >= npcropmin) then ! skip 2 generic crops - - frNdemand_npool_to_livestemn(p) = npool_to_livestemn_demand(p) / total_plant_Ndemand(p) - frNdemand_npool_to_livestemn_storage(p) = npool_to_livestemn_storage_demand(p) / total_plant_Ndemand(p) - frNdemand_npool_to_deadstemn(p) = npool_to_deadstemn_demand(p) / total_plant_Ndemand(p) - frNdemand_npool_to_deadstemn_storage(p) = npool_to_deadstemn_storage_demand(p) / total_plant_Ndemand(p) - frNdemand_npool_to_livecrootn(p) = npool_to_livecrootn_demand(p) / total_plant_Ndemand(p) - frNdemand_npool_to_livecrootn_storage(p) = npool_to_livecrootn_storage_demand(p) / total_plant_Ndemand(p) - frNdemand_npool_to_deadcrootn(p) = npool_to_deadcrootn_demand(p) / total_plant_Ndemand(p) - frNdemand_npool_to_deadcrootn_storage(p) = npool_to_deadcrootn_storage_demand(p) / total_plant_Ndemand(p) - do k = 1, nrepr - frNdemand_npool_to_reproductiven(p,k) = & - npool_to_reproductiven_demand(p,k) / total_plant_Ndemand(p) - frNdemand_npool_to_reproductiven_storage(p,k) = & - npool_to_reproductiven_storage_demand(p,k) / total_plant_Ndemand(p) - end do - end if - - end if -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - - ! computing N allocation for different plant parts - ! allocating allocation to different plant parts in proportion to the fractional demand -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - npool_to_leafn(p) = frNdemand_npool_to_leafn(p) * npool(p) / dt - npool_to_leafn_storage(p) = frNdemand_npool_to_leafn_storage(p) * npool(p) / dt - npool_to_frootn(p) = frNdemand_npool_to_frootn(p) * npool(p) / dt - npool_to_frootn_storage(p) = frNdemand_npool_to_frootn_storage(p) * npool(p) / dt - if (woody(ivt(p)) == 1._r8) then - npool_to_livestemn(p) = frNdemand_npool_to_livestemn(p) * npool(p) / dt - npool_to_livestemn_storage(p) = frNdemand_npool_to_livestemn_storage(p) * npool(p) / dt - npool_to_deadstemn(p) = frNdemand_npool_to_deadstemn(p) * npool(p) / dt - npool_to_deadstemn_storage(p) = frNdemand_npool_to_deadstemn_storage(p) * npool(p) / dt - npool_to_livecrootn(p) = frNdemand_npool_to_livecrootn(p) * npool(p) / dt - npool_to_livecrootn_storage(p) = frNdemand_npool_to_livecrootn_storage(p) * npool(p) / dt - npool_to_deadcrootn(p) = frNdemand_npool_to_deadcrootn(p) * npool(p) / dt - npool_to_deadcrootn_storage(p) = frNdemand_npool_to_deadcrootn_storage(p) * npool(p) / dt + call calc_npool_to_components_flexiblecn( & + ! Inputs + npool = npool(p), & + ivt = ivt(p), & + nlc = nlc, & + fcur = fcur, & + f1 = f1, & + f2 = f2, & + f3 = f3, & + f4 = f4, & + f5 = f5, & + + ! Outputs + npool_to_leafn = npool_to_leafn(p), & + npool_to_leafn_storage = npool_to_leafn_storage(p), & + npool_to_frootn = npool_to_frootn(p), & + npool_to_frootn_storage = npool_to_frootn_storage(p), & + npool_to_livestemn = npool_to_livestemn(p), & + npool_to_livestemn_storage = npool_to_livestemn_storage(p), & + npool_to_deadstemn = npool_to_deadstemn(p), & + npool_to_deadstemn_storage = npool_to_deadstemn_storage(p), & + npool_to_livecrootn = npool_to_livecrootn(p), & + npool_to_livecrootn_storage = npool_to_livecrootn_storage(p), & + npool_to_deadcrootn = npool_to_deadcrootn(p), & + npool_to_deadcrootn_storage = npool_to_deadcrootn_storage(p), & + npool_to_reproductiven = npool_to_reproductiven(p,:), & + npool_to_reproductiven_storage = npool_to_reproductiven_storage(p,:)) end if - if (ivt(p) >= npcropmin) then ! skip 2 generic crops - npool_to_livestemn(p) = frNdemand_npool_to_livestemn(p) * npool(p) / dt - npool_to_livestemn_storage(p) = frNdemand_npool_to_livestemn_storage(p) * npool(p) / dt - npool_to_deadstemn(p) = frNdemand_npool_to_deadstemn(p) * npool(p) / dt - npool_to_deadstemn_storage(p) = frNdemand_npool_to_deadstemn_storage(p) * npool(p) / dt - npool_to_livecrootn(p) = frNdemand_npool_to_livecrootn(p) * npool(p) / dt - npool_to_livecrootn_storage(p) = frNdemand_npool_to_livecrootn_storage(p) * npool(p) / dt - npool_to_deadcrootn(p) = frNdemand_npool_to_deadcrootn(p) * npool(p) / dt - npool_to_deadcrootn_storage(p) = frNdemand_npool_to_deadcrootn_storage(p) * npool(p) / dt - do k = 1, nrepr - npool_to_reproductiven(p,k) = frNdemand_npool_to_reproductiven(p,k) * npool(p) / dt - npool_to_reproductiven_storage(p,k) = frNdemand_npool_to_reproductiven_storage(p,k) * npool(p) / dt - end do - end if -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - cpool_to_resp(p) = 0.0_r8 cpool_to_leafc_resp(p) = 0.0_r8 @@ -888,6 +701,291 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & end subroutine calc_plant_cn_alloc + !----------------------------------------------------------------------- + subroutine calc_npool_to_components_flexiblecn( & + npool, ivt, nlc, fcur, f1, f2, f3, f4, f5, & + npool_to_leafn, npool_to_leafn_storage, & + npool_to_frootn, npool_to_frootn_storage, & + npool_to_livestemn, npool_to_livestemn_storage, & + npool_to_deadstemn, npool_to_deadstemn_storage, & + npool_to_livecrootn, npool_to_livecrootn_storage, & + npool_to_deadcrootn, npool_to_deadcrootn_storage, & + npool_to_reproductiven, npool_to_reproductiven_storage) + ! + ! !DESCRIPTION: + ! Calculate npool_to_* terms for a single patch using the FlexibleCN approach + ! + ! !ARGUMENTS: + real(r8), intent(in) :: npool ! temporary plant N pool (gN/m2) + integer, intent(in) :: ivt ! vegetation type + real(r8), intent(in) :: nlc ! new leaf carbon allocation (gC/m2/s) + real(r8), intent(in) :: fcur ! fraction of current psn displayed as growth + real(r8), intent(in) :: f1 ! C allocation parameter - fine_root:leaf ratio + real(r8), intent(in) :: f2 ! C allocation parameter - coarse_root:stem ratio + 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 + + ! 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. + real(r8), intent(inout) :: npool_to_leafn + real(r8), intent(inout) :: npool_to_leafn_storage + real(r8), intent(inout) :: npool_to_frootn + real(r8), intent(inout) :: npool_to_frootn_storage + real(r8), intent(inout) :: npool_to_livestemn + real(r8), intent(inout) :: npool_to_livestemn_storage + real(r8), intent(inout) :: npool_to_deadstemn + real(r8), intent(inout) :: npool_to_deadstemn_storage + real(r8), intent(inout) :: npool_to_livecrootn + real(r8), intent(inout) :: npool_to_livecrootn_storage + real(r8), intent(inout) :: npool_to_deadcrootn + real(r8), intent(inout) :: npool_to_deadcrootn_storage + real(r8), intent(inout) :: npool_to_reproductiven(:) + real(r8), intent(inout) :: npool_to_reproductiven_storage(:) + ! + ! !LOCAL VARIABLES: + real(r8) :: cnl,cnfr,cnlw,cndw ! C:N ratios for leaf, fine root, and wood + real(r8) :: cng ! C:N ratio for grain (= cnlw for now; slevis) + real(r8) :: dt ! model time step + integer :: k + + real(r8) :: npool_to_reproductiven_demand_tot + real(r8) :: npool_to_reproductiven_storage_demand_tot + real(r8) :: npool_to_leafn_demand + real(r8) :: npool_to_leafn_storage_demand + real(r8) :: npool_to_frootn_demand + real(r8) :: npool_to_frootn_storage_demand + real(r8) :: npool_to_livestemn_demand + real(r8) :: npool_to_livestemn_storage_demand + real(r8) :: npool_to_livecrootn_demand + real(r8) :: npool_to_livecrootn_storage_demand + real(r8) :: npool_to_deadstemn_demand + real(r8) :: npool_to_deadstemn_storage_demand + real(r8) :: npool_to_deadcrootn_demand + real(r8) :: npool_to_deadcrootn_storage_demand + real(r8) :: npool_to_reproductiven_demand(nrepr) + real(r8) :: npool_to_reproductiven_storage_demand(nrepr) + real(r8) :: total_plant_Ndemand + real(r8) :: frNdemand_npool_to_leafn + real(r8) :: frNdemand_npool_to_leafn_storage + real(r8) :: frNdemand_npool_to_frootn + real(r8) :: frNdemand_npool_to_frootn_storage + real(r8) :: frNdemand_npool_to_livestemn + real(r8) :: frNdemand_npool_to_livestemn_storage + real(r8) :: frNdemand_npool_to_deadstemn + real(r8) :: frNdemand_npool_to_deadstemn_storage + real(r8) :: frNdemand_npool_to_livecrootn + real(r8) :: frNdemand_npool_to_livecrootn_storage + real(r8) :: frNdemand_npool_to_deadcrootn + real(r8) :: frNdemand_npool_to_deadcrootn_storage + real(r8) :: frNdemand_npool_to_reproductiven(nrepr) + real(r8) :: frNdemand_npool_to_reproductiven_storage(nrepr) + + character(len=*), parameter :: subname = 'calc_npool_to_components_flexiblecn' + !----------------------------------------------------------------------- + + SHR_ASSERT_ALL_FL((ubound(f5) == [nrepr]), sourcefile, __LINE__) + SHR_ASSERT_ALL_FL((ubound(npool_to_reproductiven) == [nrepr]), sourcefile, __LINE__) + SHR_ASSERT_ALL_FL((ubound(npool_to_reproductiven_storage) == [nrepr]), sourcefile, __LINE__) + + 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) + graincn => pftcon%graincn & ! Input: grain C:N (gC/gN) + ) + + ! set time steps + dt = get_step_size_real() + + cnl = leafcn(ivt) + cnfr = frootcn(ivt) + cnlw = livewdcn(ivt) + cndw = deadwdcn(ivt) + + ! computing 1.) fractional N demand and 2.) N allocation after uptake for different plant parts +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! computing nitrogen demand for different pools based on carbon allocated and CN ratio + npool_to_leafn_demand = (nlc / cnl) * fcur + npool_to_leafn_storage_demand = (nlc / cnl) * (1._r8 - fcur) + npool_to_frootn_demand = (nlc * f1 / cnfr) * fcur + npool_to_frootn_storage_demand = (nlc * f1 / cnfr) * (1._r8 - fcur) + if (woody(ivt) == 1._r8) then + + npool_to_livestemn_demand = (nlc * f3 * f4 / cnlw) * fcur + npool_to_livestemn_storage_demand = (nlc * f3 * f4 / cnlw) * (1._r8 - fcur) + npool_to_deadstemn_demand = (nlc * f3 * (1._r8 - f4) / cndw) * fcur + npool_to_deadstemn_storage_demand = (nlc * f3 * (1._r8 - f4) / cndw) * (1._r8 - fcur) + npool_to_livecrootn_demand = (nlc * f2 * f3 * f4 / cnlw) * fcur + npool_to_livecrootn_storage_demand = (nlc * f2 * f3 * f4 / cnlw) * (1._r8 - fcur) + npool_to_deadcrootn_demand = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * fcur + npool_to_deadcrootn_storage_demand = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * (1._r8 - fcur) + end if + if (ivt >= npcropmin) then ! skip 2 generic crops + + cng = graincn(ivt) + npool_to_livestemn_demand = (nlc * f3 * f4 / cnlw) * fcur + npool_to_livestemn_storage_demand = (nlc * f3 * f4 / cnlw) * (1._r8 - fcur) + npool_to_deadstemn_demand = (nlc * f3 * (1._r8 - f4) / cndw) * fcur + npool_to_deadstemn_storage_demand = (nlc * f3 * (1._r8 - f4) / cndw) * (1._r8 - fcur) + npool_to_livecrootn_demand = (nlc * f2 * f3 * f4 / cnlw) * fcur + npool_to_livecrootn_storage_demand = (nlc * f2 * f3 * f4 / cnlw) * (1._r8 - fcur) + npool_to_deadcrootn_demand = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * fcur + npool_to_deadcrootn_storage_demand = (nlc * f2 * f3 * (1._r8 - f4) / cndw) * (1._r8 - fcur) + do k = 1, nrepr + npool_to_reproductiven_demand(k) = (nlc * f5(k) / cng) * fcur + npool_to_reproductiven_storage_demand(k) = (nlc * f5(k) / cng) * (1._r8 -fcur) + end do + end if + + + ! computing 1.) fractional N demand for different plant parts +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + total_plant_Ndemand = npool_to_leafn_demand + npool_to_leafn_storage_demand + & + npool_to_frootn_demand + npool_to_frootn_storage_demand + + if (woody(ivt) == 1._r8) then + + total_plant_Ndemand = npool_to_leafn_demand + npool_to_leafn_storage_demand + & + npool_to_frootn_demand + npool_to_frootn_storage_demand + & + npool_to_livestemn_demand + npool_to_livestemn_storage_demand + npool_to_deadstemn_demand + & + npool_to_deadstemn_storage_demand + & + npool_to_livecrootn_demand + npool_to_livecrootn_storage_demand + npool_to_deadcrootn_demand + & + npool_to_deadcrootn_storage_demand + + end if + if (ivt >= npcropmin) then ! skip 2 generic crops + + npool_to_reproductiven_demand_tot = 0._r8 + npool_to_reproductiven_storage_demand_tot = 0._r8 + do k = 1, nrepr + npool_to_reproductiven_demand_tot = npool_to_reproductiven_demand_tot + & + npool_to_reproductiven_demand(k) + npool_to_reproductiven_storage_demand_tot = npool_to_reproductiven_storage_demand_tot + & + npool_to_reproductiven_storage_demand(k) + end do + + total_plant_Ndemand = npool_to_leafn_demand + npool_to_leafn_storage_demand + & + npool_to_frootn_demand + npool_to_frootn_storage_demand + & + npool_to_livestemn_demand + npool_to_livestemn_storage_demand + npool_to_deadstemn_demand + & + npool_to_deadstemn_storage_demand + & + npool_to_livecrootn_demand + npool_to_livecrootn_storage_demand + npool_to_deadcrootn_demand + & + npool_to_deadcrootn_storage_demand + & + npool_to_reproductiven_demand_tot + npool_to_reproductiven_storage_demand_tot + + end if + + if (total_plant_Ndemand == 0.0_r8) then ! removing division by zero + + frNdemand_npool_to_leafn = 0.0_r8 + frNdemand_npool_to_leafn_storage = 0.0_r8 + frNdemand_npool_to_frootn = 0.0_r8 + frNdemand_npool_to_frootn_storage = 0.0_r8 + if (woody(ivt) == 1._r8) then + + frNdemand_npool_to_livestemn = 0.0_r8 + frNdemand_npool_to_livestemn_storage = 0.0_r8 + frNdemand_npool_to_deadstemn = 0.0_r8 + frNdemand_npool_to_deadstemn_storage = 0.0_r8 + frNdemand_npool_to_livecrootn = 0.0_r8 + frNdemand_npool_to_livecrootn_storage = 0.0_r8 + frNdemand_npool_to_deadcrootn = 0.0_r8 + frNdemand_npool_to_deadcrootn_storage = 0.0_r8 + end if + if (ivt >= npcropmin) then ! skip 2 generic crops + + frNdemand_npool_to_livestemn = 0.0_r8 + frNdemand_npool_to_livestemn_storage = 0.0_r8 + frNdemand_npool_to_deadstemn = 0.0_r8 + frNdemand_npool_to_deadstemn_storage = 0.0_r8 + frNdemand_npool_to_livecrootn = 0.0_r8 + frNdemand_npool_to_livecrootn_storage = 0.0_r8 + frNdemand_npool_to_deadcrootn = 0.0_r8 + frNdemand_npool_to_deadcrootn_storage = 0.0_r8 + do k = 1, nrepr + frNdemand_npool_to_reproductiven(k) = 0.0_r8 + frNdemand_npool_to_reproductiven_storage(k) = 0.0_r8 + end do + end if + + else + + frNdemand_npool_to_leafn = npool_to_leafn_demand / total_plant_Ndemand + frNdemand_npool_to_leafn_storage = npool_to_leafn_storage_demand / total_plant_Ndemand + frNdemand_npool_to_frootn = npool_to_frootn_demand / total_plant_Ndemand + frNdemand_npool_to_frootn_storage = npool_to_frootn_storage_demand / total_plant_Ndemand + if (woody(ivt) == 1._r8) then + + frNdemand_npool_to_livestemn = npool_to_livestemn_demand / total_plant_Ndemand + frNdemand_npool_to_livestemn_storage = npool_to_livestemn_storage_demand / total_plant_Ndemand + frNdemand_npool_to_deadstemn = npool_to_deadstemn_demand / total_plant_Ndemand + frNdemand_npool_to_deadstemn_storage = npool_to_deadstemn_storage_demand / total_plant_Ndemand + frNdemand_npool_to_livecrootn = npool_to_livecrootn_demand / total_plant_Ndemand + frNdemand_npool_to_livecrootn_storage = npool_to_livecrootn_storage_demand / total_plant_Ndemand + frNdemand_npool_to_deadcrootn = npool_to_deadcrootn_demand / total_plant_Ndemand + frNdemand_npool_to_deadcrootn_storage = npool_to_deadcrootn_storage_demand / total_plant_Ndemand + end if + if (ivt >= npcropmin) then ! skip 2 generic crops + + frNdemand_npool_to_livestemn = npool_to_livestemn_demand / total_plant_Ndemand + frNdemand_npool_to_livestemn_storage = npool_to_livestemn_storage_demand / total_plant_Ndemand + frNdemand_npool_to_deadstemn = npool_to_deadstemn_demand / total_plant_Ndemand + frNdemand_npool_to_deadstemn_storage = npool_to_deadstemn_storage_demand / total_plant_Ndemand + frNdemand_npool_to_livecrootn = npool_to_livecrootn_demand / total_plant_Ndemand + frNdemand_npool_to_livecrootn_storage = npool_to_livecrootn_storage_demand / total_plant_Ndemand + frNdemand_npool_to_deadcrootn = npool_to_deadcrootn_demand / total_plant_Ndemand + frNdemand_npool_to_deadcrootn_storage = npool_to_deadcrootn_storage_demand / total_plant_Ndemand + do k = 1, nrepr + frNdemand_npool_to_reproductiven(k) = & + npool_to_reproductiven_demand(k) / total_plant_Ndemand + frNdemand_npool_to_reproductiven_storage(k) = & + npool_to_reproductiven_storage_demand(k) / total_plant_Ndemand + end do + end if + + end if +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + + ! computing N allocation for different plant parts + ! allocating allocation to different plant parts in proportion to the fractional demand +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + npool_to_leafn = frNdemand_npool_to_leafn * npool / dt + npool_to_leafn_storage = frNdemand_npool_to_leafn_storage * npool / dt + npool_to_frootn = frNdemand_npool_to_frootn * npool / dt + npool_to_frootn_storage = frNdemand_npool_to_frootn_storage * npool / dt + if (woody(ivt) == 1._r8) then + npool_to_livestemn = frNdemand_npool_to_livestemn * npool / dt + npool_to_livestemn_storage = frNdemand_npool_to_livestemn_storage * npool / dt + npool_to_deadstemn = frNdemand_npool_to_deadstemn * npool / dt + npool_to_deadstemn_storage = frNdemand_npool_to_deadstemn_storage * npool / dt + npool_to_livecrootn = frNdemand_npool_to_livecrootn * npool / dt + npool_to_livecrootn_storage = frNdemand_npool_to_livecrootn_storage * npool / dt + npool_to_deadcrootn = frNdemand_npool_to_deadcrootn * npool / dt + npool_to_deadcrootn_storage = frNdemand_npool_to_deadcrootn_storage * npool / dt + end if + if (ivt >= npcropmin) then ! skip 2 generic crops + npool_to_livestemn = frNdemand_npool_to_livestemn * npool / dt + npool_to_livestemn_storage = frNdemand_npool_to_livestemn_storage * npool / dt + npool_to_deadstemn = frNdemand_npool_to_deadstemn * npool / dt + npool_to_deadstemn_storage = frNdemand_npool_to_deadstemn_storage * npool / dt + npool_to_livecrootn = frNdemand_npool_to_livecrootn * npool / dt + npool_to_livecrootn_storage = frNdemand_npool_to_livecrootn_storage * npool / dt + npool_to_deadcrootn = frNdemand_npool_to_deadcrootn * npool / dt + npool_to_deadcrootn_storage = frNdemand_npool_to_deadcrootn_storage * npool / dt + do k = 1, nrepr + npool_to_reproductiven(k) = frNdemand_npool_to_reproductiven(k) * npool / dt + npool_to_reproductiven_storage(k) = frNdemand_npool_to_reproductiven_storage(k) * npool / dt + end do + end if +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + end associate + end subroutine calc_npool_to_components_flexiblecn + + ! ----------------------------------------------------------------------- subroutine calc_plant_nutrient_demand(this, bounds, & num_p, filter_p, call_is_for_pcrop, & From 3cd89235667388abb54204f74de17cb7df26eb3c Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Tue, 19 Apr 2022 12:36:46 -0600 Subject: [PATCH 16/17] Introduce AgSys method for distributing npool to the components --- src/biogeochem/CNVegStateType.F90 | 20 ++- .../NutrientCompetitionFlexibleCNMod.F90 | 122 +++++++++++++++++- 2 files changed, 139 insertions(+), 3 deletions(-) diff --git a/src/biogeochem/CNVegStateType.F90 b/src/biogeochem/CNVegStateType.F90 index e39a334ab8..e30bb9c7e7 100644 --- a/src/biogeochem/CNVegStateType.F90 +++ b/src/biogeochem/CNVegStateType.F90 @@ -7,7 +7,7 @@ module CNVegStateType use abortutils , only : endrun use spmdMod , only : masterproc use clm_varpar , only : nlevsno, nlevgrnd, nlevlak, nlevsoi - use clm_varctl , only : use_cn, iulog, fsurdat, use_crop, use_cndv + use clm_varctl , only : use_cn, iulog, fsurdat, use_crop, use_cndv, use_crop_agsys use clm_varcon , only : spval, ispval, grlnd use landunit_varcon, only : istsoil, istcrop use LandunitType , only : lun @@ -39,10 +39,19 @@ module CNVegStateType real(r8) , pointer :: huigrain_patch (:) ! patch heat unit index needed to reach vegetative maturity real(r8) , pointer :: aleafi_patch (:) ! patch saved leaf allocation coefficient from phase 2 real(r8) , pointer :: astemi_patch (:) ! patch saved stem allocation coefficient from phase 2 + real(r8) , pointer :: aleaf_patch (:) ! patch leaf allocation coefficient real(r8) , pointer :: astem_patch (:) ! patch stem allocation coefficient real(r8) , pointer :: aroot_patch (:) ! patch root allocation coefficient real(r8) , pointer :: arepr_patch (:,:) ! patch reproductive allocation coefficient(s) + + ! The following nitrogen-based allocation fractions are just used with the AgSys + ! crop model (use_crop_agsys = .true.): + real(r8) , pointer :: aleaf_n_patch (:) ! patch leaf allocation coefficient for N + real(r8) , pointer :: astem_n_patch (:) ! patch stem allocation coefficient for N + real(r8) , pointer :: aroot_n_patch (:) ! patch root allocation coefficient for N + real(r8) , pointer :: arepr_n_patch (:,:) ! patch reproductive allocation coefficient(s) for N + real(r8) , pointer :: htmx_patch (:) ! patch max hgt attained by a crop during yr (m) integer , pointer :: peaklai_patch (:) ! patch 1: max allowed lai; 0: not at max @@ -202,10 +211,19 @@ subroutine InitAllocate(this, bounds) allocate(this%huigrain_patch (begp:endp)) ; this%huigrain_patch (:) = 0.0_r8 allocate(this%aleafi_patch (begp:endp)) ; this%aleafi_patch (:) = nan allocate(this%astemi_patch (begp:endp)) ; this%astemi_patch (:) = nan + allocate(this%aleaf_patch (begp:endp)) ; this%aleaf_patch (:) = nan allocate(this%astem_patch (begp:endp)) ; this%astem_patch (:) = nan allocate(this%aroot_patch (begp:endp)) ; this%aroot_patch (:) = nan allocate(this%arepr_patch (begp:endp, nrepr)) ; this%arepr_patch (:,:) = nan + + if (use_crop_agsys) then + allocate(this%aleaf_n_patch (begp:endp)) ; this%aleaf_n_patch (:) = nan + allocate(this%astem_n_patch (begp:endp)) ; this%astem_n_patch (:) = nan + allocate(this%aroot_n_patch (begp:endp)) ; this%aroot_n_patch (:) = nan + allocate(this%arepr_n_patch (begp:endp, nrepr)) ; this%arepr_n_patch (:,:) = nan + end if + allocate(this%htmx_patch (begp:endp)) ; this%htmx_patch (:) = 0.0_r8 allocate(this%peaklai_patch (begp:endp)) ; this%peaklai_patch (:) = 0 diff --git a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 index e9df168bcf..2133d010aa 100644 --- a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 +++ b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 @@ -63,6 +63,7 @@ module NutrientCompetitionFlexibleCNMod ! !PRIVATE MEMBER FUNCTIONS: private :: calc_npool_to_components_flexiblecn ! Calculate npool_to_* terms for a single patch using the FlexibleCN approach + private :: calc_npool_to_components_agsys ! Calculate npool_to_* terms for a single crop patch when using AgSys character(len=*), parameter, private :: sourcefile = & __FILE__ @@ -275,6 +276,9 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & astem => cnveg_state_inst%astem_patch , & ! Input: [real(r8) (:) ] stem allocation coefficient aroot => cnveg_state_inst%aroot_patch , & ! Input: [real(r8) (:) ] root allocation coefficient arepr => cnveg_state_inst%arepr_patch , & ! Input: [real(r8) (:,:) ] reproductive allocation coefficient(s) + ! aleaf_n, astem_n, aroot_n and arepr_n are also inputs when running with AgSys, + ! but they cannot be associated here because these pointers may be unallocated + ! if not running with AgSys c_allometry => cnveg_state_inst%c_allometry_patch , & ! Output: [real(r8) (:) ] C allocation index (DIM) annsum_npp => cnveg_carbonflux_inst%annsum_npp_patch , & ! Input: [real(r8) (:) ] annual sum of NPP, for wood allocation @@ -476,8 +480,34 @@ subroutine calc_plant_cn_alloc(this, bounds, num_soilp, filter_soilp, & cpool_to_gresp_storage(p) = gresp_storage * g1 * (1._r8 - g2) if (use_crop_agsys .and. ivt(p) >= npcropmin) then - ! FIXME(wjs, 2022-04-13) use AgSys-based method + call calc_npool_to_components_agsys( & + ! Inputs + npool = npool(p), & + fcur = fcur, & + f4 = f4, & + ! The following inputs cannot appear in the associate statement at the + ! top of the subroutine because these pointers may be unallocated if not + ! running with AgSys: + aleaf_n = cnveg_state_inst%aleaf_n_patch(p), & + astem_n = cnveg_state_inst%astem_n_patch(p), & + aroot_n = cnveg_state_inst%aroot_n_patch(p), & + arepr_n = cnveg_state_inst%arepr_n_patch(p,:), & + ! Outputs + npool_to_leafn = npool_to_leafn(p), & + npool_to_leafn_storage = npool_to_leafn_storage(p), & + npool_to_frootn = npool_to_frootn(p), & + npool_to_frootn_storage = npool_to_frootn_storage(p), & + npool_to_livestemn = npool_to_livestemn(p), & + npool_to_livestemn_storage = npool_to_livestemn_storage(p), & + npool_to_deadstemn = npool_to_deadstemn(p), & + npool_to_deadstemn_storage = npool_to_deadstemn_storage(p), & + npool_to_livecrootn = npool_to_livecrootn(p), & + npool_to_livecrootn_storage = npool_to_livecrootn_storage(p), & + npool_to_deadcrootn = npool_to_deadcrootn(p), & + npool_to_deadcrootn_storage = npool_to_deadcrootn_storage(p), & + npool_to_reproductiven = npool_to_reproductiven(p,:), & + npool_to_reproductiven_storage = npool_to_reproductiven_storage(p,:)) else call calc_npool_to_components_flexiblecn( & ! Inputs @@ -742,6 +772,7 @@ subroutine calc_npool_to_components_flexiblecn( & real(r8), intent(inout) :: npool_to_deadcrootn_storage real(r8), intent(inout) :: npool_to_reproductiven(:) real(r8), intent(inout) :: npool_to_reproductiven_storage(:) + ! ! !LOCAL VARIABLES: real(r8) :: cnl,cnfr,cnlw,cndw ! C:N ratios for leaf, fine root, and wood @@ -797,7 +828,6 @@ subroutine calc_npool_to_components_flexiblecn( & graincn => pftcon%graincn & ! Input: grain C:N (gC/gN) ) - ! set time steps dt = get_step_size_real() cnl = leafcn(ivt) @@ -985,6 +1015,94 @@ subroutine calc_npool_to_components_flexiblecn( & end associate end subroutine calc_npool_to_components_flexiblecn + !----------------------------------------------------------------------- + subroutine calc_npool_to_components_agsys( & + npool, fcur, f4, aleaf_n, astem_n, aroot_n, arepr_n, & + npool_to_leafn, npool_to_leafn_storage, & + npool_to_frootn, npool_to_frootn_storage, & + npool_to_livestemn, npool_to_livestemn_storage, & + npool_to_deadstemn, npool_to_deadstemn_storage, & + npool_to_livecrootn, npool_to_livecrootn_storage, & + npool_to_deadcrootn, npool_to_deadcrootn_storage, & + npool_to_reproductiven, npool_to_reproductiven_storage) + ! + ! !DESCRIPTION: + ! Calculate npool_to_* terms for a single crop patch when using AgSys + ! + ! Note that this assumes that there is no allocation to coarse roots + ! + ! !ARGUMENTS: + real(r8), intent(in) :: npool ! temporary plant N pool (gN/m2) + real(r8), intent(in) :: fcur ! fraction of current psn displayed as growth + real(r8), intent(in) :: f4 ! C allocation parameter - fraction of new wood that is live + real(r8), intent(in) :: aleaf_n ! leaf allocation coefficient for N + real(r8), intent(in) :: astem_n ! stem allocation coefficient for N + real(r8), intent(in) :: aroot_n ! root allocation coefficient for N + real(r8), intent(in) :: arepr_n(:) ! reproductive allocation coefficient(s) for N + + ! Each of the following output variables is in units of gN/m2/s + real(r8), intent(out) :: npool_to_leafn + real(r8), intent(out) :: npool_to_leafn_storage + real(r8), intent(out) :: npool_to_frootn + real(r8), intent(out) :: npool_to_frootn_storage + real(r8), intent(out) :: npool_to_livestemn + real(r8), intent(out) :: npool_to_livestemn_storage + real(r8), intent(out) :: npool_to_deadstemn + real(r8), intent(out) :: npool_to_deadstemn_storage + real(r8), intent(out) :: npool_to_livecrootn + real(r8), intent(out) :: npool_to_livecrootn_storage + real(r8), intent(out) :: npool_to_deadcrootn + real(r8), intent(out) :: npool_to_deadcrootn_storage + real(r8), intent(out) :: npool_to_reproductiven(:) + real(r8), intent(out) :: npool_to_reproductiven_storage(:) + + ! + ! !LOCAL VARIABLES: + real(r8) :: dt ! model time step + integer :: k + + character(len=*), parameter :: subname = 'calc_npool_to_components_agsys' + !----------------------------------------------------------------------- + + SHR_ASSERT_ALL_FL((ubound(arepr_n) == [nrepr]), sourcefile, __LINE__) + SHR_ASSERT_ALL_FL((ubound(npool_to_reproductiven) == [nrepr]), sourcefile, __LINE__) + SHR_ASSERT_ALL_FL((ubound(npool_to_reproductiven_storage) == [nrepr]), sourcefile, __LINE__) + + dt = get_step_size_real() + + npool_to_leafn = aleaf_n * fcur * npool / dt + npool_to_leafn_storage = aleaf_n * (1._r8 - fcur) * npool / dt + + npool_to_frootn = aroot_n * fcur * npool / dt + npool_to_frootn_storage = aroot_n * (1._r8 - fcur) * npool / dt + + npool_to_livestemn = astem_n * f4 * fcur * npool / dt + npool_to_livestemn_storage = astem_n * f4 * (1._r8 - fcur) * npool / dt + npool_to_deadstemn = astem_n * (1._r8 - f4) * fcur * npool / dt + npool_to_deadstemn_storage = astem_n * (1._r8 - f4) * (1._r8 - fcur) * npool / dt + + ! Assume no allocation to coarse roots for crops. If there *were* allocation to coarse + ! roots (via a non-zero croot_stem), we would have bigger issues with consistency + ! between AgSys's desired allocation and the actual C/N allocation: the way this + ! allocation is currently formulated, non-zero allocation to coarse roots implies that + ! things like aleaf and arepr aren't truly the fractional allocation to leaves and + ! reproductive organs: the actual allocation fractions end up being reduced somewhat + ! via a normalizing factor that differs from 1. + ! + ! It's not really necessary to explicitly set these to 0 every time step, but we do + ! it to try make it obvious that this will need to change if we ever want to have + ! non-zero allocation to coarse roots for crops. + npool_to_livecrootn = 0._r8 + npool_to_livecrootn_storage = 0._r8 + npool_to_deadcrootn = 0._r8 + npool_to_deadcrootn_storage = 0._r8 + + do k = 1, nrepr + npool_to_reproductiven(k) = arepr_n(k) * fcur * npool / dt + npool_to_reproductiven_storage(k) = arepr_n(k) * (1._r8 - fcur) * npool / dt + end do + + end subroutine calc_npool_to_components_agsys ! ----------------------------------------------------------------------- subroutine calc_plant_nutrient_demand(this, bounds, & From 6ec5166c87a7981f7dd16cca1b73ec2b38248aaf Mon Sep 17 00:00:00 2001 From: Bill Sacks Date: Fri, 29 Apr 2022 18:44:36 -0600 Subject: [PATCH 17/17] Update ChangeLog --- doc/ChangeLog | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++ doc/ChangeSum | 1 + 2 files changed, 79 insertions(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index ea2697281c..383ed4ecfa 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,82 @@ =============================================================== +Tag name: ctsm5.1.dev092 +Originator(s): sacks (Bill Sacks) +Date: Fri Apr 29 18:31:48 MDT 2022 +One-line Summary: Refactor NutrientCompetition / CNAllocation to provide hooks for AgSys + +Purpose and description of changes +---------------------------------- + +Major refactor of NutrientCompetition / CNAllocation to provide hooks +for AgSys crop model: separates the NutrientCompetition modules into +pieces based on (1) consolidating duplicate code between the Clm45 and +FlexibleCN versions, and (2) separating pieces that will vs. won't be +used for crop patches when running with the upcoming AgSys crop model. + +I have restored the old CNAllocationMod, with some of the +responsibilities that it used to have. (I'm not sure it's appropriate to +have the calculation of gpp and maint resp in CNAllocationMod, but I +left it there because it has always been combined with the allocation +code, including back when we had a separate CNAllocationMod.) + + +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.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- PASS + izumi ------- PASS + + +Answer changes +-------------- + +Changes answers relative to baseline: NO - though potential for answer +changes in unusual cases + + One change in this tag has the potential for answer changes in + unusual cases, even though no answer changes were observed in + testing: Previously, only the FlexibleCN code applied some logic that + changed crop allocation fractions during the grainfill period if + peaklai had been reached. I have changed this so that this logic is + applied with or without FlexibleCN (because I have moved this block + of code to the CNAllocation module, which is shared between the + FlexibleCN and non-FlexibleCN versions). I thought this would change + answers, but it appears not to, at least based on the tests in the + test suite as well as an extra 5-year test I did at f19 resolution + (ERS_Ly5.f19_g17.IHistClm45BgcCrop.cheyenne_intel.clm-cropMonthOutput). + It's possible that this changes answers in rare cases or with an + unusual combination of options that we don't test: specifically, it + might change answers for the atypical situation where you are running + with FUN but not FlexibleCN. (In this case, I believe this change is + the correct thing to do.) + + +Other details +------------- +Pull Requests that document the changes (include PR ids): +https://github.com/ESCOMP/CTSM/pull/1705 + +=============================================================== +=============================================================== Tag name: ctsm5.1.dev091 Originator(s): rgknox (Ryan Knox,rgknox@lbl.gov) Date: Fri Apr 22 14:11:50 EDT 2022 diff --git a/doc/ChangeSum b/doc/ChangeSum index 6f3b47ae7d..8280d7aefc 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.1.dev092 sacks 04/29/2022 Refactor NutrientCompetition / CNAllocation to provide hooks for AgSys ctsm5.1.dev091 rgknox 04/22/2022 clm decomp method is now passed to fates to enabled mimics coupling ctsm5.1.dev090 samrabin 03/31/2022 Fix misleading name of "gddplant" ctsm5.1.dev089 sacks 03/31/2022 For CLM45 apply peaklai to aleaf in grainfill