#include <misc.h>
#include <preproc.h>


module CNVegStructUpdateMod 3,1

#ifdef CN
!-----------------------------------------------------------------------
!BOP
!
! !MODULE: CNVegStructUpdateMod
!
! !DESCRIPTION:
! Module for vegetation structure updates (LAI, SAI, htop, hbot)
!
! !USES:
    use shr_kind_mod, only: r8 => shr_kind_r8
    implicit none
    save
    private
! !PUBLIC MEMBER FUNCTIONS:
    public :: CNVegStructUpdate
!
! !REVISION HISTORY:
! 4/23/2004: Created by Peter Thornton
!
!EOP
!-----------------------------------------------------------------------

contains

!-----------------------------------------------------------------------
!BOP
!
! !IROUTINE: CNVegStructUpdate
!
! !INTERFACE:

subroutine CNVegStructUpdate(num_soilp, filter_soilp) 1,6
!
! !DESCRIPTION:
! On the radiation time step, use C state variables and epc to diagnose
! vegetation structure (LAI, SAI, height)
!
! !USES:
   use clmtype
   use clm_atmlnd   , only: clm_a2l
   use pftvarcon    , only: noveg, ncorn, nwheat, nbrdlf_evr_shrub, nbrdlf_dcd_brl_shrub
   use shr_const_mod, only: SHR_CONST_PI
   use clm_time_manager , only : get_rad_step_size
!
! !ARGUMENTS:
   implicit none
   integer, intent(in) :: num_soilp                 ! number of column soil points in pft filter
   integer, intent(in) :: filter_soilp(:)   ! pft filter for soil points
!
! !CALLED FROM:
! subroutine CNEcosystemDyn
!
! !REVISION HISTORY:
! 10/28/03: Created by Peter Thornton
! 2/29/08, David Lawrence: revised snow burial fraction for short vegetation
!
! !LOCAL VARIABLES:
! local pointers to implicit in scalars
!
#if (defined CNDV)
   real(r8), pointer :: allom2(:)     ! ecophys const
   real(r8), pointer :: allom3(:)     ! ecophys const
   real(r8), pointer :: nind(:)       ! number of individuals (#/m**2)
   real(r8), pointer :: fpcgrid(:)    ! fractional area of pft (pft area/nat veg area)
#endif
   integer , pointer :: ivt(:)        ! pft vegetation type
   integer , pointer :: pcolumn(:)    ! column index associated with each pft
   integer , pointer :: pgridcell(:)  ! pft's gridcell index
   real(r8), pointer :: snowdp(:)     ! snow height (m)
   real(r8), pointer :: leafc(:)      ! (kgC/m2) leaf C
   real(r8), pointer :: deadstemc(:)  ! (kgC/m2) dead stem C
   real(r8), pointer :: woody(:)      !binary flag for woody lifeform (1=woody, 0=not woody)
   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 :: z0mr(:)       !ratio of momentum roughness length to canopy top height (-)
   real(r8), pointer :: displar(:)    !ratio of displacement height to canopy top height (-)
   real(r8), pointer :: forc_hgt_u_pft(:) ! observational height of wind at pft-level [m]
   real(r8), pointer :: dwood(:)      ! density of wood (kgC/m^3)
!
! local pointers to implicit in/out scalars
!
   integer , pointer :: frac_veg_nosno_alb(:) ! frac of vegetation not covered by snow [-]
   real(r8), pointer :: tlai(:) !one-sided leaf area index, no burying by snow
   real(r8), pointer :: tsai(:) !one-sided stem area index, no burying by snow
   real(r8), pointer :: htop(:) !canopy top (m)
   real(r8), pointer :: hbot(:) !canopy bottom (m)
   real(r8), pointer :: elai(:)     ! one-sided leaf area index with burying by snow
   real(r8), pointer :: esai(:)     ! one-sided stem area index with burying by snow
!
! local pointers to implicit out scalars
!
!
! !OTHER LOCAL VARIABLES:
   integer :: p,c,g        !indices
   integer :: fp           !lake filter indices
   real(r8):: taper        ! ratio of height:radius_breast_height (tree allometry)
   real(r8):: stocking     ! #stems / ha (stocking density)
   real(r8):: ol           ! thickness of canopy layer covered by snow (m)
   real(r8):: fb           ! fraction of canopy layer covered by snow
   real(r8) :: tlai_old    ! for use in Zeng tsai formula
   real(r8) :: tsai_old    ! for use in Zeng tsai formula
   real(r8) :: tsai_min    ! PFT derived minimum tsai
   real(r8) :: tsai_alpha  ! monthly decay rate of tsai
   real(r8) dt             ! radiation time step (sec)

   real(r8), parameter :: dtsmonth = 2592000._r8 ! number of seconds in a 30 day month (60x60x24x30)
!EOP
!-----------------------------------------------------------------------
! tsai formula from Zeng et. al. 2002, Journal of Climate, p1835
!
! tsai(p) = max( tsai_alpha(ivt(p))*tsai_old + max(tlai_old-tlai(p),0_r8), tsai_min(ivt(p)) )
! notes:
! * RHS tsai & tlai are from previous timestep
! * should create tsai_alpha(ivt(p)) & tsai_min(ivt(p)) in pftvarcon.F90 - slevis
! * all non-crop pfts use same values:
!   crop    tsai_alpha,tsai_min = 0.0,0.1
!   noncrop tsai_alpha,tsai_min = 0.5,1.0  (includes bare soil and urban)
!-------------------------------------------------------------------------------

   ! assign local pointers to derived type arrays (in)
#if (defined CNDV)
    allom2                         => dgv_pftcon%allom2
    allom3                         => dgv_pftcon%allom3
    nind                           => clm3%g%l%c%p%pdgvs%nind
    fpcgrid                        => clm3%g%l%c%p%pdgvs%fpcgrid
#endif
    ivt                            => clm3%g%l%c%p%itype
    pcolumn                        => clm3%g%l%c%p%column
    pgridcell                      => clm3%g%l%c%p%gridcell
    leafc                          => clm3%g%l%c%p%pcs%leafc
    deadstemc                      => clm3%g%l%c%p%pcs%deadstemc
    snowdp                         => clm3%g%l%c%cps%snowdp
    woody                          => pftcon%woody
    slatop                         => pftcon%slatop
    dsladlai                       => pftcon%dsladlai
    z0mr                           => pftcon%z0mr
    displar                        => pftcon%displar
    dwood                          => pftcon%dwood

   ! assign local pointers to derived type arrays (out)
    tlai                           => clm3%g%l%c%p%pps%tlai
    tsai                           => clm3%g%l%c%p%pps%tsai
    htop                           => clm3%g%l%c%p%pps%htop
    hbot                           => clm3%g%l%c%p%pps%hbot
    elai                           => clm3%g%l%c%p%pps%elai
    esai                           => clm3%g%l%c%p%pps%esai
    frac_veg_nosno_alb             => clm3%g%l%c%p%pps%frac_veg_nosno_alb
    forc_hgt_u_pft                 => clm3%g%l%c%p%pps%forc_hgt_u_pft

   dt = real( get_rad_step_size(), r8 )

   ! constant allometric parameters
   taper = 200._r8
   stocking = 1000._r8

   ! convert from stems/ha -> stems/m^2
   stocking = stocking / 10000._r8

   ! pft loop
   do fp = 1,num_soilp
      p = filter_soilp(fp)
      c = pcolumn(p)
      g = pgridcell(p)

      if (ivt(p) /= noveg) then

          tlai_old = tlai(p) ! n-1 value
          tsai_old = tsai(p) ! n-1 value

          ! update the leaf area index based on leafC and SLA
          ! Eq 3 from Thornton and Zimmerman, 2007, J Clim, 20, 3902-3923. 
          if (dsladlai(ivt(p)) > 0._r8) then
             tlai(p) = (slatop(ivt(p))*(exp(leafc(p)*dsladlai(ivt(p))) - 1._r8))/dsladlai(ivt(p))
          else
             tlai(p) = slatop(ivt(p)) * leafc(p)
          end if
          tlai(p) = max(0._r8, tlai(p))

          ! update the stem area index and height based on LAI, stem mass, and veg type.
          ! With the exception of htop for woody vegetation, this follows the DGVM logic.

          ! tsai formula from Zeng et. al. 2002, Journal of Climate, p1835 (see notes)
          ! Assumes doalb time step .eq. CLM time step, SAI min and monthly decay factor
          ! alpha are set by PFT, and alpha is scaled to CLM time step by multiplying by
          ! dt and dividing by dtsmonth (seconds in average 30 day month)
          ! tsai_min scaled by 0.5 to match MODIS satellite derived values
          if (ivt(p) == ncorn .or. ivt(p) == nwheat ) then    ! crops (corn, wheat in CLM)

             tsai_alpha = 1.0_r8-1.0_r8*dt/dtsmonth
             tsai_min = 0.1_r8
          else
             tsai_alpha = 1.0_r8-0.5_r8*dt/dtsmonth
             tsai_min = 1.0_r8
          end if
          tsai_min = tsai_min * 0.5_r8
          tsai(p) = max(tsai_alpha*tsai_old+max(tlai_old-tlai(p),0._r8),tsai_min)

          if (woody(ivt(p)) == 1._r8) then

             ! trees and shrubs

             ! if shrubs have a squat taper 
             if (ivt(p) >= nbrdlf_evr_shrub .and. ivt(p) <= nbrdlf_dcd_brl_shrub) then
                taper = 10._r8
             ! otherwise have a tall taper
             else
                taper = 200._r8
             end if

             ! trees and shrubs for now have a very simple allometry, with hard-wired
             ! stem taper (height:radius) and hard-wired stocking density (#individuals/area)
#if (defined CNDV)
             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
                htop(p) = allom2(ivt(p)) * ( (24._r8 * deadstemc(p) / &
                  (SHR_CONST_PI * stocking * dwood(ivt(p)) * taper))**(1._r8/3._r8) )**allom3(ivt(p)) ! lpj's htop w/ cn's stemdiam
             else
                htop(p) = 0._r8
             end if
#else
                htop(p) = ((3._r8 * deadstemc(p) * taper * taper)/ &
                  (SHR_CONST_PI * stocking * dwood(ivt(p))))**(1._r8/3._r8)
#endif

             ! Peter Thornton, 5/3/2004
             ! Adding test to keep htop from getting too close to forcing height for windspeed
             ! Also added for grass, below, although it is not likely to ever be an issue.
             htop(p) = min(htop(p),(forc_hgt_u_pft(p)/(displar(ivt(p))+z0mr(ivt(p))))-3._r8)

             ! Peter Thornton, 8/11/2004
             ! Adding constraint to keep htop from going to 0.0.
             ! This becomes an issue when fire mortality is pushing deadstemc
             ! to 0.0.
             htop(p) = max(htop(p), 0.01_r8)

             hbot(p) = max(0._r8, min(3._r8, htop(p)-1._r8))

          else
             ! grasses

             ! height for grasses depends only on LAI
             htop(p) = max(0.25_r8, tlai(p) * 0.25_r8)

             htop(p) = min(htop(p),(forc_hgt_u_pft(p)/(displar(ivt(p))+z0mr(ivt(p))))-3._r8)

             ! Peter Thornton, 8/11/2004
             ! Adding constraint to keep htop from going to 0.0.
             htop(p) = max(htop(p), 0.01_r8)

             hbot(p) = max(0.0_r8, min(0.05_r8, htop(p)-0.20_r8))
          end if

      else
          tlai(p) = 0._r8
          tsai(p) = 0._r8
          htop(p) = 0._r8
          hbot(p) = 0._r8
      end if
      
      ! adjust lai and sai for burying by snow. 

      ! snow burial fraction for short vegetation (e.g. grasses) as in
      ! Wang and Zeng, 2007.
      if (ivt(p) > noveg .and. ivt(p) <= nbrdlf_dcd_brl_shrub ) then
         ol = min( max(snowdp(c)-hbot(p), 0._r8), htop(p)-hbot(p))
         fb = 1._r8 - ol / max(1.e-06_r8, htop(p)-hbot(p))
      else
         fb = 1._r8 - max(min(snowdp(c),0.2_r8),0._r8)/0.2_r8   ! 0.2m is assumed
              !depth of snow required for complete burial of grasses
      endif

      elai(p) = max(tlai(p)*fb, 0.0_r8)
      esai(p) = max(tsai(p)*fb, 0.0_r8)

      ! Fraction of vegetation free of snow
      if ((elai(p) + esai(p)) > 0._r8) then
         frac_veg_nosno_alb(p) = 1
      else
         frac_veg_nosno_alb(p) = 0
      end if

   end do

end subroutine CNVegStructUpdate
!-----------------------------------------------------------------------
#endif

end module CNVegStructUpdateMod