physicsFunctions_mod sourceΒΆ

   1module physicsFunctions_mod
   2  ! MODULE physicsFunctions_mod (prefix='phf' category='8. Low-level utilities and constants')
   3  !
   4  !:Purpose:  A collection of basic functions for various purposes 
   5  !           (e.g. computing saturation vapour pressure).
   6  !
   7  use MathPhysConstants_mod
   8  use earthConstants_mod
   9  use utilities_mod
  10  use message_mod
  11  
  12  implicit none
  13  private
  14
  15  ! public procedures
  16  public :: phf_FOEW8, phf_FODLE8, phf_FOQST8, phf_FODQS8, phf_FOEFQ8, phf_FOQFE8, phf_FOTVT8, phf_FOTTV8
  17  public :: phf_FOHR8, phf_FOEWA8, phf_FODLA8, phf_FOQSA8, phf_FODQA8, phf_FOHRA8, phf_FOTW8, phf_FOTI8
  18  public :: phf_FODTW8, phf_FODTI8, phf_FOTWI8, phf_FODTWI8, phf_FOEW8_CMAM, phf_FOEI8_CMAM, phf_FOERAT8_CMAM
  19  public :: phf_FOEWI8_CMAM, phf_FODLE8_CMAM, phf_FOQST8_CMAM, phf_FOTW8_CMAM, phf_FOTI8_CMAM, phf_FODTW8_CMAM
  20  public :: phf_FODTI8_CMAM, phf_FOTWI8_CMAM, phf_FODTWI8_CMAM, phf_FQBRANCH, phf_FOEFQL, phf_fotvvl, phf_FOEFQA
  21  public :: phf_FOEFQPSA, phf_fottva, phf_folnqva
  22  public :: phf_convert_z_to_pressure,phf_convert_z_to_gz
  23  public :: phf_get_tropopause, phf_get_pbl, phf_calcDistance, phf_calcDistanceFast
  24  public :: phf_height2geopotential, phf_gravityalt, phf_gravitysrf
  25
  26  logical           :: phf_initialized = .false.
  27
  28  ! namelist variables:
  29  character(len=20) :: saturationCurve   ! saturationCurve must be one of 'Tetens_1930', 'Tetens_2018a', 'Tetens_2018'
  30
  31  contains
  32
  33   !--------------------------------------------------------------------------
  34   ! tetens_coefs_switch
  35   !--------------------------------------------------------------------------
  36   subroutine phf_tetens_coefs_switch
  37     !
  38     !:Purpose: Read water saturation strategy from nml. Options are:
  39     !
  40     !             - 'Tetens_1930' was active before 2018.
  41     !             - 'Tetens_2018a' is a partial update that missed some functions (active before IC4)
  42     !             - 'Tetens_2018' completes the update to the intended 2018 specification.
  43     !
  44     implicit none
  45
  46     ! Locals:
  47     integer            :: NULNAM,IERR,FNOM,FCLOS
  48     logical            :: validOption
  49     NAMELIST /NAMPHY/ saturationCurve
  50
  51     !$omp critical
  52     if (.not.phf_initialized) then
  53
  54       saturationCurve = 'Tetens_2018'
  55
  56       if ( .not. utl_isNamelistPresent('NAMPHY','./flnml') ) then
  57         call msg( 'phf_tetens_coefs_switch', &
  58              'NAMPHY is missing in the namelist. Default values will be taken.', mpiAll_opt=.False.)
  59       else
  60         ! Reading the namelist
  61         nulnam = 0
  62         ierr = fnom(nulnam, './flnml', 'FTN+SEQ+R/O', 0)
  63         read(nulnam, nml=namphy, iostat=ierr)
  64         if ( ierr /= 0) call utl_abort('tetens_coefs: Error reading namelist')
  65         ierr = fclos(nulnam)
  66       end if
  67
  68       validOption = (trim(saturationCurve) == 'Tetens_1930'  .or.  &
  69                      trim(saturationCurve) == 'Tetens_2018a' .or.  &
  70                      trim(saturationCurve) == 'Tetens_2018')
  71       if (.not.validOption) then
  72         call utl_abort('phf_tetens: WV Saturation not in expected list')
  73       end if
  74
  75       call msg( 'phf_tetens_coefs_switch ', saturationCurve )
  76       phf_initialized = .true.
  77     end if
  78     !$omp end critical
  79
  80   end subroutine phf_tetens_coefs_switch
  81
  82   !--------------------------------------------------------------------------
  83   ! phf_FOEW8
  84   !--------------------------------------------------------------------------
  85   real(8) function phf_FOEW8(TTT)
  86     !
  87     !:Purpose: Water vapour saturation pressure (Tetens) - EW or EI as a fct of TT
  88     !
  89     implicit none
  90
  91     ! Arguments:
  92     real(8), intent(in) :: TTT
  93
  94     if (.not.phf_initialized) call phf_tetens_coefs_switch
  95
  96     if (trim(saturationCurve) == 'Tetens_2018') then
  97       ! Updated coefficients 2018
  98       if (TTT > MPC_K_C_DEGREE_OFFSET_R8) then
  99         phf_FOEW8 = 610.94D0*EXP(17.625D0 * (TTT-MPC_TRIPLE_POINT_R8) / (TTT-30.11D0))
 100       else
 101         phf_FOEW8 = 610.94D0*EXP(22.587D0 * (TTT-MPC_TRIPLE_POINT_R8) / (TTT+ 0.71D0))
 102       end if
 103     else
 104       ! Classic Tetens 1930 coefficients
 105       if (TTT > MPC_TRIPLE_POINT_R8) then
 106         phf_FOEW8 = 610.78D0*EXP(17.269D0 * (TTT-MPC_TRIPLE_POINT_R8) / (TTT-35.86D0))
 107       else
 108         phf_FOEW8 = 610.78D0*EXP(21.875D0 * (TTT-MPC_TRIPLE_POINT_R8) / (TTT- 7.66D0))
 109       end if
 110     end if
 111   end function phf_foew8
 112
 113   !--------------------------------------------------------------------------
 114   ! phf_FODLE8
 115   !--------------------------------------------------------------------------
 116   real(8) function phf_FODLE8(TTT)
 117     !
 118     !:Purpose: FONCTION CALCULANT LA DERIVEE SELON T DE  LN EW (OU LN EI)
 119     !
 120     implicit none
 121
 122     ! Arguments:
 123     real(8), intent(in) :: TTT
 124
 125     if (.not.phf_initialized) call phf_tetens_coefs_switch
 126
 127     if (trim(saturationCurve) == 'Tetens_2018') then
 128       if (TTT > MPC_TRIPLE_POINT_R8) then
 129         phf_FODLE8 = 4283.76D0/(TTT-30.11D0)**2
 130       else
 131         phf_FODLE8 = 6185.90D0/(TTT+ 0.71D0)**2
 132       end if
 133     else
 134       if (TTT > MPC_TRIPLE_POINT_R8) then
 135         phf_FODLE8 = 4097.93D0/(TTT-35.86D0)**2
 136       else
 137         phf_FODLE8 = 5807.81D0/(TTT- 7.66D0)**2
 138       end if
 139     end if
 140   end function phf_FODLE8
 141
 142   !--------------------------------------------------------------------------
 143   ! phf_FOQST8
 144   !--------------------------------------------------------------------------
 145   real(8) function phf_FOQST8(TTT,PRS)
 146     !
 147     !:Purpose: FONCTION CALCULANT L'HUMIDITE SPECIFIQUE SATURANTE (QSAT)
 148     !
 149     implicit none
 150
 151     ! Arguments:
 152     real(8), intent(in) :: TTT
 153     real(8), intent(in) :: PRS
 154
 155     phf_FOQST8=MPC_EPS1_R8/(MAX(1.D0,PRS/phf_FOEW8(TTT))-MPC_EPS2_R8)
 156   end function phf_FOQST8
 157
 158   !--------------------------------------------------------------------------
 159   ! phf_FODQS8
 160   !--------------------------------------------------------------------------
 161   real(8) function phf_FODQS8(QST,TTT)
 162     !
 163     !:Purpose: FONCTION CALCULANT LA DERIVEE DE QSAT SELON T
 164     !
 165     implicit none
 166
 167     ! Arguments:
 168     real(8), intent(in) :: TTT
 169     real(8), intent(in) :: QST
 170
 171     phf_FODQS8=QST*(1.D0+MPC_DELTA_R8*QST)*phf_FODLE8(TTT)
 172   end function phf_FODQS8
 173   ! QST EST LA SORTIE DE FOQST
 174
 175   !--------------------------------------------------------------------------
 176   ! phf_FOEFQ8
 177   !--------------------------------------------------------------------------
 178   real(8) function phf_FOEFQ8(QQQ,PRS)
 179     !
 180     !:Purpose: FONCTION CALCULANT TENSION VAP (EEE) FN DE HUM SP (QQQ) ET PRS
 181     !
 182     implicit none
 183
 184     ! Arguments:
 185     real(8), intent(in) :: QQQ
 186     real(8), intent(in) :: PRS
 187
 188     phf_FOEFQ8= MIN(PRS,(QQQ*PRS) / (MPC_EPS1_R8 + MPC_EPS2_R8*QQQ))
 189   end function phf_FOEFQ8
 190
 191   !--------------------------------------------------------------------------
 192   ! FOQFE8
 193   !--------------------------------------------------------------------------
 194   real(8) function phf_FOQFE8(EEE,PRS)
 195     !
 196     !:Purpose: FONCTION CALCULANT HUM SP (QQQ) DE TENS. VAP (EEE) ET PRES (PRS)
 197     !
 198     implicit none
 199
 200     ! Arguments:
 201     real(8), intent(in) :: EEE
 202     real(8), intent(in) :: PRS
 203
 204     phf_FOQFE8= MIN(1.D0,MPC_EPS1_R8*EEE / (PRS-MPC_EPS2_R8*EEE))
 205   end function phf_FOQFE8
 206
 207   !--------------------------------------------------------------------------
 208   ! phf_FOTVT8
 209   !--------------------------------------------------------------------------
 210   real(8) function phf_FOTVT8(TTT,QQQ)
 211     !
 212     !:Purpose: FONCTION CALCULANT TEMP VIRT. (TVI) DE TEMP (TTT) ET HUM SP (QQQ)
 213     !
 214     implicit none
 215
 216     ! Arguments:
 217     real(8), intent(in) :: TTT
 218     real(8), intent(in) :: QQQ
 219
 220     phf_FOTVT8= TTT * (1.0D0 + MPC_DELTA_R8*QQQ)
 221   end function phf_FOTVT8
 222
 223   !--------------------------------------------------------------------------
 224   ! phf_FOTTV8
 225   !--------------------------------------------------------------------------
 226   real(8) function phf_FOTTV8(TVI,QQQ)
 227     !
 228     !:Purpose: FONCTION CALCULANT TTT DE TEMP VIRT. (TVI) ET HUM SP (QQQ)
 229     !
 230     implicit none
 231
 232     ! Arguments:
 233     real(8), intent(in) :: TVI
 234     real(8), intent(in) :: QQQ
 235
 236     phf_FOTTV8= TVI / (1.0D0 + MPC_DELTA_R8*QQQ)
 237   end function phf_FOTTV8
 238
 239   !--------------------------------------------------------------------------
 240   ! phf_FOHR8
 241   !--------------------------------------------------------------------------
 242   real(8) function phf_FOHR8(QQQ,TTT,PRS)
 243     !
 244     !:Purpose: FONCTION CALCULANT HUM REL DE HUM SP (QQQ), TEMP (TTT) ET PRES (PRS).
 245     !          Where HR = E/ESAT
 246     !
 247     implicit none
 248
 249     ! Arguments:
 250     real(8), intent(in) :: QQQ
 251     real(8), intent(in) :: TTT
 252     real(8), intent(in) :: PRS
 253
 254     phf_FOHR8 = MIN(PRS,phf_FOEFQ8(QQQ,PRS)) / phf_FOEW8(TTT)
 255   end function phf_FOHR8
 256
 257   ! LES 5 FONCTIONS SUIVANTES SONT VALIDES DANS LE CONTEXTE OU ON
 258   ! NE DESIRE PAS TENIR COMPTE DE LA PHASE GLACE DANS LES CALCULS
 259   ! DE SATURATION.
 260  
 261   !--------------------------------------------------------------------------
 262   ! phf_FOEWA8
 263   !--------------------------------------------------------------------------
 264   real(8) function phf_FOEWA8(TTT)
 265     !
 266     !:Purpose: FONCTION DE VAPEUR SATURANTE (TETENS)
 267     !
 268     implicit none
 269
 270     ! Arguments:
 271     real(8), intent(in) :: TTT
 272
 273     if (.not.phf_initialized) call phf_tetens_coefs_switch
 274
 275     if (trim(saturationCurve) == 'Tetens_2018') then
 276       phf_FOEWA8=610.94D0*EXP(17.625D0*(TTT-MPC_TRIPLE_POINT_R8)/(TTT-30.11D0))
 277     else
 278       phf_FOEWA8=610.78D0*EXP(17.269D0*(TTT-MPC_TRIPLE_POINT_R8)/(TTT-35.86D0))
 279     end if
 280   end function phf_FOEWA8
 281
 282   !--------------------------------------------------------------------------
 283   ! phf_FODLA8
 284   !--------------------------------------------------------------------------
 285   real(8) function phf_FODLA8(TTT)
 286     !
 287     !:Purpose: FONCTION CALCULANT LA DERIVEE SELON T DE LN EW
 288     !
 289     implicit none
 290
 291     ! Arguments:
 292     real(8), intent(in) :: TTT
 293
 294     if (.not.phf_initialized) call phf_tetens_coefs_switch
 295
 296     if (trim(saturationCurve) == 'Tetens_2018') then
 297       phf_FODLA8=17.625D0*(MPC_TRIPLE_POINT_R8-30.11D0)/(TTT-30.11D0)**2
 298     else
 299       phf_FODLA8=17.269D0*(MPC_TRIPLE_POINT_R8-35.86D0)/(TTT-35.86D0)**2
 300     end if
 301   end function phf_FODLA8
 302
 303   !--------------------------------------------------------------------------
 304   ! FOQSA8
 305   !--------------------------------------------------------------------------
 306   real(8) function phf_FOQSA8(TTT,PRS)
 307     !
 308     !:Purpose: FONCTION CALCULANT L'HUMIDITE SPECIFIQUE SATURANTE
 309     !
 310     implicit none
 311
 312     ! Arguments:
 313     real(8), intent(in) :: TTT
 314     real(8), intent(in) :: PRS
 315
 316     phf_FOQSA8=MPC_EPS1_R8/(MAX(1.D0,PRS/phf_FOEWA8(TTT))-MPC_EPS2_R8)
 317   end function phf_FOQSA8
 318
 319   !--------------------------------------------------------------------------
 320   ! phf_FODQA8
 321   !--------------------------------------------------------------------------
 322   real(8) function phf_FODQA8(QST,TTT)
 323     !
 324     !:Purpose: FONCTION CALCULANT LA DERIVEE DE QSAT SELON T
 325     !
 326     implicit none
 327
 328     ! Arguments:
 329     real(8), intent(in) :: QST
 330     real(8), intent(in) :: TTT
 331
 332     phf_FODQA8=QST*(1.D0+MPC_DELTA_R8*QST)*phf_FODLA8(TTT)
 333   end function phf_FODQA8
 334
 335   !--------------------------------------------------------------------------
 336   ! phf_FOHRA8
 337   !--------------------------------------------------------------------------
 338   real(8) function phf_FOHRA8(QQQ,TTT,PRS)
 339     !
 340     !:Purpose: FONCTION CALCULANT L'HUMIDITE RELATIVE
 341     !
 342     implicit none
 343
 344     ! Arguments:
 345     real(8), intent(in) :: QQQ
 346     real(8), intent(in) :: TTT
 347     real(8), intent(in) :: PRS
 348
 349     phf_FOHRA8=MIN(PRS,phf_FOEFQ8(QQQ,PRS))/phf_FOEWA8(TTT)
 350   end function phf_FOHRA8
 351
 352   ! LES 6 FONCTIONS SUIVANTES SONT REQUISES POUR LA TEMPERATURE
 353   ! EN FONCTION DE LA TENSION DE VAPEUR SATURANTE.
 354   ! (AJOUTE PAR YVES J. ROCHON, ARQX/SMC, JUIN 2004)
 355
 356   !--------------------------------------------------------------------------
 357   ! phf_FOTW8
 358   !--------------------------------------------------------------------------
 359   real(8) function phf_FOTW8(EEE)
 360     !
 361     !:Purpose: FONCTION DE LA TEMPERATURE EN FONCTION DE LA TENSION DE VAPEUR
 362     !          SATURANTE PAR RAPPORT A EW.
 363     !
 364     implicit none
 365
 366     ! Arguments:
 367     real(8), intent(in) :: EEE
 368
 369     if (.not.phf_initialized) call phf_tetens_coefs_switch
 370
 371     if (trim(saturationCurve) == 'Tetens_2018a' .or. trim(saturationCurve) == 'Tetens_2018') then
 372       phf_FOTW8=(30.11D0*LOG(EEE/610.94D0)-17.625D0*MPC_TRIPLE_POINT_R8)/ &
 373           (LOG(EEE/610.94D0)-17.625D0)
 374     else
 375       phf_FOTW8=(35.86D0*LOG(EEE/610.78D0)-17.269D0*MPC_TRIPLE_POINT_R8)/ &
 376           (LOG(EEE/610.78D0)-17.269D0)
 377     end if
 378   end function phf_FOTW8
 379
 380   !--------------------------------------------------------------------------
 381   ! phf_FOTI8
 382   !--------------------------------------------------------------------------
 383   real(8) function phf_FOTI8(EEE)
 384     !
 385     !:Purpose: FONCTION DE LA TEMPERATURE EN FONCTION DE LA TENSION DE VAPEUR
 386     !          SATURANTE PAR RAPPORT A EI.
 387     !
 388     implicit none
 389
 390     ! Arguments:
 391     real(8), intent(in) :: EEE
 392
 393     if (.not.phf_initialized) call phf_tetens_coefs_switch
 394
 395     if (trim(saturationCurve) == 'Tetens_2018') then
 396       phf_FOTI8=(-0.71D0*LOG(EEE/610.94D0)-22.587D0*MPC_TRIPLE_POINT_R8)/ &
 397           (LOG(EEE/610.94D0)-22.587D0)
 398     else
 399       phf_FOTI8=( 7.66D0*LOG(EEE/610.78D0)-21.875D0*MPC_TRIPLE_POINT_R8)/ &
 400           (LOG(EEE/610.78D0)-21.875D0)
 401     end if
 402   end function phf_FOTI8
 403
 404   !--------------------------------------------------------------------------
 405   ! phf_FODTH8
 406   !--------------------------------------------------------------------------
 407   real(8) function phf_FODTW8(TTT,EEE)
 408     !
 409     !:Purpose: FONCTION DE LA DERIVE DE LA TEMPERATURE EN FONCTION DE LA TENSION DE
 410     !           VAPEUR SATURANTE (EW).
 411     !
 412     implicit none
 413
 414     ! Arguments:
 415     real(8), intent(in) :: TTT
 416     real(8), intent(in) :: EEE
 417
 418     if (.not.phf_initialized) call phf_tetens_coefs_switch
 419
 420     if (trim(saturationCurve) == 'Tetens_2018a' .or. trim(saturationCurve) == 'Tetens_2018') then
 421       phf_FODTW8=(30.11D0-TTT)/EEE/(LOG(EEE/610.94D0)-17.625D0)
 422     else
 423       phf_FODTW8=(35.86D0-TTT)/EEE/(LOG(EEE/610.78D0)-17.269D0)
 424     endif
 425   end function phf_FODTW8
 426
 427   !--------------------------------------------------------------------------
 428   ! phf_FODTI8
 429   !--------------------------------------------------------------------------
 430   real(8) function phf_FODTI8(TTT,EEE)
 431     !
 432     !:Purpose: FONCTION DE LA DERIVE DE LA TEMPERATURE EN FONCTION DE LA TENSION DE
 433     !           VAPEUR SATURANTE (EI).
 434     !
 435     implicit none
 436
 437     ! Arguments:
 438     real(8), intent(in) :: TTT
 439     real(8), intent(in) :: EEE
 440
 441     if (.not.phf_initialized) call phf_tetens_coefs_switch
 442
 443     if (trim(saturationCurve) == 'Tetens_2018') then
 444       phf_FODTI8=(-0.71D0-TTT)/EEE / (LOG(EEE/610.94D0)-22.587D0)
 445     else
 446       phf_FODTI8=( 7.66D0-TTT)/EEE / (LOG(EEE/610.78D0)-21.875D0)
 447     end if
 448   end function phf_FODTI8
 449
 450   !--------------------------------------------------------------------------
 451   ! phf_FOTWI8
 452   !--------------------------------------------------------------------------
 453   real(8) function phf_FOTWI8(TTT,EEE)
 454     !
 455     !:Purpose: FONCTION DE L'AJUSTEMENT DE LA TEMPERATURE.
 456     !
 457     implicit none
 458
 459     ! Arguments:
 460     real(8), intent(in) :: TTT
 461     real(8), intent(in) :: EEE
 462
 463     phf_FOTWI8=MAX(0.0D0,SIGN(1.0D0,TTT-MPC_TRIPLE_POINT_R8))*phf_FOTW8(EEE)  &
 464               -MIN(0.0D0,SIGN(1.0D0,TTT-MPC_TRIPLE_POINT_R8))*phf_FOTI8(EEE)
 465   end function phf_FOTWI8
 466
 467   !--------------------------------------------------------------------------
 468   ! phf_FODWTI8
 469   !--------------------------------------------------------------------------
 470   real(8) function phf_FODTWI8(TTT,EEE)
 471     !
 472     !:Purpose: FONCTION DE L'AJUSTEMENT DE LA DERIVEE DE LA TEMPERATURE.
 473     !
 474     implicit none
 475 
 476     ! Arguments:
 477     real(8), intent(in) :: TTT
 478     real(8), intent(in) :: EEE
 479
 480     phf_FODTWI8=MAX(0.0D0,SIGN(1.0D0,TTT-MPC_TRIPLE_POINT_R8))*phf_FODTW8(TTT,EEE)  &
 481                -MIN(0.0D0,SIGN(1.0D0,TTT-MPC_TRIPLE_POINT_R8))*phf_FODTI8(TTT,EEE)
 482   end function phf_FODTWI8
 483
 484  ! LES 7 FONCTIONS SUIVANTES POUR EW-EI SONT REQUISES POUR LES MODELES
 485  ! CMAM ET CGCM. (AJOUTE PAR YVES J. ROCHON, ARQX/SMC, JUIN 2004)
 486  
 487  !--------------------------------------------------------------------------
 488  ! phf_FOEW8_CMAM
 489  !--------------------------------------------------------------------------
 490  real(8) function phf_FOEW8_CMAM(TTT)  
 491        !
 492        !:Purpose: FONCTION DE TENSION DE VAPEUR SATURANTE - EW
 493        !
 494        implicit none
 495 
 496        ! Arguments:
 497        real(8), intent(in) :: TTT
 498
 499        phf_FOEW8_CMAM= 100.D0*EXP(21.656D0-5418.D0/TTT)
 500  end function phf_FOEW8_CMAM
 501
 502  !--------------------------------------------------------------------------
 503  ! phf_FOEI8_CMAM
 504  !--------------------------------------------------------------------------
 505  real(8) function phf_FOEI8_CMAM(TTT)  
 506        !
 507        !:Purpose: FONCTION DE TENSION DE VAPEUR SATURANTE - EI
 508        !
 509        implicit none
 510 
 511        ! Arguments:
 512        real(8), intent(in) :: TTT
 513
 514        phf_FOEI8_CMAM= 100.D0*EXP(24.292D0-6141.D0/TTT)
 515  end function phf_FOEI8_CMAM
 516
 517  !--------------------------------------------------------------------------
 518  ! phf_FOERAT8_CMAM
 519  !--------------------------------------------------------------------------
 520  real(8) function phf_FOERAT8_CMAM(TTT) 
 521        !
 522        !:Purpose: FONCTION DE LA PROPORTION DE LA CONTRIBUTION DE EW VS EI
 523        !
 524        implicit none
 525 
 526        ! Arguments:
 527        real(8), intent(in) :: TTT
 528
 529        phf_FOERAT8_CMAM=MIN(1.0D0,0.0059D0+0.9941D0*EXP(-0.003102D0*  &
 530            MIN(0.0D0,TTT-MPC_TRIPLE_POINT_R8)**2))
 531  end function phf_FOERAT8_CMAM
 532
 533  !--------------------------------------------------------------------------
 534  ! phf_FOEWI8_CMAM
 535  !--------------------------------------------------------------------------
 536  real(8) function phf_FOEWI8_CMAM(TTT)  
 537        !
 538        !:Purpose: FONCTION DE TENSION DE VAPEUR SATURANTE RESULTANTE - EW et EI
 539        !
 540        implicit none
 541 
 542        ! Arguments:
 543        real(8), intent(in) :: TTT
 544
 545        phf_FOEWI8_CMAM = phf_FOEW8_CMAM(TTT)*phf_FOERAT8_CMAM(TTT)  &
 546               +(1.0D0-phf_FOERAT8_CMAM(TTT))*phf_FOEI8_CMAM(TTT)
 547  end function phf_FOEWI8_CMAM
 548
 549  !--------------------------------------------------------------------------
 550  ! phf_FODLE8_CMAM
 551  !--------------------------------------------------------------------------
 552  real(8) function phf_FODLE8_CMAM(TTT)  
 553        !
 554        !:Purpose: FONCTION DE LA DERIVE DE LN(E) PAR RAPPORT A LA TEMPERATURE
 555        !
 556        implicit none
 557
 558        ! Arguments:
 559        real(8), intent(in) :: TTT
 560
 561        phf_FODLE8_CMAM= phf_FOERAT8_CMAM(TTT)*5418.D0/TTT/TTT  &
 562             +(1.0D0-phf_FOERAT8_CMAM(TTT))*6141.D0/TTT/TTT
 563  end function phf_FODLE8_CMAM
 564
 565  !--------------------------------------------------------------------------
 566  ! phf_FOQST8_CMAM
 567  !--------------------------------------------------------------------------
 568  real(8) function phf_FOQST8_CMAM(TTT,PRS) 
 569        !
 570        !:Purpose: FONCTION CALCULANT L'HUMIDITE SPECIFIQUE SATURANTE (QSAT).
 571        !          PREND EN COMPTE LES PHASES GLACE ET EAU.
 572        !
 573        implicit none
 574 
 575        ! Arguments:
 576        real(8), intent(in) :: TTT
 577        real(8), intent(in) :: PRS
 578
 579        phf_FOQST8_CMAM=MPC_EPS1_R8/(MAX(1.0D0,PRS/  &
 580                           phf_FOEWI8_CMAM(TTT))-MPC_EPS2_R8)
 581  end function phf_FOQST8_CMAM
 582
 583  ! LES 6 FONCTIONS SUIVANTES SONT REQUISES POUR LA TEMPERATURE
 584  ! EN FONCTION DE LA TENSION DE VAPEUR SATURANTE POUR CMAM/CGCM
 585  ! (AJOUTE PAR YVES J. ROCHON, ARQX/SMC, JUIN 2004)
 586
 587  !--------------------------------------------------------------------------
 588  ! phf_FOTW8_CMAM
 589  !--------------------------------------------------------------------------
 590  real(8) function phf_FOTW8_CMAM(EEE) 
 591        !
 592        !:Purpose: FONCTION DE LA TEMPERATURE EN FONCTION DE LA TENSION DE VAPEUR
 593        !          SATURANTE PAR RAPPORT A EW.
 594        !
 595        implicit none
 596 
 597        ! Arguments:
 598        real(8) :: EEE
 599
 600        phf_FOTW8_CMAM=5418.D0/(21.656D0-LOG(EEE/100.0D0))
 601  end function phf_FOTW8_CMAM
 602
 603  !--------------------------------------------------------------------------
 604  ! phf_FOTI8_CMAM
 605  !--------------------------------------------------------------------------
 606  real(8) function phf_FOTI8_CMAM(EEE) 
 607        !
 608        !:Purpose: FONCTION DE LA TEMPERATURE EN FONCTION DE LA TENSION DE VAPEUR
 609        !          SATURANTE PAR RAPPORT A EI.
 610        !
 611        implicit none
 612 
 613        ! Arguments:
 614        real(8), intent(in) :: EEE
 615
 616        phf_FOTI8_CMAM=6141.D0/(24.292D0-LOG(EEE/100.0D0))
 617  end function phf_FOTI8_CMAM
 618
 619  !--------------------------------------------------------------------------
 620  ! phf_FODTW8_CMAM
 621  !--------------------------------------------------------------------------
 622  real(8) function phf_FODTW8_CMAM(TTT,EEE) 
 623        !
 624        !:Purpose: FONCTION DE LA DERIVE DE LA TEMPERATURE EN FONCTION DE LA TENSION DE
 625        !          VAPEUR SATURANTE (EW).
 626        !
 627        implicit none
 628 
 629        ! Arguments:
 630        real(8), intent(in) :: TTT
 631        real(8), intent(in) :: EEE
 632
 633        phf_FODTW8_CMAM=TTT/EEE/(21.656D0-LOG(EEE/100.0D0))
 634  end function phf_FODTW8_CMAM
 635
 636  !--------------------------------------------------------------------------
 637  ! phf_FODTI8_CMAM
 638  !--------------------------------------------------------------------------
 639  real(8) function phf_FODTI8_CMAM(TTT,EEE) 
 640        !
 641        !:Purpose: FONCTION DE LA DERIVE DE LA TEMPERATURE EN FONCTION DE LA TENSION DE
 642        !          VAPEUR SATURANTE (EI).
 643        !
 644        implicit none
 645 
 646        ! Arguments:
 647        real(8), intent(in) :: TTT
 648        real(8), intent(in) :: EEE
 649
 650        phf_FODTI8_CMAM=TTT/EEE/(24.292D0-LOG(EEE/100.0D0))
 651  end function phf_FODTI8_CMAM
 652
 653  !--------------------------------------------------------------------------
 654  ! phf_FOTWI8_CMAM
 655  !--------------------------------------------------------------------------
 656  real(8) function phf_FOTWI8_CMAM(TTT,EEE) 
 657        !
 658        !:Purpose: FONCTION DE L'AJUSTEMENT DE LA TEMPERATURE.
 659        !
 660        implicit none
 661 
 662        ! Arguments:
 663        real(8), intent(in) :: TTT
 664        real(8), intent(in) :: EEE
 665
 666        phf_FOTWI8_CMAM= phf_FOERAT8_CMAM(TTT)*phf_FOTW8_CMAM(EEE)+ &
 667                   (1.0D0-phf_FOERAT8_CMAM(TTT))*phf_FOTI8_CMAM(EEE)
 668  end function phf_FOTWI8_CMAM
 669
 670  !--------------------------------------------------------------------------
 671  ! phf_FODTWI8_CMAM
 672  !--------------------------------------------------------------------------
 673  real(8) function phf_FODTWI8_CMAM(TTT,EEE) 
 674        !
 675        !:Purpose: FONCTION DE L'AJUSTEMENT DE LA DERIVEE DE LA TEMPERATURE.
 676        !
 677        implicit none
 678 
 679        ! Arguments:
 680        real(8), intent(in) :: TTT
 681        real(8), intent(in) :: EEE
 682
 683        phf_FODTWI8_CMAM= phf_FOERAT8_CMAM(TTT)*phf_FODTW8_CMAM(TTT,EEE)+  &
 684                          (1.0D0-phf_FOERAT8_CMAM(TTT))*phf_FODTI8_CMAM(TTT,EEE)
 685  end function phf_FODTWI8_CMAM
 686
 687
 688  !--------------------------------------------------------------------------
 689  ! phf_FOBRANCH
 690  !--------------------------------------------------------------------------
 691  real(8) function phf_FQBRANCH(QQQ)  
 692        !
 693        !:Purpose: function returning 0/1 depending on the minimum q branch condition
 694        !          as discussed by Brunet (1996) to prevent getting a vapour pressure that exceeds
 695        !          the total pressure p when q exceeds 1.
 696        !
 697        implicit none
 698 
 699        ! Arguments:
 700        real(8), intent(in) :: QQQ
 701
 702        phf_FQBRANCH= 0.5D0+SIGN(0.5D0,1.D0-(QQQ))
 703  end function phf_FQBRANCH
 704
 705  !=============================================================================
 706  !
 707  !     TLM  of  THERMODYNAMIC FUNCTIONS USED IN 3DVAR
 708  !     CONSTANTS FROM COMMON /CTESDYN/
 709  !     NOTE: ALL FUNCTIONS WORK IN  S.I. UNITS
 710  !           I.E PRS IN PA, QQQ IN KG/KG 
 711  
 712  !--------------------------------------------------------------------------
 713  ! phf_FOEFQL
 714  !--------------------------------------------------------------------------
 715   real(8) function phf_FOEFQL(QQL,PRSL,QQQ,PRS,PNETA)  
 716        !
 717        !:Purpose: TLM  OF FUNCTION CALCULATING VAPOUR PRESSURE
 718        !
 719        !          INPUTS:
 720        !  
 721        !          * QQL ,  PERTURBATION OF LN SPECIFIC HUM
 722        !          * PRSL ,   PERTURBATION OF SURFACE PRESSURE
 723        !          * QQQ   ,  SPECIFIC HUMIDITY
 724        !          * PRS   , PRESSURE
 725        !          * PNETA   , VALUE OF ETA LEVEL
 726        !
 727        !          OUTPUT: 
 728        !
 729        !          * FOEFQL,  PERTURBATION  OF VAPOUR PRESSURE
 730        !
 731        implicit none
 732 
 733        ! Arguments:
 734        real(8), intent(in) :: QQL
 735        real(8), intent(in) :: PRSL
 736        real(8), intent(in) :: QQQ
 737        real(8), intent(in) :: PRS
 738        real(8), intent(in) :: PNETA
 739
 740        phf_FOEFQL= phf_FQBRANCH(QQQ)  &
 741           * ((QQL*MPC_EPS1_R8*PRS*QQQ/(MPC_EPS1_R8+MPC_EPS2_R8*QQQ)  &
 742           +  PRSL*PNETA*QQQ)/(MPC_EPS1_R8+MPC_EPS2_R8*QQQ))  &
 743           + (1.0D0 - phf_FQBRANCH(QQQ))*PRSL*PNETA
 744  end function phf_FOEFQL
 745
 746  !--------------------------------------------------------------------------
 747  ! phf_FOTVVL
 748  !--------------------------------------------------------------------------
 749   real(8) function phf_fotvvl(qqq,ttt,ttl,plnql) 
 750        !
 751        !:Purpose: Tangent-linear operator of virtual temperature 
 752        !
 753        implicit none
 754  
 755        ! Arguments:
 756        real(8), intent(in) :: qqq   ! backgroud specific humidity
 757        real(8), intent(in) :: ttt   ! backgroud temperature
 758        real(8), intent(in) :: ttl   ! temperature increment
 759        real(8), intent(in) :: plnql ! increment of logarithm specific humidity  (del(ln q))
 760
 761        phf_fotvvl=(1 + MPC_DELTA_R8*qqq)*ttl + MPC_DELTA_R8*qqq*ttt*plnql
 762  end function phf_fotvvl
 763
 764  !=============================================================================
 765  !
 766  !   DEFINITION OF ADJOINTS OF THERMODYNAMIC  FUNCTIONS
 767  !   CONSTANTS AS IN COMMON /CTESDYN/
 768  !     NOTE: ALL  UNITS S.I.
 769  !           I.E. PADES IN DEG K, PRS EN PA, QQQ EN KG/KG
 770  !
 771
 772  !--------------------------------------------------------------------------
 773  ! phf_FOEFQA
 774  !--------------------------------------------------------------------------
 775  real(8) function phf_FOEFQA(PADES,PGAMMA,QQQ,PRS)  
 776        !
 777        !:Purpose: ADJOINT OF LN SPECIFIC  HUM (QQQ) DUE TO DEWPOINT DEPRESSION CORRECTIONS
 778        !
 779        implicit none
 780 
 781        ! Arguments:
 782        real(8), intent(in) :: PADES  ! ADJOINT OF DEWPOINT DEPRESSION
 783        real(8), intent(in) :: PGAMMA ! ADOINT OF VAPOUR PRESSURE RELATIONSHIP
 784        real(8), intent(in) :: QQQ    ! SPECIFIC HUMIDITY
 785        real(8), intent(in) :: PRS    ! PRESSURE
 786
 787        phf_FOEFQA= PADES*PGAMMA*MPC_EPS1_R8*PRS*QQQ/  &
 788                    ((MPC_EPS1_R8+MPC_EPS2_R8*QQQ)*(MPC_EPS1_R8+MPC_EPS2_R8*QQQ))
 789  end function phf_FOEFQA
 790
 791  !--------------------------------------------------------------------------
 792  ! phf_FOEFQPSA
 793  !--------------------------------------------------------------------------
 794  real(8) function phf_FOEFQPSA(PADES,PGAMMA,QQQ,PNETA) 
 795        !
 796        !:Purpose: ADJOINT OF SURFACE PRESSURE  DUE TO DEWPOINT DEPRESSION CORRECTIONS
 797        !
 798        implicit none
 799 
 800        ! Arguments:
 801        real(8), intent(in) :: PADES  ! ADJOINT OF DEWPOINT DEPRESSION
 802        real(8), intent(in) :: PGAMMA ! ADOINT OF VAPOUR PRESSURE RELATIONSHIP
 803        real(8), intent(in) :: QQQ    ! SPECIFIC HUMIDITY
 804        real(8), intent(in) :: PNETA  ! VALUE OF NETA
 805
 806        phf_FOEFQPSA = PADES*PGAMMA*QQQ*PNETA/  &
 807                       (MPC_EPS1_R8+MPC_EPS2_R8*QQQ)
 808  end function phf_FOEFQPSA
 809
 810  !--------------------------------------------------------------------------
 811  ! phf_FOTTVa
 812  !--------------------------------------------------------------------------
 813  real(8) function phf_fottva(qqq,tva)  
 814        !
 815        !:Purpose: Adjoint of temperature due to virtual temperature correction
 816        !
 817        implicit none
 818 
 819        ! Arguments:
 820        real(8), intent(in) :: qqq ! background specific humidity
 821        real(8), intent(in) :: tva ! adjoint variable of virtual temperature
 822
 823        phf_fottva= (1D0 + MPC_DELTA_R8*qqq)*tva
 824  end function phf_fottva
 825
 826  !--------------------------------------------------------------------------
 827  ! phf_FOLNQVA
 828  !--------------------------------------------------------------------------
 829  real(8) function phf_folnqva(qqq,ttt,tva) 
 830        !
 831        !:Purpose:  Adjoint of logarithm of specific humidity due to virtual temperature correction
 832        !
 833        implicit none
 834 
 835        ! Arguments:
 836        real(8), intent(in) :: qqq ! background specific humidity
 837        real(8), intent(in) :: ttt ! background temperature
 838        real(8), intent(in) :: tva ! adjoint variable of virtual temperature
 839
 840        phf_folnqva = MPC_DELTA_R8*qqq*ttt*tva
 841  end function phf_folnqva
 842
 843  !-------------------------------------------------------------------------------------------
 844
 845  !--------------------------------------------------------------------------
 846  ! PHF_CONVERT_Z_TO_PRESSURE
 847  !--------------------------------------------------------------------------
 848  function  phf_convert_z_to_pressure(altitude,rgz_mod,press_mod,nlev,nlev_mod,lat,success) result(press)
 849    !          
 850    !:Purpose: Converts an array of (geometric) altitudes to pressures. Uses linear interpolation
 851    !          in log(p).  
 852    implicit none
 853
 854    ! Arguments:
 855    real(8), intent(in) :: altitude(nlev)      ! altitudes to convert to pressures (m)
 856    real(8), intent(in) :: rgz_mod(nlev_mod)   ! geopotential heights on model levels (m), assumed to be in decending order
 857    real(8), intent(in) :: press_mod(nlev_mod) ! pressure on model levels, assumed to be in ascending order
 858    real(8), intent(in) :: lat                 ! latitude (rad)
 859    integer, intent(in) :: nlev                ! length of altitude array
 860    integer, intent(in) :: nlev_mod            ! number of model levels
 861    logical, intent(inout) :: success(nlev)    ! flag indicating success
 862    ! Result:
 863    real(8) :: press(nlev)                     ! converted pressures
 864
 865    ! Locals:
 866    real(8) :: rgz(nlev)
 867    integer :: ilev,ilev_mod
 868
 869    ! Check model pressures
 870    if (any(press_mod.lt.0.0D0)) call utl_abort("phf_compute_z_to_pressure: Invalid model pressure.")
 871    
 872    ! Convert altitudes to geopotential heights
 873    rgz = phf_convert_z_to_gz(altitude,lat,nlev)
 874
 875    do ilev=1,nlev
 876
 877       ! Check if height is above or below model boundaries
 878       if ( rgz(ilev).gt.rgz_mod(1) .or. rgz(ilev).lt.rgz_mod(nlev_mod) ) then
 879          success(ilev)=.false.
 880       end if
 881
 882       if (success(ilev)) then
 883
 884          ! Find model layers directly above and below rgz(ilev).
 885          ! After exit of loop we will have 
 886          ! rgz_mod(ilev_mod) >= rgz(ilev) > rgz_mod(ilev_mod+1)
 887          do ilev_mod=1,nlev_mod-1
 888             if ( rgz(ilev).le.rgz_mod(ilev_mod) .and. &
 889                  rgz(ilev).gt.rgz_mod(ilev_mod+1) ) exit
 890          end do
 891          
 892          ! Linear interpolation in gz,log(p)
 893          press(ilev) = press_mod(ilev_mod+1) * (press_mod(ilev_mod)/press_mod(ilev_mod+1))**( &
 894               (rgz(ilev)-rgz_mod(ilev_mod+1))/(rgz_mod(ilev_mod)-rgz_mod(ilev_mod+1)) )
 895
 896       else
 897          press(ilev) = 0.0
 898       end if
 899
 900    end do
 901
 902  end function phf_convert_z_to_pressure
 903
 904  !--------------------------------------------------------------------------
 905  ! PHF_CONVERT_Z_TO_GZ
 906  !--------------------------------------------------------------------------
 907  function phf_convert_z_to_gz(altitude,lat,nlev) result(rgz)
 908    !          
 909    !:Purpose: Converts altitudes to geopotential heights. Uses the Helmert formula to
 910    !          parameterize the latitude dependence and uses analytical result of the
 911    !          integral of \int g(z)dz for the altitude dependence (see J.A. Dutton 1976,
 912    !          p.65). At an altitude of 50 km, the altitude and geopotential height
 913    !          differ by around 0.2-0.5 km, depending on the latitude.
 914    !
 915    implicit none
 916
 917    ! Arguments:
 918    real(8), intent(in) :: altitude(nlev) ! altitudes (m)
 919    real(8), intent(in) :: lat            ! latitude (rad)
 920    integer, intent(in) :: nlev           ! number of levels
 921    ! Result:
 922    real(8) :: rgz(nlev)                  ! geopotential heights (m)
 923
 924    rgz = (ec_rg/9.8) * (1.-2.64D-03*cos(2.*lat)+5.9D-6*cos(2.*lat)**2) * ec_ra*altitude/(ec_ra+altitude)
 925
 926  end function phf_convert_z_to_gz
 927
 928  !--------------------------------------------------------------------------
 929  ! PHF_GET_TROPOPAUSE
 930  !--------------------------------------------------------------------------
 931  function phf_get_tropopause(nmodlev,pressmod,tt,height,hu_opt) result(tropo_press)
 932    !          
 933    !:Purpose: Determines pressure level of tropopause. 
 934    !          Final tropopause is taken as max pressure (lowest altitude) from the
 935    !          water vapour and temperature based tropopauses.
 936    !
 937    implicit none
 938
 939    ! Arguments:
 940    integer, intent(in)           :: nmodlev           ! Number of model levels
 941    real(8), intent(in)           :: pressmod(nmodlev) ! Model pressure array (Pa)
 942    real(8), intent(in)           :: tt(nmodlev)       ! Model temperature (Kelvin)
 943    real(8), intent(in)           :: height(nmodlev)   ! Model height (m)
 944    real(8), intent(in), optional :: hu_opt(nmodlev)   ! Model specific humidity
 945    ! Result:
 946    real(8) :: tropo_press                             ! Tropopause level in Pa (output)
 947
 948    ! Locals:
 949    integer :: itop,i,k,ilaps
 950    real(8) :: hu_ppmv1,hu_ppmv2,hu_ppmv3,xlaps,tropo_press_hu
 951    real(8), parameter :: press_min=6000.         ! Min tropoause pressure 60 hPa.; equivalent to ~ 20km
 952    real(8), parameter :: height_min=6000.0           ! Min tropopause level in meters.
 953    real(8), parameter :: ppmv_threshold=10.0     
 954    real(8), parameter :: tgrad_threshold=0.002   ! degrees/m (2 degrees/km)
 955    real(8), parameter :: consth=0.160754938e+07  ! conversion from mass mixing ratio to ppmv;  1.0e+06 / (18.015/28.96)
 956
 957    tropo_press=-1.0
 958    if (all(height.lt.0.0))  call utl_abort('phf_get_tropopause: Missing height for determining tropopause pressure')
 959
 960    ! Initialize tropopause pressure level using temperature gradient.
 961    ! Thermal tropopause is defined as the lowest level (above height_min) at which (1) the lapse rate decreases
 962    ! to <= 2 C/km and (2) the average lapse rate between this level and all higher levels within 2 km are <= 2 C/km. 
 963    ! Ref: International Meteorological Vocabulary (2nd ed.). Geneva: Secretariat of the World Meteorological
 964    !      Organization. 1992. p. 636. ISBN 92-63-02182-1.
 965    ! The second requirement, based on hu, may give levels that are to high (pressure too low) in the winter hemisphere.
 966
 967    do itop=3,nmodlev
 968       if (pressmod(itop).ge.press_min) exit
 969    end do
 970    itop=itop-1
 971       
 972    do i=nmodlev,itop+1,-1
 973       if (height(i)-height(nmodlev).lt.height_min) cycle
 974       xlaps=-(tt(i)-tt(i-1))/(height(i)-height(i-1))
 975       if (xlaps.le.tgrad_threshold) then
 976          ilaps=1
 977          do k=i-1,itop,-1
 978             if (height(k)-height(i).gt.2000.0) exit
 979             xlaps=xlaps-(tt(k)-tt(k-1))/(height(k)-height(k-1))
 980             ilaps=ilaps+1
 981          end do
 982          if (xlaps/ilaps.le.tgrad_threshold) exit
 983       end if
 984    enddo
 985    tropo_press=pressmod(i)
 986    
 987    ! Improve on tropopause pressure levels using specific humidity if available,
 988
 989    if (present(hu_opt)) then
 990    
 991      !  Use water vapour
 992      
 993       hu_ppmv1=0.0
 994       do i=itop,nmodlev
 995
 996          ! Convert specific humidity to ppmv mixing ratio.
 997          ! First apply r=q/(1-q) to convert to mass mixing ratio.
 998
 999          if (hu_opt(i).le.0.8.and.hu_opt(i).ge.0) then
1000               hu_ppmv2 = consth*hu_opt(i)/(1.0-hu_opt(i))
1001          else if (hu_opt(i).gt.0.8) then
1002               hu_ppmv2 = consth*0.8/(1.0-0.8)
1003          else if (hu_opt(i).lt.0.0) then
1004               hu_ppmv2 = 0.0
1005          end if
1006
1007          ! Check if transition point reached.
1008          ! Added requirement that levels below also satisfy this condition.
1009
1010          if (hu_ppmv2.ge.ppmv_threshold) then
1011             ilaps=1
1012             do k=i+1,nmodlev
1013                if (height(i)-height(k).gt.5000.0) exit
1014                if (hu_opt(k).le.0.8.and.hu_opt(k).ge.0) then
1015                   hu_ppmv3 = consth*hu_opt(k)/(1.0-hu_opt(k))
1016                else if (hu_opt(k).gt.0.8) then
1017                   hu_ppmv3 = consth*0.8/(1.0-0.8)
1018                else
1019                   hu_ppmv3=0.0
1020                end if
1021                if (hu_ppmv3.lt.ppmv_threshold) ilaps=0
1022             end do
1023             if (ilaps.eq.1) exit
1024          end if
1025          hu_ppmv1=hu_ppmv2
1026       end do
1027       
1028       if (hu_ppmv2.ge.ppmv_threshold.and.ilaps.eq.1) then
1029       
1030            ! Interpolate between levels
1031      
1032             if (abs(hu_ppmv2-hu_ppmv1).lt.0.1) hu_ppmv1=hu_ppmv2-0.1
1033!             tropo_press_hu=(log(pressmod(i))*(ppmv_threshold-hu_ppmv1)+ &
1034!                    log(pressmod(i-1))*(hu_ppmv2-ppmv_threshold)) &
1035!                   /(hu_ppmv2-hu_ppmv1)
1036!             tropo_press_hu=exp(tropo_press_hu)
1037             tropo_press_hu=pressmod(i)
1038             
1039             tropo_press=min(tropo_press,tropo_press_hu)
1040       else
1041          write(*,*) 'phf_get_tropopause: Level and specific humidity: ',itop,hu_ppmv2
1042          call utl_abort('phf_get_tropopause: Specific humidity too small.')
1043       end if
1044                             
1045    end if
1046    
1047  end function phf_get_tropopause
1048
1049  !--------------------------------------------------------------------------
1050  ! PHF_GET_PBL
1051  !--------------------------------------------------------------------------
1052  function phf_get_pbl(nmodlev,pressmod,tt,height,hu_opt,uu_opt,vv_opt) result(pbl_press)
1053    ! 
1054    !:Purpose: Determines pressure level of planetary boundary layer using 
1055    !          a first threshold of 0.5 for the bulk Richadson number (after Mahrt, 1981; 
1056    !          requires availability of uu and vv). Threshold reduced to largest value
1057    !          between 0.25 and 0.5 if first not satisfied. 
1058    !
1059    !          If not found with this approach, applies a variant of the Heffter approach 
1060    !          described in Aliabadi et al (2016), with some local variation.
1061    !
1062    !          References:
1063    !
1064    !          * Aliabadi A.A., R.M. Staebler, J. de Grandpre, A. Zadra, and P.A.
1065    !            Vaillancourt, 2016: Comparison of Estimated Atmospheric
1066    !            Boundary Layer Mixing Height in teh Arctic and Southern Great
1067    !            Plains under Statisticallt Stable Conditions: Experimental
1068    !            and Numerical Aspects, Submitted to Atmosphere-Ocean (2015).
1069    !
1070    !          * Mahrt, L. 1981: Modelling depth of the stable boundary-layer,
1071    !            Bound-Lay. Meteorol., 21, 3-19
1072    !
1073    !          * Heffter, J.L.,1980: Transport layer depth calculations, Second 
1074    !            Joint Conference on Applications of Air Pollution Meteorology,
1075    !            New Orleans, LA, 24-27 March 1980. American Meteorological
1076    !            Society, Boston, MA.
1077    !       
1078    !          Comments: Currently assumes (uu,vv) midlayer levels approximately 
1079    !          at tt, height, and hu levels when size(uu).ne.nmodlev.
1080    !
1081    implicit none
1082
1083    ! Arguments:
1084    integer,           intent(in) :: nmodlev           ! Number of model levels for variables other than uu and vv
1085    real(8),           intent(in) :: pressmod(nmodlev) ! Model pressure array (Pa)
1086    real(8),           intent(in) :: tt(nmodlev)       ! Model temperature (Kelvin)
1087    real(8),           intent(in) :: height(nmodlev)   ! Model height (meters)
1088    real(8), optional, intent(in) :: uu_opt(:)           ! Model zonal wind component (m/s)
1089    real(8), optional, intent(in) :: vv_opt(:)           ! Model meridional wind component (m/s)
1090    real(8), optional, intent(in) :: hu_opt(nmodlev)     ! Specific humidity
1091    ! Result:
1092    real(8) :: pbl_press                     ! PBL level in Pa (output)
1093
1094    ! Locals:
1095    integer :: itop,i,id,igradmax,inv,iRiBmax
1096    real(8) :: RiB1,RiB2,RiBmax,zs,thetavs,thetavh(nmodlev),us,vs,uv,hus,huh,gradmax,grad
1097    real(8), parameter :: kappa = 287.04/1004.67  ! R/Cp
1098    real(8), parameter :: RiB_threshold=0.5, reduced=0.5 
1099    ! Imposed min presssure of PBL height of 200 hPa (extreme; PBL height should normally be under 3km)
1100    real(8), parameter :: press_min=20000.  
1101    real(8) :: huw(nmodlev)
1102
1103    pbl_press=-1.0
1104     
1105    ! Set values for lowest prognostic level 
1106
1107    i = nmodlev   
1108    
1109    if (all(height.lt.0.0))  call utl_abort('phf_get_pbl: Missing height for determining PBL pressure')
1110
1111    ! Convert hu to mass mixing ratio
1112
1113    if (present(hu_opt)) then
1114       huw(:)=hu_opt(:)
1115    else
1116       huw(:)=0.0
1117    end if
1118    
1119    if (huw(i).le.0.8.and.huw(i).ge.0) then
1120        hus = huw(i)/(1.0-huw(i))
1121    else if (huw(i).gt.0.8) then
1122        hus = 0.8/(1.0-0.8)
1123    else if (huw(i).lt.0.0) then
1124        hus = 0.0
1125    end if
1126    zs = height(i)*0.001
1127    
1128    ! Potential virtual temperature at lowest prognostic level
1129
1130    thetavs = tt(i)*(1.D5/pressmod(i))**kappa* (1.0 + 0.61*hus )
1131    thetavh(nmodlev)=thetavs
1132
1133    ! Set max vertical level
1134
1135    do itop=2,nmodlev-1
1136       if (pressmod(itop).ge.press_min) exit
1137    end do
1138
1139    RiB1=0.0
1140    RiB2=0.0
1141    RiBmax=0.0
1142    iRiBmax=0
1143    if (present(uu_opt).and.present(vv_opt)) then
1144       id=nmodlev-size(uu_opt)
1145       if (id.gt.1.or.id.lt.0) then
1146          call utl_abort('phf_get_pbl: Unexpected number of UV levels, nmodlev = ' // trim(utl_str(nmodlev)) // ' , size(uu) = ' // trim(utl_str(size(uu_opt))) )    
1147       end if
1148       us = uu_opt(size(uu_opt))
1149       vs = vv_opt(size(vv_opt))
1150       
1151!      us,vs set to 0.0
1152!       us=0.0
1153!       vs=0.0
1154 
1155       ! Calc RiB from near-surface to level attaining RiB_threshold
1156 
1157       do i=nmodlev-1,itop,-1
1158  
1159           if (huw(i).le.0.8.and.huw(i).ge.0) then
1160               huh = huw(i)/(1.0-huw(i))
1161           else if (huw(i).gt.0.8) then
1162               huh = 0.8/(1.0-0.8)
1163           else if (huw(i).lt.0.0) then
1164               huh = 0.0
1165           end if
1166           thetavh(i) = tt(i)*(1.D5/pressmod(i))**kappa* ( 1.0 + 0.61*huh )
1167
1168           if (id.eq.0) then
1169               uv = max( (uu_opt(i)-us)**2 + (vv_opt(i)-vs)**2, 1.D-8 ) 
1170           else
1171              ! Take layer midpoint values
1172              uv = max( ((uu_opt(i)+uu_opt(i-1))/2.0-us)**2 + ((vv_opt(i)+vv_opt(i-1))/2.0-vs)**2, 1.D-8 ) 
1173           end if
1174         
1175           RiB2 = ec_rg * (thetavh(i)-thetavs) * (height(i)*0.001-zs) / (thetavs*uv)
1176           if (RiBmax.lt.RiB2.and.RiB2.ge.reduced*RiB_threshold) then
1177              RiBmax=RiB2
1178              iRiBmax=i
1179           end if
1180           if (RiB2.ge.RiB_threshold) exit
1181           RiB1=RiB2
1182       end do
1183    else
1184    
1185       ! Calc only theta
1186      
1187       do i=nmodlev-1,itop,-1
1188  
1189           if (huw(i).le.0.8.and.huw(i).ge.0) then
1190               huh = huw(i)/(1.0-huw(i))
1191           else if (huw(i).gt.0.8) then
1192               huh = 0.8/(1.0-0.8)
1193           else if (huw(i).lt.0.0) then
1194               huh = 0.0
1195           end if
1196           thetavh(i) = tt(i)*(1.D5/pressmod(i))**kappa* ( 1.0 + 0.61*huh )
1197       end do
1198    end if   
1199    
1200    if (RiB2.ge.RiB_threshold) then    
1201   
1202      !  Interpolate between levels
1203
1204       pbl_press=(log(pressmod(i))*(RiB_threshold-RiB1)+ &
1205               log(pressmod(i+1))*(RiB2-RiB_threshold)) &
1206               /(RiB2-RiB1)
1207       pbl_press=exp(pbl_press)
1208    else if (RiBmax.ge.reduced*RiB_threshold) then
1209       ! Apply to level with largest RiB between reduced*RiB_threshold and RiB_threshold
1210       pbl_press=pressmod(iRiBmax)
1211    else
1212    
1213       ! Estimate PBL level using the Heffter conditions:
1214       ! First find lowest inversion layer where dtheta>2K.
1215       ! If found, assign mid of layer as PBL level. 
1216       ! Otherwise, assign PBL level as that with largest
1217       ! theta gradient.
1218       
1219       i=nmodlev-1
1220       do while (i.gt.itop) 
1221          !if (thetavh(i)-thetavh(i+1).gt.0.0) then
1222          if ((thetavh(i)-thetavh(i+1))/(height(i)-height(i+1)).ge.0.005) then
1223             ! Near bottom of inversion layer found
1224             inv=i+1
1225             i=i-1
1226             !do while (thetavh(i)-thetavh(i+1).gt.0.0.and.i.gt.itop) 
1227             do while ((thetavh(i)-thetavh(i+1))/(height(i)-height(i+1)).ge.0.005.and.i.gt.itop) 
1228                 i=i-1
1229             end do
1230             if ((thetavh(i+1)-thetavh(inv)).gt.2.0)  then
1231                ! Apply  midlayer as PBL
1232                pbl_press=sqrt(pressmod(i+1)*pressmod(inv))
1233                exit
1234             end if 
1235          else
1236             i=i-1
1237          end if
1238       end do
1239       if (pbl_press.le.0.0) then
1240          gradmax=-1.D30
1241          igradmax=nmodlev-1
1242          do i=nmodlev-1,itop,-1
1243             grad=(thetavh(i)-thetavh(i+1))/(height(i)-height(i+1))
1244             if (gradmax.lt.grad) then
1245                gradmax=(thetavh(i)-thetavh(i+1))/(height(i)-height(i+1))
1246                igradmax=i
1247                if (grad.ge.0.005) then ! Check next layer as well
1248                   if ((thetavh(i-1)-thetavh(i))/(height(i-1)-height(i)).ge.0.005) exit
1249                end if
1250            end if
1251          end do          
1252          pbl_press=pressmod(igradmax)
1253          ! write(*,*) 'phf_get_pbl: Warning2 - Max allowed altitude reached for. ',pbl_press,igradmax,gradmax,RiB2,iRiBmax,RiBmax
1254       !else
1255       !   write(*,*) 'phf_get_pbl: Warning1 - Max allowed altitude reached for. ',pbl_press,i,RiB2,iRiBmax,RiBmax     
1256       end if
1257    end if
1258
1259  end function phf_get_pbl
1260
1261  !--------------------------------------------------------------------------
1262  ! phf_calcDistance
1263  !--------------------------------------------------------------------------
1264  function phf_calcDistance(lat2, lon2, lat1, lon1) result(distanceInM)
1265    !
1266    !:Purpose: Compute the distance between two point on earth: (lat1,lon1) 
1267    !          and (lat2,lon2). Calcul utilisant la Formule d'Haversine.
1268    !
1269    !          Reference: R.W. Sinnott,'Virtues of Haversine',Sky and 
1270    !          Telescope, vol.68, no.2, 1984, p.159)
1271    !
1272    implicit none
1273
1274    ! Arguments:
1275    real(8), intent(in) :: lat1
1276    real(8), intent(in) :: lon1
1277    real(8), intent(in) :: lat2
1278    real(8), intent(in) :: lon2
1279    ! Result:
1280    real(8) :: distanceInM
1281
1282    ! Locals:
1283    real(8) :: dlat, dlon, a, c
1284
1285    dlon = lon2 - lon1
1286    dlat = lat2 - lat1
1287
1288    a = (sin(dlat/2.d0))**2 + cos(lat1)*cos(lat2)*(sin(dlon/2.d0))**2
1289    c = 2.d0 * atan2(sqrt(a),sqrt(1.d0-a))
1290    distanceInM = ec_ra * c
1291
1292  end function phf_calcDistance
1293
1294  !--------------------------------------------------------------------------
1295  ! phf_calcDistanceFast
1296  !--------------------------------------------------------------------------
1297  function phf_calcDistanceFast(lat2, lon2, lat1, lon1) result(distanceInM)
1298    !
1299    !:Purpose: Compute the distance between two point on earth: (lat1,lon1)
1300    !          and (lat2,lon2). Using a quick and dirty formula good for 
1301    !          short distances not close to the pole.
1302    !
1303    implicit none
1304
1305    ! Arguments:
1306    real(8), intent(in) :: lat1
1307    real(8), intent(in) :: lon1
1308    real(8), intent(in) :: lat2
1309    real(8), intent(in) :: lon2
1310    ! Result:
1311    real(8) :: distanceInM
1312
1313    ! Locals:
1314    real(8) :: dlat, dlon
1315
1316    dlon = (lon2 - lon1)*cos(lat1)
1317    dlat = lat2 - lat1
1318
1319    distanceInM = ec_ra * sqrt(dlon*dlon + dlat*dlat)
1320
1321  end function phf_calcDistanceFast
1322
1323  !--------------------------------------------------------------------------
1324  ! PHF_GRAVITYSRF
1325  !--------------------------------------------------------------------------
1326  function phf_gravitysrf(sLat)
1327    !
1328    !:Purpose: Normal gravity on ellipsoidal surface
1329    !
1330    implicit none
1331
1332    ! Arguments:
1333    real(8), intent(in)  :: sLat           ! sin of latitude
1334    ! Result:
1335    real(8)              :: phf_gravitysrf ! normal gravity (m/s2)
1336
1337    ! Locals:
1338    real(8)              :: ks2
1339    real(8)              :: e2s
1340
1341    ks2 = ec_wgs_TNGk * sLat*sLat
1342    e2s = 1.D0 - ec_wgs_e2 * sLat*sLat
1343    phf_gravitysrf = ec_wgs_GammaE * (1.D0 + ks2) / sqrt(e2s)
1344  end function phf_gravitysrf
1345
1346  !--------------------------------------------------------------------------
1347  ! PHF_GRAVITYALT
1348  !--------------------------------------------------------------------------
1349  function phf_gravityalt(sLat, Altitude)
1350    !
1351    !:Purpose: Normal gravity above the ellipsoidal surface
1352    !
1353    implicit none
1354
1355    ! Arguments:
1356    real(8), intent(in)  :: sLat           ! sin of latitude
1357    real(8), intent(in)  :: Altitude       ! altitude (m)
1358    ! Result:
1359    real(8)              :: phf_gravityalt ! Normal gravity (m/s2)
1360
1361    ! Locals:
1362    real(8)              :: C1
1363    real(8)              :: C2
1364
1365    C1 =-2.D0/ec_wgs_a*(1.D0+ec_wgs_f+ec_wgs_m-2*ec_wgs_f*sLat*sLat)
1366    C2 = 3.D0/ec_wgs_a**2
1367    phf_gravityalt = phf_gravitysrf(sLat)*                                   &
1368         (1.D0 + C1 * Altitude + C2 * Altitude**2)
1369  end function phf_gravityalt
1370
1371  !--------------------------------------------------------------------------
1372  ! PHF_HEIGHT2GEOPOTENTIAL
1373  !--------------------------------------------------------------------------
1374  subroutine phf_height2geopotential(altitude, latitude, geopotential, &
1375                                     printHeight_opt)
1376    !
1377    !:Purpose: Geopotential energy at a given point.
1378    !          Result is based on the WGS84 approximate expression for the
1379    !          gravity acceleration as a function of latitude and altitude,
1380    !          integrated with the trapezoidal rule.
1381    !
1382    implicit none
1383
1384    ! Arguments:
1385    real(8),           intent(in)    :: altitude(:)     ! altitude (m)
1386    real(8),           intent(in)    :: latitude        ! latitude (rad)
1387    real(8),           intent(inout) :: geopotential(:) ! geopotential (m2/s2)
1388    logical, optional, intent(inout) :: printHeight_opt
1389
1390    ! Locals:
1391    integer           :: nlev, nlev500m
1392    real(8), allocatable :: alt500m(:), gravity500m(:)
1393    real(8)           :: delAlt, aveGravity, sLat, gravity, gravityM1
1394    integer           :: i, ilev 
1395
1396    nlev = size(altitude)
1397    sLat = sin(latitude)
1398
1399    nlev500m = int(altitude(nlev) / 500.D0)
1400    if ( nlev500m >= 1) then
1401      allocate(alt500m(0:nlev500m))
1402      allocate(gravity500m(0:nlev500m))
1403
1404      ! Calculate gravity and height of levels for 500m-layers
1405      do i = 0, nlev500m
1406        alt500m(i) = i * 500.0D0
1407        gravity500m(i) = phf_gravityalt(sLat, alt500m(i))
1408      enddo
1409
1410      geopotential(nlev) = 0.0D0
1411      ! integrate from surface on the 500m-layers untill below the desired altitude
1412      do i = 1, nlev500m
1413        delAlt = alt500m(i) - alt500m(i-1)
1414        aveGravity = 0.5D0 * (gravity500m(i) + gravity500m(i-1))
1415        geopotential(nlev) = geopotential(nlev) + delAlt * aveGravity
1416      enddo
1417
1418      ! Add the contribution from top of the last 500m-layer to the altitude  
1419      delAlt = altitude(nlev) - alt500m(nlev500m)
1420      aveGravity = 0.5D0 * (phf_gravityalt(sLat, altitude(nlev)) + gravity500m(nlev500m))
1421      geopotential(nlev) = geopotential(nlev) + delAlt * aveGravity 
1422      gravityM1 = phf_gravityalt(sLat, altitude(nlev))
1423
1424      deallocate(alt500m)
1425      deallocate(gravity500m)
1426
1427    else
1428      ! At surface, use local gravity to get height
1429      gravity = phf_gravityalt(sLat,altitude(nlev))
1430      geopotential(nlev) = altitude(nlev) * gravity
1431      gravityM1 = gravity
1432
1433    endif
1434
1435    ! At upper-levels, integrate on model levels to get height 
1436    do ilev = nlev-1, 1, -1
1437      gravity = phf_gravityalt(sLat,altitude(ilev))
1438      aveGravity = 0.5D0 * (gravity + gravityM1)
1439      delAlt = altitude(ilev) - altitude(ilev+1)
1440      geopotential(ilev) = geopotential(ilev+1) + delAlt * aveGravity
1441      gravityM1 = gravity
1442    enddo
1443
1444    if ( present(printHeight_opt) ) then
1445      if ( printHeight_opt ) then
1446        write(*,*) 'phf_height2geopotential, Z_T:'
1447        write(*,*) geopotential(:)
1448
1449        printHeight_opt = .false.
1450      endif
1451    endif
1452
1453  end subroutine phf_height2geopotential
1454  
1455end module physicsFunctions_mod