calcHeightAndPressure_mod sourceΒΆ

   1module calcHeightAndPressure_mod
   2  ! MODULE calcHeightAndPressure_mod (prefix='czp' category='4. Data Object transformations')
   3  !
   4  !:Purpose:  Subroutines for computing height and/or pressure on statevectors
   5  !           and columns depending on the vgrid kind.
   6  !           Nonlinear, tangent-linear and adjoint versions of these
   7  !           transformations are included in separate subroutines.
   8  !           Depending on the vertical representation of the state or column,
   9  !           pressure or height values are either computed or retrieved using
  10  !           the vgrid (https://gitlab.science.gc.ca/RPN-SI/vgrid) library.
  11  !           When computation is required (for instance to compute height on a
  12  !           GEM-P, represented on pressure coordinates), thermodynamical 
  13  !           variables are required, typically `P0`, `TT` and `HU`.
  14  !           Height and pressure values are obtained for both thermodynamical
  15  !           and momentum levels and labeled `Z_T` (`P_T`) and `Z_M` (`P_M`).
  16  !
  17  use codePrecision_mod
  18  use mathPhysConstants_mod
  19  use earthConstants_mod
  20  use physicsFunctions_mod
  21  use verticalCoord_mod
  22  use gridstatevector_mod
  23  use columnData_mod
  24  use utilities_mod
  25  use message_mod
  26  use gps_mod
  27  use HorizontalCoord_mod
  28  use Vgrid_Descriptors
  29  implicit none
  30  save
  31  private
  32
  33  ! public procedures
  34  public :: czp_calcZandP_nl, czp_calcZandP_tl, czp_calcZandP_ad
  35  public :: czp_calcHeight_nl, czp_calcHeight_tl, czp_calcHeight_ad
  36  public :: czp_calcPressure_nl, czp_calcPressure_tl, czp_calcPressure_ad
  37  public :: czp_calcReturnHeight_gsv_nl, czp_calcReturnPressure_gsv_nl
  38  public :: czp_calcReturnHeight_col_nl, czp_calcReturnPressure_col_nl
  39  public :: czp_ensureCompatibleTops
  40  public :: czp_fetch3DLevels, czp_fetch1DLevels, czp_fetch1DdPdPs
  41
  42  interface czp_fetch3DLevels
  43    module procedure fetch3DLevels_r8
  44    module procedure fetch3DLevels_r4
  45  end interface czp_fetch3DLevels
  46  interface czp_fetch1DLevels
  47    module procedure fetch1DLevels_r8
  48  end interface czp_fetch1DLevels
  49  interface czp_fetch1DdPdPs
  50    module procedure fetch1DdPdPs_r8
  51  end interface czp_fetch1DdPdPs
  52  interface czp_calcZandP_nl
  53    module procedure calcZandP_gsv_nl
  54    module procedure calcZandP_col_nl
  55  end interface czp_calcZandP_nl
  56  interface czp_calcZandP_tl
  57    module procedure calcZandP_gsv_tl
  58    module procedure calcZandP_col_tl
  59  end interface czp_calcZandP_tl
  60  interface czp_calcZandP_ad
  61    module procedure calcZandP_gsv_ad
  62    module procedure calcZandP_col_ad
  63  end interface czp_calcZandP_ad
  64
  65  interface czp_calcHeight_nl
  66    module procedure calcHeight_gsv_nl
  67    module procedure calcHeight_col_nl
  68  end interface czp_calcHeight_nl
  69  interface czp_calcHeight_tl
  70    module procedure calcHeight_gsv_tl
  71    module procedure calcHeight_col_tl
  72  end interface czp_calcHeight_tl
  73  interface czp_calcHeight_ad
  74    module procedure calcHeight_gsv_ad
  75    module procedure calcHeight_col_ad
  76  end interface czp_calcHeight_ad
  77
  78  interface czp_calcPressure_nl
  79    module procedure calcPressure_gsv_nl
  80    module procedure calcPressure_col_nl
  81  end interface czp_calcPressure_nl
  82  interface czp_calcPressure_tl
  83    module procedure calcPressure_gsv_tl
  84    module procedure calcPressure_col_tl
  85  end interface czp_calcPressure_tl
  86  interface czp_calcPressure_ad
  87    module procedure calcPressure_gsv_ad
  88    module procedure calcPressure_col_ad
  89  end interface czp_calcPressure_ad
  90
  91  ! private module variables
  92  real(8), allocatable :: coeff_M_TT_gsv(:,:,:,:), coeff_M_HU_gsv(:,:,:,:)
  93  real(8), allocatable :: coeff_T_TT_gsv(:,:,:),   coeff_T_HU_gsv(:,:,:)
  94  real(8), allocatable :: coeff_M_P0_delPM_gsv(:,:,:,:)
  95  real(8), allocatable :: coeff_M_P0_dP_delPT_gsv(:,:,:,:)
  96  real(8), allocatable :: coeff_M_P0_dP_delP0_gsv(:,:,:,:)
  97  real(8), allocatable :: coeff_T_P0_delP1_gsv(:,:,:)
  98  real(8), allocatable :: coeff_T_P0_dP_delPT_gsv(:,:,:)
  99  real(8), allocatable :: coeff_T_P0_dP_delP0_gsv(:,:,:)
 100
 101  real(8), allocatable :: coeff_M_TT_col(:,:), coeff_M_HU_col(:,:)
 102  real(8), allocatable :: coeff_T_TT_col(:),   coeff_T_HU_col(:)
 103  real(8), allocatable :: coeff_M_P0_delPM_col(:,:)
 104  real(8), allocatable :: coeff_M_P0_dP_delPT_col(:,:)
 105  real(8), allocatable :: coeff_M_P0_dP_delP0_col(:,:)
 106  real(8), allocatable :: coeff_T_P0_delP1_col(:),   coeff_T_P0_dP_delPT_col(:)
 107  real(8), allocatable :: coeff_T_P0_dP_delP0_col(:)
 108
 109contains
 110  !---------------------------------------------------------------------
 111  ! subroutines operating on struct_gsv
 112  !---------------------------------------------------------------------
 113
 114  !---------------------------------------------------------
 115  ! calcZandP_gsv_nl
 116  !---------------------------------------------------------
 117  subroutine calcZandP_gsv_nl(statevector)
 118    !
 119    !:Purpose: Pressure and height computation on the grid in proper order
 120    !           depending on the vgrid kind.
 121    !           Depending on the vcode, the routine will check the existence of
 122    !           P_* (vcode=5xxx) or Z_* (vcode=2100x) first and proceed with
 123    !           pressure (height) computation.  Then, if the other variables are
 124    !           also present, it will secondly compute height (pressure).
 125    !           Hence if only P_* (Z_*) is present, only these are computed.
 126    !           If the first variable P_* (Z_*) is not present, nothing is done.
 127    !
 128    implicit none
 129
 130    ! Arguments:
 131    type(struct_gsv), intent(inout) :: statevector  ! statevector that will contain the Z_*/P_* fields
 132
 133    ! Locals:
 134    integer                   :: Vcode
 135
 136    call msg('calcZandP_gsv_nl (czp)', 'START', verb_opt=2)
 137
 138    Vcode = gsv_getVco(statevector)%vcode
 139
 140    if (Vcode == 5002 .or. Vcode == 5005 .or. Vcode == 5100) then
 141      ! if P_T, P_M not allocated : do nothing
 142      if (gsv_varExist(statevector, 'P_*')) then
 143        call calcPressure_gsv_nl(statevector)
 144        if (gsv_varExist(statevector, 'Z_*')) then
 145          call calcHeight_gsv_nl(statevector)
 146        end if
 147      end if
 148    else if (Vcode == 21001) then
 149      ! if Z_T, Z_M not allocated : do nothing
 150      if (gsv_varExist(statevector, 'Z_*')) then
 151          call calcHeight_gsv_nl(statevector)
 152        if (gsv_varExist(statevector, 'P_*')) then
 153          call calcPressure_gsv_nl(statevector)
 154        end if
 155      end if
 156    end if
 157
 158    call msg('calcZandP_gsv_nl (czp)', 'END', verb_opt=2)
 159  end subroutine calcZandP_gsv_nl
 160
 161  !---------------------------------------------------------
 162  ! calcZandP_gsv_tl
 163  !---------------------------------------------------------
 164  subroutine calcZandP_gsv_tl(statevector, statevectorRef)
 165    !
 166    !:Purpose: Pressure and height increment computation on the grid in proper
 167    !           order depending on the vgrid kind.
 168    !           Depending on the vcode, the routine will check the existence of
 169    !           P_* (vcode=5xxx) or Z_* (vcode=2100x) first and proceed with
 170    !           pressure (height) computation.  Then, if the other variables are
 171    !           also present, it will secondly compute height (pressure).
 172    !           Hence if only P_* (Z_*) is present, only these are computed.
 173    !           If the first variable P_* (Z_*) is not present, nothing is done.
 174    !
 175    implicit none
 176
 177    ! Arguments:
 178    type(struct_gsv), intent(inout) :: statevector      ! statevector that will contain the Z_*/P_* increments
 179    type(struct_gsv), intent(in)    :: statevectorRef   ! statevector containing needed reference fields
 180
 181    ! Locals:
 182    type(struct_vco), pointer :: vco
 183    integer                   :: Vcode
 184
 185    call msg('calcZandP_gsv_tl (czp)', 'START', verb_opt=2)
 186
 187    vco => gsv_getVco(statevector)
 188    Vcode = vco%vcode
 189
 190    if (Vcode == 0) return
 191
 192    if (Vcode == 5002 .or. Vcode == 5005) then
 193      ! if P_T, P_M not allocated : do nothing
 194      if (gsv_varExist(statevector, 'P_*')) then
 195
 196        if ( .not. gsv_containsNonZeroValues(stateVectorRef) ) then
 197          call utl_abort('calcZandP_gsv_tl: stateVectorRef not initialized')
 198        end if
 199        call calcPressure_gsv_tl(statevector, statevectorRef)
 200
 201        if (gsv_varExist(statevector, 'Z_*')) then
 202          call calcHeight_gsv_tl(statevector, statevectorRef)
 203        end if
 204
 205      end if
 206    else if (Vcode == 21001) then
 207      ! if Z_T, Z_M not allocated : do nothing
 208      if (gsv_varExist(statevector, 'Z_*')) then
 209
 210        if ( .not. gsv_containsNonZeroValues(stateVectorRef) ) then
 211          call utl_abort('calcZandP_gsv_tl: stateVectorRef not initialized')
 212        end if
 213        call calcHeight_gsv_tl(statevector, statevectorRef)
 214
 215        if (gsv_varExist(statevector, 'P_*')) then
 216          call calcPressure_gsv_tl(statevector, statevectorRef)
 217        end if
 218
 219      end if
 220    else
 221      call utl_abort('calcZandP_gsv_tl (czp): not implemented')
 222    end if
 223
 224    call msg('calcZandP_gsv_tl (czp)', 'END', verb_opt=2)
 225  end subroutine calcZandP_gsv_tl
 226
 227  !---------------------------------------------------------
 228  ! calcZandP_gsv_ad
 229  !---------------------------------------------------------
 230  subroutine calcZandP_gsv_ad(statevector, statevectorRef)
 231    !
 232    !:Purpose: Pressure and height increment adjoint computation on the grid
 233    !           in proper order depending on the vgrid kind
 234    !           Depending on the vcode, the routine will check the existence of
 235    !           Z_* (vcode=5xxx) or P_* (vcode=2100x) first and proceed with
 236    !           height (pressure) adjoint computation.  Then, if the other
 237    !           variables are also present, it will secondly proceed with
 238    !           adjoint computation of pressure (height).
 239    !           Hence if only Z_* (P_*) is present, only these are computed.
 240    !           If the first variable Z_* (P_*) is not present, nothing is done.
 241    !
 242    implicit none
 243
 244    ! Arguments:
 245    type(struct_gsv), intent(inout) :: statevector      ! statevector that will contain the Z_*/P_* increments
 246    type(struct_gsv), intent(in)    :: statevectorRef   ! statevector containing needed reference fields
 247
 248    ! Locals:
 249    type(struct_vco), pointer :: vco
 250    integer                   :: Vcode
 251
 252    call msg('calcZandP_gsv_ad (czp)', 'START', verb_opt=2)
 253
 254    vco => gsv_getVco(statevector)
 255    Vcode = vco%vcode
 256
 257    if (Vcode == 0) return
 258
 259    if (Vcode == 5002 .or. Vcode == 5005) then
 260      ! if Z_T, Z_M not allocated : do nothing
 261      if (gsv_varExist(statevector, 'Z_*')) then
 262
 263        if ( .not. gsv_containsNonZeroValues(stateVectorRef) ) then
 264          call utl_abort('calcZandP_gsv_ad: stateVectorRef not initialized')
 265        end if
 266        call calcHeight_gsv_ad(statevector, statevectorRef)
 267
 268        if (gsv_varExist(statevector, 'P_*')) then
 269          call calcPressure_gsv_ad(statevector, statevectorRef)
 270        end if
 271
 272      end if
 273    else if (Vcode == 21001) then
 274      ! if P_T, P_M not allocated : do nothing
 275      if (gsv_varExist(statevector, 'P_*')) then
 276
 277        if ( .not. gsv_containsNonZeroValues(stateVectorRef) ) then
 278          call utl_abort('calcZandP_gsv_ad: stateVectorRef not initialized')
 279        end if
 280        call calcPressure_gsv_ad(statevector, statevectorRef)
 281
 282        if (gsv_varExist(statevector, 'Z_*')) then
 283          call calcHeight_gsv_ad(statevector, statevectorRef)
 284        end if
 285
 286      end if
 287    else
 288      call utl_abort('calcZandP_gsv_ad (czp): not implemented')
 289    end if
 290
 291    call msg('calcZandP_gsv_ad (czp)', 'END', verb_opt=2)
 292  end subroutine calcZandP_gsv_ad
 293
 294  !---------------------------------------------------------
 295  ! calcHeight_gsv_nl
 296  !---------------------------------------------------------
 297  subroutine calcHeight_gsv_nl(statevector)
 298    !
 299    !:Purpose: Compute or retrieve heights and store values in statevector.
 300    !
 301    implicit none
 302
 303    ! Arguments:
 304    type(struct_gsv), intent(inout) :: statevector
 305
 306    ! Locals:
 307    integer :: Vcode
 308    real(4), pointer :: ptr_PT_r4(:,:,:,:), ptr_PM_r4(:,:,:,:)
 309    real(8), pointer :: ptr_PT_r8(:,:,:,:), ptr_PM_r8(:,:,:,:)
 310    real(4), pointer :: ptr_ZT_r4(:,:,:,:), ptr_ZM_r4(:,:,:,:)
 311    real(8), pointer :: ptr_ZT_r8(:,:,:,:), ptr_ZM_r8(:,:,:,:)
 312
 313    call utl_tmg_start(172,'low-level--czp_calcHeight_nl')
 314    call msg('calcHeight_gsv_nl (czp)', 'START', verb_opt=2)
 315
 316    Vcode = gsv_getVco(statevector)%vcode
 317    if (Vcode == 5005 .or. Vcode == 5002 .or. Vcode == 5100) then
 318      if ( gsv_getDataKind(statevector) == 4 ) then
 319        call gsv_getField(statevector, ptr_PT_r4, 'P_T')
 320        call gsv_getField(statevector, ptr_PM_r4, 'P_M')
 321        call gsv_getField(statevector, ptr_ZT_r4, 'Z_T')
 322        call gsv_getField(statevector, ptr_ZM_r4, 'Z_M')
 323        call calcHeight_gsv_nl_vcode5xxx( statevector, &
 324                                          PTin_r4_opt=ptr_PT_r4, &
 325                                          PMin_r4_opt=ptr_PM_r4, &
 326                                          ZTout_r4_opt=ptr_ZT_r4, &
 327                                          ZMout_r4_opt=ptr_ZM_r4)
 328      else
 329        call gsv_getField(statevector, ptr_PT_r8, 'P_T')
 330        call gsv_getField(statevector, ptr_PM_r8, 'P_M')
 331        call gsv_getField(statevector, ptr_ZT_r8, 'Z_T')
 332        call gsv_getField(statevector, ptr_ZM_r8, 'Z_M')
 333        call calcHeight_gsv_nl_vcode5xxx( statevector, &
 334                                          PTin_r8_opt=ptr_PT_r8, &
 335                                          PMin_r8_opt=ptr_PM_r8, &
 336                                          ZTout_r8_opt=ptr_ZT_r8, &
 337                                          ZMout_r8_opt=ptr_ZM_r8)
 338      end if
 339
 340    else if (Vcode == 21001) then
 341      ! Development notes (@mad001) 
 342      !   probably some some gsv_varExist(statevector,.) needed for GEM-H
 343      if ( gsv_getDataKind(statevector) == 4 ) then
 344        call gsv_getField(statevector, ptr_ZT_r4, 'Z_T')
 345        call gsv_getField(statevector, ptr_ZM_r4, 'Z_M')
 346        call calcHeight_gsv_nl_vcode2100x_r4(statevector, ptr_ZT_r4, ptr_ZM_r4)
 347      else
 348        call gsv_getField(statevector, ptr_ZT_r8, 'Z_T')
 349        call gsv_getField(statevector, ptr_ZM_r8, 'Z_M')
 350        call calcHeight_gsv_nl_vcode2100x_r8(statevector, ptr_ZT_r8, ptr_ZM_r8)
 351      end if
 352    end if
 353
 354    if ( gsv_getDataKind(statevector) == 4 ) then
 355      call msg('calcHeight_gsv_nl (czp)', &
 356             new_line('')//'Z_M = '&
 357           //str(ptr_ZM_r4(statevector%myLonBeg,statevector%myLatBeg,:,1), vertical_opt=.false.) &
 358           //new_line('')//'Z_T = '&
 359           //str(ptr_ZT_r4( statevector%myLonBeg,statevector%myLatBeg,:,1), vertical_opt=.false.), &
 360           verb_opt=2)
 361    else
 362      call msg('calcHeight_gsv_nl (czp)', &
 363             new_line('')//'Z_M = '&
 364           //str(ptr_ZM_r8(statevector%myLonBeg,statevector%myLatBeg,:,1), vertical_opt=.false.) &
 365           //new_line('')//'Z_T = '&
 366           //str(ptr_ZT_r8( statevector%myLonBeg,statevector%myLatBeg,:,1), vertical_opt=.false.), &
 367           verb_opt=2)
 368    end if
 369
 370    call msg('calcHeight_gsv_nl (czp)', 'END', verb_opt=2)
 371    call utl_tmg_stop(172)
 372  end subroutine calcHeight_gsv_nl
 373  
 374  !---------------------------------------------------------
 375  ! czp_calcReturnHeight_gsv_nl
 376  !---------------------------------------------------------
 377  subroutine czp_calcReturnHeight_gsv_nl( statevector, &
 378                                          PTin_r4_opt, PMin_r4_opt, &
 379                                          PTin_r8_opt, PMin_r8_opt, &
 380                                          ZTout_r4_opt, ZMout_r4_opt, &
 381                                          ZTout_r8_opt, ZMout_r8_opt)
 382    !
 383    !:Purpose: Compute or retrieve heights and return values in pointer arguments.
 384    !           Proceeds to vcode dispatching.
 385    !
 386    implicit none
 387
 388    ! Arguments:
 389    type(struct_gsv),           intent(in)    :: statevector
 390    real(4), optional, pointer, intent(in)    :: PTin_r4_opt(:,:,:,:)
 391    real(4), optional, pointer, intent(in)    :: PMin_r4_opt(:,:,:,:)
 392    real(8), optional, pointer, intent(in)    :: PTin_r8_opt(:,:,:,:)
 393    real(8), optional, pointer, intent(in)    :: PMin_r8_opt(:,:,:,:)
 394    real(4), optional, pointer, intent(inout) :: ZTout_r4_opt(:,:,:,:)
 395    real(4), optional, pointer, intent(inout) :: ZMout_r4_opt(:,:,:,:)
 396    real(8), optional, pointer, intent(inout) :: ZTout_r8_opt(:,:,:,:)
 397    real(8), optional, pointer, intent(inout) :: ZMout_r8_opt(:,:,:,:)
 398
 399    ! Locals:
 400    integer :: Vcode
 401
 402    call utl_tmg_start(172,'low-level--czp_calcHeight_nl')
 403    call msg('czp_calcReturnHeight_gsv_nl', 'START', verb_opt=2)
 404
 405    Vcode = gsv_getVco(statevector)%vcode
 406    if (Vcode == 5005 .or. Vcode == 5002 .or. Vcode == 5100) then
 407      if ( gsv_getDataKind(statevector) == 4 ) then
 408        if ( .not. (present(PTin_r4_opt) .and. present(PMin_r4_opt))) then
 409          call utl_abort('czp_calcReturnHeight_gsv_nl: dataKind=4: P{T,M}out_r4_opt expected')
 410        end if
 411        if ( .not. (present(ZTout_r4_opt) .and. present(ZMout_r4_opt))) then
 412          call utl_abort('czp_calcReturnHeight_gsv_nl: dataKind=4: Z{T,M}out_r4_opt expected')
 413        end if
 414        call calcHeight_gsv_nl_vcode5xxx( statevector, &
 415                                          PTin_r4_opt=PTin_r4_opt, & 
 416                                          PMin_r4_opt=PMin_r4_opt, & 
 417                                          ZTout_r4_opt=ZTout_r4_opt, &
 418                                          ZMout_r4_opt=ZMout_r4_opt)
 419      else ! datakind = 8
 420        if ( .not. (present(PTin_r8_opt) .and. present(PMin_r8_opt))) then
 421          call utl_abort('czp_calcReturnHeight_gsv_nl: dataKind=8: P{T,M}out_r8_opt expected')
 422        end if
 423        if ( .not. (present(ZTout_r8_opt) .and. present(ZMout_r8_opt))) then
 424          call utl_abort('czp_calcReturnHeight_gsv_nl: dataKind=8: Z{T,M}out_r8_opt expected')
 425        end if
 426        call calcHeight_gsv_nl_vcode5xxx( statevector, &
 427                                          PTin_r8_opt=PTin_r8_opt, &
 428                                          PMin_r8_opt=PMin_r8_opt, &
 429                                          ZTout_r8_opt=ZTout_r8_opt, &
 430                                          ZMout_r8_opt=ZMout_r8_opt)
 431      end if
 432
 433    else if (Vcode == 21001) then
 434      if ( gsv_getDataKind(statevector) == 4 ) then
 435        if ( .not. (present(ZTout_r4_opt) .and. present(ZMout_r4_opt))) then
 436          call utl_abort('czp_calcReturnHeight_gsv_nl: dataKind=4: Z{T,M}_r4 expected')
 437        end if
 438        call calcHeight_gsv_nl_vcode2100x_r4(statevector, ZTout_r4_opt, ZMout_r4_opt)
 439      else
 440        if ( .not. (present(ZTout_r8_opt) .and. present(ZMout_r8_opt))) then
 441          call utl_abort('czp_calcReturnHeight_gsv_nl: dataKind=4: Z{T,M}_r4 expected')
 442        end if
 443        call calcHeight_gsv_nl_vcode2100x_r8(statevector, ZTout_r8_opt, ZMout_r8_opt)
 444      end if
 445    end if
 446
 447    call msg('czp_calcReturnHeight_gsv_nl', 'END', verb_opt=2)
 448    call utl_tmg_stop(172) 
 449  end subroutine czp_calcReturnHeight_gsv_nl
 450
 451  !---------------------------------------------------------
 452  ! calcHeight_gsv_nl_vcode2100x_r4
 453  !---------------------------------------------------------
 454  subroutine calcHeight_gsv_nl_vcode2100x_r4(statevector, Z_T, Z_M)
 455    !
 456    !:Purpose: Retrieve heights for GEM-H statevector, return height values 
 457    !           in pointer arguments.
 458    !           real(4) version
 459    !
 460    implicit none
 461
 462    ! Arguments:
 463    type(struct_gsv),  intent(in)    :: statevector
 464    real(4), pointer,  intent(inout) :: Z_T(:,:,:,:)
 465    real(4), pointer,  intent(inout) :: Z_M(:,:,:,:)
 466
 467    ! Locals:
 468    integer ::  numStep, stepIndex
 469    real(kind=8), pointer       :: Hsfc(:,:)
 470    real(kind=4), allocatable   :: Hsfc4(:,:)
 471    real(kind=4), pointer       :: GZHeightM_out(:,:,:), GZHeightT_out(:,:,:)
 472
 473    call msg('calcHeight_gsv_nl_vcode2100x_r4 (czp)', 'START', verb_opt=4)
 474
 475    if ( .not. gsv_varExist(statevector,'Z_*')) then
 476      call utl_abort('calcHeight_gsv_nl_vcode2100x_r4 (czp): Z_T/Z_M do not exist in statevector!')
 477    end if
 478
 479    allocate(Hsfc4( statevector%myLonBeg:statevector%myLonEnd, &
 480                    statevector%myLatBeg:statevector%myLatEnd))
 481    Hsfc => gsv_getHeightSfc(statevector)
 482    Hsfc4 = real(Hsfc,4)
 483
 484    numStep = statevector%numStep
 485
 486    do stepIndex = 1, numStep
 487
 488      call fetch3DLevels_r4(statevector%vco, Hsfc4, &
 489                            fldM_opt=GZHeightM_out, fldT_opt=GZHeightT_out)
 490      Z_M(:,:,:,stepIndex) = gz2alt_r4(statevector, GZHeightM_out)
 491      Z_T(:,:,:,stepIndex) = gz2alt_r4(statevector, GZHeightT_out)
 492      deallocate(GZHeightM_out, GZHeightT_out)
 493
 494    end do
 495    deallocate(Hsfc4)
 496
 497    call msg('calcHeight_gsv_nl_vcode2100x_r4 (czp)', 'END', verb_opt=4)
 498  end subroutine calcHeight_gsv_nl_vcode2100x_r4
 499
 500  !---------------------------------------------------------
 501  ! gz2alt_r4
 502  !---------------------------------------------------------
 503  function gz2alt_r4(statevector, gzHeight) result(alt)
 504    !
 505    !:Purpose: Iterative conversion of geopotential height to geometric
 506    !           altitude.  (solution proposed by J. Aparicio)
 507    !           real(4) version.
 508    !
 509    implicit none
 510
 511    ! Arguments:
 512    type(struct_gsv),      intent(in)   :: statevector
 513    real(kind=4), pointer, intent(in)   :: gzHeight(:,:,:)
 514    ! Result:
 515    real(kind=4), allocatable           :: alt(:,:,:)
 516
 517    ! Locals:
 518    integer                             :: nLon, nLat, nLev
 519    type(struct_hco), pointer           :: hco
 520    real(kind=4)                        :: latitude
 521    real(kind=4)                        :: gzH, b1, b2, A2, A3
 522    integer                             :: lonIndex, latIndex, lvlIndex
 523
 524    ! gzHeight comes from external `vgd_levels` which does not know the
 525    ! mpi shifted indexes
 526    nLon = ubound(gzHeight, 1)
 527    nLat = ubound(gzHeight, 2)
 528    nLev = ubound(gzHeight, 3)
 529    allocate(alt(nLon, nLat, nLev))
 530
 531    hco => gsv_getHco(statevector)
 532
 533    do lonIndex = 1, nLon
 534      do latIndex = 1, nLat
 535        do lvlIndex = 1, nLev
 536          ! explicit shift of indexes
 537          latitude = hco%lat2d_4( lonIndex+statevector%myLonBeg-1,&
 538                                  latIndex+statevector%myLatBeg-1)
 539          gzH = gzHeight(lonIndex, latIndex, lvlIndex)
 540          ! gzH(alt) = g0 * (1 + b1*alt + b2*alt**2)
 541          b1 = -2.0/ec_wgs_a*(1.0+ec_wgs_f+ec_wgs_m-2*ec_wgs_f*latitude**2)
 542          b2 = 3.0/ec_wgs_a**2
 543          ! reversed series coefficients (Abramowitz and Stegun 3.6.25)
 544          A2 = -b1/2.0
 545          A3 = b1**2/2.0 - b2/3.0
 546          alt(lonIndex, latIndex, lvlIndex) = gzH + A2*gzH**2 + A3*gzH**3
 547        end do
 548      end do
 549    end do
 550
 551  end function gz2alt_r4
 552
 553  !---------------------------------------------------------
 554  ! calcHeight_gsv_nl_vcode2100x_r8
 555  !---------------------------------------------------------
 556  subroutine calcHeight_gsv_nl_vcode2100x_r8(statevector, Z_T, Z_M)
 557    !
 558    !:Purpose: Retrieve heights for GEM-H statevector, return height values 
 559    !           in pointer arguments.
 560    !           real(8) version
 561    !
 562    implicit none
 563
 564    ! Arguments:
 565    type(struct_gsv),  intent(in)    :: statevector
 566    real(8), pointer,  intent(inout) :: Z_T(:,:,:,:)
 567    real(8), pointer,  intent(inout) :: Z_M(:,:,:,:)
 568
 569    ! Locals:
 570    integer ::  numStep, stepIndex
 571    real(kind=8), pointer   :: Hsfc(:,:), GZHeightM_out(:,:,:), GZHeightT_out(:,:,:)
 572
 573    call msg('calcHeight_gsv_nl_vcode2100x_r8 (czp)', 'START', verb_opt=4)
 574
 575    Hsfc => gsv_getHeightSfc(statevector)
 576    numStep = statevector%numStep
 577
 578    do stepIndex = 1, numStep
 579
 580      call fetch3DLevels_r8(statevector%vco, Hsfc, &
 581                            fldM_opt=GZHeightM_out, fldT_opt=GZHeightT_out)
 582      Z_M(:,:,:,stepIndex) = gz2alt_r8(statevector, GZHeightM_out)
 583      Z_T(:,:,:,stepIndex) = gz2alt_r8(statevector, GZHeightT_out)
 584      deallocate(GZHeightM_out, GZHeightT_out)
 585    end do
 586
 587    call msg('calcHeight_gsv_nl_vcode2100x_r8 (czp)', 'END', verb_opt=4)
 588  end subroutine calcHeight_gsv_nl_vcode2100x_r8
 589
 590  !---------------------------------------------------------
 591  ! gz2alt_r8
 592  !---------------------------------------------------------
 593  function gz2alt_r8(statevector, gzHeight) result(alt)
 594    !
 595    !:Purpose: Iterative conversion of geopotential height to geometric
 596    !           altitude.  (solution proposed by J. Aparicio)
 597    !           real(8) version.
 598    !
 599    implicit none
 600
 601    ! Arguments:
 602    type(struct_gsv),      intent(in)   :: statevector
 603    real(kind=8), pointer, intent(in)   :: gzHeight(:,:,:)
 604    ! Result:
 605    real(kind=8), allocatable           :: alt(:,:,:)
 606
 607    ! Locals:
 608    integer                             :: nLon, nLat, nLev
 609    type(struct_hco), pointer           :: hco
 610    real(kind=8)                        :: latitude
 611    real(kind=8)                        :: gzH, b1, b2, A2, A3
 612    integer                             :: lonIndex, latIndex, lvlIndex
 613
 614    ! gzHeight comes from external `vgd_levels` which does not know the
 615    ! mpi shifted indexes
 616    nLon = ubound(gzHeight, 1)
 617    nLat = ubound(gzHeight, 2)
 618    nLev = ubound(gzHeight, 3)
 619    allocate(alt(nLon, nLat, nLev))
 620
 621    hco => gsv_getHco(statevector)
 622
 623    do lonIndex = 1, nLon
 624      do latIndex = 1, nLat
 625        do lvlIndex = 1, nLev
 626          ! explicit shift of indexes
 627          latitude = hco%lat2d_4( lonIndex+statevector%myLonBeg-1,&
 628                                  latIndex+statevector%myLatBeg-1)
 629          gzH = gzHeight(lonIndex, latIndex, lvlIndex)
 630          ! gzH(alt) = g0 * (1 + b1*alt + b2*alt**2)
 631          b1 = -2.0D0/ec_wgs_a*(1.0D0+ec_wgs_f+ec_wgs_m-2*ec_wgs_f*latitude**2)
 632          b2 = 3.0D0/ec_wgs_a**2
 633          ! reversed series coefficients (Abramowitz and Stegun 3.6.25)
 634          A2 = -b1/2.0D0
 635          A3 = b1**2/2.0D0 - b2/3.0D0
 636          alt(lonIndex, latIndex, lvlIndex) = gzH + A2*gzH**2 + A3*gzH**3
 637        end do
 638      end do
 639    end do
 640
 641  end function gz2alt_r8
 642
 643  !---------------------------------------------------------
 644  ! calcHeight_gsv_nl_vcode5xxx
 645  !---------------------------------------------------------
 646  subroutine calcHeight_gsv_nl_vcode5xxx( statevector, &
 647                                          PTin_r4_opt, PMin_r4_opt, &
 648                                          PTin_r8_opt, PMin_r8_opt, &
 649                                          ZTout_r4_opt, ZMout_r4_opt, &
 650                                          ZTout_r8_opt, ZMout_r8_opt)
 651    !
 652    !:Purpose: Compute heights for GEM-P statevector, return height values 
 653    !           in pointer arguments.
 654    !           Assumptions:
 655    !           1) nlev_T = nlev_M+1 (for vcode=5002)
 656    !           2) alt_T(nlev_T) = alt_M(nlev_M), both at the surface
 657    !           3) a thermo level exists at the top, higher than the highest
 658    !              momentum level
 659    !           4) the placement of the thermo levels means that alt_T is the
 660    !              average of 2 nearest alt_M (according to Ron and Claude)
 661    !
 662    implicit none
 663
 664    ! Arguments:
 665    type(struct_gsv),           intent(in)    :: statevector
 666    real(4), pointer, optional, intent(in)    :: PTin_r4_opt(:,:,:,:)
 667    real(4), pointer, optional, intent(in)    :: PMin_r4_opt(:,:,:,:)
 668    real(8), pointer, optional, intent(in)    :: PTin_r8_opt(:,:,:,:)
 669    real(8), pointer, optional, intent(in)    :: PMin_r8_opt(:,:,:,:)
 670    real(4), pointer, optional, intent(inout) :: ZTout_r4_opt(:,:,:,:)
 671    real(4), pointer, optional, intent(inout) :: ZMout_r4_opt(:,:,:,:)
 672    real(8), pointer, optional, intent(inout) :: ZTout_r8_opt(:,:,:,:)
 673    real(8), pointer, optional, intent(inout) :: ZMout_r8_opt(:,:,:,:)
 674
 675    ! Locals:
 676    integer ::  lev_M,lev_T,nlev_M,nlev_T,status,Vcode
 677    integer ::  numStep, stepIndex, latIndex,lonIndex
 678    real(4) ::  lat_4, heightSfcOffset_T_r4, heightSfcOffset_M_r4
 679    real(8) ::  delThick, ratioP
 680    real(8) ::  ScaleFactorBottom, ScaleFactorTop
 681    real(8) ::  P_M, P_M1, P_Mm1, P_T
 682    real(8) ::  hu, tt, Pr, cmp, h0, Rgh, P0, dh, rMt
 683    real(8) ::  sLat, cLat, lat_8
 684    real(8), allocatable :: tv(:), height_T(:), height_M(:)
 685    real(4), pointer     :: height_T_ptr_r4(:,:,:,:)
 686    real(4), pointer     :: height_M_ptr_r4(:,:,:,:)
 687    real(4), pointer     :: hu_ptr_r4(:,:,:,:),tt_ptr_r4(:,:,:,:)
 688    real(4), pointer     :: P_T_ptr_r4(:,:,:,:),P_M_ptr_r4(:,:,:,:)
 689    real(4), pointer     :: P0_ptr_r4(:,:,:,:)
 690    real(8), pointer     :: height_T_ptr_r8(:,:,:,:)
 691    real(8), pointer     :: height_M_ptr_r8(:,:,:,:)
 692    real(8), pointer     :: P_T_ptr_r8(:,:,:,:),P_M_ptr_r8(:,:,:,:)
 693    real(8), pointer     :: P0_ptr_r8(:,:,:,:)
 694    real(8), pointer     :: hu_ptr_r8(:,:,:,:),tt_ptr_r8(:,:,:,:)
 695    real(8), pointer     :: HeightSfc_ptr_r8(:,:)
 696
 697    call msg('calcHeight_gsv_nl_vcode5xxx (czp)', 'START', verb_opt=4)
 698
 699    nlev_T = gsv_getNumLev(statevector,'TH')
 700    nlev_M = gsv_getNumLev(statevector,'MM')
 701    Vcode = gsv_getVco(statevector)%vcode
 702    numStep = statevector%numStep
 703
 704    allocate(tv(nlev_T))
 705
 706    if (Vcode == 5002 .and. nlev_T /= nlev_M+1) then
 707      call utl_abort('calcHeight_gsv_nl_vcode5xxx (czp): nlev_T is not equal to nlev_M+1!')
 708    end if
 709    if ((Vcode == 5005 .or. Vcode == 5100) .and. nlev_T /= nlev_M) then
 710      call utl_abort('calcHeight_gsv_nl_vcode5xxx (czp): nlev_T is not equal to nlev_M!')
 711    end if
 712
 713    if (Vcode == 5005 .or. Vcode == 5100) then
 714      status = vgd_get( statevector%vco%vgrid, &
 715                        key='DHM - height of the diagnostic level (m)', &
 716                        value=heightSfcOffset_M_r4)
 717      status = vgd_get( statevector%vco%vgrid, &
 718                        key='DHT - height of the diagnostic level (t)', &
 719                        value=heightSfcOffset_T_r4)
 720      call msg('calcHeight_gsv_nl_vcode5xxx (czp)', &
 721           'height offset for near-sfc momentum level is:'//str(heightSfcOffset_M_r4)//' meters'&
 722           //new_line('')//'height offset for near-sfc thermo level is:'//str(heightSfcOffset_T_r4)//' meters', &
 723           verb_opt=2, mpiAll_opt=.false.)
 724      if ( .not.statevector%addHeightSfcOffset ) then
 725        call msg('calcHeight_gsv_nl_vcode5xxx (czp)', new_line('') &
 726             //'--------------------------------------------------------------------------'//new_line('')&
 727             //'BUT HEIGHT OFFSET REMOVED FOR DIAGNOSTIC LEVELS FOR BACKWARD COMPATIBILITY'//new_line('')&
 728             //'--------------------------------------------------------------------------', &
 729             verb_opt=2, mpiAll_opt=.false.)
 730      end if
 731    end if
 732
 733    allocate(height_T(nlev_T))
 734    allocate(height_M(nlev_M))
 735
 736    if ( statevector%dataKind == 4 ) then
 737      if ( .not. (present(PTin_r4_opt) .and. present(PMin_r4_opt))) then
 738        call utl_abort('calcHeight_gsv_nl_vcode5xxx (czp): dataKind=4: P{T,M}in_r4_opt expected')
 739      end if
 740      P_T_ptr_r4 => PTin_r4_opt
 741      P_M_ptr_r4 => PMin_r4_opt
 742
 743      if ( .not. (present(ZTout_r4_opt) .and. present(ZMout_r4_opt))) then
 744        call utl_abort('calcHeight_gsv_nl_vcode5xxx (czp): dataKind=4: Z{T,M}out_r4_opt expected')
 745      end if
 746      height_M_ptr_r4 => ZMout_r4_opt 
 747      height_T_ptr_r4 => ZTout_r4_opt
 748
 749      ! initialize the height pointer to zero
 750      height_M_ptr_r4(:,:,:,:) = 0.0
 751      height_T_ptr_r4(:,:,:,:) = 0.0
 752
 753      call gsv_getField(statevector,hu_ptr_r4,'HU')
 754      call gsv_getField(statevector,tt_ptr_r4,'TT')
 755      call gsv_getField(statevector,P0_ptr_r4,'P0')
 756
 757    else ! datakind = 8
 758      if ( .not. (present(PTin_r8_opt) .and. present(PMin_r8_opt))) then
 759        call utl_abort('calcHeight_gsv_nl_vcode5xxx (czp): dataKind=8: P{T,M}in_r8_opt expected')
 760      end if
 761      P_T_ptr_r8 => PTin_r8_opt
 762      P_M_ptr_r8 => PMin_r8_opt
 763
 764      if ( .not. (present(ZTout_r8_opt) .and. present(ZMout_r8_opt))) then
 765        call utl_abort('calcHeight_gsv_nl_vcode5xxx (czp): dataKind=8: Z{T,M}out_r8_opt expected')
 766      end if
 767      height_M_ptr_r8 => ZMout_r8_opt 
 768      height_T_ptr_r8 => ZTout_r8_opt
 769
 770      ! initialize the height pointer to zero
 771      height_M_ptr_r8(:,:,:,:) = 0.0d0
 772      height_T_ptr_r8(:,:,:,:) = 0.0d0
 773
 774      call gsv_getField(statevector,hu_ptr_r8,'HU')
 775      call gsv_getField(statevector,tt_ptr_r8,'TT')
 776      call gsv_getField(statevector,P0_ptr_r8,'P0')
 777    end if
 778
 779    HeightSfc_ptr_r8 => gsv_getHeightSfc(statevector)
 780
 781    ! compute virtual temperature on thermo levels (corrected of compressibility)
 782    do_computeHeight_gsv_nl : do stepIndex = 1, numStep
 783      !$OMP PARALLEL DO PRIVATE(latIndex, lonIndex, height_T, height_M, lat_4, lat_8, sLat, cLat, lev_T, &
 784      !$OMP                     hu, tt, Pr, cmp, tv, rMT, P_T, P_M, P0, ratioP, h0, Rgh, dh, delThick, lev_M, P_M1, &
 785      !$OMP                     scaleFactorBottom, scaleFactorTop, P_Mm1)
 786      do latIndex = statevector%myLatBeg, statevector%myLatEnd
 787        do lonIndex = statevector%myLonBeg, statevector%myLonEnd
 788
 789          height_T(:) = 0.0D0
 790          height_M(:) = 0.0D0
 791
 792          ! latitude
 793          lat_4 = statevector%hco%lat2d_4(lonIndex,latIndex)
 794          lat_8 = real(lat_4,8)
 795          sLat = sin(lat_8)
 796          cLat = cos(lat_8)
 797
 798          do lev_T = 1, nlev_T
 799            if ( statevector%dataKind == 4 ) then
 800              hu = real(hu_ptr_r4(lonIndex,latIndex,lev_T,stepIndex),8)
 801              tt = real(tt_ptr_r4(lonIndex,latIndex,lev_T,stepIndex),8)
 802              Pr = real(P_T_ptr_r4(lonIndex,latIndex,lev_T,stepIndex),8)
 803            else
 804              hu = hu_ptr_r8(lonIndex,latIndex,lev_T,stepIndex)
 805              tt = tt_ptr_r8(lonIndex,latIndex,lev_T,stepIndex)
 806              Pr = P_T_ptr_r8(lonIndex,latIndex,lev_T,stepIndex)
 807            end if
 808            cmp = gpscompressibility(Pr,tt,hu)
 809
 810            tv(lev_T) = phf_fotvt8(tt,hu) * cmp
 811          end do
 812
 813          rMT = HeightSfc_ptr_r8(lonIndex,latIndex)
 814
 815          ! compute altitude on bottom momentum level
 816          if (Vcode == 5002) then
 817            height_M(nlev_M) = rMT
 818          else if (Vcode == 5005 .or. Vcode == 5100) then
 819            height_M(nlev_M) = rMT + heightSfcOffset_M_r4
 820          end if
 821
 822          ! compute altitude on 2nd momentum level
 823          if (nlev_M > 1) then
 824            if ( statevector%dataKind == 4 ) then
 825              P_M = real(P_M_ptr_r4(lonIndex,latIndex,nlev_M-1,stepIndex), 8)
 826              P0  = real(P0_ptr_r4(lonIndex,latIndex,1,stepIndex), 8)
 827            else
 828              P_M = P_M_ptr_r8(lonIndex,latIndex,nlev_M-1,stepIndex)
 829              P0  = P0_ptr_r8(lonIndex,latIndex,1,stepIndex)
 830            end if
 831
 832            ratioP  = log( P_M / P0 )
 833
 834            ! Gravity acceleration
 835            h0  = rMT
 836            Rgh = phf_gravityalt(sLat,h0)
 837            dh  = (-MPC_RGAS_DRY_AIR_R8 / Rgh) * tv(nlev_T-1) * ratioP
 838            Rgh = phf_gravityalt(sLat, h0+0.5D0*dh)
 839
 840            delThick = (-MPC_RGAS_DRY_AIR_R8 / Rgh) * tv(nlev_T-1) * ratioP
 841            height_M(nlev_M-1) = rMT + delThick
 842          end if
 843
 844          ! compute altitude on rest of momentum levels
 845          do lev_M = nlev_M-2, 1, -1
 846            if ( statevector%dataKind == 4 ) then
 847              P_M = real(P_M_ptr_r4(lonIndex,latIndex,lev_M,stepIndex),8)
 848              P_M1 = real(P_M_ptr_r4(lonIndex,latIndex,lev_M+1,stepIndex),8)
 849            else
 850              P_M = P_M_ptr_r8(lonIndex,latIndex,lev_M,stepIndex)
 851              P_M1 = P_M_ptr_r8(lonIndex,latIndex,lev_M+1,stepIndex)
 852            end if
 853
 854            ratioP  = log( P_M / P_M1 )
 855
 856            if (Vcode == 5002) then
 857              lev_T = lev_M + 1
 858            else if (Vcode == 5005 .or. Vcode == 5100) then
 859              lev_T = lev_M
 860            end if
 861
 862            ! Gravity acceleration
 863            h0  = height_M(lev_M+1)
 864            Rgh = phf_gravityalt(sLat,h0)
 865            dh  = (-MPC_RGAS_DRY_AIR_R8 / Rgh) * tv(lev_T) * ratioP
 866            Rgh = phf_gravityalt(sLat, h0+0.5D0*dh)
 867
 868            delThick   = (-MPC_RGAS_DRY_AIR_R8 / Rgh) * tv(lev_T) * ratioP
 869            height_M(lev_M) = height_M(lev_M+1) + delThick
 870          end do
 871
 872          ! compute Altitude on thermo levels
 873          if_computeHeight_gsv_nl_vcodes : if (Vcode == 5002) then
 874            height_T(nlev_T) = height_M(nlev_M)
 875
 876            do lev_T = 2, nlev_T-1
 877              lev_M = lev_T ! momentum level just below thermo level being computed
 878
 879              if ( statevector%dataKind == 4 ) then
 880                P_T   = real(P_T_ptr_r4(&
 881                              lonIndex,latIndex,lev_T,stepIndex), 8)
 882                P_M   = real(P_M_ptr_r4(&
 883                              lonIndex,latIndex,lev_M,stepIndex), 8)
 884                P_Mm1 = real(P_M_ptr_r4(&
 885                              lonIndex,latIndex,lev_M-1,stepIndex), 8)
 886              else
 887                P_T   = P_T_ptr_r8(lonIndex,latIndex,lev_T  ,stepIndex)
 888                P_M   = P_M_ptr_r8(lonIndex,latIndex,lev_M  ,stepIndex)
 889                P_Mm1 = P_M_ptr_r8(lonIndex,latIndex,lev_M-1,stepIndex)
 890              end if
 891
 892              ScaleFactorBottom = log( P_T / P_Mm1 ) / log( P_M / P_Mm1 )
 893              ScaleFactorTop    = 1 - ScaleFactorBottom
 894              height_T(lev_T) = ScaleFactorBottom * height_M(lev_M) &
 895                + ScaleFactorTop * height_M(lev_M-1)
 896            end do
 897
 898            ! compute altitude on top thermo level
 899            if ( statevector%dataKind == 4 ) then
 900              P_T = real(P_T_ptr_r4(lonIndex,latIndex,1,stepIndex),8)
 901              P_M = real(P_M_ptr_r4(lonIndex,latIndex,1,stepIndex),8)
 902            else
 903              P_T = P_T_ptr_r8(lonIndex,latIndex,1,stepIndex)
 904              P_M = P_M_ptr_r8(lonIndex,latIndex,1,stepIndex)
 905            end if
 906
 907            ratioP = log( P_T / P_M )
 908
 909            ! Gravity acceleration
 910            h0  = height_M(1)
 911            Rgh = phf_gravityalt(sLat, h0)
 912            dh  = (-MPC_RGAS_DRY_AIR_R8 / Rgh) * tv(1) * ratioP
 913            Rgh = phf_gravityalt(sLat, h0+0.5D0*dh)
 914
 915            delThick   = (-MPC_RGAS_DRY_AIR_R8 / Rgh) * tv(1) * ratioP
 916            height_T(1) = height_M(1) + delThick
 917
 918          else if (Vcode == 5005 .or. Vcode == 5100) then if_computeHeight_gsv_nl_vcodes
 919            height_T(nlev_T) = rMT + heightSfcOffset_T_r4
 920
 921            do lev_T = 1, nlev_T-2
 922              lev_M = lev_T + 1  ! momentum level just below thermo level being computed
 923              if ( statevector%dataKind == 4 ) then
 924                P_T   = real(P_T_ptr_r4(&
 925                              lonIndex,latIndex,lev_T,stepIndex), 8)
 926                P_M   = real(P_M_ptr_r4(&
 927                              lonIndex,latIndex,lev_M,stepIndex), 8)
 928                P_Mm1 = real(P_M_ptr_r4(&
 929                              lonIndex,latIndex,lev_M-1,stepIndex), 8)
 930              else
 931                P_T   = P_T_ptr_r8(lonIndex,latIndex,lev_T  ,stepIndex)
 932                P_M   = P_M_ptr_r8(lonIndex,latIndex,lev_M  ,stepIndex)
 933                P_Mm1 = P_M_ptr_r8(lonIndex,latIndex,lev_M-1,stepIndex)
 934              end if
 935
 936              ScaleFactorBottom = log( P_T / P_Mm1 ) / log( P_M / P_Mm1 )
 937              ScaleFactorTop    = 1 - ScaleFactorBottom
 938              height_T(lev_T) = ScaleFactorBottom * height_M(lev_M) &
 939                + ScaleFactorTop * height_M(lev_M-1)
 940            end do
 941
 942            ! compute altitude on next to bottom thermo level
 943            if (nlev_T > 1) then
 944              if ( statevector%dataKind == 4 ) then
 945                P_T = real(P_T_ptr_r4(&
 946                            lonIndex,latIndex,nlev_T-1,stepIndex), 8)
 947                P0  = real(P0_ptr_r4(&
 948                            lonIndex,latIndex,1,stepIndex), 8)
 949              else
 950                P_T = P_T_ptr_r8(lonIndex,latIndex,nlev_T-1,stepIndex)
 951                P0  = P0_ptr_r8(lonIndex,latIndex,1,stepIndex)
 952              end if
 953
 954              ratioP = log( P_T / P0 )
 955
 956              h0  = rMT
 957              Rgh = phf_gravityalt(sLat,h0)
 958              dh  = (-MPC_RGAS_DRY_AIR_R8 / Rgh) * tv(nlev_T-1) * ratioP
 959              Rgh = phf_gravityalt(sLat, h0+0.5D0*dh)
 960
 961              delThick =  (-MPC_RGAS_DRY_AIR_R8 / Rgh) * tv(nlev_T-1) * &
 962                          ratioP
 963              height_T(nlev_T-1) = rMT + delThick
 964            end if
 965          end if if_computeHeight_gsv_nl_vcodes
 966
 967          ! fill the height array
 968          if ( statevector%dataKind == 4 ) then
 969            do lev_T = 1, nlev_T
 970              height_T_ptr_r4(lonIndex,latIndex,lev_T,stepIndex) &
 971                  = real(height_T(lev_T),4)
 972            end do
 973            do lev_M = 1, nlev_M
 974              height_M_ptr_r4(lonIndex,latIndex,lev_M,stepIndex) &
 975                  = real(height_M(lev_M),4)
 976            end do
 977          else
 978            height_T_ptr_r8(lonIndex,latIndex,1:nlev_T,stepIndex) &
 979                = height_T(1:nlev_T)
 980            height_M_ptr_r8(lonIndex,latIndex,1:nlev_M,stepIndex) &
 981                = height_M(1:nlev_M)
 982          end if
 983
 984          ! remove the height offset for the diagnostic levels for backward compatibility only
 985          if ( .not. statevector%addHeightSfcOffset ) then
 986            if ( statevector%dataKind == 4 ) then
 987              height_T_ptr_r4(lonIndex,latIndex,nlev_T,stepIndex) = &
 988                  real(rMT,4)
 989              height_M_ptr_r4(lonIndex,latIndex,nlev_M,stepIndex) = &
 990                  real(rMT,4)
 991            else
 992              height_T_ptr_r8(lonIndex,latIndex,nlev_T,stepIndex) = rMT
 993              height_M_ptr_r8(lonIndex,latIndex,nlev_M,stepIndex) = rMT
 994            end if
 995          end if
 996
 997        end do
 998      end do
 999      !$OMP end parallel do
1000    end do do_computeHeight_gsv_nl
1001
1002    deallocate(height_M)
1003    deallocate(height_T)
1004    deallocate(tv)
1005
1006    call msg('calcHeight_gsv_nl_vcode5xxx (czp)', 'statevector%addHeightSfcOffset='&
1007         //str(statevector%addHeightSfcOffset), verb_opt=2)
1008    call msg('calcHeight_gsv_nl_vcode5xxx (czp)', 'END', verb_opt=4)
1009  end subroutine calcHeight_gsv_nl_vcode5xxx
1010
1011  !---------------------------------------------------------
1012  ! calcHeight_gsv_tl
1013  !---------------------------------------------------------
1014  subroutine calcHeight_gsv_tl(statevector,statevectorRef)
1015    !
1016    !:Purpose: Tangent height computation on statevector.
1017    !
1018    implicit none
1019
1020    ! Arguments:
1021    type(struct_gsv), intent(inout) :: statevector
1022    type(struct_gsv), intent(in)    :: statevectorRef
1023
1024    ! Locals:
1025    integer :: Vcode
1026
1027    call utl_tmg_start(173,'low-level--czp_calcHeight_tl')
1028    call msg('calcHeight_gsv_tl (czp)', 'START', verb_opt=2)
1029
1030    Vcode = gsv_getVco(statevectorRef)%vcode
1031    if (Vcode == 5005 .or. Vcode == 5002) then
1032      if ( .not. gsv_varExist(statevector,'P_*')  ) then
1033        call utl_abort('calcHeight_gsv_tl (czp): for vcode 5xxx, variables P_T and P_M must be allocated in gridstatevector')
1034      end if
1035      if ( .not. gsv_varExist(statevector,'Z_*')  ) then
1036        call utl_abort('calcHeight_gsv_tl (czp): for vcode 5xxx, variables Z_T and Z_M must be allocated in gridstatevector')
1037      end if
1038      if ( .not. gsv_varExist(statevector,'TT')  ) then
1039        call utl_abort('calcHeight_gsv_tl (czp): for vcode 5xxx, variable TT must be allocated in gridstatevector')
1040      end if
1041      if ( .not. gsv_varExist(statevector,'HU')  ) then
1042        call utl_abort('calcHeight_gsv_tl (czp): for vcode 5xxx, variable HU must be allocated in gridstatevector')
1043      end if
1044      if ( .not. gsv_varExist(statevector,'P0')  ) then
1045        call utl_abort('calcHeight_gsv_tl (czp): for vcode 5xxx, variable P0 must be allocated in gridstatevector')
1046      end if
1047      call calcHeight_gsv_tl_vcode5xxx
1048    else if (Vcode == 21001) then
1049      ! Development notes (@mad001)
1050      !   probably some some gsv_varExist(statevector,.) needed for GEM-H
1051      call calcHeight_gsv_tl_vcode2100x
1052    else
1053      call utl_abort('calcHeight_gsv_tl (czp): not implemented')
1054    end if
1055
1056    call msg('calcHeight_gsv_tl (czp)', 'END', verb_opt=2)
1057    call utl_tmg_stop(173)
1058
1059    contains
1060      !---------------------------------------------------------
1061      ! calcHeight_gsv_tl_vcode2100x
1062      !---------------------------------------------------------
1063      subroutine calcHeight_gsv_tl_vcode2100x
1064        implicit none
1065
1066        call utl_abort('calcHeight_gsv_tl (czp): vcode 21001 not implemented yet')
1067
1068      end subroutine calcHeight_gsv_tl_vcode2100x
1069
1070      !---------------------------------------------------------
1071      ! calcHeight_gsv_tl_vcode5xxx
1072      !---------------------------------------------------------
1073      subroutine calcHeight_gsv_tl_vcode5xxx
1074        implicit none
1075
1076        ! Locals:
1077        integer ::  lev_M,lev_T,nlev_M,nlev_T,Vcode_anl
1078        integer ::  numStep,stepIndex, latIndex,lonIndex
1079        real(8) :: ScaleFactorBottom, ScaleFactorTop
1080        real(8), allocatable :: delThick(:,:,:,:)
1081        real(8), pointer     :: height_T_ptr(:,:,:,:),height_M_ptr(:,:,:,:)
1082        real(8), pointer     :: P_T(:,:,:,:), P_M(:,:,:,:)
1083        real(pre_incrReal), pointer ::  delHeight_M_ptr_r48(:,:,:,:)
1084        real(pre_incrReal), pointer ::  delHeight_T_ptr_r48(:,:,:,:)
1085        real(pre_incrReal), pointer ::  delTT_r48(:,:,:,:), delHU_r48(:,:,:,:)
1086        real(pre_incrReal), pointer ::  delP0_r48(:,:,:,:)
1087        real(pre_incrReal), pointer ::  delP_T_r48(:,:,:,:), delP_M_r48(:,:,:,:)
1088
1089        call msg('calcHeight_gsv_tl_vcode5xxx (czp)', 'START', verb_opt=4)
1090        Vcode_anl = gsv_getVco(statevectorRef)%vcode
1091
1092        nlev_T = gsv_getNumLev(statevectorRef,'TH')
1093        nlev_M = gsv_getNumLev(statevectorRef,'MM')
1094        numStep = statevectorRef%numstep
1095
1096        allocate(delThick(statevectorRef%myLonBeg:statevectorRef%myLonEnd, &
1097                          statevectorRef%myLatBeg:statevectorRef%myLatEnd, &
1098                          nlev_T,numStep))
1099
1100        ! generate the height coefficients on the grid
1101        call calcHeightCoeff_gsv(statevectorRef)
1102
1103        ! loop over all lat/lon/step
1104
1105        call gsv_getField(statevectorRef,height_M_ptr,'Z_M')
1106        call gsv_getField(statevectorRef,height_T_ptr,'Z_T')
1107        call gsv_getField(statevectorRef,P_T,'P_T')
1108        call gsv_getField(statevectorRef,P_M,'P_M')
1109        call gsv_getField(statevector,delHeight_M_ptr_r48,'Z_M')
1110        call gsv_getField(statevector,delHeight_T_ptr_r48,'Z_T')
1111        call gsv_getField(statevector,delTT_r48,'TT')
1112        call gsv_getField(statevector,delHU_r48,'HU')
1113        call gsv_getField(statevector,delP0_r48,'P0')
1114        call gsv_getField(statevector,delP_T_r48,'P_T')
1115        call gsv_getField(statevector,delP_M_r48,'P_M')
1116        ! ensure increment at sfc is zero (fixed height level)
1117        delHeight_M_ptr_r48(:,:,nlev_M,:) = 0.0d0
1118        delHeight_T_ptr_r48(:,:,nlev_T,:) = 0.0d0
1119
1120        if_computeHeight_gsv_tl_vcodes : if(Vcode_anl == 5002) then
1121
1122          ! compute increment to thickness for each layer between the two momentum levels
1123          do stepIndex = 1, numStep
1124            do lev_T = 2, (nlev_T-1)
1125              do latIndex = statevectorRef%myLatBeg, statevectorRef%myLatEnd
1126                do lonIndex = statevectorRef%myLonBeg, statevectorRef%myLonEnd
1127                  delThick(lonIndex,latIndex,lev_T,stepIndex) = &
1128                      coeff_M_TT_gsv(lonIndex,latIndex,lev_T,stepIndex) * &
1129                      delTT_r48(lonIndex,latIndex,lev_T,stepIndex) + &
1130                      coeff_M_HU_gsv(lonIndex,latIndex,lev_T,stepIndex) * &
1131                      delHU_r48(lonIndex,latIndex,lev_T,stepIndex) + &
1132                      coeff_M_P0_delPM_gsv(lonIndex,latIndex,lev_T,stepIndex) *&
1133                      ( delP_M_r48(lonIndex,latIndex,lev_T  ,stepIndex) / &
1134                        P_M(lonIndex,latIndex,lev_T  ,stepIndex) - &
1135                        delP_M_r48(lonIndex,latIndex,lev_T-1,stepIndex) / &
1136                        P_M(lonIndex,latIndex,lev_T-1,stepIndex) ) + &
1137                      coeff_M_P0_dP_delPT_gsv(&
1138                                lonIndex,latIndex,lev_T,stepIndex) * &
1139                      delP_T_r48(lonIndex,latIndex,lev_T,stepIndex) + &
1140                      coeff_M_P0_dP_delP0_gsv(&
1141                                lonIndex,latIndex,lev_T,stepIndex) * &
1142                      delP0_r48(lonIndex,latIndex,1,stepIndex)
1143                end do
1144              end do
1145            end do
1146          end do
1147
1148          ! compute height increment on momentum levels above the surface
1149          do stepIndex = 1, numStep
1150            do lev_M = (nlev_M-1), 1, -1
1151              do latIndex = statevectorRef%myLatBeg, statevectorRef%myLatEnd
1152                do lonIndex = statevectorRef%myLonBeg, statevectorRef%myLonEnd
1153                  lev_T = lev_M + 1 ! thermo level just below momentum level being computed
1154                  delHeight_M_ptr_r48(lonIndex,latIndex,lev_M,stepIndex) =  &
1155                       delHeight_M_ptr_r48(&
1156                                  lonIndex,latIndex,lev_M+1,stepIndex) + &
1157                       delThick(lonIndex,latIndex,lev_T,stepIndex)
1158                end do
1159              end do
1160            end do
1161          end do
1162
1163          ! compute height increment on thermo levels using weighted average of height increment of momentum levels
1164          do stepIndex = 1, numStep
1165            do lev_T = 1, (nlev_T-1)
1166              do latIndex = statevectorRef%myLatBeg, statevectorRef%myLatEnd
1167                do lonIndex = statevectorRef%myLonBeg, statevectorRef%myLonEnd
1168                  if ( lev_T == 1) then
1169                    ! compute height increment for top thermo level (from top momentum level)
1170                    delHeight_T_ptr_r48(lonIndex,latIndex,1,stepIndex) = &
1171                    delHeight_M_ptr_r48(lonIndex,latIndex,1,stepIndex) + &
1172                    coeff_T_TT_gsv(lonIndex,latIndex,stepIndex) * &
1173                    delTT_r48(lonIndex,latIndex,1,stepIndex) + &
1174                    coeff_T_HU_gsv(lonIndex,latIndex,stepIndex) * &
1175                    delHU_r48(lonIndex,latIndex,1,stepIndex) + &
1176                    coeff_T_P0_delP1_gsv(lonIndex,latIndex,stepIndex) * &
1177                    ( delP_M_r48(lonIndex,latIndex,1,stepIndex) / &
1178                      P_M(lonIndex,latIndex,1,stepIndex) - &
1179                      delP_T_r48(lonIndex,latIndex,1,stepIndex) / &
1180                      P_T(lonIndex,latIndex,1,stepIndex) ) + &
1181                    coeff_T_P0_dP_delPT_gsv(lonIndex,latIndex,stepIndex) * &
1182                    delP_T_r48(lonIndex,latIndex,1,stepIndex) + &
1183                    coeff_T_P0_dP_delP0_gsv(lonIndex,latIndex,stepIndex) * &
1184                    delP0_r48(lonIndex,latIndex,1,stepIndex)
1185                  else
1186                    lev_M = lev_T ! momentum level just below thermo level being computed
1187                    ScaleFactorBottom = &
1188                        (height_T_ptr(lonIndex,latIndex,lev_T,stepIndex) - &
1189                          height_M_ptr(lonIndex,latIndex,lev_M-1,stepIndex)) / &
1190                        (height_M_ptr(lonIndex,latIndex,lev_M,stepIndex) - &
1191                          height_M_ptr(lonIndex,latIndex,lev_M-1,stepIndex))
1192                    ScaleFactorTop    = 1 - ScaleFactorBottom
1193                    delHeight_T_ptr_r48(lonIndex,latIndex,lev_T,stepIndex) = &
1194                        ScaleFactorBottom * &
1195                        delHeight_M_ptr_r48(&
1196                                  lonIndex,latIndex,lev_M  ,stepIndex) + &
1197                        ScaleFactorTop * &
1198                        delHeight_M_ptr_r48(&
1199                                  lonIndex,latIndex,lev_M-1,stepIndex)
1200                  end if
1201                end do
1202              end do
1203            end do
1204          end do
1205
1206        else if(Vcode_anl == 5005) then if_computeHeight_gsv_tl_vcodes
1207
1208          ! compute increment to thickness for each layer between the two momentum levels
1209          do stepIndex = 1, numStep
1210            do lev_T = 1, (nlev_T-1)
1211              do latIndex = statevectorRef%myLatBeg, statevectorRef%myLatEnd
1212                do lonIndex = statevectorRef%myLonBeg, statevectorRef%myLonEnd
1213                  delThick(lonIndex,latIndex,lev_T,stepIndex) = &
1214                      coeff_M_TT_gsv(lonIndex,latIndex,lev_T,stepIndex) * &
1215                      delTT_r48(lonIndex,latIndex,lev_T,stepIndex) + &
1216                      coeff_M_HU_gsv(lonIndex,latIndex,lev_T,stepIndex) * &
1217                      delHU_r48(lonIndex,latIndex,lev_T,stepIndex) + &
1218                      coeff_M_P0_delPM_gsv(lonIndex,latIndex,lev_T,stepIndex) *&
1219                      ( delP_M_r48(lonIndex,latIndex,lev_T+1,stepIndex) / &
1220                        P_M(lonIndex,latIndex,lev_T+1,stepIndex) - &
1221                        delP_M_r48(lonIndex,latIndex,lev_T  ,stepIndex) / &
1222                        P_M(lonIndex,latIndex,lev_T  ,stepIndex) ) + &
1223                      coeff_M_P0_dP_delPT_gsv(&
1224                                      lonIndex,latIndex,lev_T,stepIndex) * &
1225                      delP_T_r48(lonIndex,latIndex,lev_T,stepIndex) + &
1226                      coeff_M_P0_dP_delP0_gsv(&
1227                                      lonIndex,latIndex,lev_T,stepIndex) * &
1228                      delP0_r48(lonIndex,latIndex,1,stepIndex)
1229                end do
1230              end do
1231            end do
1232          end do
1233
1234          ! compute height increment on momentum levels above the surface
1235          do stepIndex = 1, numStep
1236            do lev_M = (nlev_M-1), 1, -1
1237              do latIndex = statevectorRef%myLatBeg, statevectorRef%myLatEnd
1238                do lonIndex = statevectorRef%myLonBeg, statevectorRef%myLonEnd
1239                  lev_T = lev_M ! thermo level just below momentum level being computed
1240                  delHeight_M_ptr_r48(lonIndex,latIndex,lev_M,stepIndex) = &
1241                  delHeight_M_ptr_r48(lonIndex,latIndex,lev_M+1,stepIndex) + &
1242                  delThick(lonIndex,latIndex,lev_T,stepIndex)
1243                end do
1244              end do
1245            end do
1246          end do
1247
1248          ! compute height increment on thermo levels using weighted average of height increment of momentum levels
1249          do stepIndex = 1, numStep
1250            do lev_T = 1, (nlev_T-1)
1251              do latIndex = statevectorRef%myLatBeg, statevectorRef%myLatEnd
1252                do lonIndex = statevectorRef%myLonBeg, statevectorRef%myLonEnd
1253                  lev_M = lev_T + 1 ! momentum level just below thermo level being computed
1254                  ScaleFactorBottom = &
1255                      (height_T_ptr(lonIndex,latIndex,lev_T,stepIndex) - &
1256                        height_M_ptr(lonIndex,latIndex,lev_M-1,stepIndex)) / &
1257                      (height_M_ptr(lonIndex,latIndex,lev_M,stepIndex) - &
1258                        height_M_ptr(lonIndex,latIndex,lev_M-1,stepIndex))
1259                  ScaleFactorTop    = 1 - ScaleFactorBottom
1260                  delHeight_T_ptr_r48(lonIndex,latIndex,lev_T,stepIndex) = &
1261                      ScaleFactorBottom * &
1262                      delHeight_M_ptr_r48(lonIndex,latIndex,lev_M  ,stepIndex) + &
1263                      ScaleFactorTop * &
1264                      delHeight_M_ptr_r48(lonIndex,latIndex,lev_M-1,stepIndex)
1265                end do
1266              end do
1267            end do
1268          end do
1269
1270        else
1271
1272          call utl_abort('calcHeight_gsv_tl_vcode5xxx (czp): not implemented')
1273
1274        end if if_computeHeight_gsv_tl_vcodes
1275
1276        deallocate(delThick)
1277
1278        call msg('calcHeight_gsv_tl_vcode5xxx (czp)', 'END', verb_opt=4)
1279      end subroutine calcHeight_gsv_tl_vcode5xxx
1280
1281  end subroutine calcHeight_gsv_tl
1282
1283  !---------------------------------------------------------
1284  ! calcHeight_gsv_ad
1285  !---------------------------------------------------------
1286  subroutine calcHeight_gsv_ad(statevector,statevectorRef)
1287    !
1288    !:Purpose: Adjoint of height computation on statevector.
1289    !
1290    implicit none
1291
1292    ! Arguments:
1293    type(struct_gsv), intent(inout) :: statevector
1294    type(struct_gsv), intent(in)    :: statevectorRef
1295
1296    ! Locals:
1297    integer :: Vcode
1298
1299    call utl_tmg_start(174,'low-level--czp_calcHeight_ad')
1300    call msg('calcHeight_gsv_ad', 'START', verb_opt=2)
1301
1302    Vcode = gsv_getVco(statevectorRef)%vcode
1303    if (Vcode == 5005 .or. Vcode == 5002) then
1304      if ( .not. gsv_varExist(statevector,'P_*')  ) then
1305        call utl_abort('calcHeight_gsv_ad (czp): for vcode 5xxx, variables P_M and P_T must be allocated in gridstatevector')
1306      end if
1307      if ( .not. gsv_varExist(statevector,'Z_*')  ) then
1308        call utl_abort('calcHeight_gsv_ad (czp): for vcode 5xxx, variables Z_M and Z_T must be allocated in gridstatevector')
1309      end if
1310      if ( .not. gsv_varExist(statevector,'TT')  ) then
1311        call utl_abort('calcHeight_gsv_ad (czp): for vcode 5xxx, variable TT must be allocated in gridstatevector')
1312      end if
1313      if ( .not. gsv_varExist(statevector,'HU')  ) then
1314        call utl_abort('calcHeight_gsv_ad (czp): for vcode 5xxx, variable HU must be allocated in gridstatevector')
1315      end if
1316      if ( .not. gsv_varExist(statevector,'P0')  ) then
1317        call utl_abort('calcHeight_gsv_ad (czp): for vcode 5xxx, variable P0 must be allocated in gridstatevector')
1318      end if
1319      call calcHeight_gsv_ad_vcode5xxx
1320    else if (Vcode == 21001) then
1321      ! Development notes (@mad001)
1322      !   probably some some gsv_varExist(statevector,.) needed for GEM-H
1323      call calcHeight_gsv_ad_vcode2100x
1324    else
1325      call utl_abort('calcHeight_gsv_ad (czp): not implemented')
1326    end if
1327
1328    call msg('calcHeight_gsv_ad (czp)', 'END', verb_opt=2)
1329    call utl_tmg_stop(174)
1330
1331    contains
1332      !---------------------------------------------------------
1333      ! calcHeight_gsv_ad_vcode2100x
1334      !---------------------------------------------------------
1335      subroutine calcHeight_gsv_ad_vcode2100x
1336        implicit none
1337
1338        call utl_abort('calcHeight_gsv_ad (czp): vcode 21001 not implemented yet')
1339
1340      end subroutine calcHeight_gsv_ad_vcode2100x
1341
1342      !---------------------------------------------------------
1343      ! calcHeight_gsv_ad
1344      !---------------------------------------------------------
1345      subroutine calcHeight_gsv_ad_vcode5xxx
1346        implicit none
1347
1348        ! Locals:
1349        integer ::  lev_M,lev_T,nlev_M,nlev_T
1350        integer ::  numStep,stepIndex,latIndex,lonIndex
1351        real(8) ::  ScaleFactorBottom, ScaleFactorTop
1352        real(8), allocatable :: delThick(:,:,:,:)
1353        real(8), pointer     :: height_M_ptr(:,:,:,:),height_T_ptr(:,:,:,:)
1354        real(8), allocatable :: delHeight_M(:,:,:,:)
1355        real(8), pointer     :: P_M(:,:,:,:),P_T(:,:,:,:)
1356        real(pre_incrReal), pointer :: delHeight_M_ptr_r48(:,:,:,:)
1357        real(pre_incrReal), pointer :: delHeight_T_ptr_r48(:,:,:,:)
1358        real(pre_incrReal), pointer :: delTT_r48(:,:,:,:),delHU_r48(:,:,:,:)
1359        real(pre_incrReal), pointer :: delP0_r48(:,:,:,:)
1360        real(pre_incrReal), pointer :: delP_M_r48(:,:,:,:),delP_T_r48(:,:,:,:)
1361
1362        call msg('calcHeight_gsv_ad_vcode5xxx (czp)', 'START', verb_opt=4)
1363
1364        nlev_T = gsv_getNumLev(statevectorRef,'TH')
1365        nlev_M = gsv_getNumLev(statevectorRef,'MM')
1366        numStep = statevectorRef%numstep
1367
1368        allocate(delHeight_M(statevectorRef%myLonBeg:statevectorRef%myLonEnd, &
1369                             statevectorRef%myLatBeg:statevectorRef%myLatEnd, &
1370                             nlev_M,numStep))
1371        allocate(delThick(statevectorRef%myLonBeg:statevectorRef%myLonEnd, &
1372                          statevectorRef%myLatBeg:statevectorRef%myLatEnd, &
1373                          nlev_T,numStep))
1374
1375        ! generate the height coefficients on the grid
1376        call calcHeightCoeff_gsv(statevectorRef)
1377
1378        ! loop over all lat/lon/step
1379        call gsv_getField(statevectorRef,height_M_ptr,'Z_M')
1380        call gsv_getField(statevectorRef,height_T_ptr,'Z_T')
1381        call gsv_getField(statevectorRef,P_T,'P_T')
1382        call gsv_getField(statevectorRef,P_M,'P_M')
1383
1384        call gsv_getField(statevector,delHeight_M_ptr_r48,'Z_M')
1385        call gsv_getField(statevector,delHeight_T_ptr_r48,'Z_T')
1386        call gsv_getField(statevector,delTT_r48,'TT')
1387        call gsv_getField(statevector,delHU_r48,'HU')
1388        call gsv_getField(statevector,delP0_r48,'P0')
1389        call gsv_getField(statevector,delP_T_r48,'P_T')
1390        call gsv_getField(statevector,delP_M_r48,'P_M')
1391        delHeight_M(:,:,:,:) = delHeight_M_ptr_r48(:,:,:,:)
1392
1393        if_computeHeight_gsv_ad_vcodes : if(Vcode == 5002) then
1394
1395          ! adjoint of compute height increment on thermo levels by simple averaging
1396          do stepIndex = 1, numStep
1397            do lev_T = 1, (nlev_T-1)
1398              do latIndex = statevectorRef%myLatBeg, statevectorRef%myLatEnd
1399                do lonIndex = statevectorRef%myLonBeg, statevectorRef%myLonEnd
1400                  lev_M = lev_T ! momentum level just below thermo level being computed
1401
1402                  ! adjoint of compute height increment on top thermo level
1403                  ! (from top momentum level)
1404                  if (lev_T == 1) then
1405                    delHeight_M(lonIndex,latIndex,1,stepIndex)  =  &
1406                        delHeight_M(lonIndex,latIndex,1,stepIndex) + &
1407                        delHeight_T_ptr_r48(lonIndex,latIndex,1,stepIndex)
1408
1409                    delTT_r48(lonIndex,latIndex,1,stepIndex) =  &
1410                        delTT_r48(lonIndex,latIndex,1,stepIndex) + &
1411                        coeff_T_TT_gsv(lonIndex,latIndex,stepIndex) * &
1412                        delHeight_T_ptr_r48(lonIndex,latIndex,1,stepIndex)
1413
1414                    delHU_r48(lonIndex,latIndex,1,stepIndex) =  &
1415                        delHU_r48(lonIndex,latIndex,1,stepIndex) + &
1416                        coeff_T_HU_gsv   (lonIndex,latIndex,stepIndex) * &
1417                        delHeight_T_ptr_r48(lonIndex,latIndex,1,stepIndex)
1418
1419                    delP_M_r48(lonIndex,latIndex,1,stepIndex) =  &
1420                        delP_M_r48(lonIndex,latIndex,1,stepIndex) + &
1421                        coeff_T_P0_delP1_gsv(lonIndex,latIndex,stepIndex) / &
1422                        P_M(lonIndex,latIndex,1,stepIndex) * &
1423                        delHeight_T_ptr_r48(lonIndex,latIndex,1,stepIndex)
1424
1425                    delP_T_r48(lonIndex,latIndex,1,stepIndex) =  &
1426                        delP_T_r48(lonIndex,latIndex,1,stepIndex) - &
1427                        coeff_T_P0_delP1_gsv(lonIndex,latIndex,stepIndex) / &
1428                        P_T(lonIndex,latIndex,1,stepIndex) * &
1429                        delHeight_T_ptr_r48(lonIndex,latIndex,1,stepIndex)
1430
1431                    delP_T_r48(lonIndex,latIndex,1,stepIndex) =  &
1432                        delP_T_r48(lonIndex,latIndex,1,stepIndex) + &
1433                        coeff_T_P0_dP_delPT_gsv(lonIndex,latIndex,stepIndex) * &
1434                        delHeight_T_ptr_r48(lonIndex,latIndex,1,stepIndex)
1435
1436                    delP0_r48(lonIndex,latIndex,1,stepIndex) =  &
1437                        delP0_r48(lonIndex,latIndex,1,stepIndex) + &
1438                        coeff_T_P0_dp_delP0_gsv(lonIndex,latIndex,stepIndex) * &
1439                        delHeight_T_ptr_r48(lonIndex,latIndex,1,stepIndex)
1440                  else
1441                    ScaleFactorBottom =  &
1442                        (height_T_ptr(lonIndex,latIndex,lev_T,stepIndex) - &
1443                          height_M_ptr(lonIndex,latIndex,lev_M-1,stepIndex)) / &
1444                        (height_M_ptr(lonIndex,latIndex,lev_M,stepIndex) - &
1445                          height_M_ptr(lonIndex,latIndex,lev_M-1,stepIndex))
1446                    ScaleFactorTop    = 1 - ScaleFactorBottom
1447
1448                    delHeight_M(lonIndex,latIndex,lev_M-1,stepIndex) =  &
1449                        delHeight_M(lonIndex,latIndex,lev_M-1,stepIndex) + &
1450                        ScaleFactorTop * &
1451                        delHeight_T_ptr_r48(lonIndex,latIndex,lev_T,stepIndex)
1452
1453                    delHeight_M(lonIndex,latIndex,lev_M,stepIndex) =  &
1454                        delHeight_M(lonIndex,latIndex,lev_M  ,stepIndex) + &
1455                        ScaleFactorBottom * &
1456                        delHeight_T_ptr_r48(lonIndex,latIndex,lev_T,stepIndex)
1457                  end if
1458                end do
1459              end do
1460            end do
1461          end do
1462
1463          ! adjoint of compute height increment on momentum levels above the surface
1464          delThick(:,:,1,:) = 0.0d0
1465          do stepIndex = 1, numStep
1466            do lev_M = 1, (nlev_M-1)
1467              do latIndex = statevectorRef%myLatBeg, statevectorRef%myLatEnd
1468                do lonIndex = statevectorRef%myLonBeg, statevectorRef%myLonEnd
1469                  lev_T = lev_M + 1 ! thermo level just below momentum level being computed
1470                  delThick(lonIndex,latIndex,lev_T,stepIndex) =  &
1471                       delThick(lonIndex,latIndex,lev_T-1,stepIndex) + &
1472                       delHeight_M(lonIndex,latIndex,lev_M  ,stepIndex)
1473                end do
1474              end do
1475            end do
1476          end do
1477
1478          ! adjoint of compute increment to thickness for each layer between the two momentum levels
1479          do stepIndex = 1, numStep
1480            do lev_T = 2, nlev_T-1
1481              do latIndex = statevectorRef%myLatBeg, statevectorRef%myLatEnd
1482                do lonIndex = statevectorRef%myLonBeg, statevectorRef%myLonEnd
1483                  delTT_r48(lonIndex,latIndex,lev_T,stepIndex) =  &
1484                      delTT_r48(lonIndex,latIndex,lev_T,stepIndex) + &
1485                      coeff_M_TT_gsv(lonIndex,latIndex,lev_T,stepIndex) * &
1486                      delThick(lonIndex,latIndex,lev_T,stepIndex)
1487
1488                  delHU_r48(lonIndex,latIndex,lev_T,stepIndex) =  &
1489                      delHU_r48(lonIndex,latIndex,lev_T,stepIndex) + &
1490                      coeff_M_HU_gsv(lonIndex,latIndex,lev_T,stepIndex) * &
1491                      delThick(lonIndex,latIndex,lev_T,stepIndex)
1492
1493                  delP_M_r48(lonIndex,latIndex,lev_T,stepIndex)=  &
1494                      delP_M_r48(lonIndex,latIndex,lev_T,stepIndex) + &
1495                      coeff_M_P0_delPM_gsv(lonIndex,latIndex,lev_T,stepIndex) /&
1496                      P_M(lonIndex,latIndex,lev_T,stepIndex) * &
1497                      delThick(lonIndex,latIndex,lev_T,stepIndex)
1498
1499                  delP_M_r48(lonIndex,latIndex,lev_T-1,stepIndex) =  &
1500                      delP_M_r48(lonIndex,latIndex,lev_T-1,stepIndex) - &
1501                      coeff_M_P0_delPM_gsv(lonIndex,latIndex,lev_T,stepIndex) /&
1502                      P_M(lonIndex,latIndex,lev_T-1,stepIndex) * &
1503                      delThick(lonIndex,latIndex,lev_T,stepIndex)
1504
1505                  delP_T_r48(lonIndex,latIndex,lev_T,stepIndex) =  &
1506                      delP_T_r48(lonIndex,latIndex,lev_T,stepIndex) + &
1507                      coeff_M_P0_dP_delPT_gsv(&
1508                                    lonIndex,latIndex,lev_T,stepIndex) * &
1509                      delThick(lonIndex,latIndex,lev_T,stepIndex)
1510
1511                  delP0_r48(lonIndex,latIndex,1,stepIndex) =  &
1512                      delP0_r48(lonIndex,latIndex,1,stepIndex) + &
1513                      coeff_M_P0_dP_delP0_gsv(&
1514                                    lonIndex,latIndex,lev_T,stepIndex) * &
1515                      delThick(lonIndex,latIndex,lev_T,stepIndex)
1516                end do
1517              end do
1518            end do
1519          end do
1520
1521        else if(Vcode == 5005) then if_computeHeight_gsv_ad_vcodes
1522
1523          ! adjoint of compute height increment on thermo levels by simple averaging
1524          do stepIndex = 1, numStep
1525            do lev_T = 1, (nlev_T-1)
1526              do latIndex = statevectorRef%myLatBeg, statevectorRef%myLatEnd
1527                do lonIndex = statevectorRef%myLonBeg, statevectorRef%myLonEnd
1528                  lev_M = lev_T+1 ! momentum level just below thermo level being computed
1529                  ScaleFactorBottom = &
1530                      (height_T_ptr(lonIndex,latIndex,lev_T,stepIndex) - &
1531                        height_M_ptr(lonIndex,latIndex,lev_M-1,stepIndex)) / &
1532                      (height_M_ptr(lonIndex,latIndex,lev_M,stepIndex) -&
1533                        height_M_ptr(lonIndex,latIndex,lev_M-1,stepIndex))
1534                  ScaleFactorTop = 1 - ScaleFactorBottom
1535                  delHeight_M(lonIndex,latIndex,lev_M-1,stepIndex) = &
1536                      delHeight_M(lonIndex,latIndex,lev_M-1,stepIndex) + &
1537                      ScaleFactorTop * &
1538                      delHeight_T_ptr_r48(lonIndex,latIndex,lev_T,stepIndex)
1539                  delHeight_M(lonIndex,latIndex,lev_M,stepIndex) = &
1540                      delHeight_M(lonIndex,latIndex,lev_M  ,stepIndex) + &
1541                      ScaleFactorBottom * &
1542                      delHeight_T_ptr_r48(lonIndex,latIndex,lev_T,stepIndex)
1543                end do
1544              end do
1545            end do
1546          end do
1547
1548          ! adjoint of compute height increment on momentum levels
1549          do stepIndex = 1, numStep
1550            do lev_M = 1, (nlev_M-1)
1551              do latIndex = statevectorRef%myLatBeg, statevectorRef%myLatEnd
1552                do lonIndex = statevectorRef%myLonBeg, statevectorRef%myLonEnd
1553                  lev_T = lev_M ! thermo level just below momentum level being computed
1554                  if (lev_T == 1) then
1555                    delThick(lonIndex,latIndex,lev_T,stepIndex) = &
1556                        delHeight_M (lonIndex,latIndex,lev_M,stepIndex)
1557                  else
1558                    delThick(lonIndex,latIndex,lev_T,stepIndex) = &
1559                        delThick(lonIndex,latIndex,lev_T-1,stepIndex) + &
1560                        delHeight_M (lonIndex,latIndex,lev_M,stepIndex)
1561                  end if
1562                end do
1563              end do
1564            end do
1565          end do
1566
1567          do stepIndex = 1, numStep
1568            do lev_T = 1, nlev_T-1
1569              do latIndex = statevectorRef%myLatBeg, statevectorRef%myLatEnd
1570                do lonIndex = statevectorRef%myLonBeg, statevectorRef%myLonEnd
1571                  delTT_r48(lonIndex,latIndex,lev_T,stepIndex) =  &
1572                      delTT_r48(lonIndex,latIndex,lev_T,stepIndex) + &
1573                      coeff_M_TT_gsv(lonIndex,latIndex,lev_T,stepIndex) * &
1574                      delThick(lonIndex,latIndex,lev_T,stepIndex)
1575
1576                  delHU_r48(lonIndex,latIndex,lev_T,stepIndex) =  &
1577                      delHU_r48(lonIndex,latIndex,lev_T,stepIndex) + &
1578                      coeff_M_HU_gsv(lonIndex,latIndex,lev_T,stepIndex) * &
1579                      delThick(lonIndex,latIndex,lev_T,stepIndex)
1580
1581                  delP_M_r48(lonIndex,latIndex,lev_T+1,stepIndex) =  &
1582                      delP_M_r48(lonIndex,latIndex,lev_T+1,stepIndex) + &
1583                      coeff_M_P0_delPM_gsv(lonIndex,latIndex,lev_T,stepIndex) /&
1584                      P_M(lonIndex,latIndex,lev_T+1,stepIndex) * &
1585                      delThick(lonIndex,latIndex,lev_T,stepIndex)
1586
1587                  delP_M_r48(lonIndex,latIndex,lev_T  ,stepIndex) =  &
1588                      delP_M_r48(lonIndex,latIndex,lev_T  ,stepIndex) - &
1589                      coeff_M_P0_delPM_gsv(lonIndex,latIndex,lev_T,stepIndex) /&
1590                      P_M(lonIndex,latIndex,lev_T  ,stepIndex) * &
1591                      delThick(lonIndex,latIndex,lev_T,stepIndex)
1592
1593                  delP_T_r48(lonIndex,latIndex,lev_T  ,stepIndex) =  &
1594                      delP_T_r48(lonIndex,latIndex,lev_T  ,stepIndex) + &
1595                      coeff_M_P0_dP_delPT_gsv( lonIndex,latIndex,lev_T,stepIndex) * &
1596                      delThick(lonIndex,latIndex,lev_T,stepIndex)
1597
1598                  delP0_r48(lonIndex,latIndex,1,stepIndex)     =  &
1599                      delP0_r48(lonIndex,latIndex,1,stepIndex) + &
1600                      coeff_M_P0_dP_delP0_gsv(lonIndex,latIndex,lev_T,stepIndex) * &
1601                      delThick(lonIndex,latIndex,lev_T,stepIndex)
1602                end do
1603              end do
1604            end do
1605          end do
1606
1607        else
1608
1609          call utl_abort('calcHeight_gsv_ad_vcode5xxx (czp): not implemented')
1610
1611        end if if_computeHeight_gsv_ad_vcodes
1612
1613        deallocate(delThick)
1614        deallocate(delHeight_M)
1615
1616        call msg('calcHeight_gsv_ad_vcode5xxx (czp)', 'END', verb_opt=4)
1617      end subroutine calcHeight_gsv_ad_vcode5xxx
1618
1619  end subroutine calcHeight_gsv_ad
1620
1621  !---------------------------------------------------------
1622  ! calcPressure_gsv_nl
1623  !---------------------------------------------------------
1624  subroutine calcPressure_gsv_nl(statevector, Ps_in_hPa_opt)
1625    !
1626    !:Purpose: Pressure computation, values stored in statevector.
1627    !
1628    implicit none
1629
1630    ! Arguments:
1631    type(struct_gsv),  intent(inout) :: statevector
1632    logical, optional, intent(in)    :: Ps_in_hPa_opt  ! If true, conversion from hPa to mbar done for surface pressure
1633
1634    ! Locals:
1635    integer :: Vcode
1636    real(4), pointer :: ptr_ZT_r4(:,:,:,:), ptr_ZM_r4(:,:,:,:)
1637    real(8), pointer :: ptr_ZT_r8(:,:,:,:), ptr_ZM_r8(:,:,:,:)
1638    real(4), pointer :: ptr_PT_r4(:,:,:,:), ptr_PM_r4(:,:,:,:)
1639    real(8), pointer :: ptr_PT_r8(:,:,:,:), ptr_PM_r8(:,:,:,:)
1640
1641    call utl_tmg_start(177,'low-level--czp_calcPressure_nl')
1642    call msg('calcPressure_gsv_nl (czp)', 'START', verb_opt=2)
1643
1644    Vcode = gsv_getVco(statevector)%vcode
1645    if (Vcode == 5005 .or. Vcode == 5002 .or. Vcode == 5100) then
1646      if ( gsv_getDataKind(statevector) == 4 ) then
1647        call gsv_getField(statevector, ptr_PT_r4, 'P_T')
1648        call gsv_getField(statevector, ptr_PM_r4, 'P_M')
1649        call calcPressure_gsv_nl_vcode5xxx_r4(statevector, &
1650                                              ptr_PT_r4, ptr_PM_r4, &
1651                                              Ps_in_hPa_opt=Ps_in_hPa_opt)
1652      else
1653        call gsv_getField(statevector, ptr_PT_r8, 'P_T')
1654        call gsv_getField(statevector, ptr_PM_r8, 'P_M')
1655        call calcPressure_gsv_nl_vcode5xxx_r8(statevector, &
1656                                              ptr_PT_r8, ptr_PM_r8, &
1657                                              Ps_in_hPa_opt=Ps_in_hPa_opt)
1658      end if
1659    else if (Vcode == 21001) then
1660      ! Development notes (@mad001)
1661      !   probably some some gsv_varExist(statevector,.) needed for GEM-H
1662      if ( gsv_getDataKind(statevector) == 4 ) then
1663        call gsv_getField(statevector, ptr_ZT_r4, 'Z_T')
1664        call gsv_getField(statevector, ptr_ZM_r4, 'Z_M')
1665        call gsv_getField(statevector, ptr_PT_r4, 'P_T')
1666        call gsv_getField(statevector, ptr_PM_r4, 'P_M')
1667        call calcPressure_gsv_nl_vcode2100x(statevector, &
1668                                            ZTin_r4_opt=ptr_ZT_r4, &
1669                                            ZMin_r4_opt=ptr_ZM_r4, &
1670                                            PTout_r4_opt=ptr_PT_r4, &
1671                                            PMout_r4_opt=ptr_PM_r4)
1672      else
1673        call gsv_getField(statevector, ptr_ZT_r8, 'Z_T')
1674        call gsv_getField(statevector, ptr_ZM_r8, 'Z_M')
1675        call gsv_getField(statevector, ptr_PT_r8, 'P_T')
1676        call gsv_getField(statevector, ptr_PM_r8, 'P_M')
1677        call calcPressure_gsv_nl_vcode2100x(statevector, &
1678                                            ZTin_r8_opt=ptr_ZT_r8, &
1679                                            ZMin_r8_opt=ptr_ZM_r8, &
1680                                            PTout_r8_opt=ptr_PT_r8, &
1681                                            PMout_r8_opt=ptr_PM_r8)
1682      end if
1683    end if
1684
1685    if ( gsv_getDataKind(statevector) == 4 ) then
1686      call msg('calcPressure_gsv_nl (czp)', &
1687             new_line('')//'P_M = '&
1688           //str(ptr_PM_r4( statevector%myLonBeg,statevector%myLatBeg,:,1), vertical_opt=.false.) &
1689           //new_line('')//'P_T = '&
1690           //str(ptr_PT_r4( statevector%myLonBeg,statevector%myLatBeg,:,1), vertical_opt=.false.), &
1691           verb_opt=2)
1692    else
1693      call msg('calcPressure_gsv_nl (czp)', &
1694             new_line('')//'P_M = '&
1695           //str(ptr_PM_r8( statevector%myLonBeg,statevector%myLatBeg,:,1), vertical_opt=.false.) &
1696           //new_line('')//'P_T = '&
1697           //str(ptr_PT_r8( statevector%myLonBeg,statevector%myLatBeg,:,1), vertical_opt=.false.), &
1698           verb_opt=2)
1699    end if
1700
1701    call msg('calcPressure_gsv_nl (czp)', 'END', verb_opt=2)
1702    call utl_tmg_stop(177)
1703  end subroutine calcPressure_gsv_nl
1704
1705  !---------------------------------------------------------
1706  ! czp_calcReturnPressure_gsv_nl
1707  !---------------------------------------------------------
1708  subroutine czp_calcReturnPressure_gsv_nl( statevector, &
1709                                            ZTin_r4_opt, ZMin_r4_opt, &
1710                                            ZTin_r8_opt, ZMin_r8_opt, &
1711                                            PTout_r4_opt, PMout_r4_opt, & 
1712                                            PTout_r8_opt, PMout_r8_opt, &
1713                                            Ps_in_hPa_opt)
1714    !
1715    !:Purpose: Compute or retrieve pressures and return values in pointer arguments.
1716    !           Proceeds to vcode dispatching.
1717    !
1718    implicit none
1719
1720    ! Arguments:
1721    type(struct_gsv),           intent(in)    :: statevector
1722    real(4), optional, pointer, intent(in)    :: ZTin_r4_opt(:,:,:,:)
1723    real(4), optional, pointer, intent(in)    :: ZMin_r4_opt(:,:,:,:)
1724    real(8), optional, pointer, intent(in)    :: ZTin_r8_opt(:,:,:,:)
1725    real(8), optional, pointer, intent(in)    :: ZMin_r8_opt(:,:,:,:)
1726    real(4), optional, pointer, intent(inout) :: PTout_r4_opt(:,:,:,:)
1727    real(4), optional, pointer, intent(inout) :: PMout_r4_opt(:,:,:,:)
1728    real(8), optional, pointer, intent(inout) :: PTout_r8_opt(:,:,:,:)
1729    real(8), optional, pointer, intent(inout) :: PMout_r8_opt(:,:,:,:)
1730    logical, optional,          intent(in)    :: Ps_in_hPa_opt  ! If true, conversion from hPa to mbar done for surface pressure
1731
1732    ! Locals:
1733    integer :: Vcode
1734
1735    call utl_tmg_start(177,'low-level--czp_calcPressure_nl')
1736    call msg('czp_calcReturnPressure_gsv_nl', 'START', verb_opt=2)
1737
1738    Vcode = gsv_getVco(statevector)%vcode
1739    if (Vcode == 5005 .or. Vcode == 5002 .or. Vcode == 5100) then
1740      if ( gsv_getDataKind(statevector) == 4 ) then
1741        if ( .not. (present(PTout_r4_opt) .and. present(PMout_r4_opt))) then
1742          call utl_abort('czp_calcReturnPressure_gsv_nl: dataKind=4: P{T,M}out_r4_opt expected')
1743        end if
1744        call calcPressure_gsv_nl_vcode5xxx_r4(statevector, PTout_r4_opt, PMout_r4_opt, &
1745                                              Ps_in_hPa_opt)
1746      else
1747        if ( .not. (present(PTout_r8_opt) .and. present(PMout_r8_opt))) then
1748          call utl_abort('czp_calcReturnPressure_gsv_nl: dataKind=8: P{T,M}out_r8_opt expected')
1749        end if
1750        call calcPressure_gsv_nl_vcode5xxx_r8(statevector, PTout_r8_opt, PMout_r8_opt, &
1751                                              Ps_in_hPa_opt)
1752      end if
1753    else if (Vcode == 21001) then
1754      if ( gsv_getDataKind(statevector) == 4 ) then
1755        if ( .not. (present(ZTin_r4_opt) .and. present(ZMin_r4_opt))) then
1756          call utl_abort('czp_calcReturnPressure_gsv_nl: dataKind=4: Z{T,M}out_r4_opt expected')
1757        end if
1758        if ( .not. (present(PTout_r4_opt) .and. present(PMout_r4_opt))) then
1759          call utl_abort('czp_calcReturnPressure_gsv_nl: dataKind=4: P{T,M}out_r4_opt expected')
1760        end if
1761        call calcPressure_gsv_nl_vcode2100x(statevector, &
1762                                            ZTin_r4_opt=ZTin_r4_opt, &
1763                                            ZMin_r4_opt=ZMin_r4_opt, &
1764                                            PTout_r4_opt=PTout_r4_opt, &
1765                                            PMout_r4_opt=PMout_r4_opt)
1766      else ! datakind = 8
1767        if ( .not. (present(ZTin_r8_opt) .and. present(ZMin_r8_opt))) then
1768          call utl_abort('czp_calcReturnPressure_gsv_nl: dataKind=8: Z{T,M}out_r8_opt expected')
1769        end if
1770        if ( .not. (present(PTout_r8_opt) .and. present(PMout_r8_opt))) then
1771          call utl_abort('czp_calcReturnPressure_gsv_nl: dataKind=8: P{T,M}out_r8_opt expected')
1772        end if
1773        call calcPressure_gsv_nl_vcode2100x(statevector, &
1774                                            ZTin_r8_opt=ZTin_r8_opt, &
1775                                            ZMin_r8_opt=ZMin_r8_opt, &
1776                                            PTout_r8_opt=PTout_r8_opt, &
1777                                            PMout_r8_opt=PMout_r8_opt)
1778      end if
1779    end if
1780
1781    call msg('czp_calcReturnPressure_gsv_nl', 'END', verb_opt=2)
1782    call utl_tmg_stop(177)
1783  end subroutine czp_calcReturnPressure_gsv_nl
1784  
1785  !---------------------------------------------------------
1786  ! calcPressure_gsv_nl_vcode2100x
1787  !---------------------------------------------------------
1788  subroutine calcPressure_gsv_nl_vcode2100x(statevector, &
1789                                            ZTin_r4_opt, ZMin_r4_opt, &
1790                                            ZTin_r8_opt, ZMin_r8_opt, &
1791                                            PTout_r4_opt, PMout_r4_opt, &
1792                                            PTout_r8_opt, PMout_r8_opt)
1793    !
1794    !:Purpose: Compute pressure and return values in pointer arguments.
1795    !           GEM-H statevector input.
1796    !
1797    implicit none
1798
1799    ! Arguments:
1800    type(struct_gsv),           intent(in)    :: statevector
1801    real(4), pointer, optional, intent(in)    :: ZTin_r4_opt(:,:,:,:)
1802    real(4), pointer, optional, intent(in)    :: ZMin_r4_opt(:,:,:,:)
1803    real(8), pointer, optional, intent(in)    :: ZTin_r8_opt(:,:,:,:)
1804    real(8), pointer, optional, intent(in)    :: ZMin_r8_opt(:,:,:,:)
1805    real(4), pointer, optional, intent(inout) :: PTout_r4_opt(:,:,:,:)
1806    real(4), pointer, optional, intent(inout) :: PMout_r4_opt(:,:,:,:)
1807    real(8), pointer, optional, intent(inout) :: PTout_r8_opt(:,:,:,:)
1808    real(8), pointer, optional, intent(inout) :: PMout_r8_opt(:,:,:,:)
1809
1810    ! Locals:
1811    integer ::  stepIndex, latIndex, lonIndex, numStep
1812    integer ::  lev_M,lev_T,nlev_M,nlev_T,status
1813    real(4) ::  heightSfcOffset_T_r4, heightSfcOffset_M_r4
1814    real(4) ::  lat_4
1815    real(8) ::  hu, tt, cmp, Rgh, P0, dh, tv0, rMt, Z_T, Z_M, Z_M1, logP
1816    real(8) ::  sLat, cLat, lat_8
1817    real(8) ::  ScaleFactorBottom
1818    real(8), allocatable :: tv(:),  pressure_T(:), pressure_M(:)
1819    real(4), pointer     :: height_T_ptr_r4(:,:,:,:)
1820    real(4), pointer     :: height_M_ptr_r4(:,:,:,:)
1821    real(4), pointer     :: hu_ptr_r4(:,:,:,:),tt_ptr_r4(:,:,:,:)
1822    real(4), pointer     :: P_T_ptr_r4(:,:,:,:),P_M_ptr_r4(:,:,:,:)
1823    real(4), pointer     :: P0_ptr_r4(:,:,:,:)
1824    real(8), pointer     :: height_T_ptr_r8(:,:,:,:)
1825    real(8), pointer     :: height_M_ptr_r8(:,:,:,:)
1826    real(8), pointer     :: P_T_ptr_r8(:,:,:,:),P_M_ptr_r8(:,:,:,:)
1827    real(8), pointer     :: P0_ptr_r8(:,:,:,:)
1828    real(8), pointer     :: hu_ptr_r8(:,:,:,:),tt_ptr_r8(:,:,:,:)
1829    real(8), pointer     :: HeightSfc_ptr_r8(:,:)
1830
1831    call msg('calcPressure_gsv_nl_vcode2100x (czp)', 'START', verb_opt=4)
1832    
1833    nlev_T = gsv_getNumLev(statevector,'TH')
1834    nlev_M = gsv_getNumLev(statevector,'MM')
1835    numStep = statevector%numStep
1836
1837    allocate(tv(nlev_T))
1838
1839    if (nlev_T /= nlev_M) then
1840      call utl_abort('calcPressure_gsv_nl_vcode2100x: nlev_T is not equal to nlev_M!')
1841    end if
1842
1843    status = vgd_get( statevector%vco%vgrid, &
1844                      key='DHM - height of the diagnostic level (m)', &
1845                      value=heightSfcOffset_M_r4)
1846    status = vgd_get( statevector%vco%vgrid, &
1847                      key='DHT - height of the diagnostic level (t)', &
1848                      value=heightSfcOffset_T_r4)
1849    call msg('calcPressure_gsv_nl_vcode2100x (czp)', &
1850         'height offset for near-sfc momentum level is:'//str(heightSfcOffset_M_r4)//' meters'&
1851         //new_line('')//'height offset for near-sfc thermo level is:'//str(heightSfcOffset_T_r4)//' meters', &
1852         verb_opt=2, mpiAll_opt=.false.)
1853    if ( .not.statevector%addHeightSfcOffset ) then
1854      call msg('calcPressure_gsv_nl_vcode2100x (czp)', new_line('') &
1855             //'--------------------------------------------------------------------------'//new_line('')&
1856             //'BUT HEIGHT OFFSET REMOVED FOR DIAGNOSTIC LEVELS FOR BACKWARD COMPATIBILITY'//new_line('')&
1857             //'--------------------------------------------------------------------------', &
1858             verb_opt=2, mpiAll_opt=.false.)
1859    end if
1860
1861    allocate(pressure_T(nlev_T))
1862    allocate(pressure_M(nlev_M))
1863
1864    if ( statevector%dataKind == 4 ) then
1865      if ( .not. (present(ZTin_r4_opt) .and. present(ZMin_r4_opt))) then
1866        call utl_abort('calcPressure_gsv_nl_vcode2100x (czp): dataKind=4: Z{T,M}in_r4_opt expected')
1867      end if
1868      height_T_ptr_r4 => ZTin_r4_opt
1869      height_M_ptr_r4 => ZMin_r4_opt
1870
1871      if ( .not. (present(PTout_r4_opt) .and. present(PMout_r4_opt))) then
1872        call utl_abort('calcPressure_gsv_nl_vcode2100x (czp): dataKind=4: P{T,M}out_r4_opt expected')
1873      end if
1874      P_M_ptr_r4 => PMout_r4_opt
1875      P_T_ptr_r4 => PTout_r4_opt
1876      call gsv_getField(statevector,hu_ptr_r4,'HU')
1877      call gsv_getField(statevector,tt_ptr_r4,'TT')
1878      call gsv_getField(statevector,P0_ptr_r4,'P0')
1879
1880      ! initialize the pressure pointer to zero
1881      P_M_ptr_r4(:,:,:,:) = 0.0
1882      P_T_ptr_r4(:,:,:,:) = 0.0
1883    else ! datakind = 8
1884      if ( .not. (present(ZTin_r8_opt) .and. present(ZMin_r8_opt))) then
1885        call utl_abort('calcPressure_gsv_nl_vcode2100x (czp): dataKind=4: Z{T,M}in_r8_opt expected')
1886      end if
1887      height_T_ptr_r8 => ZTin_r8_opt
1888      height_M_ptr_r8 => ZMin_r8_opt
1889
1890      if ( .not. (present(PTout_r8_opt) .and. present(PMout_r8_opt))) then
1891        call utl_abort('calcPressure_gsv_nl_vcode2100x (czp): dataKind=8: P{T,M}_r8_opt expected')
1892      end if
1893      P_M_ptr_r8 => PMout_r8_opt
1894      P_T_ptr_r8 => PTout_r8_opt
1895      call gsv_getField(statevector,hu_ptr_r8,'HU')
1896      call gsv_getField(statevector,tt_ptr_r8,'TT')
1897      call gsv_getField(statevector,P0_ptr_r8,'P0')
1898
1899      ! initialize the pressure pointer to zero
1900      P_M_ptr_r8(:,:,:,:) = 0.0d0
1901      P_T_ptr_r8(:,:,:,:) = 0.0d0
1902    end if
1903    HeightSfc_ptr_r8 => gsv_getHeightSfc(statevector)
1904
1905    ! Development notes (@mad001)
1906    !   if feasible, consider reusing the same code for both 
1907    !   `calcPressure_{gsv,col}_nl_vcode2100x`
1908    do_computePressure_gsv_nl: do stepIndex = 1, numStep
1909      do latIndex = statevector%myLatBeg, statevector%myLatEnd
1910        do lonIndex = statevector%myLonBeg, statevector%myLonEnd
1911
1912          pressure_T(:) = 0.0D0
1913          pressure_M(:) = 0.0D0
1914
1915          ! latitude
1916          lat_4 = statevector%hco%lat2d_4(lonIndex,latIndex)
1917          lat_8 = real(lat_4,8)
1918          sLat = sin(lat_8)
1919          cLat = cos(lat_8)
1920
1921          if ( statevector%dataKind == 4 ) then
1922            P0 = real(P0_ptr_r4(lonIndex,latIndex,1, stepIndex),8)
1923          else
1924            P0 = P0_ptr_r8(lonIndex,latIndex,1, stepIndex)
1925          end if
1926
1927          rMT = HeightSfc_ptr_r8(lonIndex,latIndex)
1928
1929          ! compute pressure on diagnostic levels
1930          if ( statevector%dataKind == 4 ) then
1931            hu = real(hu_ptr_r4(lonIndex,latIndex,nlev_T,stepIndex),8)
1932            tt = real(tt_ptr_r4(lonIndex,latIndex,nlev_T,stepIndex),8)
1933          else
1934            hu = hu_ptr_r8(lonIndex,latIndex,nlev_T,stepIndex)
1935            tt = tt_ptr_r8(lonIndex,latIndex,nlev_T,stepIndex)
1936          end if
1937          tv0 = phf_fotvt8(tt,hu)
1938
1939          ! thermo diagnostic level
1940          if ( statevector%dataKind == 4 ) then
1941            Z_T = real(height_T_ptr_r4(lonIndex,latIndex,nlev_T,stepIndex),8)
1942          else
1943            Z_T = height_T_ptr_r8(lonIndex,latIndex,nlev_T,stepIndex)
1944          end if
1945          cmp = gpscompressibility(P0,tt,hu) 
1946          tv(nlev_T) = tv0*cmp
1947          dh = Z_T - rMT
1948          Rgh = phf_gravityalt(sLat, rMT+0.5D0*dh)
1949          pressure_T(nlev_T) = P0*exp(-Rgh*dh/MPC_RGAS_DRY_AIR_R8/tv(nlev_T))
1950
1951          ! momentum diagnostic level
1952          if ( statevector%dataKind == 4 ) then
1953            Z_M = real(height_M_ptr_r4(lonIndex,latIndex,nlev_M,stepIndex),8)
1954          else
1955            Z_M = height_M_ptr_r8(lonIndex,latIndex,nlev_M,stepIndex)
1956          end if
1957          dh = Z_M - rMT
1958          Rgh = phf_gravityalt(sLat, rMT+0.5D0*dh)
1959          pressure_M(nlev_M) = P0*exp(-Rgh*dh/MPC_RGAS_DRY_AIR_R8/tv(nlev_T))
1960
1961          ! compute pressure on all levels above except the last 
1962          do lev_M = nlev_M-1, 1, -1
1963            lev_T = lev_M ! thermo level just below
1964            if ( statevector%dataKind == 4 ) then
1965              hu = real(hu_ptr_r4(lonIndex,latIndex,lev_T,stepIndex),8)
1966              tt = real(tt_ptr_r4(lonIndex,latIndex,lev_T,stepIndex),8)
1967              Z_M = real(height_M_ptr_r4(lonIndex,latIndex,lev_M,stepIndex),8)
1968              Z_M1 = real(height_M_ptr_r4(lonIndex,latIndex,lev_M+1,stepIndex),8)
1969              Z_T = real(height_T_ptr_r4(lonIndex,latIndex,lev_T,stepIndex),8)
1970            else
1971              hu = hu_ptr_r8(lonIndex,latIndex,lev_T,stepIndex)
1972              tt = tt_ptr_r8(lonIndex,latIndex,lev_T,stepIndex)
1973              Z_M = height_M_ptr_r8(lonIndex,latIndex,lev_M,stepIndex)
1974              Z_M1 = height_M_ptr_r8(lonIndex,latIndex,lev_M+1,stepIndex)
1975              Z_T = height_T_ptr_r8(lonIndex,latIndex,lev_T,stepIndex)
1976            end if
1977            tv0 = phf_fotvt8(tt,hu)
1978            dh = Z_M - Z_M1
1979            Rgh = phf_gravityalt(sLat, Z_M1+0.5D0*dh)
1980
1981            ! approximation of tv from pressure on previous momentum level
1982            cmp = gpscompressibility(pressure_M(lev_M+1),tt,hu) 
1983            tv(lev_T) = tv0*cmp
1984            pressure_M(lev_M) = pressure_M(lev_M+1) * &
1985                                exp(-Rgh*dh/MPC_RGAS_DRY_AIR_R8/tv(lev_T))
1986            ! first interpolation of thermo pressure
1987            scaleFactorBottom = (Z_T-Z_M1)/(Z_M-Z_M1)
1988            logP = (1.0D0-scaleFactorBottom)*log(pressure_M(lev_M+1)) + &
1989                                  scaleFactorBottom*log(pressure_M(lev_M))
1990            pressure_T(lev_T) = exp(logP)
1991
1992            ! second iteration on tv
1993            cmp = gpscompressibility(pressure_T(lev_T),tt,hu)
1994            tv(lev_T) = tv0*cmp
1995            pressure_M(lev_M) = pressure_M(lev_M+1) * &
1996                                exp(-Rgh*dh/MPC_RGAS_DRY_AIR_R8/tv(lev_T))
1997
1998            ! second iteration interpolation of thermo pressure
1999            logP = (1.0D0-scaleFactorBottom)*log(pressure_M(lev_M+1)) + &
2000                                  scaleFactorBottom*log(pressure_M(lev_M))
2001            pressure_T(lev_T) = exp(logP)
2002
2003          end do
2004
2005          ! fill the height array
2006          if ( statevector%dataKind == 4 ) then
2007            do lev_T = 1, nlev_T
2008              P_T_ptr_r4(lonIndex,latIndex,lev_T,stepIndex) = &
2009                  real(pressure_T(lev_T),4)
2010            end do
2011            do lev_M = 1, nlev_M
2012            P_M_ptr_r4(lonIndex,latIndex,lev_M,stepIndex) = &
2013                  real(pressure_M(lev_M),4)
2014            end do
2015          else
2016            P_T_ptr_r8(lonIndex,latIndex,1:nlev_T,stepIndex)=pressure_T(1:nlev_T)
2017            P_M_ptr_r8(lonIndex,latIndex,1:nlev_M,stepIndex)=pressure_M(1:nlev_M)
2018          end if
2019
2020        end do ! lonIndex
2021      end do ! latIndex
2022    end do do_computePressure_gsv_nl
2023
2024    deallocate(pressure_T)
2025    deallocate(pressure_M)
2026    deallocate(tv)
2027
2028    call msg('calcPressure_gsv_nl_vcode2100x (czp)', 'END', verb_opt=4)
2029  end subroutine calcPressure_gsv_nl_vcode2100x
2030
2031  !---------------------------------------------------------
2032  ! calcPressure_gsv_nl_vcode5xxx_r8
2033  !---------------------------------------------------------
2034  subroutine calcPressure_gsv_nl_vcode5xxx_r8(statevector, P_T, P_M, Ps_in_hPa_opt)
2035    !
2036    !:Purpose: Pressure retrieval for GEM-P real(8) statevector, values
2037    !           values returned in pointers.
2038    !
2039    implicit none
2040
2041    ! Arguments:
2042    type(struct_gsv),           intent(in)    :: statevector
2043    real(8),           pointer, intent(inout) :: P_T(:,:,:,:)
2044    real(8),           pointer, intent(inout) :: P_M(:,:,:,:)
2045    logical, optional,          intent(in)    :: Ps_in_hPa_opt  ! If true, conversion from hPa to mbar done for surface pressure
2046
2047    ! Locals:
2048    real(kind=8), allocatable   :: Psfc(:,:), PsfcLS(:,:)
2049    real(kind=8), pointer       :: PressureM_out(:,:,:), PressureT_out(:,:,:)
2050    real(kind=8), pointer       :: field_Psfc(:,:,:,:), field_PsfcLS(:,:,:,:)
2051    integer                     :: stepIndex, numStep, Vcode
2052
2053    call msg('calcPressure_gsv_nl_vcode5xxx_r8 (czp)', 'START', verb_opt=4)
2054
2055    Vcode = gsv_getVco(statevector)%vcode
2056
2057    allocate(Psfc(statevector%myLonBeg:statevector%myLonEnd, &
2058                  statevector%myLatBeg:statevector%myLatEnd))
2059    call gsv_getField(statevector,field_Psfc,'P0')
2060
2061    if (Vcode == 5100) then
2062      allocate(PsfcLS(statevector%myLonBeg:statevector%myLonEnd, &
2063                      statevector%myLatBeg:statevector%myLatEnd))
2064      call gsv_getField(statevector,field_PsfcLS,'P0LS')
2065    end if
2066
2067    numStep = statevector%numStep
2068
2069    do stepIndex = 1, numStep
2070      Psfc(:,:) = field_Psfc(:,:,1,stepIndex)
2071      if ( present(Ps_in_hPa_opt) ) then
2072        if ( Ps_in_hPa_opt ) Psfc = Psfc * mpc_pa_per_mbar_r8
2073      end if
2074
2075      if (Vcode == 5100) then
2076        PsfcLS(:,:) = field_PsfcLS(:,:,1,stepIndex)
2077        if ( present(Ps_in_hPa_opt) ) then
2078          if ( Ps_in_hPa_opt ) PsfcLS = PsfcLS * mpc_pa_per_mbar_r8
2079        end if
2080        call fetch3DLevels_r8(statevector%vco, Psfc, sfcFldLS_opt=PsfcLS, &
2081                              fldM_opt=PressureM_out, fldT_opt=PressureT_out)
2082      else
2083        call fetch3DLevels_r8(statevector%vco, Psfc, &
2084                              fldM_opt=PressureM_out, fldT_opt=PressureT_out)
2085      end if
2086      P_M(:,:,:,stepIndex) = PressureM_out(:,:,:)
2087      P_T(:,:,:,stepIndex) = PressureT_out(:,:,:)
2088      deallocate(PressureM_out, PressureT_out)
2089
2090    end do
2091
2092    deallocate(Psfc)
2093    if (Vcode == 5100) deallocate(PsfcLS)
2094
2095    call msg('calcPressure_gsv_nl_vcode5xxx_r8 (czp)', 'END', verb_opt=4)
2096  end subroutine calcPressure_gsv_nl_vcode5xxx_r8
2097
2098  !---------------------------------------------------------
2099  ! calcPressure_gsv_nl_vcode5xxx_r4
2100  !---------------------------------------------------------
2101  subroutine calcPressure_gsv_nl_vcode5xxx_r4(statevector, P_T, P_M, Ps_in_hPa_opt)
2102    !
2103    !:Purpose: Pressure retrieval for GEM-P real(4) statevector, values
2104    !           values returned in pointers.
2105    !
2106    implicit none
2107
2108    ! Arguments:
2109    type(struct_gsv),           intent(in)    :: statevector
2110    real(4),           pointer, intent(inout) :: P_T(:,:,:,:)
2111    real(4),           pointer, intent(inout) :: P_M(:,:,:,:)
2112    logical, optional,          intent(in)    :: Ps_in_hPa_opt  ! If true, conversion from hPa to mbar done for surface pressure
2113
2114    ! Locals:
2115    real(kind=4), allocatable   :: Psfc(:,:), PsfcLS(:,:)
2116    real(kind=4), pointer       :: PressureM_out(:,:,:), PressureT_out(:,:,:)
2117    real(kind=4), pointer       :: field_Psfc(:,:,:,:), field_PsfcLS(:,:,:,:)
2118    integer                     :: stepIndex, numStep, Vcode
2119
2120    call msg('calcPressure_gsv_nl_vcode5xxx_r4 (czp)', 'START', verb_opt=4)
2121
2122    Vcode = gsv_getVco(statevector)%vcode
2123
2124    allocate(Psfc(statevector%myLonBeg:statevector%myLonEnd, &
2125                  statevector%myLatBeg:statevector%myLatEnd))
2126    call gsv_getField(statevector,field_Psfc,'P0')
2127
2128    if (Vcode == 5100) then
2129      allocate(PsfcLS(statevector%myLonBeg:statevector%myLonEnd, &
2130                      statevector%myLatBeg:statevector%myLatEnd))
2131      call gsv_getField(statevector,field_PsfcLS,'P0LS')
2132    end if
2133
2134    numStep = statevector%numStep
2135
2136    do stepIndex = 1, numStep
2137      Psfc(:,:) = field_Psfc(:,:,1,stepIndex)
2138      if ( present(Ps_in_hPa_opt) ) then
2139        if ( Ps_in_hPa_opt ) Psfc = Psfc * mpc_pa_per_mbar_r4
2140      end if
2141
2142      if (Vcode == 5100) then
2143        PsfcLS(:,:) = field_PsfcLS(:,:,1,stepIndex)
2144        if ( present(Ps_in_hPa_opt) ) then
2145          if ( Ps_in_hPa_opt ) PsfcLS = PsfcLS * mpc_pa_per_mbar_r4
2146        end if
2147
2148        call fetch3DLevels_r4(statevector%vco, Psfc, sfcFldLS_opt=PsfcLS, & 
2149                              fldM_opt=PressureM_out, fldT_opt=PressureT_out)
2150      else
2151        call fetch3DLevels_r4(statevector%vco, Psfc, &
2152                              fldM_opt=PressureM_out, fldT_opt=PressureT_out)
2153      end if
2154      P_M(:,:,:,stepIndex) = PressureM_out(:,:,:)
2155      P_T(:,:,:,stepIndex) = PressureT_out(:,:,:)
2156      deallocate(PressureM_out, PressureT_out)
2157
2158    end do
2159
2160    deallocate(Psfc)
2161    if (Vcode == 5100) deallocate(PsfcLS)
2162
2163    call msg('calcPressure_gsv_nl_vcode5xxx_r4 (czp)', 'START', verb_opt=4)
2164  end subroutine calcPressure_gsv_nl_vcode5xxx_r4
2165
2166  !---------------------------------------------------------
2167  ! calcPressure_gsv_tl
2168  !---------------------------------------------------------
2169  subroutine calcPressure_gsv_tl( statevector, statevectorRef)
2170    !
2171    !:Purpose: Tangent of pressure computation.
2172    !
2173    implicit none
2174
2175    ! Arguments:
2176    type(struct_gsv), intent(inout) :: statevector      ! statevector that will contain the P_T/P_M increments
2177    type(struct_gsv), intent(in)    :: statevectorRef   ! statevector containing needed reference fields
2178
2179    ! Locals:
2180    integer :: Vcode
2181
2182    call utl_tmg_start(178,'low-level--czp_calcPressure_tl')
2183    call msg('calcPressure_gsv_tl (czp)', 'START', verb_opt=2)
2184
2185    Vcode = gsv_getVco(statevectorRef)%vcode
2186    if (Vcode == 5005 .or. Vcode == 5002) then
2187      if ( .not. gsv_varExist(statevector,'P_*')  ) then
2188        call utl_abort('calcPressure_gsv_tl (czp): for vcode 5xxx, variables P_T and P_M must be allocated in gridstatevector')
2189      end if
2190      if ( .not. gsv_varExist(statevector,'P0')  ) then
2191        call utl_abort('calcPressure_gsv_tl (czp): for vcode 5xxx, variable P0 must be allocated in gridstatevector')
2192      end if
2193      call calcPressure_gsv_tl_vcode5xxx
2194    else if (Vcode == 21001) then
2195      ! Development notes (@mad001)
2196      !   probably some some gsv_varExist(statevector,.) needed for GEM-H
2197      call calcPressure_gsv_tl_vcode2100x
2198    else
2199      call utl_abort('calcPressure_gsv_tl (czp): not implemented')
2200    end if
2201
2202    call msg('calcPressure_gsv_tl (czp)', 'END', verb_opt=2)
2203    call utl_tmg_stop(178)
2204
2205    contains
2206
2207      !---------------------------------------------------------
2208      ! calcPressure_gsv_tl_vcode2100x
2209      !---------------------------------------------------------
2210      subroutine calcPressure_gsv_tl_vcode2100x
2211        implicit none
2212
2213        call utl_abort('calcPressure_gsv_tl (czp): vcode 21001 not implemented yet')
2214
2215      end subroutine calcPressure_gsv_tl_vcode2100x
2216
2217      !---------------------------------------------------------
2218      ! calcPressure_gsv_tl_vcode5xxx
2219      !---------------------------------------------------------
2220      subroutine calcPressure_gsv_tl_vcode5xxx
2221        implicit none
2222
2223        ! Locals:
2224        real(8), allocatable  :: Psfc(:,:)
2225        real(4), pointer      :: delPsfc_r4(:,:,:,:)
2226        real(8), pointer      :: delPsfc_r8(:,:,:,:)
2227        real(8), pointer      :: field_Psfc(:,:,:,:)
2228        real(4), pointer      :: delP_T_r4(:,:,:,:)
2229        real(8), pointer      :: delP_T_r8(:,:,:,:)
2230        real(4), pointer      :: delP_M_r4(:,:,:,:)
2231        real(8), pointer      :: delP_M_r8(:,:,:,:)
2232        real(8), pointer      :: dP_dPsfc_T(:,:,:)
2233        real(8), pointer      :: dP_dPsfc_M(:,:,:)
2234        integer               :: status, stepIndex,lonIndex,latIndex
2235        integer               :: lev_M, lev_T, nlev_T, nlev_M, numStep
2236
2237        call msg('calcPressure_gsv_tl_vcode5xxx (czp)', 'START', verb_opt=4)
2238
2239        nullify(dP_dPsfc_T)
2240        nullify(dP_dPsfc_M)
2241        nullify(delPsfc_r4,delPsfc_r8)
2242        nullify(delP_T_r4,delP_T_r8)
2243        nullify(delP_M_r4,delP_M_r8)
2244
2245        if (gsv_getDataKind(statevector) == 4) then
2246          call gsv_getField(statevector,delP_T_r4,'P_T')
2247          call gsv_getField(statevector,delP_M_r4,'P_M')
2248          call gsv_getField(statevector,delPsfc_r4,'P0')
2249        else
2250          call gsv_getField(statevector,delP_T_r8,'P_T')
2251          call gsv_getField(statevector,delP_M_r8,'P_M')
2252          call gsv_getField(statevector,delPsfc_r8,'P0')
2253        end if
2254
2255        nullify(field_Psfc)
2256        call gsv_getField(statevectorRef,field_Psfc,'P0')
2257
2258        nlev_T = gsv_getNumLev(statevector,'TH')
2259        nlev_M = gsv_getNumLev(statevector,'MM')
2260        numStep = statevector%numstep
2261
2262        allocate(Psfc(statevector%myLonBeg:statevector%myLonEnd, &
2263                      statevector%myLatBeg:statevector%myLatEnd))
2264
2265        do stepIndex = 1, numStep
2266
2267          Psfc(:,:) = field_Psfc(:,:,1,stepIndex)
2268
2269          ! dP_dPsfc_M
2270          nullify(dP_dPsfc_M)
2271          status = vgd_dpidpis(statevector%vco%vgrid, &
2272                               statevector%vco%ip1_M, &
2273                               dP_dPsfc_M, &
2274                               Psfc)
2275          if( status .ne. VGD_OK ) then
2276              call utl_abort('calcPressure_gsv_tl (czp): ERROR with vgd_dpidpis')
2277          end if
2278          ! calculate delP_M
2279          if (gsv_getDataKind(statevector) == 4) then
2280            do lev_M = 1, nlev_M
2281              do latIndex = statevector%myLatBeg, statevector%myLatEnd
2282                do lonIndex = statevector%myLonBeg, statevector%myLonEnd
2283                  delP_M_r4(lonIndex,latIndex,lev_M,stepIndex) =  &
2284                       dP_dPsfc_M(lonIndex-statevector%myLonBeg+1,&
2285                                  latIndex-statevector%myLatBeg+1,lev_M) * &
2286                       delPsfc_r4(lonIndex,latIndex,1,stepIndex)
2287                end do
2288              end do
2289            end do
2290          else
2291            do lev_M = 1, nlev_M
2292              do latIndex = statevector%myLatBeg, statevector%myLatEnd
2293                do lonIndex = statevector%myLonBeg, statevector%myLonEnd
2294                  delP_M_r8(lonIndex,latIndex,lev_M,stepIndex) =  &
2295                       dP_dPsfc_M(lonIndex-statevector%myLonBeg+1,&
2296                                  latIndex-statevector%myLatBeg+1,lev_M) * &
2297                       delPsfc_r8(lonIndex,latIndex,1,stepIndex)
2298                end do
2299              end do
2300            end do
2301          end if
2302          deallocate(dP_dPsfc_M)
2303
2304          ! dP_dPsfc_T
2305          nullify(dP_dPsfc_T)
2306          status = vgd_dpidpis(statevector%vco%vgrid, &
2307                               statevector%vco%ip1_T, &
2308                               dP_dPsfc_T, &
2309                               Psfc)
2310          if( status .ne. VGD_OK ) then
2311              call utl_abort('calcPressure_gsv_tl (czp): ERROR with vgd_dpidpis')
2312          end if
2313          ! calculate delP_T
2314          if (gsv_getDataKind(statevector) == 4) then
2315            do lev_T = 1, nlev_T
2316              do latIndex = statevector%myLatBeg, statevector%myLatEnd
2317                do lonIndex = statevector%myLonBeg, statevector%myLonEnd
2318                  delP_T_r4(lonIndex,latIndex,lev_T,stepIndex) =  &
2319                       dP_dPsfc_T(lonIndex-statevector%myLonBeg+1,&
2320                                  latIndex-statevector%myLatBeg+1,lev_T) * &
2321                       delPsfc_r4(lonIndex,latIndex,1,stepIndex)
2322                end do
2323              end do
2324            end do
2325          else
2326            do lev_T = 1, nlev_T
2327              do latIndex = statevector%myLatBeg, statevector%myLatEnd
2328                do lonIndex = statevector%myLonBeg, statevector%myLonEnd
2329                  delP_T_r8(lonIndex,latIndex,lev_T,stepIndex) =  &
2330                       dP_dPsfc_T(lonIndex-statevector%myLonBeg+1,&
2331                                  latIndex-statevector%myLatBeg+1,lev_T) * &
2332                       delPsfc_r8(lonIndex,latIndex,1,stepIndex)
2333                end do
2334              end do
2335            end do
2336          end if
2337          deallocate(dP_dPsfc_T)
2338
2339        end do
2340
2341        deallocate(Psfc)
2342
2343        call msg('calcPressure_gsv_tl_vcode5xxx (czp)', 'END', verb_opt=4)
2344      end subroutine calcPressure_gsv_tl_vcode5xxx
2345
2346  end subroutine calcPressure_gsv_tl
2347
2348  !---------------------------------------------------------
2349  ! calcPressure_gsv_ad
2350  !---------------------------------------------------------
2351  subroutine calcPressure_gsv_ad( statevector, statevectorRef)
2352    !
2353    !:Purpose: Adjoint of pressure computation.
2354    !
2355    implicit none
2356
2357    ! Arguments:
2358    type(struct_gsv), intent(inout) :: statevector    ! statevector that will contain increment of P_T/P_M
2359    type(struct_gsv), intent(in)    :: statevectorRef ! statevector containing needed reference fields
2360
2361    ! Locals:
2362    integer :: Vcode
2363
2364    call utl_tmg_start(179,'low-level--czp_calcPressure_ad')
2365    call msg('calcPressure_gsv_ad (czp)', 'START', verb_opt=2)
2366
2367    Vcode = gsv_getVco(statevectorRef)%vcode
2368    if (Vcode == 5005 .or. Vcode == 5002) then
2369      if ( .not. gsv_varExist(statevector,'P_*')  ) then
2370        call utl_abort('calcPressure_gsv_ad (czp): for vcode 5xxx, variables P_M and P_T must be allocated in gridstatevector')
2371      end if
2372      if ( .not. gsv_varExist(statevector,'P0')  ) then
2373        call utl_abort('calcPressure_gsv_ad (czp): for vcode 5xxx, variable P0 must be allocated in gridstatevector')
2374      end if
2375      call calcPressure_gsv_ad_vcode5xxx
2376    else if (Vcode == 21001) then
2377      ! Development notes (@mad001)
2378      !   probably some some gsv_varExist(statevector,.) needed for GEM-H
2379      call calcPressure_gsv_ad_vcode2100x
2380    else
2381      call utl_abort('calcPressure_gsv_ad (czp): not implemented')
2382    end if
2383
2384    call msg('calcPressure_gsv_ad (czp)', 'END', verb_opt=2)
2385    call utl_tmg_stop(179)
2386
2387    contains
2388
2389      !---------------------------------------------------------
2390      ! calcPressure_gsv_ad_vcode2100x
2391      !---------------------------------------------------------
2392      subroutine calcPressure_gsv_ad_vcode2100x
2393        implicit none
2394
2395        call utl_abort('calcPressure_gsv_ad (czp): vcode 21001 not implemented yet')
2396
2397      end subroutine calcPressure_gsv_ad_vcode2100x
2398
2399      !---------------------------------------------------------
2400      ! calcPressure_gsv_ad_vcode5xxx
2401      !---------------------------------------------------------
2402      subroutine calcPressure_gsv_ad_vcode5xxx
2403        implicit none
2404
2405        ! Locals:
2406        real(8), allocatable     :: Psfc(:,:)
2407        real(4), pointer         :: delPsfc_r4(:,:,:,:)
2408        real(8), pointer         :: delPsfc_r8(:,:,:,:)
2409        real(8), pointer         :: field_Psfc(:,:,:,:)
2410        real(4), pointer         :: delP_T_r4(:,:,:,:)
2411        real(8), pointer         :: delP_T_r8(:,:,:,:)
2412        real(4), pointer         :: delP_M_r4(:,:,:,:)
2413        real(8), pointer         :: delP_M_r8(:,:,:,:)
2414        real(8), pointer         :: dP_dPsfc_T(:,:,:)
2415        real(8), pointer         :: dP_dPsfc_M(:,:,:)
2416        integer                  :: status, stepIndex,lonIndex,latIndex
2417        integer                  :: lev_M, lev_T, nlev_T, nlev_M, numStep
2418
2419        call msg('calcPressure_gsv_ad_vcode5xxx (czp)', 'START', verb_opt=4)
2420
2421        nullify(delPsfc_r4, delPsfc_r8)
2422        nullify(field_Psfc)
2423        nullify(delP_T_r4, delP_T_r8)
2424        nullify(delP_M_r4, delP_M_r8)
2425        nullify(dP_dPsfc_T)
2426        nullify(dP_dPsfc_M)
2427
2428        if (gsv_getDataKind(statevector) == 4) then
2429          call gsv_getField(statevector,delP_T_r4,'P_T')
2430          call gsv_getField(statevector,delP_M_r4,'P_M')
2431          call gsv_getField(statevector,delPsfc_r4,'P0')
2432        else
2433          call gsv_getField(statevector,delP_T_r8,'P_T')
2434          call gsv_getField(statevector,delP_M_r8,'P_M')
2435          call gsv_getField(statevector,delPsfc_r8,'P0')
2436        end if
2437        call gsv_getField(statevectorRef,field_Psfc,'P0')
2438
2439        nlev_T = gsv_getNumLev(statevector,'TH')
2440        nlev_M = gsv_getNumLev(statevector,'MM')
2441        numStep = statevector%numstep
2442
2443        allocate(Psfc(statevector%myLonBeg:statevector%myLonEnd, &
2444                      statevector%myLatBeg:statevector%myLatEnd))
2445
2446        do stepIndex = 1, numStep
2447
2448          Psfc(:,:) = field_Psfc(:,:,1,stepIndex)
2449
2450          ! dP_dPsfc_M
2451          nullify(dP_dPsfc_M)
2452          status = vgd_dpidpis(statevector%vco%vgrid, &
2453                               statevector%vco%ip1_M, &
2454                               dP_dPsfc_M, &
2455                               Psfc)
2456          if( status .ne. VGD_OK ) then
2457            call utl_abort('calcPressure_gsv_ad (czp): ERROR with vgd_dpidpis')
2458          end if
2459          ! calculate delP_M
2460          if (gsv_getDataKind(statevector) == 4) then
2461            do lev_M = 1, nlev_M
2462              do latIndex = statevector%myLatBeg, statevector%myLatEnd
2463                do lonIndex = statevector%myLonBeg, statevector%myLonEnd
2464                  delPsfc_r4(lonIndex,latIndex,1,stepIndex) =  &
2465                       delPsfc_r4(lonIndex,latIndex,1,stepIndex) + &
2466                       dP_dPsfc_M(lonIndex-statevector%myLonBeg+1,&
2467                                  latIndex-statevector%myLatBeg+1,lev_M) * &
2468                       delP_M_r4(lonIndex,latIndex,lev_M,stepIndex)
2469                end do
2470              end do
2471            end do
2472          else
2473            do lev_M = 1, nlev_M
2474              do latIndex = statevector%myLatBeg, statevector%myLatEnd
2475                do lonIndex = statevector%myLonBeg, statevector%myLonEnd
2476                  delPsfc_r8(lonIndex,latIndex,1,stepIndex) =  &
2477                       delPsfc_r8(lonIndex,latIndex,1,stepIndex) + &
2478                       dP_dPsfc_M(lonIndex-statevector%myLonBeg+1,&
2479                                  latIndex-statevector%myLatBeg+1,lev_M) * &
2480                       delP_M_r8(lonIndex,latIndex,lev_M,stepIndex)
2481                end do
2482              end do
2483            end do
2484          end if
2485          deallocate(dP_dPsfc_M)
2486
2487          ! dP_dPsfc_T
2488          nullify(dP_dPsfc_T)
2489          status = vgd_dpidpis(statevector%vco%vgrid, &
2490                               statevector%vco%ip1_T, &
2491                               dP_dPsfc_T, &
2492                               Psfc)
2493          if( status .ne. VGD_OK ) then
2494              call utl_abort('calcPressure_gsv_ad (czp): ERROR with vgd_dpidpis')
2495          end if
2496          ! calculate delP_T
2497          if (gsv_getDataKind(statevector) == 4) then
2498            do lev_T = 1, nlev_T
2499              do latIndex = statevector%myLatBeg, statevector%myLatEnd
2500                do lonIndex = statevector%myLonBeg, statevector%myLonEnd
2501                  delPsfc_r4(lonIndex,latIndex,1,stepIndex) =  &
2502                       delPsfc_r4(lonIndex,latIndex,1,stepIndex) + &
2503                       dP_dPsfc_T(lonIndex-statevector%myLonBeg+1,&
2504                                  latIndex-statevector%myLatBeg+1,lev_T) * &
2505                       delP_T_r4(lonIndex,latIndex,lev_T,stepIndex)
2506                end do
2507              end do
2508            end do
2509          else
2510            do lev_T = 1, nlev_T
2511              do latIndex = statevector%myLatBeg, statevector%myLatEnd
2512                do lonIndex = statevector%myLonBeg, statevector%myLonEnd
2513                  delPsfc_r8(lonIndex,latIndex,1,stepIndex) =  &
2514                       delPsfc_r8(lonIndex,latIndex,1,stepIndex) + &
2515                       dP_dPsfc_T(lonIndex-statevector%myLonBeg+1,&
2516                                  latIndex-statevector%myLatBeg+1,lev_T) * &
2517                       delP_T_r8(lonIndex,latIndex,lev_T,stepIndex)
2518                end do
2519              end do
2520            end do
2521          end if
2522          deallocate(dP_dPsfc_T)
2523
2524        end do
2525
2526        deallocate(Psfc)
2527
2528        call msg('calcPressure_gsv_ad_vcode5xxx (czp)', 'END', verb_opt=4)
2529      end subroutine calcPressure_gsv_ad_vcode5xxx
2530
2531  end subroutine calcPressure_gsv_ad
2532
2533  !---------------------------------------------------------------------
2534  ! subroutines operating on struct_columnData
2535  !---------------------------------------------------------------------
2536
2537  !---------------------------------------------------------
2538  ! calcZandP_col_nl
2539  !---------------------------------------------------------
2540  subroutine calcZandP_col_nl(column)
2541    !
2542    !:Purpose: Compute pressure and height in the column in proper order
2543    !           depending on the vgrid kind
2544    !
2545    implicit none
2546
2547    ! Arguments:
2548    type(struct_columnData), intent(inout) :: column  ! column that will contain the Z_*/P_* fields
2549
2550    ! Locals:
2551    integer   :: Vcode
2552
2553    call msg('calcZandP_col_nl (czp)', 'START', verb_opt=2)
2554
2555    Vcode = column%vco%vcode
2556    if (Vcode == 5002 .or. Vcode == 5005 .or. Vcode == 5100) then
2557      ! if P_T, P_M not allocated : do nothing
2558      if (col_varExist(column,'P_*')) then
2559        call calcPressure_col_nl(column)
2560        if (col_varExist(column,'Z_*')) then
2561          call calcHeight_col_nl(column)
2562        end if
2563      end if
2564    else if (Vcode == 21001) then
2565      ! if Z_T, Z_M not allocated : do nothing
2566      if (col_varExist(column,'Z_*')) then
2567        call calcHeight_col_nl(column)
2568        if (col_varExist(column,'P_*')) then
2569          call calcPressure_col_nl(column)
2570        end if
2571      end if
2572    end if
2573  
2574    call msg('calcZandP_col_nl (czp)', 'END', verb_opt=2)
2575  end subroutine calcZandP_col_nl
2576
2577  !---------------------------------------------------------
2578  ! calcZandP_col_tl
2579  !---------------------------------------------------------
2580  subroutine calcZandP_col_tl(columnInc, columnIncRef)
2581    !
2582    !:Purpose: Compute pressure and height increment in the column in proper
2583    !           order depending on the vgrid kind
2584    !
2585    implicit none
2586
2587    ! Arguments:
2588    type(struct_columnData), intent(inout) :: columnInc    ! column that will contain the Z_*/P_* increments
2589    type(struct_columnData), intent(in)    :: columnIncRef ! column containing needed reference fields
2590
2591    ! Locals:
2592    integer   :: Vcode
2593
2594    call msg('calcZandP_col_tl (czp)', 'START', verb_opt=2)
2595
2596    Vcode = columnInc%vco%vcode
2597    if (Vcode == 5002 .or. Vcode == 5005) then
2598      ! if P_T, P_M not allocated : do nothing
2599      if (col_varExist(columnInc,'P_*')) then
2600        call calcPressure_col_tl( columnInc, columnIncRef)
2601        if (col_varExist(columnInc,'Z_*')) then
2602          call calcHeight_col_tl(columnInc, columnIncRef)
2603        end if
2604      end if
2605    else if (Vcode == 21001) then
2606      ! if Z_T, Z_M not allocated : do nothing
2607      if (col_varExist(columnInc,'Z_*')) then
2608        call calcHeight_col_tl(columnInc, columnIncRef)
2609
2610        if (col_varExist(columnInc,'P_*')) then
2611          call calcPressure_col_tl( columnInc, columnIncRef)
2612        end if
2613      end if
2614    else
2615      call utl_abort('calcZandP_col_tl (czp): not implemented')
2616    end if
2617
2618    call msg('calcZandP_col_tl (czp)', 'END', verb_opt=2)
2619  end subroutine calcZandP_col_tl
2620
2621  !---------------------------------------------------------
2622  ! calcZandP_col_ad
2623  !---------------------------------------------------------
2624  subroutine calcZandP_col_ad(columnInc, columnIncRef)
2625    !
2626    !:Purpose: Adjoint of pressure and height increment computation in the
2627    !           column in proper order depending on the vgrid kind
2628    !
2629    implicit none
2630
2631    ! Arguments:
2632    type(struct_columnData), intent(inout) :: columnInc    ! column that will contain the Z_*/P_* increments
2633    type(struct_columnData), intent(in)    :: columnIncRef ! column containing needed reference fields
2634
2635    ! Locals:
2636    integer   :: Vcode
2637
2638    call msg('calcZandP_col_ad (czp)', 'START', verb_opt=2)
2639
2640    Vcode = columnInc%vco%vcode
2641    if (Vcode == 5002 .or. Vcode == 5005) then
2642      ! if Z_T, Z_M not allocated : do nothing
2643      if (col_varExist(columnInc,'Z_*')) then
2644        call calcHeight_col_ad(columnInc, columnIncRef)
2645        if (col_varExist(columnInc,'P_*')) then
2646          call calcPressure_col_ad( columnInc, columnIncRef)
2647        end if
2648      end if
2649    else if (Vcode == 21001) then
2650      ! if P_T, P_M not allocated : do nothing
2651      if (col_varExist(columnInc,'P_*')) then
2652        call calcPressure_col_ad( columnInc, columnIncRef)
2653        if (col_varExist(columnInc,'Z_*')) then
2654          call calcHeight_col_ad(columnInc, columnIncRef)
2655        end if
2656      end if
2657    else
2658      call utl_abort('calcZandP_col_ad (czp): not implelmented')
2659    end if
2660
2661    call msg('calcZandP_col_ad (czp)', 'END', verb_opt=2)
2662  end subroutine calcZandP_col_ad
2663
2664  !---------------------------------------------------------
2665  ! calcHeight_col_nl
2666  !---------------------------------------------------------
2667  subroutine calcHeight_col_nl(column)
2668    !
2669    !:Purpose: Compute or retrieve heights on the column.
2670    !
2671    implicit none
2672
2673    ! Arguments:
2674    type(struct_columnData), intent(inout) :: column  ! column that will contain the Z_M/Z_T fields
2675
2676    ! Locals:
2677    real(8), pointer  ::  Z_T(:,:), Z_M(:,:)
2678
2679    call msg('calcHeight_col_nl (czp)', 'START', verb_opt=2)
2680
2681    Z_T => col_getAllColumns(column, 'Z_T')
2682    Z_M => col_getAllColumns(column, 'Z_M')
2683    call czp_calcReturnHeight_col_nl(column, Z_T, Z_M)
2684
2685    call msg('calcHeight_col_nl (czp)', &
2686           new_line('')//'Z_M = '//str(col_getColumn(column,1,'Z_M')) &
2687         //new_line('')//'Z_T = '//str(col_getColumn(column,1,'Z_T')), &
2688         verb_opt=2)
2689
2690    call msg('calcHeight_col_nl (czp)', 'END', verb_opt=2)
2691  end subroutine calcHeight_col_nl
2692
2693  !---------------------------------------------------------
2694  ! czp_calcReturnHeight_col_nl
2695  !---------------------------------------------------------
2696  subroutine czp_calcReturnHeight_col_nl(column, Z_T, Z_M)
2697    !
2698    !:Purpose: Return heights on the column.
2699    !
2700    implicit none
2701
2702    ! Arguments:
2703    type(struct_columnData),  intent(in)    :: column   ! reference column containing temperature and geopotential
2704    real(8), pointer,         intent(inout) :: Z_T(:,:) ! computed column height values on thermodynamic levels
2705    real(8), pointer,         intent(inout) :: Z_M(:,:) ! computed column height values on momentum levels
2706
2707    ! Locals:
2708    integer :: vcode
2709
2710    call msg('czp_calcReturnHeight_col_nl (czp)', 'START', verb_opt=2)
2711
2712    Vcode = col_getVco(column)%vcode
2713    if (Vcode == 5005 .or. Vcode == 5002 .or. Vcode == 5100) then
2714      if ( .not. (col_varExist(column,'P0') .and. col_varExist(column,'TT') .and. &
2715                  col_varExist(column,'HU'))  ) then
2716        call utl_abort('czp_calcReturnHeight_col_nl: for vcode 5xxx, variables P0, TT and HU must be allocated in column')
2717      end if
2718      call calcHeight_col_nl_vcode5xxx(column, Z_T, Z_M)
2719    else if (Vcode == 21001) then
2720      call calcHeight_col_nl_vcode2100x(column, Z_T, Z_M)
2721
2722    end if
2723
2724    call msg('czp_calcReturnHeight_col_nl (czp)', 'END', verb_opt=2)
2725  end subroutine czp_calcReturnHeight_col_nl
2726
2727  !---------------------------------------------------------
2728  ! calcHeight_col_nl_vcode2100x
2729  !---------------------------------------------------------
2730  subroutine calcHeight_col_nl_vcode2100x(column, Z_T, Z_M)
2731    !
2732    !:Purpose: Return heights on a GEM-H column.
2733    !
2734    implicit none
2735
2736    ! Arguments:
2737    type(struct_columnData),  intent(in)    :: column   ! reference column containing temperature and geopotential
2738    real(8), pointer,         intent(inout) :: Z_T(:,:) ! computed column height values on thermodynamic levels
2739    real(8), pointer,         intent(inout) :: Z_M(:,:) ! computed column height values on momentum levels
2740
2741    ! Locals:
2742    real(8), allocatable  :: hSfc(:,:)
2743    real(8), pointer      :: hPtrM(:,:,:), hPtrT(:,:,:)
2744    integer :: numCol, colIndex
2745
2746    call msg('calcHeight_col_nl_vcode2100x (czp)', 'START', verb_opt=4)
2747    if ( col_getNumCol(column) <= 0 ) then
2748      call msg('calcHeight_col_nl_vcode2100x (czp)',&
2749           'END (number of columns <= 0)', verb_opt=2)
2750      return
2751    end if
2752
2753    numCol = col_getNumCol(column)
2754    allocate(hSfc(1, numCol))
2755    do colIndex = 1, numCol
2756      hSfc(1,colIndex) = col_getHeight(column,1,colIndex, 'SF')
2757    end do
2758
2759    call fetch3DLevels_r8(column%vco, hSfc, fldM_opt=hPtrM, fldT_opt=hPtrT)
2760    Z_M(:,:) = hPtrM(1,:,:)
2761    Z_T(:,:) = hPtrT(1,:,:)
2762    deallocate(hPtrM, hPtrT)
2763
2764    deallocate(hSfc)
2765    call msg('calcHeight_col_nl_vcode2100x (czp)', 'END', verb_opt=4)
2766  end subroutine calcHeight_col_nl_vcode2100x
2767
2768  !---------------------------------------------------------
2769  ! calcHeight_col_nl_vcode5xxx
2770  !---------------------------------------------------------
2771  subroutine calcHeight_col_nl_vcode5xxx(column, Z_T, Z_M)
2772    !
2773    !:Purpose: Compute heights for GEM-P columns, return height values 
2774    !           in pointer arguments.
2775    !
2776    implicit none
2777
2778    ! Arguments:
2779    type(struct_columnData),  intent(in)    :: column   ! reference column containing temperature and geopotential
2780    real(8), pointer,         intent(inout) :: Z_T(:,:) ! computed column height values on thermodynamic levels
2781    real(8), pointer,         intent(inout) :: Z_M(:,:) ! computed column height values on momentum levels
2782
2783    ! Developement notes (@mad001)
2784    !   Null subroutine, no computation needed at time of writing.
2785    !   The code is traversed because of `calcZandP_nl` call in `cvt` (agnostic if
2786    !   dealing with GEM-P or GEM-H), but the results for heights are not used 
2787    !   at this time.
2788    !   We keep that stub however for future functionalities.
2789    call msg('calcHeight_col_nl_vcode5xxx (czp)', 'END (nothing done)', verb_opt=4)
2790    return
2791
2792    ! to prevent 'variable not used' remark
2793    if (.false.) then
2794      write(*,*) column%nk
2795      Z_T = 0.0
2796      Z_M = 0.0
2797    end if
2798
2799  end subroutine calcHeight_col_nl_vcode5xxx
2800
2801  !---------------------------------------------------------
2802  ! calcHeight_col_tl
2803  !---------------------------------------------------------
2804  subroutine calcHeight_col_tl(columnInc,columnIncRef)
2805    !
2806    !:Purpose: Tangent height computation on the column.
2807    !
2808    implicit none
2809
2810    ! Arguments:
2811    type(struct_columnData), intent(inout)  :: columnInc
2812    type(struct_columnData), intent(in)     :: columnIncRef
2813
2814    ! Locals:
2815    integer :: Vcode
2816
2817    call utl_tmg_start(173,'low-level--czp_calcHeight_tl')
2818    call msg('calcHeight_col_tl (czp)', 'START', verb_opt=2)
2819
2820    Vcode = col_getVco(columnIncRef)%vcode
2821    if (Vcode == 5005 .or. Vcode == 5002) then
2822      if ( .not. col_varExist(columnInc,'P_*')  ) then
2823        call utl_abort('calcHeight_col_tl (czp): for vcode 5xxx, variables P_M and P_T must be allocated in column')
2824      end if
2825      if ( .not. col_varExist(columnInc,'Z_*')  ) then
2826        call utl_abort('calcHeight_col_tl (czp): for vcode 5xxx, variables Z_M and Z_T must be allocated in column')
2827      end if
2828      if ( .not. col_varExist(columnInc,'TT')  ) then
2829        call utl_abort('calcHeight_col_tl (czp): for vcode 5xxx, variable TT must be allocated in column')
2830      end if
2831      if ( .not. col_varExist(columnInc,'HU')  ) then
2832        call utl_abort('calcHeight_col_tl (czp): for vcode 5xxx, variable HU must be allocated in column')
2833      end if
2834      if ( .not. col_varExist(columnInc,'P0')  ) then
2835        call utl_abort('calcHeight_col_tl (czp): for vcode 5xxx, variable P0 must be allocated in column')
2836      end if
2837      call calcHeight_col_tl_vcode5xxx
2838    else if (Vcode == 21001) then
2839      ! Development notes (@mad001)
2840      !   probably some some gsv_varExist(statevector,.) needed for GEM-H
2841      call calcHeight_col_tl_vcode2100x
2842    else
2843      call utl_abort('calcHeight_col_tl (czp): not implemented')
2844    end if
2845
2846    call msg('calcHeight_col_tl (czp)', 'END', verb_opt=2)
2847    call utl_tmg_stop(173)
2848
2849    contains
2850      !---------------------------------------------------------
2851      ! calcHeight_col_tl_vcode2100x
2852      !---------------------------------------------------------
2853      subroutine calcHeight_col_tl_vcode2100x
2854        implicit none
2855
2856        call utl_abort('calcHeight_col_tl: vcode 21001 not implemented yet')
2857
2858      end subroutine calcHeight_col_tl_vcode2100x
2859
2860      !---------------------------------------------------------
2861      ! calcHeight_col_tl_vcode5xxx
2862      !---------------------------------------------------------
2863      subroutine calcHeight_col_tl_vcode5xxx
2864        implicit none
2865
2866        ! Locals:
2867        integer :: lev_M,lev_T,nlev_M,nlev_T,colIndex,numColumns
2868        real(8) :: ScaleFactorBottom, ScaleFactorTop
2869        real(8), allocatable :: delThick(:,:)
2870        real(8), pointer     :: height_T_ptr(:,:),height_M_ptr(:,:)
2871        real(8), pointer     :: P_T(:,:), P_M(:,:)
2872        real(8), pointer  :: delHeight_M_ptr(:,:),delHeight_T_ptr(:,:)
2873        real(8), pointer  :: delTT(:,:),delHU(:,:),delP0(:,:)
2874        real(8), pointer  :: delP_T(:,:), delP_M(:,:)
2875
2876        call msg('calcHeight_col_tl_vcode5xxx (czp)', 'START', verb_opt=4)
2877
2878        nlev_T = col_getNumLev(columnIncRef,'TH')
2879        nlev_M = col_getNumLev(columnIncRef,'MM')
2880
2881        numColumns = col_getNumCol(columnInc)
2882
2883        allocate(delThick(nlev_T,numColumns))
2884        delThick(:,:) = 0.0d0
2885
2886        ! generate the height coefficients on the grid
2887        call calcHeightCoeff_col(columnIncRef)
2888
2889        ! loop over all lat/lon/step
2890
2891        height_M_ptr => col_getAllColumns(columnIncRef,'Z_M')
2892        height_T_ptr => col_getAllColumns(columnIncRef,'Z_T')
2893        P_M          => col_getAllColumns(columnIncRef,'P_M')
2894        P_T          => col_getAllColumns(columnIncRef,'P_T')
2895
2896        delHeight_M_ptr => col_getAllColumns(columnInc,'Z_M')
2897        delHeight_T_ptr => col_getAllColumns(columnInc,'Z_T')
2898        delTT           => col_getAllColumns(columnInc,'TT')
2899        delHU           => col_getAllColumns(columnInc,'HU')
2900        delP0           => col_getAllColumns(columnInc,'P0')
2901        delP_M          => col_getAllColumns(columnInc,'P_M')
2902        delP_T          => col_getAllColumns(columnInc,'P_T')
2903
2904        ! ensure increment at sfc is zero (fixed height level)
2905        delHeight_M_ptr(nlev_M,:) = 0.0d0
2906        delHeight_T_ptr(nlev_T,:) = 0.0d0
2907
2908        if_computeHeight_col_tl_vcodes : if (Vcode == 5002) then
2909
2910          ! compute increment to thickness for each layer between the two momentum levels
2911          do colIndex = 1, numColumns
2912            do lev_T = 2, (nlev_T-1)
2913              delThick(lev_T,colIndex) =  &
2914                    coeff_M_TT_col(lev_T,colIndex) * delTT(lev_T,colIndex) + &
2915                    coeff_M_HU_col(lev_T,colIndex) * delHU(lev_T,colIndex) + &
2916                    coeff_M_P0_delPM_col(lev_T,colIndex) * &
2917                    ( delP_M(lev_T  ,colIndex) / P_M(lev_T  ,colIndex) - &
2918                      delP_M(lev_T-1,colIndex) / P_M(lev_T-1,colIndex) ) + &
2919                    coeff_M_P0_dP_delPT_col(lev_T,colIndex) * &
2920                    delP_T(lev_T,colIndex) + &
2921                    coeff_M_P0_dP_delP0_col(lev_T,colIndex) * delP0(1,colIndex)
2922            end do
2923          end do
2924
2925          ! compute height increment on momentum levels above the surface
2926          do colIndex = 1, numColumns
2927            do lev_M = (nlev_M-1), 1, -1
2928              lev_T = lev_M + 1 ! thermo level just below momentum level being computed
2929              delHeight_M_ptr(lev_M,colIndex) =  &
2930                   delHeight_M_ptr(lev_M+1,colIndex) + delThick(lev_T,colIndex)
2931            end do
2932          end do
2933
2934          ! compute height increment on thermo levels using weighted average of height increment of momentum levels
2935          do colIndex = 1, numColumns
2936            do lev_T = 1, (nlev_T-1)
2937              if ( lev_T == 1) then
2938                ! compute height increment for top thermo level (from top momentum level)
2939                delHeight_T_ptr(1,colIndex) = delHeight_M_ptr(1,colIndex) +  &
2940                     coeff_T_TT_col(colIndex) * delTT(1,colIndex) + &
2941                     coeff_T_HU_col(colIndex) * delHU(1,colIndex) + &
2942                     coeff_T_P0_delP1_col(colIndex) * &
2943                     ( delP_M(1,colIndex) / P_M(1,colIndex) - &
2944                       delP_T(1,colIndex) / P_T(1,colIndex) ) + &
2945                     coeff_T_P0_dP_delPT_col(colIndex) * delP_T(1,colIndex) + &
2946                     coeff_T_P0_dP_delP0_col(colIndex) * delP0(1,colIndex)
2947              else
2948                lev_M = lev_T ! momentum level just below thermo level being computed
2949                ScaleFactorBottom = (height_T_ptr(lev_T,colIndex) - &
2950                    height_M_ptr(lev_M-1,colIndex)) / &
2951                    (height_M_ptr(lev_M,colIndex) - height_M_ptr(lev_M-1,colIndex))
2952                ScaleFactorTop    = 1 - ScaleFactorBottom
2953                delHeight_T_ptr(lev_T,colIndex) =  &
2954                     ScaleFactorBottom * delHeight_M_ptr(lev_M  ,colIndex) + &
2955                     ScaleFactorTop * delHeight_M_ptr(lev_M-1,colIndex)
2956              end if
2957            end do
2958          end do
2959
2960        else if(Vcode == 5005) then if_computeHeight_col_tl_vcodes
2961
2962          ! compute increment to thickness for each layer between the two momentum levels
2963          do colIndex = 1, numColumns
2964            do lev_T = 1, (nlev_T-1)
2965              delThick(lev_T,colIndex) =  &
2966                   coeff_M_TT_col(lev_T,colIndex) * delTT(lev_T,colIndex) + &
2967                   coeff_M_HU_col(lev_T,colIndex) * delHU(lev_T,colIndex) + &
2968                   coeff_M_P0_delPM_col(lev_T,colIndex) * &
2969                   ( delP_M(lev_T+1,colIndex) / P_M(lev_T+1,colIndex) - &
2970                     delP_M(lev_T  ,colIndex) / P_M(lev_T  ,colIndex) ) + &
2971                   coeff_M_P0_dP_delPT_col(lev_T,colIndex) * &
2972                   delP_T(lev_T,colIndex) + &
2973                   coeff_M_P0_dP_delP0_col(lev_T,colIndex) * delP0(1,colIndex)
2974            end do
2975          end do
2976
2977          ! compute height increment on momentum levels above the surface
2978          do colIndex = 1, numColumns
2979            do lev_M = (nlev_M-1), 1, -1
2980              lev_T = lev_M ! thermo level just below momentum level being computed
2981              delHeight_M_ptr(lev_M,colIndex) =  &
2982                   delHeight_M_ptr(lev_M+1,colIndex) + delThick(lev_T,colIndex)
2983            end do
2984          end do
2985
2986          ! compute height increment on thermo levels using weighted average of height increment of momentum levels
2987          do colIndex = 1, numColumns
2988            do lev_T = 1, (nlev_T-1)
2989              lev_M = lev_T + 1 ! momentum level just below thermo level being computed
2990              ScaleFactorBottom =  &
2991                   (height_T_ptr(lev_T,colIndex) - height_M_ptr(lev_M-1,colIndex)) / &
2992                   (height_M_ptr(lev_M,colIndex) - height_M_ptr(lev_M-1,colIndex))
2993              ScaleFactorTop    = 1 - ScaleFactorBottom
2994              delHeight_T_ptr(lev_T,colIndex) =  &
2995                   ScaleFactorBottom * delHeight_M_ptr(lev_M  ,colIndex) + &
2996                   ScaleFactorTop * delHeight_M_ptr(lev_M-1,colIndex)
2997            end do
2998          end do
2999        else
3000          call utl_abort('calcHeight_col_tl_vcode5xxx (czp): not implemented')
3001        end if if_computeHeight_col_tl_vcodes
3002
3003        deallocate(delThick)
3004
3005        call msg('calcHeight_col_tl_vcode5xxx (czp)', 'END', verb_opt=4)
3006      end subroutine calcHeight_col_tl_vcode5xxx
3007
3008  end subroutine calcHeight_col_tl
3009
3010  !---------------------------------------------------------
3011  ! calcHeight_col_ad
3012  !---------------------------------------------------------
3013  subroutine calcHeight_col_ad(columnInc,columnIncRef)
3014    !
3015    !:Purpose: Adjoint of height computation on the column.
3016    !
3017    !
3018    implicit none
3019
3020    ! Arguments:
3021    type(struct_columnData), intent(inout) :: columnInc
3022    type(struct_columnData), intent(in)    :: columnIncRef
3023
3024    ! Locals:
3025    integer :: Vcode
3026
3027    call utl_tmg_start(174,'low-level--czp_calcHeight_ad')
3028    call msg('calcHeight_col_ad (czp)', 'START', verb_opt=2)
3029
3030    Vcode = col_getVco(columnIncRef)%vcode
3031    if (Vcode == 5005 .or. Vcode == 5002) then
3032      if ( .not. col_varExist(columnInc,'P_*')  ) then
3033        call utl_abort('calcHeight_col_ad (czp): for vcode 5xxx, variables P_M and P_T must be allocated in column')
3034      end if
3035      if ( .not. col_varExist(columnInc,'Z_*')  ) then
3036        call utl_abort('calcHeight_col_ad (czp): for vcode 5xxx, variables Z_M and Z_T must be allocated in column')
3037      end if
3038      if ( .not. col_varExist(columnInc,'TT')  ) then
3039        call utl_abort('calcHeight_col_ad (czp): for vcode 5xxx, variable TT must be allocated in column')
3040      end if
3041      if ( .not. col_varExist(columnInc,'HU')  ) then
3042        call utl_abort('calcHeight_col_ad (czp): for vcode 5xxx, variable HU must be allocated in column')
3043      end if
3044      if ( .not. col_varExist(columnInc,'P0')  ) then
3045        call utl_abort('calcHeight_col_ad (czp): for vcode 5xxx, variable P0 must be allocated in column')
3046      end if
3047      call calcHeight_col_ad_vcode5xxx
3048    else if (Vcode == 21001) then
3049      ! Development notes (@mad001)
3050      !   probably some some gsv_varExist(statevector,.) needed for GEM-H
3051      call calcHeight_col_ad_vcode2100x
3052    else
3053      call utl_abort('calcHeight_col_ad (czp): not implemented')
3054    end if
3055
3056    call msg('calcHeight_col_ad (czp)', 'END', verb_opt=2)
3057    call utl_tmg_stop(174)
3058
3059    contains
3060      !---------------------------------------------------------
3061      ! calcHeight_col_ad_vcode2100x
3062      !---------------------------------------------------------
3063      subroutine calcHeight_col_ad_vcode2100x
3064        implicit none
3065
3066        call utl_abort('calcHeight_col_ad (czp): vcode 21001 not implemented yet')
3067
3068      end subroutine calcHeight_col_ad_vcode2100x
3069
3070      !---------------------------------------------------------
3071      ! calcHeight_col_ad_vcode5xxx
3072      !---------------------------------------------------------
3073      subroutine calcHeight_col_ad_vcode5xxx
3074        implicit none
3075
3076        ! Locals:
3077        integer :: lev_M,lev_T,nlev_M,nlev_T,numColumns,colIndex
3078        real(8) :: ScaleFactorBottom, ScaleFactorTop
3079        real(8), allocatable :: delThick(:,:)
3080        real(8), pointer     :: height_M_ptr(:,:),height_T_ptr(:,:)
3081        real(8), allocatable :: delHeight_M(:,:)
3082        real(8), pointer     :: P_M(:,:),P_T(:,:)
3083        real(8), pointer     :: delHeight_M_ptr(:,:),delHeight_T_ptr(:,:)
3084        real(8), pointer     :: delTT(:,:),delHU(:,:),delP0(:,:)
3085        real(8), pointer     :: delP_M(:,:),delP_T(:,:)
3086
3087        call msg('calcHeight_col_ad_vcode5xxx (czp)', 'START', verb_opt=4)
3088
3089        nlev_T = col_getNumLev(columnIncRef,'TH')
3090        nlev_M = col_getNumLev(columnIncRef,'MM')
3091        numColumns = col_getNumCol(columnIncRef)
3092
3093        allocate(delHeight_M(nlev_M,numColumns))
3094        allocate(delThick(0:nlev_T,numColumns))
3095
3096        ! generate the height coefficients on the grid
3097        call calcHeightCoeff_col(columnIncRef)
3098
3099        height_M_ptr => col_getAllColumns(columnIncRef,'Z_M')
3100        height_T_ptr => col_getAllColumns(columnIncRef,'Z_T')
3101        P_M          => col_getAllColumns(columnIncRef,'P_M')
3102        P_T          => col_getAllColumns(columnIncRef,'P_T')
3103
3104        delHeight_M_ptr => col_getAllColumns(columnInc,'Z_M')
3105        delHeight_T_ptr => col_getAllColumns(columnInc,'Z_T')
3106        delTT           => col_getAllColumns(columnInc,'TT')
3107        delHU           => col_getAllColumns(columnInc,'HU')
3108        delP0           => col_getAllColumns(columnInc,'P0')
3109        delP_M          => col_getAllColumns(columnInc,'P_M')
3110        delP_T          => col_getAllColumns(columnInc,'P_T')
3111
3112        delHeight_M(:,:) = delHeight_M_ptr(:,:)
3113
3114        if_computeHeight_col_ad_vcodes : if(Vcode == 5002) then
3115
3116          ! adjoint of compute height increment on thermo levels by simple averaging
3117          do colIndex = 1, numColumns
3118            do lev_T = 1, (nlev_T-1)
3119              lev_M = lev_T ! momentum level just below thermo level being computed
3120
3121              ! adjoint of compute height increment on top thermo level (from top momentum level)
3122              if (lev_T == 1) then
3123                delHeight_M(1,colIndex)  =  &
3124                     delHeight_M(1,colIndex) + &
3125                     delHeight_T_ptr(1,colIndex)
3126
3127                delTT(1,colIndex) =  &
3128                     delTT(1,colIndex) + &
3129                     coeff_T_TT_col(colIndex) * delHeight_T_ptr(1,colIndex)
3130
3131                delHU(1,colIndex) =  &
3132                     delHU(1,colIndex) + &
3133                     coeff_T_HU_col   (colIndex) * delHeight_T_ptr(1,colIndex)
3134
3135                delP_M(1,colIndex) =  &
3136                     delP_M(1,colIndex) + &
3137                     coeff_T_P0_delP1_col(colIndex) / P_M(1,colIndex) * &
3138                     delHeight_T_ptr(1,colIndex)
3139
3140                delP_T(1,colIndex) =  &
3141                     delP_T(1,colIndex) - &
3142                     coeff_T_P0_delP1_col(colIndex) / P_T(1,colIndex) * &
3143                     delHeight_T_ptr(1,colIndex)
3144
3145                delP_T(1,colIndex) =  &
3146                     delP_T(1,colIndex) + &
3147                     coeff_T_P0_dP_delPT_col(colIndex) * delHeight_T_ptr(1,colIndex)
3148
3149                delP0(1,colIndex) =  &
3150                     delP0(1,colIndex) + &
3151                     coeff_T_P0_dp_delP0_col(colIndex) * delHeight_T_ptr(1,colIndex)
3152              else
3153                ScaleFactorBottom =  &
3154                    (height_T_ptr(lev_T,colIndex) - &
3155                      height_M_ptr(lev_M-1,colIndex)) / &
3156                    (height_M_ptr(lev_M,colIndex) - &
3157                      height_M_ptr(lev_M-1,colIndex))
3158                ScaleFactorTop    = 1 - ScaleFactorBottom
3159
3160                delHeight_M(lev_M-1,colIndex) =  &
3161                     delHeight_M(lev_M-1,colIndex) + &
3162                     ScaleFactorTop * delHeight_T_ptr(lev_T,colIndex)
3163
3164                delHeight_M(lev_M,colIndex) =  &
3165                     delHeight_M(lev_M  ,colIndex) + &
3166                     ScaleFactorBottom * delHeight_T_ptr(lev_T,colIndex)
3167              end if
3168            end do
3169          end do
3170
3171          ! adjoint of compute height increment on momentum levels above the surface
3172          delThick(0:1,:) = 0.0d0
3173          do colIndex = 1, numColumns
3174            do lev_M = 1, (nlev_M-1)
3175              lev_T = lev_M + 1 ! thermo level just below momentum level being computed
3176              delThick(lev_T,colIndex) =  &
3177                   delThick(lev_T-1,colIndex) + &
3178                   delHeight_M(lev_M  ,colIndex)
3179            end do
3180          end do
3181
3182          ! adjoint of compute increment to thickness for each layer between the two momentum levels
3183          do colIndex = 1, numColumns
3184            do lev_T = 2, nlev_T-1
3185              delTT(lev_T,colIndex) =  &
3186                  delTT            (lev_T,colIndex) + &
3187                  coeff_M_TT_col   (lev_T,colIndex) * delThick(lev_T,colIndex)
3188
3189              delHU(lev_T,colIndex) =  &
3190                  delHU            (lev_T,colIndex) + &
3191                  coeff_M_HU_col   (lev_T,colIndex) * delThick(lev_T,colIndex)
3192
3193              delP_M(lev_T,colIndex)=  &
3194                  delP_M(lev_T,colIndex) + &
3195                  coeff_M_P0_delPM_col(lev_T,colIndex) / P_M(lev_T,colIndex) * &
3196                  delThick(lev_T,colIndex)
3197
3198              delP_M(lev_T-1,colIndex) =  &
3199                  delP_M(lev_T-1,colIndex) - &
3200                  coeff_M_P0_delPM_col(lev_T,colIndex) / P_M(lev_T-1,colIndex)*&
3201                  delThick(lev_T,colIndex)
3202
3203              delP_T(lev_T,colIndex) =  &
3204                  delP_T(lev_T,colIndex) + &
3205                  coeff_M_P0_dP_delPT_col(lev_T,colIndex) * &
3206                  delThick(lev_T,colIndex)
3207
3208              delP0(1,colIndex) =  &
3209                  delP0(1,colIndex) + &
3210                  coeff_M_P0_dP_delP0_col(lev_T,colIndex) * &
3211                  delThick(lev_T,colIndex)
3212            end do
3213          end do
3214
3215        else if(Vcode == 5005) then if_computeHeight_col_ad_vcodes
3216
3217          ! adjoint of compute height increment on thermo levels by simple averaging
3218          do colIndex = 1, numColumns
3219            do lev_T = 1, (nlev_T-1)
3220              lev_M = lev_T+1 ! momentum level just below thermo level being computed
3221              ScaleFactorBottom = (height_T_ptr(lev_T,colIndex) - &
3222                    height_M_ptr(lev_M-1,colIndex)) / &
3223                  (height_M_ptr(lev_M,colIndex) - &
3224                    height_M_ptr(lev_M-1,colIndex))
3225              ScaleFactorTop    = 1 - ScaleFactorBottom
3226              delHeight_M(lev_M-1,colIndex) = delHeight_M(lev_M-1,colIndex) + &
3227                  ScaleFactorTop * delHeight_T_ptr(lev_T  ,colIndex)
3228              delHeight_M(lev_M,colIndex)   = delHeight_M(lev_M  ,colIndex) + &
3229                  ScaleFactorBottom * delHeight_T_ptr(lev_T  ,colIndex)
3230            end do
3231          end do
3232
3233          ! adjoint of compute height increment on momentum levels
3234          delThick(0,:) = 0.0d0
3235          do colIndex = 1, numColumns
3236            do lev_M = 1, (nlev_M-1)
3237              lev_T = lev_M ! thermo level just below momentum level being computed
3238              delThick(lev_T,colIndex) = delThick(lev_T-1,colIndex) + &
3239                                         delHeight_M (lev_M  ,colIndex)
3240            end do
3241          end do
3242
3243          do colIndex = 1, numColumns
3244            do lev_T = 1, nlev_T-1
3245              delTT(lev_T,colIndex) =  &
3246                  delTT(lev_T,colIndex) + &
3247                  coeff_M_TT_col(lev_T,colIndex) * delThick(lev_T,colIndex)
3248
3249              delHU(lev_T,colIndex) =  &
3250                  delHU(lev_T,colIndex) + &
3251                  coeff_M_HU_col(lev_T,colIndex) * delThick(lev_T,colIndex)
3252
3253              delP_M(lev_T+1,colIndex) =  &
3254                  delP_M(lev_T+1,colIndex) + &
3255                  coeff_M_P0_delPM_col(lev_T,colIndex) / P_M(lev_T+1,colIndex)*&
3256                  delThick(lev_T,colIndex)
3257
3258              delP_M(lev_T  ,colIndex) =  &
3259                  delP_M(lev_T  ,colIndex) - &
3260                  coeff_M_P0_delPM_col(lev_T,colIndex) / P_M(lev_T  ,colIndex)*&
3261                  delThick(lev_T,colIndex)
3262
3263              delP_T(lev_T  ,colIndex) =  &
3264                  delP_T(lev_T  ,colIndex) + &
3265                  coeff_M_P0_dP_delPT_col(lev_T,colIndex) * &
3266                  delThick(lev_T,colIndex)
3267
3268              delP0(1,colIndex)     =  &
3269                  delP0(1,colIndex) + &
3270                  coeff_M_P0_dP_delP0_col(lev_T,colIndex) * &
3271                  delThick(lev_T,colIndex)
3272            end do
3273          end do
3274        else
3275          call utl_abort('calcHeight_col_ad_vcode5xxx (czp): not implemented')
3276        end if if_computeHeight_col_ad_vcodes
3277
3278        deallocate(delThick)
3279        deallocate(delHeight_M)
3280
3281        call msg('calcHeight_col_ad_vcode5xxx (czp)', 'END', verb_opt=4)
3282      end subroutine calcHeight_col_ad_vcode5xxx
3283
3284  end subroutine calcHeight_col_ad
3285
3286  !---------------------------------------------------------
3287  ! calcPressure_col_nl
3288  !---------------------------------------------------------
3289  subroutine calcPressure_col_nl(column)
3290    !
3291    !:Purpose: Pressure computation on the column.
3292    !
3293    implicit none
3294
3295    ! Arguments:
3296    type(struct_columnData), intent(inout)  :: column
3297
3298    ! Locals:
3299    real(8), pointer  ::  P_T(:,:), P_M(:,:)
3300
3301    call msg('calcPressure_col_nl (czp)', 'START', verb_opt=2)
3302
3303    P_T => col_getAllColumns(column, 'P_T')
3304    P_M => col_getAllColumns(column, 'P_M')
3305    call czp_calcReturnPressure_col_nl(column, P_T, P_M)
3306
3307    call msg('calcPressure_col_nl (czp)', &
3308           new_line('')//'P_M = '//str(col_getColumn(column,1,'P_M')) &
3309         //new_line('')//'P_T = '//str(col_getColumn(column,1,'P_T')), &
3310         verb_opt=2)
3311
3312    call msg('calcPressure_col_nl (czp)', 'END', verb_opt=2)
3313  end subroutine calcPressure_col_nl
3314
3315  !---------------------------------------------------------
3316  ! czp_calcReturnPressure_col_nl
3317  !---------------------------------------------------------
3318  subroutine czp_calcReturnPressure_col_nl(column, P_T, P_M)
3319    !
3320    !:Purpose: Pressure computation on the column, return values in pointers.
3321    !
3322    implicit none
3323
3324    ! Arguments:
3325    type(struct_columnData),  intent(in)    :: column   ! reference column
3326    real(8), pointer,         intent(inout) :: P_T(:,:) ! computed column pressure values on thermodynamic levels
3327    real(8), pointer,         intent(inout) :: P_M(:,:) ! computed column pressure values on momentum levels
3328
3329    ! Locals:
3330    integer :: Vcode
3331
3332    call msg('czp_calcReturnPressure_col_nl (czp)', 'START', verb_opt=2)
3333
3334    Vcode = col_getVco(column)%vcode
3335    if (Vcode == 5005 .or. Vcode == 5002 .or. Vcode == 5100) then
3336      if ( .not. col_varExist(column,'P0')  ) then
3337        call utl_abort('czp_calcReturnPressure_col_nl (czp): for vcode 5xxx, variable P0 must be allocated in column')
3338      end if
3339      call calcPressure_col_nl_vcode5xxx(column, P_T, P_M)
3340    else if (Vcode == 21001) then
3341      if ( .not. (col_varExist(column,'P0') .and. col_varExist(column,'TT') .and. &
3342                  col_varExist(column,'HU'))  ) then
3343        call utl_abort('czp_calcReturnPressure_col_nl (czp): for vcode 2100x, variables P0, TT and HU must be allocated in column')
3344      end if
3345      call calcPressure_col_nl_vcode2100x(column, P_T, P_M)
3346    end if
3347
3348    call msg('czp_calcReturnPressure_col_nl (czp)', 'END', verb_opt=2)
3349  end subroutine czp_calcReturnPressure_col_nl
3350
3351  !---------------------------------------------------------
3352  ! calcPressure_col_nl_vcode2100x
3353  !---------------------------------------------------------
3354  subroutine calcPressure_col_nl_vcode2100x(column, P_T, P_M)
3355    !
3356    !:Purpose: Compute pressure and return values in pointer arguments.
3357    !           GEM-H column input.
3358    !
3359    implicit none
3360    ! Development notes (@mad001)
3361    !   if feasible, consider reusing the same code for both 
3362    !   `calcPressure_{gsv,col}_nl_vcode2100x`
3363    !   (@mab001) Also should remove need for `Z_T/M` to be allocated in column
3364    !   and use local array instead.
3365
3366    ! Arguments:
3367    type(struct_columnData),  intent(in)    :: column   ! reference column
3368    real(8), pointer,         intent(inout) :: P_T(:,:) ! computed column pressure values on thermodynamic levels
3369    real(8), pointer,         intent(inout) :: P_M(:,:) ! computed column pressure values on momentum levels
3370
3371    ! Locals:
3372    real(8), allocatable  :: tv(:)
3373    integer :: numCol, nLev_T, nLev_M
3374    integer :: colIndex, lev_T, lev_M
3375    real(8) :: lat, sLat, cLat
3376    real(8) :: P0, rMT, hu, tt, tv0, cmp, dh, Rgh
3377    real(8) :: scaleFactorBottom, logP
3378    real(8) :: Z_T, Z_M, Z_M1
3379
3380    call msg('calcPressure_col_nl_vcode2100x (czp)', 'START', verb_opt=4)
3381
3382    numCol = col_getNumCol(column)
3383    nLev_M = col_getNumLev(column, 'MM')
3384    nLev_T = col_getNumLev(column, 'TH')
3385
3386    allocate(tv(nLev_T))
3387
3388    do_onAllcolumns: do colIndex = lbound(column%lat,1), ubound(column%lat,1)
3389      ! column%lat populated in innovation_mod from obsSpaceData latitudes
3390      lat = column%lat(colIndex)
3391      sLat = sin(lat)
3392      cLat = cos(lat)
3393
3394      ! surface values
3395      P0  = col_getElem(  column, 1, colIndex, 'P0') ! surface pressure
3396      rMT = col_getHeight(column, 1, colIndex, 'SF') ! surface height
3397
3398      ! compute pressure on diagnostic (nLev_{T,M}) levels
3399      hu = col_getElem(column, nLev_T, colIndex, 'HU')
3400      tt = col_getElem(column, nLev_T, colIndex, 'TT')
3401      tv0 = phf_fotvt8(tt,hu)
3402
3403      ! thermo diagnostic level
3404      Z_T = col_getHeight(column, nLev_T, colIndex, 'TH')
3405      cmp = gpscompressibility(P0,tt,hu)
3406      tv(nlev_T) = tv0*cmp
3407      dh = Z_T - rMT
3408      Rgh = phf_gravityalt(sLat, rMT+0.5D0*dh)
3409      P_T(colIndex, nlev_T) = P0*exp(-Rgh*dh/MPC_RGAS_DRY_AIR_R8/tv(nlev_T))
3410
3411      ! momentum diagnostic level
3412      Z_M = col_getHeight(column,nLev_M,colIndex,'MM')
3413      dh = Z_M - rMT
3414      Rgh = phf_gravityalt(sLat, rMT+0.5D0*dh)
3415      P_M(colIndex, nlev_M) = P0*exp(-Rgh*dh/MPC_RGAS_DRY_AIR_R8/tv(nlev_T))
3416
3417      call msg('calcPressure_col_nl_vcode2100x (czp)', &
3418           'Column index '//str(colIndex) //': lat='//str(lat)&
3419           //'   Surface: height='//str(rMT)//', P0='//str(P0) &
3420           //'   TH_diag_lvl ('//str(nLev_T)//'): height='//str(Z_T)&
3421                                  //', P_T='//str(P_T(colIndex, nlev_T)) &
3422           //'   MM_diag_lvl ('//str(nLev_M)//'): height='//str(Z_M)&
3423                                  //', P_M='//str(P_M(colIndex, nlev_M)), &
3424           verb_opt=6)
3425
3426      ! compute pressure on all levels above except the last
3427      do lev_M = nlev_M-1, 1, -1
3428        lev_T = lev_M ! thermo level just below
3429        hu   = col_getElem(  column, lev_T,   colIndex, 'HU')
3430        tt   = col_getElem(  column, lev_T,   colIndex, 'TT')
3431        Z_M  = col_getHeight(column, lev_M,   colIndex, 'MM')
3432        Z_M1 = col_getHeight(column, lev_M+1, colIndex, 'MM')
3433        Z_T  = col_getHeight(column, lev_T,   colIndex, 'TH')
3434
3435        tv0 = phf_fotvt8(tt,hu)
3436        dh = Z_M - Z_M1
3437        Rgh = phf_gravityalt(sLat, Z_M1+0.5D0*dh)
3438
3439        ! approximation of tv from pressure on previous momentum level
3440        cmp = gpscompressibility(P_M(colIndex, lev_M+1),tt,hu)
3441        tv(lev_T) = tv0*cmp
3442        P_M(colIndex, lev_M) = P_M(colIndex, lev_M+1) * &
3443                            exp(-Rgh*dh/MPC_RGAS_DRY_AIR_R8/tv(lev_T))
3444        ! first interpolation of thermo pressure
3445        scaleFactorBottom = (Z_T-Z_M1)/(Z_M-Z_M1)
3446        logP = (1.0D0-scaleFactorBottom)*log(P_M(colIndex, lev_M+1)) + &
3447                              scaleFactorBottom*log(P_M(colIndex, lev_M))
3448        P_T(colIndex, lev_T) = exp(logP)
3449
3450        ! second iteration on tv
3451        cmp = gpscompressibility(P_T(colIndex, lev_T),tt,hu)
3452        tv(lev_T) = tv0*cmp
3453        P_M(colIndex, lev_M) = P_M(colIndex, lev_M+1) * &
3454                            exp(-Rgh*dh/MPC_RGAS_DRY_AIR_R8/tv(lev_T))
3455
3456        ! second iteration interpolation of thermo pressure
3457        logP = (1.0D0-scaleFactorBottom)*log(P_M(colIndex, lev_M+1)) + &
3458                              scaleFactorBottom*log(P_M(colIndex, lev_M))
3459        P_T(colIndex, lev_T) = exp(logP)
3460
3461      end do
3462    end do do_onAllColumns
3463
3464    deallocate(tv)
3465
3466    call msg('calcPressure_col_nl_vcode2100x (czp)', 'END', verb_opt=4)
3467  end subroutine calcPressure_col_nl_vcode2100x
3468
3469  !---------------------------------------------------------
3470  ! calcPressure_col_nl_vcode5xxx
3471  !---------------------------------------------------------
3472  subroutine calcPressure_col_nl_vcode5xxx(column, P_T, P_M)
3473    implicit none
3474
3475    ! Arguments:
3476    type(struct_columnData),  intent(in)    :: column   ! reference column
3477    real(8), pointer,         intent(inout) :: P_T(:,:) ! computed column pressure values on thermodynamic levels
3478    real(8), pointer,         intent(inout) :: P_M(:,:) ! computed column pressure values on momentum levels
3479
3480    ! Locals:
3481    real(kind=8), allocatable :: Psfc(:,:), PsfcLS(:,:)
3482    real(kind=8), pointer     :: zppobsM(:,:,:), zppobsT(:,:,:)
3483    integer :: headerIndex, Vcode
3484
3485    call msg('calcPressure_col_nl_vcode5xxx (czp)', 'START', verb_opt=4)
3486    if ( col_getNumCol(column) <= 0 ) then
3487      call msg('calcPressure_col_nl_vcode5xxx (czp)',&
3488           'END (number of columns <= 0)', verb_opt=2)
3489      return
3490    end if
3491
3492    Vcode = col_getVco(column)%vcode
3493
3494    if (.not.col_varExist(column,'P0')) then
3495      call utl_abort('calcPressure_col_nl (czp): P0 must be present as an analysis variable!')
3496    end if
3497
3498    allocate(Psfc(1,col_getNumCol(column)))
3499    do headerIndex = 1,col_getNumCol(column)
3500      Psfc(1,headerIndex) = col_getElem(column,1,headerIndex,'P0')
3501    end do
3502
3503    if (Vcode == 5100) then
3504      if (.not.col_varExist(column,'P0LS')) then
3505        call utl_abort('calcPressure_col_nl (czp): P0LS must be present as an analysis variable!')
3506      end if
3507
3508      allocate(PsfcLS(1,col_getNumCol(column)))
3509      do headerIndex = 1,col_getNumCol(column)
3510        PsfcLS(1,headerIndex) = col_getElem(column,1,headerIndex,'P0LS')
3511      end do
3512
3513      call fetch3DLevels_r8(column%vco, Psfc ,sfcFldLS_opt=PsfcLS, &
3514                            fldM_opt=zppobsM, fldT_opt=zppobsT)
3515      deallocate(PsfcLS)
3516    else
3517      call fetch3DLevels_r8(column%vco, Psfc ,fldM_opt=zppobsM, fldT_opt=zppobsT)
3518    end if
3519    P_M(:,:) = zppobsM(1,:,:)
3520    P_T(:,:) = zppobsT(1,:,:)
3521    deallocate(zppobsM, zppobsT)
3522    deallocate(Psfc)
3523
3524    call msg('calcPressure_col_nl_vcode5xxx (czp)', 'END', verb_opt=4)
3525  end subroutine calcPressure_col_nl_vcode5xxx
3526
3527
3528  !---------------------------------------------------------
3529  ! calcPressure_col_tl
3530  !---------------------------------------------------------
3531  subroutine calcPressure_col_tl(columnInc, columnIncRef)
3532    !
3533    !:Purpose: Tangent pressure computation on the column.
3534    !
3535    implicit none
3536
3537    ! Arguments:
3538    type(struct_columnData), intent(inout) :: columnInc    ! column that will contain the P_T/P_M increments
3539    type(struct_columnData), intent(in)    :: columnIncRef ! column containing needed reference fields
3540
3541    ! Locals:
3542    integer :: Vcode
3543
3544    call msg('calcPressure_col_tl (czp)', 'START', verb_opt=2)
3545
3546    Vcode = col_getVco(columnIncRef)%vcode
3547    if (Vcode == 5005 .or. Vcode == 5002) then
3548      if ( .not. col_varExist(columnInc,'P_*')  ) then
3549        call utl_abort('calcPressure_col_tl (czp): for vcode 5xxx, variables P_M and P_T must be allocated in column')
3550      end if
3551      if ( .not. col_varExist(columnInc,'P0')  ) then
3552        call utl_abort('calcPressure_col_tl (czp): for vcode 5xxx, variable P0 must be allocated in column')
3553      end if
3554      call calcPressure_col_tl_vcode5xxx
3555    else if (Vcode == 21001) then
3556      ! Development notes (@mad001)
3557      !   probably some some gsv_varExist(statevector,.) needed for GEM-H
3558      call calcPressure_col_tl_vcode2100x
3559    else
3560      call utl_abort('calcPressure_col_tl (czp): not implemented')
3561    end if
3562
3563    call msg('calcPressure_col_tl (czp)', 'END', verb_opt=2)
3564
3565    contains
3566      !---------------------------------------------------------
3567      ! calcPressure_col_tl_vcode2100x
3568      !---------------------------------------------------------
3569      subroutine calcPressure_col_tl_vcode2100x
3570        implicit none
3571
3572        call utl_abort('calcPressure_col_tl (czp): vcode 21001 not implemented yet')
3573
3574      end subroutine calcPressure_col_tl_vcode2100x
3575
3576      !---------------------------------------------------------
3577      ! calcPressure_col_tl_vcode5xxx
3578      !---------------------------------------------------------
3579      subroutine calcPressure_col_tl_vcode5xxx
3580        implicit none
3581
3582        ! Locals:
3583        real(8)          :: Psfc
3584        real(8), pointer :: delPsfc(:,:), PsfcRef(:,:)
3585        real(8), pointer :: delP_T(:,:), delP_M(:,:)
3586        real(8), pointer :: dP_dPsfc_T(:), dP_dPsfc_M(:)
3587        integer          :: status, colIndex
3588        integer          :: lev_M, lev_T, nlev_T, nlev_M, numColumns
3589
3590        call msg('calcPressure_col_tl_vcode5xxx (czp)', 'START', verb_opt=4)
3591
3592        nullify(dP_dPsfc_T)
3593        nullify(dP_dPsfc_M)
3594        nullify(delPsfc)
3595        nullify(delP_T)
3596        nullify(delP_M)
3597
3598        delP_M  => col_getAllColumns(columnInc,'P_M')
3599        delP_T  => col_getAllColumns(columnInc,'P_T')
3600        delPsfc => col_getAllColumns(columnInc,'P0')
3601        PsfcRef => col_getAllColumns(columnIncRef,'P0')
3602
3603        nlev_T = col_getNumLev(columnInc,'TH')
3604        nlev_M = col_getNumLev(columnInc,'MM')
3605        numColumns = col_getNumCol(columnInc)
3606
3607        do colIndex = 1, numColumns
3608
3609          Psfc = PsfcRef(1,colIndex)
3610
3611          ! dP_dPsfc_M
3612          nullify(dP_dPsfc_M)
3613          status = vgd_dpidpis(columnInc%vco%vgrid, &
3614                               columnInc%vco%ip1_M, &
3615                               dP_dPsfc_M, &
3616                               Psfc)
3617          if( status .ne. VGD_OK ) then
3618              call utl_abort('calcPressure_col_tl (czp): ERROR with vgd_dpidpis')
3619          end if
3620          ! calculate delP_M
3621          do lev_M = 1, nlev_M
3622            delP_M(lev_M,colIndex) = dP_dPsfc_M(lev_M) * delPsfc(1,colIndex)
3623          end do
3624          deallocate(dP_dPsfc_M)
3625
3626          ! dP_dPsfc_T
3627          nullify(dP_dPsfc_T)
3628          status = vgd_dpidpis(columnInc%vco%vgrid, &
3629                               columnInc%vco%ip1_T, &
3630                               dP_dPsfc_T, &
3631                               Psfc)
3632          if( status .ne. VGD_OK ) then
3633              call utl_abort('calcPressure_col_tl (czp): ERROR with vgd_dpidpis')
3634          end if
3635          ! calculate delP_T
3636          do lev_T = 1, nlev_T
3637            delP_T(lev_T,colIndex) = dP_dPsfc_T(lev_T) * delPsfc(1,colIndex)
3638          end do
3639          deallocate(dP_dPsfc_T)
3640
3641        end do
3642
3643        call msg('calcPressure_col_tl_vcode5xxx (czp)', 'END', verb_opt=4)
3644      end subroutine calcPressure_col_tl_vcode5xxx
3645
3646  end subroutine calcPressure_col_tl
3647
3648  !---------------------------------------------------------
3649  ! calcPressure_col_ad
3650  !---------------------------------------------------------
3651  subroutine calcPressure_col_ad( columnInc, columnIncRef)
3652    !
3653    !:Purpose: Adjoint of pressure computation on the column.
3654    !
3655    implicit none
3656
3657    ! Arguments:
3658    type(struct_columnData), intent(inout) :: columnInc    ! column that will contain increments of P_M/P_T
3659    type(struct_columnData), intent(in)    :: columnIncRef ! column containing needed reference fields
3660
3661    ! Locals:
3662    integer :: Vcode
3663
3664    call msg('calcPressure_col_ad (czp)', 'START', verb_opt=2)
3665
3666    Vcode = col_getVco(columnIncRef)%vcode
3667    if (Vcode == 5005 .or. Vcode == 5002) then
3668      if ( .not. col_varExist(columnInc,'P_*')  ) then
3669        call utl_abort('calcPressure_col_ad (czp): for vcode 5xxx, variables P_M and P_T must be allocated in column')
3670      end if
3671      if ( .not. col_varExist(columnInc,'P0')  ) then
3672        call utl_abort('calcPressure_col_ad (czp): for vcode 5xxx, variable P0 must be allocated in column')
3673      end if
3674      call calcPressure_col_ad_vcode5xxx
3675    else if (Vcode == 21001) then
3676      ! Development notes (@mad001)
3677      !   probably some some gsv_varExist(statevector,.) needed for GEM-H
3678      call calcPressure_col_ad_vcode2100x
3679    else
3680      call utl_abort('calcPressure_col_ad (czp): not implemented')
3681    end if
3682
3683    call msg('calcPressure_col_ad (czp)', 'END', verb_opt=2)
3684
3685    contains
3686      !---------------------------------------------------------
3687      ! calcPressure_col_ad_vcode2100x
3688      !---------------------------------------------------------
3689      subroutine calcPressure_col_ad_vcode2100x
3690        implicit none
3691
3692        call utl_abort('calcPressure_col_ad (czp): vcode 21001 not implemented yet')
3693
3694      end subroutine calcPressure_col_ad_vcode2100x
3695
3696      !---------------------------------------------------------
3697      ! calcPressure_col_ad_vcode5xxx
3698      !---------------------------------------------------------
3699      subroutine calcPressure_col_ad_vcode5xxx
3700        implicit none
3701
3702        ! Locals:
3703        real(8)          :: Psfc
3704        real(8), pointer :: delPsfc(:,:), PsfcRef(:,:)
3705        real(8), pointer :: delP_T(:,:), delP_M(:,:)
3706        real(8), pointer :: dP_dPsfc_T(:), dP_dPsfc_M(:)
3707        integer          :: status, colIndex
3708        integer          :: lev_M, lev_T, nlev_T, nlev_M, numColumns
3709
3710        call msg('calcPressure_col_ad_vcode5xxx (czp)', 'START', verb_opt=4)
3711
3712        nullify(delPsfc)
3713        nullify(PsfcRef)
3714        nullify(delP_T)
3715        nullify(delP_M)
3716        nullify(dP_dPsfc_T)
3717        nullify(dP_dPsfc_M)
3718
3719        delP_M  => col_getAllColumns(columnInc,'P_M')
3720        delP_T  => col_getAllColumns(columnInc,'P_T')
3721        delPsfc => col_getAllColumns(columnInc,'P0')
3722        PsfcRef => col_getAllColumns(columnIncRef,'P0')
3723
3724        nlev_T = col_getNumLev(columnInc,'TH')
3725        nlev_M = col_getNumLev(columnInc,'MM')
3726        numColumns = col_getNumCol(columnInc)
3727
3728        do colIndex = 1, numColumns
3729
3730          Psfc = PsfcRef(1,colIndex)
3731
3732          ! dP_dPsfc_M
3733          nullify(dP_dPsfc_M)
3734          status = vgd_dpidpis(columnInc%vco%vgrid, &
3735                               columnInc%vco%ip1_M, &
3736                               dP_dPsfc_M, &
3737                               Psfc)
3738          if( status .ne. VGD_OK ) then
3739              call utl_abort('calcPressure_col_ad (czp): ERROR with vgd_dpidpis')
3740          end if
3741          ! calculate delP_M
3742          do lev_M = 1, nlev_M
3743            delPsfc(1,colIndex) = delPsfc(1,colIndex) + &
3744                 dP_dPsfc_M(lev_M) * delP_M(lev_M,colIndex)
3745          end do
3746          deallocate(dP_dPsfc_M)
3747
3748          ! dP_dPsfc_T
3749          nullify(dP_dPsfc_T)
3750          status = vgd_dpidpis(columnInc%vco%vgrid, &
3751                               columnInc%vco%ip1_T, &
3752                               dP_dPsfc_T, &
3753                               Psfc)
3754          if( status .ne. VGD_OK ) then
3755            call utl_abort('calcPressure_col_ad (czp): ERROR with vgd_dpidpis')
3756          end if
3757          ! calculate delP_T
3758          do lev_T = 1, nlev_T
3759            delPsfc(1,colIndex) = &
3760                delPsfc(1,colIndex) + dP_dPsfc_T(lev_T) * delP_T(lev_T,colIndex)
3761          end do
3762          deallocate(dP_dPsfc_T)
3763
3764        end do
3765        call msg('calcPressure_col_ad_vcode5xxx (czp)', 'END', verb_opt=4)
3766      end subroutine calcPressure_col_ad_vcode5xxx
3767
3768  end subroutine calcPressure_col_ad
3769
3770  !---------------------------------------------------------------------
3771  ! subroutines wrapping vgd_levels and vgd_dpidpis queries
3772  !---------------------------------------------------------------------
3773
3774  !---------------------------------------------------------
3775  ! fetch3DLevels_r8
3776  !---------------------------------------------------------
3777  subroutine fetch3DLevels_r8(vco, sfcFld, sfcFldLS_opt, fldM_opt, fldT_opt)
3778    !
3779    !:Purpose: Main vgd_levels wrapper for field queries. Return vertical coordinate
3780    !           fields for both momentum and thermodynamic levels; real(8) flavor.
3781    !
3782    implicit none
3783
3784    ! Arguments:
3785    type(struct_vco),           intent(in)    :: vco              ! Vertical descriptor
3786    real(8),                    intent(in)    :: sfcFld(:,:)      ! Surface field reference for coordinate
3787    real(8), optional,          intent(in)    :: sfcFldLS_opt(:,:)! Large scale surface field reference for coordinate (SLEVE)
3788    real(8), optional, pointer, intent(inout) :: fldM_opt(:,:,:)  ! Momemtum levels field
3789    real(8), optional, pointer, intent(inout) :: fldT_opt(:,:,:)  ! Thermodynamic levels field
3790
3791    ! Locals:
3792    integer :: status
3793
3794    if ( minval(sfcFld) <=0 ) then
3795      if ( vco%vcode == 21001 ) then
3796          call msg('fetch3DLevels_r8','WARNING negative surface height reference')
3797      else
3798          call utl_abort('fetch3DLevels_r8: negative surface reference')
3799      end if
3800    end if
3801
3802    if (present(fldM_opt)) then
3803      nullify(fldM_opt)
3804      if (vco%vcode == 5100) then
3805        if (.not. present(sfcFldLS_opt) ) then
3806          call utl_abort('fetch3DLevels_r8: require sfcFldLS_opt for SLEVE')
3807        end if
3808        status = vgd_levels(vco%vgrid, ip1_list=vco%ip1_M, &
3809                            levels=fldM_opt, &
3810                            sfc_field=sfcFld, sfc_field_ls=sfcFldLS_opt, &
3811                            in_log=.false.)
3812      else
3813        status = vgd_levels(vco%vgrid, ip1_list=vco%ip1_M, &
3814                            levels=fldM_opt, sfc_field=sfcFld, in_log=.false.)
3815      end if
3816      if ( status .ne. VGD_OK ) then
3817        call utl_abort('fetch3DLevels_r8:  ERROR with vgd_levels (momentum levels)')
3818      end if
3819    end if
3820
3821    if (present(fldT_opt)) then
3822      nullify(fldT_opt)
3823      if (vco%vcode == 5100) then
3824        if (.not. present(sfcFldLS_opt) ) then
3825          call utl_abort('fetch3DLevels_r8: require sfcFldLS_opt for SLEVE')
3826        end if
3827        status = vgd_levels(vco%vgrid, ip1_list=vco%ip1_T, &
3828                            levels=fldT_opt, &
3829                            sfc_field=sfcFld, sfc_field_ls=sfcFldLS_opt, &
3830                            in_log=.false.)
3831      else
3832        status = vgd_levels(vco%vgrid, ip1_list=vco%ip1_T, &
3833                            levels=fldT_opt, sfc_field=sfcFld, in_log=.false.)
3834      end if
3835      if ( status .ne. VGD_OK ) then
3836        call utl_abort('fetch3DLevels_r8:  ERROR with vgd_levels (thermodynamic levels)')
3837      end if
3838    end if
3839  end subroutine fetch3DLevels_r8
3840
3841  !---------------------------------------------------------
3842  ! fetch3DLevels_r4
3843  !---------------------------------------------------------
3844  subroutine fetch3DLevels_r4(vco, sfcFld, sfcFldLS_opt, fldM_opt, fldT_opt)
3845    !
3846    !:Purpose: Main vgd_levels wrapper for field query. Return vertical coordinate
3847    !           fields for both momentum and thermodynamic levels; real(4) flavor.
3848    !
3849    implicit none
3850
3851    ! Arguments:
3852    type(struct_vco),           intent(in)    :: vco              ! Vertical descriptor
3853    real(4),                    intent(in)    :: sfcFld(:,:)      ! Surface field reference for coordinate
3854    real(4), optional,          intent(in)    :: sfcFldLS_opt(:,:)! Large scale surface field reference for coordinate (SLEVE)
3855    real(4), optional, pointer, intent(inout) :: fldM_opt(:,:,:)  ! Momemtum levels field
3856    real(4), optional, pointer, intent(inout) :: fldT_opt(:,:,:)  ! Thermodynamic levels field
3857
3858    ! Locals:
3859    integer :: status
3860
3861    if ( minval(sfcFld) <=0 ) then
3862      if ( vco%vcode == 21001 ) then
3863          call msg('fetch3DLevels_r4','WARNING negative surface height reference')
3864      else
3865          call utl_abort('fetch3DLevels_r4: negative surface reference')
3866      end if
3867    end if
3868
3869    if (present(fldM_opt)) then
3870      nullify(fldM_opt)
3871      if (vco%vcode == 5100) then
3872        if (.not. present(sfcFldLS_opt) ) then
3873          call utl_abort('fetch3DLevels_r4: require sfcFldLS_opt for SLEVE')
3874        end if
3875        status = vgd_levels(vco%vgrid, ip1_list=vco%ip1_M, &
3876                            levels=fldM_opt, &
3877                            sfc_field=sfcFld, sfc_field_ls=sfcFldLS_opt, &
3878                            in_log=.false.)
3879      else
3880        status = vgd_levels(vco%vgrid, ip1_list=vco%ip1_M, &
3881                            levels=fldM_opt, sfc_field=sfcFld, in_log=.false.)
3882      end if
3883      if ( status .ne. VGD_OK ) then
3884        call utl_abort('fetch3DLevels_r4:  ERROR with vgd_levels (momentum levels)')
3885      end if
3886    end if
3887
3888    if (present(fldT_opt)) then
3889      nullify(fldT_opt)
3890      if (vco%vcode == 5100) then
3891        if (.not. present(sfcFldLS_opt) ) then
3892          call utl_abort('fetch3DLevels_r4: require sfcFldLS_opt for SLEVE')
3893        end if
3894        status = vgd_levels(vco%vgrid, ip1_list=vco%ip1_T, &
3895                            levels=fldT_opt, &
3896                            sfc_field=sfcFld, sfc_field_ls=sfcFldLS_opt, &
3897                            in_log=.false.)
3898      else
3899        status = vgd_levels(vco%vgrid, ip1_list=vco%ip1_T, &
3900                            levels=fldT_opt, sfc_field=sfcFld, in_log=.false.)
3901      end if
3902      if ( status .ne. VGD_OK ) then
3903        call utl_abort('fetch3DLevels_r4:  ERROR with vgd_levels (thermodynamic levels)')
3904      end if
3905    end if
3906  end subroutine fetch3DLevels_r4
3907
3908  !---------------------------------------------------------
3909  ! fetch1DLevels_r8
3910  !---------------------------------------------------------
3911  subroutine fetch1DLevels_r8(vco, sfcValue, profM_opt, profT_opt)
3912    !
3913    !:Purpose: Main vgd_levels wrapper for profile query. Return vertical coordinate
3914    !           profile for both momentum and thermodynamic levels; real(8) flavor.
3915    !
3916    implicit none
3917
3918    ! Arguments:
3919    type(struct_vco),           intent(in)    :: vco          ! Vertical descriptor
3920    real(8),                    intent(in)    :: sfcValue     ! Surface field reference for coordinate
3921    real(8), pointer, optional, intent(inout) :: profM_opt(:) ! Momemtum levels profile
3922    real(8), pointer, optional, intent(inout) :: profT_opt(:) ! Thermodynamic levels profile
3923
3924    ! Locals:
3925    integer :: status
3926
3927    if ( sfcValue <=0 ) then
3928      if ( vco%vcode == 21001 ) then
3929          call msg('fetch1DLevels_r8','WARNING negative surface height reference')
3930      else
3931        call utl_abort('fetch1DLevels_r8: negative surface reference')
3932      end if
3933    end if
3934
3935    if (present(profM_opt)) then
3936      nullify(profM_opt)
3937      status = vgd_levels(vco%vgrid, ip1_list=vco%ip1_M, &
3938                          levels=profM_opt, sfc_field=sfcValue, in_log=.false.)
3939      if ( status .ne. VGD_OK ) then
3940        call utl_abort('fetch1DLevels_r8:  ERROR with vgd_levels (momentum levels)')
3941      end if
3942    end if
3943
3944    if (present(profT_opt)) then
3945      nullify(profT_opt)
3946      status = vgd_levels(vco%vgrid, ip1_list=vco%ip1_T, &
3947                          levels=profT_opt, sfc_field=sfcValue, in_log=.false.)
3948      if ( status .ne. VGD_OK ) then
3949        call utl_abort('fetch1DLevels_r8:  ERROR with vgd_levels (thermodynamic levels)')
3950      end if
3951    end if
3952  end subroutine fetch1DLevels_r8
3953
3954  !---------------------------------------------------------
3955  ! fetch1DdPdPs_r8
3956  !---------------------------------------------------------
3957  subroutine fetch1DdPdPs_r8(vco, sfcValue, profM_opt, profT_opt)
3958    !
3959    !:Purpose: Main vgd_levels wrapper for iderivative profile query. Return vertical
3960    !           coordinate profile for both momentum and thermodynamic levels; 
3961    !           real(8) flavor.
3962    !
3963    implicit none
3964
3965    ! Arguments:
3966    type(struct_vco),           intent(in)    :: vco          ! Vertical descriptor
3967    real(8),                    intent(in)    :: sfcValue     ! Surface field reference for coordinate
3968    real(8), pointer, optional, intent(inout) :: profM_opt(:) ! Momemtum levels profile
3969    real(8), pointer, optional, intent(inout) :: profT_opt(:) ! Thermodynamic levels profile
3970
3971    ! Locals:
3972    integer :: status
3973
3974    if ( sfcValue <=0 ) then
3975      if ( vco%vcode == 21001 ) then
3976          call msg('fetch1DdPdPs_r8','WARNING negative surface height reference')
3977      else
3978        call utl_abort('fetch1DdPdPs_r8: negative surface reference')
3979      end if
3980    end if
3981
3982    if (present(profM_opt)) then
3983      nullify(profM_opt)
3984      status = vgd_dpidpis(vco%vgrid, vco%ip1_M, profM_opt, sfcValue)
3985      if ( status .ne. VGD_OK ) then
3986        call utl_abort('fetch1DdPdPs_r8:  ERROR with vgd_dpidpis (momentum levels)')
3987      end if
3988    end if
3989
3990    if (present(profT_opt)) then
3991      nullify(profT_opt)
3992      status = vgd_dpidpis(vco%vgrid, vco%ip1_T, profT_opt, sfcValue)
3993      if ( status .ne. VGD_OK ) then
3994        call utl_abort('fetch1DdPdPs_r8:  ERROR with vgd_dpidpis (thermodynamic levels)')
3995      end if
3996    end if
3997  end subroutine fetch1DdPdPs_r8
3998
3999  !---------------------------------------------------------------------
4000  ! other vertical coordinate related functions and subroutines
4001  !---------------------------------------------------------------------
4002
4003  !--------------------------------------------------------------------------
4004  ! czp_ensureCompatibleTops
4005  !--------------------------------------------------------------------------
4006  subroutine czp_ensureCompatibleTops(vco_sourceGrid,vco_destGrid)
4007    !
4008    !:Purpose: This function checks if the top of a destination grid
4009    !           is ~equal or lower than the top of the source grid
4010    !           the code aborts if this is not the case
4011    !
4012    implicit none
4013
4014    ! arguments:
4015    type(struct_vco), pointer, intent(in) :: vco_sourceGrid ! vertical coordinate source grid
4016    type(struct_vco), pointer, intent(in) :: vco_destGrid   ! vertical coordinate destination grid
4017
4018    ! locals:
4019    integer :: nAbove, numLevSource, numLevDest
4020    real(8) :: sourceModelTop
4021    real(8)           :: pSfc(1,1), pSfcLS(1,1)
4022    real(8), pointer  :: sourcePressureLevels(:,:,:)
4023    real(8), pointer  :: destPressureLevels(:,:,:)
4024
4025    nullify(sourcePressureLevels)
4026    nullify(destPressureLevels)
4027
4028    numLevSource = vco_getNumLev(vco_sourceGrid,'MM')
4029    numLevDest   = vco_getNumLev(vco_destGrid,'MM')
4030    if (numLevSource == 1 .and. numLevDest == 1) then
4031      write(*,*) 'czp_ensureCompatibleTops: both grids only have 1 level, skip the test'
4032      return
4033    end if
4034
4035    ! dummy pressure value
4036    pSfc(1,1) = 100.0D3 !100 kPa
4037    pSfcLS(1,1) = 100.0D3 !100 kPa
4038
4039    ! pressure on momentum levels of source grid
4040    if (vco_sourceGrid%vcode == 5100) then
4041      call fetch3DLevels_r8(vco_sourceGrid, pSfc, sfcFldLS_opt=pSfcLS, &
4042                            fldM_opt=sourcePressureLevels)
4043    else
4044      call fetch3DLevels_r8(vco_sourceGrid, pSfc, fldM_opt=sourcePressureLevels)
4045    end if
4046    ! pressure on momentum levels of destination grid
4047    if (vco_destGrid%vcode == 5100) then
4048      call fetch3DLevels_r8(vco_destGrid, pSfc, sfcFldLS_opt=pSfcLS, &
4049                            fldM_opt=destPressureLevels)
4050    else
4051      call fetch3DLevels_r8(vco_destGrid, pSfc, fldM_opt=destPressureLevels)
4052    end if
4053
4054    ! count number of levels where output grid is higher than input grid
4055    sourceModelTop = sourcePressureLevels(1,1,1)
4056    nAbove=0
4057    do while (sourceModelTop > destPressureLevels(1,1,nAbove+1))
4058      nAbove = nAbove + 1
4059    end do
4060
4061    ! Destination grid has "nAbove" levels above source grid;  tolerate one
4062    if ( nAbove > 1 ) then
4063      write(*,*) 'czp_ensureCompatibleTops: numLevSource/Dest    = ', numLevSource, numLevDest
4064      write(*,*) 'czp_ensureCompatibleTops: sourcePressureLevels = ', sourcePressureLevels(1,1,:)
4065      write(*,*) 'czp_ensureCompatibleTops: destPressureLevels   = ', destPressureLevels(1,1,:)
4066      call utl_abort('czp_ensureCompatibleTops: top of destination grid more than one level higher than top of source grid')
4067    end if
4068
4069    deallocate(sourcePressureLevels)
4070    deallocate(destPressureLevels)
4071
4072  end subroutine czp_ensureCompatibleTops
4073
4074  !---------------------------------------------------------------------
4075  ! helper private functions and subroutines
4076  !---------------------------------------------------------------------
4077
4078  !---------------------------------------------------------
4079  ! calcHeightCoeff_gsv
4080  !---------------------------------------------------------
4081  subroutine calcHeightCoeff_gsv(statevector)
4082    !
4083    !:Purpose: Calculating the coefficients of height for
4084    !           czp_calcHeight_tl/czp_calcHeight_ad
4085    !
4086    implicit none
4087
4088    ! Arguments:
4089    type(struct_gsv), intent(in) :: statevector
4090
4091    ! Locals:
4092    integer :: lev_T,nlev_M,nlev_T,numStep,stepIndex,latIndex,lonIndex,Vcode
4093    real(8) :: hu,tt,Pr,height_T,cmp,cmp_TT,cmp_HU,cmp_P0_1,cmp_P0_2,ratioP1
4094    real(4) :: lat_4
4095    real(8) :: Rgh, sLat, lat_8
4096    real(8), pointer :: hu_ptr(:,:,:,:),tt_ptr(:,:,:,:)
4097    real(8), pointer :: P_T_ptr(:,:,:,:),P_M_ptr(:,:,:,:)
4098    real(8), pointer :: height_T_ptr(:,:,:,:)
4099    type(struct_vco), pointer :: vco
4100    logical, save :: firstTimeHeightCoeff_gsv = .true.
4101
4102    if ( .not. firstTimeHeightCoeff_gsv ) return
4103
4104    call msg('calcHeightCoeff_gsv (czp)', 'START', verb_opt=2)
4105
4106    ! initialize and save coefficients for increased efficiency
4107    ! (assumes no relinearization)
4108    firstTimeHeightCoeff_gsv = .false.
4109
4110    vco => gsv_getVco(statevector)
4111    Vcode = vco%vcode
4112
4113    nlev_T = gsv_getNumLev(statevector,'TH')
4114    nlev_M = gsv_getNumLev(statevector,'MM')
4115    numStep = statevector%numstep
4116
4117    ! saved arrays
4118    allocate(coeff_M_TT_gsv(&
4119        statevector%myLonBeg:statevector%myLonEnd, &
4120        statevector%myLatBeg:statevector%myLatEnd, &
4121        nlev_T,numStep))
4122    allocate(coeff_M_HU_gsv(&
4123        statevector%myLonBeg:statevector%myLonEnd, &
4124        statevector%myLatBeg:statevector%myLatEnd, &
4125        nlev_T,numStep))
4126    allocate(coeff_M_P0_delPM_gsv(&
4127        statevector%myLonBeg:statevector%myLonEnd, &
4128        statevector%myLatBeg:statevector%myLatEnd, &
4129        nlev_T,numStep))
4130    allocate(coeff_M_P0_dP_delPT_gsv(&
4131        statevector%myLonBeg:statevector%myLonEnd, &
4132        statevector%myLatBeg:statevector%myLatEnd, &
4133        nlev_T,numStep))
4134    allocate(coeff_M_P0_dP_delP0_gsv(&
4135        statevector%myLonBeg:statevector%myLonEnd, &
4136        statevector%myLatBeg:statevector%myLatEnd, &
4137        nlev_T,numStep))
4138
4139    allocate(coeff_T_TT_gsv(&
4140        statevector%myLonBeg:statevector%myLonEnd, &
4141        statevector%myLatBeg:statevector%myLatEnd, &
4142        numStep))
4143    allocate(coeff_T_HU_gsv(&
4144        statevector%myLonBeg:statevector%myLonEnd, &
4145        statevector%myLatBeg:statevector%myLatEnd, &
4146        numStep))
4147    allocate(coeff_T_P0_delP1_gsv(&
4148        statevector%myLonBeg:statevector%myLonEnd, &
4149        statevector%myLatBeg:statevector%myLatEnd, &
4150        numStep))
4151    allocate(coeff_T_P0_dP_delPT_gsv(&
4152        statevector%myLonBeg:statevector%myLonEnd, &
4153        statevector%myLatBeg:statevector%myLatEnd, &
4154        numStep))
4155    allocate(coeff_T_P0_dP_delP0_gsv(&
4156        statevector%myLonBeg:statevector%myLonEnd, &
4157        statevector%myLatBeg:statevector%myLatEnd, &
4158        numStep))
4159
4160    coeff_M_TT_gsv(:,:,:,:) = 0.0D0
4161    coeff_M_HU_gsv(:,:,:,:) = 0.0D0
4162
4163    coeff_M_P0_delPM_gsv(:,:,:,:) = 0.0D0
4164
4165    coeff_M_P0_dP_delPT_gsv(:,:,:,:) = 0.0D0
4166    coeff_M_P0_dP_delP0_gsv(:,:,:,:) = 0.0D0
4167
4168    coeff_T_TT_gsv(:,:,:) = 0.0D0
4169    coeff_T_HU_gsv(:,:,:) = 0.0D0
4170
4171    coeff_T_P0_delP1_gsv(:,:,:) = 0.0D0
4172
4173    coeff_T_P0_dP_delPT_gsv(:,:,:) = 0.0D0
4174    coeff_T_P0_dP_delP0_gsv(:,:,:) = 0.0D0
4175
4176    call gsv_getField(statevector,hu_ptr,'HU')
4177    call gsv_getField(statevector,tt_ptr,'TT')
4178    call gsv_getField(statevector,P_T_ptr,'P_T')
4179    call gsv_getField(statevector,P_M_ptr,'P_M')
4180    call gsv_getField(statevector,height_T_ptr,'Z_T')
4181
4182    if_calcHeightCoeff_gsv_vcodes : if (Vcode == 5002) then
4183
4184      do stepIndex = 1, numStep
4185        do lev_T = 1, (nlev_T-1)
4186          do latIndex = statevector%myLatBeg, statevector%myLatEnd
4187            do lonIndex = statevector%myLonBeg, statevector%myLonEnd
4188              if ( lev_T == 1 ) then
4189                ! compute height coefficients on only the top thermo level
4190                ratioP1 = log( P_M_ptr(lonIndex,latIndex,1,stepIndex) / &
4191                               P_T_ptr(lonIndex,latIndex,1,stepIndex) )
4192                hu = max(hu_ptr(lonIndex,latIndex,1,stepIndex),&
4193                                MPC_MINIMUM_HU_R8)
4194                tt = tt_ptr(lonIndex,latIndex,1,stepIndex)
4195                Pr = P_T_ptr(lonIndex,latIndex,1,stepIndex)
4196                height_T = height_T_ptr(lonIndex,latIndex,1,stepIndex)
4197
4198                cmp = gpscompressibility(Pr,tt,hu)
4199                cmp_TT = gpscompressibility_TT(Pr,tt,hu)
4200                cmp_HU = gpscompressibility_HU(Pr,tt,hu)
4201                cmp_P0_1 = gpscompressibility_P0_1(Pr,tt,hu,1.0d0)
4202                cmp_P0_2 = gpscompressibility_P0_2(Pr,tt,hu)
4203
4204                ! Gravity acceleration
4205                lat_4 = statevector%hco%lat2d_4(lonIndex,latIndex)
4206                lat_8 = real(lat_4,8)
4207                sLat = sin(lat_8)
4208                Rgh = phf_gravityalt(sLat, height_T)
4209
4210                coeff_T_TT_gsv(lonIndex,latIndex,stepIndex) = &
4211                    (MPC_RGAS_DRY_AIR_R8 / Rgh) * (phf_fottva(hu,1.0D0) * cmp + &
4212                    phf_fotvt8(tt,hu) * cmp_TT) * ratioP1
4213
4214                coeff_T_HU_gsv(lonIndex,latIndex,stepIndex) = &
4215                    (MPC_RGAS_DRY_AIR_R8 / Rgh) * (phf_folnqva(hu,tt,1.0d0) / &
4216                    hu * cmp + phf_fotvt8(tt,hu) * cmp_HU) * ratioP1
4217
4218                coeff_T_P0_delP1_gsv(lonIndex,latIndex,stepIndex) = &
4219                    (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp
4220
4221                coeff_T_P0_dP_delPT_gsv(lonIndex,latIndex,stepIndex) = &
4222                    (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp_P0_1 * &
4223                    ratioP1
4224
4225                coeff_T_P0_dP_delP0_gsv(lonIndex,latIndex,stepIndex) = &
4226                    (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp_P0_2 * &
4227                    ratioP1
4228              else
4229                ! compute height coefficients on momentum levels
4230                ratioP1 = log( P_M_ptr(lonIndex,latIndex,lev_T  ,stepIndex) / &
4231                               P_M_ptr(lonIndex,latIndex,lev_T-1,stepIndex) )
4232                hu = max( hu_ptr(lonIndex,latIndex,lev_T,stepIndex),&
4233                          MPC_MINIMUM_HU_R8)
4234                tt = tt_ptr(lonIndex,latIndex,lev_T,stepIndex)
4235                Pr = P_T_ptr(lonIndex,latIndex,lev_T,stepIndex)
4236                height_T = height_T_ptr(lonIndex,latIndex,lev_T,stepIndex)
4237
4238                cmp = gpscompressibility(Pr,tt,hu)
4239                cmp_TT = gpscompressibility_TT(Pr,tt,hu)
4240                cmp_HU = gpscompressibility_HU(Pr,tt,hu)
4241                cmp_P0_1 = gpscompressibility_P0_1(Pr,tt,hu,1.0d0)
4242                cmp_P0_2 = gpscompressibility_P0_2(Pr,tt,hu)
4243
4244                ! Gravity acceleration
4245                lat_4 = statevector%hco%lat2d_4(lonIndex,latIndex)
4246                lat_8 = real(lat_4,8)
4247                sLat = sin(lat_8)
4248                Rgh = phf_gravityalt(sLat, height_T)
4249
4250                coeff_M_TT_gsv(lonIndex,latIndex,lev_T,stepIndex) = &
4251                    (MPC_RGAS_DRY_AIR_R8 / Rgh) * (phf_fottva(hu,1.0D0) * cmp + &
4252                    phf_fotvt8(tt,hu) * cmp_TT) * ratioP1
4253
4254                coeff_M_HU_gsv(lonIndex,latIndex,lev_T,stepIndex) = &
4255                    (MPC_RGAS_DRY_AIR_R8 / RgH) * (phf_folnqva(hu,tt,1.0d0) / hu * &
4256                    cmp + phf_fotvt8(tt,hu) * cmp_HU) * ratioP1
4257
4258                coeff_M_P0_delPM_gsv   (lonIndex,latIndex,lev_T,stepIndex) = &
4259                    (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp
4260
4261                coeff_M_P0_dP_delPT_gsv(lonIndex,latIndex,lev_T,stepIndex) = &
4262                    (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp_P0_1 * &
4263                    ratioP1
4264
4265                coeff_M_P0_dP_delP0_gsv(lonIndex,latIndex,lev_T,stepIndex) = &
4266                    (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp_P0_2 * &
4267                    ratioP1
4268              end if
4269            end do
4270          end do
4271        end do
4272      end do
4273
4274    else if (Vcode == 5005) then if_calcHeightCoeff_gsv_vcodes
4275
4276      do stepIndex = 1, numStep
4277        do lev_T = 1, (nlev_T-1)
4278          do latIndex = statevector%myLatBeg, statevector%myLatEnd
4279            do lonIndex = statevector%myLonBeg, statevector%myLonEnd
4280              ! compute height coefficients on momentum levels
4281              ratioP1 = log( P_M_ptr(lonIndex,latIndex,lev_T+1,stepIndex) / &
4282                             P_M_ptr(lonIndex,latIndex,lev_T  ,stepIndex) )
4283              hu = max( hu_ptr(lonIndex,latIndex,lev_T,stepIndex),&
4284                        MPC_MINIMUM_HU_R8)
4285              tt = tt_ptr(lonIndex,latIndex,lev_T,stepIndex)
4286              Pr = P_T_ptr(lonIndex,latIndex,lev_T,stepIndex)
4287              height_T = height_T_ptr(lonIndex,latIndex,lev_T,stepIndex)
4288
4289              cmp = gpscompressibility(Pr,tt,hu)
4290              cmp_TT = gpscompressibility_TT(Pr,tt,hu)
4291              cmp_HU = gpscompressibility_HU(Pr,tt,hu)
4292              cmp_P0_1 = gpscompressibility_P0_1(Pr,tt,hu,1.0d0)
4293              cmp_P0_2 = gpscompressibility_P0_2(Pr,tt,hu)
4294
4295              ! Gravity acceleration
4296              lat_4 = statevector%hco%lat2d_4(lonIndex,latIndex)
4297              lat_8 = real(lat_4,8)
4298              sLat = sin(lat_8)
4299              Rgh = phf_gravityalt(sLat, height_T)
4300
4301              coeff_M_TT_gsv(lonIndex,latIndex,lev_T,stepIndex) = &
4302                  (MPC_RGAS_DRY_AIR_R8 / Rgh) * (phf_fottva(hu,1.0D0) * cmp + &
4303                  phf_fotvt8(tt,hu) * cmp_TT) * ratioP1
4304
4305              coeff_M_HU_gsv(lonIndex,latIndex,lev_T,stepIndex) = &
4306                  (MPC_RGAS_DRY_AIR_R8 / RgH) * (phf_folnqva(hu,tt,1.0d0) / hu * &
4307                  cmp + phf_fotvt8(tt,hu) * cmp_HU) * ratioP1
4308
4309              coeff_M_P0_delPM_gsv(lonIndex,latIndex,lev_T,stepIndex) = &
4310                  (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp
4311
4312              coeff_M_P0_dP_delPT_gsv(lonIndex,latIndex,lev_T,stepIndex) = &
4313                  (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp_P0_1 * &
4314                  ratioP1
4315
4316              coeff_M_P0_dP_delP0_gsv(lonIndex,latIndex,lev_T,stepIndex) = &
4317                  (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp_P0_2 * &
4318                  ratioP1
4319            end do
4320          end do
4321        end do
4322      end do
4323
4324    else if_calcHeightCoeff_gsv_vcodes
4325
4326      call utl_abort('calcHeightCoeff_gsv (czp): only vcode 5002 and 5005 implemented')
4327
4328    end if if_calcHeightCoeff_gsv_vcodes
4329
4330    call msg('calcHeightCoeff_gsv (czp)', 'END', verb_opt=2)
4331  end subroutine calcHeightCoeff_gsv
4332
4333  !---------------------------------------------------------
4334  ! calcHeightCoeff_col
4335  !---------------------------------------------------------
4336  subroutine calcHeightCoeff_col(column)
4337    !
4338    !:Purpose: Calculating the coefficients of height for
4339    !           czp_calcHeight_tl/czp_calcHeight_ad
4340    !
4341    implicit none
4342
4343    ! Arguments:
4344    type(struct_columnData), intent(in) :: column
4345
4346    ! Locals:
4347    integer :: lev_T,nlev_M,nlev_T,numColumns,colIndex,Vcode
4348    real(8) :: hu,tt,Pr,height_T,cmp,cmp_TT,cmp_HU,cmp_P0_1,cmp_P0_2,ratioP1
4349    real(8) :: Rgh, sLat, lat_8
4350    real(8), pointer :: hu_ptr(:,:),tt_ptr(:,:)
4351    real(8), pointer :: P_T_ptr(:,:),P_M_ptr(:,:)
4352    real(8), pointer :: height_T_ptr(:,:)
4353    type(struct_vco), pointer :: vco
4354    logical, save :: firstTimeHeightCoeff_col = .true.
4355
4356    if ( .not. firstTimeHeightCoeff_col ) return
4357
4358    call msg('calcHeightCoeff_col (czp)', 'START', verb_opt=2)
4359
4360    ! initialize and save coefficients for increased efficiency
4361    ! (assumes no relinearization)
4362    firstTimeHeightCoeff_col = .false.
4363
4364    vco => col_getVco(column)
4365    Vcode = vco%vcode
4366
4367    nlev_T = col_getNumLev(column,'TH')
4368    nlev_M = col_getNumLev(column,'MM')
4369    numColumns = col_getNumCol(column)
4370
4371    ! saved arrays
4372    allocate(coeff_M_TT_col         (nlev_T,numColumns))
4373    allocate(coeff_M_HU_col         (nlev_T,numColumns))
4374    allocate(coeff_M_P0_delPM_col   (nlev_T,numColumns))
4375    allocate(coeff_M_P0_dP_delPT_col(nlev_T,numColumns))
4376    allocate(coeff_M_P0_dP_delP0_col(nlev_T,numColumns))
4377
4378    allocate(coeff_T_TT_col         (numColumns))
4379    allocate(coeff_T_HU_col         (numColumns))
4380    allocate(coeff_T_P0_delP1_col   (numColumns))
4381    allocate(coeff_T_P0_dP_delPT_col(numColumns))
4382    allocate(coeff_T_P0_dP_delP0_col(numColumns))
4383
4384    coeff_M_TT_col(:,:) = 0.0D0
4385    coeff_M_HU_col(:,:) = 0.0D0
4386
4387    coeff_M_P0_delPM_col(:,:) = 0.0D0
4388
4389    coeff_M_P0_dP_delPT_col(:,:) = 0.0D0
4390    coeff_M_P0_dP_delP0_col(:,:) = 0.0D0
4391
4392    coeff_T_TT_col(:) = 0.0D0
4393    coeff_T_HU_col(:) = 0.0D0
4394
4395    coeff_T_P0_delP1_col(:) = 0.0D0
4396
4397    coeff_T_P0_dP_delPT_col(:) = 0.0D0
4398    coeff_T_P0_dP_delP0_col(:) = 0.0D0
4399
4400    hu_ptr       => col_getAllColumns(column,'HU')
4401    tt_ptr       => col_getAllColumns(column,'TT')
4402    P_T_ptr      => col_getAllColumns(column,'P_T')
4403    P_M_ptr      => col_getAllColumns(column,'P_M')
4404    height_T_ptr => col_getAllColumns(column,'Z_T')
4405
4406    if_calcHeightCoeff_col_vcodes : if (Vcode == 5002) then
4407
4408      do colIndex = 1, numColumns
4409        do lev_T = 1, (nlev_T-1)
4410          if ( lev_T == 1 ) then
4411            ! compute height coefficients on only the top thermo level
4412            ratioP1 = log( P_M_ptr(1,colIndex) / &
4413                           P_T_ptr(1,colIndex) )
4414            hu = max(hu_ptr(1,colIndex),MPC_MINIMUM_HU_R8)
4415            tt = tt_ptr(1,colIndex)
4416            Pr = P_T_ptr(1,colIndex)
4417            height_T = height_T_ptr(1,colIndex)
4418
4419            cmp = gpscompressibility(Pr,tt,hu)
4420            cmp_TT = gpscompressibility_TT(Pr,tt,hu)
4421            cmp_HU = gpscompressibility_HU(Pr,tt,hu)
4422            cmp_P0_1 = gpscompressibility_P0_1(Pr,tt,hu,1.0d0)
4423            cmp_P0_2 = gpscompressibility_P0_2(Pr,tt,hu)
4424
4425            ! Gravity acceleration
4426            lat_8 = column%lat(colIndex)
4427            sLat = sin(lat_8)
4428            Rgh = phf_gravityalt(sLat, height_T)
4429
4430            coeff_T_TT_col(colIndex) = &
4431                (MPC_RGAS_DRY_AIR_R8 / Rgh) * (phf_fottva(hu,1.0D0) * cmp + &
4432                phf_fotvt8(tt,hu) * cmp_TT) * ratioP1
4433
4434            coeff_T_HU_col(colIndex) = &
4435                (MPC_RGAS_DRY_AIR_R8 / Rgh) * (phf_folnqva(hu,tt,1.0d0) / hu * &
4436                cmp + phf_fotvt8(tt,hu) * cmp_HU) * ratioP1
4437
4438            coeff_T_P0_delP1_col   (colIndex) = &
4439                (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp
4440
4441            coeff_T_P0_dP_delPT_col(colIndex) = &
4442                (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp_P0_1 * ratioP1
4443
4444            coeff_T_P0_dP_delP0_col(colIndex) = &
4445                (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp_P0_2 * ratioP1
4446          else
4447            ! compute height coefficients on momentum levels
4448            ratioP1 = log( P_M_ptr(lev_T  ,colIndex) / &
4449                           P_M_ptr(lev_T-1,colIndex) )
4450            hu = max(hu_ptr(lev_T,colIndex),MPC_MINIMUM_HU_R8)
4451            tt = tt_ptr(lev_T,colIndex)
4452            Pr = P_T_ptr(lev_T,colIndex)
4453            height_T = height_T_ptr(lev_T,colIndex)
4454
4455            cmp = gpscompressibility(Pr,tt,hu)
4456            cmp_TT = gpscompressibility_TT(Pr,tt,hu)
4457            cmp_HU = gpscompressibility_HU(Pr,tt,hu)
4458            cmp_P0_1 = gpscompressibility_P0_1(Pr,tt,hu,1.0d0)
4459            cmp_P0_2 = gpscompressibility_P0_2(Pr,tt,hu)
4460
4461            ! Gravity acceleration
4462            lat_8 = column%lat(colIndex)
4463            sLat = sin(lat_8)
4464            Rgh = phf_gravityalt(sLat, height_T)
4465
4466            coeff_M_TT_col(lev_T,colIndex) = &
4467                (MPC_RGAS_DRY_AIR_R8 / Rgh) * (phf_fottva(hu,1.0D0) * cmp + &
4468                phf_fotvt8(tt,hu) * cmp_TT) * ratioP1
4469
4470            coeff_M_HU_col(lev_T,colIndex) = &
4471                (MPC_RGAS_DRY_AIR_R8 / RgH) * (phf_folnqva(hu,tt,1.0d0) / hu * &
4472                cmp + phf_fotvt8(tt,hu) * cmp_HU) * ratioP1
4473
4474            coeff_M_P0_delPM_col(lev_T,colIndex) = &
4475                (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp
4476
4477            coeff_M_P0_dP_delPT_col(lev_T,colIndex) = &
4478                (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp_P0_1 * ratioP1
4479
4480            coeff_M_P0_dP_delP0_col(lev_T,colIndex) = &
4481                (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp_P0_2 * ratioP1
4482          end if
4483        end do
4484      end do
4485
4486    else if (Vcode == 5005) then if_calcHeightCoeff_col_vcodes
4487
4488      do colIndex = 1, numColumns
4489        do lev_T = 1, (nlev_T-1)
4490          ! compute height coefficients on momentum levels
4491          ratioP1 = log( P_M_ptr(lev_T+1,colIndex) / &
4492                         P_M_ptr(lev_T  ,colIndex) )
4493          hu = max(hu_ptr(lev_T,colIndex),MPC_MINIMUM_HU_R8)
4494          tt = tt_ptr(lev_T,colIndex)
4495          Pr = P_T_ptr(lev_T,colIndex)
4496          height_T = height_T_ptr(lev_T,colIndex)
4497
4498          cmp = gpscompressibility(Pr,tt,hu)
4499          cmp_TT = gpscompressibility_TT(Pr,tt,hu)
4500          cmp_HU = gpscompressibility_HU(Pr,tt,hu)
4501          cmp_P0_1 = gpscompressibility_P0_1(Pr,tt,hu,1.0d0)
4502          cmp_P0_2 = gpscompressibility_P0_2(Pr,tt,hu)
4503
4504          ! Gravity acceleration
4505          lat_8 = column%lat(colIndex)
4506          sLat = sin(lat_8)
4507          Rgh = phf_gravityalt(sLat, height_T)
4508
4509          coeff_M_TT_col(lev_T,colIndex) = &
4510              (MPC_RGAS_DRY_AIR_R8 / Rgh) * (phf_fottva(hu,1.0D0) * cmp + &
4511              phf_fotvt8(tt,hu) * cmp_TT) * ratioP1
4512
4513          coeff_M_HU_col(lev_T,colIndex) = &
4514              (MPC_RGAS_DRY_AIR_R8 / RgH) * (phf_folnqva(hu,tt,1.0d0) / hu * cmp + &
4515              phf_fotvt8(tt,hu) * cmp_HU) * ratioP1
4516
4517          coeff_M_P0_delPM_col   (lev_T,colIndex) = &
4518              (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp
4519
4520          coeff_M_P0_dP_delPT_col(lev_T,colIndex) = &
4521              (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp_P0_1 * ratioP1
4522
4523          coeff_M_P0_dP_delP0_col(lev_T,colIndex) = &
4524              (MPC_RGAS_DRY_AIR_R8 / Rgh) * phf_fotvt8(tt,hu) * cmp_P0_2 * ratioP1
4525        end do
4526      end do
4527
4528    else
4529
4530      call utl_abort('calcHeightCoeff_col (czp): only vcode 5002 and 5005 implemented')
4531
4532    end if if_calcHeightCoeff_col_vcodes
4533
4534    call msg('calcHeightCoeff_col (czp)', 'END', verb_opt=2)
4535  end subroutine calcHeightCoeff_col
4536
4537  !---------------------------------------------------------
4538  ! gpscompressibility
4539  !---------------------------------------------------------
4540  function gpscompressibility(p,t,q)
4541    implicit none
4542
4543    ! Arguments:
4544    real(8), intent(in)  :: p
4545    real(8), intent(in)  :: t
4546    real(8), intent(in)  :: q
4547    ! Result:
4548    real(8)              :: gpscompressibility
4549
4550    ! Locals:
4551    real(8), parameter   :: a0= 1.58123D-6
4552    real(8), parameter   :: a1=-2.9331D-8
4553    real(8), parameter   :: a2= 1.1043D-10
4554    real(8), parameter   :: b0= 5.707D-6
4555    real(8), parameter   :: b1=-2.051D-8
4556    real(8), parameter   :: c0= 1.9898D-4
4557    real(8), parameter   :: c1=-2.376D-6
4558    real(8), parameter   :: d = 1.83D-11
4559    real(8), parameter   :: e =-0.765D-8
4560    real(8)         :: x,tc,pt,tc2,x2
4561
4562    if ( t <= 0 ) call utl_abort('gpscompressibility: t <= 0')
4563
4564    x  = gps_p_wa * q / (1.D0 + gps_p_wb * q)
4565    ! Estimate, from CIPM, Picard (2008)
4566    tc = t - MPC_K_C_DEGREE_OFFSET_R8
4567    pt = p / t
4568    tc2= tc * tc
4569    x2 = x * x
4570    gpscompressibility = 1.D0 - pt * &
4571        (a0+a1*tc+a2*tc2+(b0+b1*tc)*x+(c0+c1*tc)*x2) + pt*pt*(d+e*x2)
4572  end function gpscompressibility
4573
4574  !---------------------------------------------------------
4575  ! gpscompressibility_TT
4576  !---------------------------------------------------------
4577  function gpscompressibility_TT(p,t,q)
4578    implicit none
4579
4580    ! Arguments:
4581    real(8), intent(in)  :: p
4582    real(8), intent(in)  :: t
4583    real(8), intent(in)  :: q
4584    ! Result:
4585    real(8)              :: gpscompressibility_TT
4586
4587    ! Locals:
4588    real(8), parameter   :: a0= 1.58123D-6
4589    real(8), parameter   :: a1=-2.9331D-8
4590    real(8), parameter   :: a2= 1.1043D-10
4591    real(8), parameter   :: b0= 5.707D-6
4592    real(8), parameter   :: b1=-2.051D-8
4593    real(8), parameter   :: c0= 1.9898D-4
4594    real(8), parameter   :: c1=-2.376D-6
4595    real(8), parameter   :: d = 1.83D-11
4596    real(8), parameter   :: e =-0.765D-8
4597    real(8)         :: x,tc,pt,tc2,x2
4598    real(8)         :: d_x,d_tc,d_pt,d_tc2,d_x2
4599
4600    if ( t <= 0 ) call utl_abort('gpscompressibility_TT: t <= 0')
4601
4602    x  = gps_p_wa * q / (1.D0 + gps_p_wb * q)
4603    d_x  = 0.0D0
4604    ! Estimate, from CIPM, Picard (2008)
4605    tc = t - MPC_K_C_DEGREE_OFFSET_R8
4606    d_tc = 1.D0
4607    pt = p / t
4608    d_pt = - p / t**2
4609    tc2= tc * tc
4610    d_tc2= 2 * tc * d_tc
4611    x2 = x * x
4612    d_x2 = 2 * x * d_x
4613    gpscompressibility_TT = &
4614        -d_pt * (a0+a1*tc+a2*tc2+(b0+b1*tc)*x+(c0+c1*tc)*x2) - pt * &
4615        (a1*d_tc + a2*d_tc2 + b1*d_tc*x + (b0+b1*tc)*d_x + c1*d_tc*x2 + &
4616        (c0+c1*tc)*d_x2) + 2*pt*d_pt*(d+e*x2) + pt*pt*e*d_x2
4617  end function gpscompressibility_TT
4618
4619  !---------------------------------------------------------
4620  ! gpscompressibility_HU
4621  !---------------------------------------------------------
4622  function gpscompressibility_HU(p,t,q)
4623    implicit none
4624
4625    ! Arguments:
4626    real(8), intent(in)  :: p
4627    real(8), intent(in)  :: t
4628    real(8), intent(in)  :: q
4629    ! Result:
4630    real(8)              :: gpscompressibility_HU
4631
4632    ! Locals:
4633    real(8), parameter   :: a0= 1.58123D-6
4634    real(8), parameter   :: a1=-2.9331D-8
4635    real(8), parameter   :: a2= 1.1043D-10
4636    real(8), parameter   :: b0= 5.707D-6
4637    real(8), parameter   :: b1=-2.051D-8
4638    real(8), parameter   :: c0= 1.9898D-4
4639    real(8), parameter   :: c1=-2.376D-6
4640    real(8), parameter   :: d = 1.83D-11
4641    real(8), parameter   :: e =-0.765D-8
4642    real(8)         :: x,tc,pt,tc2,x2
4643    real(8)         :: d_x,d_tc,d_pt,d_tc2,d_x2
4644
4645    if ( t <= 0 ) call utl_abort('gpscompressibility_HU: t <= 0')
4646
4647    x  = gps_p_wa * q / (1.D0+gps_p_wb*q)
4648    d_x  = gps_p_wa * (1.0D0 / (1.D0+gps_p_wb*q) - q / (1.D0+gps_p_wb*q)**2 * gps_p_wb * 1.0D0)
4649    ! Estimate, from CIPM, Picard (2008)
4650    tc = t - MPC_K_C_DEGREE_OFFSET_R8
4651    d_tc = 0.0D0
4652    pt = p / t
4653    d_pt = 0.0D0
4654    tc2= tc * tc
4655    d_tc2= 2 * tc * d_tc
4656    x2 = x * x
4657    d_x2 = 2 * x * d_x
4658    gpscompressibility_HU = &
4659        -d_pt * (a0+a1*tc+a2*tc2+(b0+b1*tc)*x+(c0+c1*tc)*x2) - &
4660        pt * (a1*d_tc + a2*d_tc2 + b1*d_tc*x + (b0+b1*tc)*d_x + c1*d_tc*x2 + &
4661        (c0+c1*tc)*d_x2) + 2*pt*d_pt*(d+e*x2) + pt*pt*e*d_x2
4662  end function gpscompressibility_HU
4663
4664  !---------------------------------------------------------
4665  ! gpscompressibility_P0_1
4666  !---------------------------------------------------------
4667  function gpscompressibility_P0_1(p,t,q,dpdp0)
4668    ! gpscompressibility_P0_1 has dpdp0 dependency
4669    implicit none
4670
4671    ! Arguments:
4672    real(8), intent(in)  :: p
4673    real(8), intent(in)  :: t
4674    real(8), intent(in)  :: q
4675    real(8), intent(in)  :: dpdp0
4676    ! Result:
4677    real(8)              :: gpscompressibility_P0_1
4678
4679    ! Locals:
4680    real(8), parameter   :: a0= 1.58123D-6
4681    real(8), parameter   :: a1=-2.9331D-8
4682    real(8), parameter   :: a2= 1.1043D-10
4683    real(8), parameter   :: b0= 5.707D-6
4684    real(8), parameter   :: b1=-2.051D-8
4685    real(8), parameter   :: c0= 1.9898D-4
4686    real(8), parameter   :: c1=-2.376D-6
4687    real(8), parameter   :: d = 1.83D-11
4688    real(8), parameter   :: e =-0.765D-8
4689    real(8)         :: x,tc,pt,tc2,x2
4690    real(8)         :: d_x,d_tc,d_pt,d_tc2,d_x2
4691
4692    if ( t <= 0 ) call utl_abort('gpscompressibility_P0_1: t <= 0')
4693
4694    x  = gps_p_wa * q / (1.D0+gps_p_wb*q)
4695    d_x  = 0.0D0
4696    ! Estimate, from CIPM, Picard (2008)
4697    tc = t - MPC_K_C_DEGREE_OFFSET_R8
4698    d_tc = 0.0D0
4699    pt = p / t
4700    d_pt = dpdp0 / t
4701    tc2= tc * tc
4702    d_tc2= 2 * tc * d_tc
4703    x2 = x * x
4704    d_x2 = 2 * x * d_x
4705    gpscompressibility_P0_1 = &
4706        -d_pt * (a0+a1*tc+a2*tc2+(b0+b1*tc)*x+(c0+c1*tc)*x2) + &
4707        2*pt*d_pt*(d+e*x2)
4708  end function gpscompressibility_P0_1
4709
4710  !---------------------------------------------------------
4711  ! gpscompressibility_P0_2
4712  !---------------------------------------------------------
4713  function gpscompressibility_P0_2(p,t,q)
4714    ! gpscompressibility_P0_2 has NO dpdp0 dependency
4715    implicit none
4716
4717    ! Arguments:
4718    real(8), intent(in)  :: p
4719    real(8), intent(in)  :: t
4720    real(8), intent(in)  :: q
4721    ! Result:
4722    real(8)              :: gpscompressibility_P0_2
4723
4724    ! Locals:
4725    real(8), parameter   :: a0= 1.58123D-6
4726    real(8), parameter   :: a1=-2.9331D-8
4727    real(8), parameter   :: a2= 1.1043D-10
4728    real(8), parameter   :: b0= 5.707D-6
4729    real(8), parameter   :: b1=-2.051D-8
4730    real(8), parameter   :: c0= 1.9898D-4
4731    real(8), parameter   :: c1=-2.376D-6
4732    real(8), parameter   :: d = 1.83D-11
4733    real(8), parameter   :: e =-0.765D-8
4734    real(8)         :: x,tc,pt,tc2,x2
4735    real(8)         :: d_x,d_tc,d_tc2,d_x2
4736
4737    if ( t <= 0 ) call utl_abort('gpscompressibility_P0_2: t <= 0')
4738
4739    x  = gps_p_wa * q / (1.D0+gps_p_wb*q)
4740    d_x  = 0.0D0
4741    ! Estimate, from CIPM, Picard (2008)
4742    tc = t - MPC_K_C_DEGREE_OFFSET_R8
4743    d_tc = 0.0D0
4744    pt = p / t
4745    tc2= tc * tc
4746    d_tc2= 2 * tc * d_tc
4747    x2 = x * x
4748    d_x2 = 2 * x * d_x
4749    gpscompressibility_P0_2 = &
4750        -pt * (a1*d_tc + a2*d_tc2 + b1*d_tc*x + (b0+b1*tc)*d_x + c1*d_tc*x2 &
4751              + (c0+c1*tc)*d_x2) + pt*pt*e*d_x2
4752  end function gpscompressibility_P0_2
4753
4754end module calcHeightAndPressure_mod