!===============================================================================
! Age of air test tracers
! provides dissipation rate and surface fluxes for diagnostic constituents
!===============================================================================
module aoa_tracers 6,5
use shr_kind_mod
, only: r8 => shr_kind_r8
use spmd_utils
, only: masterproc
use ppgrid
, only: pcols, pver
use constituents
, only: pcnst, cnst_add, cnst_name, cnst_longname
use cam_logfile
, only: iulog
implicit none
private
save
! Public interfaces
public :: aoa_tracers_register ! register constituents
public :: aoa_tracers_implements_cnst ! true if named constituent is implemented by this package
public :: aoa_tracers_init_cnst ! initialize constituent field
public :: aoa_tracers_init ! initialize history fields, datasets
public :: aoa_tracers_timestep_init ! place to perform per timestep initialization
public :: aoa_tracers_timestep_tend ! calculate tendencies
public :: aoa_tracers_readnl ! read namelist options
! Private module data
integer, parameter :: ncnst=4 ! number of constituents implemented by this module
! constituent names
character(len=8), parameter :: c_names(ncnst) = (/'AOA1', 'AOA2', 'HORZ', 'VERT'/)
! constituent source/sink names
character(len=8), parameter :: src_names(ncnst) = (/'AOA1SRC', 'AOA2SRC', 'HORZSRC', 'VERTSRC'/)
integer :: ifirst ! global index of first constituent
integer :: ixaoa1 ! global index for AOA1 tracer
integer :: ixaoa2 ! global index for AOA2 tracer
integer :: ixht ! global index for HORZ tracer
integer :: ixvt ! global index for VERT tracer
! Data from namelist variables
logical :: aoa_tracers_flag = .false. ! true => turn on test tracer code, namelist variable
logical :: aoa_read_from_ic_file = .false. ! true => tracers initialized from IC file
!===============================================================================
contains
!===============================================================================
!================================================================================
subroutine aoa_tracers_readnl(nlfile) 1,10
use namelist_utils
, only: find_group_name
use units
, only: getunit, freeunit
use mpishorthand
use abortutils
, only: endrun
implicit none
character(len=*), intent(in) :: nlfile ! filepath for file containing namelist input
! Local variables
integer :: unitn, ierr
character(len=*), parameter :: subname = 'aoa_tracers_readnl'
namelist /aoa_tracers_nl/ aoa_tracers_flag, aoa_read_from_ic_file
!-----------------------------------------------------------------------------
if (masterproc) then
unitn = getunit
()
open( unitn, file=trim(nlfile), status='old' )
call find_group_name
(unitn, 'aoa_tracers_nl', status=ierr)
if (ierr == 0) then
read(unitn, aoa_tracers_nl, iostat=ierr)
if (ierr /= 0) then
call endrun
(subname // ':: ERROR reading namelist')
end if
end if
close(unitn)
call freeunit
(unitn)
end if
#ifdef SPMD
call mpibcast
(aoa_tracers_flag, 1, mpilog, 0, mpicom)
call mpibcast
(aoa_read_from_ic_file, 1, mpilog, 0, mpicom)
#endif
endsubroutine aoa_tracers_readnl
!================================================================================
subroutine aoa_tracers_register 1,5
!-----------------------------------------------------------------------
!
! Purpose: register advected constituents
!
!-----------------------------------------------------------------------
use physconst
, only: cpair, mwdry
!-----------------------------------------------------------------------
if (.not. aoa_tracers_flag) return
call cnst_add
(c_names(1), mwdry, cpair, 0._r8, ixaoa1, readiv=aoa_read_from_ic_file, &
longname='Age-of_air tracer 1')
ifirst = ixaoa1
call cnst_add
(c_names(2), mwdry, cpair, 0._r8, ixaoa2, readiv=aoa_read_from_ic_file, &
longname='Age-of_air tracer 2')
call cnst_add
(c_names(3), mwdry, cpair, 1._r8, ixht, readiv=aoa_read_from_ic_file, &
longname='horizontal tracer')
call cnst_add
(c_names(4), mwdry, cpair, 0._r8, ixvt, readiv=aoa_read_from_ic_file, &
longname='vertical tracer')
end subroutine aoa_tracers_register
!===============================================================================
function aoa_tracers_implements_cnst(name) 1
!-----------------------------------------------------------------------
!
! Purpose: return true if specified constituent is implemented by this package
!
!-----------------------------------------------------------------------
character(len=*), intent(in) :: name ! constituent name
logical :: aoa_tracers_implements_cnst ! return value
!---------------------------Local workspace-----------------------------
integer :: m
!-----------------------------------------------------------------------
aoa_tracers_implements_cnst = .false.
if (.not. aoa_tracers_flag) return
do m = 1, ncnst
if (name == c_names(m)) then
aoa_tracers_implements_cnst = .true.
return
end if
end do
end function aoa_tracers_implements_cnst
!===============================================================================
subroutine aoa_tracers_init_cnst(name, q, gcid) 1,1
!-----------------------------------------------------------------------
!
! Purpose: initialize test tracers mixing ratio fields
! This subroutine is called at the beginning of an initial run ONLY
!
!-----------------------------------------------------------------------
character(len=*), intent(in) :: name
real(r8), intent(out) :: q(:,:) ! kg tracer/kg dry air (gcol, plev)
integer, intent(in) :: gcid(:) ! global column id
integer :: m
!-----------------------------------------------------------------------
if (.not. aoa_tracers_flag) return
do m = 1, ncnst
if (name == c_names(m)) then
! pass global constituent index
call init_cnst_3d
(ifirst+m-1, q, gcid)
endif
end do
end subroutine aoa_tracers_init_cnst
!===============================================================================
subroutine aoa_tracers_init 1,5
!-----------------------------------------------------------------------
!
! Purpose: initialize age of air constituents
! (declare history variables)
!-----------------------------------------------------------------------
use cam_history
, only: addfld, add_default, phys_decomp
integer :: m, mm
!-----------------------------------------------------------------------
if (.not. aoa_tracers_flag) return
! Set names of tendencies and declare them as history variables
do m = 1, ncnst
mm = ifirst+m-1
call addfld
(cnst_name(mm), 'kg/kg ', pver, 'A', cnst_longname(mm), phys_decomp)
call addfld
(src_names(m), 'kg/kg/s ', pver, 'A', trim(cnst_name(mm))//' source/sink', phys_decomp)
call add_default
(cnst_name(mm), 1, ' ')
call add_default
(src_names(m), 1, ' ')
end do
end subroutine aoa_tracers_init
!===============================================================================
subroutine aoa_tracers_timestep_init( phys_state ) 1,4
!-----------------------------------------------------------------------
! Provides a place to reinitialize diagnostic constituents HORZ and VERT
!-----------------------------------------------------------------------
use time_manager
, only: get_curr_date
use ppgrid
, only: begchunk, endchunk
use physics_types
, only: physics_state
type(physics_state), intent(inout), dimension(begchunk:endchunk), optional :: phys_state
integer c, i, k, ncol
integer yr, mon, day, tod
!--------------------------------------------------------------------------
if (.not. aoa_tracers_flag) return
call get_curr_date
(yr,mon,day,tod)
if ( day == 1 .and. tod == 0) then
if (masterproc) then
write(iulog,*) 'AGE_OF_AIR_CONSTITUENTS: RE-INITIALIZING HORZ/VERT CONSTITUENTS'
endif
do c = begchunk, endchunk
ncol = phys_state(c)%ncol
do k = 1, pver
do i = 1, ncol
phys_state(c)%q(i,k,ixht) = 2._r8 + sin(phys_state(c)%lat(i))
phys_state(c)%q(i,k,ixvt) = real(pver-k+1,r8)
end do
end do
end do
end if
end subroutine aoa_tracers_timestep_init
!===============================================================================
subroutine aoa_tracers_timestep_tend(state, ptend, cflx, landfrac, dt) 1,10
use physics_types
, only: physics_state, physics_ptend, physics_ptend_init
use phys_grid
, only: get_rlat_all_p , get_lat_all_p
use cam_history
, only: outfld
use time_manager
, only: get_nstep
! Arguments
type(physics_state), intent(in) :: state ! state variables
type(physics_ptend), intent(out) :: ptend ! package tendencies
real(r8), intent(inout) :: cflx(pcols,pcnst) ! Surface constituent flux (kg/m^2/s)
real(r8), intent(in) :: landfrac(pcols) ! Land fraction
real(r8), intent(in) :: dt ! timestep
!----------------- Local workspace-------------------------------
integer :: i, k
integer :: lchnk ! chunk identifier
integer :: ncol ! no. of column in chunk
integer :: nstep ! current timestep number
real(r8), parameter :: teul=3.9e-7_r8 ! rate
real(r8) :: qrel ! value to be relaxed to
real(r8) :: xhorz ! updated value of HORZ
real(r8) :: xvert ! updated value of VERT
!------------------------------------------------------------------
call physics_ptend_init
(ptend)
if (.not. aoa_tracers_flag) return
nstep = get_nstep
()
lchnk = state%lchnk
ncol = state%ncol
do k = 1, pver
do i = 1, ncol
! AOA1
ptend%q(i,k,ixaoa1) = 0.0_r8
! AOA2
ptend%q(i,k,ixaoa2) = 0.0_r8
! HORZ
qrel = 2._r8 + sin(state%lat(i))
xhorz = (state%q(i,k,ixht) + dt*teul*qrel)/ (1._r8 + teul * dt)
ptend%q(i,k,ixht) = (xhorz - state%q(i,k,ixht)) / dt
! VERT
qrel = real(pver-k+1,r8)
xvert = (state%q(i,k,ixvt) + dt*teul*qrel)/ (1._r8 + teul * dt)
ptend%q(i,k,ixvt) = (xvert - state%q(i,k,ixvt)) / dt
end do
end do
ptend%lq(ixaoa1) = .TRUE.
ptend%lq(ixaoa2) = .TRUE.
ptend%lq(ixht) = .TRUE.
ptend%lq(ixvt) = .TRUE.
! record tendencies on history files
call outfld
(src_names(1), ptend%q(:,:,ixaoa1), pcols, lchnk)
call outfld
(src_names(2), ptend%q(:,:,ixaoa2), pcols, lchnk)
call outfld
(src_names(3), ptend%q(:,:,ixht), pcols, lchnk)
call outfld
(src_names(4), ptend%q(:,:,ixvt), pcols, lchnk)
! Set tracer fluxes
do i = 1, ncol
! AOA1
cflx(i,ixaoa1) = 1.e-6_r8
! AOA2
if (landfrac(i) .eq. 1._r8 .and. state%lat(i) .gt. 0.35_r8) then
cflx(i,ixaoa2) = 1.e-6_r8 + 1e-6_r8*0.0434_r8*real(nstep,r8)*dt/(86400._r8*365._r8)
else
cflx(i,ixaoa2) = 0._r8
endif
! HORZ
cflx(i,ixht) = 0._r8
! VERT
cflx(i,ixvt) = 0._r8
end do
end subroutine aoa_tracers_timestep_tend
!===========================================================================
subroutine init_cnst_3d(m, q, gcid) 1,4
use dyn_grid
, only : get_horiz_grid_d, get_horiz_grid_dim_d
use dycore
, only : dycore_is
integer, intent(in) :: m ! global constituent index
real(r8), intent(out) :: q(:,:) ! kg tracer/kg dry air (gcol,plev)
integer, intent(in) :: gcid(:) ! global column id
real(r8), allocatable :: lat(:)
integer :: plon, plat, ngcols
integer :: j, k, gsize
!-----------------------------------------------------------------------
if (masterproc) write(iulog,*) 'AGE-OF-AIR CONSTITUENTS: INITIALIZING ',cnst_name(m),m
if (m == ixaoa1) then
q(:,:) = 0.0_r8
else if (m == ixaoa2) then
q(:,:) = 0.0_r8
else if (m == ixht) then
call get_horiz_grid_dim_d
( plon, plat )
ngcols = plon*plat
gsize = size(gcid)
allocate(lat(ngcols))
call get_horiz_grid_d
(ngcols,clat_d_out=lat)
do j = 1, gsize
q(j,:) = 2._r8 + sin(lat(gcid(j)))
end do
deallocate(lat)
else if (m == ixvt) then
do k = 1, pver
do j = 1, size(q,1)
q(j,k) = real(pver-k+1,r8)
end do
end do
end if
end subroutine init_cnst_3d
!=====================================================================
end module aoa_tracers