include file: modgps08ztdop.cdk90
! -*- F90 -*-
!-----------------------------------------------------------------------------------
! GB-GPS ZTD OPERATOR MODULE
!-----------------------------------------------------------------------------------
!
! Contains:
! ZTD operator gpsZTDopv, with derivatives
! subroutine gpsPW for computing model PW
!
! Stephen Macpherson, 2012-2013.
! ARMA/ASTD
! Environment Canada
!
! Revisions:
! S. Macpherson 5 Feb 2013
! -- added mode argument for option to compute ZTD = ZHD(Psfc)+ZWD (old way)
! S. Macpherson 26 June 2013
! -- use modgps01ctphys
module modgps08ztdop 3,10
use modgps00base
, only : i4, dp, ngpssize
use modgps01ctphys
use modgps03diff
use modgps04profilezd
, only : gpsprofilezd
use modgps02wgs84grav
,only : gpsgravityalt
implicit none
contains
!
! GB-GPS ZTD operator
! On input:
! -hv height of ZTD observation Zobs (m)
! -prf local model profile (type gpsprofilezd)
! -lbevis true/false --> use Bevis instead of Rueger k values
! -dzmin Minimum DZ = Zobs-Zmod (m) for which DZ adjustment to ZTD will be made
! when Zobs < Zmod.
! -mode 1 = normal mode: use stored ZTD profiles
! 2 = Vedel & Huang ZTD formulation: ZTD = ZHD(Pobs) + ZWD
! Pobs computed from P0 using CMC hydrostatic extrapolation.
!
! On output:
! -ZTDopv ZTD (m) at height of observation (with derivatives)
! -rPobs Pressure (Pa) at height of observation
!
pure subroutine gpsZTDopv(hv, prf, lbevis, dzmin, ZTDopv, rPobs, mode) 8,4
real(dp) , intent(in) :: hv
type(gpsprofilezd) , intent(in) :: prf
logical , intent(in) :: lbevis
real(dp) , intent(in) :: dzmin
type(gpsdiff) , intent(out):: ZTDopv
real(dp) , intent(out):: rPobs
integer , intent(in) :: mode
integer(i4) :: ngpslev
integer(i4) :: j, jloc
real(dp) :: h, x, lat, sLat, dh
real(dp) :: k1, k2, k3, k2p
real(dp) :: zcon, zcon1, zconh, zfph, zconw
type(gpsdiff) :: dz, tvsfc, tobs, qobs, tvobs, naobs, Pobs
type(gpsdiff) :: dztddp, dztddpm
type(gpsdiff) :: zhd, tbar, qbar, qtterm, zsum, ztmobs, zqmobs
type(gpsdiff) :: zpbar, ztbar, zqbar, zrmean, zwd
type(gpsdiff) :: dzm, dzp
real(dp), parameter :: delta = 0.6077686814144_dp
real(dp), parameter :: eps = 0.6219800221014_dp
real(dp), parameter :: kappa = (1.0_dp/eps)-1.0_dp
real(dp), parameter :: gamma = 0.0065_dp ! -dT/dz (K/m)
! real(dp), parameter :: Rg = 9.80616_dp --> p_g_GEM
real(dp), parameter :: Rgm = 9.784_dp
! real(dp), parameter :: Rd = 287.05_dp --> p_Rd
real(dp), parameter :: dzmax = 100.0
! Reuger (2002) refractivity constants (MKS units)
real(dp), parameter :: k1r = 0.776890_dp
real(dp), parameter :: k2r = 0.712952_dp
real(dp), parameter :: k3r = 3754.63_dp
! Bevis (1994) refractivity constants (MKS units)
real(dp), parameter :: k1b = 0.776000_dp
real(dp), parameter :: k2b = 0.704000_dp
real(dp), parameter :: k3b = 3739.000_dp
! Refractivity constants to use
if ( lbevis ) then
k1 = k1b
k2 = k2b
k3 = k3b
else
k1 = k1r
k2 = k2r
k3 = k3r
endif
k2p = k2-(eps*k1)
ngpslev = prf%ngpslev
lat = prf%rLat
sLat = sin(lat)
!
! Given obs height hv
!
h = hv
dh = h - prf%gst(ngpslev)%Var ! dh = Zgps-Zmod
!
! Search where it is located
!
do j = 1, ngpslev-1
if ((h <= prf%gst(j)%Var) .and. (h > prf%gst(j+1)%Var)) then
jloc = j ! the model level above the observation
exit
endif
enddo
if (h <= prf%gst(ngpslev)%Var) then ! obs is at or below model surface
jloc = ngpslev
endif
if ( mode == 2 ) then
! Compute ZTD the Vedel and Huang (2004) way: (as in old s/r gpsztdop.ftn)
zcon = 1.0e-06_dp*p_Rd
zcon1 = zcon*k1
zconw = zcon/eps
zconh = zcon1/Rgm
zfph = (1.0_dp - 2.66e-03_dp*cos(2.0*lat) - 2.8e-07_dp*h)
! Pressure at obs height (CMC hydrostatic extrapolation from Psfc)
x = p_g_GEM/(p_Rd*gamma)
tvsfc = prf%tst(ngpslev)*(1._dp+delta*prf%qst(ngpslev))
Pobs = prf%pst(ngpslev)*(((tvsfc-gamma*dh)/tvsfc)**x)
! Dry delay ZHD (m) at obs height
zhd = (zconh/zfph) * Pobs
! Integrate column q/T on pressure levels to get model ZWD
do j = 1, ngpslev-1
tbar = (prf%tst(j) + prf%tst(j+1))*0.5_dp
qbar = (prf%qst(j) + prf%qst(j+1))*0.5_dp
qtterm = ((qbar + kappa*qbar**2 )/gpsgravityalt
(sLat,prf%gst(j)%Var))*(k2p + k3/tbar)
if ( j == 1 ) then
zsum = qtterm*(prf%pst(j+1)-prf%pst(j))
else
zsum = zsum + qtterm*(prf%pst(j+1)-prf%pst(j))
endif
enddo
! Compute ZWD at obs height using Higgins method (HU constant over dh layer)
ztmobs = prf%tst(ngpslev) - (gamma * dh)
zqmobs = prf%qst(ngpslev)
zpbar = (Pobs + prf%pst(ngpslev)) * 0.5_dp
ztbar = (ztmobs + prf%tst(ngpslev)) * 0.5_dp
zqbar = (zqmobs + prf%qst(ngpslev)) * 0.5_dp
! Mean (wet) refractivity of dz layer
zrmean = 1.0e-06_dp*(k2p*((zpbar*zqbar)/(eps*ztbar)) + k3*((zpbar*zqbar)/(eps*ztbar**2)))
! Make sure adjusted ZWD >= 0
if ( (zsum%Var*zconw)-(zrmean%Var*dh) > 0._dp ) then
zwd = (zsum*zconw) - (zrmean*dh)
else
zwd = (zsum*zconw)
endif
! Compute ZTD as sum of ZHD and ZWD
ZTDopv = zhd + zwd
else ! mode = 1: Compute ZTD using stored ZTD profile
if ( jloc /= ngpslev ) then
!
! Linear-log interpolation in height between levels when obs above surface
!
dz = prf%gst(jloc) - prf%gst(jloc+1)
dzm = h - prf%gst(jloc+1)
dzp = prf%gst(jloc) - h
ZTDopv = exp( (dzm*log(prf%ztd(jloc)) + dzp*log(prf%ztd(jloc+1))) / dz )
Pobs = exp( (dzm*log(prf%pst(jloc)) + dzp*log(prf%pst(jloc+1))) / dz )
else ! jloc = ngpslev ; obs is at or below model surface
!
if ( abs(dh) <= dzmin ) then ! take surface values when obs is close to sfc
ZTDopv = prf%ztd(jloc)
Pobs = prf%pst(jloc)
else ! otherwise do extrapolation from surface values
x = p_g_GEM/(p_Rd*gamma)
tvsfc = prf%tst(jloc)*(1._dp+delta*prf%qst(jloc))
Pobs = prf%pst(jloc)*(((tvsfc-gamma*dh)/tvsfc)**x)
if ( abs(dh) <= dzmax ) then
dztddpm = prf%rst(jloc) ! surface value of dZTD/dp
else
tobs = prf%tst(jloc)-gamma*dh
qobs = prf%qst(jloc)
tvobs = tvsfc-gamma*dh
naobs = (k1/tvobs) + (k2p*(qobs/(eps*tobs))) + (k3*(qobs/(eps*tobs**2)))
dztddp = 1.e-6_dp * naobs * (p_Rd*tvobs)/gpsgravityalt
(sLat, h)
dztddpm = (dztddp + prf%rst(jloc))/2._dp ! mean value of dZTD/dp over dh layer
endif
ZTDopv = prf%ztd(jloc) + dztddpm*(Pobs-prf%pst(jloc))
endif
endif
endif
rPobs = Pobs%Var
end subroutine gpsZTDopv
subroutine gpsPW(prf, PW) 3,4
!
! Subroutine to compute surface PW (kg/m2) using layer mean Q and layer delta_p (Pa)
!
! Author: S. Macpherson, 2010-2012
!
type(gpsprofilezd) , intent(in) :: prf
real(dp) , intent(out) :: PW
integer(i4) :: i, ngpslev
real(dp) :: qbar, gt, gb, g, lat, sLat
real(dp) :: pt, pb
ngpslev = prf%ngpslev
lat = prf%rLat
sLat = sin(lat)
PW = 0.0_dp
do i = 1, ngpslev-1
qbar = 0.5_dp * (prf%qst(i+1)%Var + prf%qst(i)%Var)
gt = gpsgravityalt
(sLat, prf%gst(i)%Var)
gb = gpsgravityalt
(sLat, prf%gst(i+1)%Var)
pt = prf%pst(i)%Var
pb = prf%pst(i+1)%Var
g = 0.5_dp * (gt + gb)
PW = PW + (qbar/g)*(pb-pt)
enddo
end subroutine gpsPW
end module modgps08ztdop