#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