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


module CNDVMod 1,4

#if (defined CNDV)

!-----------------------------------------------------------------------
!BOP
!
! !MODULE: CNDVMod
!
! !DESCRIPTION:
! Module containing routines to drive the annual dynamic vegetation
! that works with CN, reset related variables,
! and initialize/reset time invariant variables
!
! !USES:
  use shr_kind_mod        , only : r8 => shr_kind_r8
  use clm_varpar          , only : maxpatch_pft, lsmlon, lsmlat, nlevsoi
  use abortutils          , only : endrun
  use CNVegStructUpdateMod, only : CNVegStructUpdate
!
! !PUBLIC TYPES:
  implicit none
  private
  save
!
! !PUBLIC MEMBER FUNCTIONS:
  public dv                 ! Drives the annual dynamic vegetation that
                            ! works with CN
  public histCNDV           ! Output CNDV history file
!
! !REVISION HISTORY:
! Module modified by Sam Levis from similar module DGVMMod
! created by Mariana Vertenstein
!
!EOP
!-----------------------------------------------------------------------

contains

!-----------------------------------------------------------------------
!BOP
!
! !IROUTINE: dv
!
! !INTERFACE:

  subroutine dv(lbg, ubg, lbp, ubp, num_natvegp, filter_natvegp, kyr) 1,6
!
! !DESCRIPTION:
! Drives the annual dynamic vegetation that works with CN
!
! !USES:
    use clmtype
    use CNDVLightMod        , only : Light
    use CNDVEstablishmentMod, only : Establishment
!
! !ARGUMENTS:
    implicit none
    integer, intent(in) :: lbg, ubg       ! gridcell bounds
    integer, intent(in) :: lbp, ubp       ! pft bounds
    integer, intent(inout) :: num_natvegp ! number of naturally-vegetated
                                          ! pfts in filter
    integer, intent(inout) :: filter_natvegp(ubp-lbp+1) ! filter for
                                          ! naturally-vegetated pfts
    integer, intent(in) :: kyr            ! used in routine climate20 below
!
! !CALLED FROM:
!
! !REVISION HISTORY:
! Author: Sam Levis
!
! !LOCAL VARIABLES:
!
! local pointers to implicit in arguments
!
   integer , pointer :: mxy(:)         ! pft m index (for laixy(i,j,m),etc.)
   integer , pointer :: pgridcell(:)   ! gridcell of corresponding pft
   real(r8), pointer :: fpcgrid(:)     ! foliar projective cover on gridcell (fraction)
   real(r8), pointer :: agdd(:)        ! accumulated growing degree days above 5
   real(r8), pointer :: t_mo_min(:)    ! annual min of t_mo (Kelvin)
!
! local pointers to implicit inout arguments
!
   real(r8), pointer :: tmomin20(:)         ! 20-yr running mean of tmomin
   real(r8), pointer :: agdd20(:)           ! 20-yr running mean of agdd
!
!EOP
!
! !LOCAL VARIABLES:
    integer  :: g,p                    ! indices
!-----------------------------------------------------------------------

    ! 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 (pft-level)

    mxy       => clm3%g%l%c%p%mxy
    pgridcell => clm3%g%l%c%p%gridcell
    fpcgrid   => clm3%g%l%c%p%pdgvs%fpcgrid
    t_mo_min  => clm3%g%l%c%p%pdgvs%t_mo_min
    agdd      => clm3%g%l%c%p%pdgvs%agdd

    ! *************************************************************************
    ! S. Levis version of LPJ's routine climate20: 'Returns' tmomin20 & agdd20
    ! for use in routine bioclim, which I have placed in routine Establishment
    ! Instead of 20-yr running mean of coldest monthly temperature,
    ! use 20-yr running mean of minimum 10-day running mean
    ! *************************************************************************

    do p = lbp,ubp
       g = pgridcell(p)
       if (kyr == 2) then ! slevis: add ".and. start_type==arb_ic" here?
          tmomin20(g) = t_mo_min(p) ! NO, b/c want to be able to start dgvm
          agdd20(g) = agdd(p)       ! w/ clmi file from non-dgvm simulation
       end if
       tmomin20(g) = (19._r8 * tmomin20(g) + t_mo_min(p)) / 20._r8
       agdd20(g)   = (19._r8 * agdd20(g)   + agdd(p)    ) / 20._r8
    end do

    ! Rebuild filter of present natually-vegetated pfts after Kill()

    call BuildNatVegFilter(lbp, ubp, num_natvegp, filter_natvegp)

    ! Returns fpcgrid and nind

    call Light(lbg, ubg, lbp, ubp, num_natvegp, filter_natvegp)

    ! Returns updated fpcgrid, nind, crownarea, and present. Due to updated
    ! present, we do not use the natveg filter in this subroutine.

    call Establishment(lbg, ubg, lbp, ubp)

    ! Reset dgvm variables needed in next yr (too few to keep subr. dvreset)

    do p = lbp,ubp
       clm3%g%l%c%p%pcs%leafcmax(p) = 0._r8
       clm3%g%l%c%p%pdgvs%t_mo_min(p) = 1.0e+36_r8
    end do
  end subroutine dv

!-----------------------------------------------------------------------
!BOP
!
! !IROUTINE: histCNDV
!
! !INTERFACE:

  subroutine histCNDV() 1,85
!
! !DESCRIPTION:
! Create CNDV history file
!
! !USES:
    use clmtype
    use ncdio
    use decompMod       , only : get_proc_bounds, get_proc_global, ldecomp
    use clm_varpar      , only : lsmlon, lsmlat, maxpatch_pft
    use domainMod       , only : ldomain,llatlon
    use clm_varctl      , only : caseid, ctitle, finidat, fsurdat, fpftcon, &
                                 frivinp_rtm
    use clm_varcon      , only : spval
    use clm_time_manager, only : get_ref_date, get_nstep, get_curr_date, &
                                 get_curr_time
    use fileutils       , only : set_filename, putfil, get_filename
    use shr_sys_mod     , only : shr_sys_getenv
    use spmdMod         , only : masterproc
    use shr_const_mod   , only : SHR_CONST_CDAY
!
! !ARGUMENTS:
    implicit none
!
! !CALLED FROM:
!
! !REVISION HISTORY:
! Author: Sam Levis
!
! !LOCAL VARIABLES:
!
! local pointers to implicit in arguments
!
   logical , pointer :: ifspecial(:)        ! true=>landunit is not vegetated (landunit-level)
   integer , pointer :: pgridcell(:)        ! gridcell index of corresponding pft (pft-level)
   integer , pointer :: plandunit(:)        ! landunit index of corresponding pft (pft-level)
   integer , pointer :: mxy(:)              ! pft m index (for laixy(i,j,m),etc.)
   real(r8), pointer :: fpcgrid(:)          ! foliar projective cover on gridcell (fraction)
   real(r8), pointer :: nind(:)             ! number of individuals (#/m**2)
!
!EOP
!
! !LOCAL VARIABLES:
    character(len=256) :: dgvm_fn      ! dgvm history filename
    integer :: ncid                    ! dgvm netcdf file id
    integer :: omode                   ! returned mode from netCDF call
    integer :: ncprec                  ! output precision
    integer :: g,p,l                   ! indices
    integer :: begp, endp              ! per-proc beginning and ending pft indices
    integer :: begc, endc              ! per-proc beginning and ending column indices
    integer :: begl, endl              ! per-proc beginning and ending landunit indices
    integer :: begg, endg              ! per-proc gridcell ending gridcell indices
    integer :: numg,numl,numc,nump     ! global glcp cells
    integer :: ier                     ! error status
    integer :: mdcur, mscur, mcdate    ! outputs from get_curr_time
    integer :: yr,mon,day,mcsec        ! outputs from get_curr_date
    integer :: hours,minutes,secs      ! hours,minutes,seconds of hh:mm:ss
    integer :: nstep                   ! time step
    integer :: nbsec                   ! seconds components of a date
    integer :: dimid                   ! dimension, variable id
    real(r8):: time                    ! current time
    character(len=256) :: str          ! temporary string
    character(len=  8) :: curdate      ! current date
    character(len=  8) :: curtime      ! current time
    character(len= 10) :: basedate     ! base date (yyyymmdd)
    character(len=  8) :: basesec      ! base seconds
    character(len=256) :: rem_dir      ! remote (archive) directory
    character(len=256) :: rem_fn       ! remote (archive) filename
    real(r8), pointer :: rbuf2dg(:,:)  ! temporary
    integer , pointer :: ibuf2dg(:,:)  ! temporary
    character(len=32) :: subname='histCNDV'
!-----------------------------------------------------------------------

    ! Assign local pointers to derived type members (gridcell-level)

    ! NONE

    ! Assign local pointers to derived type members (landunit-level)

    ifspecial  => clm3%g%l%ifspecial

    ! Assign local pointers to derived subtypes components (pft-level)

    mxy       => clm3%g%l%c%p%mxy
    pgridcell => clm3%g%l%c%p%gridcell
    plandunit => clm3%g%l%c%p%landunit
    fpcgrid   => clm3%g%l%c%p%pdgvs%fpcgrid
    nind      => clm3%g%l%c%p%pdgvs%nind

    ! Determine subgrid bounds for this processor and allocate dynamic memory

    call get_proc_bounds(begg, endg, begl, endl, begc, endc, begp, endp)
    call get_proc_global(numg, numl, numc, nump)

    allocate(rbuf2dg(begg:endg,maxpatch_pft), ibuf2dg(begg:endg,maxpatch_pft), stat=ier)
    if (ier /= 0) call endrun('histCNDV: allocation error for rbuf2dg, ibuf2dg')

    ! Set output precision

    ncprec = nf_double

    if (masterproc) then

       ! -----------------------------------------------------------------------
       ! Create new netCDF file. File will be in define mode
       ! Set fill mode to "no fill" to optimize performance
       ! -----------------------------------------------------------------------

       dgvm_fn = set_dgvm_filename()
       call check_ret(nf_create(dgvm_fn, nf_clobber, ncid), subname)
       call check_ret(nf_set_fill(ncid, nf_nofill, omode), subname)

       ! -----------------------------------------------------------------------
       ! Create global attributes.
       ! -----------------------------------------------------------------------

       str = 'CF1.0'
       call check_ret(&
            nf_put_att_text (ncid, NF_GLOBAL, 'conventions', len_trim(str), trim(str)), subname)

       call getdatetime(curdate, curtime)
       str = 'created on ' // curdate // ' ' // curtime
       call check_ret(&
            nf_put_att_text(ncid, NF_GLOBAL,'history', len_trim(str), trim(str)), subname)

       call shr_sys_getenv('LOGNAME', str, ier)
       if (ier /= 0) call endrun('error: LOGNAME environment variable not defined')
       
       call check_ret(&
            nf_put_att_text (ncid, NF_GLOBAL, 'logname',len_trim(str), trim(str)), subname)
       
       call shr_sys_getenv('HOST', str, ier)
       call check_ret(&
            nf_put_att_text (ncid, NF_GLOBAL, 'host', len_trim(str), trim(str)), subname)
       
       str = 'Community Land Model: CLM3'
       call check_ret(&
            nf_put_att_text (ncid, NF_GLOBAL, 'source', len_trim(str), trim(str)), subname)
       
       str = '$Name$'
       call check_ret(&
            nf_put_att_text (ncid, NF_GLOBAL, 'version', len_trim(str), trim(str)), subname)
       
       str = '$Id$'
       call check_ret(&
            nf_put_att_text (ncid, NF_GLOBAL, 'revision_id', len_trim(str), trim(str)), subname)

       str = ctitle
       call check_ret(&
            nf_put_att_text (ncid, NF_GLOBAL, 'case_title', len_trim(str), trim(str)), subname)

       str = caseid
       call check_ret(&
            nf_put_att_text (ncid, NF_GLOBAL, 'case_id', len_trim(str), trim(str)), subname)
       
       str = get_filename(fsurdat)
       call check_ret(&
            nf_put_att_text(ncid, NF_GLOBAL, 'Surface_dataset', len_trim(str), trim(str)), subname)
       
       str = 'arbitrary initialization'
       if (finidat /= ' ') str = get_filename(finidat)
       call check_ret(&
            nf_put_att_text(ncid, NF_GLOBAL, 'Initial_conditions_dataset', len_trim(str), trim(str)), subname)
       
       str = get_filename(fpftcon)
       call check_ret(&
            nf_put_att_text(ncid, NF_GLOBAL, 'PFT_physiological_constants_dataset', len_trim(str), trim(str)), subname)
       
       if (frivinp_rtm /= ' ') then
          str = get_filename(frivinp_rtm)
          call check_ret(&
               nf_put_att_text(ncid, NF_GLOBAL, 'RTM_input_datset', len_trim(str), trim(str)), subname)
       end if

       ! -----------------------------------------------------------------------
       ! Define dimensions.
       ! -----------------------------------------------------------------------
       
       call check_ret(nf_def_dim (ncid, 'lon', lsmlon, dimid), subname)
       call check_ret(nf_def_dim (ncid, 'lat', lsmlat, dimid), subname)
       call check_ret(nf_def_dim (ncid, 'pft', maxpatch_pft, dimid), subname)
       call check_ret(nf_def_dim (ncid, 'time', nf_unlimited, dimid), subname)
       call check_ret(nf_def_dim (ncid, 'string_length', 80, dimid), subname)
       
       ! -----------------------------------------------------------------------
       ! Define variables
       ! -----------------------------------------------------------------------
       
       ! Define coordinate variables (including time)
       
       call ncd_defvar(ncid=ncid, varname='lon', xtype=ncprec, dim1name='lon', &
            long_name='coordinate longitude', units='degrees_east')
          
       call ncd_defvar(ncid=ncid, varname='lat', xtype=ncprec, dim1name='lat', &
            long_name='coordinate latitude', units='degrees_north')

       call get_curr_time(mdcur, mscur)
       call get_ref_date(yr, mon, day, nbsec)
       hours   = nbsec / 3600
       minutes = (nbsec - hours*3600) / 60
       secs    = (nbsec - hours*3600 - minutes*60)
       write(basedate,80) yr,mon,day
80     format(i4.4,'-',i2.2,'-',i2.2)
       write(basesec ,90) hours, minutes, secs
90     format(i2.2,':',i2.2,':',i2.2)
       str = 'days since ' // basedate // " " // basesec
       time = mdcur + mscur/SHR_CONST_CDAY
       
       call ncd_defvar(ncid=ncid, varname='time', xtype=nf_double, dim1name='time', &
            long_name='time', units=str)
       
       call ncd_defvar(ncid=ncid, varname='edgen', xtype=ncprec, &
            long_name='northern edge of surface grid', units='degrees_north')
          
       call ncd_defvar(ncid=ncid, varname='edgee', xtype=ncprec, &
            long_name='eastern edge of surface grid', units='degrees_east')
          
       call ncd_defvar(ncid=ncid, varname='edges', xtype=ncprec, &
            long_name='southern edge of surface grid', units='degrees_north')
          
       call ncd_defvar(ncid=ncid, varname='edgew', xtype=ncprec, &
            long_name='western edge of surface grid', units='degrees_east')

       ! Define surface grid (coordinate variables, latitude, longitude, surface type).
       
       call ncd_defvar(ncid=ncid, varname='longxy', xtype=ncprec, dim1name='lon', dim2name='lat', &
            long_name='longitude', units='degrees_east')
       
       call ncd_defvar(ncid=ncid, varname='latixy', xtype=ncprec, dim1name='lon', dim2name='lat', &
            long_name='latitude', units='degrees_north')
       
       call ncd_defvar(ncid=ncid, varname='landmask', xtype=nf_int, dim1name='lon', dim2name='lat', &
            long_name='land/ocean mask (0.=ocean and 1.=land)')
       
       ! Define time information
       
       call ncd_defvar(ncid=ncid, varname='mcdate', xtype=nf_int, dim1name='time',&
            long_name='current date (YYYYMMDD)')
       
       call ncd_defvar(ncid=ncid, varname='mcsec', xtype=nf_int, dim1name='time',&
            long_name='current seconds of current date', units='s')
       
       call ncd_defvar(ncid=ncid, varname='mdcur', xtype=nf_int, dim1name='time',&
            long_name='current day (from base day)')
       
       call ncd_defvar(ncid=ncid, varname='mscur', xtype=nf_int, dim1name='time',&
            long_name='current seconds of current day', units='s')

       call ncd_defvar(ncid=ncid, varname='nstep', xtype=nf_int, dim1name='time',&
            long_name='time step', units='s')

       ! Define time dependent variables
       
       call ncd_defvar(ncid=ncid, varname='FPCGRID', xtype=ncprec, &
            dim1name='lon', dim2name='lat', dim3name='pft', dim4name='time', &
            long_name='plant functional type cover', units='fraction of vegetated area', &
            missing_value=spval, fill_value=spval)

       call ncd_defvar(ncid=ncid, varname='NIND', xtype=ncprec, &
            dim1name='lon', dim2name='lat', dim3name='pft', dim4name='time', &
            long_name='number of individuals', units='individuals/m2 vegetated land', &
            missing_value=spval, fill_value=spval)
       
       call check_ret(nf_enddef(ncid), subname)

    end if   ! End of if-masterproc block

    ! -----------------------------------------------------------------------
    ! Write variables
    ! -----------------------------------------------------------------------

    call ncd_ioglobal(varname='edgen', data=llatlon%edges(1), ncid=ncid, flag='write')
    call ncd_ioglobal(varname='edgee', data=llatlon%edges(2), ncid=ncid, flag='write')
    call ncd_ioglobal(varname='edges', data=llatlon%edges(3), ncid=ncid, flag='write')
    call ncd_ioglobal(varname='edgew', data=llatlon%edges(4), ncid=ncid, flag='write')

    ! Write surface grid (coordinate variables, latitude, longitude, surface type).

    if (masterproc) then
       call ncd_ioglobal(varname='lon', data=llatlon%lonc, ncid=ncid, flag='write')
       call ncd_ioglobal(varname='lat', data=llatlon%latc, ncid=ncid, flag='write')
    end if
    call ncd_iolocal(varname='longxy'  , data=ldomain%lonc, ncid=ncid, &
                      flag='write', dim1name=grlnd)
    call ncd_iolocal(varname='latixy'  , data=ldomain%latc, ncid=ncid, &
                      flag='write', dim1name=grlnd)
    call ncd_iolocal(varname='landmask', data=ldomain%mask, ncid=ncid, &
                      flag='write', dim1name=grlnd)
    ! Write current date, current seconds, current day, current nstep

    call get_curr_date(yr, mon, day, mcsec)
    mcdate = yr*10000 + mon*100 + day
    nstep = get_nstep()

    call ncd_ioglobal(varname='mcdate', data=mcdate, nt=1, ncid=ncid, flag='write')
    call ncd_ioglobal(varname='mcsec' , data=mcsec , nt=1, ncid=ncid, flag='write')
    call ncd_ioglobal(varname='mdcur' , data=mdcur , nt=1, ncid=ncid, flag='write')
    call ncd_ioglobal(varname='mscur' , data=mcsec , nt=1, ncid=ncid, flag='write')
    call ncd_ioglobal(varname='nstep' , data=nstep , nt=1, ncid=ncid, flag='write')
    call ncd_ioglobal(varname='time'  , data=time  , nt=1, ncid=ncid, flag='write')

    ! Write time dependent variables to CNDV history file

    ! The if .not. ifspecial statment below guarantees that the m index will
    ! always lie between 1 and maxpatch_pft

    rbuf2dg(:,:) = 0._r8
    do p = begp,endp
       g = pgridcell(p)
       l = plandunit(p)
       if (.not. ifspecial(l)) rbuf2dg(g,mxy(p)) = fpcgrid(p)*100._r8
    end do
    call ncd_iolocal(varname='FPCGRID', dim1name=grlnd, dim2name='pft', data=rbuf2dg, &
         nt=1, ncid=ncid, flag='write')

    rbuf2dg(:,:) = 0._r8
    do p = begp,endp
       g = pgridcell(p)
       l = plandunit(p)
       if (.not. ifspecial(l)) rbuf2dg(g,mxy(p)) = nind(p)
    end do
    call ncd_iolocal(varname='NIND',dim1name=grlnd, dim2name='pft', data=rbuf2dg, &
         nt=1, ncid=ncid, flag='write')

    ! Deallocate dynamic memory

    deallocate(rbuf2dg, ibuf2dg)

    !------------------------------------------------------------------
    ! Close and archive netcdf CNDV history file
    !------------------------------------------------------------------

    if (masterproc) then
       call check_ret(nf_close(ncid), subname)
       write(6,*)'(histCNDV): Finished writing CNDV history dataset ',&
            trim(dgvm_fn), 'at nstep = ',get_nstep()
    end if

  end subroutine histCNDV

!-----------------------------------------------------------------------
!BOP
!
! !IROUTINE: set_dgvm_filename
!
! !INTERFACE:

  character(len=256) function set_dgvm_filename () 1,3
!
! !DESCRIPTION:
! Determine initial dataset filenames
!
! !USES:
    use clm_varctl      , only : caseid
    use clm_time_manager, only : get_curr_date
!
! !ARGUMENTS:
    implicit none
!
! !CALLED FROM:
!
! !REVISION HISTORY:
! Author: Mariana Vertenstein
!
!EOP
!
! !LOCAL VARIABLES:
    character(len=256) :: cdate       !date char string
    integer :: day                    !day (1 -> 31)
    integer :: mon                    !month (1 -> 12)
    integer :: yr                     !year (0 -> ...)
    integer :: sec                    !seconds into current day
!-----------------------------------------------------------------------

    call get_curr_date (yr, mon, day, sec)
    write(cdate,'(i4.4,"-",i2.2,"-",i2.2,"-",i5.5)') yr,mon,day,sec
    set_dgvm_filename = "./"//trim(caseid)//".clm2.hv."//trim(cdate)//".nc"

  end function set_dgvm_filename

!-----------------------------------------------------------------------
!BOP
!
! !IROUTINE: BuildNatVegFilter
!
! !INTERFACE:

  subroutine BuildNatVegFilter(lbp, ubp, num_natvegp, filter_natvegp) 1,1
!
! !DESCRIPTION:
! Reconstruct a filter of naturally-vegetated PFTs for use in DGVM
!
! !USES:
    use clmtype
!
! !ARGUMENTS:
    implicit none
    integer, intent(in)  :: lbp, ubp                   ! pft bounds
    integer, intent(out) :: num_natvegp                ! number of pfts in naturally-vegetated filter
    integer, intent(out) :: filter_natvegp(ubp-lbp+1)  ! pft filter for naturally-vegetated points
!
! !CALLED FROM:
! subroutine lpj in this module
!
! !REVISION HISTORY:
! Author: Forrest Hoffman
!
! !LOCAL VARIABLES:
! local pointers to implicit in arguments
    logical , pointer :: present(:)     ! whether this pft present in patch
!EOP
!
! !LOCAL VARIABLES:
    integer :: p
!-----------------------------------------------------------------------

    ! Assign local pointers to derived type members (pft-level)
    present   => clm3%g%l%c%p%pdgvs%present

    num_natvegp = 0
    do p = lbp,ubp
       if (present(p)) then
          num_natvegp = num_natvegp + 1
          filter_natvegp(num_natvegp) = p
       end if
    end do

  end subroutine BuildNatVegFilter

#endif

end module CNDVMod