! SUMMA - Structure for Unifying Multiple Modeling Alternatives
! Copyright (C) 2014-2020 NCAR/RAL; University of Saskatchewan; University of Washington
!
! This file is part of SUMMA
!
! For more information see: http://www.ral.ucar.edu/projects/summa
!
! This program is free software: you can redistribute it and/or modify
! it under the terms of the GNU General Public License as published by
! the Free Software Foundation, either version 3 of the License, or
! (at your option) any later version.
!
! This program is distributed in the hope that it will be useful,
! but WITHOUT ANY WARRANTY; without even the implied warranty of
! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
! GNU General Public License for more details.
!
! You should have received a copy of the GNU General Public License
! along with this program.  If not, see <http://www.gnu.org/licenses/>.

module vegPhenlgy_module

! data types
USE nrtype

! global variables
USE globalData,only:urbanVegCategory    ! vegetation category for urban areas
!USE globalData,only:fracJulday          ! fractional julian days since the start of year
! USE globalData,only:yearLength          ! number of days in the current year

! provide access to the derived types to define the data structures
USE data_types,only:&
                    var_i,            & ! data vector (i4b)
                    var_d,            & ! data vector (dp)
                    var_dlength,      & ! data vector with variable length dimension (dp)
                    model_options       ! defines the model decisions

! named variables defining elements in the data structures
USE var_lookup,only:iLookTYPE,iLookATTR,iLookPARAM,iLookDIAG,iLookPROG  ! named variables for structure elements
USE var_lookup,only:iLookDECISIONS                                      ! named variables for elements of the decision structure

! look-up values for the boundary conditions
USE mDecisions_module,only:      &
 prescribedHead,                 &      ! prescribed head (volumetric liquid water content for mixed form of Richards' eqn)
 prescribedTemp,                 &      ! prescribed temperature
 zeroFlux                               ! zero flux

! look-up values for the choice of canopy shortwave radiation method
USE mDecisions_module,only:      &
 noah_mp,                        &      ! full Noah-MP implementation (including albedo)
 CLM_2stream,                    &      ! CLM 2-stream model (see CLM documentation)
 UEB_2stream,                    &      ! UEB 2-stream model (Mahat and Tarboton, WRR 2011)
 NL_scatter,                     &      ! Simplified method Nijssen and Lettenmaier (JGR 1999)
 BeersLaw                               ! Beer's Law (as implemented in VIC)

! privacy
implicit none
private
public::vegPhenlgy
! algorithmic parameters
real(dp),parameter     :: valueMissing=-9999._dp  ! missing value, used when diagnostic or state variables are undefined
real(dp),parameter     :: verySmall=1.e-6_dp      ! used as an additive constant to check if substantial difference among real numbers
contains


 ! ************************************************************************************************
 ! public subroutine vegPhenlgy: compute vegetation phenology
 ! ************************************************************************************************
 subroutine vegPhenlgy(&
                       ! input/output: data structures
                       model_decisions,             & ! intent(in):    model decisions
                       type_data,                   & ! intent(in):    type of vegetation and soil
                       attr_data,                   & ! intent(in):    spatial attributes
                       mpar_data,                   & ! intent(in):    model parameters
                       prog_data,                   & ! intent(in):    prognostic variables for a local HRU
                       diag_data,                   & ! intent(inout): diagnostic variables for a local HRU
                       ! output
                       computeVegFlux,              & ! intent(out): flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow)
                       canopyDepth,                 & ! intent(out): canopy depth (m)
                       exposedVAI,                  & ! intent(out): exposed vegetation area index (LAI + SAI)
                       fracJulDay,                  &
                       yearLength,                  &
                       err,message)                   ! intent(out): error control
 ! -------------------------------------------------------------------------------------------------
 ! modules
 USE NOAHMP_ROUTINES,only:phenology         ! determine vegetation phenology
 implicit none
 ! -------------------------------------------------------------------------------------------------
 ! input/output
 type(model_options),intent(in)  :: model_decisions(:)  ! model decisions
 type(var_i),intent(in)          :: type_data           ! type of vegetation and soil
 type(var_d),intent(in)          :: attr_data           ! spatial attributes
 type(var_dlength),intent(in)    :: mpar_data           ! model parameters
 type(var_dlength),intent(in)    :: prog_data           ! prognostic variables for a local HRU
 type(var_dlength),intent(inout) :: diag_data           ! diagnostic variables for a local HRU
 ! output
 logical(lgt),intent(out)        :: computeVegFlux      ! flag to indicate if we are computing fluxes over vegetation (.false. means veg is buried with snow)
 real(dp),intent(out)            :: canopyDepth         ! canopy depth (m)
 real(dp),intent(out)            :: exposedVAI          ! exposed vegetation area index (LAI + SAI)
 real(dp),intent(inout)          :: fracJulDay
 integer(i4b),intent(inout)      :: yearLength
 integer(i4b),intent(out)        :: err                 ! error code
 character(*),intent(out)        :: message             ! error message
 ! -------------------------------------------------------------------------------------------------
 ! local
 real(dp)                 :: notUsed_heightCanopyTop    ! height of the top of the canopy layer (m)
 real(dp)                 :: heightAboveSnow            ! height top of canopy is above the snow surface (m)
 ! initialize error control
 err=0; message="vegPhenlgy/"
 ! ----------------------------------------------------------------------------------------------------------------------------------
 ! associate variables in the data structure
 associate(&

 ! input: model decisions
 ix_bcUpprTdyn                   => model_decisions(iLookDECISIONS%bcUpprTdyn)%iDecision,      & ! intent(in): [i4b] choice of upper boundary condition for thermodynamics
 ix_bcUpprSoiH                   => model_decisions(iLookDECISIONS%bcUpprSoiH)%iDecision,      & ! intent(in): [i4b] index of method used for the upper boundary condition for soil hydrology

 ! local attributes
 vegTypeIndex                    => type_data%var(iLookTYPE%vegTypeIndex),                     & ! intent(in): [i4b] vegetation type index
 latitude                        => attr_data%var(iLookATTR%latitude),                         & ! intent(in): [dp] latitude

 ! model state variables
 scalarSnowDepth                 => prog_data%var(iLookPROG%scalarSnowDepth)%dat(1),           & ! intent(in):    [dp] snow depth on the ground surface (m)
 scalarCanopyTemp                => prog_data%var(iLookPROG%scalarCanopyTemp)%dat(1),          & ! intent(in):    [dp] temperature of the vegetation canopy at the start of the sub-step (K)

 ! diagnostic variables and parameters (input)
 heightCanopyTop                 => mpar_data%var(iLookPARAM%heightCanopyTop)%dat(1),          & ! intent(in):    [dp] height of the top of the canopy layer (m)
 heightCanopyBottom              => mpar_data%var(iLookPARAM%heightCanopyBottom)%dat(1),       & ! intent(in):    [dp] height of the bottom of the canopy layer (m)
 scalarRootZoneTemp              => diag_data%var(iLookDIAG%scalarRootZoneTemp)%dat(1),        & ! intent(in):    [dp] root zone temperature (K)

 ! diagnostic variables and parameters (input/output)
 scalarLAI                       => diag_data%var(iLookDIAG%scalarLAI)%dat(1),                 & ! intent(inout): [dp] one-sided leaf area index (m2 m-2)
 scalarSAI                       => diag_data%var(iLookDIAG%scalarSAI)%dat(1),                 & ! intent(inout): [dp] one-sided stem area index (m2 m-2)

 ! diagnostic variables and parameters (output)
 scalarExposedLAI                => diag_data%var(iLookDIAG%scalarExposedLAI)%dat(1),          & ! intent(out): [dp] exposed leaf area index after burial by snow (m2 m-2)
 scalarExposedSAI                => diag_data%var(iLookDIAG%scalarExposedSAI)%dat(1),          & ! intent(out): [dp] exposed stem area index after burial by snow (m2 m-2)
 scalarGrowingSeasonIndex        => diag_data%var(iLookDIAG%scalarGrowingSeasonIndex)%dat(1)   & ! intent(out): [dp] growing season index (0=off, 1=on)

 ) ! associate variables in data structure
 ! ----------------------------------------------------------------------------------------------------------------------------------

 ! check if we have isolated the snow-soil domain (used in test cases)
 if(ix_bcUpprTdyn == prescribedTemp .or. ix_bcUpprTdyn == zeroFlux .or. ix_bcUpprSoiH == prescribedHead)then

  ! isolated snow-soil domain: do not compute fluxes over vegetation
  computeVegFlux = .false.

  ! set vegetation phenology variables to missing
  scalarLAI                = valueMissing    ! one-sided leaf area index (m2 m-2)
  scalarSAI                = valueMissing    ! one-sided stem area index (m2 m-2)
  scalarExposedLAI         = valueMissing    ! exposed leaf area index after burial by snow (m2 m-2)
  scalarExposedSAI         = valueMissing    ! exposed stem area index after burial by snow (m2 m-2)
  scalarGrowingSeasonIndex = valueMissing    ! growing season index (0=off, 1=on)
  exposedVAI               = valueMissing    ! exposed vegetation area index (m2 m-2)
  canopyDepth              = valueMissing    ! canopy depth (m)
  heightAboveSnow          = valueMissing    ! height top of canopy is above the snow surface (m)

 ! compute vegetation phenology (checks for complete burial of vegetation)
 else

  ! determine vegetation phenology
  ! NOTE: recomputing phenology every sub-step accounts for changes in exposed vegetation associated with changes in snow depth
  call phenology(&
                 ! input
                 vegTypeIndex,                & ! intent(in): vegetation type index
                 urbanVegCategory,            & ! intent(in): vegetation category for urban areas
                 scalarSnowDepth,             & ! intent(in): snow depth on the ground surface (m)
                 scalarCanopyTemp,            & ! intent(in): temperature of the vegetation canopy at the start of the sub-step (K)
                 latitude,                    & ! intent(in): latitude
                 yearLength,                  & ! intent(in): number of days in the current year
                 fracJulDay,                  & ! intent(in): fractional julian days since the start of year
                 scalarLAI,                   & ! intent(inout): one-sided leaf area index (m2 m-2)
                 scalarSAI,                   & ! intent(inout): one-sided stem area index (m2 m-2)
                 scalarRootZoneTemp,          & ! intent(in): root zone temperature (K)
                 ! output
                 notUsed_heightCanopyTop,     & ! intent(out): height of the top of the canopy layer (m)
                 scalarExposedLAI,            & ! intent(out): exposed leaf area index after burial by snow (m2 m-2)
                 scalarExposedSAI,            & ! intent(out): exposed stem area index after burial by snow (m2 m-2)
                 scalarGrowingSeasonIndex     ) ! intent(out): growing season index (0=off, 1=on)

  ! determine additional phenological variables
  exposedVAI      = scalarExposedLAI + scalarExposedSAI   ! exposed vegetation area index (m2 m-2)
  canopyDepth     = heightCanopyTop - heightCanopyBottom  ! canopy depth (m)
  heightAboveSnow = heightCanopyTop - scalarSnowDepth     ! height top of canopy is above the snow surface (m)

  ! determine if need to include vegetation in the energy flux routines
  computeVegFlux  = (exposedVAI > 0.05_dp .and. heightAboveSnow > 0.05_dp)
  !write(*,'(a,1x,i2,1x,L1,1x,10(f12.5,1x))') 'vegTypeIndex, computeVegFlux, heightCanopyTop, heightAboveSnow, scalarSnowDepth = ', &
  !                                            vegTypeIndex, computeVegFlux, heightCanopyTop, heightAboveSnow, scalarSnowDepth

 end if  ! (check if the snow-soil column is isolated)

 ! end association to variables in the data structure
 end associate

 end subroutine vegPhenlgy


end module vegPhenlgy_module