#include <misc.h>
#include <preproc.h>
module accumulMod 5,7
!-----------------------------------------------------------------------
!BOP
!
! !MODULE: accumulMod
!
! !DESCRIPTION:
! This module contains generic subroutines that can be used to
! define, accumulate and extract user-specified fields over
! user-defined intervals. Each interval and accumulation type is
! unique to each field processed.
! Subroutine [init_accumulator] defines the values of the accumulated
! field data structure. Subroutine [update_accum_field] does
! the actual accumulation for a given field.
! Four types of accumulations are possible:
! - Average over time interval. Time average fields are only
! valid at the end of the averaging interval.
! - Running mean over time interval. Running means are valid once the
! length of the simulation exceeds the
! - Running accumulation over time interval. Accumulated fields are
! continuously accumulated. The trigger value "-99999." resets
! the accumulation to zero.
!
! !USES:
use shr_kind_mod
, only: r8 => shr_kind_r8
use abortutils
, only: endrun
use clm_varctl
, only: iulog
!
! !PUBLIC TYPES:
implicit none
save
!
! !PUBLIC MEMBER FUNCTIONS:
public :: accumulRest ! Write/read restart of accumulation fields
public :: init_accum_field ! Initialize an accumulator field
public :: print_accum_fields ! Print info about accumulator fields
public :: extract_accum_field ! Extracts the current value of an accumulator field
public :: update_accum_field ! Update the current value of an accumulator field
interface extract_accum_field 31
module procedure extract_accum_field_sl
! Extract current val of single-level accumulator field
module procedure extract_accum_field_ml
! Extract current val of multi-level accumulator field
end interface
interface update_accum_field ! Updates the current value of an accumulator field 17
module procedure update_accum_field_sl
! Update single-level accumulator field
module procedure update_accum_field_ml
! Update multi-level accumulator field
end interface
!
! !REVISION HISTORY:
! Created by Sam Levis
! Updated to clm2.1 data structures by Mariana Vertenstein
! Updated to include all subgrid type and multilevel fields, M. Vertenstein 03/2003
!
!
private
!
! !PRIVATE TYPES:
!EOP
!
type accum_field
character(len= 8) :: name !field name
character(len=128) :: desc !field description
character(len= 8) :: units !field units
character(len= 8) :: acctype !accumulation type: ["timeavg","runmean","runaccum"]
character(len= 8) :: type1d !subgrid type: ["gridcell","landunit","column" or "pft"]
character(len= 8) :: type2d !type2d ('','levsoi','numrad',..etc. )
integer :: beg1d !subgrid type beginning index
integer :: end1d !subgrid type ending index
integer :: num1d !total subgrid points
integer :: numlev !number of vertical levels in field
real(r8):: initval !initial value of accumulated field
real(r8), pointer :: val(:,:) !accumulated field
integer :: period !field accumulation period (in model time steps)
end type accum_field
integer, parameter :: max_accum = 100 !maximum number of accumulated fields
type (accum_field) :: accum(max_accum) !array accumulated fields
integer :: naccflds = 0 !accumulator field counter
!------------------------------------------------------------------------
contains
!------------------------------------------------------------------------
!BOP
!
! !IROUTINE: init_accum_field
!
! !INTERFACE:
subroutine init_accum_field (name, units, desc, & 17,8
accum_type, accum_period, numlev, subgrid_type, init_value, type2d)
!
! !DESCRIPTION:
! Initialize accumulation fields. This subroutine sets:
! o name of accumulated field
! o units of accumulated field
! o accumulation type of accumulated field
! o description of accumulated fields: accdes
! o accumulation period for accumulated field (in iterations)
! o initial value of accumulated field
!
! !USES:
use shr_const_mod
, only: SHR_CONST_CDAY
use clm_time_manager
, only : get_step_size
use decompMod
, only : get_proc_bounds, get_proc_global
!
! !ARGUMENTS:
implicit none
character(len=*), intent(in) :: name !field name
character(len=*), intent(in) :: units !field units
character(len=*), intent(in) :: desc !field description
character(len=*), intent(in) :: accum_type !field type: tavg, runm, runa, ins
integer , intent(in) :: accum_period !field accumulation period
character(len=*), intent(in) :: subgrid_type !["gridcell","landunit","column" or "pft"]
integer , intent(in) :: numlev !number of vertical levels
real(r8), intent(in) :: init_value !field initial or reset value
character(len=*), intent(in), optional :: type2d !level type (optional) - needed if numlev > 1
!
! !REVISION HISTORY:
! Created by Mariana Vertenstein 03/2003
!
!
! !LOCAL VARIABLES:
!EOP
integer :: nf ! field index
integer :: beg1d,end1d ! beggining and end subgrid indices
integer :: num1d ! total number subgrid 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 ! total number of gridcells across all processors
integer :: numl ! total number of landunits across all processors
integer :: numc ! total number of columns across all processors
integer :: nump ! total number of pfts across all processors
!------------------------------------------------------------------------
! Determine necessary indices
call get_proc_bounds
(begg, endg, begl, endl, begc, endc, begp, endp)
call get_proc_global
(numg, numl, numc, nump)
! update field index
! Consistency check that number of accumulated does not exceed maximum.
naccflds = naccflds + 1
if (naccflds > max_accum) then
write(iulog,*) 'ACCUMULINIT error: user-defined accumulation fields ', &
'equal to ',naccflds,' exceeds max_accum'
call endrun
end if
nf = naccflds
! Note accumulation period must be converted from days
! to number of iterations
accum(nf)%name = trim(name)
accum(nf)%units = trim(units)
accum(nf)%desc = trim(desc)
accum(nf)%acctype = trim(accum_type)
accum(nf)%initval = init_value
accum(nf)%period = accum_period
if (accum(nf)%period < 0) then
accum(nf)%period = -accum(nf)%period * nint(SHR_CONST_CDAY) / get_step_size
()
end if
select case (trim(subgrid_type))
case ('gridcell')
beg1d = begg
end1d = endg
num1d = numg
case ('landunit')
beg1d = begl
end1d = endl
num1d = numl
case ('column')
beg1d = begc
end1d = endc
num1d = numc
case ('pft')
beg1d = begp
end1d = endp
num1d = nump
case default
write(iulog,*)'ACCUMULINIT: unknown subgrid type ',subgrid_type
call endrun
()
end select
accum(nf)%type1d = trim(subgrid_type)
accum(nf)%beg1d = beg1d
accum(nf)%end1d = end1d
accum(nf)%num1d = num1d
accum(nf)%numlev = numlev
if (present(type2d)) then
accum(nf)%type2d = type2d
else
accum(nf)%type2d = ' '
end if
! Allocate and initialize accumulation field
allocate(accum(nf)%val(beg1d:end1d,numlev))
accum(nf)%val(beg1d:end1d,numlev) = init_value
end subroutine init_accum_field
!------------------------------------------------------------------------
!BOP
!
! !IROUTINE: print_accum_fields
!
! !INTERFACE:
subroutine print_accum_fields() 1,2
!
! !DESCRIPTION:
! Diagnostic printout of accumulated fields
!
! !USES:
use spmdMod
, only : masterproc
use nanMod
, only : bigint
!
! !ARGUMENTS:
implicit none
!
! !REVISION HISTORY:
! Created by Mariana Vertenstein 03/2003
!
!EOP
!
integer :: i,nf !indices
!------------------------------------------------------------------------
if (masterproc) then
write(iulog,*)
write(iulog,*) 'Initializing variables for time accumulation .....'
write(iulog,'(72a1)') ("-",i=1,60)
write(iulog,*) 'Accumulated fields'
write(iulog,1002)
write(iulog,'(72a1)') ("_",i=1,71)
do nf = 1, naccflds
if (accum(nf)%period /= bigint) then
write(iulog,1003) nf,accum(nf)%name,accum(nf)%units,&
accum(nf)%acctype, accum(nf)%period, accum(nf)%initval, &
accum(nf)%desc
else
write(iulog,1004) nf,accum(nf)%name,accum(nf)%units,&
accum(nf)%acctype, accum(nf)%initval, accum(nf)%desc
endif
end do
write(iulog,'(72a1)') ("_",i=1,71)
write(iulog,*)
write(iulog,'(72a1)') ("-",i=1,60)
write(iulog,*) 'Successfully initialized variables for accumulation'
write(iulog,*)
endif
1002 format(' No',' Name ',' Units ',' Type ','Period',' Inival',' Description')
1003 format((1x,i2),(1x,a8),(1x,a8),(1x,a8), (1x,i5),(1x,f4.0),(1x,a40))
1004 format((1x,i2),(1x,a8),(1x,a8),(1x,a8),' N.A.',(1x,f4.0),(1x,a40))
end subroutine print_accum_fields
!------------------------------------------------------------------------
!BOP
!
! !IROUTINE: extract_accum_field_sl
!
! !INTERFACE:
subroutine extract_accum_field_sl (name, field, nstep) 1,3
!
! !DESCRIPTION:
! Extract single-level accumulated field.
! This routine extracts the field values from the multi-level
! accumulation field. It extracts the current value except if
! the field type is a time average. In this case, an absurd value
! is assigned to indicate the time average is not yet valid.
!
! !USES:
use clm_varcon
, only : spval
!
! !ARGUMENTS:
implicit none
character(len=*), intent(in) :: name !field name
real(r8), pointer, dimension(:) :: field !field values for current time step
integer , intent(in) :: nstep !timestep index
!
! !REVISION HISTORY:
! Created by Sam Levis
! Updated to clm2.1 data structures by Mariana Vertenstein
! Updated to include all subgrid type and multilevel fields, Mariana Vertenstein 03-2003
!
!
! !LOCAL VARIABLES:
!EOP
integer :: i,k,nf !indices
integer :: beg,end !subgrid beginning,ending indices
!------------------------------------------------------------------------
! find field index. return if "name" is not on list
nf = 0
do i = 1, naccflds
if (name == accum(i)%name) nf = i
end do
if (nf == 0) then
write(iulog,*) 'EXTRACT_ACCUM_FIELD_SL error: field name ',name,' not found'
call endrun
endif
! error check
beg = accum(nf)%beg1d
end = accum(nf)%end1d
if (size(field,dim=1) /= end-beg+1) then
write(iulog,*)'ERROR in extract_accum_field for field ',accum(nf)%name
write(iulog,*)'size of first dimension of field is ',&
size(field,dim=1),' and should be ',end-beg+1
call endrun
endif
! extract field
if (accum(nf)%acctype == 'timeavg' .and. &
mod(nstep,accum(nf)%period) /= 0) then
!dir$ concurrent
!cdir nodep
do k = beg,end
field(k) = spval !assign absurd value when avg not ready
end do
else
!dir$ concurrent
!cdir nodep
do k = beg,end
field(k) = accum(nf)%val(k,1)
end do
end if
end subroutine extract_accum_field_sl
!------------------------------------------------------------------------
!BOP
!
! !IROUTINE: extract_accum_field_ml
!
! !INTERFACE:
subroutine extract_accum_field_ml (name, field, nstep) 1,4
!
! !DESCRIPTION:
! Extract mutli-level accumulated field.
! This routine extracts the field values from the multi-level
! accumulation field. It extracts the current value except if
! the field type is a time average. In this case, an absurd value
! is assigned to indicate the time average is not yet valid.
!
! !USES:
use clm_varcon
, only : spval
!
! !ARGUMENTS:
implicit none
character(len=*), intent(in) :: name !field name
real(r8), pointer, dimension(:,:) :: field !field values for current time step
integer, intent(in) :: nstep !timestep index
!
! !REVISION HISTORY:
! Created by Sam Levis
! Updated to clm2.1 data structures by Mariana Vertenstein
! Updated to include all subgrid type and multilevel fields, M. Vertenstein 03/2003
!
!
! !LOCAL VARIABLES:
!EOP
integer :: i,j,k,nf !indices
integer :: beg,end !subgrid beginning,ending indices
integer :: numlev !number of vertical levels
!------------------------------------------------------------------------
! find field index. return if "name" is not on list
nf = 0
do i = 1, naccflds
if (name == accum(i)%name) nf = i
end do
if (nf == 0) then
write(iulog,*) 'EXTRACT_ACCUM_FIELD_ML error: field name ',name,' not found'
call endrun
endif
! error check
numlev = accum(nf)%numlev
beg = accum(nf)%beg1d
end = accum(nf)%end1d
if (size(field,dim=1) /= end-beg+1) then
write(iulog,*)'ERROR in extract_accum_field for field ',accum(nf)%name
write(iulog,*)'size of first dimension of field is ',&
size(field,dim=1),' and should be ',end-beg+1
call endrun
else if (size(field,dim=2) /= numlev) then
write(iulog,*)'ERROR in extract_accum_field for field ',accum(nf)%name
write(iulog,*)'size of second dimension of field iis ',&
size(field,dim=2),' and should be ',numlev
call endrun
endif
!extract field
if (accum(nf)%acctype == 'timeavg' .and. &
mod(nstep,accum(nf)%period) /= 0) then
do j = 1,numlev
!dir$ concurrent
!cdir nodep
do k = beg,end
field(k,j) = spval !assign absurd value when avg not ready
end do
end do
else
do j = 1,numlev
!dir$ concurrent
!cdir nodep
do k = beg,end
field(k,j) = accum(nf)%val(k,j)
end do
end do
end if
end subroutine extract_accum_field_ml
!------------------------------------------------------------------------
!BOP
!
! !IROUTINE: update_accum_field_sl
!
! !INTERFACE:
subroutine update_accum_field_sl (name, field, nstep) 1,3
!
! !DESCRIPTION:
! Accumulate single level field over specified time interval.
! The appropriate field is accumulated in the array [accval].
!
! !ARGUMENTS:
implicit none
character(len=*), intent(in) :: name !field name
real(r8), pointer, dimension(:) :: field !field values for current time step
integer , intent(in) :: nstep !time step index
!
! !REVISION HISTORY:
! Created by Sam Levis
! Updated to clm2.1 data structures by Mariana Vertenstein
! Updated to include all subgrid type and multilevel fields by M. Vertenstein 03/2003
!
!
! !LOCAL VARIABLES:
!EOP
integer :: i,k,nf !indices
integer :: accper !temporary accumulation period
integer :: beg,end !subgrid beginning,ending indices
!------------------------------------------------------------------------
! find field index. return if "name" is not on list
nf = 0
do i = 1, naccflds
if (name == accum(i)%name) nf = i
end do
if (nf == 0) then
write(iulog,*) 'UPDATE_ACCUM_FIELD_SL error: field name ',name,' not found'
call endrun
endif
! error check
beg = accum(nf)%beg1d
end = accum(nf)%end1d
if (size(field,dim=1) /= end-beg+1) then
write(iulog,*)'ERROR in UPDATE_ACCUM_FIELD_SL for field ',accum(nf)%name
write(iulog,*)'size of first dimension of field is ',size(field,dim=1),&
' and should be ',end-beg+1
call endrun
endif
! accumulate field
if (accum(nf)%acctype /= 'timeavg' .AND. &
accum(nf)%acctype /= 'runmean' .AND. &
accum(nf)%acctype /= 'runaccum') then
write(iulog,*) 'UPDATE_ACCUM_FIELD_SL error: incorrect accumulation type'
write(iulog,*) ' was specified for field ',name
write(iulog,*)' accumulation type specified is ',accum(nf)%acctype
write(iulog,*)' only [timeavg, runmean, runaccum] are currently acceptable'
call endrun
()
end if
! reset accumulated field value if necessary and update
! accumulation field
! running mean never reset
if (accum(nf)%acctype == 'timeavg') then
!time average field reset every accumulation period
!normalize at end of accumulation period
if ((mod(nstep,accum(nf)%period) == 1) .and. (nstep /= 0)) then
accum(nf)%val(beg:end,1) = 0._r8
end if
accum(nf)%val(beg:end,1) = accum(nf)%val(beg:end,1) + field(beg:end)
if (mod(nstep,accum(nf)%period) == 0) then
accum(nf)%val(beg:end,1) = accum(nf)%val(beg:end,1) / accum(nf)%period
endif
else if (accum(nf)%acctype == 'runmean') then
!running mean - reset accumulation period until greater than nstep
accper = min (nstep,accum(nf)%period)
accum(nf)%val(beg:end,1) = ((accper-1)*accum(nf)%val(beg:end,1) + field(beg:end)) / accper
else if (accum(nf)%acctype == 'runaccum') then
!running accumulation field reset at trigger -99999
!dir$ concurrent
!cdir nodep
do k = beg,end
if (nint(field(k)) == -99999) then
accum(nf)%val(k,1) = 0._r8
end if
end do
accum(nf)%val(beg:end,1) = min(max(accum(nf)%val(beg:end,1) + field(beg:end), 0._r8), 99999._r8)
end if
end subroutine update_accum_field_sl
!------------------------------------------------------------------------
!BOP
!
! !IROUTINE: update_accum_field_ml
!
! !INTERFACE:
subroutine update_accum_field_ml (name, field, nstep) 1,4
!
! !DESCRIPTION:
! Accumulate multi level field over specified time interval.
!
! !ARGUMENTS:
implicit none
character(len=*), intent(in) :: name !field name
real(r8), pointer, dimension(:,:) :: field !field values for current time step
integer , intent(in) :: nstep !time step index
!
! !REVISION HISTORY:
! Created by Sam Levis
! Updated to clm2.1 data structures by Mariana Vertenstein
! Updated to include all subgrid type and multilevel fields by M. Vertenstein 03/2003
!
!
! !LOCAL VARIABLES:
!EOP
integer :: i,j,k,nf !indices
integer :: accper !temporary accumulation period
integer :: beg,end !subgrid beginning,ending indices
integer :: numlev !number of vertical levels
!------------------------------------------------------------------------
! find field index. return if "name" is not on list
nf = 0
do i = 1, naccflds
if (name == accum(i)%name) nf = i
end do
if (nf == 0) then
write(iulog,*) 'UPDATE_ACCUM_FIELD_ML error: field name ',name,' not found'
call endrun
endif
! error check
numlev = accum(nf)%numlev
beg = accum(nf)%beg1d
end = accum(nf)%end1d
if (size(field,dim=1) /= end-beg+1) then
write(iulog,*)'ERROR in UPDATE_ACCUM_FIELD_ML for field ',accum(nf)%name
write(iulog,*)'size of first dimension of field is ',size(field,dim=1),&
' and should be ',end-beg+1
call endrun
else if (size(field,dim=2) /= numlev) then
write(iulog,*)'ERROR in UPDATE_ACCUM_FIELD_ML for field ',accum(nf)%name
write(iulog,*)'size of second dimension of field is ',size(field,dim=2),&
' and should be ',numlev
call endrun
endif
! accumulate field
if (accum(nf)%acctype /= 'timeavg' .AND. &
accum(nf)%acctype /= 'runmean' .AND. &
accum(nf)%acctype /= 'runaccum') then
write(iulog,*) 'UPDATE_ACCUM_FIELD_ML error: incorrect accumulation type'
write(iulog,*) ' was specified for field ',name
write(iulog,*)' accumulation type specified is ',accum(nf)%acctype
write(iulog,*)' only [timeavg, runmean, runaccum] are currently acceptable'
call endrun
()
end if
! accumulate field
! reset accumulated field value if necessary and update
! accumulation field
! running mean never reset
if (accum(nf)%acctype == 'timeavg') then
!time average field reset every accumulation period
!normalize at end of accumulation period
if ((mod(nstep,accum(nf)%period) == 1) .and. (nstep /= 0)) then
accum(nf)%val(beg:end,1:numlev) = 0._r8
endif
accum(nf)%val(beg:end,1:numlev) = accum(nf)%val(beg:end,1:numlev) + field(beg:end,1:numlev)
if (mod(nstep,accum(nf)%period) == 0) then
accum(nf)%val(beg:end,1:numlev) = accum(nf)%val(beg:end,1:numlev) / accum(nf)%period
endif
else if (accum(nf)%acctype == 'runmean') then
!running mean - reset accumulation period until greater than nstep
accper = min (nstep,accum(nf)%period)
accum(nf)%val(beg:end,1:numlev) = &
((accper-1)*accum(nf)%val(beg:end,1:numlev) + field(beg:end,1:numlev)) / accper
else if (accum(nf)%acctype == 'runaccum') then
!running accumulation field reset at trigger -99999
do j = 1,numlev
!dir$ concurrent
!cdir nodep
do k = beg,end
if (nint(field(k,j)) == -99999) then
accum(nf)%val(k,j) = 0._r8
end if
end do
end do
accum(nf)%val(beg:end,1:numlev) = &
min(max(accum(nf)%val(beg:end,1:numlev) + field(beg:end,1:numlev), 0._r8), 99999._r8)
end if
end subroutine update_accum_field_ml
!------------------------------------------------------------------------
!BOP
!
! !IROUTINE: accumulRest
!
! !INTERFACE:
subroutine accumulRest( ncid, flag ) 3,11
!
! !DESCRIPTION:
! Read/write accumulation restart data
!
! !USES:
use ncdio
!
! !ARGUMENTS:
implicit none
integer, intent(in) :: ncid !netcdf unit
character(len=*), intent(in) :: flag !'define','read', or 'write'
!
! !REVISION HISTORY:
! Created by Mariana Vertenstein
!
!
! !LOCAL VARIABLES:
!EOP
integer :: nf,k,j ! indices
integer :: beg1d, end1d ! buffer bounds
integer :: ier ! error status
real(r8), pointer :: rbuf1d(:) ! temporary 1d buffer
logical :: readvar ! determine if variable is on initial file
character(len=128) :: varname ! temporary
character(len= 32) :: subname='AccumRest' ! subroutine name
!------------------------------------------------------------------------
do nf = 1,naccflds
! Note = below need to allocate rbuf for single level variables, since
! accum(nf)%val is always 2d
varname = trim(accum(nf)%name) // '_VALUE'
if (flag == 'define') then
if (accum(nf)%numlev == 1) then
call ncd_defvar
(ncid=ncid, varname=varname, xtype=nf_double, &
dim1name=accum(nf)%type1d, &
long_name=accum(nf)%desc, units=accum(nf)%units)
else
call ncd_defvar
(ncid=ncid, varname=varname, xtype=nf_double, &
dim1name=accum(nf)%type1d, dim2name=accum(nf)%type2d, &
long_name=accum(nf)%desc, units=accum(nf)%units)
end if
else if (flag == 'read' .or. flag == 'write') then
if (accum(nf)%numlev == 1) then
beg1d = accum(nf)%beg1d
end1d = accum(nf)%end1d
allocate(rbuf1d(beg1d:end1d))
if (flag == 'write') then
rbuf1d(beg1d:end1d) = accum(nf)%val(beg1d:end1d,1)
end if
call ncd_iolocal
(varname=varname, data=rbuf1d, &
dim1name=accum(nf)%type1d, &
ncid=ncid, flag=flag, readvar=readvar)
if (flag == 'read' .and. readvar) then
accum(nf)%val(beg1d:end1d,1) = rbuf1d(beg1d:end1d)
end if
deallocate(rbuf1d)
else
call ncd_iolocal
(varname=varname, data=accum(nf)%val, &
dim1name=accum(nf)%type1d, dim2name=accum(nf)%type2d, &
ncid=ncid, flag=flag, readvar=readvar)
end if
if (flag=='read' .and. .not. readvar) then
if (is_restart
()) call endrun
()
end if
end if
varname = trim(accum(nf)%name) // '_PERIOD'
if (flag == 'define') then
call ncd_defvar
(ncid=ncid, varname=varname, xtype=nf_int, &
long_name='', units='time steps')
else if (flag == 'read' .or. flag == 'write') then
call ncd_ioglobal
(varname=varname, data=accum(nf)%period, &
ncid=ncid, flag=flag, readvar=readvar, bcast=.true.)
if (flag=='read' .and. .not. readvar) then
if (is_restart
()) call endrun
()
end if
end if
end do
end subroutine accumulRest
!-----------------------------------------------------------------------
!BOP
!
! !IROUTINE: is_restart
!
! !INTERFACE:
logical function is_restart( ) 307,6
!
! !DESCRIPTION:
! Determine if restart run
!
! !USES:
use clm_varctl
, only : nsrest
!
! !ARGUMENTS:
implicit none
!
! !CALLED FROM:
! subroutine initialize in this module
!
! !REVISION HISTORY:
! Created by Mariana Vertenstein
!
!EOP
!-----------------------------------------------------------------------
if (nsrest == 1) then
is_restart = .true.
else
is_restart = .false.
end if
end function is_restart
end module accumulMod