module FatesLitterMod

  ! -------------------------------------------------------------------------------------
  ! This module contains methods and type definitions for all things litter.
  ! "litter" means all organic material that is no longer associated with a live plant.
  ! Also, in FATES we only track "un-fragmented" and "un-decomposed" litter. This
  ! is a decision of pragmatism, as FATES is not a soil decomposition model, yet FATES
  ! does need to retain litter for fire calculations.  Therefore, we retain
  ! undecomposed litter for a period of time in FATES, until it fragments and is passed
  ! to another model to handle deocomposition.
  ! 
  ! This encompasses:  1) "Coarse Woody Debris"
  !                    2) fine materials leaves, roots etc 
  !                       (sometimes exclusively refered to as litter)
  !                    3) Reproductive materials (seeds, nuts, fruits)
  !
  ! Important point:   THESE POOLS DO NOT CONTAIN DECOMPOSING MATTER !!!!!
  !
  ! Another Important Point: We track the fine litter by its "decomposability" pool.
  !                          However, we don't actually apply any differential
  !                          turnover rates based on these pools, we are just
  !                          differentiating, tracking and preserving them to be 
  !                          passed in the correct partitions to the BGC model. 
  !                          Their partitions are a PFT parameter.
  ! 
  ! -------------------------------------------------------------------------------------


   ! To-do:
   ! 8) In CWD_IN, add the flux diagnostics, then remove the
   !    patch level rate in the history code

   
   use FatesConstantsMod, only : r8 => fates_r8
   use FatesConstantsMod, only : i4 => fates_int
   use FatesConstantsMod, only : nearzero
   use FatesConstantsMod, only : calloc_abs_error
   use FatesConstantsMod, only : fates_unset_r8

   use FatesGlobals     , only : endrun => fates_endrun
   use FatesGlobals     , only : fates_log 
   use shr_log_mod      , only : errMsg => shr_log_errMsg

   implicit none
   private


   integer, public, parameter :: ncwd  = 4    ! number of coarse woody debris pools 
                                              ! (twig,s branch,l branch, trunk)

   integer, public, parameter :: ndcmpy = 3   ! number of "decomposability" pools in
                                              ! fines (lignin, cellulose, labile)

   integer, public, parameter :: ilabile     = 1   ! Array index for labile portion
   integer, public, parameter :: icellulose  = 2   ! Array index for cellulose portion
   integer, public, parameter :: ilignin     = 3   ! Array index for the lignin portion


   type, public ::  litter_type

      
      ! This object is allocated for each element (C, N, P, etc) that we wish to track.

      integer              :: element_id            ! This element ID should
                                                    ! be associated with the element
                                                    ! types listed in parteh/PRTGenericMod.F90

      ! ---------------------------------------------------------------------------------
      ! Prognostic variables (litter and coarse woody debris)
      ! Note that we do not track the fines (leaf/fine-root debris) by PFT. We track them 
      ! by their decomposing pools (i.e. chemical fraction).  This is the same dimensioning
      ! that gets passed back to the external BGC model, and saves a lot of space.
      ! ---------------------------------------------------------------------------------


      real(r8)             :: ag_cwd(ncwd)          ! above ground coarse wood debris (cwd)         [kg/m2]
      real(r8),allocatable :: bg_cwd(:,:)           ! below ground coarse wood debris (cwd x soil)  [kg/m2]
      real(r8),allocatable :: leaf_fines(:)         ! above ground leaf litter (dcmpy)              [kg/m2]
      real(r8),allocatable :: root_fines(:,:)       ! below ground fine root litter (dcmpy x soil)  [kg/m2]
      
      real(r8),allocatable :: seed(:)               ! the seed pool (viable)    (pft) [kg/m2]
      real(r8),allocatable :: seed_germ(:)          ! the germinated seed pool  (pft) [kg/m2]


      ! ---------------------------------------------------------------------------------
      ! Fluxes in - dying trees / seed rain  (does not include disturbance fluxes)
      ! ---------------------------------------------------------------------------------

      real(r8)             ::  ag_cwd_in(ncwd)      ! (cwd)        [kg/m2/day]
      real(r8),allocatable ::  bg_cwd_in(:,:)       ! (cwd x soil) [kg/m2/day]
      real(r8),allocatable ::  leaf_fines_in(:)     ! (dcmpy)       [kg/m2/day]
      real(r8),allocatable ::  root_fines_in(:,:)   ! (dcmpy x soil [kg/m2/day]

      real(r8),allocatable ::  seed_in_local(:)     ! (pft)        [kg/m2/day] (from local sources)
      real(r8),allocatable ::  seed_in_extern(:)    ! (pft)        [kg/m2/day] (from outside cell)

                                                    
      ! ---------------------------------------------------------------------------------
      ! Fluxes out - fragmentation, seed decay (does not include disturbance)
      ! ---------------------------------------------------------------------------------

      real(r8)             ::  ag_cwd_frag(ncwd)    ! above ground cwd fragmentation flux   [kg/m2/day]
      real(r8),allocatable ::  bg_cwd_frag(:,:)     ! below ground cwd fragmentation flux   [kg/m2/day]
      real(r8),allocatable ::  leaf_fines_frag(:)   ! above ground fines fragmentation flux [kg/m2/day]
      real(r8),allocatable ::  root_fines_frag(:,:) ! kg/m2/day

      real(r8), allocatable :: seed_decay(:)      ! decay of viable seeds to litter     [kg/m2/day]
      real(r8), allocatable :: seed_germ_decay(:) ! decay of germinated seeds to litter [kg/m2/day]
      real(r8), allocatable :: seed_germ_in(:)    ! flux from viable to germinated seed [kg/m2/day]

    contains
      
      procedure,non_overridable :: InitAllocate
      procedure,non_overridable :: DeallocateLitt
      procedure,non_overridable :: InitConditions
      procedure,non_overridable :: FuseLitter
      procedure,non_overridable :: CopyLitter
      procedure,non_overridable :: ZeroFlux
      procedure,non_overridable :: GetTotalLitterMass
      
   end type litter_type

   ! Part 3: Public extended types

   character(len=*), parameter, private :: sourcefile = __FILE__

contains

  subroutine FuseLitter(this,self_area,donor_area,donor_litt)

    ! -----------------------------------------------------------------------------------
    ! The litter pools are all area normalized.  This routine
    ! will use area weighting to determine the resulting
    ! litter density per area of all the pools. Essentially
    ! summing up the total mass by multiplying each component
    ! area, and then normalizing by the new total.
    ! -----------------------------------------------------------------------------------


    class(litter_type) :: this
    real(r8),intent(in)           :: self_area
    real(r8),intent(in)           :: donor_area
    type(litter_type),intent(in)  :: donor_litt

    ! locals
    integer  :: nlevsoil        ! number of soil layers
    integer  :: c               ! cwd index
    integer  :: pft             ! pft index
    integer  :: ilyr            ! soil layer index
    integer  :: dcmpy           ! dcmpyical pool index
    integer  :: npft            ! number of PFTs
    real(r8) :: self_weight     ! weighting of the recieving litter pool
    real(r8) :: donor_weight    ! weighting of the donating litter pool
    

    nlevsoil = size(this%bg_cwd,dim=2)
    npft     = size(this%seed,dim=1)

    self_weight  = self_area /(donor_area+self_area)
    donor_weight = 1._r8 - self_weight

    
    do c=1,ncwd
       this%ag_cwd(c)      = this%ag_cwd(c) *self_weight +  &
                             donor_litt%ag_cwd(c) * donor_weight
       this%ag_cwd_in(c)   = this%ag_cwd_in(c) *self_weight + &
                             donor_litt%ag_cwd_in(c) * donor_weight
       this%ag_cwd_frag(c) = this%ag_cwd_frag(c) *self_weight + &
                             donor_litt%ag_cwd_frag(c) * donor_weight
       do ilyr = 1,nlevsoil
          this%bg_cwd(c,ilyr)      = this%bg_cwd(c,ilyr) * self_weight + &
                                     donor_litt%bg_cwd(c,ilyr) * donor_weight
          this%bg_cwd_in(c,ilyr)   = this%bg_cwd_in(c,ilyr) * self_weight + &
                                     donor_litt%bg_cwd_in(c,ilyr) * donor_weight
          this%bg_cwd_frag(c,ilyr) = this%bg_cwd_frag(c,ilyr) * self_weight + &
                                     donor_litt%bg_cwd_frag(c,ilyr) * donor_weight
       end do

    end do

    
    do pft=1,npft
       
       this%seed(pft)            = this%seed(pft) * self_weight + &
                                   donor_litt%seed(pft) * donor_weight
       this%seed_germ(pft)       = this%seed_germ(pft) * self_weight + &
                                   donor_litt%seed_germ(pft) * donor_weight
       
       this%seed_in_local(pft)   = this%seed_in_local(pft) * self_weight + &
                                   donor_litt%seed_in_local(pft) * donor_weight
       this%seed_in_extern(pft)  = this%seed_in_extern(pft) * self_weight + &
                                   donor_litt%seed_in_extern(pft) * donor_weight
       
       this%seed_decay(pft)      = this%seed_decay(pft) * self_weight + &
                                   donor_litt%seed_decay(pft) * donor_weight
       this%seed_germ_decay(pft) = this%seed_germ_decay(pft) * self_weight + &
                                   donor_litt%seed_germ_decay(pft) * donor_weight
       this%seed_germ_in(pft)    = this%seed_germ_in(pft) * self_weight + &
                                   donor_litt%seed_germ_in(pft) * donor_weight
   end do


   do dcmpy=1,ndcmpy

       this%leaf_fines(dcmpy)      = this%leaf_fines(dcmpy) * self_weight + &
                                   donor_litt%leaf_fines(dcmpy) * donor_weight
       this%leaf_fines_in(dcmpy)   = this%leaf_fines_in(dcmpy) * self_weight + &
                                   donor_litt%leaf_fines_in(dcmpy) * donor_weight
       this%leaf_fines_frag(dcmpy) = this%leaf_fines_frag(dcmpy) * self_weight + &
                                   donor_litt%leaf_fines_frag(dcmpy) * donor_weight

       do ilyr=1,nlevsoil
           this%root_fines(dcmpy,ilyr)     = this%root_fines(dcmpy,ilyr) * self_weight + &
                                            donor_litt%root_fines(dcmpy,ilyr) * donor_weight
          this%root_fines_in(dcmpy,ilyr)   = this%root_fines_in(dcmpy,ilyr) * self_weight + &
                                            donor_litt%root_fines_in(dcmpy,ilyr) * donor_weight
          this%root_fines_frag(dcmpy,ilyr) = this%root_fines_frag(dcmpy,ilyr) * self_weight + &
                                            donor_litt%root_fines_frag(dcmpy,ilyr) * donor_weight
       end do
    end do

    return
  end subroutine FuseLitter

  ! =====================================================================================

  subroutine CopyLitter(this,donor_litt)

    ! This might not need to ever be called.  When a new patch is created
    ! from disturbance, litter initialization is handled elsewhere (EDPatchDynamics)


    class(litter_type) :: this
    type(litter_type),intent(in) :: donor_litt


    this%ag_cwd(:)      = donor_litt%ag_cwd(:)
    this%ag_cwd_in(:)   = donor_litt%ag_cwd_in(:)
    this%ag_cwd_frag(:) = donor_litt%ag_cwd_frag(:)
    
    this%bg_cwd(:,:)      = donor_litt%bg_cwd(:,:)
    this%bg_cwd_in(:,:)   = donor_litt%bg_cwd_in(:,:)
    this%bg_cwd_frag(:,:) = donor_litt%bg_cwd_frag(:,:)

    this%leaf_fines(:)    = donor_litt%leaf_fines(:)
    this%seed(:)          = donor_litt%seed(:)
    this%seed_germ(:)     = donor_litt%seed_germ(:)
    this%leaf_fines_in(:) = donor_litt%leaf_fines_in(:)
    this%seed_in_local(:) = donor_litt%seed_in_local(:)
    
    this%seed_in_extern(:)    = donor_litt%seed_in_extern(:)
    this%leaf_fines_frag(:)   = donor_litt%leaf_fines_frag(:)
    
    this%seed_decay(:)        = donor_litt%seed_decay(:)
    this%seed_germ_decay(:)   = donor_litt%seed_germ_decay(:)
    this%seed_germ_in(:)      = donor_litt%seed_germ_in(:)
    this%root_fines(:,:)      = donor_litt%root_fines(:,:)
    this%root_fines_in(:,:)   = donor_litt%root_fines_in(:,:)
    this%root_fines_frag(:,:) = donor_litt%root_fines_frag(:,:)

    return
  end subroutine CopyLitter

  ! =====================================================================================

  subroutine InitAllocate(this,numpft,numlevsoil,element_id)

    class(litter_type) :: this
    integer,intent(in)  :: numpft     ! number of plant functional types
    integer,intent(in)  :: numlevsoil ! number of soil layers
    integer,intent(in)  :: element_id ! PARTEH compliant element index

    this%element_id = element_id

    allocate(this%bg_cwd_in(ncwd,numlevsoil))
    allocate(this%bg_cwd(ncwd,numlevsoil))
    allocate(this%bg_cwd_frag(ncwd,numlevsoil))

    allocate(this%leaf_fines(ndcmpy))
    allocate(this%root_fines(ndcmpy,numlevsoil))
    allocate(this%leaf_fines_in(ndcmpy))
    allocate(this%root_fines_in(ndcmpy,numlevsoil))
    allocate(this%leaf_fines_frag(ndcmpy))
    allocate(this%root_fines_frag(ndcmpy,numlevsoil))

    allocate(this%seed_in_local(numpft))
    allocate(this%seed_in_extern(numpft))
    allocate(this%seed(numpft))
    allocate(this%seed_germ(numpft))
    allocate(this%seed_germ_in(numpft))
    allocate(this%seed_germ_decay(numpft))
    allocate(this%seed_decay(numpft))

    ! Initialize everything to a nonsense flag
    this%ag_cwd(:)            = fates_unset_r8
    this%bg_cwd(:,:)          = fates_unset_r8
    this%leaf_fines(:)        = fates_unset_r8
    this%root_fines(:,:)      = fates_unset_r8
    this%seed(:)              = fates_unset_r8
    this%seed_germ(:)         = fates_unset_r8

    this%ag_cwd_in(:)         = fates_unset_r8
    this%bg_cwd_in(:,:)       = fates_unset_r8
    this%leaf_fines_in(:)     = fates_unset_r8
    this%root_fines_in(:,:)   = fates_unset_r8
    this%seed_in_local(:)     = fates_unset_r8
    this%seed_in_extern(:)    = fates_unset_r8

    this%ag_cwd_frag(:)       = fates_unset_r8
    this%bg_cwd_frag(:,:)     = fates_unset_r8
    this%leaf_fines_frag(:)   = fates_unset_r8
    this%root_fines_frag(:,:) = fates_unset_r8

    this%seed_decay(:)        = fates_unset_r8
    this%seed_germ_decay(:)   = fates_unset_r8
    this%seed_germ_in(:)      = fates_unset_r8

    return
  end subroutine InitAllocate

  ! =====================================================================================

  subroutine InitConditions(this, &
                            init_leaf_fines, &
                            init_root_fines, &
                            init_ag_cwd,     &
                            init_bg_cwd,     &
                            init_seed,       &
                            init_seed_germ)
    
    ! This procedure initialized litter pools.  This does not allow initialization
    ! of each soil layer depth, or decomposability pool. This is meant for
    ! uniform initializations. This is used for cold-starts, but is not
    ! used in restarts.  For patch fusion, this routine is used to zero the pools
    ! before accumulating debris from multiple patches.


    class(litter_type) :: this
    real(r8),intent(in) :: init_leaf_fines
    real(r8),intent(in) :: init_root_fines
    real(r8),intent(in) :: init_ag_cwd
    real(r8),intent(in) :: init_bg_cwd
    real(r8),intent(in) :: init_seed
    real(r8),intent(in) :: init_seed_germ
    
    this%ag_cwd(:)              = init_ag_cwd
    this%bg_cwd(:,:)            = init_bg_cwd
    this%leaf_fines(:)          = init_leaf_fines
    this%root_fines(:,:)        = init_root_fines
    this%seed(:)                = init_seed
    this%seed_germ(:)           = init_seed_germ

    return
  end subroutine InitConditions
  
  ! =====================================================================================
  
  subroutine DeallocateLitt(this)
    
    class(litter_type) :: this

    deallocate(this%bg_cwd)
    deallocate(this%leaf_fines)
    deallocate(this%root_fines)
    deallocate(this%seed)
    deallocate(this%seed_germ)

    deallocate(this%bg_cwd_in)
    deallocate(this%leaf_fines_in)
    deallocate(this%root_fines_in)
    deallocate(this%seed_in_local)
    deallocate(this%seed_in_extern)
    
    deallocate(this%bg_cwd_frag)
    deallocate(this%leaf_fines_frag)
    deallocate(this%root_fines_frag)
   
    deallocate(this%seed_decay)
    deallocate(this%seed_germ_decay)
    deallocate(this%seed_germ_in)

    return
  end subroutine DeallocateLitt
  
  ! =====================================================================================

  subroutine ZeroFlux(this)
    
    class(litter_type) :: this
    
    this%ag_cwd_in(:)         = 0._r8
    this%bg_cwd_in(:,:)       = 0._r8
    this%leaf_fines_in(:)     = 0._r8
    this%root_fines_in(:,:)   = 0._r8
    this%seed_in_local(:)     = 0._r8
    this%seed_in_extern(:)    = 0._r8

    this%ag_cwd_frag(:)       = 0._r8
    this%bg_cwd_frag(:,:)     = 0._r8
    this%leaf_fines_frag(:)   = 0._r8
    this%root_fines_frag(:,:) = 0._r8
    
    this%seed_germ_in(:)      = 0._r8
    this%seed_decay(:)        = 0._r8
    this%seed_germ_decay(:)   = 0._r8


    return
  end subroutine ZeroFlux

  ! ===================================================

  function GetTotalLitterMass(this) result(total_mass)
    
    class(litter_type) :: this
    real(r8) :: total_mass
    
    total_mass = sum(this%ag_cwd) + &
                 sum(this%bg_cwd) + &
                 sum(this%root_fines) + &
                 sum(this%leaf_fines) + & 
                 sum(this%seed) + & 
                 sum(this%seed_germ)
    
    return
  end function GetTotalLitterMass

  
end module FatesLitterMod
