include file: modgps04profilezd.cdk90
! -*- F90 -*-

!-----------------------------------------------------------------------------------
!                GB-GPS ZTD PROFILE MODULE
!-----------------------------------------------------------------------------------

! Contains:
!    TYPE gpsprofilezd:
!       ngpslev   = Number of model levels
!       rLat      = latitude, [rad]
!       rLon      = longitude, [rad]
!       rMT       = model surface elevation (ASL), [m]
!       pst       = pressure profile, [Pa]
!       tst       = temperature (TT) profile, [K]
!       qst       = humidity (HU) profile, [kg/kg]
!       rst       = dZTD/dp profile
!       gst       = geometric height ASL (z) profile, [m]
!       ztd       = ZTD profile, [m]
!       bpst      = .true. when prf%pst() is filled
!
!    Subroutine gpsstructztd:   fills model profiles of type gpsprofilezd (for ZTD operator)
!
!    Subroutine gpsdpress:      computes dP/dP0 for non-staggered HYBRID or ETA vertical grids
!
!
! Stephen Macpherson, 2012.
! ARMA/ASTD
! Environment Canada
!
! Revisions:
!        S. Macpherson 26 June 2013
!           -- use modgps01ctphys
!


module modgps04profilezd 5,8
  use modgps00base,      only : i4,dp,ngpssize,ngpsxlow
  use modgps01ctphys
!  use modgps02wgs84const,only : WGS_a, WGS_OmegaPrime
!  use modgps02wgs84grav ,only : gpsgravityalt, gpsRadii
  use modgps02wgs84grav ,only : gpsgravityalt
  use modgps03diff
  
  implicit none
  
  type gpsprofilezd
     integer(i4)                                     :: ngpslev
     real(dp)                                        :: rLat
     real(dp)                                        :: rLon
     real(dp)                                        :: rMT

     type(gpsdiff)    , dimension(ngpssize)          :: pst
     type(gpsdiff)    , dimension(ngpssize)          :: tst
     type(gpsdiff)    , dimension(ngpssize)          :: qst
     type(gpsdiff)    , dimension(ngpssize)          :: rst
     type(gpsdiff)    , dimension(ngpssize)          :: gst
     type(gpsdiff)    , dimension(ngpssize)          :: ztd
     logical                                         :: bpst
  end type gpsprofilezd

contains


  subroutine gpsstructztd(ngpslev,rLat,rLon,rMT,rPP,rDP,rTT,rLQ,lbevis,refopt,prf) 8,8
!
! This subroutine fills GPS profiles of type gpsprofilezd (for ZTD operator)
!
    integer(i4)       , intent(in)  :: ngpslev          ! number of profile levels
    real(dp)          , intent(in)  :: rLat             ! radians
    real(dp)          , intent(in)  :: rLon             ! radians
    real(dp)          , intent(in)  :: rMT              ! height (ASL) of surface level (m)
    real(dp)          , intent(in)  :: rPP (ngpssize)   ! pressure P at each level (Pa)
    real(dp)          , intent(in)  :: rDP (ngpssize)   ! dP/dP0 at each level (Pa/Pa)
    real(dp)          , intent(in)  :: rTT (ngpssize)   ! temperature T at each level (C)
    real(dp)          , intent(in)  :: rLQ (ngpssize)   ! LOG(q) at each level
    
!   lbevis determines which set of refractivity constants to use (Bevis or Rueger)
    logical           , intent(in)  :: lbevis

!   refopt=1 --> use conventional expression for refractivity N
!   refopt=2 --> use new Aparicio & Laroche refractivity N
    integer           , intent(in)  :: refopt

    type(gpsprofilezd), intent(out) :: prf

!!        ******** PARAMETERS *************

    real(dp), parameter :: delta = 0.6077686814144_dp
    real(dp), parameter :: eps   = 0.6219800221014_dp

! 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

!    real(dp), parameter :: Avog  = 6.02214e26_dp
!    real(dp), parameter :: Boltz = 1.38065e-23_dp
!    real(dp), parameter :: mwDAir= 28.966_dp
!    real(dp), parameter :: Rd = Avog*Boltz/mwDAir    --> p_Rd
!    real(dp), parameter :: Rd = 287.05_dp            --> p_Rd
!    real(dp), parameter :: Rg = 9.80616_dp           --> p_g_GEM
    
!    real(dp), parameter :: R  = 8.314472_dp          --> p_R
!    real(dp), parameter :: md = 28.965516_dp         --> p_md
!    real(dp), parameter :: mw = 18.015254_dp         --> p_mw
!    real(dp), parameter :: wa = md/mw                --> p_wa
!    real(dp), parameter :: wb = (md-mw)/mw           --> p_wb

!!       ******** VARIABLES *************

    real(dp)            :: a0,a1,a2,b0,b1,c0,c1,d,e
    type(gpsdiff)       :: tc, pt, tc2, x2, tr
    type(gpsdiff)       :: mold, dd, dw, dx, n0, nd1, nw1
    integer(i4)         :: i
    real(dp)            :: k1, k2, k3, k2p
    real(dp)            :: h0, dh, Rgh, sLat, ptop
    type(gpsdiff)       :: p, t, q, x, na, tvm, z
    type(gpsdiff)       :: xi(ngpssize), tv(ngpssize), cmp(ngpssize), N(ngpssize) 

    prf%ngpslev = ngpslev
    prf%rLat    = rLat
    prf%rLon    = rLon
    prf%rMT     = rMT
    prf%bpst    = .false.
    !
    ! Fill pressure (P) placeholders (Pa):
    !
    do i = 1, ngpslev
       prf%pst(i)%Var               = rPP(i)
       prf%pst(i)%DVar              = 0._dp
       prf%pst(i)%DVar(2*ngpslev+1) = rDP(i)
    enddo
    ! Pressure at model top (Pa)
    ptop = rPP(1)
    prf%bpst = .true.
    !
    ! Fill temperature (T) placeholders (C--> K):
    !
    do i = 1, ngpslev
       prf%tst(i)%Var               = rTT(i)+p_TC
       prf%tst(i)%DVar              = 0._dp
       prf%tst(i)%DVar(i)           = 1._dp
    enddo

    !
    ! Fill moisture (Q) placeholders (kg/kg):
    !
    do i = 1, ngpslev
       prf%qst(i)%Var               = exp(rLQ(i))
       prf%qst(i)%DVar              = 0._dp
       prf%qst(i)%DVar(ngpslev+i)   = prf%qst(i)%Var
    enddo

    if ( refopt == 2 ) then  ! use Aparicio & Laroche refractivity
    !
    ! Compressibility:
    !
      a0 = 1.58123e-6_dp
      a1 = -2.9331e-8_dp
      a2 = 1.1043e-10_dp
      b0 = 5.707e-6_dp
      b1 = -2.051e-8_dp
      c0 = 1.9898e-4_dp
      c1 = -2.376e-6_dp
      d = 1.83e-11_dp
      e = -0.765e-8_dp
      do i = 1, ngpslev
        p  = prf%pst(i)
        t  = prf%tst(i)
        q  = prf%qst(i)
        x  = p_wa*q/(1._dp+p_wb*q)
        ! Estimate, from CIPM, Piccard (2008)
        tc = t-p_TC
        pt = p/t
        tc2 = tc*tc
        x2 = x*x
        cmp(i) = 1._dp-pt*(a0+a1*tc+a2*tc2+(b0+b1*tc)*x+(c0+c1*tc)*x2)+pt*pt*(d+e*x2)
      enddo

    ! Refractivity:
      do i = 1, ngpslev
        p  = prf%pst(i)
        t  = prf%tst(i)
        q  = prf%qst(i)
        x  = p_wa*q/(1._dp+p_wb*q)

        ! Densities (molar, total, dry, water vapor):
        mold  = p/(p_R*t*cmp(i))
        dd = mold * (1._dp-x) * (p_md/1000._dp)
        dw = mold * x         * (p_mw/1000._dp)
        ! Aparicio (2011) expression
        tr = p_TC/t-1._dp
        nd1= ( 222.682_dp+   0.069_dp*tr) * dd
        nw1= (6701.605_dp+6385.886_dp*tr) * dw
        n0 = (nd1+nw1)
        na = n0*(1._dp+1.e-6_dp*n0/6._dp)
        N(i) = na
      enddo

    endif

    ! Refractivity constants
    if ( lbevis ) then
      k1 = k1b
      k2 = k2b
      k3 = k3b
    else
      k1 = k1r
      k2 = k2r
      k3 = k3r
    endif
    k2p = k2-(eps*k1)

    ! Virtual temperature Tv and log(P) profiles
    !
    do i = 1, ngpslev
       p = prf%pst(i)
       t = prf%tst(i)
       q = prf%qst(i)
       xi(i) = log(p)
       tv(i) = (1._dp+delta*q) * t
    enddo
    
    ! Geometric height (m) profile from surface to top  --> prf%gst
    prf%gst(ngpslev) = rMT
    sLat = sin(rLat)
    do i = ngpslev-1, 1, -1
       dx = xi(i)-xi(i+1)
       tvm = 0.5_dp*(tv(i)+tv(i+1))
       !
       ! Gravity acceleration
       !
       h0  = prf%gst(i+1)%Var
       Rgh = gpsgravityalt(sLat, h0)
       dh  = (-p_Rd/Rgh) * tvm%Var * dx%Var
       Rgh = gpsgravityalt(sLat, h0+0.5_dp*dh)
       !
       ! Height increment (m)
       !
       z   = (-p_Rd/Rgh) * tvm * dx
       prf%gst(i) = prf%gst(i+1) + z
    enddo

    ! Profile of dZTD/dp --> prf%rst
    do i = 1, ngpslev
       p  = prf%pst(i)
       t  = prf%tst(i)
       q  = prf%qst(i)
       if ( refopt == 1 ) then
         na = (k1/tv(i)) + (k2p*(q/(eps*t))) + (k3*(q/(eps*t**2)))
       else
         na = N(i) / p
       endif
       prf%rst(i) = 1.e-6_dp * na * (p_Rd*tv(i))/gpsgravityalt(sLat, prf%gst(i)%Var)
    enddo

    ! ZTD (m) profile from model top down to surface --> prf%ztd
    prf%ztd(1) = 1.e-6_dp * ((k1*p_Rd*ptop)/(gpsgravityalt(sLat, prf%gst(1)%Var)))
    do i = 2, ngpslev
      !
      ! ZTD increment = Avg(dZTD/dP) * delta_P
      !
      z = ((prf%rst(i-1) + prf%rst(i))/2._dp) * (prf%pst(i)-prf%pst(i-1))
      prf%ztd(i) = prf%ztd(i-1) + z
    enddo

  end subroutine gpsstructztd


  subroutine gpsdpress(nlev,rHYB,rP0,rPT,rPR,rCF,rDP)
!  
! Computes dP/dP0 for HYBRID or ETA vertical grids
!
    integer           , intent(in)    :: nlev
    real(dp)          , intent(in)    :: rP0
    real(dp)          , intent(in)    :: rPT
    real(dp)          , intent(in)    :: rPR
    real(dp)          , intent(in)    :: rCF  ! = 1.0 for eta level grid
    real(dp)          , intent(in)    :: rHYB (ngpssize)
    real(dp)          , intent(out)   :: rDP  (ngpssize)

    integer(i4)      :: i, ngpslev
    real(dp)         :: pr1

    ngpslev = nlev
    if ( abs(rCF-1._dp) .lt. 0.01_dp ) then ! eta
        do i = 1, ngpslev
          rDP(i) = rHYB(i)
        enddo
    else                                    ! hybrid
        rDP(1) = 0._dp
        pr1 = 1._dp/(1._dp - rPT/rPR)
        do i = 2, ngpslev
          rDP(i) = ( (rHYB(i) - rPT/rPR)*pr1 )**rCF
        enddo
    endif

  end subroutine gpsdpress

end module modgps04profilezd