gps_mod sourceΒΆ

   1module gps_mod
   2  ! MODULE gps_mod (prefix='gps' category='5. Observation operators')
   3  !
   4  !:Purpose: Code related to GPS-RO and ground-based GPS observation operators.
   5  !
   6  use midasMpi_mod
   7  use utilities_mod
   8  use mathPhysConstants_mod
   9  use earthConstants_mod
  10  implicit none
  11  save
  12  private
  13
  14  ! public types
  15  public :: gps_profile, gps_profilezd, gps_diff
  16
  17  ! public variables
  18  public :: gps_numROProfiles, gps_vRO_IndexPrf
  19  public :: gps_Level_RO, gps_RO_MAXPRFSIZE, gps_SURFMIN, gps_HSFMIN, gps_HTPMAX, gps_HTPMAXER, gps_BGCKBAND, gps_WGPS
  20  public :: gps_roError, gps_roBNorm, gps_roNsigma
  21  public :: gps_gravitysrf, gps_gb_maxdata, gps_gb_dzmin
  22  public :: gps_gb_ltestop, gps_gb_llblmet, gps_gb_lbevis, gps_gb_irefopt, gps_gb_iztdop, gps_gb_lassmet, gps_gb_l1obs, gps_gb_yzderrwgt, gps_gb_numztd
  23  public :: gps_ZTD_Index, gps_ncvmx, gps_gb_dzmax, gps_gb_yztderr, gps_gb_ysferrwgt
  24
  25  ! public procedures
  26  public :: gps_setupro, gps_iprofile_from_index
  27  public :: gps_setupgb, gps_iztd_from_index
  28  public :: gps_struct1sw, gps_struct1sw_v2, gps_bndopv1, gps_refopv, gps_structztd_v2, gps_ztdopv, gps_pw
  29  public :: gps_geopotential
  30
  31  ! public constants
  32  integer, parameter, public :: gps_Level_RO_Bnd       = 1
  33  integer, parameter, public :: gps_Level_RO_Ref       = 2
  34  integer, parameter, public :: gps_Level_RO_BndandRef = 3
  35  public :: gps_p_md, gps_p_mw, gps_p_wa, gps_p_wb
  36
  37!modgps00base
  38  
  39  ! 32-bit integers
  40  integer, parameter     :: i4 = selected_int_kind(9)
  41  
  42  ! Short floats
  43  integer, parameter     :: sp = selected_real_kind(6)
  44  
  45  ! Long floats
  46  integer, parameter     :: dp = selected_real_kind(12)
  47
  48  ! Maximum number of gps levels:
  49  integer(i4), parameter :: ngpssize  = 100
  50
  51  ! Maximum number of gps extra fictitious low levels:
  52  integer(i4), parameter :: ngpsxlow  = 20
  53
  54  ! Associated maximum number of control variables:
  55  integer(i4), parameter :: gps_ncvmx  = 4*ngpssize
  56
  57!modgps01ctphys
  58  
  59  ! Avogadro constant:
  60  real(dp), parameter           :: p_Avog  = 6.02214129e23_dp        ! From CODATA
  61
  62  ! Boltzmann constant:
  63  real(dp), parameter           :: p_Boltz = 1.3806488e-23_dp        ! From CODATA
  64
  65  ! Air properties (public):
  66  real(dp), parameter           :: gps_p_md    = 28.965516_dp            ! From Aparicio(2011)
  67  real(dp), parameter           :: gps_p_mw    = 18.015254_dp            ! From Aparicio(2011)
  68  real(dp), parameter           :: gps_p_wa    = gps_p_md/gps_p_mw
  69  real(dp), parameter           :: gps_p_wb    = (gps_p_md-gps_p_mw)/gps_p_mw
  70
  71  ! Gas constants:
  72  real(dp), parameter           :: p_R     = p_Avog*p_Boltz          ! per mol
  73  real(dp), parameter           :: p_Rd    = p_Avog*p_Boltz/(1.e-3_dp*gps_p_md)   ! per air mass
  74
  75!modgps03diff
  76
  77  type gps_diff
  78     real(dp)           :: Var
  79     real(dp)           :: DVar(gps_ncvmx)
  80  end type gps_diff
  81  
  82  interface assignment(=)
  83     module procedure gpsdiffasfd, gpsdiffasff
  84  end interface
  85  
  86  interface operator(+)
  87     module procedure gpsdiffsmfd, gpsdiffsmdf, gpsdiffsmfi, gpsdiffsmif, gpsdiffsmff
  88  end interface
  89  
  90  interface operator(-)
  91     module procedure gpsdiffsbfd, gpsdiffsbdf, gpsdiffsbfi, gpsdiffsbif, gpsdiffsbff
  92  end interface
  93  
  94  interface operator(*)
  95     module procedure gpsdiffmlfd, gpsdiffmldf, gpsdiffmlfi, gpsdiffmlif, gpsdiffmlff
  96  end interface
  97  
  98  interface operator(/)
  99     module procedure gpsdiffdvfd, gpsdiffdvdf, gpsdiffdvfi, gpsdiffdvif, gpsdiffdvff
 100  end interface
 101  
 102  interface operator(**)
 103     module procedure gpsdiffpwfd, gpsdiffpwdf, gpsdiffpwfi, gpsdiffpwif, gpsdiffpwff
 104  end interface
 105
 106  interface sqrt
 107     module procedure gpsdiffsqr
 108  end interface
 109
 110  interface exp
 111     module procedure gpsdiffexp
 112  end interface
 113  
 114  interface log
 115     module procedure gpsdifflog
 116  end interface
 117
 118  interface cos
 119     module procedure gpsdiffcos
 120  end interface
 121
 122  interface tan
 123     module procedure gpsdifftan
 124  end interface
 125
 126  interface acos
 127     module procedure gpsdiffacos
 128  end interface
 129
 130  interface atan
 131     module procedure gpsdiffatan
 132  end interface
 133
 134  interface erf
 135     module procedure gpsdifferf
 136  end interface
 137
 138!modgps04profile
 139
 140  type gps_profile
 141     integer(i4)                                  :: ngpslev
 142     real(dp)                                     :: rLat
 143     real(dp)                                     :: rLon
 144     real(dp)                                     :: rAzm
 145     real(dp)                                     :: rMT
 146     real(dp)                                     :: Rad
 147     real(dp)                                     :: geoid
 148     real(dp)                                     :: RadN
 149     real(dp)                                     :: RadM
 150
 151     type(gps_diff)                               :: P0
 152
 153     type(gps_diff), dimension(ngpssize)          :: pst
 154     type(gps_diff), dimension(ngpssize)          :: tst
 155     type(gps_diff), dimension(ngpssize)          :: qst
 156     type(gps_diff), dimension(ngpssize)          :: rst
 157     type(gps_diff), dimension(ngpssize)          :: gst
 158
 159     logical                                      :: bbst
 160     type(gps_diff), dimension(ngpssize)          :: dst
 161     type(gps_diff), dimension(ngpssize+ngpsxlow) :: ast
 162     type(gps_diff), dimension(ngpssize+ngpsxlow) :: bst
 163  end type gps_profile
 164
 165!modgps04profilezd
 166  
 167  type gps_profilezd
 168     integer(i4)                                  :: ngpslev
 169     real(dp)                                     :: rLat
 170     real(dp)                                     :: rLon
 171     real(dp)                                     :: rMT
 172
 173     type(gps_diff)                               :: P0
 174     
 175     type(gps_diff), dimension(ngpssize)          :: pst
 176     type(gps_diff), dimension(ngpssize)          :: tst
 177     type(gps_diff), dimension(ngpssize)          :: qst
 178     type(gps_diff), dimension(ngpssize)          :: rst
 179     type(gps_diff), dimension(ngpssize)          :: gst
 180     type(gps_diff), dimension(ngpssize)          :: ztd
 181     logical                                      :: bpst
 182  end type gps_profilezd
 183
 184!modgpsro_mod
 185
 186  !
 187  ! Values determined by input data:
 188  !
 189  integer                                :: gps_numROProfiles
 190  integer         , allocatable          :: gps_vRO_IndexPrf(:,:)   ! index for each profile
 191
 192  ! Public versions of namelist variables
 193  INTEGER gps_Level_RO, gps_RO_MAXPRFSIZE
 194  REAL*8  gps_SurfMin, gps_HsfMin, gps_HtpMax, gps_BgckBand, gps_HtpMaxEr
 195  REAL*8  gps_roNsigma
 196  REAL*4  gps_Wgps(0:1023,4)
 197  character(len=20) :: gps_roError
 198  LOGICAL :: gps_roBNorm, gps_gpsroEotvos
 199
 200
 201!modgpsztd_mod
 202
 203  integer, parameter      ::  max_gps_sites = 1200
 204  integer, parameter      ::  gps_gb_maxdata  = max_gps_sites*24     ! (max_gps_sites) * (max_num_obs in 6h)
 205
 206  integer                 :: gps_gb_numZTD            ! number of ZTD data to be assimilated
 207  integer , allocatable   :: gps_ZTD_Index (:)        ! INDEX_HEADER in CMA (ObsSpace) for each ZTD observation
 208
 209  ! Public versions of namelist variables
 210  REAL*8 gps_gb_DZMIN, gps_gb_YZTDERR, gps_gb_YSFERRWGT, gps_gb_YZDERRWGT
 211  REAL(8) :: gps_gb_DZMAX = 1000.d0 ! need to give it a default value here in case setup not called
 212  INTEGER gps_gb_IREFOPT, gps_gb_IZTDOP
 213  LOGICAL gps_gb_LASSMET, gps_gb_LLBLMET, gps_gb_LBEVIS, gps_gb_L1OBS, gps_gb_LTESTOP
 214
 215contains
 216
 217!modgps02wgs84grav
 218
 219  pure function gps_gravitysrf(sLat)
 220    !
 221    !:Purpose: Normal gravity on ellipsoidal surface
 222    !
 223    implicit none
 224
 225    ! Arguments:
 226    real(dp), intent(in)  :: sLat ! sin(Latitude)
 227    ! Result:
 228    real(dp)              :: gps_gravitysrf ! Normal gravity (m/s2)
 229
 230    ! Locals:
 231    real(dp)              :: ks2
 232    real(dp)              :: e2s
 233
 234    ks2 = ec_wgs_TNGk * sLat*sLat
 235    e2s = 1._dp - ec_wgs_e2 * sLat*sLat
 236    gps_gravitysrf = ec_wgs_GammaE * (1._dp + ks2) / sqrt(e2s)
 237  end function gps_gravitysrf
 238
 239  pure function gps_gravityalt(sLat, Altitude)
 240    !
 241    !:Purpose: Normal gravity above the ellipsoidal surface
 242    !
 243    implicit none
 244
 245
 246    ! Arguments:
 247    real(dp), intent(in)  :: sLat     ! sin(Latitude)
 248    real(dp), intent(in)  :: Altitude ! Altitude (m)
 249    ! Result:
 250    real(dp)              :: gps_gravityalt ! Normal gravity (m/s2)
 251
 252    ! Locals:
 253    real(dp)              :: C1
 254    real(dp)              :: C2
 255
 256    C1 =-2._dp/ec_wgs_a*(1._dp+ec_wgs_f+ec_wgs_m-2*ec_wgs_f*sLat*sLat)
 257    C2 = 3._dp/ec_wgs_a**2
 258    gps_gravityalt = gps_gravitysrf(sLat)*                                   &
 259         (1._dp + C1 * Altitude + C2 * Altitude**2)
 260  end function gps_gravityalt
 261
 262  pure function gps_geopotential(Latitude, Altitude)
 263    !
 264    !:Purpose: Geopotential energy at a given point.
 265    !          Result is based on the WGS84 approximate expression for the
 266    !          gravity acceleration as a function of latitude and altitude,
 267    !          integrated with the trapezoidal rule.
 268    !
 269    implicit none
 270
 271
 272    ! Arguments:
 273    real(dp), intent(in)  :: Latitude ! (rad)
 274    real(dp), intent(in)  :: Altitude ! (m)
 275    ! Result:
 276    real(dp)              :: gps_geopotential ! Geopotential (m2/s2)
 277
 278    ! Locals:
 279    real(dp)              :: dh, sLat
 280    integer               :: n, i
 281    real(dp), allocatable :: hi(:)
 282    real(dp), allocatable :: gi(:)
 283    
 284    dh = 500._dp
 285    n = 1 + int(Altitude/dh)
 286
 287    allocate(hi(0:n))
 288    allocate(gi(0:n))
 289
 290    sLat=sin(Latitude)
 291
 292    do i = 0, n-1
 293       hi(i) = i * dh
 294       gi(i) = gps_gravityalt(sLat, hi(i))
 295    end do
 296    hi(n) = Altitude
 297    gi(n) = gps_gravityalt(sLat, hi(n))
 298
 299    gps_geopotential = 0._dp
 300    do i = 1, n
 301       gps_geopotential = gps_geopotential + 0.5_dp * (gi(i)+gi(i-1)) * (hi(i)-hi(i-1))
 302    end do
 303
 304    deallocate(hi)
 305    deallocate(gi)
 306  end function gps_geopotential
 307
 308  subroutine gpsRadii(Latitude, RadN, RadM)
 309    implicit none
 310
 311    ! Arguments:
 312    real(dp), intent(in)  :: Latitude
 313    real(dp), intent(out) :: RadN, RadM
 314
 315    ! Locals:
 316    real(dp)              :: sLat, e2s
 317
 318    sLat = sin(Latitude)
 319    e2s = 1._dp - ec_wgs_e2 * sLat * sLat
 320    RadN = ec_wgs_a / sqrt(e2s)
 321    RadM = ec_wgs_a * (1._dp - ec_wgs_e2) / (e2s*sqrt(e2s))
 322  end subroutine gpsRadii
 323
 324
 325!modgps03diff
 326  pure subroutine gpsdiffasfd(gd1, d2)
 327    implicit none
 328    
 329    ! Arguments:
 330    type(gps_diff), intent(out) :: gd1
 331    real(dp)      , intent(in)  :: d2
 332    
 333    gd1%Var  = d2
 334    gd1%DVar = 0._dp
 335  end subroutine gpsdiffasfd
 336
 337  pure subroutine gpsdiffasff(gd1, gd2)
 338    implicit none
 339
 340    ! Arguments:
 341    type(gps_diff), intent(out) :: gd1
 342    type(gps_diff), intent(in)  :: gd2
 343    
 344    gd1%Var  = gd2%Var
 345    gd1%DVar = gd2%DVar
 346  end subroutine gpsdiffasff
 347
 348  pure function gpsdiffsmfd(gd1, d2)
 349    implicit none
 350
 351    ! Arguments:
 352    type(gps_diff), intent(in)  :: gd1
 353    real(dp)      , intent(in)  :: d2
 354    ! Result:
 355    type(gps_diff)              :: gpsdiffsmfd
 356
 357    gpsdiffsmfd%Var  = gd1%Var  + d2
 358    gpsdiffsmfd%DVar = gd1%DVar
 359  end function gpsdiffsmfd
 360
 361  pure function gpsdiffsmdf(d1, gd2)
 362    implicit none
 363
 364    ! Arguments:
 365    real(dp)      , intent(in)  :: d1
 366    type(gps_diff), intent(in)  :: gd2
 367    ! Result:
 368    type(gps_diff)              :: gpsdiffsmdf
 369
 370    gpsdiffsmdf%Var  = d1 + gd2%Var
 371    gpsdiffsmdf%DVar =      gd2%DVar
 372  end function gpsdiffsmdf
 373
 374  pure function gpsdiffsmfi(gd1, i2)
 375    implicit none
 376
 377    ! Arguments:
 378    type(gps_diff), intent(in)  :: gd1
 379    integer(i4)   , intent(in)  :: i2
 380    ! Result:
 381    type(gps_diff)              :: gpsdiffsmfi
 382
 383    gpsdiffsmfi%Var  = gd1%Var  + i2
 384    gpsdiffsmfi%DVar = gd1%DVar
 385  end function gpsdiffsmfi
 386
 387  pure function gpsdiffsmif(i1, gd2)
 388    implicit none
 389
 390    ! Arguments:
 391    integer(i4)   , intent(in)  :: i1
 392    type(gps_diff), intent(in)  :: gd2
 393    ! Result:
 394    type(gps_diff)              :: gpsdiffsmif
 395
 396    gpsdiffsmif%Var  = i1 + gd2%Var
 397    gpsdiffsmif%DVar =      gd2%DVar
 398  end function gpsdiffsmif
 399
 400  pure function gpsdiffsmff(gd1, gd2)
 401    implicit none
 402
 403    ! Arguments:
 404    type(gps_diff), intent(in)  :: gd1
 405    type(gps_diff), intent(in)  :: gd2
 406    ! Result:
 407    type(gps_diff)              :: gpsdiffsmff
 408
 409    gpsdiffsmff%Var  = gd1%Var  + gd2%Var
 410    gpsdiffsmff%DVar = gd1%DVar + gd2%DVar
 411  end function gpsdiffsmff
 412  
 413  pure function gpsdiffsbfd(gd1, d2)
 414    implicit none 
 415
 416    ! Arguments:
 417    type(gps_diff), intent(in)  :: gd1
 418    real(dp)      , intent(in)  :: d2
 419    ! Result:
 420    type(gps_diff)              :: gpsdiffsbfd
 421
 422    gpsdiffsbfd%Var  = gd1%Var  - d2
 423    gpsdiffsbfd%DVar = gd1%DVar
 424  end function gpsdiffsbfd
 425
 426  pure function gpsdiffsbdf(d1, gd2)
 427    implicit none
 428
 429    ! Arguments:
 430    real(dp)      , intent(in)  :: d1
 431    type(gps_diff), intent(in)  :: gd2
 432    ! Result:
 433    type(gps_diff)              :: gpsdiffsbdf
 434
 435    gpsdiffsbdf%Var  = d1 - gd2%Var
 436    gpsdiffsbdf%DVar =    - gd2%DVar
 437  end function gpsdiffsbdf
 438
 439  pure function gpsdiffsbfi(gd1, i2)
 440    implicit none
 441
 442    ! Arguments:
 443    type(gps_diff), intent(in)  :: gd1
 444    integer(i4)   , intent(in)  :: i2
 445    ! Result:
 446    type(gps_diff)              :: gpsdiffsbfi
 447
 448    gpsdiffsbfi%Var  = gd1%Var  - i2
 449    gpsdiffsbfi%DVar = gd1%DVar
 450  end function gpsdiffsbfi
 451
 452  pure function gpsdiffsbif(i1, gd2)
 453    implicit none
 454
 455    ! Arguments:
 456    integer(i4)   , intent(in)  :: i1
 457    type(gps_diff), intent(in)  :: gd2
 458    ! Result:
 459    type(gps_diff)              :: gpsdiffsbif
 460
 461    gpsdiffsbif%Var  = i1 - gd2%Var
 462    gpsdiffsbif%DVar =    - gd2%DVar
 463  end function gpsdiffsbif
 464
 465  pure function gpsdiffsbff(gd1, gd2)
 466    implicit none
 467
 468    ! Arguments:
 469    type(gps_diff), intent(in)  :: gd1
 470    type(gps_diff), intent(in)  :: gd2
 471    ! Result:
 472    type(gps_diff)              :: gpsdiffsbff
 473
 474    gpsdiffsbff%Var  = gd1%Var  - gd2%Var
 475    gpsdiffsbff%DVar = gd1%DVar - gd2%DVar
 476  end function gpsdiffsbff
 477
 478  pure function gpsdiffmlfd(gd1, d2)
 479    implicit none
 480
 481    ! Arguments:
 482    type(gps_diff), intent(in)  :: gd1
 483    real(dp)      , intent(in)  :: d2
 484    ! Result:
 485    type(gps_diff)              :: gpsdiffmlfd
 486
 487    gpsdiffmlfd%Var  = d2 * gd1%Var
 488    gpsdiffmlfd%DVar = d2 * gd1%DVar
 489  end function gpsdiffmlfd
 490
 491  pure function gpsdiffmldf(d1, gd2)
 492    implicit none
 493
 494    ! Arguments:
 495    real(dp)      , intent(in)  :: d1
 496    type(gps_diff), intent(in)  :: gd2
 497    ! Result:
 498    type(gps_diff)              :: gpsdiffmldf
 499
 500    gpsdiffmldf%Var  = d1 * gd2%Var
 501    gpsdiffmldf%DVar = d1 * gd2%DVar
 502  end function gpsdiffmldf
 503
 504  pure function gpsdiffmlfi(gd1, i2)
 505    implicit none
 506
 507    ! Arguments:
 508    type(gps_diff), intent(in)  :: gd1
 509    integer(i4)   , intent(in)  :: i2
 510    ! Result:
 511    type(gps_diff)              :: gpsdiffmlfi
 512
 513    gpsdiffmlfi%Var  = i2 * gd1%Var
 514    gpsdiffmlfi%DVar = i2 * gd1%DVar
 515  end function gpsdiffmlfi
 516
 517  pure function gpsdiffmlif(i1, gd2)
 518    implicit none
 519
 520    ! Arguments:
 521    integer(i4)   , intent(in)  :: i1
 522    type(gps_diff), intent(in)  :: gd2
 523    ! Result:
 524    type(gps_diff)              :: gpsdiffmlif
 525
 526    gpsdiffmlif%Var  = i1 * gd2%Var
 527    gpsdiffmlif%DVar = i1 * gd2%DVar
 528  end function gpsdiffmlif
 529
 530  pure function gpsdiffmlff(gd1, gd2)
 531    implicit none
 532
 533    ! Arguments:
 534    type(gps_diff), intent(in)  :: gd1
 535    type(gps_diff), intent(in)  :: gd2
 536    ! Result:
 537    type(gps_diff)              :: gpsdiffmlff
 538    
 539    gpsdiffmlff%Var  = gd1%Var * gd2%Var
 540    gpsdiffmlff%DVar = (gd2%Var * gd1%DVar) + (gd1%Var * gd2%DVar)
 541  end function gpsdiffmlff
 542
 543  pure function gpsdiffdvfd(gd1, d2)
 544    implicit none
 545
 546    ! Arguments:
 547    type(gps_diff), intent(in)  :: gd1
 548    real(dp)      , intent(in)  :: d2
 549    ! Result:
 550    type(gps_diff)              :: gpsdiffdvfd
 551    
 552    gpsdiffdvfd%Var  = gd1%Var  / d2
 553    gpsdiffdvfd%DVar = gd1%DVar / d2
 554  end function gpsdiffdvfd
 555
 556  pure function gpsdiffdvdf(d1, gd2)
 557    implicit none
 558
 559    ! Arguments:
 560    real(dp)      , intent(in)  :: d1
 561    type(gps_diff), intent(in)  :: gd2
 562    ! Result:
 563    type(gps_diff)              :: gpsdiffdvdf
 564    
 565    gpsdiffdvdf%Var  =  d1 / gd2%Var
 566    gpsdiffdvdf%DVar = (-d1 / gd2%Var**2) * gd2%DVar
 567  end function gpsdiffdvdf
 568
 569  pure function gpsdiffdvfi(gd1, i2)
 570    implicit none
 571
 572    ! Arguments:
 573    type(gps_diff), intent(in)  :: gd1
 574    integer(i4)   , intent(in)  :: i2
 575    ! Result:
 576    type(gps_diff)              :: gpsdiffdvfi
 577    
 578    gpsdiffdvfi%Var  = gd1%Var  / i2
 579    gpsdiffdvfi%DVar = gd1%DVar / i2
 580  end function gpsdiffdvfi
 581
 582  pure function gpsdiffdvif(i1, gd2)
 583    implicit none
 584
 585    ! Arguments:
 586    integer(i4)   , intent(in)  :: i1
 587    type(gps_diff), intent(in)  :: gd2
 588    ! Result:
 589    type(gps_diff)              :: gpsdiffdvif
 590    
 591    gpsdiffdvif%Var  = i1 / gd2%Var
 592    gpsdiffdvif%DVar = (-i1 / gd2%Var**2) * gd2%DVar
 593  end function gpsdiffdvif
 594
 595  pure function gpsdiffdvff(gd1, gd2)
 596    implicit none
 597
 598    ! Arguments:
 599    type(gps_diff), intent(in)  :: gd1
 600    type(gps_diff), intent(in)  :: gd2
 601    ! Result:
 602    type(gps_diff)              :: gpsdiffdvff
 603
 604    ! Locals:
 605    real(dp)                    :: onegd2
 606    
 607    onegd2 = 1._dp / gd2%Var
 608    gpsdiffdvff%Var  = gd1%Var * onegd2
 609    gpsdiffdvff%DVar = onegd2 * gd1%DVar - (gd1%Var*onegd2*onegd2) * gd2%DVar
 610  end function gpsdiffdvff
 611
 612  pure function gpsdiffpwfd(gd1, d2)
 613    implicit none
 614
 615    ! Arguments:
 616    type(gps_diff), intent(in)  :: gd1
 617    real(dp)      , intent(in)  :: d2
 618    ! Result:
 619    type(gps_diff)              :: gpsdiffpwfd
 620    
 621    gpsdiffpwfd%Var  = gd1%Var  ** d2
 622    gpsdiffpwfd%DVar = (d2*(gd1%Var**(d2-1._dp))) * gd1%DVar
 623  end function gpsdiffpwfd
 624
 625  pure function gpsdiffpwdf(d1, gd2)
 626    implicit none
 627    
 628    ! Arguments:
 629    real(dp)      , intent(in)  :: d1
 630    type(gps_diff), intent(in)  :: gd2
 631    ! Result:
 632    type(gps_diff)              :: gpsdiffpwdf
 633    
 634    gpsdiffpwdf%Var  =  d1 ** gd2%Var
 635    gpsdiffpwdf%DVar = (log(d1)*d1**gd2%Var) * gd2%DVar
 636  end function gpsdiffpwdf
 637
 638  pure function gpsdiffpwfi(gd1, i2)
 639    implicit none
 640
 641    ! Arguments:
 642    type(gps_diff), intent(in)  :: gd1
 643    integer(i4)   , intent(in)  :: i2
 644    ! Result:
 645    type(gps_diff)              :: gpsdiffpwfi
 646    
 647    gpsdiffpwfi%Var  = gd1%Var  ** i2
 648    gpsdiffpwfi%DVar = (i2*(gd1%Var**(i2-1))) * gd1%DVar
 649  end function gpsdiffpwfi
 650
 651  pure function gpsdiffpwif(i1, gd2)
 652    implicit none
 653
 654    ! Arguments:
 655    integer(i4)   , intent(in)  :: i1
 656    type(gps_diff), intent(in)  :: gd2
 657    ! Result:
 658    type(gps_diff)              :: gpsdiffpwif
 659    
 660    gpsdiffpwif%Var  = i1 ** gd2%Var
 661    gpsdiffpwif%DVar = (log(1._dp*i1)*i1**gd2%Var) * gd2%DVar
 662  end function gpsdiffpwif
 663
 664  pure function gpsdiffpwff(gd1, gd2)
 665    implicit none
 666
 667    ! Arguments:
 668    type(gps_diff), intent(in)  :: gd1
 669    type(gps_diff), intent(in)  :: gd2
 670    ! Result:
 671    type(gps_diff)              :: gpsdiffpwff
 672    
 673    gpsdiffpwff%Var  = gd1%Var ** gd2%Var
 674    gpsdiffpwff%DVar = ( gd2%Var * ( gd1%Var**(gd2%Var-1) ) ) * gd1%DVar +    &
 675         (log(gd1%Var)*(gd1%Var**gd2%Var))*gd2%DVar
 676  end function gpsdiffpwff
 677
 678  pure function gpsdiffsqr(gd1)
 679    implicit none
 680
 681    ! Arguments:
 682    type(gps_diff), intent(in)  :: gd1
 683    ! Result:
 684    type(gps_diff)              :: gpsdiffsqr
 685    
 686    gpsdiffsqr%Var  = sqrt( gd1%Var )
 687    gpsdiffsqr%DVar = (0.5_dp / sqrt( gd1%Var )) * gd1%DVar
 688  end function gpsdiffsqr
 689
 690  pure function gpsdiffexp(gd1)
 691    implicit none
 692
 693    ! Arguments:
 694    type(gps_diff), intent(in)  :: gd1
 695    ! Result:
 696    type(gps_diff)              :: gpsdiffexp
 697    
 698    gpsdiffexp%Var  = exp(gd1%Var)
 699    gpsdiffexp%DVar = gd1%DVar * exp(gd1%Var)
 700  end function gpsdiffexp
 701  
 702  pure function gpsdifflog(gd1)
 703    implicit none
 704
 705    ! Arguments:
 706    type(gps_diff), intent(in)  :: gd1
 707    ! Result:
 708    type(gps_diff)              :: gpsdifflog
 709    
 710    gpsdifflog%Var  = log(gd1%Var)
 711    gpsdifflog%DVar = gd1%DVar / gd1%Var
 712  end function gpsdifflog
 713
 714  pure function gpsdiffcos(gd1)
 715    implicit none
 716
 717    ! Arguments:
 718    type(gps_diff), intent(in)  :: gd1
 719    ! Result:
 720    type(gps_diff)              :: gpsdiffcos
 721    
 722    gpsdiffcos%Var  = cos(gd1%Var)
 723    gpsdiffcos%DVar = gd1%DVar * (-1._dp*sin(gd1%Var))
 724  end function gpsdiffcos
 725
 726  pure function gpsdifftan(gd1)
 727    implicit none
 728
 729    ! Arguments:
 730    type(gps_diff), intent(in)  :: gd1
 731    ! Result:
 732    type(gps_diff)              :: gpsdifftan
 733    
 734    gpsdifftan%Var  = tan(gd1%Var)
 735    gpsdifftan%DVar = (1._dp/cos(gd1%Var)**2) * gd1%DVar
 736  end function gpsdifftan
 737
 738  pure function gpsdiffacos(gd1)
 739    implicit none
 740
 741    ! Arguments:
 742    type(gps_diff), intent(in)  :: gd1
 743    ! Result:
 744    type(gps_diff)              :: gpsdiffacos
 745    
 746    gpsdiffacos%Var  = acos(gd1%Var)
 747    gpsdiffacos%DVar = gd1%DVar * (-1._dp/(1._dp-gd1%Var*gd1%Var))
 748  end function gpsdiffacos
 749
 750  pure function gpsdiffatan(gd1)
 751    implicit none
 752
 753    ! Arguments:
 754    type(gps_diff), intent(in)  :: gd1
 755    ! Result:
 756    type(gps_diff)              :: gpsdiffatan
 757    
 758    gpsdiffatan%Var  = atan(gd1%Var)
 759    gpsdiffatan%DVar = (1._dp/(1._dp+gd1%Var**2)) * gd1%DVar
 760  end function gpsdiffatan
 761
 762  pure function gpsdifferf(gd1)
 763    implicit none
 764
 765    ! Arguments:
 766    type(gps_diff), intent(in)  :: gd1
 767    ! Result:
 768    type(gps_diff)              :: gpsdifferf
 769
 770    ! Locals:
 771    real(dp) , parameter :: pi = MPC_PI_R8
 772    ! real(dp)                   ::m_sqrtpi
 773    gpsdifferf%Var  = erf(gd1%Var)
 774    gpsdifferf%DVar = ((2._dp/sqrt(pi)) * exp(-gd1%Var**2)) * gd1%DVar
 775  end function gpsdifferf
 776
 777!modgps04profile
 778
 779  subroutine gps_struct1sw(ngpslev,rLat,rLon,rAzm,rMT,Rad,geoid,    &
 780       rP0,rPP,rDP,rTT,rHU,rUU,rVV,prf,printHeight_opt)
 781    implicit none
 782 
 783    ! Arguments:
 784    integer(i4)      , intent(in)    :: ngpslev
 785    real(dp)         , intent(in)    :: rLat
 786    real(dp)         , intent(in)    :: rLon
 787    real(dp)         , intent(in)    :: rAzm
 788    real(dp)         , intent(in)    :: rMT
 789    real(dp)         , intent(in)    :: Rad
 790    real(dp)         , intent(in)    :: geoid
 791    real(dp)         , intent(in)    :: rP0
 792    real(dp)         , intent(in)    :: rPP (ngpssize)
 793    real(dp)         , intent(in)    :: rDP (ngpssize)
 794    real(dp)         , intent(in)    :: rTT (ngpssize)
 795    real(dp)         , intent(in)    :: rHU (ngpssize)
 796    real(dp)         , intent(in)    :: rUU (ngpssize)
 797    type(gps_profile), intent(out)   :: prf
 798    real(dp)         , intent(in)    :: rVV (ngpssize)
 799    logical, optional, intent(inout) :: printHeight_opt
 800
 801    ! Locals:
 802    integer(i4)                    :: i
 803    real(dp) , parameter           :: delta = 0.6077686814144_dp
 804    type(gps_diff)                 :: cmp(ngpssize)
 805    real(dp)                       :: h0,dh,Rgh,Eot,Eot2, sLat, cLat
 806    type(gps_diff)                 :: p, t, q, x
 807    type(gps_diff)                 :: tr, z
 808    type(gps_diff)                 :: mold, dd, dw, dx, n0, nd1, nw1, tvm
 809    type(gps_diff)                 :: xi(ngpssize), tv(ngpssize)
 810
 811    prf%ngpslev = ngpslev
 812    prf%rLat    = rLat
 813    prf%rLon    = rLon
 814    prf%rAzm    = rAzm
 815    prf%rMT     = rMT
 816    prf%Rad     = Rad
 817    prf%geoid   = geoid
 818    call gpsRadii(rLat, prf%RadN, prf%RadM)
 819
 820    !
 821    ! Fill pressure placeholders:
 822    !
 823    prf%P0%Var               = 0.01_dp*rP0
 824    prf%P0%DVar              = 0._dp
 825    prf%P0%DVar(2*ngpslev+1) = 0.01_dp
 826    do i=1,ngpslev
 827       prf%pst(i)%Var               = 0.01_dp*rPP(i)
 828       prf%pst(i)%DVar              = 0._dp
 829       prf%pst(i)%DVar(2*ngpslev+1) = 0.01_dp*rDP(i)
 830    end do
 831
 832    !
 833    ! Fill temperature placeholders:
 834    !
 835    do i = 1, ngpslev
 836       prf%tst(i)%Var               = rTT(i)+MPC_K_C_DEGREE_OFFSET_R8
 837       prf%tst(i)%DVar              = 0._dp
 838       prf%tst(i)%DVar(i)           = 1._dp
 839    end do
 840
 841    !
 842    ! Fill moisture placeholders:
 843    !
 844    do i = 1, ngpslev
 845       prf%qst(i)%Var               = rHU(i)
 846       prf%qst(i)%DVar              = 0._dp
 847       prf%qst(i)%DVar(ngpslev+i)   = 1._dp
 848    end do
 849
 850    ! Compressibility:
 851    do i = 1, ngpslev
 852       cmp(i)= gpscompressibility(prf%pst(i),prf%tst(i),prf%qst(i))
 853    end do
 854
 855    ! Refractivity:
 856    do i = 1, ngpslev
 857       p  = prf%pst(i)
 858       t  = prf%tst(i)
 859       q  = prf%qst(i)
 860       x  = gps_p_wa*q/(1._dp+gps_p_wb*q)
 861
 862       ! Densities (molar, total, dry, water vapor):
 863       mold  = p/t * (100._dp/(p_R*cmp(i)))               ! note that p is in hPa
 864       dd = mold * (1._dp-x) * (gps_p_md/1000._dp)
 865       dw = mold * x         * (gps_p_mw/1000._dp)
 866       ! Aparicio (2011) expression
 867       tr = MPC_K_C_DEGREE_OFFSET_R8/t-1._dp
 868       nd1= ( 222.682_dp+   0.069_dp*tr) * dd
 869       nw1= (6701.605_dp+6385.886_dp*tr) * dw
 870       n0 = (nd1+nw1)
 871       prf%rst(i) = n0*(1._dp+(1.e-6_dp/6._dp)*n0)
 872    end do
 873
 874    !
 875    ! Hydrostatic equation
 876    !
 877    do i = 1, ngpslev
 878       p = prf%pst(i)
 879       t = prf%tst(i)
 880       q = prf%qst(i)
 881       !
 882       ! Log(P)
 883       !
 884       xi(i) = log(p)
 885       !
 886       ! Virtual temperature (K) (corrected of compressibility)
 887       !
 888       tv(i) = (1._dp+delta*q) * t * cmp(i)
 889    end do
 890
 891    sLat=sin(rLat)
 892    cLat=cos(rLat)
 893    dx  = xi(ngpslev)-log(prf%P0)
 894    Rgh = gps_gravitysrf(sLat)
 895    z   = (-p_Rd/Rgh) * tv(ngpslev) * dx
 896    prf%gst(ngpslev) = rMT + z
 897    do i=ngpslev-1,1,-1
 898       dx = xi(i)-xi(i+1)
 899       tvm = 0.5_dp*(tv(i)+tv(i+1))
 900       !
 901       ! Gravity acceleration (includes 2nd-order Eotvos effect)
 902       !
 903       h0  = prf%gst(i+1)%Var
 904       Eot = 2*ec_wgs_OmegaPrime*cLat*rUU(i)
 905       Eot2= (rUU(i)**2+rVV(i)**2)/ec_wgs_a
 906       Rgh = gps_gravityalt(sLat, h0)-Eot-Eot2
 907       dh  = (-p_Rd/Rgh) * tvm%Var * dx%Var
 908       Rgh = gps_gravityalt(sLat, h0+0.5_dp*dh)-Eot-Eot2
 909       !
 910       ! Height increment
 911       !
 912       z   = (-p_Rd/Rgh) * tvm * dx
 913       prf%gst(i) = prf%gst(i+1) + z
 914    end do
 915
 916    if ( present(printHeight_opt) ) then
 917      if ( printHeight_opt ) then
 918        write(*,*) 'gps_struct1sw, height='
 919        write(*,*) prf%gst(1:ngpslev)%Var
 920
 921        printHeight_opt = .false.
 922      end if
 923    end if
 924
 925    prf%bbst=.false.
 926  end subroutine gps_struct1sw
 927
 928  subroutine gps_struct1sw_v2(ngpslev,rLat,rLon,rAzm,rMT,Rad,geoid,    &
 929       rP0,rPP,rTT,rHU,rUU,rVV,rALT,prf)
 930    implicit none
 931
 932    ! Arguments:
 933    integer(i4)      , intent(in)  :: ngpslev
 934    real(dp)         , intent(in)  :: rLat
 935    real(dp)         , intent(in)  :: rLon
 936    real(dp)         , intent(in)  :: rAzm
 937    real(dp)         , intent(in)  :: rMT
 938    real(dp)         , intent(in)  :: Rad
 939    real(dp)         , intent(in)  :: geoid
 940    real(dp)         , intent(in)  :: rP0
 941    real(dp)         , intent(in)  :: rPP (ngpssize)
 942    real(dp)         , intent(in)  :: rTT (ngpssize)
 943    real(dp)         , intent(in)  :: rHU (ngpssize)
 944    real(dp)         , intent(in)  :: rUU (ngpssize)
 945    real(dp)         , intent(in)  :: rVV (ngpssize)
 946    real(dp)         , intent(in)  :: rALT (ngpssize)
 947    type(gps_profile), intent(out) :: prf
 948
 949    ! Locals:
 950    integer(i4)                   :: i
 951    real(dp)                      :: rALT_E(ngpssize)
 952    real(dp) , parameter           :: delta = 0.6077686814144_dp
 953    type(gps_diff)                 :: cmp(ngpssize)
 954    type(gps_diff)                 :: p, t, q, x
 955    type(gps_diff)                 :: tr
 956    type(gps_diff)                 :: mold, dd, dw, n0, nd1, nw1
 957
 958    prf%ngpslev = ngpslev
 959    prf%rLat    = rLat
 960    prf%rLon    = rLon
 961    prf%rAzm    = rAzm
 962    prf%rMT     = rMT
 963    prf%Rad     = Rad
 964    prf%geoid   = geoid
 965    call gpsRadii(rLat, prf%RadN, prf%RadM)
 966
 967    !
 968    ! Fill pressure placeholders:
 969    !
 970    prf%P0%Var               = 0.01_dp*rP0
 971    prf%P0%DVar              = 0._dp
 972    prf%P0%DVar(4*ngpslev)   = 0.01_dp
 973    do i=1,ngpslev
 974       prf%pst(i)%Var               = 0.01_dp*rPP(i)
 975       prf%pst(i)%DVar              = 0._dp
 976       prf%pst(i)%DVar(3*ngpslev+i) = 0.01_dp
 977    end do
 978
 979    !
 980    ! Fill temperature placeholders:
 981    !
 982    do i = 1, ngpslev
 983       prf%tst(i)%Var               = rTT(i)+MPC_K_C_DEGREE_OFFSET_R8
 984       prf%tst(i)%DVar              = 0._dp
 985       prf%tst(i)%DVar(i)           = 1._dp
 986    end do
 987
 988    !
 989    ! Fill moisture placeholders:
 990    !
 991    do i = 1, ngpslev
 992       prf%qst(i)%Var               = rHU(i)
 993       prf%qst(i)%DVar              = 0._dp
 994       prf%qst(i)%DVar(ngpslev+i)   = 1._dp
 995    end do
 996
 997    !
 998    ! Fill altitude placeholders:
 999    !
1000    if (gps_gpsroEotvos) then
1001      call gpsro_Eotvos_dH(ngpslev, rLat, rALT, rUU, rVV, rALT_E)
1002    else
1003      rALT_E(1:ngpslev) = rALT(1:ngpslev)
1004    end if
1005    do i = 1, ngpslev
1006       prf%gst(i)%Var                 = rALT_E(i)
1007       prf%gst(i)%DVar                = 0._dp
1008       prf%gst(i)%DVar(2*ngpslev+i)   = 1._dp
1009    end do
1010
1011    ! Compressibility:
1012    do i = 1, ngpslev
1013       cmp(i)= gpscompressibility(prf%pst(i),prf%tst(i),prf%qst(i))
1014    end do
1015
1016    ! Refractivity:
1017    do i = 1, ngpslev
1018       p  = prf%pst(i)
1019       t  = prf%tst(i)
1020       q  = prf%qst(i)
1021       x  = gps_p_wa*q/(1._dp+gps_p_wb*q)
1022
1023       ! Densities (molar, total, dry, water vapor):
1024       mold  = p/t * (100._dp/(p_R*cmp(i)))               ! note that p is in hPa
1025       dd = mold * (1._dp-x) * (gps_p_md/1000._dp)
1026       dw = mold * x         * (gps_p_mw/1000._dp)
1027       ! Aparicio (2011) expression
1028       tr = MPC_K_C_DEGREE_OFFSET_R8/t-1._dp
1029       nd1= ( 222.682_dp+   0.069_dp*tr) * dd
1030       nw1= (6701.605_dp+6385.886_dp*tr) * dw
1031       n0 = (nd1+nw1)
1032       prf%rst(i) = n0*(1._dp+(1.e-6_dp/6._dp)*n0)
1033    end do
1034
1035    prf%bbst=.false.
1036  end subroutine gps_struct1sw_v2
1037
1038  function gpscompressibility(p,t,q)
1039    implicit none
1040
1041    ! Arguments:
1042    type(gps_diff), intent(in)  :: p
1043    type(gps_diff), intent(in)  :: t
1044    type(gps_diff), intent(in)  :: q
1045    ! Result:
1046    type(gps_diff)              :: gpscompressibility
1047
1048    ! Locals:
1049    real(dp) , parameter   :: a0= 1.58123e-6_dp
1050    real(dp) , parameter   :: a1=-2.9331e-8_dp
1051    real(dp) , parameter   :: a2= 1.1043e-10_dp
1052    real(dp) , parameter   :: b0= 5.707e-6_dp
1053    real(dp) , parameter   :: b1=-2.051e-8_dp
1054    real(dp) , parameter   :: c0= 1.9898e-4_dp
1055    real(dp) , parameter   :: c1=-2.376e-6_dp
1056    real(dp) , parameter   :: d = 1.83e-11_dp
1057    real(dp) , parameter   :: e =-0.765e-8_dp
1058    type(gps_diff)         :: x,tc,pt,tc2,x2
1059
1060    x  = gps_p_wa*q/(1._dp+gps_p_wb*q)
1061    ! Estimate, from CIPM, Picard (2008)
1062    tc = t-MPC_K_C_DEGREE_OFFSET_R8
1063    pt = 1.e2_dp*p/t
1064    tc2= tc*tc
1065    x2 = x*x
1066    gpscompressibility = 1._dp-pt*(a0+a1*tc+a2*tc2+(b0+b1*tc)*x+(c0+c1*tc)*x2)+pt*pt*(d+e*x2)
1067  end function gpscompressibility
1068
1069  subroutine gpsro_Eotvos_dH(ngpslev, rLat, rALT, rUU, rVV, rALT_E)
1070    implicit none
1071
1072    ! Arguments:
1073    integer,  intent(in)   :: ngpslev
1074    real(dp), intent(in)   :: rLat
1075    real(dp), intent(in)   :: rALT(ngpslev)
1076    real(dp), intent(in)   :: rUU (ngpslev)
1077    real(dp), intent(in)   :: rVV (ngpslev)
1078    real(dp), intent(out)  :: rALT_E(ngpslev)
1079
1080    ! Locals:
1081    integer                :: i
1082    real(dp)               :: cLat, dALT, Eot, Eot2, dALTE, ddAL, acc
1083
1084    cLat=cos(rLat)
1085    rALT_E(ngpslev) = rALT(ngpslev)
1086    acc = 0.d0
1087    do i = ngpslev-1, 1, -1
1088      dALT = rALT(i) - rALT(i+1)
1089      Eot = 2*ec_wgs_OmegaPrime*cLat*rUU(i)
1090      Eot2= (rUU(i)**2+rVV(i)**2)/ec_wgs_a
1091      dALTE = dALT*(1.d0+(Eot+Eot2)/ec_rg)
1092      ddAL = dALTE - dALT
1093      acc = acc + ddAL
1094      rALT_E(i) = rALT(i) + acc
1095      !write(*,'(A15,I4,8F15.8)')'EOTVOS shift', i, rALT(i), rALT_E(i), dALT, Eot, Eot2, ddAL, acc
1096    end do
1097  end subroutine gpsro_Eotvos_dH
1098
1099!modgps04profilezd
1100
1101  subroutine gps_structztd(ngpslev,rLat,rLon,rMT,rP0,rPP,rDP,rTT,rHU,lbevis,&
1102                           refopt,prf)
1103    !
1104    !:Purpose: This subroutine fills GPS profiles of type gps_profilezd (for ZTD
1105    !          operator)
1106    !
1107    !:Arguments:
1108    !     :refopt:
1109    !               =1 --> use conventional expression for refractivity N
1110    !
1111    !               =2 --> use new Aparicio & Laroche refractivity N
1112    implicit none
1113    
1114    ! Arguments:
1115    integer(i4)        , intent(in)  :: ngpslev          ! number of profile levels
1116    real(dp)           , intent(in)  :: rLat             ! radians
1117    real(dp)           , intent(in)  :: rLon             ! radians
1118    real(dp)           , intent(in)  :: rMT              ! height (ASL) of model surface (m)
1119    real(dp)           , intent(in)  :: rP0              ! surface pressure (Pa)
1120    real(dp)           , intent(in)  :: rPP (ngpssize)   ! pressure P at each level (Pa)
1121    real(dp)           , intent(in)  :: rDP (ngpssize)   ! dP/dP0 at each level (Pa/Pa)
1122    real(dp)           , intent(in)  :: rTT (ngpssize)   ! temperature T at each level (C)
1123    real(dp)           , intent(in)  :: rHU (ngpssize)   ! q at each level
1124    logical            , intent(in)  :: lbevis ! determines which set of refractivity constants to use (Bevis or Rueger)
1125    integer            , intent(in)  :: refopt
1126    type(gps_profilezd), intent(out) :: prf
1127
1128    ! Locals:
1129    ! ******** PARAMETERS *************
1130    real(dp), parameter :: delta = 0.6077686814144_dp
1131    real(dp), parameter :: eps   = 0.6219800221014_dp
1132    ! Reuger (2002) refractivity constants (MKS units)
1133    real(dp), parameter :: k1r = 0.776890_dp
1134    real(dp), parameter :: k2r = 0.712952_dp
1135    real(dp), parameter :: k3r = 3754.63_dp
1136    ! Bevis (1994) refractivity constants (MKS units)
1137    real(dp), parameter :: k1b = 0.776000_dp
1138    real(dp), parameter :: k2b = 0.704000_dp
1139    real(dp), parameter :: k3b = 3739.000_dp
1140    ! ******** VARIABLES *************
1141    real(dp)             :: a0,a1,a2,b0,b1,c0,c1,d,e
1142    type(gps_diff)       :: tc, pt, tc2, x2, tr
1143    type(gps_diff)       :: mold, dd, dw, dx, n0, nd1, nw1
1144    integer(i4)          :: i
1145    real(dp)             :: k1, k2, k3, k2p
1146    real(dp)             :: h0, dh, Rgh, sLat, ptop
1147    type(gps_diff)       :: p, t, q, x, na, tvm, z
1148    type(gps_diff)       :: xi(ngpssize), tv(ngpssize), cmp(ngpssize), N(ngpssize) 
1149
1150    prf%ngpslev = ngpslev
1151    prf%rLat    = rLat
1152    prf%rLon    = rLon
1153    prf%rMT     = rMT
1154    prf%bpst    = .false.
1155    !
1156    ! Fill pressure (P) placeholders (Pa):
1157    !
1158    prf%P0%Var               = rP0
1159    prf%P0%DVar              = 0._dp
1160    prf%P0%DVar(2*ngpslev+1) = 1._dp
1161    do i = 1, ngpslev
1162       prf%pst(i)%Var               = rPP(i)
1163       prf%pst(i)%DVar              = 0._dp
1164       prf%pst(i)%DVar(2*ngpslev+1) = rDP(i)
1165    end do
1166    ! Pressure at model top (Pa)
1167    ptop = rPP(1)
1168    prf%bpst = .true.
1169    !
1170    ! Fill temperature (T) placeholders (C--> K):
1171    !
1172    do i = 1, ngpslev
1173       prf%tst(i)%Var               = rTT(i)+MPC_K_C_DEGREE_OFFSET_R8
1174       prf%tst(i)%DVar              = 0._dp
1175       prf%tst(i)%DVar(i)           = 1._dp
1176    end do
1177
1178    !
1179    ! Fill moisture (Q) placeholders (kg/kg):
1180    !
1181    do i = 1, ngpslev
1182       prf%qst(i)%Var               = rHU(i)
1183       prf%qst(i)%DVar              = 0._dp
1184       prf%qst(i)%DVar(ngpslev+i)   = 1._dp
1185    end do
1186
1187    if ( refopt == 2 ) then  ! use Aparicio & Laroche refractivity
1188    ! This code is copied from modgps04profile.cdk90
1189    !
1190    ! Compressibility:
1191    !
1192      a0 = 1.58123e-6_dp
1193      a1 = -2.9331e-8_dp
1194      a2 = 1.1043e-10_dp
1195      b0 = 5.707e-6_dp
1196      b1 = -2.051e-8_dp
1197      c0 = 1.9898e-4_dp
1198      c1 = -2.376e-6_dp
1199      d = 1.83e-11_dp
1200      e = -0.765e-8_dp
1201      do i = 1, ngpslev
1202        p  = prf%pst(i)
1203        t  = prf%tst(i)
1204        q  = prf%qst(i)
1205        x  = gps_p_wa*q/(1._dp+gps_p_wb*q)
1206        ! Estimate, from CIPM, Piccard (2008)
1207        tc = t-MPC_K_C_DEGREE_OFFSET_R8
1208        pt = p/t
1209        tc2 = tc*tc
1210        x2 = x*x
1211        cmp(i) = 1._dp-pt*(a0+a1*tc+a2*tc2+(b0+b1*tc)*x+(c0+c1*tc)*x2)+pt*pt*(d+e*x2)
1212      end do
1213
1214    ! Refractivity:
1215      do i = 1, ngpslev
1216        p  = prf%pst(i)
1217        t  = prf%tst(i)
1218        q  = prf%qst(i)
1219        x  = gps_p_wa*q/(1._dp+gps_p_wb*q)
1220
1221        ! Densities (molar, total, dry, water vapor):
1222        mold  = p/(p_R*t*cmp(i))
1223        dd = mold * (1._dp-x) * (gps_p_md/1000._dp)
1224        dw = mold * x         * (gps_p_mw/1000._dp)
1225        ! Aparicio (2011) expression
1226        tr = MPC_K_C_DEGREE_OFFSET_R8/t-1._dp
1227        nd1= ( 222.682_dp+   0.069_dp*tr) * dd
1228        nw1= (6701.605_dp+6385.886_dp*tr) * dw
1229        n0 = (nd1+nw1)
1230        na = n0*(1._dp+1.e-6_dp*n0/6._dp)
1231        N(i) = na
1232      end do
1233
1234    end if
1235
1236    ! Refractivity constants
1237    if ( lbevis ) then
1238      k1 = k1b
1239      k2 = k2b
1240      k3 = k3b
1241    else
1242      k1 = k1r
1243      k2 = k2r
1244      k3 = k3r
1245    end if
1246    k2p = k2-(eps*k1)
1247
1248    ! Virtual temperature Tv and log(P) profiles
1249    !
1250    do i = 1, ngpslev
1251       p = prf%pst(i)
1252       t = prf%tst(i)
1253       q = prf%qst(i)
1254       xi(i) = log(p)
1255       tv(i) = (1._dp+delta*q) * t
1256    end do
1257    
1258    ! Geometric height (m) profile from lowest model level to top  --> prf%gst
1259    sLat = sin(rLat)
1260    dx  = xi(ngpslev)-log(prf%P0)
1261    Rgh = gps_gravitysrf(sLat)
1262    z   = (-p_Rd/Rgh) * tv(ngpslev) * dx
1263    prf%gst(ngpslev) = rMT + z
1264    do i = ngpslev-1, 1, -1
1265       dx = xi(i)-xi(i+1)
1266       tvm = 0.5_dp*(tv(i)+tv(i+1))
1267       !
1268       ! Gravity acceleration
1269       !
1270       h0  = prf%gst(i+1)%Var
1271       Rgh = gps_gravityalt(sLat, h0)
1272       dh  = (-p_Rd/Rgh) * tvm%Var * dx%Var
1273       Rgh = gps_gravityalt(sLat, h0+0.5_dp*dh)
1274       !
1275       ! Height increment (m)
1276       !
1277       z   = (-p_Rd/Rgh) * tvm * dx
1278       prf%gst(i) = prf%gst(i+1) + z
1279    end do
1280
1281    ! Profile of dZTD/dp --> prf%rst
1282    do i = 1, ngpslev
1283       p  = prf%pst(i)
1284       t  = prf%tst(i)
1285       q  = prf%qst(i)
1286       if ( refopt == 1 ) then
1287         na = (k1/tv(i)) + (k2p*(q/(eps*t))) + (k3*(q/(eps*t**2)))
1288       else
1289         na = N(i) / p
1290       end if
1291       prf%rst(i) = 1.e-6_dp * na * (p_Rd*tv(i))/gps_gravityalt(sLat, prf%gst(i)%Var)
1292    end do
1293
1294    ! ZTD (m) profile from model top down to lowest model level --> prf%ztd
1295    prf%ztd(1) = 1.e-6_dp * ((k1*p_Rd*ptop)/(gps_gravityalt(sLat, prf%gst(1)%Var)))
1296    do i = 2, ngpslev
1297      !
1298      ! ZTD increment = Avg(dZTD/dP) * delta_P
1299      !
1300      z = ((prf%rst(i-1) + prf%rst(i))/2._dp) * (prf%pst(i)-prf%pst(i-1))
1301      prf%ztd(i) = prf%ztd(i-1) + z
1302    end do
1303
1304  end subroutine gps_structztd
1305
1306  subroutine gps_structztd_v2(ngpslev,rLat,rLon,rMT,rP0,rPP,rTT,rHU,rALT,&
1307                              lbevis,refopt,prf)
1308    !
1309    !:Purpose: This subroutine fills GPS profiles of type gps_profilezd (for ZTD
1310    !          operator)
1311    !
1312    !:Arguments:
1313    !     :refopt:  =1 --> use conventional expression for refractivity N
1314    !
1315    !               =2 --> use new Aparicio & Laroche refractivity N
1316    implicit none
1317
1318    ! Arguments:
1319    integer(i4)        , intent(in)  :: ngpslev          ! number of profile levels
1320    real(dp)           , intent(in)  :: rLat             ! radians
1321    real(dp)           , intent(in)  :: rLon             ! radians
1322    real(dp)           , intent(in)  :: rMT              ! height (ASL) of model surface (m)
1323    real(dp)           , intent(in)  :: rP0              ! surface pressure (Pa)
1324    real(dp)           , intent(in)  :: rPP (ngpssize)   ! pressure P at each level (Pa)
1325    real(dp)           , intent(in)  :: rTT (ngpssize)   ! temperature T at each level (C)
1326    real(dp)           , intent(in)  :: rHU (ngpssize)   ! q at each level
1327    real(dp)           , intent(in)  :: rALT (ngpssize)   ! altitude at each level
1328    logical            , intent(in)  :: lbevis ! determines which set of refractivity constants to use (Bevis or Rueger)
1329    integer            , intent(in)  :: refopt
1330    type(gps_profilezd), intent(out) :: prf
1331
1332    ! Locals:
1333    ! ******** PARAMETERS *************
1334    real(dp), parameter :: delta = 0.6077686814144_dp
1335    real(dp), parameter :: eps   = 0.6219800221014_dp
1336    ! Reuger (2002) refractivity constants (MKS units)
1337    real(dp), parameter :: k1r = 0.776890_dp
1338    real(dp), parameter :: k2r = 0.712952_dp
1339    real(dp), parameter :: k3r = 3754.63_dp
1340    ! Bevis (1994) refractivity constants (MKS units)
1341    real(dp), parameter :: k1b = 0.776000_dp
1342    real(dp), parameter :: k2b = 0.704000_dp
1343    real(dp), parameter :: k3b = 3739.000_dp
1344    ! ******** VARIABLES *************
1345    real(dp)             :: a0,a1,a2,b0,b1,c0,c1,d,e
1346    type(gps_diff)       :: tc, pt, tc2, x2, tr
1347    type(gps_diff)       :: mold, dd, dw, n0, nd1, nw1
1348    integer(i4)          :: i
1349    real(dp)             :: k1, k2, k3, k2p
1350    real(dp)             :: sLat, ptop
1351    type(gps_diff)       :: p, t, q, x, na, z
1352    type(gps_diff)       :: tv(ngpssize), cmp(ngpssize), N(ngpssize) 
1353
1354    prf%ngpslev = ngpslev
1355    prf%rLat    = rLat
1356    prf%rLon    = rLon
1357    prf%rMT     = rMT
1358    prf%bpst    = .false.
1359    !
1360    ! Fill pressure (P) placeholders (Pa):
1361    !
1362    prf%P0%Var               = rP0
1363    prf%P0%DVar              = 0._dp
1364    prf%P0%DVar(4*ngpslev)   = 1._dp
1365    do i = 1, ngpslev
1366       prf%pst(i)%Var               = rPP(i)
1367       prf%pst(i)%DVar              = 0._dp
1368       prf%pst(i)%DVar(3*ngpslev+i) = 1._dp
1369    end do
1370    ! Pressure at model top (Pa)
1371    ptop = rPP(1)
1372    prf%bpst = .true.
1373    !
1374    ! Fill temperature (T) placeholders (C--> K):
1375    !
1376    do i = 1, ngpslev
1377       prf%tst(i)%Var               = rTT(i)+MPC_K_C_DEGREE_OFFSET_R8
1378       prf%tst(i)%DVar              = 0._dp
1379       prf%tst(i)%DVar(i)           = 1._dp
1380    end do
1381
1382    !
1383    ! Fill moisture (Q) placeholders (kg/kg):
1384    !
1385    do i = 1, ngpslev
1386       prf%qst(i)%Var               = rHU(i)
1387       prf%qst(i)%DVar              = 0._dp
1388       prf%qst(i)%DVar(ngpslev+i)   = 1._dp
1389    end do
1390
1391    !
1392    ! Fill altitude (AL) placeholders (m):
1393    !
1394    do i = 1, ngpslev
1395       prf%gst(i)%Var               = rALT(i)
1396       prf%gst(i)%DVar              = 0._dp
1397       prf%gst(i)%DVar(2*ngpslev+i) = 1._dp
1398    end do
1399
1400    if ( refopt == 2 ) then  ! use Aparicio & Laroche refractivity
1401    ! This code is copied from modgps04profile.cdk90
1402    !
1403    ! Compressibility:
1404    !
1405      a0 = 1.58123e-6_dp
1406      a1 = -2.9331e-8_dp
1407      a2 = 1.1043e-10_dp
1408      b0 = 5.707e-6_dp
1409      b1 = -2.051e-8_dp
1410      c0 = 1.9898e-4_dp
1411      c1 = -2.376e-6_dp
1412      d = 1.83e-11_dp
1413      e = -0.765e-8_dp
1414      do i = 1, ngpslev
1415        p  = prf%pst(i)
1416        t  = prf%tst(i)
1417        q  = prf%qst(i)
1418        x  = gps_p_wa*q/(1._dp+gps_p_wb*q)
1419        ! Estimate, from CIPM, Piccard (2008)
1420        tc = t-MPC_K_C_DEGREE_OFFSET_R8
1421        pt = p/t
1422        tc2 = tc*tc
1423        x2 = x*x
1424        cmp(i) = 1._dp-pt*(a0+a1*tc+a2*tc2+(b0+b1*tc)*x+(c0+c1*tc)*x2)+pt*pt*(d+e*x2)
1425      end do
1426
1427    ! Refractivity:
1428      do i = 1, ngpslev
1429        p  = prf%pst(i)
1430        t  = prf%tst(i)
1431        q  = prf%qst(i)
1432        x  = gps_p_wa*q/(1._dp+gps_p_wb*q)
1433
1434        ! Densities (molar, total, dry, water vapor):
1435        mold  = p/(p_R*t*cmp(i))
1436        dd = mold * (1._dp-x) * (gps_p_md/1000._dp)
1437        dw = mold * x         * (gps_p_mw/1000._dp)
1438        ! Aparicio (2011) expression
1439        tr = MPC_K_C_DEGREE_OFFSET_R8/t-1._dp
1440        nd1= ( 222.682_dp+   0.069_dp*tr) * dd
1441        nw1= (6701.605_dp+6385.886_dp*tr) * dw
1442        n0 = (nd1+nw1)
1443        na = n0*(1._dp+1.e-6_dp*n0/6._dp)
1444        N(i) = na
1445      end do
1446
1447    end if
1448
1449    ! Refractivity constants
1450    if ( lbevis ) then
1451      k1 = k1b
1452      k2 = k2b
1453      k3 = k3b
1454    else
1455      k1 = k1r
1456      k2 = k2r
1457      k3 = k3r
1458    end if
1459    k2p = k2-(eps*k1)
1460
1461    ! Virtual temperature Tv and log(P) profiles
1462    !
1463    do i = 1, ngpslev
1464       p = prf%pst(i)
1465       t = prf%tst(i)
1466       q = prf%qst(i)
1467       tv(i) = (1._dp+delta*q) * t
1468    end do
1469
1470    sLat = sin(rLat)
1471
1472    ! Profile of dZTD/dp --> prf%rst
1473    do i = 1, ngpslev
1474       p  = prf%pst(i)
1475       t  = prf%tst(i)
1476       q  = prf%qst(i)
1477       if ( refopt == 1 ) then
1478         na = (k1/tv(i)) + (k2p*(q/(eps*t))) + (k3*(q/(eps*t**2)))
1479       else
1480         na = N(i) / p
1481       end if
1482       prf%rst(i) = 1.e-6_dp * na * (p_Rd*tv(i))/gps_gravityalt(sLat, prf%gst(i)%Var)
1483    end do
1484
1485    ! ZTD (m) profile from model top down to lowest model level --> prf%ztd
1486    prf%ztd(1) = 1.e-6_dp * ((k1*p_Rd*ptop)/(gps_gravityalt(sLat, prf%gst(1)%Var)))
1487    do i = 2, ngpslev
1488      !
1489      ! ZTD increment = Avg(dZTD/dP) * delta_P
1490      !
1491      z = ((prf%rst(i-1) + prf%rst(i))/2._dp) * (prf%pst(i)-prf%pst(i-1))
1492      prf%ztd(i) = prf%ztd(i-1) + z
1493    end do
1494
1495  end subroutine gps_structztd_v2
1496
1497!modgps05refstruct
1498
1499  subroutine gpscmp(prf, cmp)
1500    implicit none
1501
1502    ! Arguments:
1503    type(gps_profile), intent(in)  :: prf
1504    type(gps_diff)   , intent(out) :: cmp(:)
1505
1506    ! Locals:
1507    integer(i4)      :: i, ngpslev
1508    type(gps_diff)               :: p, t, q
1509    type(gps_diff)               :: x,tc,pt,tc2,x2,ZtC
1510    real(dp)                     :: a0,a1,a2,b0,b1,c0,c1,d,e
1511    real(dp) , parameter         :: md=28.965516_dp
1512    real(dp) , parameter         :: mw=18.015254_dp
1513    real(dp) , parameter         :: wa=md/mw
1514    real(dp) , parameter         :: wb=(md-mw)/mw
1515
1516    a0=1.58123e-6_dp
1517    a1=-2.9331e-8_dp
1518    a2=1.1043e-10_dp
1519    b0=5.707e-6_dp
1520    b1=-2.051e-8_dp
1521    c0=1.9898e-4_dp
1522    c1=-2.376e-6_dp
1523    d =1.83e-11_dp
1524    e =-0.765e-8_dp
1525    !
1526    ngpslev = prf%ngpslev
1527    do i = 1, ngpslev
1528       p  = prf%pst(i)
1529       t  = prf%tst(i)
1530       q  = prf%qst(i)
1531       x  = wa*q/(1._dp+wb*q)
1532       ! First implementation (2007)
1533       !Zn=1._dp+(0.03913_dp-1.408_dp/(0.08314472_dp*t))*p/(83.14472_dp*t)
1534       !Zo=1._dp+(0.03183_dp-1.378_dp/(0.08314472_dp*t))*p/(83.14472_dp*t)
1535       !Za=1._dp+(0.03219_dp-1.363_dp/(0.08314472_dp*t))*p/(83.14472_dp*t)
1536       !Zw=1._dp+(0.03049_dp-5.536_dp/(0.08314472_dp*t))*p/(83.14472_dp*t)
1537       !Zd=0.78_dp*Zn+0.21_dp*Zo+0.01_dp*Za
1538       !Zt=(1._dp-q)*Zd+q*Zw
1539       ! Better estimate, from CIPM, Piccard (2008)
1540       tc = t-MPC_K_C_DEGREE_OFFSET_R8
1541       pt = 1.e2_dp*p/t
1542       tc2= tc*tc
1543       x2 = x*x
1544       ZtC= 1._dp-pt*(a0+a1*tc+a2*tc2+(b0+b1*tc)*x+(c0+c1*tc)*x2)       !+pt*pt*(d+e*x2)
1545       ! Either choose Zt (First implementation) or ZtC (CIPM, better)
1546       cmp(i)=ZtC
1547    end do
1548  end subroutine gpscmp
1549
1550  subroutine gpsden(prf, den)
1551    implicit none
1552
1553    ! Arguments:
1554    type(gps_profile), intent(in)  :: prf
1555    type(gps_diff)   , intent(out) :: den(:)
1556
1557    ! Locals:
1558    type(gps_diff)                 :: mold, dd, dw, cmp(ngpssize)
1559    integer(i4)      :: i, ngpslev
1560    real(dp) , parameter         :: R=8.314472_dp
1561    real(dp) , parameter         :: md=28.965516_dp
1562    real(dp) , parameter         :: mw=18.015254_dp
1563    real(dp) , parameter         :: wa=md/mw
1564    real(dp) , parameter         :: wb=(md-mw)/mw
1565    type(gps_diff)               :: p, t, q, x
1566
1567    call gpscmp(prf, cmp)
1568    ngpslev = prf%ngpslev
1569    do i = 1, ngpslev
1570       p  = prf%pst(i)
1571       t  = prf%tst(i)
1572       q  = prf%qst(i)
1573       x  = wa*q/(1._dp+wb*q)
1574
1575       ! Densities (molar, total, dry, water vapor):
1576       mold  = 100._dp*p/(R*t*cmp(i))               ! note that p is in hPa
1577       dd = mold * (1._dp-x) * (md/1000._dp)
1578       dw = mold * x         * (mw/1000._dp)
1579       den(i)=dd+dw
1580    end do
1581  end subroutine gpsden
1582
1583
1584!modgps07geostruct
1585  
1586  subroutine gpsbvf(prf, bvf)
1587    implicit none
1588 
1589    ! Arguments:
1590    type(gps_profile), intent(in)  :: prf
1591    type(gps_diff)   , intent(out) :: bvf(ngpssize)
1592
1593    ! Locals:
1594    type(gps_diff)                 :: den(ngpssize), dddz(ngpssize)
1595    integer(i4)                    :: i, ngpslev, im, ip
1596    real(dp)                       :: g, sLat
1597
1598    call gpsden(prf, den)
1599
1600    ngpslev = prf%ngpslev
1601    sLat=sin(prf%rLat)
1602    do i = 1, ngpslev
1603       ip=i+1
1604       im=i-1
1605       if (i==1)       im=1
1606       if (i==ngpslev) ip=ngpslev
1607       dddz(i)=den(i)*(log(den(ip))-log(den(im)))/(prf%gst(ip)-prf%gst(im))
1608       g=gps_gravityalt(sLat, prf%gst(i)%Var)
1609       bvf(i)=sqrt((-g)/den(i)*dddz(i))
1610    end do
1611  end subroutine gpsbvf
1612
1613
1614!modgps08refop
1615
1616  pure subroutine gps_refopv(hv, nval, prf, refopv)
1617    !
1618    !:Purpose: GPSRO Refractivity operator
1619    !
1620    implicit none
1621
1622    ! Arguments:
1623    real(dp)              , intent(in) :: hv(:) ! an array of height values
1624    integer(i4)           , intent(in) :: nval
1625    type(gps_profile)     , intent(in) :: prf   ! local profile
1626    type(gps_diff)        , intent(out):: refopv(:) ! an array of refractivity values (with derivatives)
1627
1628    ! Locals:
1629    integer(i4)                       :: iSize, i, ngpslev
1630    integer(i4)                       :: j, jloc
1631    real(dp)                           :: h
1632    type(gps_diff)                     :: dz
1633    type(gps_diff)                     :: dzm
1634    type(gps_diff)                     :: dzp
1635    
1636    ngpslev=prf%ngpslev
1637    iSize = size(hv)
1638    if (nval < iSize) iSize=nval
1639    !
1640    ! Given a height
1641    !
1642    jloc = 1
1643    do i = 1, iSize
1644       h = hv(i)
1645       !
1646       ! Search where it is located
1647       !
1648       if (h > prf%gst(1)%Var) then
1649          jloc = 1
1650       end if
1651       
1652       do j=1, ngpslev-1
1653          if ((h <= prf%gst(j)%Var) .and. (h > prf%gst(j+1)%Var)) then
1654             jloc = j
1655             exit
1656          end if
1657       end do
1658       
1659       if (h <= prf%gst(ngpslev)%Var) then
1660          jloc = ngpslev-1
1661       end if
1662       !
1663       ! Interpolation/extrapolation
1664       !
1665       if (h >= prf%gst(ngpslev)%Var) then
1666          !
1667          ! Either linear-log interpolation
1668          !
1669          dz  = prf%gst(jloc) - prf%gst(jloc+1)
1670       
1671          dzm = h - prf%gst(jloc+1)
1672          dzp = prf%gst(jloc) - h
1673       
1674          refopv(i) = exp( (dzm * log(prf%rst(jloc)) + dzp * log(prf%rst(jloc+1))) / dz )
1675       else
1676          !
1677          ! Or exp extrapolation at the lower edge
1678          ! (better standard exp profile than linear-log, which may be unstable)
1679          !
1680          dzm = h - prf%gst(jloc+1)
1681          refopv(i) = prf%rst(jloc+1) * exp((-1._dp/6500._dp)*dzm)
1682       end if
1683    end do
1684  end subroutine gps_refopv
1685
1686  subroutine gpshgtopv(pr, prf, hgtopv)
1687    implicit none
1688 
1689    ! Arguments:
1690    real(dp)         , intent(in) :: pr
1691    type(gps_profile), intent(in) :: prf
1692    type(gps_diff)   , intent(out):: hgtopv
1693
1694    ! Locals:
1695    integer(i4)                       :: j, jloc, ngpslev
1696    real(dp)                           :: p
1697    type(gps_diff)                     :: vpm
1698    type(gps_diff)                     :: vpp
1699    type(gps_diff)                     :: dpr    
1700    type(gps_diff)                     :: dxm
1701    type(gps_diff)                     :: dxp
1702    type(gps_diff)                     :: Hm
1703    type(gps_diff)                     :: Hp    
1704    type(gps_diff)                     :: H
1705
1706    ngpslev=prf%ngpslev
1707    !
1708    ! Given a pressure
1709    !
1710    p = pr
1711    !
1712    ! Search where it is located
1713    !
1714    if (p < prf%pst(1)%Var) then
1715       jloc = 1
1716    end if
1717    
1718    do j=1, ngpslev-1
1719       if ((p >= prf%pst(j)%Var) .and. (p < prf%pst(j+1)%Var)) then
1720          jloc = j
1721          exit
1722       end if
1723    end do
1724    
1725    if (p >= prf%pst(ngpslev)%Var) then
1726       jloc = ngpslev-1
1727    end if
1728    !
1729    ! Find properties in that band
1730    !
1731    vpm = log(prf%pst(jloc))
1732    vpp = log(prf%pst(jloc+1))
1733    
1734    dpr  = vpp-vpm
1735    
1736    dxm = (vpp-log(p)) / dpr
1737    dxp = (log(p)-vpm) / dpr
1738    
1739    Hm  = prf%gst(jloc)
1740    Hp  = prf%gst(jloc+1)
1741    
1742    H   = dxm * Hm + dxp * Hp
1743    
1744    hgtopv = H
1745  end subroutine gpshgtopv
1746
1747  subroutine gpstemopv(pr, nval, prf, temopv)
1748    implicit none
1749
1750    ! Arguments:
1751    real(dp)              , intent(in) :: pr(:)
1752    integer(i4)           , intent(in) :: nval
1753    type(gps_profile)     , intent(in) :: prf
1754    type(gps_diff)        , intent(out):: temopv(:)
1755
1756    ! Locals:
1757    integer                           :: iSize, ngpslev
1758    integer(i4)                        :: i, j, jloc
1759    real(dp)                           :: p
1760    type(gps_diff)                     :: vpm
1761    type(gps_diff)                     :: vpp
1762    type(gps_diff)                     :: dpr
1763    type(gps_diff)                     :: dxm
1764    type(gps_diff)                     :: dxp
1765    type(gps_diff)                     :: Tm
1766    type(gps_diff)                     :: Tp
1767    type(gps_diff)                     :: T
1768
1769    ngpslev=prf%ngpslev
1770    iSize = size(pr)
1771    if (nval < iSize) iSize=nval
1772    do i = 1, iSize
1773       !
1774       ! Given a pressure
1775       !
1776       p = pr(i)
1777       !
1778       ! Search where it is located
1779       !
1780       if (p < prf%pst(1)%Var) then
1781          jloc = 1
1782       end if
1783    
1784       do j=1, ngpslev-1
1785          if ((p >= prf%pst(j)%Var) .and. (p < prf%pst(j+1)%Var)) then
1786             jloc = j
1787             exit
1788          end if
1789       end do
1790    
1791       if (p >= prf%pst(ngpslev)%Var) then
1792          jloc = ngpslev-1
1793       end if
1794       !
1795       ! Find properties in that band
1796       !
1797       vpm = log(prf%pst(jloc))
1798       vpp = log(prf%pst(jloc+1))
1799       
1800       dpr  = vpp-vpm
1801    
1802       dxm = (vpp-log(p)) / dpr
1803       dxp = (log(p)-vpm) / dpr
1804    
1805       Tm  = prf%tst(jloc)
1806       Tp  = prf%tst(jloc+1)
1807    
1808       T   = dxm * Tm + dxp * Tp
1809       
1810       temopv(i) = T
1811    end do
1812  end subroutine gpstemopv
1813
1814  subroutine gpswmropv(pr, prf, wmropv)
1815    implicit none
1816
1817    ! Arguments:
1818    real(dp)              , intent(in) :: pr(:)
1819    type(gps_profile)     , intent(in) :: prf
1820    type(gps_diff)        , intent(out):: wmropv(:)
1821
1822    ! Locals:
1823    integer                           :: iSize, ngpslev
1824    integer(i4)                        :: i, j, jloc
1825    real(dp)                           :: p
1826    type(gps_diff)                     :: vpm
1827    type(gps_diff)                     :: vpp
1828    type(gps_diff)                     :: dpr
1829    type(gps_diff)                     :: dxm
1830    type(gps_diff)                     :: dxp
1831    type(gps_diff)                     :: Rm
1832    type(gps_diff)                     :: Rp
1833    type(gps_diff)                     :: R
1834
1835    ngpslev=prf%ngpslev
1836    iSize = size(pr)
1837    do i = 1, iSize
1838       !
1839       ! Given a pressure
1840       !
1841       p = pr(i)
1842       !
1843       ! Search where it is located
1844       !
1845       if (p < prf%pst(1)%Var) then
1846          jloc = 1
1847       end if
1848    
1849       do j=1, ngpslev-1
1850          if ((p >= prf%pst(j)%Var) .and. (p < prf%pst(j+1)%Var)) then
1851             jloc = j
1852             exit
1853          end if
1854       end do
1855    
1856       if (p >= prf%pst(ngpslev)%Var) then
1857          jloc = ngpslev-1
1858       end if
1859       !
1860       ! Find properties in that band
1861       !
1862       vpm = log(prf%pst(jloc))
1863       vpp = log(prf%pst(jloc+1))
1864       
1865       dpr  = vpp-vpm
1866    
1867       dxm = (vpp-log(p)) / dpr
1868       dxp = (log(p)-vpm) / dpr
1869
1870       Rm  = prf%qst(jloc)
1871       Rp  = prf%qst(jloc+1)
1872    
1873       R   = dxm * Rm + dxp * Rp
1874       
1875       wmropv(i) = R * 28.97_dp / 18.01528_dp
1876    end do
1877  end subroutine gpswmropv
1878
1879  subroutine gpsbvfopv(hv, nval, prf, bvfopv)
1880    implicit none
1881
1882    ! Arguments:
1883    real(dp)              , intent(in) :: hv(:)
1884    integer(i4)           , intent(in) :: nval
1885    type(gps_profile)     , intent(in) :: prf
1886    type(gps_diff)        , intent(out):: bvfopv(:)
1887
1888    ! Locals:
1889    integer(i4)                        :: iSize, i, ngpslev
1890    integer(i4)                        :: j, jloc
1891    real(dp)                           :: h
1892    type(gps_diff)                     :: bvf(ngpssize)
1893    type(gps_diff)                     :: gpm
1894    type(gps_diff)                     :: gpp
1895    type(gps_diff)                     :: dz
1896    type(gps_diff)                     :: dxm
1897    type(gps_diff)                     :: dxp
1898    type(gps_diff)                     :: BVm
1899    type(gps_diff)                     :: BVp
1900    
1901    call gpsbvf(prf,bvf)
1902
1903    ngpslev=prf%ngpslev
1904    iSize = size(hv)
1905    if (nval < iSize) iSize=nval
1906    !
1907    ! Given a height
1908    !
1909    do i = 1, iSize
1910       h = hv(i)
1911       !
1912       ! Search where it is located
1913       !
1914       if (h > prf%gst(1)%Var) then
1915          jloc = 1
1916       end if
1917       
1918       do j=1, ngpslev-1
1919          if ((h <= prf%gst(j)%Var) .and. (h > prf%gst(j+1)%Var)) then
1920             jloc = j
1921             exit
1922          end if
1923       end do
1924       
1925       if (h <= prf%gst(ngpslev)%Var) then
1926          jloc = ngpslev-1
1927       end if
1928       !
1929       ! Find properties in that band
1930       !
1931       gpm = prf%gst(jloc)
1932       gpp = prf%gst(jloc+1)
1933       
1934       dz  = gpm - gpp
1935       
1936       dxm = (h-gpp) / dz
1937       dxp = (gpm-h) / dz
1938       
1939       BVm = bvf(jloc)
1940       BVp = bvf(jloc+1)
1941       
1942       bvfopv (i) = dxm * BVm + dxp * BVp
1943    end do
1944  end subroutine gpsbvfopv
1945
1946
1947!modgps08ztdop
1948
1949  pure subroutine gps_ztdopv(hv, prf, lbevis, dzmin, ZTDopv, rPobs, mode)
1950    !
1951    !:Purpose: GB-GPS ZTD operator
1952    !
1953    !:Arguments:
1954    !   :dzmin:   Minimum DZ = Zobs-Zmod (m) for which DZ adjustment to ZTD will
1955    !             be made when Zobs < Zmod.
1956    !   :mode:    1 = normal mode: use stored ZTD profiles
1957    !
1958    !             2 = Vedel & Huang ZTD formulation: ZTD = ZHD(Pobs) + ZWD
1959    !                 Pobs computed from P0 using CMC hydrostatic extrapolation.
1960    !
1961    implicit none
1962
1963    ! Arguments:
1964    real(dp)           , intent(in)  :: hv    ! height of ZTD observation Zobs (m)
1965    type(gps_profilezd), intent(in)  :: prf   ! local model profile (type gps_profilezd)
1966    logical            , intent(in)  :: lbevis! true/false --> use Bevis instead of Rueger k values
1967    real(dp)           , intent(in)  :: dzmin
1968    type(gps_diff)     , intent(out) :: ZTDopv! ZTD (m) at height of observation (with derivatives)
1969    real(dp)           , intent(out) :: rPobs ! Pressure (Pa) at height of observation
1970    integer            , intent(in)  :: mode
1971
1972    ! Locals:
1973    integer(i4)                        :: ngpslev
1974    integer(i4)                        :: j, jloc
1975    real(dp)                           :: h, x, lat, sLat, dh
1976    real(dp)                           :: k1, k2, k3, k2p
1977    real(dp)                           :: zcon, zcon1, zconh, zfph, zconw
1978    type(gps_diff)                     :: dz, tvsfc, tobs, qobs, tvobs, naobs, Pobs
1979    type(gps_diff)                     :: dztddp, dztddpm
1980    type(gps_diff)                     :: zhd, tbar, qbar, qtterm, zsum, ztmobs, zqmobs
1981    type(gps_diff)                     :: zpbar, ztbar, zqbar, zrmean, zwd
1982    type(gps_diff)                     :: dzm, dzp
1983    real(dp), parameter :: delta = 0.6077686814144_dp
1984    real(dp), parameter :: eps   = 0.6219800221014_dp
1985    real(dp), parameter :: kappa = (1.0_dp/eps)-1.0_dp
1986    real(dp), parameter :: gamma = 0.0065_dp    ! -dT/dz (K/m)
1987    real(dp), parameter :: Rgm = 9.784_dp
1988    real(dp), parameter :: dzmax = 100.0
1989    ! Reuger (2002) refractivity constants (MKS units)
1990    real(dp), parameter :: k1r = 0.776890_dp
1991    real(dp), parameter :: k2r = 0.712952_dp
1992    real(dp), parameter :: k3r = 3754.63_dp
1993    ! Bevis (1994) refractivity constants (MKS units)
1994    real(dp), parameter :: k1b = 0.776000_dp
1995    real(dp), parameter :: k2b = 0.704000_dp
1996    real(dp), parameter :: k3b = 3739.000_dp
1997
1998    ! Refractivity constants to use
1999    if ( lbevis ) then
2000      k1 = k1b
2001      k2 = k2b
2002      k3 = k3b
2003    else
2004      k1 = k1r
2005      k2 = k2r
2006      k3 = k3r
2007    end if
2008    k2p = k2-(eps*k1)
2009
2010    ngpslev = prf%ngpslev
2011    lat     = prf%rLat
2012    sLat    = sin(lat)
2013    !
2014    ! Given obs height hv
2015    !
2016    h  = hv
2017    dh = h - prf%gst(ngpslev)%Var   ! dh = Zgps-Zmod
2018    !
2019    ! Search where it is located
2020    !
2021    do j = 1, ngpslev-1
2022      if ((h <= prf%gst(j)%Var) .and. (h > prf%gst(j+1)%Var)) then
2023        jloc = j   ! the model level above the observation
2024        exit
2025      end if
2026    end do
2027
2028    if (h <= prf%gst(ngpslev)%Var) then  ! obs is at or below model lowest level
2029      jloc = ngpslev
2030    end if
2031    
2032    if ( mode == 2 ) then
2033    
2034      ! Compute ZTD the Vedel and Huang (2004) way: (as in old s/r gpsztdop.ftn)
2035
2036       zcon  = 1.0e-06_dp*p_Rd
2037       zcon1 = zcon*k1
2038       zconw = zcon/eps
2039       zconh = zcon1/Rgm
2040       zfph = (1.0_dp - 2.66e-03_dp*cos(2.0*lat) - 2.8e-07_dp*h)
2041
2042       ! Pressure at obs height (CMC hydrostatic extrapolation from Psfc)
2043       x      = ec_rg/(p_Rd*gamma)
2044       tvsfc  = prf%tst(ngpslev)*(1._dp+delta*prf%qst(ngpslev))
2045       Pobs   = prf%pst(ngpslev)*(((tvsfc-gamma*dh)/tvsfc)**x)
2046       ! Dry delay ZHD (m) at obs height
2047       zhd    = (zconh/zfph) * Pobs
2048
2049       ! Integrate column q/T on pressure levels to get model ZWD
2050       do j = 1, ngpslev-1
2051         tbar = (prf%tst(j) + prf%tst(j+1))*0.5_dp
2052         qbar = (prf%qst(j) + prf%qst(j+1))*0.5_dp
2053         qtterm = ((qbar + kappa*qbar**2 )/gps_gravityalt(sLat,prf%gst(j)%Var))*(k2p + k3/tbar)
2054         if ( j == 1 ) then
2055           zsum = qtterm*(prf%pst(j+1)-prf%pst(j))
2056         else
2057           zsum = zsum + qtterm*(prf%pst(j+1)-prf%pst(j))
2058         end if
2059       end do
2060       
2061       ! Compute ZWD at obs height using Higgins method (HU constant over dh layer)
2062       ztmobs = prf%tst(ngpslev) - (gamma * dh)
2063       zqmobs = prf%qst(ngpslev)
2064       zpbar  = (Pobs + prf%pst(ngpslev)) * 0.5_dp
2065       ztbar  = (ztmobs + prf%tst(ngpslev)) * 0.5_dp
2066       zqbar  = (zqmobs + prf%qst(ngpslev)) * 0.5_dp
2067       ! Mean (wet) refractivity of dz layer
2068       zrmean = 1.0e-06_dp*(k2p*((zpbar*zqbar)/(eps*ztbar)) + k3*((zpbar*zqbar)/(eps*ztbar**2)))
2069       
2070       ! Make sure adjusted ZWD >= 0
2071       if ( (zsum%Var*zconw)-(zrmean%Var*dh) > 0._dp ) then
2072         zwd = (zsum*zconw) - (zrmean*dh)
2073       else
2074         zwd = (zsum*zconw)
2075       end if
2076       
2077       ! Compute ZTD as sum of ZHD and ZWD      
2078       ZTDopv = zhd + zwd
2079
2080    
2081    else   !  mode = 1: Compute ZTD using stored ZTD profile
2082    
2083
2084      if ( jloc /= ngpslev ) then
2085        !
2086        ! Linear-log interpolation in height between levels when obs above lowest level
2087        !
2088        dz  = prf%gst(jloc) - prf%gst(jloc+1)
2089
2090        dzm = h - prf%gst(jloc+1)
2091        dzp = prf%gst(jloc) - h
2092
2093        ZTDopv = exp( (dzm*log(prf%ztd(jloc)) + dzp*log(prf%ztd(jloc+1))) / dz )
2094        Pobs   = exp( (dzm*log(prf%pst(jloc)) + dzp*log(prf%pst(jloc+1))) / dz )
2095
2096      else   ! jloc = ngpslev ; obs is at or below model lowest level
2097        !
2098        if ( abs(dh) <= dzmin ) then  ! take lowest level values when obs is close to sfc
2099          ZTDopv = prf%ztd(jloc)
2100          Pobs   = prf%pst(jloc)
2101        else ! otherwise do extrapolation from lowest level values
2102          x      = ec_rg/(p_Rd*gamma)
2103          tvsfc  = prf%tst(jloc)*(1._dp+delta*prf%qst(jloc))
2104          Pobs   = prf%pst(jloc)*(((tvsfc-gamma*dh)/tvsfc)**x)
2105          if ( abs(dh) <= dzmax ) then
2106            dztddpm = prf%rst(jloc)   ! lowest level value of dZTD/dp
2107          else
2108            tobs   = prf%tst(jloc)-gamma*dh
2109            qobs   = prf%qst(jloc)
2110            tvobs  = tvsfc-gamma*dh
2111            naobs  = (k1/tvobs) + (k2p*(qobs/(eps*tobs))) + (k3*(qobs/(eps*tobs**2)))
2112            dztddp = 1.e-6_dp * naobs * (p_Rd*tvobs)/gps_gravityalt(sLat, h)
2113            dztddpm = (dztddp + prf%rst(jloc))/2._dp  ! mean value of dZTD/dp over dh layer
2114          end if
2115          ZTDopv = prf%ztd(jloc) + dztddpm*(Pobs-prf%pst(jloc))
2116        end if
2117
2118      end if
2119    
2120    end if
2121    
2122    rPobs = Pobs%Var
2123
2124  end subroutine gps_ztdopv
2125
2126  subroutine gps_pw(prf, PW)
2127    !
2128    !:Purpose: To compute lowest level PW (kg/m2) using layer mean Q and layer
2129    !          delta_p (Pa)
2130    !
2131    implicit none
2132
2133    ! Arguments:
2134    type(gps_profilezd)     , intent(in)  :: prf
2135    real(dp)                , intent(out) :: PW
2136
2137    ! Locals:
2138    integer(i4)                       :: i, ngpslev
2139    real(dp)                          :: qbar, gt, gb, g, lat, sLat
2140    real(dp)                          :: pt, pb
2141
2142    ngpslev = prf%ngpslev
2143    lat     = prf%rLat
2144    sLat    = sin(lat)
2145
2146    PW = 0.0_dp
2147
2148    do i = 1, ngpslev-1
2149      qbar = 0.5_dp * (prf%qst(i+1)%Var + prf%qst(i)%Var)
2150      gt  = gps_gravityalt(sLat, prf%gst(i)%Var)
2151      gb  = gps_gravityalt(sLat, prf%gst(i+1)%Var)
2152      pt  = prf%pst(i)%Var
2153      pb  = prf%pst(i+1)%Var
2154      g   = 0.5_dp * (gt + gb)
2155      PW = PW + (qbar/g)*(pb-pt)
2156    end do
2157
2158  end subroutine gps_pw
2159
2160
2161!modgps09bend
2162
2163  subroutine gpsbend(prf)
2164    implicit none
2165 
2166    ! Arguments:
2167    type(gps_profile), intent(inout) :: prf
2168
2169    ! Locals:
2170    type(gps_diff)                     :: sum,ta,tb,tm,trap,simp,boole,num,fa,fb,fm,nm,alpha_B
2171    type(gps_diff)                     :: sa,sb,sm,ra,rm,rb,dlnndra,dlnndrb,dlnndrm
2172    type(gps_diff)                     :: s1,s2,s3,s4,s5,r1,r2,r3,r4,r5
2173    type(gps_diff)                     :: nu1,nu2,nu3,nu4,nu5,n1,n2,n3,n4,n5
2174    type(gps_diff)                     :: t1,t2,t3,t4,t5,dlnndr1,dlnndr2,dlnndr3,dlnndr4,dlnndr5
2175    type(gps_diff)                     :: f1,f2,f3,f4,f5
2176    type(gps_diff)                     :: r  (ngpssize)
2177    type(gps_diff)                     :: ref(ngpssize)
2178    type(gps_diff)                     :: nu (ngpssize)
2179    type(gps_diff)                     :: lnu(ngpssize)
2180    type(gps_diff)                     :: n  (ngpssize)
2181    type(gps_diff)                     :: dlgnudr(ngpssize-1)
2182    type(gps_diff)                     :: rsq(ngpssize)
2183    type(gps_diff)                     :: nsq(ngpssize)
2184    type(gps_diff)                     :: x  (-ngpsxlow+1:ngpssize)
2185    type(gps_diff)                     :: xsq(-ngpsxlow+1:ngpssize)
2186    type(gps_diff)                     :: s(ngpssize),t(ngpssize)
2187    integer                            :: i,j,ngpslev
2188    logical                            :: lok
2189
2190    if (.not. prf%bbst) then
2191       ngpslev=prf%ngpslev
2192
2193       ! Radial distances and impact parameters:
2194       do i=1,ngpslev
2195          prf%dst(i)= (prf%Rad+prf%geoid+prf%gst(i))
2196          prf%ast(i)= prf%dst(i) * (1._dp+1.e-6_dp*prf%rst(i))
2197       end do
2198       ! Extended lower levels:
2199       do i=ngpslev+1,ngpslev+ngpsxlow
2200          prf%ast(i)= prf%ast(i-1)-50._dp
2201       end do
2202
2203       ! Standard levels:
2204       do i=1,ngpslev
2205          r  (i)=prf%dst(ngpslev-i+1)
2206          ref(i)=prf%rst(ngpslev-i+1)
2207          !ref(i)=300._dp*exp((-1._dp/7000._dp)*(r(i)%Var-prf%Rad))
2208       end do
2209       ! Extended upper levels:
2210       do i=ngpslev+1,ngpssize
2211          r  (i)=r  (i-1)+1000._dp
2212          ref(i)=ref(i-1)*exp(-1000._dp/7000_dp)
2213       end do
2214
2215       ! log n and x:
2216       do i=1,ngpssize
2217          nu(i)=1.e-6_dp*ref(i)
2218          lnu(i)=log(nu(i))
2219          n (i)=1._dp+nu(i)
2220          x (i)=n(i)*r(i)
2221          rsq(i)=r(i)**2
2222          nsq(i)=n(i)**2
2223          xsq(i)=x(i)**2
2224       end do
2225       do i=0,-ngpsxlow+1,-1
2226          x  (i)=x(i+1)-50._dp
2227          xsq(i)=x(i)**2
2228       end do
2229
2230       ! Radial derivatives of log refractivity.
2231       ! Refractivity will be assumed exponential within each shell.
2232       ! We store the derivative of log(nu).
2233       ! dn/dr = nu * dlgnudr
2234       do i=1,ngpssize-1
2235          dlgnudr(i)=(lnu(i+1)-lnu(i))/(r(i+1)-r(i))
2236       end do
2237
2238       ! Evaluation of complete bending for ray tangent at r(i):
2239       do i=1,ngpslev
2240          ! Check that ray is not trapped
2241          lok=.true.
2242          do j = i+1,ngpssize
2243             lok= lok .and. (x(j)%Var .gt. x(i)%Var)
2244          end do
2245          if (lok) then
2246             s(i)=0._dp
2247             t(i)=1._dp
2248             do j=i+1,ngpssize
2249                s(j)=sqrt(nsq(i)*rsq(j)-xsq(i))
2250                t(j)=s(j)/sqrt(xsq(j)-xsq(i))
2251             end do
2252
2253             ! Trapezoid integration:
2254             sum=0._dp
2255             do j=i, ngpssize-1
2256                sa=s(j)
2257                sb=s(j+1)
2258                ta=t(j)
2259                tb=t(j+1)
2260                dlnndra=dlgnudr(j)*nu(j  )/n(j  )
2261                dlnndrb=dlgnudr(j)*nu(j+1)/n(j+1)
2262                fa=dlnndra*ta/sqrt(xsq(i)+sa*sa)
2263                fb=dlnndrb*tb/sqrt(xsq(i)+sb*sb)
2264                sum=sum+(1._dp/2._dp)*(fa+fb)*(sb-sa)
2265             end do
2266             trap=(-2)*r(i)*sum
2267
2268             ! Simpson 1/3 integration:
2269             sum=0._dp
2270             do j=i, ngpssize-1
2271                sa=s(j)
2272                sb=s(j+1)
2273                sm=0.5_dp*(sa+sb)
2274                !
2275                ra=r(j)
2276                rb=r(j+1)
2277                rm=sqrt(xsq(i)+sm*sm)/n(i)
2278                !
2279                num=nu(j)*exp(dlgnudr(j)*(rm-ra))
2280                nm=(1._dp+num)
2281                !
2282                ta=t(j)
2283                tb=t(j+1)
2284                tm=sm/sqrt(nm*nm*rm*rm-xsq(i))
2285                !
2286                dlnndra=dlgnudr(j)*nu(j  )/n(j  )
2287                dlnndrb=dlgnudr(j)*nu(j+1)/n(j+1)
2288                dlnndrm=dlgnudr(j)*num    /nm
2289                !
2290                fa=dlnndra*ta/sqrt(xsq(i)+sa*sa)
2291                fb=dlnndrb*tb/sqrt(xsq(i)+sb*sb)
2292                fm=dlnndrm*tm/sqrt(xsq(i)+sm*sm)
2293                !
2294                sum=sum+(1._dp/6._dp)*(fa+4*fm+fb)*(sb-sa)
2295             end do
2296             simp=(-2)*r(i)*sum
2297
2298             ! Boole 2/45 integration:
2299             sum=0._dp
2300             do j=i, ngpssize-1
2301                s1=s(j)
2302                s5=s(j+1)
2303                s2=0.75_dp*s1+0.25_dp*s5
2304                s3=0.50_dp*s1+0.50_dp*s5
2305                s4=0.25_dp*s1+0.75_dp*s5
2306                !
2307                r1=r(j)
2308                r5=r(j+1)
2309                r2=sqrt(xsq(i)+s2*s2)/n(i)
2310                r3=sqrt(xsq(i)+s3*s3)/n(i)
2311                r4=sqrt(xsq(i)+s4*s4)/n(i)
2312                !
2313                nu1=nu(j)
2314                nu2=nu(j)*exp(dlgnudr(j)*(r2-r1))
2315                nu3=nu(j)*exp(dlgnudr(j)*(r3-r1))
2316                nu4=nu(j)*exp(dlgnudr(j)*(r4-r1))
2317                nu5=nu(j+1)
2318                n1=n(j)
2319                n2=(1._dp+nu2)
2320                n3=(1._dp+nu3)
2321                n4=(1._dp+nu4)
2322                n5=n(j+1)
2323                !
2324                t1=t(j)
2325                t2=s2/sqrt(n2*n2*r2*r2-xsq(i))
2326                t3=s3/sqrt(n3*n3*r3*r3-xsq(i))
2327                t4=s4/sqrt(n4*n4*r4*r4-xsq(i))
2328                t5=t(j+1)
2329                !
2330                dlnndr1=dlgnudr(j)*nu(j  )/n(j  )
2331                dlnndr5=dlgnudr(j)*nu(j+1)/n(j+1)
2332                dlnndr2=dlgnudr(j)*nu2    /n2
2333                dlnndr3=dlgnudr(j)*nu3    /n3
2334                dlnndr4=dlgnudr(j)*nu4    /n4
2335                !
2336                f1=dlnndr1*t1/sqrt(xsq(i)+s1*s1)
2337                f2=dlnndr2*t2/sqrt(xsq(i)+s2*s2)
2338                f3=dlnndr3*t3/sqrt(xsq(i)+s3*s3)
2339                f4=dlnndr4*t4/sqrt(xsq(i)+s4*s4)
2340                f5=dlnndr5*t5/sqrt(xsq(i)+s5*s5)
2341                !
2342                sum=sum+(1._dp/90._dp)*(7*f1+32*f2+12*f3+32*f4+7*f5)*(s5-s1)
2343             end do
2344             boole=(-2)*r(i)*sum
2345
2346             prf%bst(ngpslev-i+1)=boole
2347          else
2348             prf%bst(ngpslev-i+1)=-10._dp
2349          end if
2350       end do
2351
2352       ! Extended low levels:
2353       do i=0,-ngpsxlow+1,-1
2354          lok=.true.
2355          do j = 1,ngpssize
2356             lok= lok .and. (x(j)%Var .gt. x(i)%Var)
2357          end do
2358          if (lok) then
2359             do j=1,ngpssize
2360                s(j)=sqrt(nsq(1)*rsq(j)-xsq(i))
2361                t(j)=s(j)/sqrt(xsq(j)-xsq(i))
2362             end do
2363
2364             ! Simpson integration:
2365             sum=0._dp
2366             do j=1, ngpssize-1
2367                sa=s(j)
2368                sb=s(j+1)
2369                sm=0.5_dp*(sa+sb)
2370                !
2371                ra=r(j)
2372                rb=r(j+1)
2373                rm=sqrt(xsq(i)+sm*sm)/n(1)
2374                !
2375                num=nu(j)*exp(dlgnudr(j)*(rm-ra))
2376                nm=(1._dp+num)
2377                !
2378                ta=t(j)
2379                tb=t(j+1)
2380                tm=sm/sqrt(nm*nm*rm*rm-xsq(i))
2381                !
2382                dlnndra=dlgnudr(j)*nu(j  )/n(j  )
2383                dlnndrb=dlgnudr(j)*nu(j+1)/n(j+1)
2384                dlnndrm=dlgnudr(j)*num/nm
2385                !
2386                fa=dlnndra*ta/sqrt(xsq(i)+sa*sa)
2387                fb=dlnndrb*tb/sqrt(xsq(i)+sb*sb)
2388                fm=dlnndrm*tm/sqrt(xsq(i)+sm*sm)
2389                !
2390                sum=sum+(1._dp/6._dp)*(fa+4*fm+fb)*(sb-sa)
2391             end do
2392             simp=(-2)*(x(i)/n(1))*sum
2393             alpha_B=acos(x(i)/x(1))
2394             prf%bst(ngpslev-i+1)=simp-2*alpha_B
2395          else
2396             prf%bst(ngpslev-i+1)=-10._dp
2397          end if
2398       end do
2399
2400       prf%bbst=.true.
2401    end if
2402  end subroutine gpsbend
2403
2404  subroutine gpsbend1(prf)
2405    implicit none
2406
2407    ! Arguments:
2408    type(gps_profile), intent(inout)   :: prf
2409
2410    ! Locals:
2411    type(gps_diff)                     :: r  (ngpssize)
2412    type(gps_diff)                     :: ref(ngpssize)
2413    type(gps_diff)                     :: nu (ngpssize)
2414    type(gps_diff)                     :: lnu(ngpssize)
2415    type(gps_diff)                     :: n  (ngpssize)
2416    type(gps_diff)                     :: dlgnudr(ngpssize-1)
2417    type(gps_diff)                     :: x  (-ngpsxlow+1:ngpssize)
2418    type(gps_diff)                     :: angle0,angle,angleB,bend,nu0,th,sum,nexp
2419    real(dp)                           :: dxn
2420    integer                            :: ngpslev,i,j,jmin
2421    logical                            :: lok, lok2
2422
2423    if (.not. prf%bbst) then
2424       ngpslev=prf%ngpslev
2425
2426       ! Radial distances and impact parameters:
2427       do i=1,ngpslev
2428          prf%dst(i)= (prf%Rad+prf%geoid+prf%gst(i))
2429          prf%ast(i)= prf%dst(i) * (1._dp+1.e-6_dp*prf%rst(i))
2430       end do
2431       ! Extended lower levels:
2432       do i=ngpslev+1,ngpslev+ngpsxlow
2433          prf%ast(i)= prf%ast(i-1)-50._dp
2434       end do
2435
2436       ! Standard levels:
2437       do i=1,ngpslev
2438          r  (i)=prf%dst(ngpslev-i+1)
2439          ref(i)=prf%rst(ngpslev-i+1)
2440       end do
2441       ! Extended upper levels:
2442       do i=ngpslev+1,ngpssize
2443          r  (i)=r  (i-1)+1000._dp
2444          ref(i)=ref(i-1)*exp(-1000._dp/7000_dp)
2445       end do
2446
2447       ! log n and x:
2448       do i=1,ngpssize
2449          nu(i) = 1.e-6_dp*ref(i)
2450          lnu(i)= log(nu(i))
2451          n (i) = 1._dp+nu(i)
2452          x (i) = n(i)*r(i)
2453       end do
2454       dxn=20._dp
2455       do i=0,-ngpsxlow+1,-1
2456          x (i) = x(i+1)-dxn
2457       end do
2458
2459       ! Radial derivatives of log refractivity.
2460       ! Refractivity will be assumed exponential within each shell.
2461       ! We store the derivative of log(nu).
2462       ! dn/dr = nu * dlgnudr
2463       do i=1,ngpssize-1
2464          dlgnudr(i)=(lnu(i+1)-lnu(i))/(r(i+1)-r(i))
2465       end do
2466
2467       ! Evaluation of complete bending for ray tangent at r(i):
2468       do i=-ngpsxlow+1,ngpslev
2469          lok=.true.
2470          lok2=.false.
2471          ! Check that the ray is not trapped
2472          ! For low impact (reflected, jmin<1) rays, begin at the surface
2473          jmin = i
2474          if (jmin < 1) jmin=1
2475          do j = jmin+1,ngpssize
2476             lok= lok .and. (x(j)%Var .gt. x(i)%Var)
2477          end do
2478          if (lok) then
2479             ! Integration:
2480             sum=0._dp
2481             if (i.ge.1) then
2482                ! Direct rays
2483                angleB=0._dp
2484             else
2485                ! Reflected
2486                angleB=sqrt(2*(-i+1)*dxn/x(1))
2487             end if
2488             angle0=angleB
2489             do j=jmin, ngpssize-1
2490                th=r(j+1)-r(j)
2491                nu0=nu(j)
2492                nexp=dlgnudr(j)
2493                call gpsbendlayer(r(j), th, nu0, nexp, angle0, angle, bend, lok2)
2494                sum=sum+bend
2495                angle0=angle
2496             end do
2497          end if
2498          if (lok2) then
2499             prf%bst(ngpslev-i+1)=(-2)*(sum+angleB)
2500          else
2501             prf%bst(ngpslev-i+1)=-10._dp
2502          end if
2503       end do
2504    end if
2505  end subroutine gpsbend1
2506
2507  subroutine gpsbendlayer(ra, th, nu0, nexp, angle0, angle, bend, lok)
2508    !
2509    !:Arguments:
2510    !     :nu0, nexp:  Refraction index coefs: n=1+nu0*exp(nexp*(r-ra));
2511    !                  nexp in in 1/m
2512    implicit none
2513
2514    ! Arguments:
2515    type(gps_diff), intent(in)  :: ra    ! Radius of inner shell (m)
2516    type(gps_diff), intent(in)  :: th    ! Shell thickness (m)
2517    type(gps_diff), intent(in)  :: nu0   
2518    type(gps_diff), intent(in)  :: nexp
2519    type(gps_diff), intent(in)  :: angle0 ! Ray angle above horizon at ra
2520    type(gps_diff), intent(out) :: angle  ! Ray angle above horizon at rb
2521    type(gps_diff), intent(out) :: bend   ! Accumulated bending over the layer
2522    logical       , intent(out) :: lok
2523
2524    ! Locals:
2525    type(gps_diff) :: rb,angle0i,dh,hi,rai,nu0i,anglei
2526    integer :: i,numunits
2527    
2528    lok=.false.
2529    if (th%Var.lt.0._dp) return
2530
2531    ! Radius of the outer shell:
2532    rb = ra + th
2533
2534    ! Divide layer in smaller layers:
2535    numunits=10
2536    dh =th/(1._dp*numunits)
2537    angle0i=angle0
2538    bend   =0._dp
2539    do i = 1, numunits
2540       hi =(i-1)*dh
2541       rai=ra+(i-1)*dh
2542       nu0i=nu0*exp(nexp*hi)
2543       call gpsbendunit(rai, dh, nu0i, nexp, angle0i, anglei, bend, lok)
2544       angle0i=anglei
2545       if (.not.lok) return
2546    end do
2547    angle=anglei
2548  end subroutine gpsbendlayer
2549
2550  subroutine gpsbendunit(ra, th, nu0, nexp, angle0, angle, bend, lok)
2551    !
2552    !:Arguments:
2553    !    :nu0, nexp:  Refraction index coefs: n=1+nu0*exp(nexp*(r-ra));
2554    !                 nexp in 1/m
2555    implicit none
2556
2557    ! Arguments:
2558    type(gps_diff), intent(in)    :: ra ! Radius of inner shell (m)
2559    type(gps_diff), intent(in)    :: th ! Shell thickness (m)
2560    type(gps_diff), intent(in)    :: nu0, nexp
2561    type(gps_diff), intent(in)    :: angle0 ! Ray angle above horizon at ra
2562    type(gps_diff), intent(out)   :: angle  ! Ray angle above horizon at rb
2563    type(gps_diff), intent(inout) :: bend ! Accumulated bending over the layer
2564    logical       , intent(out)   :: lok
2565
2566    ! Locals:
2567    type(gps_diff) :: rb, nu, dlnndh, g0,g1,g2,f0,f1,f2,x,a,b,c,disc,ds,bendi,g1av
2568
2569    lok=.false.
2570    if (th%Var.lt.0._dp) return
2571
2572    ! Radius of the outer shell:
2573    rb = ra + th
2574
2575    ! Excess refraction index:
2576    !    at ra:    nu0
2577    !    at rb:    nu0*exp(nexp*h)
2578    nu     = nu0*exp(0.5_dp*nexp*th)
2579    dlnndh = nexp*nu/(1._dp+nu)
2580
2581    ! Geometric trajectory:
2582    ! g(x) = g0+g1*x+g2*x^2
2583    !
2584    g0=0._dp
2585    g1=tan(angle0)
2586    g2=0.5_dp*dlnndh*cos(angle0)
2587
2588    ! Outer circle:
2589    ! f(x) = f0+f1*x+f2*x^2
2590    !
2591    f0=th
2592    f1=0._dp
2593    f2=(-0.5_dp)/rb
2594
2595    ! Difference:
2596    a=f2-g2
2597    b=f1-g1
2598    c=f0-g0
2599
2600    ! Discriminant:
2601    disc=b*b-4*a*c
2602    if (disc%Var.lt.0._dp) then
2603       lok=.false.
2604       return
2605    else
2606       x =((-1)*b-sqrt(disc))/(2*a)
2607       g1av=g1+g2*x
2608       ds=x*(1._dp+(g2*x)**2)
2609       bendi = 2 * g2 * ds
2610       angle = angle0+atan(x/rb)+bendi
2611       bend  = bend + bendi
2612       if (angle%Var .gt. 0) lok=.true.
2613    end if
2614  end subroutine gpsbendunit
2615
2616  subroutine gpsbndopv(impv, azmv, nval, prf, bstv)
2617    implicit none
2618
2619    ! Arguments:
2620    real(dp)              , intent(in)    :: impv(:)
2621    real(dp)              , intent(in)    :: azmv(:)
2622    integer(i4)           , intent(in)    :: nval
2623    type(gps_profile)     , intent(inout) :: prf
2624    type(gps_diff)        , intent(out)   :: bstv(:)
2625
2626    ! Locals:
2627    integer                            :: iSize, i, j, ngpslev, jlocm, jlocp
2628    real(dp)                           :: imp1,azm1,rad, rad0
2629    real(dp)                           :: imp(ngpssize+ngpsxlow)
2630    type(gps_diff)                     :: am, ap, da, dam, dap
2631
2632    call gpsbend(prf)
2633    ngpslev=prf%ngpslev
2634    iSize = size(impv)
2635    if (nval < iSize) iSize=nval
2636    rad0=prf%rad
2637    !
2638    ! Given an impact
2639    !
2640    do i = 1, iSize
2641       imp1 = impv(i)
2642       azm1 = azmv(i)
2643       rad=1._dp/(cos(azm1)**2/prf%radM+sin(azm1)**2/prf%radN)
2644       do j=1, ngpslev+ngpsxlow
2645          imp(j)=prf%ast(j)%Var
2646       end do
2647       !
2648       ! Search where it is located
2649       !
2650       jlocm = -1000
2651       jlocp = -1000
2652       if (imp1 > imp(1)) then
2653          jlocm = 1
2654          jlocp = 2
2655       end if
2656
2657       do j=1, ngpslev+ngpsxlow-1
2658          if ((imp1 <= imp(j)) .and. (abs(prf%bst(j)%Var) < 1._dp)) then
2659             jlocm = j
2660          end if
2661       end do
2662
2663       do j=jlocm+1, ngpslev+ngpsxlow
2664          if ((imp1 >  imp(j)) .and. (abs(prf%bst(j)%Var) < 1._dp)) then
2665             jlocp = j
2666             exit
2667          end if
2668       end do
2669      
2670       if (jlocm == -1000) jlocm = ngpslev+ngpsxlow-1
2671       if (jlocp == -1000) jlocp = ngpslev+ngpsxlow
2672
2673       !
2674       ! Find properties in that band
2675       !
2676       am = prf%ast(jlocm)
2677       ap = prf%ast(jlocp)
2678       
2679       da = am - ap
2680       dam = (imp1-ap) / da
2681       dap = (am-imp1) / da
2682       
2683       ! Use loglinear interpolation for most data (notably direct rays)
2684       if (prf%bst(jlocm)%Var > 1.e-6_dp .and. prf%bst(jlocp)%Var > 1.e-6_dp) then
2685          bstv(i)=exp(dam*log(prf%bst(jlocm))+dap*log(prf%bst(jlocp)))*(rad/rad0)
2686       else
2687          ! Use linear interpolation for near-zero or negative bending (most reflected rays)
2688          bstv(i)=(dam*prf%bst(jlocm)+dap*prf%bst(jlocp))*(rad/rad0)
2689       end if
2690    end do
2691  end subroutine gpsbndopv
2692
2693  subroutine gps_bndopv1(impv, azmv, nval, prf, bstv)
2694    !:Purpose: Computation of the observation operator for Bending 
2695    !
2696    !:Note: The Operator is based  from Assimilation experiments withCHAMP GPS radio occultation measurements
2697    !         (S. B. HEALY and J.-N. THEPAUT, 2005)
2698    implicit none
2699
2700    ! Arguments:
2701    real(dp)              , intent(in)  :: impv(:)
2702    real(dp)              , intent(in)  :: azmv(:)
2703    integer(i4)           , intent(in)  :: nval
2704    type(gps_profile)     , intent(in)  :: prf
2705    type(gps_diff)        , intent(out) :: bstv(:)
2706
2707    ! Locals:
2708    integer                            :: levIndexObs, ngpslev,last_levIndexObs,numLevels,levelHigh,levelLow,levIndexAnl 
2709    type(gps_diff)                     :: h(ngpssize), nu(ngpssize), lnu(ngpssize), n(ngpssize), z(ngpssize)
2710    type(gps_diff)                     :: N0a, N1a, ka, NAa,Aa, Ba, Ba2, Ba3, delta_alpha, delta_alpha_top, z_0, h_0
2711    real(dp)                           :: a2, a, gz, cazm, sazm, last_a
2712
2713    ! model levels 
2714    ngpslev=prf%ngpslev
2715    do levIndexAnl = 1,ngpslev 
2716      h(levIndexAnl)  = prf%geoid+prf%gst(levIndexAnl)
2717      nu(levIndexAnl) = prf%rst(levIndexAnl)
2718      lnu(levIndexAnl)=log(nu(levIndexAnl)) 
2719      n(levIndexAnl)  = 1._dp+nu(levIndexAnl)*1e-6_dp
2720      z(levIndexAnl)  = n(levIndexAnl)*(prf%Rad+prf%geoid+prf%gst(levIndexAnl))
2721    end do
2722    ! number of observed levels in the profile
2723    numLevels  = size(impv)
2724    if (nval < numLevels) numLevels=nval
2725    
2726    do levIndexObs =  numLevels,1,-1
2727      a2 = impv(levIndexObs)*impv(levIndexObs)
2728      a  = impv(levIndexObs)
2729      cazm = cos(azmv(levIndexObs))
2730      sazm = sin(azmv(levIndexObs))
2731      !find model levels that bracket the observation
2732      !   note to self:   like in GEM, level=1 is the highest level
2733      do levIndexAnl = 1, ngpslev-1
2734        levelHigh = levIndexAnl - 1
2735        levelLow  = levIndexAnl
2736        if (z(levIndexAnl)%VaR< a) exit 
2737          levelLow  = 0
2738      end do
2739    
2740      if  (levelLow/=0) then
2741        h_0  = h(levelLow)+(((a-z(levelLow))/(z(levelHigh)-z(levelLow)))*(h(levelHigh)-h(levelLow)))  
2742        N0a  = nu(levelLow)
2743        gz   = (lnu(levelLow+1)%Var-lnu(levelLow)%Var)/(h(levelLow+1)%Var-h(levelLow)%Var)
2744        NAa  = nu(levelLow)*exp(gz*(h_0-h(levelLow)))
2745        delta_alpha = 0.d0
2746        delta_alpha_top = 0.d0
2747        z_0 = a
2748        do while ((levelHigh)>=1) 
2749          N1a = nu(levelHigh)
2750          ka  = log(N0a/N1a)/(z(levelHigh) - z(levelLow))
2751          ! Test of Reflected
2752          do while (ka%Var<0.or.ka%Var>0.1) 
2753            levelHigh = levelHigh - 1
2754            N1a = nu(levelHigh)
2755            ka = log(N0a/N1a)/(z(levelHigh) - z(levelLow))
2756          end do
2757          Aa = 1e-6_dp* sqrt((MPC_PI_R8/2.d0*a)/ka )*NAa* exp(ka*(z_0-a))  
2758          if (z_0%Var==a) then
2759            Ba  = erf(sqrt(ka*(z(levelHigh)-a)))
2760          else  
2761            Ba2 = erf(sqrt(ka*(z(levelHigh)-a)))
2762            Ba3 = erf(sqrt((ka*(z_0-a))))
2763            Ba  = Ba2 - Ba3
2764          end if   
2765          delta_alpha = delta_alpha+2*ka*Ba*Aa 
2766          N0a = N1a
2767          NAa = N1a 
2768          z_0 = z(levelHigh)
2769
2770          levelLow  = levelHigh 
2771          levelHigh = levelLow-1
2772        end do
2773        Ba = erf(1-erf(sqrt((ka*(z_0-a)))))
2774        delta_alpha_top = 2*Aa*ka*Ba 
2775        last_a = a
2776        last_levIndexObs = levIndexObs
2777        bstv(levIndexObs)= delta_alpha +delta_alpha_top
2778      else  ! (levelLow==0) 
2779        ! Use loglinear extrapolation for most data (notably direct rays)
2780        if (a>(1._dp+prf%rst(ngpslev)%Var*1e-6_dp)*prf%Rad) then
2781          bstv(levIndexObs)=bstv(last_levIndexObs)*exp((-1._dp/6500._dp)*(a-last_a))
2782        else
2783        ! Use linear extrapolation (most reflected rays) from Information content in reflected
2784        ! signals during GPS Radio Occultation observations (Josep Aparicio et al.,2017) 
2785          bstv(levIndexObs)=bstv(last_levIndexObs)*exp((-1._dp/6500._dp)*(a-last_a))-2*acos(a/((1._dp+prf%rst(ngpslev)*1e-6_dp)*prf%Rad))
2786        end if
2787
2788      end if
2789    end do
2790  end subroutine gps_bndopv1
2791
2792
2793!modgpsro_mod
2794
2795  subroutine gps_setupro
2796    implicit none
2797
2798    ! Locals:
2799    integer :: nulnam,ierr,fnom,fclos,SatID
2800    
2801    ! Namelist variables for GPS-RO
2802    INTEGER :: LEVELGPSRO       ! Data level to use (1 for bending angle, 2 for refractivity)
2803    INTEGER :: GPSRO_MAXPRFSIZE ! Maximal number of data that is expected from a profile (default 300)
2804    REAL(8) :: SURFMIN          ! Minimum allowed distance to the model surface (default 0 m)
2805    REAL(8) :: HSFMIN           ! Minimum allowed MSL height of an obs          (default 0 m)
2806    REAL(8) :: HTPMAX           ! Maximum allowed MSL height of an obs          (default 70000 m)
2807    REAL(8) :: BGCKBAND         ! Maximum allowed deviation abs(O-P)/P          (default 0.05)
2808    REAL(8) :: HTPMAXER         ! Maximum MSL height to evaluate the obs error  (default to HTPMAX)
2809    REAL(4) :: WGPS(0:1023,4)   ! WGPS values for each satellite sensor
2810    character(len=20) :: gpsroError ! key for using dynamic/static refractivity error estimation (default 'DYNAMIC')
2811    LOGICAL :: gpsroBNorm       ! Choose to normalize based on B=H(x) (default=.True.), or approximate exponential reference
2812    LOGICAL :: gpsroEotvos      ! Add an operator-only Eotvos correction to local gravity (shift of altitudes, default False)
2813    REAL(8) :: gpsroNsigma      ! Factor applied to observation error for background departure check when gpsroBNorm is .true. (default 1.d6)
2814
2815    NAMELIST /NAMGPSRO/ LEVELGPSRO, GPSRO_MAXPRFSIZE, SURFMIN, HSFMIN, HTPMAX, HTPMAXER, &
2816        BGCKBAND, WGPS, gpsroError, gpsroBNorm, gpsroEotvos, gpsroNsigma
2817
2818    !
2819    !   Define default values:
2820    !
2821    LEVELGPSRO = gps_Level_RO_Ref
2822    GPSRO_MAXPRFSIZE = 300
2823    SURFMIN    = 0.d0
2824    HSFMIN     = 0.d0
2825    HTPMAX     = 70000.d0
2826    HTPMAXER   = -1.d0
2827    BGCKBAND   = 0.05d0
2828    gpsroError = 'DYNAMIC'
2829    gpsroBNorm = .True.
2830    gpsroEotvos= .False.
2831    gpsroNsigma= 1000000.d0
2832    !
2833    !   Force a pre-NML default for the effective data weight of all
2834    !   GPSRO satellites. This array has rows 0-1023 (following BUFR element
2835    !   SATID), and 4 cols. The 4 parameters for each SATID are used to
2836    !   represent data correlation, a combined property of the satellite
2837    !   hardware and provider postprocessing.
2838    !   The default assumes no correlation. 
2839    !
2840    WGPS = 0.
2841    WGPS(:,1) = 1.
2842    !
2843    !   Override with NML values:
2844    !     
2845    nulnam=0
2846    ierr=fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
2847    read(nulnam,nml=NAMGPSRO,iostat=ierr)
2848    if(ierr.ne.0) call utl_abort('gps_setupro: Error reading namelist')
2849    !if(mmpi_myid.eq.0) write(*,nml=NAMGPSRO)
2850    ierr=fclos(nulnam)
2851    if (HTPMAXER < 0.0D0) HTPMAXER = HTPMAX
2852    gps_Level_RO      = LEVELGPSRO
2853    gps_RO_MAXPRFSIZE = GPSRO_MAXPRFSIZE
2854    gps_SurfMin       = SURFMIN
2855    gps_HsfMin        = HSFMIN
2856    gps_HtpMax        = HTPMAX
2857    gps_HtpMaxEr      = HTPMAXER
2858    gps_BgckBand      = BGCKBAND
2859    gps_roError       = gpsroError
2860    gps_roBNorm       = gpsroBNorm
2861    gps_WGPS = WGPS
2862    gps_gpsroEotvos = gpsroEotvos
2863    gps_roNsigma = gpsroNsigma
2864
2865    if(mmpi_myid.eq.0) then
2866      write(*,*)'NAMGPSRO',gps_Level_RO, gps_RO_MAXPRFSIZE, gps_SurfMin, gps_HsfMin, &
2867           gps_HtpMax, gps_HtpMaxEr, gps_BgckBand, trim(gps_roError), gps_roBNorm, gpsroEotvos
2868      do SatID = 0, 1023
2869        if (WGPS(SatID,2) /= 0.) then
2870          write(*,*)'WGPS', SatID, gps_WGPS(SatID, 1:4)
2871        end if
2872      end do
2873    end if
2874  end subroutine gps_setupro
2875
2876  integer function gps_iprofile_from_index(index)
2877    implicit none
2878
2879    ! Arguments:
2880    integer, intent(in) :: index
2881
2882    ! Locals:
2883    integer :: i
2884
2885    gps_iprofile_from_index=-1
2886    do i=1,gps_numROProfiles
2887       if (index.eq.gps_vRO_IndexPrf(i, 1)) then
2888          gps_iprofile_from_index=i
2889          return
2890       end if
2891    end do
2892    return
2893  end function gps_iprofile_from_index
2894
2895
2896!modgpsztd_mod
2897
2898  subroutine gps_setupgb
2899    !
2900    !:Purpose: Initialisation of ground-based GPS - to read and to initialize
2901    !          GB-GPS namelist parameters and print information on options
2902    !          selected.
2903    !
2904    implicit none
2905
2906    ! Locals:
2907    integer :: nulnam,ierr,fnom,fclos
2908
2909    ! Namelist variables for Ground-based GPS (ZTD)
2910    REAL(8) :: DZMIN            ! Minimum DZ = Zobs-Zmod (m) for which DZ adjustment to ZTD will be made
2911    REAL(8) :: DZMAX = 1000.0D0 ! Maximum DZ (m) over which ZTD rejected due to topography (when LTOPOFILT = .TRUE.)
2912    REAL(8) :: YZTDERR          ! If < 0 use errors in input files; if > 0 use value as constant error (m); if 0 compute error as f(ZWD)
2913    REAL(8) :: YSFERRWGT        ! Scale factor for GPS surface met errors (account for time series obs with error correlations)
2914    REAL(8) :: YZDERRWGT        ! Scale factor for GPS ZTD errors (account for time series obs with error correlations)
2915    LOGICAL :: LASSMET          ! Choose to assimilate GPS Met surface P, T, T-Td
2916    LOGICAL :: LLBLMET          ! Indicate that surface met data blacklisted for GPS sites close to surface weather stations.
2917    LOGICAL :: LBEVIS           ! If .true. use Bevis(1994); if .false. use Rueger(2002) refractivity (k1,k2,k3) constants
2918    LOGICAL :: L1OBS            ! Choose to select a single ZTD observation
2919    LOGICAL :: LTESTOP          ! Choose to test ZTD observation operator (Omp and Bgck modes only)
2920    INTEGER :: IREFOPT          ! 1 = conventional expression for N using k1,k2,k3; 2 = Aparicio & Laroche N (incl. compressibility)
2921    INTEGER :: IZTDOP           ! 1 = use stored ZTD profiles to get ZTDmod; 2 = Vedel & Huang ZTD formulation: ZTDmod = ZHD(Pobs) + ZWD
2922
2923    NAMELIST /NAMGPSGB/ DZMIN, DZMAX, YZTDERR, LASSMET, YSFERRWGT,  &
2924         LLBLMET, YZDERRWGT, LBEVIS, L1OBS, LTESTOP, IREFOPT, IZTDOP
2925
2926    !*  .  1.1 Default values
2927    !!  .      --------------
2928
2929    DZMIN  = 2.0D0
2930    DZMAX  = 1000.0D0
2931    YZTDERR = 0.012D0
2932    LASSMET = .TRUE.
2933    YSFERRWGT = 1.0D0
2934    LLBLMET = .FALSE.
2935    YZDERRWGT = 1.0D0
2936    LBEVIS = .TRUE.
2937    IREFOPT = 1
2938    L1OBS = .FALSE.
2939    LTESTOP = .FALSE.
2940    IZTDOP = 1
2941
2942    nulnam=0
2943    ierr=fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
2944    read(nulnam,nml=NAMGPSGB,iostat=ierr)
2945    if(ierr.ne.0) call utl_abort('gps_setupgb: Error reading namelist')
2946    gps_gb_DZMIN     = DZMIN
2947    gps_gb_DZMAX     = DZMAX
2948    gps_gb_YZTDERR   = YZTDERR
2949    gps_gb_LASSMET   = LASSMET
2950    gps_gb_YSFERRWGT = YSFERRWGT
2951    gps_gb_LLBLMET   = LLBLMET
2952    gps_gb_YZDERRWGT = YZDERRWGT
2953    gps_gb_LBEVIS    = LBEVIS
2954    gps_gb_IREFOPT   = IREFOPT
2955    gps_gb_L1OBS     = L1OBS
2956    gps_gb_LTESTOP   = LTESTOP
2957    gps_gb_IZTDOP    = IZTDOP
2958    if(mmpi_myid.eq.0) write(*,nml=NAMGPSGB)
2959    ierr=fclos(nulnam)
2960
2961    IF (L1OBS.and.mmpi_myid.eq.0) THEN
2962      write(*,*)' '
2963      write(*,*)' ******************************************'
2964      write(*,*)' *        GB-GPS OBSERVATIONS             *'
2965      write(*,*)' *                                        *'
2966      write(*,*)' *        ONE OBSERVATION MODE            *'
2967      write(*,*)' *                                        *'
2968      write(*,*)' ******************************************'
2969      write(*,*)' '
2970    END IF
2971
2972    !   Options to fix/adjust model ZTD to observation height and
2973    !   assimilate GPS met data
2974
2975    if(mmpi_myid.eq.0) then
2976      write(*,*)' '
2977      write(*,*)' ******************************************'
2978      write(*,*)' *        GB-GPS OBSERVATIONS             *'
2979      write(*,*)' * DZ ADJUSTMENT IN gps_ztdopv IF DZ>DZMIN *'
2980      write(*,*)' * ZTD NOT ASSIM. IF DZ > DZMAX           *'
2981      write(*,*)' *                                        *'
2982      write(*,*)' ******************************************'
2983      write(*,*) ' '
2984      write(*,*) 'DZMIN, DZMAX = ', DZMIN, DZMAX
2985      write(*,*) ' '
2986
2987      IF (LASSMET) THEN
2988        IF ( LLBLMET ) THEN
2989        write(*,*)' '
2990        write(*,*)' *****************************************'
2991        write(*,*)' *          GB-GPS OBSERVATIONS          *'
2992        write(*,*)' *     GPS MET DATA ARE ASSIMILATED      *'
2993        write(*,*)' *     BUT BLACKLISTED NEAR SYNO STNS    *'
2994        write(*,*)' *                                       *'
2995        write(*,*)' *****************************************'
2996        write(*,*) 'YSFERRWGT = ', YSFERRWGT
2997        write(*,*) 'YZDERRWGT = ', YZDERRWGT
2998        write(*,*) ' '        
2999        ELSE
3000        write(*,*)' '
3001        write(*,*)' *****************************************'
3002        write(*,*)' *          GB-GPS OBSERVATIONS          *'
3003        write(*,*)' *     GPS MET DATA ARE ASSIMILATED      *'
3004        write(*,*)' *                                       *'
3005        write(*,*)' *****************************************'
3006        write(*,*) 'YSFERRWGT = ', YSFERRWGT
3007        write(*,*) 'YZDERRWGT = ', YZDERRWGT
3008        write(*,*) ' '
3009        END IF
3010      ELSE
3011        write(*,*)' '
3012        write(*,*)' *****************************************'
3013        write(*,*)' *          GB-GPS OBSERVATIONS          *'
3014        write(*,*)' *   GPS MET DATA ARE NOT ASSIMILATED    *'
3015        write(*,*)' *                                       *'
3016        write(*,*)' *****************************************'
3017        write(*,*) 'YZDERRWGT = ', YZDERRWGT
3018        write(*,*) ' '
3019      END IF
3020
3021      IF (YZTDERR .LT. 0.0D0) THEN
3022        write(*,*)' '
3023        write(*,*)' *****************************************'
3024        write(*,*)' *          GB-GPS OBSERVATIONS          *'
3025        write(*,*)' *    ZTD OBSERVATION ERROR FROM FERR    *'
3026        write(*,*)' *                                       *'
3027        write(*,*)' *****************************************'
3028      ELSE IF (YZTDERR .GT. 0.0D0) THEN
3029        write(*,*)' '
3030        write(*,*)' *****************************************'
3031        write(*,*)' *          GB-GPS OBSERVATIONS          *'
3032        write(*,*)' *     ZTD OBSERVATION ERROR IS FIXED    *'
3033        write(*,*)' *                                       *'
3034        write(*,*)' *****************************************'
3035        write(*,*)' '
3036        write(*,*)'YZTDERR (mm) = ', YZTDERR*1000.D0
3037      ELSE
3038        write(*,*)' '
3039        write(*,*)' *****************************************'
3040        write(*,*)' *          GB-GPS OBSERVATIONS          *'
3041        write(*,*)' *   ZTD OBSERVATION ERROR IS FROM ZWD   *'
3042        write(*,*)' *   USING SD(O-P) STATS (REGRESSION)    *'
3043        write(*,*)' *                                       *'
3044        write(*,*)' *****************************************'
3045        write(*,*)' '
3046      END IF
3047
3048      IF (IREFOPT .EQ. 1) THEN
3049        IF (LBEVIS) THEN
3050        write(*,*)' '
3051        write(*,*)' *****************************************'
3052        write(*,*)' *          GB-GPS OBSERVATIONS          *'
3053        write(*,*)' *                                       *'
3054        write(*,*)' *  CONVENTIONAL REFACTIVITY N USING     *'
3055        write(*,*)' *  BEVIS 92 K1, K2, K3 TO COMPUTE ZTD   *'
3056        write(*,*)' *****************************************'
3057        write(*,*)' ' 
3058        ELSE
3059        write(*,*)' '
3060        write(*,*)' *****************************************'
3061        write(*,*)' *          GB-GPS OBSERVATIONS          *'
3062        write(*,*)' *                                       *'
3063        write(*,*)' *  CONVENTIONAL REFACTIVITY N USING     *'
3064        write(*,*)' *  RUEGER 02 K1, K2, K3 TO COMPUTE ZTD  *'
3065        write(*,*)' *****************************************'
3066        write(*,*)' ' 
3067        END IF
3068        IF (IZTDOP .EQ. 1) THEN
3069        write(*,*)' '
3070        write(*,*)' *****************************************'
3071        write(*,*)' *          GB-GPS OBSERVATIONS          *'
3072        write(*,*)' *                                       *'
3073        write(*,*)' *   NORMAL ZTD OPERATOR -- ZTD COMPUTED *'
3074        write(*,*)' *           FROM ZTD(K) PROFILE         *'
3075        write(*,*)' *****************************************'
3076        write(*,*)' ' 
3077        ELSE
3078        write(*,*)' '
3079        write(*,*)' *****************************************'
3080        write(*,*)' *          GB-GPS OBSERVATIONS          *'
3081        write(*,*)' *                                       *'
3082        write(*,*)' *   ORIGINAL OPERATOR -- ZTD = ZHD+ZWD  *'
3083        write(*,*)' *        VEDEL AND HUANG (2004)         *'
3084        write(*,*)' *****************************************'
3085        write(*,*)' ' 
3086        END IF       
3087      ELSE
3088        write(*,*)' '
3089        write(*,*)' *****************************************'
3090        write(*,*)' *          GB-GPS OBSERVATIONS          *'
3091        write(*,*)' *                                       *'
3092        write(*,*)' *  APARICIO & LAROCHE REFRACTIVITY N    *'
3093        write(*,*)' *         USED TO COMPUTE ZTD           *'
3094        write(*,*)' *****************************************'
3095        write(*,*)' '       
3096      END IF
3097
3098    end if
3099
3100  end subroutine gps_setupgb
3101
3102  integer function gps_iztd_from_index(index)
3103    implicit none
3104 
3105    ! Arguments:
3106    integer, intent(in) :: index
3107
3108    ! Locals:
3109    integer :: i
3110
3111    gps_iztd_from_index = -1
3112    do i = 1, size(gps_ZTD_Index)
3113       if (index .eq. gps_ZTD_Index(i)) then
3114          gps_iztd_from_index = i
3115          return
3116       end if
3117    end do
3118    return
3119  end function gps_iztd_from_index
3120
3121end module gps_mod