!
module gw_drag 5,8
!---------------------------------------------------------------------------------
! Purpose:
!
! Module to compute the forcing due to parameterized gravity waves. Both an
! orographic and an internal source spectrum are considered.
!
! Author: Byron Boville
!
!---------------------------------------------------------------------------------
use shr_kind_mod
, only: r8 => shr_kind_r8
use spmd_utils
, only: masterproc
use ppgrid
, only: pcols, pver
use physics_types
, only: physics_state, physics_ptend
use cam_history
, only: outfld
use scamMod
, only: single_column
use cam_logfile
, only: iulog
use abortutils
, only: endrun
implicit none
save
private ! Make default type private to the module
!
! PUBLIC: interfaces
!
public gw_drag_readnl ! Read namelist
public gw_inti ! Initialization
public gw_intr ! interface to actual parameterization
!
! PRIVATE: Rest of the data and interfaces are private to this module
!
real(r8), parameter :: unset_r8 = huge(1._r8)
! fcrit2 has been made a namelist variable to facilitate backwards compatibility
! with the CAM3 version of this parameterization. In CAM3 fcrit2=0.5
real(r8) :: fcrit2 = unset_r8 ! critical froude number squared
integer, parameter :: pgwv = 0 ! number of waves allowed
integer :: kbotbg, kbotoro ! interface of gwd source
integer :: ktopbg, ktoporo ! top interface of gwd region
real(r8) :: alpha(0:pver) ! newtonian cooling coefficients
real(r8) :: c(-pgwv:pgwv) ! list of wave phase speeds
real(r8) :: cpair ! specific heat of dry air (constant p)
real(r8) :: dback ! background diffusivity
real(r8) :: effkwv ! effective wavenumber (fcrit2*kwv)
real(r8) :: effgw_oro ! tendency efficiency for orographic gw
real(r8) :: effgw_spec=.125_r8 ! tendency efficiency for internal gw
real(r8) :: fracldv ! fraction of stress deposited in low level region
real(r8) :: g ! acceleration of gravity
real(r8) :: kwv ! effective horizontal wave number
real(r8) :: lzmax ! maximum vertical wavelength at source
real(r8) :: mxasym ! max asymmetry between tau(c) and tau(-c)
real(r8) :: mxrange ! max range of tau for all c
real(r8) :: n2min ! min value of bouyancy frequency
real(r8) :: oroko2 ! 1/2 * horizontal wavenumber
real(r8) :: orohmin ! min surface displacment height for orographic waves
real(r8) :: orovmin ! min wind speed for orographic waves
real(r8) :: r ! gas constant for dry air
real(r8) :: rog ! r / g
real(r8) :: taubgnd ! background source strength (/tauscal)
real(r8) :: taumin ! minimum (nonzero) stress
real(r8) :: tauscal ! scale factor for background stress source
real(r8) :: tndmax ! maximum wind tendency
real(r8) :: umcfac ! factor to limit tendency to prevent reversing u-c
real(r8) :: ubmc2mn ! min (u-c)**2
real(r8) :: zldvcon ! constant for determining zldv from tau0
!===============================================================================
contains
!===============================================================================
subroutine gw_drag_readnl(nlfile) 1,9
use namelist_utils
, only: find_group_name
use units
, only: getunit, freeunit
use mpishorthand
character(len=*), intent(in) :: nlfile ! filepath for file containing namelist input
! Local variables
integer :: unitn, ierr
character(len=*), parameter :: subname = 'gw_drag_readnl'
namelist /gw_drag_nl/ fcrit2
!-----------------------------------------------------------------------------
if (masterproc) then
unitn = getunit
()
open( unitn, file=trim(nlfile), status='old' )
call find_group_name
(unitn, 'gw_drag_nl', status=ierr)
if (ierr == 0) then
read(unitn, gw_drag_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
! Broadcast namelist variables
call mpibcast
(fcrit2, 1, mpir8, 0, mpicom)
#endif
! Error checking:
! check if fcrit2 was set.
if (fcrit2 == unset_r8) then
call endrun
('gw_dray_readnl: fcrit2 must be set via the namelist')
end if
end subroutine gw_drag_readnl
!================================================================================
subroutine gw_inti (cpairx, cpwv, gx, rx, hypi) 1,16
!-----------------------------------------------------------------------
! Time independent initialization for multiple gravity wave parameterization.
!-----------------------------------------------------------------------
use cam_history
, only: addfld, add_default, phys_decomp
use dycore
, only: dycore_is
use phys_control
,only: phys_getopts
!------------------------------Arguments--------------------------------
real(r8), intent(in) :: cpairx ! specific heat of dry air (constant p)
real(r8), intent(in) :: cpwv ! specific heat of water vapor (constant p)
real(r8), intent(in) :: gx ! acceleration of gravity
real(r8), intent(in) :: rx ! gas constant for dry air
real(r8), intent(in) :: hypi(pver+1) ! reference interface pressures
!---------------------------Local storage-------------------------------
integer :: k
logical :: history_budget ! output tendencies and state variables for CAM4
! temperature, water vapor, cloud ice and cloud
! liquid budgets.
!-----------------------------------------------------------------------
! Copy model constants
cpair = cpairx
g = gx
r = rx
! Set MGWD constants
kwv = 6.28e-5_r8 ! 100 km wave length
dback = 0.05_r8 ! background diffusivity
tauscal= 0.001_r8 ! scale factor for background stress
taubgnd= 6.4_r8 ! background stress amplitude
zldvcon= 10._r8 ! constant for determining zldv
lzmax = 7.E3_r8 ! maximum vertical wavelength at source (m)
if ( dycore_is
('LR') ) then
effgw_oro = 0.125_r8
fracldv= 0.0_r8
! Restore these to work with turulent mountain stress
! effgw_oro = 1.0
! fracldv= 0.7 ! fraction of tau0 diverged in low level region
else
effgw_oro = 0.125_r8
fracldv= 0.0_r8
endif
! Set phase speeds
do k = -pgwv, pgwv
c(k) = 10._r8 * k ! 0, +/- 10, +/- 20, ... m/s
end do
if (masterproc) then
write(iulog,*) ' '
write(iulog,*) 'GW_INTI: pgwv = ', pgwv
write(iulog,*) 'GW_INTI: c(l) = ', c
write(iulog,*) 'GW_INTI: effgw_oro = ', effgw_oro
write(iulog,*) 'GW_INTI: kwv = ', kwv
write(iulog,*) 'GW_INTI: fcrit2 = ', fcrit2
write(iulog,*) ' '
end if
! Set radiative damping times
do k = 0, pver
alpha(k) = 1.e-6_r8 ! about 10 days.
end do
! Min and max values to keep things reasonable
mxasym = 0.1_r8 ! max factor of 10 from |tau(c)| to |tau(-c)|
mxrange= 0.001_r8 ! factor of 100 from max to min |tau(c)|
n2min = 1.e-8_r8 ! min value of Brunt-Vaisalla freq squared
orohmin= 10._r8 ! min surface displacement for orographic wave drag
orovmin= 2._r8 ! min wind speed for orographic wave drag
taumin = 1.e-10_r8 ! min stress considered > 0
tndmax = 500._r8 / 86400._r8 ! max permitted tendency (500 m/s/day)
umcfac = 0.5_r8 ! max permitted reduction in u-c
ubmc2mn= 0.01_r8 ! min value of (u-c)^2
! Determine other derived constants
oroko2 = 0.5_r8 * kwv
effkwv = fcrit2 * kwv
rog = r/g
! Determine the bounds of the background and orographic stress regions
ktopbg = 0
kbotoro = pver
do k = 0, pver
if (hypi(k+1) .lt. 10000._r8) kbotbg = k ! spectrum source at 100 mb
!!$ if (hypi(k+1) .lt. 3000.) ktoporo = k
end do
ktoporo = 0
if (masterproc) then
write(iulog,*) 'KTOPBG =',ktopbg
write(iulog,*) 'KBOTBG =',kbotbg
write(iulog,*) 'KTOPORO =',ktoporo
write(iulog,*) 'KBOTORO =',kbotoro
end if
! Declare history variables for orgraphic term
call addfld
('TTGWORO ','K/s ',pver, 'A','T tendency - orographic gravity wave drag',phys_decomp)
call addfld
('UTGWORO ','m/s2 ',pver, 'A','U tendency - orographic gravity wave drag',phys_decomp)
call addfld
('VTGWORO ','m/s2 ',pver, 'A','V tendency - orographic gravity wave drag',phys_decomp)
call addfld
('TAUGWX ','N/m2 ',1, 'A','Zonal gravity wave surface stress', phys_decomp)
call addfld
('TAUGWY ','N/m2 ',1, 'A','Meridional gravity wave surface stress', phys_decomp)
call phys_getopts
(history_budget_out = history_budget)
if ( history_budget ) then
call add_default
('TTGWORO', 1, ' ')
end if
! call add_default ('UTGWORO ', 1, ' ')
! call add_default ('VTGWORO ', 1, ' ')
! call add_default ('TAUGWX ', 1, ' ')
! call add_default ('TAUGWY ', 1, ' ')
! Declare history variables for spectrum
if (pgwv > 0) then
call addfld
('TTGWSPEC','K/s ',pver, 'A','T tendency - gravity wave spectrum', phys_decomp)
call addfld
('UTGWSPEC','m/s2 ',pver, 'A','U tendency - gravity wave spectrum', phys_decomp)
call addfld
('VTGWSPEC','m/s2 ',pver, 'A','V tendency - gravity wave spectrum', phys_decomp)
call add_default
('UTGWSPEC', 1, ' ')
call add_default
('VTGWSPEC', 1, ' ')
end if
return
end subroutine gw_inti
!===============================================================================
subroutine gw_intr (state, sgh, pblh, dt, ptend, landfrac, kvh) 1,13
!-----------------------------------------------------------------------
! Interface for multiple gravity wave drag parameterization.
!-----------------------------------------------------------------------
!------------------------------Arguments--------------------------------
real(r8), intent(in) :: sgh(pcols) ! standard deviation of orography
real(r8), intent(in) :: pblh(pcols) ! planetary boundary layer height
real(r8), intent(in) :: dt ! time step
real(r8), intent(in) :: landfrac(pcols) ! Land fraction
real(r8), intent(in) :: kvh(pcols,0:pver) ! molecular thermal diffusivity
type(physics_state), intent(in) :: state ! physics state structure
type(physics_ptend), intent(inout):: ptend ! parameterization tendency structure
!---------------------------Local storage-------------------------------
integer :: lchnk ! chunk identifier
integer :: ncol ! number of atmospheric columns
integer :: i,k ! loop indexes
integer :: kldv(pcols) ! top interface of low level stress divergence region
integer :: kldvmn ! min value of kldv
integer :: ksrc(pcols) ! index of top interface of source region
integer :: ksrcmn ! min value of ksrc
real(r8) :: ttgw(pcols,pver) ! temperature tendency
real(r8) :: utgw(pcols,pver) ! zonal wind tendency
real(r8) :: vtgw(pcols,pver) ! meridional wind tendency
real(r8) :: ni(pcols,0:pver) ! interface Brunt-Vaisalla frequency
real(r8) :: nm(pcols,pver) ! midpoint Brunt-Vaisalla frequency
real(r8) :: rdpldv(pcols) ! 1/dp across low level divergence region
real(r8) :: rhoi(pcols,0:pver) ! interface density
real(r8) :: tau(pcols,-pgwv:pgwv,0:pver) ! wave Reynolds stress
real(r8) :: tau0x(pcols) ! c=0 sfc. stress (zonal)
real(r8) :: tau0y(pcols) ! c=0 sfc. stress (meridional)
real(r8) :: ti(pcols,0:pver) ! interface temperature
real(r8) :: ubi(pcols,0:pver) ! projection of wind at interfaces
real(r8) :: ubm(pcols,pver) ! projection of wind at midpoints
real(r8) :: xv(pcols) ! unit vectors of source wind (x)
real(r8) :: yv(pcols) ! unit vectors of source wind (y)
!-----------------------------------------------------------------------------
lchnk = state%lchnk
ncol = state%ncol
! Profiles of background state variables
call gw_prof
(lchnk, ncol, &
state%u , state%v , state%t , state%pmid , state%pint, &
rhoi , ni , ti , nm)
!-----------------------------------------------------------------------------
! Non-orographic backgound gravity wave spectrum
!-----------------------------------------------------------------------------
if (pgwv >0) then
! Determine the wave source for a background spectrum at ~100 mb
call gw_bgnd
(lchnk , ncol , &
state%u , state%v , state%t , state%pmid , state%pint , &
state%pdel , state%rpdel, state%lnpint,kldv , kldvmn , &
ksrc , ksrcmn , rdpldv , tau , ubi , &
ubm , xv , yv , PGWV , kbotbg )
! Solve for the drag profile
call gw_drag_prof
(lchnk , ncol , &
PGWV , kbotbg , ktopbg , state%u , state%v , &
state%t , state%pint , state%pdel , state%rpdel, state%lnpint,&
rhoi , ni , ti , nm , dt , &
kldv , kldvmn , ksrc , ksrcmn , rdpldv , &
tau , ubi , ubm , xv , yv , &
effgw_spec , utgw , vtgw , tau0x , tau0y )
! Add the momentum tendencies to the output tendency arrays
do k = 1, pver
do i = 1, ncol
ptend%u(i,k) = utgw(i,k)
ptend%v(i,k) = vtgw(i,k)
end do
end do
! Write output fields to history file
call outfld
('UTGWSPEC', utgw, pcols, lchnk)
call outfld
('VTGWSPEC', vtgw, pcols, lchnk)
! zero net tendencies if no spectrum computed
else
ptend%u = 0._r8
ptend%v = 0._r8
end if
!-----------------------------------------------------------------------------
! Orographic stationary gravity wave
!-----------------------------------------------------------------------------
! Determine the orographic wave source
call gw_oro
(lchnk, ncol, &
state%u , state%v , state%t , sgh , state%pmid , &
state%pint , state%pdel , state%zm , nm , pblh , &
kldv , kldvmn , ksrc , ksrcmn , rdpldv , &
tau , ubi , ubm , xv , yv )
! Solve for the drag profile
call gw_drag_prof
(lchnk, ncol, &
0 , kbotoro , ktoporo , state%u , state%v , &
state%t , state%pint , state%pdel , state%rpdel, state%lnpint,&
rhoi , ni , ti , nm , dt , &
kldv , kldvmn , ksrc , ksrcmn , rdpldv , &
tau , ubi , ubm , xv , yv , &
effgw_oro , utgw , vtgw , tau0x , tau0y )
! Add the orographic tendencies to the spectrum tendencies
! Compute the temperature tendency from energy conservation (includes spectrum).
do k = 1, pver
do i = 1, ncol
ptend%u(i,k) = ptend%u(i,k) + utgw(i,k) * landfrac(i)
ptend%v(i,k) = ptend%v(i,k) + vtgw(i,k) * landfrac(i)
ptend%s(i,k) = -(ptend%u(i,k) * (state%u(i,k) + ptend%u(i,k)*0.5_r8*dt) &
+ptend%v(i,k) * (state%v(i,k) + ptend%v(i,k)*0.5_r8*dt))
ttgw(i,k) = ptend%s(i,k) / cpair
utgw(i,k) = ptend%u(i,k)
vtgw(i,k) = ptend%v(i,k)
end do
end do
! Set flags for nonzero tendencies, q not yet affected by gwd
ptend%name = "vertical diffusion"
ptend%lq(:) = .FALSE.
ptend%ls = .TRUE.
ptend%lu = .TRUE.
ptend%lv = .TRUE.
! Write output fields to history file
call outfld
('UTGWORO', utgw, pcols, lchnk)
call outfld
('VTGWORO', vtgw, pcols, lchnk)
call outfld
('TTGWORO', ttgw, pcols, lchnk)
call outfld
('TAUGWX', tau0x, pcols, lchnk)
call outfld
('TAUGWY', tau0y, pcols, lchnk)
call outfld
('SGH ', sgh, pcols, lchnk)
return
end subroutine gw_intr
!===============================================================================
subroutine gw_prof (lchnk, ncol, u, v, t, pm, pi, rhoi, ni, ti, nm) 1
!-----------------------------------------------------------------------
! Compute profiles of background state quantities for the multiple
! gravity wave drag parameterization.
!
! The parameterization is assumed to operate only where water vapor
! concentrations are negligible in determining the density.
!-----------------------------------------------------------------------
!------------------------------Arguments--------------------------------
integer, intent(in) :: lchnk ! chunk identifier
integer, intent(in) :: ncol ! number of atmospheric columns
real(r8), intent(in) :: u(pcols,pver) ! midpoint zonal wind
real(r8), intent(in) :: v(pcols,pver) ! midpoint meridional wind
real(r8), intent(in) :: t(pcols,pver) ! midpoint temperatures
real(r8), intent(in) :: pm(pcols,pver) ! midpoint pressures
real(r8), intent(in) :: pi(pcols,0:pver) ! interface pressures
real(r8), intent(out) :: rhoi(pcols,0:pver) ! interface density
real(r8), intent(out) :: ni(pcols,0:pver) ! interface Brunt-Vaisalla frequency
real(r8), intent(out) :: ti(pcols,0:pver) ! interface temperature
real(r8), intent(out) :: nm(pcols,pver) ! midpoint Brunt-Vaisalla frequency
!---------------------------Local storage-------------------------------
integer :: i,k ! loop indexes
real(r8) :: dtdp
real(r8) :: n2 ! Brunt-Vaisalla frequency squared
!-----------------------------------------------------------------------------
! Determine the interface densities and Brunt-Vaisala frequencies.
!-----------------------------------------------------------------------------
! The top interface values are calculated assuming an isothermal atmosphere
! above the top level.
k = 0
do i = 1, ncol
ti(i,k) = t(i,k+1)
rhoi(i,k) = pi(i,k) / (r*ti(i,k))
ni(i,k) = sqrt (g*g / (cpair*ti(i,k)))
end do
! Interior points use centered differences
do k = 1, pver-1
do i = 1, ncol
ti(i,k) = 0.5_r8 * (t(i,k) + t(i,k+1))
rhoi(i,k) = pi(i,k) / (r*ti(i,k))
dtdp = (t(i,k+1)-t(i,k)) / (pm(i,k+1)-pm(i,k))
n2 = g*g/ti(i,k) * (1._r8/cpair - rhoi(i,k)*dtdp)
ni(i,k) = sqrt (max (n2min, n2))
end do
end do
! Bottom interface uses bottom level temperature, density; next interface
! B-V frequency.
k = pver
do i = 1, ncol
ti(i,k) = t(i,k)
rhoi(i,k) = pi(i,k) / (r*ti(i,k))
ni(i,k) = ni(i,k-1)
end do
!-----------------------------------------------------------------------------
! Determine the midpoint Brunt-Vaisala frequencies.
!-----------------------------------------------------------------------------
do k=1,pver
do i=1,ncol
nm(i,k) = 0.5_r8 * (ni(i,k-1) + ni(i,k))
end do
end do
return
end subroutine gw_prof
!===============================================================================
subroutine gw_oro (lchnk, ncol, & 1
u, v, t, sgh, pm, pi, dpm, zm, nm, pblh, &
kldv, kldvmn, ksrc, ksrcmn, rdpldv, tau, ubi, ubm, xv, yv)
!-----------------------------------------------------------------------
! Orographic source for multiple gravity wave drag parameterization.
!
! The stress is returned for a single wave with c=0, over orography.
! For points where the orographic variance is small (including ocean),
! the returned stress is zero.
!------------------------------Arguments--------------------------------
integer, intent(in) :: lchnk ! chunk identifier
integer, intent(in) :: ncol ! number of atmospheric columns
real(r8), intent(in) :: u(pcols,pver) ! midpoint zonal wind
real(r8), intent(in) :: v(pcols,pver) ! midpoint meridional wind
real(r8), intent(in) :: t(pcols,pver) ! midpoint temperatures
real(r8), intent(in) :: sgh(pcols) ! standard deviation of orography
real(r8), intent(in) :: pm(pcols,pver) ! midpoint pressures
real(r8), intent(in) :: pi(pcols,0:pver) ! interface pressures
real(r8), intent(in) :: dpm(pcols,pver) ! midpoint delta p (pi(k)-pi(k-1))
real(r8), intent(in) :: zm(pcols,pver) ! midpoint heights
real(r8), intent(in) :: nm(pcols,pver) ! midpoint Brunt-Vaisalla frequency
real(r8), intent(in) :: pblh(pcols) ! planetary boundary layer height
integer, intent(out) :: kldv(pcols) ! top interface of low level stress div region
integer, intent(out) :: kldvmn ! min value of kldv
integer, intent(out) :: ksrc(pcols) ! index of top interface of source region
integer, intent(out) :: ksrcmn ! min value of ksrc
real(r8), intent(out) :: rdpldv(pcols) ! 1/dp across low level divergence region
real(r8), intent(out) :: tau(pcols,-pgwv:pgwv,0:pver)! wave Reynolds stress
real(r8), intent(out) :: ubi(pcols,0:pver) ! projection of wind at interfaces
real(r8), intent(out) :: ubm(pcols,pver) ! projection of wind at midpoints
real(r8), intent(out) :: xv(pcols) ! unit vectors of source wind (x)
real(r8), intent(out) :: yv(pcols) ! unit vectors of source wind (y)
!---------------------------Local storage-------------------------------
integer :: i,k ! loop indexes
real(r8) :: pil ! don't we have pi somewhere?
real(r8) :: lzsrc ! vertical wavelength at source
real(r8) :: hdsp(pcols) ! surface streamline displacment height (2*sgh)
real(r8) :: sghmax ! max orographic sdv to use
real(r8) :: tauoro(pcols) ! c=0 stress from orography
real(r8) :: zldv(pcols) ! top of the low level stress divergence region
real(r8) :: nsrc(pcols) ! b-f frequency averaged over source region
real(r8) :: psrc(pcols) ! interface pressure at top of source region
real(r8) :: rsrc(pcols) ! density averaged over source region
real(r8) :: usrc(pcols) ! u wind averaged over source region
real(r8) :: vsrc(pcols) ! v wind averaged over source region
!---------------------------------------------------------------------------
! Average the basic state variables for the wave source over the depth of
! the orographic standard deviation. Here we assume that the apropiate
! values of wind, stability, etc. for determining the wave source are
! averages over the depth of the atmosphere pentrated by the typical mountain.
! Reduces to the bottom midpoint values when sgh=0, such as over ocean.
!---------------------------------------------------------------------------
k = pver
do i = 1, ncol
ksrc(i) = k-1
psrc(i) = pi(i,k-1)
rsrc(i) = pm(i,k)/(r*t(i,k)) * dpm(i,k)
usrc(i) = u(i,k) * dpm(i,k)
vsrc(i) = v(i,k) * dpm(i,k)
nsrc(i) = nm(i,k)* dpm(i,k)
hdsp(i) = 2.0_r8 * sgh(i)
end do
do k = pver-1, pver/2, -1
do i = 1, ncol
if (hdsp(i) > sqrt(zm(i,k)*zm(i,k+1))) then
ksrc(i) = k-1
psrc(i) = pi(i,k-1)
rsrc(i) = rsrc(i) + pm(i,k) / (r*t(i,k))* dpm(i,k)
usrc(i) = usrc(i) + u(i,k) * dpm(i,k)
vsrc(i) = vsrc(i) + v(i,k) * dpm(i,k)
nsrc(i) = nsrc(i) + nm(i,k)* dpm(i,k)
end if
end do
end do
do i = 1, ncol
rsrc(i) = rsrc(i) / (pi(i,pver) - psrc(i))
usrc(i) = usrc(i) / (pi(i,pver) - psrc(i))
vsrc(i) = vsrc(i) / (pi(i,pver) - psrc(i))
nsrc(i) = nsrc(i) / (pi(i,pver) - psrc(i))
if (single_column) then
! needed the following fix when winds are identically 0
! orig -> ubi(i,pver) = sqrt (usrc(i)**2 + vsrc(i)**2)
ubi(i,pver) = max(sqrt (usrc(i)**2 + vsrc(i)**2),orovmin)
else
ubi(i,pver) = sqrt (usrc(i)**2 + vsrc(i)**2)
#if (defined BFB_CAM_SCAM_IOP )
ubi(i,pver) = max(sqrt (usrc(i)**2 + vsrc(i)**2),orovmin)
#endif
endif
xv(i) = usrc(i) / ubi(i,pver)
yv(i) = vsrc(i) / ubi(i,pver)
end do
! Project the local wind at midpoints onto the source wind.
do k = 1, pver
do i = 1, ncol
ubm(i,k) = u(i,k) * xv(i) + v(i,k) * yv(i)
end do
end do
! Compute the interface wind projection by averaging the midpoint winds.
! Use the top level wind at the top interface.
do i = 1, ncol
ubi(i,0) = ubm(i,1)
end do
do k = 1, pver-1
do i = 1, ncol
ubi(i,k) = 0.5_r8 * (ubm(i,k) + ubm(i,k+1))
end do
end do
!---------------------------------------------------------------------------
! Determine the depth of the low level stress divergence region, as
! the max of the source region depth and 1/2 the vertical wavelength at the
! source.
!---------------------------------------------------------------------------
pil = acos(-1._r8)
do i = 1, ncol
lzsrc = min(2._r8 * pil * usrc(i) / nsrc(i), lzmax)
zldv(i) = max(hdsp(i), 0.5_r8 * lzsrc)
end do
! find the index of the top of the low level divergence region
kldv(:) = pver-1
do k = pver-1, pver/2, -1
do i = 1, ncol
if (zldv(i) .gt. sqrt(zm(i,k)*zm(i,k+1))) then
kldv(i) = k-1
end if
end do
end do
! Determine the orographic c=0 source term following McFarlane (1987).
! Set the source top interface index to pver, if the orographic term is zero.
do i = 1, ncol
if ((ubi(i,pver) .gt. orovmin) .and. (hdsp(i) .gt. orohmin)) then
sghmax = fcrit2 * (ubi(i,pver) / nsrc(i))**2
tauoro(i) = oroko2 * min(hdsp(i)**2, sghmax) * rsrc(i) * nsrc(i) * ubi(i,pver)
else
tauoro(i) = 0._r8
ksrc(i) = pver
kldv(i) = pver
end if
end do
! Set the phase speeds and wave numbers in the direction of the source wind.
! Set the source stress magnitude (positive only, note that the sign of the
! stress is the same as (c-u).
do i = 1, ncol
tau(i,0,pver) = tauoro(i)
end do
! Determine the min value of kldv and ksrc for limiting later loops
! and the pressure at the top interface of the low level stress divergence
! region.
ksrcmn = pver
kldvmn = pver
do i = 1, ncol
ksrcmn = min(ksrcmn, ksrc(i))
kldvmn = min(kldvmn, kldv(i))
if (kldv(i) .ne. pver) then
rdpldv(i) = 1._r8 / (pi(i,kldv(i)) - pi(i,pver))
end if
end do
if (fracldv .le. 0._r8) kldvmn = pver
return
end subroutine gw_oro
!===============================================================================
subroutine gw_bgnd (lchnk, ncol, & 1,2
u, v, t, pm, pi, dpm, rdpm, piln, &
kldv, kldvmn, ksrc, ksrcmn, rdpldv, tau, ubi, ubm, xv, yv, &
ngwv, kbot)
!-----------------------------------------------------------------------
! Driver for multiple gravity wave drag parameterization.
!
! The parameterization is assumed to operate only where water vapor
! concentrations are negligible in determining the density.
!-----------------------------------------------------------------------
use phys_grid
, only: get_rlat_all_p
!------------------------------Arguments--------------------------------
integer, intent(in) :: lchnk ! chunk identifier
integer, intent(in) :: ncol ! number of atmospheric columns
integer, intent(in) :: kbot ! index of bottom (source) interface
integer, intent(in) :: ngwv ! number of gravity waves to use
real(r8), intent(in) :: u(pcols,pver) ! midpoint zonal wind
real(r8), intent(in) :: v(pcols,pver) ! midpoint meridional wind
real(r8), intent(in) :: t(pcols,pver) ! midpoint temperatures
real(r8), intent(in) :: pm(pcols,pver) ! midpoint pressures
real(r8), intent(in) :: pi(pcols,0:pver) ! interface pressures
real(r8), intent(in) :: dpm(pcols,pver) ! midpoint delta p (pi(k)-pi(k-1))
real(r8), intent(in) :: rdpm(pcols,pver) ! 1. / (pi(k)-pi(k-1))
real(r8), intent(in) :: piln(pcols,0:pver) ! ln(interface pressures)
integer, intent(out) :: kldv(pcols) ! top interface of low level stress divergence region
integer, intent(out) :: kldvmn ! min value of kldv
integer, intent(out) :: ksrc(pcols) ! index of top interface of source region
integer, intent(out) :: ksrcmn ! min value of ksrc
real(r8), intent(in) :: rdpldv(pcols) ! 1/dp across low level divergence region
real(r8), intent(out) :: tau(pcols,-pgwv:pgwv,0:pver)! wave Reynolds stress
real(r8), intent(out) :: ubi(pcols,0:pver) ! projection of wind at interfaces
real(r8), intent(out) :: ubm(pcols,pver) ! projection of wind at midpoints
real(r8), intent(out) :: xv(pcols) ! unit vectors of source wind (x)
real(r8), intent(out) :: yv(pcols) ! unit vectors of source wind (y)
!---------------------------Local storage-------------------------------
integer :: i,k,l ! loop indexes
real(r8) :: rlat(pcols) ! latitude in radians for columns
real(r8) :: tauback(pcols) ! background stress at c=0
real(r8) :: usrc(pcols) ! u wind averaged over source region
real(r8) :: vsrc(pcols) ! v wind averaged over source region
real(r8) :: al0 ! Used in lat dependence of GW spec.
real(r8) :: dlat0 ! Used in lat dependence of GW spec.
real(r8) :: flat_gw ! The actual lat dependence of GW spec.
real(r8) :: pi_g ! 3.14........
!---------------------------------------------------------------------------
! Determine the source layer wind and unit vectors, then project winds.
!---------------------------------------------------------------------------
! Just use the source level interface values for the source
! wind speed and direction (unit vector).
k = kbot
do i = 1, ncol
ksrc(i) = k
kldv(i) = k
usrc(i) = 0.5_r8*(u(i,k+1)+u(i,k))
vsrc(i) = 0.5_r8*(v(i,k+1)+v(i,k))
ubi(i,kbot) = sqrt (usrc(i)**2 + vsrc(i)**2)
xv(i) = usrc(i) / ubi(i,k)
yv(i) = vsrc(i) / ubi(i,k)
end do
! Project the local wind at midpoints onto the source wind.
do k = 1, kbot
do i = 1, ncol
ubm(i,k) = u(i,k) * xv(i) + v(i,k) * yv(i)
end do
end do
! Compute the interface wind projection by averaging the midpoint winds.
! Use the top level wind at the top interface.
do i = 1, ncol
ubi(i,0) = ubm(i,1)
end do
do k = 1, kbot-1
do i = 1, ncol
ubi(i,k) = 0.5_r8 * (ubm(i,k) + ubm(i,k+1))
end do
end do
!-----------------------------------------------------------------------
! Gravity wave sources
!-----------------------------------------------------------------------
! Determine the background stress at c=0
do i=1,ncol
tauback(i) = taubgnd * tauscal
enddo
! Include dependence on latitude:
! The lat function was obtained by RR Garcia as
! currently used in his 2D model
! [Added by F. Sassi on May 30, 1997]
pi_g=4._r8*atan(1._r8)
al0=40._r8*pi_g/180._r8
dlat0=10._r8*pi_g/180._r8
!
call get_rlat_all_p
(lchnk, ncol, rlat)
do i=1,ncol
flat_gw= 0.5_r8*(1._r8+tanh((rlat(i)-al0)/dlat0)) + 0.5_r8*(1._r8+tanh(-(rlat(i)+al0)/dlat0))
flat_gw=max(flat_gw,0.2_r8)
tauback(i)=tauback(i)*flat_gw
enddo
! Set the phase speeds and wave numbers in the direction of the source wind.
! Set the source stress magnitude (positive only, note that the sign of the
! stress is the same as (c-u).
do l = 1, ngwv
do i = 1, ncol
tau(i, l,kbot) = tauback(i) * exp(-(c(l)/30._r8)**2)
tau(i,-l,kbot) = tau(i, l,kbot)
end do
end do
do i = 1, ncol
tau(i,0,kbot) = tauback(i)
end do
! Determine the min value of kldv and ksrc for limiting later loops
! and the pressure at the top interface of the low level stress divergence
! region.
ksrcmn = pver
kldvmn = pver
return
end subroutine gw_bgnd
!===============================================================================
subroutine gw_drag_prof (lchnk, ncol, & 2
ngwv, kbot, ktop, u, v, t, &
pi, dpm, rdpm, piln, rhoi, ni, ti, nm, dt, &
kldv, kldvmn, ksrc, ksrcmn, rdpldv, tau, ubi, ubm, xv, yv, &
effgw, ut, vt, tau0x, tau0y)
!-----------------------------------------------------------------------
! Solve for the drag profile from the multiple gravity wave drag
! parameterization.
! 1. scan up from the wave source to determine the stress profile
! 2. scan down the stress profile to determine the tendencies
! => apply bounds to the tendency
! a. from wkb solution
! b. from computational stability constraints
! => adjust stress on interface below to reflect actual bounded tendency
!-----------------------------------------------------------------------
!------------------------------Arguments--------------------------------
integer, intent(in) :: lchnk ! chunk identifier
integer, intent(in) :: ncol ! number of atmospheric columns
integer, intent(in) :: kbot ! index of bottom (source) interface
integer, intent(in) :: ktop ! index of top interface of gwd region
integer, intent(in) :: ngwv ! number of gravity waves to use
integer, intent(in) :: kldv(pcols) ! top interface of low level stress divergence region
integer, intent(in) :: kldvmn ! min value of kldv
integer, intent(in) :: ksrc(pcols) ! index of top interface of source region
integer, intent(in) :: ksrcmn ! min value of ksrc
real(r8), intent(in) :: u(pcols,pver) ! midpoint zonal wind
real(r8), intent(in) :: v(pcols,pver) ! midpoint meridional wind
real(r8), intent(in) :: t(pcols,pver) ! midpoint temperatures
real(r8), intent(in) :: pi(pcols,0:pver) ! interface pressures
real(r8), intent(in) :: dpm(pcols,pver) ! midpoint delta p (pi(k)-pi(k-1))
real(r8), intent(in) :: rdpm(pcols,pver) ! 1. / (pi(k)-pi(k-1))
real(r8), intent(in) :: piln(pcols,0:pver) ! ln(interface pressures)
real(r8), intent(in) :: rhoi(pcols,0:pver) ! interface density
real(r8), intent(in) :: ni(pcols,0:pver) ! interface Brunt-Vaisalla frequency
real(r8), intent(in) :: ti(pcols,0:pver) ! interface temperature
real(r8), intent(in) :: nm(pcols,pver) ! midpoint Brunt-Vaisalla frequency
real(r8), intent(in) :: dt ! time step
real(r8), intent(in) :: rdpldv(pcols) ! 1/dp across low level divergence region
real(r8), intent(in) :: ubi(pcols,0:pver) ! projection of wind at interfaces
real(r8), intent(in) :: ubm(pcols,pver) ! projection of wind at midpoints
real(r8), intent(in) :: xv(pcols) ! unit vectors of source wind (x)
real(r8), intent(in) :: yv(pcols) ! unit vectors of source wind (y)
real(r8), intent(in) :: effgw ! tendency efficiency
real(r8), intent(inout) :: tau(pcols,-pgwv:pgwv,0:pver)! wave Reynolds stress
real(r8), intent(out) :: ut(pcols,pver) ! zonal wind tendency
real(r8), intent(out) :: vt(pcols,pver) ! meridional wind tendency
real(r8), intent(out) :: tau0x(pcols) ! c=0 sfc. stress (zonal)
real(r8), intent(out) :: tau0y(pcols) ! c=0 sfc. stress (meridional)
!---------------------------Local storage-------------------------------
integer :: i,k,l ! loop indexes
real(r8) :: d(pcols) ! "total" diffusivity
real(r8) :: dsat(pcols,-pgwv:pgwv) ! saturation diffusivity
real(r8) :: dscal ! fraction of dsat to use
real(r8) :: mi ! imaginary part of vertical wavenumber
real(r8) :: taudmp ! stress after damping
real(r8) :: taumax(pcols) ! max(tau) for any l
real(r8) :: tausat(pcols,-pgwv:pgwv) ! saturation stress
real(r8) :: ubmc(pcols,-pgwv:pgwv) ! (ub-c)
real(r8) :: ubmc2 ! (ub-c)**2
real(r8) :: ubt(pcols,pver) ! ubar tendency
real(r8) :: ubtl ! ubar tendency from wave l
real(r8) :: ubtlsat ! saturation tendency
! Initialize gravity wave drag tendencies to zero
do k=1,pver
do i=1,pcols
ut(i,k) = 0._r8
vt(i,k) = 0._r8
end do
end do
!---------------------------------------------------------------------------
! Compute the stress profiles and diffusivities
!---------------------------------------------------------------------------
! Loop from bottom to top to get stress profiles
do k = kbot-1, ktop, -1
! Determine the absolute value of the saturation stress and the diffusivity
! for each wave.
! Define critical levels where the sign of (u-c) changes between interfaces.
d(:ncol) = dback
do l = -ngwv, ngwv
do i = 1, ncol
ubmc(i,l) = ubi(i,k) - c(l)
tausat(i,l) = abs (effkwv * rhoi(i,k) * ubmc(i,l)**3 / (2._r8*ni(i,k)) )
if (tausat(i,l) .le. taumin) tausat(i,l) = 0.0_r8
if (single_column) then
! needed the following fix when winds are identically 0
! ie. thermal equilibrium case
if ((ubi(i,k+1) .ne. c(l))) then
if (ubmc(i,l) / (ubi(i,k+1) - c(l)) .le. 0.0_r8) tausat(i,l) = 0.0_r8
else
tausat(i,l) = 0.0_r8
end if
else
if (ubmc(i,l) / (ubi(i,k+1) - c(l)) .le. 0.0_r8) tausat(i,l) = 0.0_r8
end if
dsat(i,l) = (ubmc(i,l) / ni(i,k))**2 * &
(effkwv * ubmc(i,l)**2 / (rog * ti(i,k) * ni(i,k)) - alpha(k))
dscal = min (1.0_r8, tau(i,l,k+1) / (tausat(i,l)+taumin))
d(i) = max( d(i), dscal * dsat(i,l))
end do
end do
! Compute stress for each wave. The stress at this level is the min of
! the saturation stress and the stress at the level below reduced by damping.
! The sign of the stress must be the same as at the level below.
do l = -ngwv, ngwv
do i = 1, ncol
ubmc2 = max(ubmc(i,l)**2, ubmc2mn)
mi = ni(i,k) / (2._r8 * kwv * ubmc2) * (alpha(k) + ni(i,k)**2/ubmc2 * d(i))
taudmp = tau(i,l,k+1) * exp(-2._r8*mi*rog*t(i,k+1)*(piln(i,k+1)-piln(i,k)))
if (taudmp .le. taumin) taudmp = 0._r8
tau(i,l,k) = min (taudmp, tausat(i,l))
end do
end do
! The orographic stress term does not change across the source region
! Note that k ge ksrcmn cannot occur without an orographic source term
if (k .ge. ksrcmn) then
do i = 1, ncol
if (k .ge. ksrc(i)) then
tau(i,0,k) = tau(i,0,pver)
end if
end do
end if
! Require that the orographic term decrease linearly (with pressure)
! within the low level stress divergence region. This supersedes the above
! requirment of constant stress within the source region.
! Note that k ge kldvmn cannot occur without an orographic source term, since
! kldvmn=pver then and k<=pver-1
if (k .ge. kldvmn) then
do i = 1, ncol
if (k .ge. kldv(i)) then
tau(i,0,k) = min (tau(i,0,k), tau(i,0,pver) * &
(1._r8 - fracldv * (pi(i,k)-pi(i,pver)) * rdpldv(i)))
end if
end do
end if
! Apply lower bounds to the stress if ngwv > 0.
if (ngwv .ge. 1) then
! Determine the max value of tau for any l
do i = 1, ncol
taumax(i) = tau(i,-ngwv,k)
end do
do l = -ngwv+1, ngwv
do i = 1, ncol
taumax(i) = max(taumax(i), tau(i,l,k))
end do
end do
do i = 1, ncol
taumax(i) = mxrange * taumax(i)
end do
! Set the min value of tau for each wave to the max of mxrange*taumax or
! mxasym*tau(-c)
do l = 1, ngwv
do i = 1, ncol
tau(i, l,k) = max(tau(i, l,k), taumax(i))
tau(i, l,k) = max(tau(i, l,k), mxasym*tau(i,-l,k))
tau(i,-l,k) = max(tau(i,-l,k), taumax(i))
tau(i,-l,k) = max(tau(i,-l,k), mxasym*tau(i, l,k))
end do
end do
l = 0
do i = 1, ncol
tau(i,l,k) = max(tau(i,l,k), mxasym * 0.5_r8 * (tau(i,l-1,k) + tau(i,l+1,k)))
end do
end if
end do
! Put an upper bound on the stress at the top interface to force some stress
! divergence in the top layer. This prevents the easterlies from running
! away in the summer mesosphere, since most of the gravity wave activity
! will pass through a top interface at 75--80 km under strong easterly
! conditions.
!++BAB fix to match ccm3.10
!!$ do l = -ngwv, ngwv
!!$ do i = 1, ncol
!!$ tau(i,l,0) = min(tau(i,l,0), 0.5*tau(i,l,1))
!!$ end do
!!$ end do
!--BAB fix to match ccm3.10
!---------------------------------------------------------------------------
! Compute the tendencies from the stress divergence.
!---------------------------------------------------------------------------
! Loop over levels from top to bottom
do k = ktop+1, kbot
! Accumulate the mean wind tendency over wavenumber.
do i = 1, ncol
ubt (i,k) = 0.0_r8
end do
do l = -ngwv, ngwv
do i = 1, ncol
! Determine the wind tendency including excess stress carried down from above.
ubtl = g * (tau(i,l,k)-tau(i,l,k-1)) * rdpm(i,k)
! Require that the tendency be no larger than the analytic solution for
! a saturated region [proportional to (u-c)^3].
ubtlsat = effkwv * abs((c(l)-ubm(i,k))**3) /(2._r8*rog*t(i,k)*nm(i,k))
ubtl = min(ubtl, ubtlsat)
! Apply tendency limits to maintain numerical stability.
! 1. du/dt < |c-u|/dt so u-c cannot change sign (u^n+1 = u^n + du/dt * dt)
! 2. du/dt < tndmax so that ridicuously large tendency are not permitted
ubtl = min(ubtl, umcfac * abs(c(l)-ubm(i,k)) / dt)
ubtl = min(ubtl, tndmax)
! Accumulate the mean wind tendency over wavenumber.
ubt (i,k) = ubt (i,k) + sign(ubtl, c(l)-ubm(i,k))
! Redetermine the effective stress on the interface below from the wind
! tendency. If the wind tendency was limited above, then the new stress
! will be small than the old stress and will cause stress divergence in
! the next layer down. This has the effect of smoothing large stress
! divergences downward while conserving total stress.
tau(i,l,k) = tau(i,l,k-1) + ubtl * dpm(i,k) / g
end do
end do
! Project the mean wind tendency onto the components and scale by "efficiency".
do i = 1, ncol
ut(i,k) = ubt(i,k) * xv(i) * effgw
vt(i,k) = ubt(i,k) * yv(i) * effgw
end do
! End of level loop
end do
!-----------------------------------------------------------------------
! Project the c=0 stress (scaled) in the direction of the source wind for recording
! on the output file.
!-----------------------------------------------------------------------
do i = 1, ncol
tau0x(i) = tau(i,0,kbot) * xv(i) * effgw
tau0y(i) = tau(i,0,kbot) * yv(i) * effgw
end do
return
end subroutine gw_drag_prof
end module gw_drag