#include <misc.h> #include <preproc.h> module CNDVEstablishmentMod 1,2 #if (defined CNDV) !----------------------------------------------------------------------- !BOP ! ! !MODULE: CNDVEstablishmentMod ! ! !DESCRIPTION: ! Calculates establishment of new pfts ! Called once per year ! ! !USES: use shr_kind_mod, only: r8 => shr_kind_r8 use abortutils , only: endrun ! ! !PUBLIC TYPES: implicit none save ! ! !PUBLIC MEMBER FUNCTIONS: public :: Establishment ! ! !REVISION HISTORY: ! Module created by Mariana Vertenstein ! !EOP !----------------------------------------------------------------------- contains !----------------------------------------------------------------------- !BOP ! ! !IROUTINE: Establishment ! ! !INTERFACE: subroutine Establishment(lbg, ubg, lbp, ubp) 1,7 ! ! !DESCRIPTION: ! Calculates establishment of new pfts ! Called once per year ! ! !USES: use clmtype use clm_varpar , only : numpft use clm_varcon , only : istsoil use clm_varctl , only : iulog use pftvarcon , only : noveg, nc3_arctic_grass use shr_const_mod, only : SHR_CONST_CDAY, SHR_CONST_PI, SHR_CONST_TKFRZ ! ! !ARGUMENTS: implicit none integer , intent(in) :: lbg, ubg ! gridcell bounds integer , intent(in) :: lbp, ubp ! pft bounds ! ! !CALLED FROM: ! subroutine dv in module CNDVMod ! ! !REVISION HISTORY: ! Author: Sam Levis (adapted from Stephen Sitch's LPJ subr. establishment) ! 3/4/02, Peter Thornton: Migrated to new data structures. ! 10/05 , Sam Levis: adapted to work with CN ! 09/07 , Sam Levis: as 10/05 but with current CN ! ! !LOCAL VARIABLES: ! ! local pointers to implicit in arguments ! logical , pointer :: pftmayexist(:) ! exclude seasonal decid pfts from tropics [1=true, 0=false] integer , pointer :: plandunit(:) ! landunit of corresponding pft integer , pointer :: pgridcell(:) ! gridcell of corresponding pft integer , pointer :: ltype(:) ! landunit type for corresponding pft real(r8), pointer :: tmomin20(:) ! 20-yr running mean of tmomin real(r8), pointer :: agdd20(:) ! 20-yr running mean of agdd real(r8), pointer :: agddtw(:) ! accumulated growing degree days above twmax real(r8), pointer :: prec365(:) ! 365-day running mean of tot. precipitation real(r8), pointer :: slatop(:) !specific leaf area at top of canopy, projected area basis [m^2/gC] real(r8), pointer :: dsladlai(:) !dSLA/dLAI, projected area basis [m^2/gC] real(r8), pointer :: woody(:) ! ecophys const - woody pft or not real(r8), pointer :: crownarea_max(:) ! ecophys const - tree maximum crown area [m2] real(r8), pointer :: twmax(:) ! ecophys const - upper limit of temperature of the warmest month real(r8), pointer :: reinickerp(:) ! ecophys const - parameter in allometric equation real(r8), pointer :: dwood(:) ! ecophys const - wood density (gC/m3) real(r8), pointer :: allom1(:) ! ecophys const - parameter in allometric real(r8), pointer :: tcmin(:) ! ecophys const - minimum coldest monthly mean temperature real(r8), pointer :: tcmax(:) ! ecophys const - maximum coldest monthly mean temperature real(r8), pointer :: gddmin(:) ! ecophys const - minimum growing degree days (at or above 5 C) real(r8), pointer :: leafcmax(:) ! (gC/m2) ann max leaf C real(r8), pointer :: deadstemc(:) ! (gC/m2) dead stem C real(r8), pointer :: annsum_npp(:) ! annual sum NPP (gC/m2/yr) real(r8), pointer :: annsum_litfall(:) ! annual sum litfall (gC/m2/yr) ! ! local pointers to implicit in/out arguments ! integer , pointer :: ivt(:) ! vegetation type for this pft logical , pointer :: present(:) ! true=> PFT present in patch real(r8), pointer :: nind(:) ! number of individuals (#/m**2) ! ! local pointers to implicit out arguments ! real(r8), pointer :: fpcgrid(:) ! foliar projective cover on gridcell (fraction) real(r8), pointer :: crownarea(:) ! area that each individual tree takes up (m^2) real(r8), pointer :: greffic(:) ! lpj's growth efficiency real(r8), pointer :: heatstress(:) ! !EOP ! ! !OTHER LOCAL VARIABLES: ! integer :: g,l,p,m ! indices integer :: fn, filterg(ubg-lbg+1) ! local gridcell filter for error check ! ! gridcell level variables ! integer :: ngrass(lbg:ubg) ! counter integer :: npft_estab(lbg:ubg) ! counter real(r8) :: fpc_tree_total(lbg:ubg) ! total fractional cover of trees in vegetated portion of gridcell real(r8) :: fpc_total(lbg:ubg) ! old-total fractional vegetated portion of gridcell (without bare ground) real(r8) :: fpc_total_new(lbg:ubg) ! new-total fractional vegetated portion of gridcell (without bare ground) ! ! pft level variables ! logical :: survive(lbp:ubp) ! true=>pft survives logical :: estab(lbp:ubp) ! true=>pft is established real(r8) :: dstemc(lbp:ubp) ! local copy of deadstemc ! ! local and temporary variables or parameters ! real(r8) :: taper ! ratio of height:radius_breast_height (tree allometry) real(r8) :: estab_rate !establishment rate real(r8) :: estab_grid !establishment rate on grid cell real(r8) :: fpcgridtemp ! temporary real(r8) :: stemdiam ! stem diameter real(r8) :: stocking ! #stems / ha (stocking density) real(r8) :: lai_ind ! LAI per individual real(r8) :: lm_ind !leaf carbon (gC/ind) real(r8) :: fpc_ind !individual foliage projective cover real(r8):: bm_delta real(r8), parameter :: ramp_agddtw = 300.0 ! minimum individual density for persistence of PFT (indiv/m2) ! real(r8), parameter :: nind_min = 1.0e-10_r8 ! ! minimum precip. for establishment (mm/s) ! real(r8), parameter :: prec_min_estab = 100._r8/(365._r8*SHR_CONST_CDAY) ! ! maximum sapling establishment rate (indiv/m2) ! real(r8), parameter :: estab_max = 0.24_r8 !----------------------------------------------------------------------- ! Assign local pointers to derived type members (gridcell-level) agdd20 => clm3%g%gdgvs%agdd20 tmomin20 => clm3%g%gdgvs%tmomin20 ! Assign local pointers to derived type members (landunit-level) ltype => clm3%g%l%itype ! Assign local pointers to derived type members (pft-level) ivt => clm3%g%l%c%p%itype pgridcell => clm3%g%l%c%p%gridcell plandunit => clm3%g%l%c%p%landunit present => clm3%g%l%c%p%pdgvs%present nind => clm3%g%l%c%p%pdgvs%nind fpcgrid => clm3%g%l%c%p%pdgvs%fpcgrid crownarea => clm3%g%l%c%p%pdgvs%crownarea greffic => clm3%g%l%c%p%pdgvs%greffic heatstress => clm3%g%l%c%p%pdgvs%heatstress annsum_npp => clm3%g%l%c%p%pepv%annsum_npp annsum_litfall => clm3%g%l%c%p%pepv%annsum_litfall prec365 => clm3%g%l%c%p%pdgvs%prec365 agddtw => clm3%g%l%c%p%pdgvs%agddtw pftmayexist => clm3%g%l%c%p%pdgvs%pftmayexist ! Assign local pointers to derived type members (vegetation types) crownarea_max => dgv_pftcon%crownarea_max twmax => dgv_pftcon%twmax reinickerp => dgv_pftcon%reinickerp allom1 => dgv_pftcon%allom1 tcmax => dgv_pftcon%tcmax tcmin => dgv_pftcon%tcmin gddmin => dgv_pftcon%gddmin leafcmax => clm3%g%l%c%p%pcs%leafcmax deadstemc => clm3%g%l%c%p%pcs%deadstemc slatop => pftcon%slatop dsladlai => pftcon%dsladlai dwood => pftcon%dwood woody => pftcon%woody ! ********************************************************************** ! Slevis version of LPJ's subr. bioclim ! Limits based on 20-year running averages of coldest-month mean ! temperature and growing degree days (5 degree base). ! For SURVIVAL, coldest month temperature and GDD should be ! at least as high as PFT-specific limits. ! For REGENERATION, PFT must be able to survive AND coldest month ! temperature should be no higher than a PFT-specific limit. ! ********************************************************************** taper = 200._r8 ! make a global constant as with dwood (lpj's wooddens) ! Initialize gridcell-level metrics do g = lbg, ubg ngrass(g) = 0 npft_estab(g) = 0 fpc_tree_total(g) = 0._r8 fpc_total(g) = 0._r8 fpc_total_new(g) = 0._r8 end do do p = lbp, ubp g = pgridcell(p) ! Set the presence of pft for this gridcell if (nind(p) == 0._r8) present(p) = .false. if (.not. present(p)) then nind(p) = 0._r8 fpcgrid(p) = 0._r8 end if survive(p) = .false. estab(p) = .false. dstemc(p) = deadstemc(p) end do ! Must go thru all 16 pfts and decide which can/cannot establish or survive ! Determine present, survive, estab. Note: Even if tmomin20>tcmax, crops ! and 2nd boreal summergreen tree cannot exist (see ! EcosystemDynini) because this model cannot simulate such pfts, yet. ! Note - agddtw is only defined at the pft level and has now been moved ! to an if-statement below to determine establishment of boreal trees do p = lbp, ubp g = pgridcell(p) if (tmomin20(g) >= tcmin(ivt(p)) + SHR_CONST_TKFRZ ) then if (tmomin20(g) <= tcmax(ivt(p)) + SHR_CONST_TKFRZ .and. agdd20(g) >= gddmin(ivt(p))) then estab(p) = .true. end if survive(p) = .true. ! seasonal decid. pfts that would have occurred in regions without ! short winter day lengths (see CNPhenology) if (.not. pftmayexist(p)) then survive(p) = .false. estab(p) = .false. pftmayexist(p) = .true. end if end if end do do p = lbp, ubp g = pgridcell(p) l = plandunit(p) ! Case 1 -- pft ceases to exist -kill pfts not adapted to current climate if (present(p) .and. (.not. survive(p) .or. nind(p)<nind_min)) then present(p) = .false. fpcgrid(p) = 0._r8 nind(p) = 0._r8 end if ! Case 2 -- pft begins to exist - introduce newly "adapted" pfts if (ltype(l) == istsoil) then if (.not. present(p) .and. prec365(p) >= prec_min_estab .and. estab(p)) then if (twmax(ivt(p)) > 999._r8 .or. agddtw(p) == 0._r8) then present(p) = .true. nind(p) = 0._r8 ! lpj starts with fpcgrid=0 and calculates ! seed fpcgrid from the carbon of saplings; ! with CN we need the seed fpcgrid up front ! to scale seed leafc to lm_ind to get fpcgrid; ! sounds circular; also seed fpcgrid depends on sla, ! so theoretically need diff value for each pft;slevis fpcgrid(p) = 0.000844_r8 if (woody(ivt(p)) < 1._r8) then fpcgrid(p) = 0.05_r8 end if ! Seed carbon for newly established pfts ! Equiv. to pleaf=1 & pstor=1 set in subr pftwt_cnbal (slevis) ! ***Dangerous*** to hardwire leafcmax here; find alternative! ! Consider just assigning nind and fpcgrid for newly ! established pfts instead of entering the circular procedure ! outlined in the paragraph above leafcmax(p) = 1._r8 if (dstemc(p) <= 0._r8) dstemc(p) = 0.1_r8 end if ! conditions required for establishment end if ! conditions required for establishment end if ! if soil ! Case 3 -- some pfts continue to exist (no change) and some pfts ! continue to not exist (no change). Do nothing for this case. end do ! Sapling and grass establishment ! Calculate total woody FPC, FPC increment and grass cover (= crown area) ! Calculate total woody FPC and number of woody PFTs present and able to establish do p = lbp, ubp g = pgridcell(p) if (present(p)) then if (woody(ivt(p)) == 1._r8) then fpc_tree_total(g) = fpc_tree_total(g) + fpcgrid(p) if (estab(p)) npft_estab(g) = npft_estab(g) + 1 else if (woody(ivt(p)) < 1._r8 .and. ivt(p) > noveg) then !grass ngrass(g) = ngrass(g) + 1 end if end if end do ! Above grid-level establishment counters are required for the next steps. do p = lbp, ubp g = pgridcell(p) if (present(p) .and. woody(ivt(p)) == 1._r8 .and. estab(p)) then ! Calculate establishment rate over available space, per tree PFT ! Max establishment rate reduced by shading as tree FPC approaches 1 ! Total establishment rate partitioned equally among regenerating woody PFTs estab_rate = estab_max * (1._r8-exp(5._r8*(fpc_tree_total(g)-1._r8))) / real(npft_estab(g)) ! Calculate grid-level establishment rate per woody PFT ! Space available for woody PFT establishment is fraction of grid cell ! not currently occupied by woody PFTs estab_grid = estab_rate * (1._r8-fpc_tree_total(g)) ! Add new saplings to current population nind(p) = nind(p) + estab_grid !slevis: lpj's lm_ind was the max leaf mass for the year; !now lm_ind is the max leaf mass for the year calculated in CNFire !except when a pft is newly established (nind==0); then lm_ind !is assigned a leafcmax above lm_ind = leafcmax(p) * fpcgrid(p) / nind(p) ! nind>0 for sure if (fpcgrid(p) > 0._r8 .and. nind(p) > 0._r8) then stocking = nind(p)/fpcgrid(p) !#ind/m2 nat veg area -> #ind/m2 pft area ! stemdiam derived here from cn's formula for htop found in ! CNVegStructUpdate and cn's assumption stemdiam=2*htop/taper ! this derivation neglects upper htop limit enforced elsewhere stemdiam = (24._r8 * dstemc(p) / (SHR_CONST_PI * stocking * dwood(ivt(p)) * taper))**(1._r8/3._r8) else stemdiam = 0._r8 end if crownarea(p) = min(crownarea_max(ivt(p)), allom1(ivt(p))*stemdiam**reinickerp(ivt(p))) ! Eqn D (now also in Light; need here for 1st yr when pfts haven't established, yet) ! Update LAI and FPC if (crownarea(p) > 0._r8) then if (dsladlai(ivt(p)) > 0._r8) then ! make lai_ind >= 0.001 to avoid killing plants at this stage lai_ind = max(0.001_r8,((exp(lm_ind*dsladlai(ivt(p)) + log(slatop(ivt(p)))) - & slatop(ivt(p)))/dsladlai(ivt(p))) / crownarea(p)) else ! currently redundant because dsladlai=0 for grasses only lai_ind = lm_ind * slatop(ivt(p)) / crownarea(p) ! lpj's formula end if else lai_ind = 0._r8 end if fpc_ind = 1._r8 - exp(-0.5_r8*lai_ind) fpcgrid(p) = crownarea(p) * nind(p) * fpc_ind end if ! add new saplings block if (present(p) .and. woody(ivt(p)) == 1._r8) then fpc_total_new(g) = fpc_total_new(g) + fpcgrid(p) end if end do ! close loop to update fpc_total_new ! Adjustments- don't allow trees to exceed 95% of vegetated landunit do p = lbp, ubp g = pgridcell(p) if (fpc_total_new(g) > 0.95_r8) then if (woody(ivt(p)) == 1._r8 .and. present(p)) then nind(p) = nind(p) * 0.95_r8 / fpc_total_new(g) fpcgrid(p) = fpcgrid(p) * 0.95_r8 / fpc_total_new(g) end if fpc_total(g) = 0.95_r8 else fpc_total(g) = fpc_total_new(g) end if end do ! Section for grasses. Grasses can establish in non-vegetated areas do p = lbp, ubp g = pgridcell(p) if (present(p) .and. woody(ivt(p)) < 1._r8) then if (leafcmax(p) <= 0._r8 .or. fpcgrid(p) <= 0._r8 ) then present(p) = .false. nind(p) = 0._r8 else nind(p) = 1._r8 ! in case these grasses just established crownarea(p) = 1._r8 lm_ind = leafcmax(p) * fpcgrid(p) / nind(p) if (dsladlai(ivt(p)) > 0._r8) then lai_ind = max(0.001_r8,((exp(lm_ind*dsladlai(ivt(p)) + log(slatop(ivt(p)))) - & slatop(ivt(p)))/dsladlai(ivt(p))) / crownarea(p)) else ! 'if' is currently redundant b/c dsladlai=0 for grasses only lai_ind = lm_ind * slatop(ivt(p)) / crownarea(p) end if fpc_ind = 1._r8 - exp(-0.5_r8*lai_ind) fpcgrid(p) = crownarea(p) * nind(p) * fpc_ind fpc_total(g) = fpc_total(g) + fpcgrid(p) end if end if #ifdef DISTURB present(p) = .false. nind(p) = 0._r8 fpcgrid(p) = 0._r8 if (ivt(p) == noveg) fpcgrid(p) = 1._r8 fpc_total(g) = 1._r8 #endif end do ! end of pft-loop ! Adjustment of fpc_total > 1 due to grasses (ivt >= nc3_arctic_grass) do p = lbp, ubp g = pgridcell(p) if (fpc_total(g) > 1._r8) then if (ivt(p) >= nc3_arctic_grass .and. fpcgrid(p) > 0._r8) then fpcgridtemp = fpcgrid(p) fpcgrid(p) = max(0._r8, fpcgrid(p) - (fpc_total(g)-1._r8)) fpc_total(g) = fpc_total(g) - fpcgridtemp + fpcgrid(p) end if end if ! Remove tiny fpcgrid amounts if (fpcgrid(p) < 1.e-15_r8) then fpc_total(g) = fpc_total(g) - fpcgrid(p) fpcgrid(p) = 0._r8 present(p) = .false. nind(p) = 0._r8 end if ! Set the fpcgrid for bare ground if there is bare ground in ! vegetated landunit and pft is bare ground so that everything ! can add up to one. if (fpc_total(g) < 1._r8 .and. ivt(p) == noveg) then fpcgrid(p) = 1._r8 - fpc_total(g) fpc_total(g) = fpc_total(g) + fpcgrid(p) end if end do ! Annual calculations used hourly in GapMortality ! Ultimately may wish to place in separate subroutine... do p = lbp, ubp g = pgridcell(p) ! Stress mortality from lpj's subr Mortality if (woody(ivt(p)) == 1._r8 .and. nind(p) > 0._r8 .and. & leafcmax(p) > 0._r8 .and. fpcgrid(p) > 0._r8) then if (twmax(ivt(p)) < 999._r8) then heatstress(p) = max(0._r8, min(1._r8, agddtw(p) / ramp_agddtw)) else heatstress(p) = 0._r8 end if ! Net individual living biomass increment ! NB: lpj's turnover not exactly same as cn's litfall: ! lpj's sap->heartwood turnover not included in litfall (slevis) bm_delta = max(0._r8, annsum_npp(p) - annsum_litfall(p)) lm_ind = leafcmax(p) * fpcgrid(p) / nind(p) ! Growth efficiency (net biomass increment per unit leaf area) if (dsladlai(ivt(p)) > 0._r8) then greffic(p) = bm_delta / (max(0.001_r8, & ( ( exp(lm_ind*dsladlai(ivt(p)) + log(slatop(ivt(p)))) & - slatop(ivt(p)) ) / dsladlai(ivt(p)) ))) else ! currently redundant because dsladlai=0 for grasses only greffic(p) = bm_delta / (lm_ind * slatop(ivt(p))) end if else greffic(p) = 0. heatstress(p) = 0. end if end do ! Check for error in establishment fn = 0 do g = lbg, ubg if (abs(fpc_total(g) - 1._r8) > 1.e-6) then fn = fn + 1 filterg(fn) = g end if end do ! Just print out the first error if (fn > 0) then g = filterg(1) write(6,*) 'Error in Establishment: fpc_total =',fpc_total(g), ' at gridcell ',g call endrun end if end subroutine Establishment #endif end module CNDVEstablishmentMod