module modgps05refstruct 6,4
#if defined (DOC)
  !
  ! Structure containing the refractivity and derivatives
  ! of a model profile.
  !
  ! Functions:
  !      Construct the cached profiles.
  !
  ! Josep M. Aparicio
  ! Meteorological Service of Canada, 2006.
  !
  ! Revisions:
  !        J.M. Aparicio - ARMA Feb 2012
  !          Improved compressibility and refractivity.
  !
#endif
  use modgps00base   , only : i4, dp, ngpssize
  use modgps03diff
  use modgps04profile, only : gpsprofile
  use modgps06gravity, only : gpsgravitysurf, gpsgravityabove
  
  implicit none
  
contains


  subroutine gpspre(prf) 9
    type(gpsprofile) :: prf

    integer(i4)      :: i, ngpslev
    real(dp)         :: rpt, rpr, rcf, pr1, termA, termB

    if (prf%bpst .EQV. .false.) then
       ngpslev = prf%ngpslev
       if (prf%rPP(ngpslev) <0.01_dp) then
          rpt = prf%rPT
          rpr = prf%rPR
          rcf = prf%rCF
          if ( abs(rcf-1._dp) .lt. 0.01_dp ) then
             do i = 1, ngpslev
                prf%pst(i)%Var               = 0.01_dp * (rpt+prf%rHYB(i)*(prf%rP0-rpt))
                prf%pst(i)%DVar              = 0._dp
                prf%pst(i)%DVar(2*ngpslev+1) = 0.01_dp * prf%rHYB(i)
             enddo
          else
             prf%pst(1)%Var               = 0.01_dp * rpt
             prf%pst(1)%DVar              = 0._dp
             prf%pst(1)%DVar(2*ngpslev+1) = 0._dp
             pr1 = 1._dp/(1._dp - rpt/rpr)
             do i = 2, ngpslev
                termB = ( (prf%rHYB(i) - rpt/rpr)*pr1 ) ** rcf
                termA = rpr * ( prf%rHYB(i) - termB )
                prf%pst(i)%Var               = 0.01_dp * (termA + termB * prf%rP0)
                prf%pst(i)%DVar              = 0._dp
                prf%pst(i)%DVar(2*ngpslev+1) = 0.01_dp * termB
             enddo
          endif
       else
          do i = 1, ngpslev
             prf%pst(i)%Var               = 0.01_dp * prf%rPP(i)
             prf%pst(i)%DVar              = 0._dp
             prf%pst(i)%DVar(2*ngpslev+1) = 0.01_dp * prf%rDP(i)
          enddo
       endif
       prf%bpst=.true.
    endif
  end subroutine gpspre


  subroutine gpstem(prf) 7
    type(gpsprofile) :: prf

    integer(i4)      :: i, ngpslev

    if (prf%btst .EQV. .false.) then
       ngpslev = prf%ngpslev
       do i = 1, ngpslev
          prf%tst(i)%Var               = prf%rTT(i)+273.15_dp
          prf%tst(i)%DVar              = 0._dp
          prf%tst(i)%DVar(i)           = 1._dp
       enddo
       prf%btst=.true.
    endif
  end subroutine gpstem


  subroutine gpsQ(prf) 8
    type(gpsprofile) :: prf

    integer(i4)      :: i, ngpslev

    if (prf%bqst .EQV. .false.) then
       ngpslev = prf%ngpslev
       do i = 1, ngpslev
          prf%qst(i)%Var               = exp(prf%rLQ(i))
          prf%qst(i)%DVar              = 0._dp
          prf%qst(i)%DVar(ngpslev+i)   = exp(prf%rLQ(i))
       enddo
       prf%bqst=.true.
    endif
  end subroutine gpsQ


  subroutine gpsref(prf) 2,4
    type(gpsprofile) :: prf

    integer(i4)      :: i, ngpslev

    type(gpsdiff)               :: p, t, q, x
    type(gpsdiff)               :: pd
    type(gpsdiff)               :: pw
    type(gpsdiff)               :: tc

    type(gpsdiff)               :: mold, dd, dw, cmp(ngpssize), tr
    real(dp), parameter         :: R=8.314472_dp
    real(dp), parameter         :: md=28.965516_dp
    real(dp), parameter         :: mw=18.015254_dp
    real(dp), parameter         :: wa=md/mw
    real(dp), parameter         :: wb=(md-mw)/mw
    real(dp)                    :: x1, x2

    !type(gpsdiff)               :: nd,nw,nr
    type(gpsdiff)               :: nd1,nw1,n0,na

    if (prf%brst .EQV. .false.) then
       call gpspre(prf)
       call gpstem(prf)
       call gpsQ  (prf)
       call gpscmp(prf, cmp)
       ngpslev = prf%ngpslev
       do i = 1, ngpslev
          p  = prf%pst(i)
          t  = prf%tst(i)
          q  = prf%qst(i)
          x  = wa*q/(1._dp+wb*q)

          ! Densities (molar, total, dry, water vapor):
          mold  = 100._dp*p/(R*t*cmp(i))               ! note that p is in hPa
          dd = mold * (1._dp-x) * (md/1000._dp)
          dw = mold * x         * (mw/1000._dp)

          ! Rueger (2002) expression 
          !pw = 1.6069_dp*q*p/(1._dp+0.6069_dp*q)
          !pd = p - pw
          !nd = (77.6890_dp*pd/t                   )
          !nw = (71.2952_dp*pw/t+375463._dp*pw/t**2)
          !nr = nd+nw
          ! Aparicio (2011) expression
          tr = 273.15_dp/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)
          ! Either choose nr (Rueger) or na (Aparicio)
          prf%rst(i)=na
       enddo
       prf%brst=.true.
    endif
  end subroutine gpsref


  subroutine gpscmp(prf, cmp) 2,3
    type(gpsprofile) :: prf
    type(gpsdiff)   , intent(out):: cmp(:)

    integer(i4)      :: i, ngpslev
    type(gpsdiff)               :: p, t, q
    !type(gpsdiff)               :: Zd,Zn,Zo,Za,Zw,Zt
    type(gpsdiff)               :: x,tc,pt,tc2,x2,ZtC
    real(dp)                    :: a0,a1,a2,b0,b1,c0,c1,d,e
    real(dp), parameter         :: md=28.965516_dp
    real(dp), parameter         :: mw=18.015254_dp
    real(dp), parameter         :: wa=md/mw
    real(dp), parameter         :: wb=(md-mw)/mw
    !
    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
    !
    call gpspre(prf)
    call gpstem(prf)
    call gpsQ  (prf)
    ngpslev = prf%ngpslev
    do i = 1, ngpslev
       p  = prf%pst(i)
       t  = prf%tst(i)
       q  = prf%qst(i)
       x  = wa*q/(1._dp+wb*q)
       ! First implementation (2007)
       !Zn=1._dp+(0.03913_dp-1.408_dp/(0.08314472_dp*t))*p/(83.14472_dp*t)
       !Zo=1._dp+(0.03183_dp-1.378_dp/(0.08314472_dp*t))*p/(83.14472_dp*t)
       !Za=1._dp+(0.03219_dp-1.363_dp/(0.08314472_dp*t))*p/(83.14472_dp*t)
       !Zw=1._dp+(0.03049_dp-5.536_dp/(0.08314472_dp*t))*p/(83.14472_dp*t)
       !Zd=0.78_dp*Zn+0.21_dp*Zo+0.01_dp*Za
       !Zt=(1._dp-q)*Zd+q*Zw
       ! Better estimate, from CIPM, Piccard (2008)
       tc = t-273.15_dp
       pt = 1.e2_dp*p/t
       tc2= tc*tc
       x2 = x*x
       ZtC= 1._dp-pt*(a0+a1*tc+a2*tc2+(b0+b1*tc)*x+(c0+c1*tc)*x2)
       ! Either choose Zt (First implementation) or ZtC (CIPM, better)
       cmp(i)=ZtC
    enddo
  end subroutine gpscmp


  subroutine gpsprew(prf, prew),3
    type(gpsprofile) :: prf
    type(gpsdiff)   , intent(out):: prew(:)

    integer(i4)      :: i, ngpslev

    type(gpsdiff)               :: p, q
    type(gpsdiff)               :: pw

    call gpspre(prf)
    call gpstem(prf)
    call gpsQ  (prf)
    ngpslev = prf%ngpslev
    do i = 1, ngpslev
       p  = prf%pst(i)
       q  = prf%qst(i)
       pw = 1.6069_dp*q*p/(1._dp+0.6069_dp*q)
       prew(i) = pw
    enddo
  end subroutine gpsprew


  subroutine gpsiprew(prf, iprew),3
    type(gpsprofile) :: prf
    type(gpsdiff)   , intent(out):: iprew

    integer(i4)      :: i, ngpslev

    type(gpsdiff)    :: pm, qm, pp, qp
    real(dp)         :: g

    call gpspre(prf)
    call gpsQ  (prf)
    ngpslev = prf%ngpslev
    iprew=0._dp
    do i = 1, ngpslev-1
       pm = prf%pst(i)
       qm = prf%qst(i)
       pp = prf%pst(i+1)
       qp = prf%qst(i+1)
       iprew=iprew+(qm+qp)*(pp-pm)
    enddo
    g  = gpsgravitysurf(prf%rLat)
    iprew = (100._dp * 0.5_dp/g) * iprew
  end subroutine gpsiprew


  subroutine gpsrefw(prf, refw) 1,3
    type(gpsprofile) :: prf
    type(gpsdiff)   , intent(out):: refw(:)

    integer(i4)      :: i, ngpslev

    type(gpsdiff)               :: p, t, q
    type(gpsdiff)               :: pd
    type(gpsdiff)               :: pw
    type(gpsdiff)               :: tc

    type(gpsdiff)               :: nd
    type(gpsdiff)               :: nw
    type(gpsdiff)               :: n

    call gpspre(prf)
    call gpstem(prf)
    call gpsQ  (prf)
!    if (prf%brst .EQV. .false.) then
    ngpslev = prf%ngpslev
    do i = 1, ngpslev
       p  = prf%pst(i)
       t  = prf%tst(i)
       q  = prf%qst(i)
       pw = 1.6069_dp*q*p/(1._dp+0.6069_dp*q)
       pd = p - pw
       nd = (77.6890_dp*pd/t                   )
       nw = (71.2952_dp*pw/t+375463._dp*pw/t**2)
       refw(i)=nw
!          prf%rst(i)=nd+nw
    enddo
!   prf%brst=.true.
!   endif
  end subroutine gpsrefw


  subroutine gpsZTD(prf, ZTD) 1,4
    type(gpsprofile)             :: prf
    type(gpsdiff)   , intent(out):: ZTD(:)

    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
    
    integer(i4)                  :: i, ngpslev
    type(gpsdiff)                :: p, t, q, tv
    real(dp)                     :: g
    
    call gpspre(prf)
    call gpstem(prf)
    call gpsQ  (prf)
    ngpslev=prf%ngpslev
    do i = 1, ngpslev
       tv = (1._dp+0.6069_dp*prf%qst(i))*prf%tst(i)
       g  = gpsgravityabove(prf%rLat, prf%gst(i)%Var)
       ZTD(i)=prf%rst(i)*Rd*tv/(g*prf%pst(i))
    enddo
  end subroutine gpsZTD


  subroutine gpssat(prf, sat),3
    type(gpsprofile)             :: prf
    type(gpsdiff)   , intent(out):: sat(:)

    real(dp)        , parameter  :: lnPw0 = 1.809926_dp;
    real(dp)        , parameter  :: Aliq  = -5.350334e3_dp;
    real(dp)        , parameter  :: Asol  = -6.149077e3_dp;
    real(dp)        , parameter  :: InvT0 = 1._dp / 273.15_dp;
    
    integer(i4)                  :: i, ngpslev
    type(gpsdiff)                :: p, t, q, pw
    type(gpsdiff)                :: ExcInvT, lnPw, wvp
    
    call gpspre(prf)
    call gpstem(prf)
    call gpsQ  (prf)
    ngpslev=prf%ngpslev

    do i = 1, ngpslev
       p  = prf%pst(i)
       t  = prf%tst(i)
       q  = prf%qst(i)
       pw = 1.6069_dp*q*p/(1._dp+0.6069_dp*q)

       ExcInvT = 1._dp / t - InvT0;
       if ( t%Var .GE. 273.15_dp ) then
          lnPw = lnPw0 + Aliq * ExcInvT
       else
          lnPw = lnPw0 + Asol * ExcInvT
       endif
       wvp = 1.e2_dp * exp( lnPw )

       sat(i) = pw / wvp
    enddo
  end subroutine gpssat

end module modgps05refstruct