interpolation_mod sourceΒΆ

   1module interpolation_mod
   2  ! MODULE interpolation_mod (prefix='int' category='4. Data Object transformations')
   3  !
   4  !:Purpose: The grid-point state vector interpolation.
   5  !
   6  use midasMpi_mod
   7  use gridstatevector_mod
   8  use columnData_mod
   9  use calcHeightAndPressure_mod
  10  use varNameList_mod
  11  use verticalCoord_mod
  12  use horizontalCoord_mod
  13  use mathPhysConstants_mod
  14  use utilities_mod
  15  use message_mod
  16  use kdTree2_mod
  17  implicit none
  18  save
  19  private
  20
  21  ! public subroutines and functions
  22  public :: int_interp_gsv
  23  public :: int_hInterp_gsv
  24  public :: int_vInterp_gsv
  25  public :: int_tInterp_gsv
  26  public :: int_vInterp_col
  27  public :: int_hInterpScalar, int_ezgdef, int_cxgaig
  28
  29  ! module interfaces
  30  ! -----------------
  31
  32  interface int_hInterpScalar
  33    module procedure int_hInterpScalar_gsv
  34    module procedure int_hInterpScalar_r4_2d
  35    module procedure int_hInterpScalar_r8_2d
  36  end interface int_hInterpScalar
  37
  38  interface int_hInterpUV
  39    module procedure int_hInterpUV_gsv
  40    module procedure int_hInterpUV_r4_2d
  41    module procedure int_hInterpUV_r8_2d
  42  end interface int_hInterpUV
  43
  44  ! Namelist variables
  45  ! ------------------
  46  logical :: vInterpCopyLowestLevel ! overwrite values at lowest level to avoid extrapolation
  47  logical :: checkCloudToGridUnassigned ! abort if unmasked points not assigned from cloudToGrid interp
  48  integer :: maxBoxSize             ! max size used to fill values for cloudToGrid interpolation
  49
  50contains
  51
  52  !--------------------------------------------------------------------------
  53  ! int_readNml (private subroutine)
  54  !--------------------------------------------------------------------------
  55  subroutine int_readNml()
  56    !
  57    !:Purpose: Read the namelist block NAMINT.
  58    !
  59    !:Namelist parameters:
  60    !         :vInterpCopyLowestLevel:  if true, will overwrite values at the lowest
  61    !                                   levels to avoid extrapolation
  62    !         :maxBoxSize: maximum size in terms of grid points used for filling
  63    !                      in undefined values from neighbours when doing interpolation
  64    !                      from a cloud of points to a grid
  65    !         :checkCloudToGridUnassigned: abort if any unmasked points are not assigned
  66    !                                      after doing cloudToGrid interpolation
  67    !
  68    implicit none
  69
  70    ! Locals:
  71    integer :: ierr, nulnam, fnom, fclos
  72    logical, save :: alreadyRead = .false.
  73
  74    NAMELIST /NAMINT/ vInterpCopyLowestLevel, checkCloudToGridUnassigned, maxBoxSize
  75
  76    if (alreadyRead) then
  77      return
  78    else
  79      alreadyRead = .true.
  80    end if
  81    call msg('int_readNml', 'START', verb_opt=2)
  82
  83    ! default values
  84    vInterpCopyLowestLevel = .false.
  85    checkCloudToGridUnassigned = .true.
  86    maxBoxSize = 1
  87
  88    if ( .not. utl_isNamelistPresent('NAMINT','./flnml') ) then
  89      call msg('int_readNml', 'namint is missing in the namelist.'&
  90           //'The default values will be taken.', mpiAll_opt=.false.)
  91    else
  92      ! Read namelist NAMINT
  93      nulnam = 0
  94      ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
  95      read(nulnam,nml=namint,iostat=ierr)
  96      if (ierr /= 0) call utl_abort('int_readlNml: Error reading namelist NAMINT')
  97      if (mmpi_myid == 0) write(*,nml=namint)
  98      ierr = fclos(nulnam)
  99    end if
 100
 101    call msg('int_readNml', 'END', verb_opt=2)
 102  end subroutine int_readNml
 103
 104  !--------------------------------------------------------------------------
 105  ! int_interp_gsv
 106  !--------------------------------------------------------------------------
 107  subroutine int_interp_gsv(statevector_in, statevector_out, statevectorRef_opt, &
 108                            checkModelTop_opt)
 109    !
 110    !:Purpose: high-level interpolation subroutine that proceed with
 111    !           horizontal and vertical interpolation
 112    !
 113    implicit none
 114
 115    ! Arguments:
 116    type(struct_gsv),           intent(in)    :: statevector_in     ! Statevector input
 117    type(struct_gsv),           intent(inout) :: statevector_out    ! Statevector output (with target grids)
 118    type(struct_gsv), optional, intent(in)    :: statevectorRef_opt ! Reference statevector with fields P0, TT and HU
 119    logical,          optional, intent(in)    :: checkModelTop_opt  ! If true, model top consistency checked
 120    
 121    ! Locals:
 122    logical :: checkModelTop
 123    character(len=4), pointer :: varNamesToInterpolate(:)
 124    type(struct_gsv) :: statevector_in_varsLevs, statevector_in_varsLevs_hInterp
 125    type(struct_gsv) :: statevector_in_hInterp
 126
 127    call msg('int_interp_gsv', 'START', verb_opt=2)
 128
 129    !
 130    !- Error traps
 131    !
 132    if (.not.gsv_isAllocated(statevector_in)) then
 133      call utl_abort('int_interp_gsv: gridStateVector_in not yet allocated')
 134    end if
 135    if (.not.gsv_isAllocated(statevector_out)) then
 136      call utl_abort('int_interp_gsv: gridStateVector_out not yet allocated')
 137    end if
 138
 139    !
 140    !- Do the interpolation of statevector_in onto the grid of statevector_out
 141    !
 142    nullify(varNamesToInterpolate)
 143    call gsv_varNamesList(varNamesToInterpolate, statevector_in)
 144
 145    !- Horizontal interpolation
 146    call gsv_allocate(statevector_in_VarsLevs, statevector_in%numstep, &
 147                      statevector_in%hco, statevector_in%vco,          &
 148                      mpi_local_opt=statevector_in%mpi_local, mpi_distribution_opt='VarsLevs',  &
 149                      dataKind_opt=gsv_getDataKind(statevector_in), &
 150                      allocHeightSfc_opt=statevector_in%heightSfcPresent, &
 151                      varNames_opt=varNamesToInterpolate, &
 152                      hInterpolateDegree_opt=statevector_out%hInterpolateDegree, &
 153                      hExtrapolateDegree_opt=statevector_out%hExtrapolateDegree )
 154
 155    call gsv_transposeTilesToVarsLevs( statevector_in, statevector_in_VarsLevs )
 156
 157    call gsv_allocate(statevector_in_VarsLevs_hInterp, statevector_in%numstep, &
 158                      statevector_out%hco, statevector_in%vco,  &
 159                      mpi_local_opt=statevector_out%mpi_local, mpi_distribution_opt='VarsLevs', &
 160                      dataKind_opt=gsv_getDataKind(statevector_out), &
 161                      allocHeightSfc_opt=statevector_out%heightSfcPresent, &
 162                      varNames_opt=varNamesToInterpolate, &
 163                      hInterpolateDegree_opt=statevector_out%hInterpolateDegree, &
 164                      hExtrapolateDegree_opt=statevector_out%hExtrapolateDegree )
 165
 166    call int_hInterp_gsv(statevector_in_VarsLevs, statevector_in_VarsLevs_hInterp)
 167    call gsv_deallocate(statevector_in_VarsLevs)
 168
 169    call gsv_allocate(statevector_in_hInterp, statevector_in%numstep, &
 170                      statevector_out%hco, statevector_in%vco,      &
 171                      mpi_local_opt=statevector_out%mpi_local, mpi_distribution_opt='Tiles', &
 172                      dataKind_opt=gsv_getDataKind(statevector_out), &
 173                      allocHeightSfc_opt=statevector_out%heightSfcPresent, &
 174                      varNames_opt=varNamesToInterpolate )
 175
 176    call gsv_transposeVarsLevsToTiles( statevector_in_varsLevs_hInterp, statevector_in_hInterp )
 177    call gsv_deallocate(statevector_in_varsLevs_hInterp)
 178
 179    !- Vertical interpolation
 180    
 181    ! the default is to ensure that the top of the output grid is ~equal or lower than the top of the input grid 
 182    if ( present(checkModelTop_opt) ) then
 183      checkModelTop = checkModelTop_opt
 184    else
 185      checkModelTop = .true.
 186    end if
 187    
 188    call int_vInterp_gsv(statevector_in_hInterp,statevector_out,&
 189                         statevectorRef_opt=statevectorRef_opt, &
 190                         checkModelTop_opt=checkModelTop)
 191
 192    call gsv_deallocate(statevector_in_hInterp)
 193    nullify(varNamesToInterpolate)
 194
 195    call msg('int_interp_gsv', 'END', verb_opt=2)
 196  end subroutine int_interp_gsv
 197
 198  !--------------------------------------------------------------------------
 199  ! int_hInterp_gsv
 200  !--------------------------------------------------------------------------
 201  subroutine int_hInterp_gsv(statevector_in,statevector_out)
 202    !
 203    !:Purpose: Horizontal interpolation
 204    !
 205    implicit none
 206
 207    ! Arguments:
 208    type(struct_gsv), intent(inout) :: statevector_in  ! Statevector input
 209    type(struct_gsv), intent(inout) :: statevector_out ! Statevector with target horiz and vert grids and result
 210
 211    ! Locals:
 212    integer :: varIndex, levIndex, nlev, stepIndex, ierr, kIndex
 213    character(len=4) :: varName
 214    character(len=12):: interpolationDegree, extrapolationDegree
 215
 216    call msg('int_hInterp_gsv', 'START', verb_opt=2)
 217
 218    if ( hco_equal(statevector_in%hco,statevector_out%hco) ) then
 219      call msg('int_hInterp_gsv', 'The input and output statevectors are already on same horizontal grids')
 220      call gsv_copy(statevector_in, statevector_out)
 221      return
 222    end if
 223
 224    if ( .not. vco_equal(statevector_in%vco, statevector_out%vco) ) then
 225      call utl_abort('int_hInterp_gsv: The input and output statevectors are not on the same vertical levels.')
 226    end if
 227
 228    ! set the interpolation degree
 229    interpolationDegree = statevector_out%hInterpolateDegree
 230    extrapolationDegree = statevector_out%hExtrapolateDegree
 231
 232    if ( .not.statevector_in%mpi_local .and. .not.statevector_out%mpi_local ) then
 233
 234      call msg('int_hInterp_gsv', 'before interpolation (no mpi)')
 235
 236      step_loop: do stepIndex = 1, statevector_out%numStep
 237        ! copy over some time related parameters
 238        statevector_out%deet                      = statevector_in%deet
 239        statevector_out%dateOriginList(stepIndex) = statevector_in%dateOriginList(stepIndex)
 240        statevector_out%npasList(stepIndex)       = statevector_in%npasList(stepIndex)
 241        statevector_out%ip2List(stepIndex)        = statevector_in%ip2List(stepIndex)
 242        statevector_out%etiket                    = statevector_in%etiket
 243
 244        ! Do horizontal interpolation for mpi global statevectors
 245        var_loop: do varIndex = 1, vnl_numvarmax
 246          varName = vnl_varNameList(varIndex)
 247          if ( .not. gsv_varExist(statevector_in,varName) ) cycle var_loop
 248          if ( trim(varName) == 'VV' ) cycle var_loop
 249
 250          nlev = statevector_out%varNumLev(varIndex)
 251
 252          ! horizontal interpolation
 253
 254          if ( trim(varName) == 'UU' ) then
 255            ! interpolate both UV components and keep both UU and VV
 256            do levIndex = 1, nlev
 257              ierr = int_hInterpUV( statevector_out, statevector_in, 'BOTH', levIndex, stepIndex, &
 258                                    interpDegree=trim(interpolationDegree), &
 259                                    extrapDegree_opt=trim(extrapolationDegree) )
 260            end do
 261          else
 262            ! interpolate scalar variable
 263            do levIndex = 1, nlev
 264              ierr = int_hInterpScalar( statevector_out, statevector_in, varName, levIndex, stepIndex, &
 265                                        interpDegree=trim(interpolationDegree), &
 266                                        extrapDegree_opt=trim(extrapolationDegree) )
 267            end do
 268          end if
 269        end do var_loop
 270
 271      end do step_loop
 272
 273    else
 274
 275      call msg('int_hInterp_gsv', 'before interpolation (with mpi)')
 276
 277      if ( statevector_in%mpi_distribution /= 'VarsLevs' .or.   &
 278          statevector_out%mpi_distribution /= 'VarsLevs' ) then
 279        call utl_abort('int_hInterp_gsv: The input or output statevector is not distributed by VarsLevs.')
 280      end if
 281
 282      do stepIndex = 1, statevector_out%numStep
 283        k_loop: do kIndex = statevector_in%mykBeg, statevector_in%mykEnd
 284          varName = gsv_getVarNameFromK(statevector_in,kIndex)
 285          if ( .not. gsv_varExist(statevector_in,varName) ) cycle k_loop
 286
 287          ! horizontal interpolation
 288
 289          if ( trim(varName) == 'UU' ) then
 290            ! interpolate both UV components and keep UU in main vector
 291            ierr = int_hInterpUV( statevector_out, statevector_in, 'UU', kIndex, stepIndex, &
 292                                  interpDegree=trim(interpolationDegree), &
 293                                  extrapDegree_opt=trim(extrapolationDegree) )
 294          else if ( trim(varName) == 'VV' ) then
 295            ! interpolate both UV components and keep VV in main vector
 296            ierr = int_hInterpUV( statevector_out, statevector_in, 'VV', kIndex, stepIndex, &
 297                                  interpDegree=trim(interpolationDegree), &
 298                                  extrapDegree_opt=trim(extrapolationDegree) )
 299          else
 300            ! interpolate scalar variable
 301            ierr = int_hInterpScalar( statevector_out, statevector_in, 'ALL', kIndex, stepIndex, &
 302                                      interpDegree=trim(interpolationDegree), &
 303                                      extrapDegree_opt=trim(extrapolationDegree) )
 304          end if
 305        end do k_loop
 306
 307      end do ! stepIndex
 308
 309    end if
 310
 311    if ( gsv_isAssocHeightSfc(statevector_in) .and. gsv_isAssocHeightSfc(statevector_out) ) then
 312      call msg('int_hInterp_gsv','interpolating surface height')
 313      ierr = int_hInterpScalar( statevector_out, statevector_in, 'ZSFC', 1, 1, &
 314                                interpDegree=trim(interpolationDegree), &
 315                                extrapDegree_opt=trim(extrapolationDegree) )
 316    end if
 317
 318    call msg('int_hInterp_gsv', 'END', verb_opt=2)
 319  end subroutine int_hInterp_gsv
 320
 321  !--------------------------------------------------------------------------
 322  ! int_vInterp_gsv
 323  !--------------------------------------------------------------------------
 324  subroutine int_vInterp_gsv(statevector_in,statevector_out,statevectorRef_opt, &
 325                             Ps_in_hPa_opt,checkModelTop_opt)
 326    !
 327    !:Purpose: Vertical interpolation.
 328    !           Interpolation coordinates are either height or log(P)
 329    !           based on target vertical descriptor vcode.
 330    !           * When target vertical coordinates are pressure (interpolating
 331    !             to GEM-P), log(P) is used;
 332    !           * When they are height (interpolating to GEM-H), height is used.
 333    !           Call to ``calcHeightAndPressure_mod`` will return required
 334    !           interpolation coordinates (log is then applied when required).
 335    !
 336    implicit none
 337
 338    ! Arguments:
 339    type(struct_gsv),           target, intent(in)    :: statevector_in     ! Statevector input
 340    type(struct_gsv),                   intent(inout) :: statevector_out    ! Statevector with target horiz/vert grids and result
 341    type(struct_gsv), optional, target, intent(in)    :: statevectorRef_opt ! Reference statevector with P0, TT and HU
 342    logical,          optional,         intent(in)    :: Ps_in_hPa_opt      ! If true, surface pressure is in hPa, not Pa
 343    logical,          optional,         intent(in)    :: checkModelTop_opt  ! Model top consistency will be checked
 344
 345    call msg('int_vInterp_gsv', 'START', verb_opt=2)
 346
 347    ! read the namelist
 348    call int_readNml()
 349
 350    if ( gsv_getDataKind(statevector_in) == 8 & 
 351         .and. gsv_getDataKind(statevector_out) == 8 ) then 
 352      call vInterp_gsv_r8(statevector_in,statevector_out,statevectorRef_opt, &
 353                          Ps_in_hPa_opt,checkModelTop_opt)
 354    else if ( gsv_getDataKind(statevector_in) == 4 &
 355         .and. gsv_getDataKind(statevector_out) == 4 ) then 
 356      call vInterp_gsv_r4(statevector_in,statevector_out,statevectorRef_opt, &
 357                          Ps_in_hPa_opt,checkModelTop_opt)
 358    else
 359      call utl_abort('int_vInterp_gsv: input and output statevectors must be of same dataKind')
 360    end if
 361
 362    call msg('int_vInterp_gsv', 'END', verb_opt=2)
 363
 364  end subroutine int_vInterp_gsv
 365
 366  !--------------------------------------------------------------------------
 367  ! vInterp_gsv_r8
 368  !--------------------------------------------------------------------------
 369  subroutine vInterp_gsv_r8(statevector_in,statevector_out,statevectorRef_opt, &
 370                            Ps_in_hPa_opt,checkModelTop_opt)
 371    !
 372    !:Purpose: Vertical interpolation, ``real(8)`` version.
 373    !
 374    implicit none
 375
 376    ! Arguments:
 377    type(struct_gsv),           target, intent(in)    :: statevector_in     ! Statevector input
 378    type(struct_gsv),                   intent(inout) :: statevector_out    ! Statevector with target horiz/vert grids and result
 379    type(struct_gsv), optional, target, intent(in)    :: statevectorRef_opt ! Reference statevector with P0, TT and HU
 380    logical,          optional,         intent(in)    :: Ps_in_hPa_opt      ! If true, surface pressure is in hPa, not Pa
 381    logical,          optional,         intent(in)    :: checkModelTop_opt  ! Model top consistency will be checked
 382
 383    ! Locals:
 384    logical :: checkModelTop, hLikeCalc
 385    integer :: vcode_in, vcode_out
 386    integer :: nlev_out, nlev_in
 387    integer :: varIndex, stepIndex
 388    type(struct_gsv), pointer   :: statevectorRef
 389    type(struct_gsv)            :: statevectorRef_out
 390    real(8), pointer  :: hLikeT_in(:,:,:,:), hLikeM_in(:,:,:,:)   ! abstract height dimensioned coordinate
 391    real(8), pointer  :: hLikeT_out(:,:,:,:), hLikeM_out(:,:,:,:) ! abstract height dimensioned coordinate
 392    real(8), pointer  :: field_in(:,:,:,:), field_out(:,:,:,:)
 393    real(8), pointer  :: heightSfcIn(:,:), heightSfcOut(:,:)
 394    real(8), pointer  :: tmpCoord_T(:,:,:,:), tmpCoord_M(:,:,:,:)
 395    character(len=4) :: varName
 396    type(struct_vco), pointer :: vco_in, vco_out
 397
 398    call msg('vInterp_gsv_r8', 'START', verb_opt=4)
 399
 400    vco_in  => gsv_getVco(statevector_in)
 401    vco_out => gsv_getVco(statevector_out)
 402    vcode_in  = vco_in%vcode
 403    vcode_out = vco_out%vcode
 404    nullify(hLikeT_in,hLikeM_in,hLikeT_out,hLikeM_out)
 405
 406    hLikeCalc = .true.
 407
 408    if (present(statevectorRef_opt)) then
 409      if ( .not. vco_equal(gsv_getVco(statevectorRef_opt), gsv_getVco(statevector_in))) then
 410        call utl_abort('vInterp_gsv_r8: reference must have input vertical structure')
 411      end if
 412      statevectorRef => statevectorRef_opt
 413    else
 414      statevectorRef => statevector_in
 415    end if
 416
 417    if ( vco_equal(statevector_in%vco, statevector_out%vco) ) then
 418      call msg('vInterp_gsv_r8', 'The input and output statevectors are already on same vertical levels')
 419      call gsv_copy(statevector_in, statevector_out)
 420      return
 421    end if
 422
 423    if ( .not. hco_equal(statevector_in%hco, statevector_out%hco) ) then
 424      call utl_abort('vInterp_gsv_r8: The input and output statevectors are not on the same horizontal grid.')
 425    end if
 426
 427    if ( gsv_getDataKind(statevector_in) /= 8 .or. gsv_getDataKind(statevector_out) /= 8 ) then
 428      call utl_abort('vInterp_gsv_r8: Incorrect value for dataKind. Only compatible with dataKind=8')
 429    end if
 430
 431    if (gsv_isAssocHeightSfc(statevector_in) .and. gsv_isAssocHeightSfc(statevector_out) ) then
 432      heightSfcIn => gsv_getHeightSfc(statevector_in)
 433      heightSfcOut => gsv_getHeightSfc(statevector_out)
 434      heightSfcOut(:,:) = heightSfcIn(:,:)
 435    end if
 436
 437    ! the default is to ensure that the top of the output grid is ~equal or lower than the top of the input grid 
 438    if ( present(checkModelTop_opt) ) then
 439      checkModelTop = checkModelTop_opt
 440    else
 441      checkModelTop = .true.
 442    end if
 443    if (checkModelTop) then
 444      call msg('vInterp_gsv_r8', ' Checking that that the top of the destination grid is not higher than the top of the source grid.')
 445      if ( vcode_in == 21001 .or. vcode_out == 21001 ) then
 446        call msg('vInterp_gsv_r8', 'bypassing top check, '&
 447             //'vcode_in='//str(vcode_in)//', vcode_out='//str(vcode_out))
 448        ! Development notes (@mad001)
 449        !   we should consider having a new criterion that works for GEM-H as well
 450      else
 451        call czp_ensureCompatibleTops(vco_in, vco_out)
 452      end if
 453    end if
 454
 455    ! create reference state on output vco with reference surface fields
 456    call gsv_allocate(statevectorRef_out, statevectorRef%numstep, &
 457                      statevectorRef%hco, statevector_out%vco,      &
 458                      mpi_local_opt=statevectorRef%mpi_local, mpi_distribution_opt='Tiles', &
 459                      dataKind_opt=gsv_getDataKind(statevectorRef), &
 460                      allocHeightSfc_opt=statevectorRef%heightSfcPresent, &
 461                      varNames_opt=(/'P0','P0LS'/) )
 462    call gsv_copy(stateVectorRef, stateVectorRef_out, allowVcoMismatch_opt=.true., &
 463                  allowVarMismatch_opt=.true.)
 464
 465    var_loop: do varIndex = 1, vnl_numvarmax
 466      varName = vnl_varNameList(varIndex)
 467      if ( .not. gsv_varExist(statevector_in,varName) ) cycle var_loop
 468
 469      nlev_in  = statevector_in%varNumLev(varIndex)
 470      nlev_out = statevector_out%varNumLev(varIndex)
 471
 472      call gsv_getField(statevector_in ,field_in,varName)
 473      call gsv_getField(statevector_out,field_out,varName)
 474
 475      ! for 2D fields and variables on "other" levels, just copy and cycle to next variable
 476      if ( (nlev_in == 1 .and. nlev_out == 1) .or. &
 477           (vnl_varLevelFromVarname(varName) == 'OT') ) then
 478        field_out(:,:,:,:) = field_in(:,:,:,:)
 479        cycle var_loop
 480      end if
 481
 482      hLikeCalcIf: if (hLikeCalc) then
 483        hLikeCalc = .false.
 484
 485        ! allocating for temporary pressure references
 486        allocate(hLikeT_in( statevector_in%myLonBeg:statevector_in%myLonEnd, &
 487                            statevector_in%myLatBeg:statevector_in%myLatEnd, &
 488                            gsv_getNumLev(statevector_in,'TH'), statevector_in%numStep))
 489        allocate(hLikeM_in( statevector_in%myLonBeg:statevector_in%myLonEnd, &
 490                            statevector_in%myLatBeg:statevector_in%myLatEnd, &
 491                            gsv_getNumLev(statevector_in,'MM'), statevector_in%numStep))
 492        allocate(hLikeT_out(statevector_out%myLonBeg:statevector_out%myLonEnd, &
 493                            statevector_out%myLatBeg:statevector_out%myLatEnd, &
 494                            gsv_getNumLev(statevector_out,'TH'), statevector_out%numStep))
 495        allocate(hLikeM_out(statevector_out%myLonBeg:statevector_out%myLonEnd, &
 496                            statevector_out%myLatBeg:statevector_out%myLatEnd, &
 497                            gsv_getNumLev(statevector_out,'MM'), statevector_out%numStep))
 498        allocate(tmpCoord_T(statevector_in%myLonBeg:statevector_in%myLonEnd, &
 499                            statevector_in%myLatBeg:statevector_in%myLatEnd, &
 500                            gsv_getNumLev(statevector_in,'TH'), statevector_in%numStep))
 501        allocate(tmpCoord_M(statevector_in%myLonBeg:statevector_in%myLonEnd, &
 502                            statevector_in%myLatBeg:statevector_in%myLatEnd, &
 503                            gsv_getNumLev(statevector_in,'MM'), statevector_in%numStep))
 504
 505        ! output grid GEM-P interpolation in log-pressure
 506        if ( vcode_out==5002 .or. vcode_out==5005 .or. vcode_out==5100 ) then
 507          call czp_calcReturnPressure_gsv_nl( statevectorRef_out, &
 508                                              PTout_r8_opt=hLikeT_out, &
 509                                              PMout_r8_opt=hLikeM_out, &
 510                                              Ps_in_hPa_opt=Ps_in_hPa_opt)
 511
 512          if ( vcode_in==5002 .or. vcode_in==5005 .or. vcode_in==5100 ) then
 513            call czp_calcReturnPressure_gsv_nl( statevectorRef, &
 514                                                PTout_r8_opt=hLikeT_in, &
 515                                                PMout_r8_opt=hLikeM_in, &
 516                                                Ps_in_hPa_opt=Ps_in_hPa_opt)
 517          else if ( vcode_in==21001 ) then
 518            call czp_calcReturnHeight_gsv_nl( statevectorRef, &
 519                                              ZTout_r8_opt=tmpCoord_T, &
 520                                              ZMout_r8_opt=tmpCoord_M)
 521            call czp_calcReturnPressure_gsv_nl( statevectorRef, &
 522                                                ZTin_r8_opt=tmpCoord_T, &
 523                                                ZMin_r8_opt=tmpCoord_M, &
 524                                                PTout_r8_opt=hLikeT_in, &
 525                                                PMout_r8_opt=hLikeM_in, &
 526                                                Ps_in_hPa_opt=Ps_in_hPa_opt)
 527          end if
 528
 529          call msg('vInterp_gsv_r8','converting pressure coordinates to height-like, '&
 530                   //'vcode_in='//str(vcode_in)//', vcode_out='//str(vcode_out))
 531          call logP_r8(hLikeM_out)
 532          call logP_r8(hLikeT_out)
 533          call logP_r8(hLikeM_in)
 534          call logP_r8(hLikeT_in)
 535
 536          ! output grid GEM-H interpolation in height
 537        else if ( vcode_out==21001 ) then
 538          call czp_calcReturnHeight_gsv_nl( statevectorRef_out, &
 539                                            ZTout_r8_opt=hLikeT_out, &
 540                                            ZMout_r8_opt=hLikeM_out)
 541          if ( vcode_in==21001 ) then
 542            call czp_calcReturnHeight_gsv_nl( statevectorRef, &
 543                                              ZTout_r8_opt=hLikeT_in, &
 544                                              ZMout_r8_opt=hLikeM_in)
 545          else if ( vcode_in==5002 .or. vcode_in==5005 .or. vcode_in==5100 ) then
 546            call czp_calcReturnPressure_gsv_nl( statevectorRef, &
 547                                                PTout_r8_opt=tmpCoord_T, &
 548                                                PMout_r8_opt=tmpCoord_M, &
 549                                                Ps_in_hPa_opt=Ps_in_hPa_opt)
 550            call czp_calcReturnHeight_gsv_nl( statevectorRef, &
 551                                              PTin_r8_opt=tmpCoord_T, &
 552                                              PMin_r8_opt=tmpCoord_M, &
 553                                              ZTout_r8_opt=hLikeT_in, &
 554                                              ZMout_r8_opt=hLikeM_in)
 555          end if
 556        end if
 557        deallocate(tmpCoord_T)
 558        deallocate(tmpCoord_M)
 559
 560      end if hLikeCalcIf
 561
 562      step_loop: do stepIndex = 1, statevector_out%numStep
 563  
 564        ! copy over some time related and other parameters
 565        statevector_out%deet                      = statevector_in%deet
 566        statevector_out%dateOriginList(stepIndex) = statevector_in%dateOriginList(stepIndex)
 567        statevector_out%npasList(stepIndex)       = statevector_in%npasList(stepIndex)
 568        statevector_out%ip2List(stepIndex)        = statevector_in%ip2List(stepIndex)
 569        statevector_out%etiket                    = statevector_in%etiket
 570        statevector_out%onPhysicsGrid(:)          = statevector_in%onPhysicsGrid(:)
 571        statevector_out%hco_physics              => statevector_in%hco_physics
 572  
 573        ! do the vertical interpolation
 574        field_out(:,:,:,stepIndex) = 0.0d0
 575        if (vnl_varLevelFromVarname(varName) == 'TH') then
 576          call hLike_interpolation_r8(hLikeT_in, hLikeT_out)
 577        else
 578          call hLike_interpolation_r8(hLikeM_in, hLikeM_out)
 579        end if
 580
 581        ! overwrite values at the lowest levels to avoid extrapolation
 582        if (vInterpCopyLowestLevel) then
 583          field_out(:,:,nlev_out,stepIndex) = field_in(:,:,nlev_in,stepIndex)
 584        end if
 585
 586      end do step_loop
 587
 588    end do var_loop
 589
 590    if (associated(hLikeT_in)) then
 591      deallocate(hLikeT_in, hLikeM_in, hLikeT_out, hLikeM_out)
 592    end if
 593    call gsv_deallocate(statevectorRef_out)
 594
 595    call msg('vInterp_gsv_r8', 'END', verb_opt=4)
 596
 597    contains
 598
 599      subroutine hLike_interpolation_r8(hLike_in, hLike_out)
 600        !
 601        !:Purpose: Proceed to actual interpolation in H-logP representation
 602        !
 603        implicit none
 604
 605        ! Arguments:
 606        real(8), pointer, intent(in) :: hLike_in(:,:,:,:)  ! abstract height dimensioned input coordinate
 607        real(8), pointer, intent(in) :: hLike_out(:,:,:,:) ! abstract height dimensioned target coordinate
 608
 609        ! Locals:
 610        integer :: latIndex, lonIndex, levIndex_out, levIndex_in
 611        real(8) :: zwb, zwt
 612
 613        !$OMP PARALLEL DO PRIVATE(latIndex,lonIndex,levIndex_in,levIndex_out,zwb,zwt)
 614        do latIndex = statevector_out%myLatBeg, statevector_out%myLatEnd
 615          do lonIndex = statevector_out%myLonBeg, statevector_out%myLonEnd
 616            levIndex_in = 1
 617            do levIndex_out = 1, nlev_out
 618              levIndex_in = levIndex_in + 1
 619              do while(hLike_out(lonIndex,latIndex,levIndex_out,stepIndex) &
 620                        .gt.hLike_in(lonIndex,latIndex,levIndex_in,stepIndex)  &
 621                       .and.levIndex_in.lt.nlev_in)
 622                levIndex_in = levIndex_in + 1
 623              end do
 624              levIndex_in = levIndex_in - 1
 625              zwb = (hLike_out(lonIndex,latIndex,levIndex_out,stepIndex) &
 626                      - hLike_in(lonIndex,latIndex,levIndex_in,stepIndex)) &
 627                   /(hLike_in(lonIndex,latIndex,levIndex_in+1,stepIndex) &
 628                      - hLike_in(lonIndex,latIndex,levIndex_in,stepIndex))
 629              zwt = 1.d0 - zwb
 630              field_out(lonIndex,latIndex,levIndex_out,stepIndex) =   &
 631                                 zwb*field_in(lonIndex,latIndex,levIndex_in+1,stepIndex) &
 632                               + zwt*field_in(lonIndex,latIndex,levIndex_in,stepIndex)
 633            end do
 634          end do
 635        end do
 636        !$OMP END PARALLEL DO
 637
 638      end subroutine hLike_interpolation_r8
 639
 640  end subroutine vInterp_gsv_r8
 641
 642  !--------------------------------------------------------------------------
 643  ! logP_r8
 644  !--------------------------------------------------------------------------
 645  subroutine logP_r8(presInLogOut)
 646    !
 647    !:Purpose: compute log of pressurce field, real(8) version
 648    !
 649    implicit none
 650
 651    ! Arguments:
 652    real(8), intent(inout) :: presInLogOut(:,:,:,:)
 653
 654    ! Locals:
 655    integer :: latIdx, lonIdx, levIdx, stepIdx
 656
 657    !$OMP PARALLEL DO PRIVATE(lonIdx,latIdx,levIdx,stepIdx)
 658    do stepIdx = lbound(presInLogOut,4), ubound(presInLogOut,4)
 659      do levIdx = lbound(presInLogOut,3), ubound(presInLogOut,3)
 660        do latIdx = lbound(presInLogOut,2), ubound(presInLogOut,2)
 661          do lonIdx = lbound(presInLogOut,1), ubound(presInLogOut,1)
 662            presInLogOut(lonIdx,latIdx,levIdx,stepIdx) = &
 663                 log(presInLogOut(lonIdx,latIdx,levIdx,stepIdx))
 664          end do
 665        end do
 666      end do
 667    end do
 668    !$OMP END PARALLEL DO
 669
 670  end subroutine logP_r8
 671
 672  !--------------------------------------------------------------------------
 673  ! vInterp_gsv_r4
 674  !--------------------------------------------------------------------------
 675  subroutine vInterp_gsv_r4(statevector_in,statevector_out,statevectorRef_opt, &
 676                            Ps_in_hPa_opt,checkModelTop_opt)
 677    !
 678    !:Purpose: Vertical interpolation, ``real(4)`` version.
 679    !
 680    !
 681    implicit none
 682
 683    ! Arguments:
 684    type(struct_gsv),           target, intent(in)    :: statevector_in     ! Statevector input
 685    type(struct_gsv),                   intent(inout) :: statevector_out    ! Statevector with the target horiz/vert grids and result
 686    type(struct_gsv), optional, target, intent(in)    :: statevectorRef_opt ! Reference statevector with P0, TT and HU
 687    logical,          optional,         intent(in)    :: Ps_in_hPa_opt      ! If true, surface pressure in in hPa, not Pa
 688    logical,          optional,         intent(in)    :: checkModelTop_opt  ! Model top consistency will be checked
 689
 690    ! Locals:
 691    logical :: checkModelTop, hLikeCalc
 692    integer :: vcode_in, vcode_out
 693    integer :: nlev_out, nlev_in
 694    integer :: varIndex, stepIndex
 695    type(struct_gsv), pointer   :: statevectorRef
 696    type(struct_gsv)            :: statevectorRef_out
 697    real(4), pointer  :: hLikeT_in(:,:,:,:), hLikeM_in(:,:,:,:)   ! abstract height dimensioned coordinate
 698    real(4), pointer  :: hLikeT_out(:,:,:,:), hLikeM_out(:,:,:,:) ! abstract height dimensioned coordinate
 699    real(4), pointer  :: field_in(:,:,:,:), field_out(:,:,:,:)
 700    real(8), pointer  :: heightSfcIn(:,:), heightSfcOut(:,:)
 701    real(4), pointer  :: tmpCoord_T(:,:,:,:), tmpCoord_M(:,:,:,:)
 702    character(len=4) :: varName
 703    type(struct_vco), pointer :: vco_in, vco_out
 704
 705    call msg('vInterp_gsv_r4', 'START', verb_opt=4)
 706
 707    vco_in  => gsv_getVco(statevector_in)
 708    vco_out => gsv_getVco(statevector_out)
 709    vcode_in  = vco_in%vcode
 710    vcode_out = vco_out%vcode
 711    nullify(hLikeT_in,hLikeM_in,hLikeT_out,hLikeM_out)
 712
 713    hLikeCalc = .true.
 714
 715    if (present(statevectorRef_opt)) then
 716      if ( .not. vco_equal(gsv_getVco(statevectorRef_opt), gsv_getVco(statevector_in))) then
 717        call utl_abort('vInterp_gsv_r4: reference must have input vertical structure')
 718      end if
 719      statevectorRef => statevectorRef_opt
 720    else
 721      statevectorRef => statevector_in
 722    end if
 723
 724    if ( vco_equal(statevector_in%vco, statevector_out%vco) ) then
 725      call msg('vInterp_gsv_r4', 'The input and output statevectors are already on same vertical levels')
 726      call gsv_copy(statevector_in, statevector_out)
 727      return
 728    end if
 729
 730    if ( .not. hco_equal(statevector_in%hco, statevector_out%hco) ) then
 731      call utl_abort('vInterp_gsv_r4: The input and output statevectors are not on the same horizontal grid.')
 732    end if
 733
 734    ! DBGmad remove?
 735    if ( gsv_getDataKind(statevector_in) /= 4 .or. gsv_getDataKind(statevector_out) /= 4 ) then
 736      call utl_abort('vInterp_gsv_r4: Incorrect value for dataKind. Only compatible with dataKind=4')
 737    end if
 738
 739    if (gsv_isAssocHeightSfc(statevector_in) .and. gsv_isAssocHeightSfc(statevector_out) ) then
 740      heightSfcIn => gsv_getHeightSfc(statevector_in)
 741      heightSfcOut => gsv_getHeightSfc(statevector_out)
 742      heightSfcOut(:,:) = heightSfcIn(:,:)
 743    end if
 744
 745    ! DBGmad move to int_vInterp_gsv?
 746    ! the default is to ensure that the top of the output grid is ~equal or lower than the top of the input grid 
 747    if ( present(checkModelTop_opt) ) then
 748      checkModelTop = checkModelTop_opt
 749    else
 750      checkModelTop = .true.
 751    end if
 752    if (checkModelTop) then
 753      call msg('vInterp_gsv_r4', ' Checking that that the top of the destination grid is not higher than the top of the source grid.')
 754      if ( vcode_in == 21001 .or. vcode_out == 21001 ) then
 755        call msg('vInterp_gsv_r4', 'bypassing top check, '&
 756             //'vcode_in='//str(vcode_in)//', vcode_out='//str(vcode_out))
 757        ! Development notes (@mad001)
 758        !   we should consider having a new criterion that works for GEM-H as well
 759      else
 760        call czp_ensureCompatibleTops(vco_in, vco_out)
 761      end if
 762    end if
 763
 764    ! create reference state on output vco with reference surface fields
 765    call gsv_allocate(statevectorRef_out, statevectorRef%numstep, &
 766                      statevectorRef%hco, statevector_out%vco,      &
 767                      mpi_local_opt=statevectorRef%mpi_local,  &
 768                      mpi_distribution_opt=statevectorRef%mpi_distribution, &
 769                      dataKind_opt=gsv_getDataKind(statevectorRef), &
 770                      allocHeightSfc_opt=statevectorRef%heightSfcPresent, &
 771                      varNames_opt=(/'P0','P0LS'/) )
 772    call gsv_copy(stateVectorRef, stateVectorRef_out, allowVcoMismatch_opt=.true., &
 773                  allowVarMismatch_opt=.true.)
 774
 775    var_loop: do varIndex = 1, vnl_numvarmax
 776      varName = vnl_varNameList(varIndex)
 777      if ( .not. gsv_varExist(statevector_in,varName) ) cycle var_loop
 778
 779      nlev_in  = statevector_in%varNumLev(varIndex)
 780      nlev_out = statevector_out%varNumLev(varIndex)
 781
 782      call gsv_getField(statevector_in ,field_in,varName)
 783      call gsv_getField(statevector_out,field_out,varName)
 784
 785      ! for 2D fields and variables on "other" levels, just copy and cycle to next variable
 786      if ( (nlev_in == 1 .and. nlev_out == 1) .or. &
 787           (vnl_varLevelFromVarname(varName) == 'OT') ) then
 788        field_out(:,:,:,:) = field_in(:,:,:,:)
 789        cycle var_loop
 790      end if
 791
 792      hLikeCalcIf: if (hLikeCalc) then
 793        hLikeCalc = .false.
 794
 795        ! allocating for temporary pressure references
 796        allocate(hLikeT_in( statevector_in%myLonBeg:statevector_in%myLonEnd, &
 797                            statevector_in%myLatBeg:statevector_in%myLatEnd, &
 798                            gsv_getNumLev(statevector_in,'TH'), statevector_in%numStep))
 799        allocate(hLikeM_in( statevector_in%myLonBeg:statevector_in%myLonEnd, &
 800                            statevector_in%myLatBeg:statevector_in%myLatEnd, &
 801                            gsv_getNumLev(statevector_in,'MM'), statevector_in%numStep))
 802        allocate(hLikeT_out(statevector_out%myLonBeg:statevector_out%myLonEnd, &
 803                            statevector_out%myLatBeg:statevector_out%myLatEnd, &
 804                            gsv_getNumLev(statevector_out,'TH'), statevector_out%numStep))
 805        allocate(hLikeM_out(statevector_out%myLonBeg:statevector_out%myLonEnd, &
 806                            statevector_out%myLatBeg:statevector_out%myLatEnd, &
 807                            gsv_getNumLev(statevector_out,'MM'), statevector_out%numStep))
 808        allocate(tmpCoord_T(statevector_in%myLonBeg:statevector_in%myLonEnd, &
 809                            statevector_in%myLatBeg:statevector_in%myLatEnd, &
 810                            gsv_getNumLev(statevector_in,'TH'), statevector_in%numStep))
 811        allocate(tmpCoord_M(statevector_in%myLonBeg:statevector_in%myLonEnd, &
 812                            statevector_in%myLatBeg:statevector_in%myLatEnd, &
 813                          gsv_getNumLev(statevector_in,'MM'), statevector_in%numStep))
 814
 815        ! output grid GEM-P interpolation in log-pressure
 816        if ( vcode_out==5002 .or. vcode_out==5005 .or. vcode_out==5100 ) then
 817          call czp_calcReturnPressure_gsv_nl( statevectorRef_out, &
 818                                              PTout_r4_opt=hLikeT_out, &
 819                                              PMout_r4_opt=hLikeM_out, &
 820                                              Ps_in_hPa_opt=Ps_in_hPa_opt)
 821
 822          if ( vcode_in==5002 .or. vcode_in==5005 .or. vcode_in==5100 ) then
 823            call czp_calcReturnPressure_gsv_nl( statevectorRef, &
 824                                                PTout_r4_opt=hLikeT_in, &
 825                                                PMout_r4_opt=hLikeM_in, &
 826                                                Ps_in_hPa_opt=Ps_in_hPa_opt)
 827          else if ( vcode_in==21001 ) then
 828            call czp_calcReturnHeight_gsv_nl( statevectorRef, &
 829                                              ZTout_r4_opt=tmpCoord_T, &
 830                                              ZMout_r4_opt=tmpCoord_M)
 831            call czp_calcReturnPressure_gsv_nl( statevectorRef, &
 832                                                ZTin_r4_opt=tmpCoord_T, &
 833                                                ZMin_r4_opt=tmpCoord_M, &
 834                                                PTout_r4_opt=hLikeT_in, &
 835                                                PMout_r4_opt=hLikeM_in, &
 836                                                Ps_in_hPa_opt=Ps_in_hPa_opt)
 837          end if
 838
 839          call msg('vInterp_gsv_r4','converting pressure coordinates to height-like, '&
 840                   //'vcode_in='//str(vcode_in)//', vcode_out='//str(vcode_out))
 841          call logP_r4(hLikeM_out)
 842          call logP_r4(hLikeT_out)
 843          call logP_r4(hLikeM_in)
 844          call logP_r4(hLikeT_in)
 845
 846          ! output grid GEM-H interpolation in height
 847        else if ( vcode_out==21001 ) then
 848          call czp_calcReturnHeight_gsv_nl( statevectorRef_out, &
 849                                            ZTout_r4_opt=hLikeT_out, &
 850                                            ZMout_r4_opt=hLikeM_out)
 851          if ( vcode_in==21001 ) then
 852            call czp_calcReturnHeight_gsv_nl( statevectorRef, &
 853                                              ZTout_r4_opt=hLikeT_in, &
 854                                              ZMout_r4_opt=hLikeM_in)
 855          else if ( vcode_in==5002 .or. vcode_in==5005 .or. vcode_in==5100 ) then
 856            call czp_calcReturnPressure_gsv_nl( statevectorRef, &
 857                                                PTout_r4_opt=tmpCoord_T, &
 858                                                PMout_r4_opt=tmpCoord_M, &
 859                                                Ps_in_hPa_opt=Ps_in_hPa_opt)
 860            call czp_calcReturnHeight_gsv_nl( statevectorRef, &
 861                                              PTin_r4_opt=tmpCoord_T, &
 862                                              PMin_r4_opt=tmpCoord_M, &
 863                                              ZTout_r4_opt=hLikeT_in, &
 864                                              ZMout_r4_opt=hLikeM_in)
 865          end if
 866        end if
 867        deallocate(tmpCoord_T)
 868        deallocate(tmpCoord_M)
 869
 870      end if hLikeCalcIf
 871
 872      step_loop: do stepIndex = 1, statevector_out%numStep
 873  
 874        ! copy over some time related and other parameters
 875        statevector_out%deet                      = statevector_in%deet
 876        statevector_out%dateOriginList(stepIndex) = statevector_in%dateOriginList(stepIndex)
 877        statevector_out%npasList(stepIndex)       = statevector_in%npasList(stepIndex)
 878        statevector_out%ip2List(stepIndex)        = statevector_in%ip2List(stepIndex)
 879        statevector_out%etiket                    = statevector_in%etiket
 880        statevector_out%onPhysicsGrid(:)          = statevector_in%onPhysicsGrid(:)
 881        statevector_out%hco_physics              => statevector_in%hco_physics
 882  
 883        ! do the vertical interpolation
 884        field_out(:,:,:,stepIndex) = 0.0d0
 885        if (vnl_varLevelFromVarname(varName) == 'TH') then
 886          call hLike_interpolation_r4(hLikeT_in, hLikeT_out)
 887        else
 888          call hLike_interpolation_r4(hLikeM_in, hLikeM_out)
 889        end if
 890
 891        ! overwrite values at the lowest levels to avoid extrapolation
 892        if (vInterpCopyLowestLevel) then
 893          field_out(:,:,nlev_out,stepIndex) = field_in(:,:,nlev_in,stepIndex)
 894        end if
 895
 896      end do step_loop
 897
 898    end do var_loop
 899
 900    if (associated(hLikeT_in)) then
 901      deallocate(hLikeT_in, hLikeM_in, hLikeT_out, hLikeM_out)
 902    end if
 903    call gsv_deallocate(statevectorRef_out)
 904
 905    call msg('vInterp_gsv_r4', 'END', verb_opt=4)
 906
 907    contains
 908
 909      subroutine hLike_interpolation_r4(hLike_in, hLike_out)
 910        !
 911        !:Purpose: Proceed to actual interpolation in H-logP representation
 912        !
 913        implicit none
 914
 915        ! Arguments:
 916        real(4), pointer, intent(in)  :: hLike_in(:,:,:,:)  ! abstract height dimensioned input coordinate
 917        real(4), pointer, intent(in)  :: hLike_out(:,:,:,:) ! abstract height dimensioned target coordinate
 918
 919        ! Locals:
 920        integer :: latIndex, lonIndex, levIndex_out, levIndex_in
 921        real(4) :: zwb, zwt
 922
 923        !$OMP PARALLEL DO PRIVATE(latIndex,lonIndex,levIndex_in,levIndex_out,zwb,zwt)
 924        do latIndex = statevector_out%myLatBeg, statevector_out%myLatEnd
 925          do lonIndex = statevector_out%myLonBeg, statevector_out%myLonEnd
 926            levIndex_in = 1
 927            do levIndex_out = 1, nlev_out
 928              levIndex_in = levIndex_in + 1
 929              do while(hLike_out(lonIndex,latIndex,levIndex_out,stepIndex) &
 930                        .gt.hLike_in(lonIndex,latIndex,levIndex_in,stepIndex)  &
 931                       .and.levIndex_in.lt.nlev_in)
 932                levIndex_in = levIndex_in + 1
 933              end do
 934              levIndex_in = levIndex_in - 1
 935              zwb = (hLike_out(lonIndex,latIndex,levIndex_out,stepIndex) &
 936                      - hLike_in(lonIndex,latIndex,levIndex_in,stepIndex)) &
 937                   /(hLike_in(lonIndex,latIndex,levIndex_in+1,stepIndex) &
 938                      - hLike_in(lonIndex,latIndex,levIndex_in,stepIndex))
 939              zwt = 1.d0 - zwb
 940              field_out(lonIndex,latIndex,levIndex_out,stepIndex) =   &
 941                                 zwb*field_in(lonIndex,latIndex,levIndex_in+1,stepIndex) &
 942                               + zwt*field_in(lonIndex,latIndex,levIndex_in,stepIndex)
 943            end do
 944          end do
 945        end do
 946        !$OMP END PARALLEL DO
 947
 948      end subroutine hLike_interpolation_r4
 949
 950  end subroutine vInterp_gsv_r4
 951
 952  !--------------------------------------------------------------------------
 953  ! logP_r4
 954  !--------------------------------------------------------------------------
 955  subroutine logP_r4(presInLogOut)
 956    !
 957    !:Purpose: compute log of pressurce field
 958    !
 959    implicit none
 960
 961    ! Arguments:
 962    real(4), intent(inout) :: presInLogOut(:,:,:,:)
 963
 964    ! Locals:
 965    integer :: latIdx, lonIdx, levIdx, stepIdx
 966
 967    !$OMP PARALLEL DO PRIVATE(lonIdx,latIdx,levIdx,stepIdx)
 968    do stepIdx = lbound(presInLogOut,4), ubound(presInLogOut,4)
 969      do levIdx = lbound(presInLogOut,3), ubound(presInLogOut,3)
 970        do latIdx = lbound(presInLogOut,2), ubound(presInLogOut,2)
 971          do lonIdx = lbound(presInLogOut,1), ubound(presInLogOut,1)
 972            presInLogOut(lonIdx,latIdx,levIdx,stepIdx) = &
 973                 log(presInLogOut(lonIdx,latIdx,levIdx,stepIdx))
 974          end do
 975        end do
 976      end do
 977    end do
 978    !$OMP END PARALLEL DO
 979
 980  end subroutine logP_r4
 981
 982  !--------------------------------------------------------------------------
 983  ! int_tInterp_gsv
 984  !--------------------------------------------------------------------------
 985  subroutine int_tInterp_gsv(statevector_in,statevector_out)
 986    !
 987    !:Purpose: Time interpolation from statevector with low temporal resolution
 988    !           to statevector with high temporal resolution.
 989    !
 990    implicit none
 991
 992    ! Arguments:
 993    type(struct_gsv),  intent(in)    :: statevector_in  ! Statevector input
 994    type(struct_gsv),  intent(inout) :: statevector_out ! Statevector with target temporal structure and results
 995
 996    ! Locals:
 997    integer :: kIndex, latIndex, lonIndex
 998    integer :: stepIndexIn1, stepIndexIn2, stepIndexOut, numStepIn, numStepOut
 999    integer :: lon1, lon2, lat1, lat2, k1, k2
1000    integer :: dateStampIn, dateStampOut 
1001    real(8) :: weight1, weight2
1002    real(8) :: deltaHour, deltaHourInOut
1003    real(4), pointer  :: gdIn_r4(:,:,:,:), gdOut_r4(:,:,:,:)
1004    real(8), pointer  :: gdIn_r8(:,:,:,:), gdOut_r8(:,:,:,:)
1005
1006    call msg('int_tInterp_gsv', 'START', verb_opt=2)
1007
1008    ! read the namelist
1009    call int_readNml()
1010
1011    if ( .not. vco_equal(statevector_in%vco, statevector_out%vco) ) then
1012      call utl_abort('int_tInterp_gsv: The input and output statevectors are not on the same vertical levels')
1013    end if
1014
1015    if ( .not. hco_equal(statevector_in%hco, statevector_out%hco) ) then
1016      call utl_abort('int_tInterp_gsv: The input and output statevectors are not on the same horizontal grid.')
1017    end if
1018
1019    if ( statevector_in%numStep > statevector_out%numStep ) then
1020      call msg('int_tInterp_gsv', 'numStep_out is less than numStep_in, calling gsv_copy.')
1021      call gsv_copy(statevector_in, statevector_out, allowTimeMismatch_opt=.true.)
1022      return
1023    else if ( statevector_in%numStep == statevector_out%numStep ) then
1024      call msg('int_tInterp_gsv', 'numStep_out is equal to numStep_in, calling gsv_copy.')
1025      call gsv_copy(statevector_in, statevector_out, allowVarMismatch_opt=.true.)
1026      return
1027    end if
1028
1029    lon1 = statevector_in%myLonBeg
1030    lon2 = statevector_in%myLonEnd
1031    lat1 = statevector_in%myLatBeg
1032    lat2 = statevector_in%myLatEnd
1033    k1 = statevector_in%mykBeg
1034    k2 = statevector_in%mykEnd
1035
1036    numStepIn = statevector_in%numStep
1037    numStepOut = statevector_out%numStep
1038    call msg('int_tInterp_gsv', 'numStepIn='//str(numStepIn)&
1039         //', numStepOut='//str(numStepOut), mpiAll_opt=.false.)
1040
1041    ! compute positive deltaHour between two first stepIndex of statevector_in (input temporal grid). 
1042    ! If numStepIn == 1, no time interpolation needed (weights are set to zero).
1043    if ( numStepIn > 1 ) then
1044      call difdatr(statevector_in%dateStampList(1), statevector_in%dateStampList(2), deltaHour)
1045      deltaHour = abs(deltaHour)
1046    else
1047      deltaHour = 0.0d0
1048    end if
1049
1050    do stepIndexOut = 1, numStepOut
1051      if ( numStepIn == 1 ) then
1052        stepIndexIn1 = 1
1053        stepIndexIn2 = 1
1054        weight1 = 1.0d0 
1055        weight2 = 0.0d0 
1056        deltaHourInOut = 0.0d0
1057      else
1058        ! find staevector_in%dateStamp on the left and right
1059        if ( statevector_in%dateStampList(numStepIn) == statevector_out%dateStampList(stepIndexOut) ) then
1060          stepIndexIn2 = numStepIn
1061        else
1062          stepInLoop: do stepIndexIn2 = 1, numStepIn
1063            dateStampIn = statevector_in%dateStampList(stepIndexIn2)
1064            dateStampOut = statevector_out%dateStampList(stepIndexOut)
1065            call difdatr(dateStampIn, dateStampOut, deltaHourInOut)
1066            if ( deltaHourInOut > 0.0d0 ) exit stepInLoop
1067          end do stepInLoop
1068        end if
1069        stepIndexIn1 = stepIndexIn2 - 1
1070
1071        ! compute deltaHour between left stepIndex of statevector_in and statevector_out
1072        dateStampIn = statevector_in%dateStampList(stepIndexIn1)
1073        dateStampOut = statevector_out%dateStampList(stepIndexOut)
1074        call difdatr(dateStampOut, dateStampIn, deltaHourInOut)
1075        if ( deltaHourInOut < 0.0d0 ) then 
1076          call utl_abort('int_tInterp_gsv: deltaHourInOut should be greater or equal to 0')
1077        end if
1078
1079        ! compute the interpolation weights for left stepIndex of statevector_in (weight1) and right stepIndex of statevector_in (weight2) 
1080        weight1 = 1.0d0 - deltaHourInOut / deltaHour
1081        weight2 = deltaHourInOut / deltaHour
1082      end if
1083
1084      call msg('int_tInterp_gsv', 'for stepIndexOut='//str(stepIndexOut) &
1085           //', stepIndexIn1='//str(stepIndexIn1)//', stepIndexIn2='//str(stepIndexIn2) &
1086           //', weight1='//str(weight1)//', weight2='//str(weight2) &
1087           //', deltaHourInOut/deltaHour='//str(deltaHourInOut)//'/'//str(deltaHour), &
1088           mpiAll_opt=.false.)
1089
1090      if ( gsv_getDataKind(statevector_in) == 4 .and. gsv_getDataKind(statevector_out) == 4 ) then
1091        call gsv_getField(statevector_in, gdIn_r4)
1092        call gsv_getField(statevector_out, gdOut_r4)
1093        !$OMP PARALLEL DO PRIVATE (latIndex,kIndex,lonIndex)
1094        do kIndex = k1, k2
1095          do latIndex = lat1, lat2
1096            do lonIndex = lon1, lon2
1097              gdOut_r4(lonIndex,latIndex,kIndex,stepIndexOut) =  &
1098                real(weight1,4) * gdIn_r4(lonIndex,latIndex,kIndex,stepIndexIn1) + &
1099                real(weight2,4) * gdIn_r4(lonIndex,latIndex,kIndex,stepIndexIn2)
1100            end do
1101          end do
1102        end do
1103        !$OMP END PARALLEL DO
1104      else if ( gsv_getDataKind(statevector_in) == 4 .and. gsv_getDataKind(statevector_out) == 8 ) then
1105        call gsv_getField(statevector_in, gdIn_r4)
1106        call gsv_getField(statevector_out, gdOut_r8)
1107        !$OMP PARALLEL DO PRIVATE (latIndex,kIndex,lonIndex)
1108        do kIndex = k1, k2
1109          do latIndex = lat1, lat2
1110            do lonIndex = lon1, lon2
1111              gdOut_r8(lonIndex,latIndex,kIndex,stepIndexOut) =  &
1112                weight1 * real(gdIn_r4(lonIndex,latIndex,kIndex,stepIndexIn1),8) + &
1113                weight2 * real(gdIn_r4(lonIndex,latIndex,kIndex,stepIndexIn2),8)
1114            end do
1115          end do
1116        end do
1117        !$OMP END PARALLEL DO
1118      else if ( gsv_getDataKind(statevector_in) == 8 .and. gsv_getDataKind(statevector_out) == 4 ) then
1119        call gsv_getField(statevector_in, gdIn_r8)
1120        call gsv_getField(statevector_out, gdOut_r4)
1121        !$OMP PARALLEL DO PRIVATE (latIndex,kIndex,lonIndex)
1122        do kIndex = k1, k2
1123          do latIndex = lat1, lat2
1124            do lonIndex = lon1, lon2
1125              gdOut_r4(lonIndex,latIndex,kIndex,stepIndexOut) =  &
1126                real(weight1 * gdIn_r8(lonIndex,latIndex,kIndex,stepIndexIn1),4) + &
1127                real(weight2 * gdIn_r8(lonIndex,latIndex,kIndex,stepIndexIn2),4)
1128            end do
1129          end do
1130        end do
1131        !$OMP END PARALLEL DO
1132      else if ( gsv_getDataKind(statevector_in) == 8 .and. gsv_getDataKind(statevector_out) == 8 ) then
1133        call gsv_getField(statevector_in, gdIn_r8)
1134        call gsv_getField(statevector_out, gdOut_r8)
1135        !$OMP PARALLEL DO PRIVATE (latIndex,kIndex,lonIndex)
1136        do kIndex = k1, k2
1137          do latIndex = lat1, lat2
1138            do lonIndex = lon1, lon2
1139              gdOut_r8(lonIndex,latIndex,kIndex,stepIndexOut) =  &
1140                weight1 * gdIn_r8(lonIndex,latIndex,kIndex,stepIndexIn1) + &
1141                weight2 * gdIn_r8(lonIndex,latIndex,kIndex,stepIndexIn2)
1142            end do
1143          end do
1144        end do
1145        !$OMP END PARALLEL DO
1146      end if
1147
1148    end do
1149
1150    call msg('int_tInterp_gsv', 'END', verb_opt=2)
1151
1152  end subroutine int_tInterp_gsv
1153
1154  !--------------------------------------------------------------------------
1155  ! int_vInterp_col
1156  !--------------------------------------------------------------------------
1157  subroutine int_vInterp_col(column_in,column_out,varName,useColumnPressure_opt)
1158    !
1159    !:Purpose: Vertical interpolation of a columData object
1160    !
1161    implicit none
1162
1163    ! Arguments:
1164    type(struct_columnData),  intent(in)    :: column_in              ! ColumnData input
1165    type(struct_columnData),  intent(inout) :: column_out             ! columnData with the vert structure and results
1166    character(len=*),         intent(in)    :: varName                ! variable name to be interpolated
1167    logical, optional,        intent(in)    :: useColumnPressure_opt  ! if .true. use P_* instead of the pressure provided by calcHeightAndPressure_mod
1168
1169    ! Locals:
1170    real(8), pointer :: varInterp_in(:), varInterp_out(:)
1171    real(8), pointer :: coordRef_in(:)  , coordRef_out(:)
1172    real(8), pointer :: ZT_in(:,:), ZM_in(:,:), PT_in(:,:), PM_in(:,:)
1173    real(8), pointer :: PT_out(:,:), PM_out(:,:)
1174    character(len=4) :: varLevel
1175    real(8)          :: zwb, zwt
1176    integer          :: levIndex_out, levIndex_in, columnIndex
1177    integer          :: vcode_in, vcode_out
1178    integer          :: nLevIn_T, nLevIn_M, nLevOut_T, nLevOut_M
1179    logical          :: vInterp, useColumnPressure
1180    integer, allocatable, target :: THlevelWanted(:), MMlevelWanted(:)
1181    integer, pointer :: levelWanted(:)
1182
1183    call msg('int_vInterp_col', 'START', verb_opt=2)
1184    varLevel = vnl_varLevelFromVarname(varName)
1185
1186    if ( present(useColumnPressure_opt) ) then
1187      useColumnPressure = useColumnPressure_opt
1188    else
1189      useColumnPressure = .true.
1190    end if
1191
1192    call msg('int_vInterp_col', varName//' ('//varLevel &
1193         //'), useColumnPressure='//str(useColumnPressure), verb_opt=3)
1194
1195    vInterp = .true.
1196    if ( .not. col_varExist(column_in,'P0' ) ) then
1197      call msg('int_vInterp_col', 'P0 is missing. Vertical interpolation WILL NOT BE PERFORMED')
1198      vInterp = .false.
1199    else if ( col_getNumLev(column_in ,'TH') <= 1 .or. &
1200              col_getNumLev(column_in ,'MM') <= 1 ) then
1201      vInterp = .false.
1202      call msg('int_vInterp_col', 'The input backgrounds are 2D. Vertical interpolation WILL NOT BE PERFORMED')
1203    end if
1204
1205    vcode_in  = column_in%vco%vcode
1206    vcode_out = column_out%vco%vcode
1207    nLevIn_T  = col_getNumLev(column_in,  'TH')
1208    nLevIn_M  = col_getNumLev(column_in,  'MM')
1209    nLevOut_T = col_getNumLev(column_out, 'TH')
1210    nLevOut_M = col_getNumLev(column_out, 'MM')
1211
1212    if_vInterp: if (vInterp) then
1213      if ( .not. useColumnPressure ) then
1214
1215        call msg('int_vInterp_col', 'vcode_in='//str(vcode_in)//', '&
1216             //'vcode_out='//str(vcode_out), verb_opt=3)
1217
1218        ! Compute interpolation variables (pressures and or heights)
1219        allocate(PT_in( col_getNumCol(column_in),  nLevIn_T))
1220        allocate(PM_in( col_getNumCol(column_in),  nLevIn_M))
1221        allocate(ZT_in( col_getNumCol(column_in),  nLevIn_T))
1222        allocate(ZM_in( col_getNumCol(column_in),  nLevIn_M))
1223        allocate(PT_out(col_getNumCol(column_out), nLevOut_T))
1224        allocate(PM_out(col_getNumCol(column_out), nLevOut_M))
1225
1226        call czp_calcReturnPressure_col_nl(column_in, PT_in, PM_in)
1227        call czp_calcReturnHeight_col_nl(column_in, ZT_in, ZM_in)
1228        call czp_calcReturnPressure_col_nl(column_out, PT_out, PM_out)
1229
1230      end if    ! useColumnPressure
1231
1232      do columnIndex = 1, col_getNumCol(column_out)
1233
1234        ! coordRef_{in,out}
1235        if ( varLevel == 'TH' ) then
1236          if ( .not. useColumnPressure ) then
1237            coordRef_in  => PT_in(columnIndex,:)
1238            coordRef_out => PT_out(columnIndex,:)
1239          else
1240            coordRef_in  => col_getColumn(column_in,columnIndex,'P_T')
1241            coordRef_out => col_getColumn(column_out,columnIndex,'P_T')
1242          end if
1243        else if ( varLevel == 'MM' ) then
1244          if ( .not. useColumnPressure ) then
1245            coordRef_in  => PM_in(columnIndex,:)
1246            coordRef_out => PM_out(columnIndex,:)
1247          else
1248            coordRef_in  => col_getColumn(column_in,columnIndex,'P_M')
1249            coordRef_out => col_getColumn(column_out,columnIndex,'P_M')
1250          end if
1251        else
1252          call utl_abort('int_vInterp_col: only varLevel TH/MM is allowed')
1253        end if
1254
1255        varInterp_in  => col_getColumn(column_in ,columnIndex,varName)
1256        varInterp_out => col_getColumn(column_out,columnIndex,varName)
1257
1258        if ( columnIndex == 1 .and. (trim(varName) == 'P_T' ) ) then
1259          call msg('int_vInterp_col', 'useColumnPressure='//str(useColumnPressure) &
1260               //', '//trim(varName)//':' &
1261               //new_line('')//'COLUMN_IN(1):'//str(varInterp_in(:))&
1262               //new_line('')//'COLUMN_OUT(1):'//str(varInterp_out(:)), &
1263               mpiAll_opt=.false.)
1264        end if
1265
1266        ! actual interpolation
1267        !   Development notes (@mad001)
1268        !     Potential issue with GEM-H height based interpolation
1269        !     we should consider to convert to pure logP interpolation
1270        !     as we did for int_vInterp_gsv
1271        !     see also #466 https://gitlab.science.gc.ca/atmospheric-data-assimilation/midas/issues/466#note_497052
1272        levIndex_in = 1
1273        do levIndex_out = 1, col_getNumLev(column_out,varLevel)
1274          levIndex_in = levIndex_in + 1
1275          do while( coordRef_out(levIndex_out) .gt. coordRef_in(levIndex_in) .and. &
1276               levIndex_in .lt. col_getNumLev(column_in,varLevel) )
1277            levIndex_in = levIndex_in + 1
1278          end do
1279          levIndex_in = levIndex_in - 1
1280          zwb = log(coordRef_out(levIndex_out)/coordRef_in(levIndex_in))/  &
1281               log(coordRef_in(levIndex_in+1)/coordRef_in(levIndex_in))
1282          zwt = 1. - zwb
1283          if (  useColumnPressure .and. &
1284              (trim(varName) == 'P_T' .or. trim(varName) == 'P_M' ) ) then
1285            ! do nothing, i.e. use the pressures from column_in
1286          else if ( .not. useColumnPressure .and. &
1287              (trim(varName) == 'P_T' .or. trim(varName) == 'P_M' ) ) then
1288            varInterp_out(levIndex_out) = exp(zwb*log(varInterp_in(levIndex_in+1)) + zwt*log(varInterp_in(levIndex_in)))
1289          else
1290            varInterp_out(levIndex_out) = zwb*varInterp_in(levIndex_in+1) + zwt*varInterp_in(levIndex_in)
1291          end if
1292        end do
1293      end do
1294
1295      call msg('int_vInterp_col', trim(varName)//' (in): '//str(varInterp_in)&
1296           //new_line('')//trim(varName)//' (out): '//str(varInterp_out), verb_opt=3)
1297
1298    else if_vInterp
1299
1300      if (column_out%vco%nlev_T > 0 .and. column_out%vco%nlev_M > 0) then
1301
1302        ! Find which levels in column_in matches column_out
1303        allocate(THlevelWanted(column_out%vco%nlev_T))
1304        allocate(MMlevelWanted(column_out%vco%nlev_M))
1305
1306        call vco_levelMatchingList( THlevelWanted, MMlevelWanted, & ! OUT
1307                                    column_out%vco, column_in%vco ) ! IN
1308
1309        if ( any(THlevelWanted == -1) .or. any(MMlevelWanted == -1) ) then
1310          call utl_abort('int_vInterp_col: column_out is not a subsets of column_in!')
1311        end if
1312
1313        ! Transfer the corresponding data
1314        do columnIndex = 1, col_getNumCol(column_out)
1315          varInterp_in  => col_getColumn(column_in ,columnIndex,varName)
1316          varInterp_out => col_getColumn(column_out,columnIndex,varName)
1317          if (vnl_varLevelFromVarname(varName) == 'TH') then
1318            levelWanted => THlevelWanted
1319          else
1320            levelWanted => MMlevelWanted
1321          end if
1322          do levIndex_out = 1, col_getNumLev(column_out,varLevel)
1323            varInterp_out(levIndex_out) = varInterp_in(levelWanted(levIndex_out))
1324          end do
1325        end do
1326
1327        deallocate(THlevelWanted)
1328        deallocate(MMlevelWanted)
1329
1330      else if (column_out%vco%nlev_depth > 0) then
1331        call msg('int_vInterp_col', 'vco_levelMatchingList: no MM and TH levels, but depth levels exist')
1332        if (any(column_out%vco%depths(:) /= column_in%vco%depths(:))) then
1333          call utl_abort('int_vInterp_col: some depth levels not equal')
1334        else
1335          ! copy over depth levels
1336          do columnIndex = 1, col_getNumCol(column_out)
1337            varInterp_in  => col_getColumn(column_in ,columnIndex,varName)
1338            varInterp_out => col_getColumn(column_out,columnIndex,varName)
1339            do levIndex_out = 1, col_getNumLev(column_out,varLevel)
1340              varInterp_out(levIndex_out) = varInterp_in(levIndex_out)
1341            end do
1342          end do
1343        end if
1344
1345      end if
1346
1347    end if if_vInterp
1348
1349    call msg('int_vInterp_col', 'END', verb_opt=2)
1350  end subroutine int_vInterp_col
1351
1352  !--------------------------------------------------------------------------
1353  ! int_setezopt
1354  !--------------------------------------------------------------------------
1355  subroutine int_setezopt(interpDegree, extrapDegree_opt)
1356    !
1357    !:Purpose: Wrapper subroutine for rmnlib routine setezopt.
1358    !
1359    implicit none
1360
1361    ! Arguments:
1362    character(len=*),           intent(in) :: interpDegree
1363    character(len=*), optional, intent(in) :: extrapDegree_opt
1364
1365    ! Locals:
1366    character(len=12) :: extrapDegree
1367    integer           :: ierr, ezsetopt, ezsetval
1368
1369    call msg('int_setezopt', 'START', verb_opt=4)
1370    if ( trim(interpDegree) /= 'LINEAR' .and. &
1371         trim(interpDegree) /= 'CUBIC' .and. &
1372         trim(interpDegree) /= 'NEAREST' ) then
1373
1374      call utl_abort('int_setezopt: invalid interpolation degree = '//trim(interpDegree))
1375    end if
1376
1377    if ( present(extrapDegree_opt) ) then
1378      extrapDegree = extrapDegree_opt
1379    else
1380      extrapDegree = 'VALUE'
1381    end if
1382
1383    ierr = ezsetopt('INTERP_DEGREE', interpDegree)
1384    if ( trim(extrapDegree) == 'VALUE' ) then
1385      ierr = ezsetval('EXTRAP_VALUE', 0.0)
1386    end if
1387    ierr = ezsetopt('EXTRAP_DEGREE', extrapDegree)
1388
1389    call msg('int_setezopt', 'END', verb_opt=4)
1390  end subroutine int_setezopt
1391
1392  !--------------------------------------------------------------------------
1393  ! int_hInterpScalar_gsv
1394  !--------------------------------------------------------------------------
1395  function int_hInterpScalar_gsv(stateVectorOut, stateVectorIn, varName, levIndex, stepIndex, &
1396                        interpDegree, extrapDegree_opt) result(ierr)
1397    !
1398    !:Purpose: Horizontal interpolation of 2D scalar field that use stateVector
1399    !           objects for input and output. Accessed through int_hInterpScalar.
1400    !
1401    implicit none
1402
1403    ! Arguments:
1404    type(struct_gsv),           intent(inout) :: stateVectorOut
1405    type(struct_gsv),           intent(inout) :: stateVectorIn
1406    character(len=*),           intent(in)    :: varName
1407    integer         ,           intent(in)    :: levIndex
1408    integer         ,           intent(in)    :: stepIndex
1409    character(len=*),           intent(in)    :: interpDegree
1410    character(len=*), optional, intent(in)    :: extrapDegree_opt
1411    ! Result:
1412    integer :: ierr
1413
1414    ! Locals:
1415    real(4), pointer :: fieldOut_r4(:,:,:,:), fieldIn_r4(:,:,:,:)
1416    real(8), pointer :: fieldOut_r8(:,:,:,:), fieldIn_r8(:,:,:,:)
1417    real(8), pointer :: heightSfcOut(:,:), heightSfcIn(:,:)
1418    integer :: ezsint, ezdefset
1419
1420    call msg('int_hInterpScalar_gsv', 'START', verb_opt=4)
1421    ! read the namelist
1422    call int_readNml()
1423
1424    ! check if special interpolation is required
1425    if (stateVectorIn%hco%initialized .and. stateVectorOut%hco%initialized) then
1426      if (stateVectorIn%hco%grtyp == 'Y') then
1427        ! for now, only comptable for real(4)
1428        if( gsv_getDataKind(stateVectorOut) /= 4 .or. gsv_getDataKind(stateVectorIn) /= 4) then
1429          call utl_abort('int_hInterpScalar_gsv: cloudToGrid only implemented for real(4)')
1430        end if
1431        ierr = int_sintCloudToGrid_gsv(stateVectorOut, stateVectorIn, varName, levIndex, stepIndex)
1432        return
1433      end if
1434    end if
1435
1436    ! do the standard interpolation
1437
1438    ierr = ezdefset(stateVectorOut%hco%EZscintID, stateVectorIn%hco%EZscintID)
1439    call int_setezopt(interpDegree, extrapDegree_opt)   
1440
1441    if (trim(varName) == 'ZSFC') then
1442
1443      heightSfcIn  => gsv_getHeightSfc(stateVectorIn)
1444      heightSfcOut => gsv_getHeightSfc(stateVectorOut)
1445
1446      ! allocate real(4) buffers and copy to/from for interpolation
1447      allocate(fieldIn_r4(stateVectorIn%hco%ni,stateVectorIn%hco%nj,1,1))
1448      allocate(fieldOut_r4(stateVectorOut%hco%ni,stateVectorOut%hco%nj,1,1))
1449      fieldIn_r4(:,:,1,1) = heightSfcIn(:,:)
1450      ierr = ezsint(fieldOut_r4(:,:,1,1),fieldIn_r4(:,:,1,1))
1451      heightSfcOut(:,:) = fieldOut_r4(:,:,1,1)
1452      deallocate(fieldIn_r4,fieldOut_r4)
1453
1454    else if ( gsv_getDataKind(stateVectorOut) == 4 .and. gsv_getDataKind(stateVectorIn) == 4) then
1455
1456      if (trim(varName) == 'ALL') then
1457        call gsv_getField(stateVectorOut, fieldOut_r4)
1458        call gsv_getField(stateVectorIn,  fieldIn_r4)
1459     else
1460        call gsv_getField(stateVectorOut, fieldOut_r4, varName)
1461        call gsv_getField(stateVectorIn,  fieldIn_r4,  varName)
1462      end if
1463
1464      ierr = ezsint(fieldOut_r4(:,:,levIndex,stepIndex),fieldIn_r4(:,:,levIndex,stepIndex))
1465
1466    else if ( gsv_getDataKind(stateVectorOut) == 8 .and. gsv_getDataKind(stateVectorIn) == 8) then
1467
1468      if (trim(varName) == 'ALL') then
1469        call gsv_getField(stateVectorOut, fieldOut_r8)
1470        call gsv_getField(stateVectorIn,  fieldIn_r8)
1471      else
1472        call gsv_getField(stateVectorOut, fieldOut_r8, varName)
1473        call gsv_getField(stateVectorIn,  fieldIn_r8,  varName)
1474      end if
1475
1476      ! allocate real(4) buffers and copy to/from for interpolation
1477      allocate(fieldIn_r4(stateVectorIn%hco%ni,stateVectorIn%hco%nj,1,1))
1478      allocate(fieldOut_r4(stateVectorOut%hco%ni,stateVectorOut%hco%nj,1,1))
1479      fieldIn_r4(:,:,1,1) = fieldIn_r8(:,:,levIndex,stepIndex)
1480      ierr = ezsint(fieldOut_r4(:,:,1,1),fieldIn_r4(:,:,1,1))
1481      fieldOut_r8(:,:,levIndex,stepIndex) = fieldOut_r4(:,:,1,1)
1482      deallocate(fieldIn_r4,fieldOut_r4)
1483
1484    else
1485
1486      call utl_abort('int_hInterpScalar_gsv: not implemented for mixed dataKind')
1487
1488    end if
1489
1490    call msg('int_hInterpScalar_gsv', 'END', verb_opt=4)
1491  end function int_hInterpScalar_gsv
1492
1493  !--------------------------------------------------------------------------
1494  ! int_hInterpScalar_r4_2d
1495  !--------------------------------------------------------------------------
1496  function int_hInterpScalar_r4_2d(fieldOut_r4, fieldIn_r4, interpDegree, extrapDegree_opt) result(ierr)
1497    !
1498    !:Purpose: Horizontal interpolation of 2D scalar field that use real(4) arrays
1499    !           for input and output. Accessed through int_hInterpScalar.
1500    !
1501    implicit none
1502
1503    ! Arguments:
1504    real(4),                    intent(inout) :: fieldOut_r4(:,:)
1505    real(4),                    intent(in)    :: fieldIn_r4(:,:)
1506    character(len=*),           intent(in)    :: interpDegree
1507    character(len=*), optional, intent(in)    :: extrapDegree_opt
1508    ! Result:
1509    integer :: ierr
1510
1511    ! Locals:
1512    integer :: ezsint
1513
1514    call msg('int_hInterpScalar_r4_2d', 'START', verb_opt=4)
1515    ! read the namelist
1516    call int_readNml()
1517
1518    ! do the standard interpolation
1519    call int_setezopt(interpDegree, extrapDegree_opt)   
1520    ierr = ezsint(fieldOut_r4,fieldIn_r4)
1521
1522    call msg('int_hInterpScalar_r4_2d', 'END', verb_opt=4)
1523  end function int_hInterpScalar_r4_2d
1524
1525  !--------------------------------------------------------------------------
1526  ! int_sintCloudToGrid_gsv
1527  !--------------------------------------------------------------------------
1528  function int_sintCloudToGrid_gsv(stateVectorGrid, stateVectorCloud, varName, levIndex, stepIndex) result(ierr)
1529    !
1530    !:Purpose: Perform horizontal interpolation for 1 level and time step (and variable)
1531    !           in the case where the input data is a cloud of points (i.e. a Y grid) and
1532    !           the output is on a regular grid. Accessed through int_hInterpScalar.
1533    !
1534    !:Note:  When varName=='ALL', the argument levIndex is actually kIndex
1535    !
1536    implicit none
1537
1538    ! Arguments:
1539    type(struct_gsv), intent(inout) :: stateVectorGrid
1540    type(struct_gsv), intent(inout) :: stateVectorCloud
1541    character(len=*), intent(in)    :: varName
1542    integer,          intent(in)    :: levIndex
1543    integer,          intent(in)    :: stepIndex
1544    ! Result:
1545    integer                         :: ierr
1546
1547    ! Locals:
1548    integer :: gdxyfll, omp_get_thread_num
1549    integer :: niCloud, njCloud, niGrid, njGrid, myThreadNum
1550    integer :: top, bottom, left, right, numBoxIndexes, lonIndexCloud, latIndexCloud
1551    integer :: boxSize, lonBoxIndex, latBoxIndex, boxIndex, lonIndexGrid, latIndexGrid
1552    integer :: lonBoxIndexes(100), latBoxIndexes(100), ngp
1553    integer :: nfill(mmpi_numThread), nhole(mmpi_numThread), nextrap0, nextrap1
1554    integer, allocatable :: numFilledByAvg(:,:), filledByInterp(:,:), maskGrid(:,:), maskCloud(:,:)
1555    real(4), pointer     :: fieldCloud_4d(:,:,:,:), fieldGrid_4d(:,:,:,:)
1556    real(4), pointer     :: fieldCloud(:,:), fieldGrid(:,:)
1557    real(4), allocatable :: fieldGrid_tmp(:,:)
1558    real(4), allocatable :: xCloud(:,:), yCloud(:,:)
1559    real(8) :: extrapValue
1560    integer, parameter        :: maxNumLocalGridPointsSearch = 200000
1561    integer                   :: numLocalGridPointsFound, gridIndex
1562    type(kdtree2), pointer    :: tree => null()
1563    real(kdkind), allocatable :: positionArray(:,:)
1564    type(kdtree2_result)      :: searchResults(maxNumLocalGridPointsSearch)
1565    real(kdkind)              :: searchRadiusSquared
1566    real(kdkind)              :: refPosition(3)
1567
1568    call utl_tmg_start(176, 'low-level--int_sintCloudToGrid_gsv')
1569    call msg('int_sintCloudToGrid_gsv', 'START', verb_opt=2)
1570
1571    niCloud = stateVectorCloud%hco%ni
1572    njCloud = stateVectorCloud%hco%nj
1573    niGrid  = stateVectorGrid%hco%ni
1574    njGrid  = stateVectorGrid%hco%nj
1575
1576    if (trim(varName) == 'ALL') then
1577      call gsv_getField(stateVectorGrid,  fieldGrid_4d)
1578      call gsv_getField(stateVectorCloud, fieldCloud_4d)
1579    else
1580      call gsv_getField(stateVectorGrid,  fieldGrid_4d,  varName)
1581      call gsv_getField(stateVectorCloud, fieldCloud_4d, varName)
1582    end if
1583    fieldGrid  => fieldGrid_4d(:,:,levIndex,stepIndex)
1584    fieldCloud => fieldCloud_4d(:,:,levIndex,stepIndex)
1585
1586    allocate(xCloud(niCloud, njCloud))
1587    allocate(yCloud(niCloud, njCloud))
1588    allocate(numFilledByAvg(niGrid, njGrid))
1589    allocate(filledByInterp(niGrid, njGrid))
1590    allocate(maskCloud(niCloud, njCloud))
1591    allocate(maskGrid(niGrid, njGrid))
1592    allocate(fieldGrid_tmp(niGrid,njGrid))
1593
1594    ! set masks based on oceanMasks (if present)
1595    if (stateVectorCloud%oceanMask%maskPresent) then
1596      maskCloud(:,:) = 0
1597      if (trim(varName) == 'ALL') then
1598        ! when varName==ALL, the argument levIndex is actually kIndex
1599        where(stateVectorCloud%oceanMask%mask(:,:,gsv_getLevFromK(stateVectorCloud,levIndex))) maskCloud(:,:) = 1
1600      else
1601        where(stateVectorCloud%oceanMask%mask(:,:,levIndex)) maskCloud(:,:) = 1
1602      end if
1603    else
1604      maskCloud(:,:) = 1
1605    end if
1606    if (stateVectorGrid%oceanMask%maskPresent) then
1607      maskGrid(:,:) = 0
1608      if (trim(varName) == 'ALL') then
1609        ! when varName==ALL, the argument levIndex is actually kIndex
1610        where(stateVectorGrid%oceanMask%mask(:,:,gsv_getLevFromK(stateVectorGrid,levIndex))) maskGrid(:,:) = 1
1611      else
1612        where(stateVectorGrid%oceanMask%mask(:,:,levIndex)) maskGrid(:,:) = 1
1613      end if
1614    else
1615      maskGrid(:,:)  = 1
1616    end if
1617
1618    ! Calcul des pos. x-y des eclairs sur la grille modele
1619    ierr = gdxyfll(stateVectorGrid%hco%EZscintID, xCloud, yCloud, &
1620                   stateVectorCloud%hco%lat2d_4*MPC_DEGREES_PER_RADIAN_R4, &
1621                   stateVectorCloud%hco%lon2d_4*MPC_DEGREES_PER_RADIAN_R4, &
1622                   stateVectorCloud%hco%ni*stateVectorCloud%hco%nj)
1623
1624    ! Average values of cloud points neighbouring a grid location
1625    fieldGrid(:,:) = 0.0
1626    numFilledByAvg(:,:) = 0
1627    do latIndexCloud = 1, njCloud
1628      do lonIndexCloud = 1, niCloud
1629        lonIndexGrid = nint(xCloud(lonIndexCloud,latIndexCloud))
1630        latIndexGrid = nint(yCloud(lonIndexCloud,latIndexCloud))
1631        if ( lonIndexGrid >= 1 .and. latIndexGrid >= 1 .and. &
1632             lonIndexGrid <= niGrid .and. latIndexGrid <= njGrid ) then
1633          if ( maskCloud(lonIndexCloud,latIndexCloud) == 1 ) then
1634            fieldGrid(lonIndexGrid,latIndexGrid) = fieldGrid(lonIndexGrid,latIndexGrid) +  &
1635                                                   fieldCloud(lonIndexCloud,latIndexCloud)
1636            numFilledByAvg(lonIndexGrid,latIndexGrid) = numFilledByAvg(lonIndexGrid,latIndexGrid) + 1
1637          end if
1638        end if
1639      end do
1640    end do
1641
1642    do latIndexGrid = 1, njGrid
1643      do lonIndexGrid = 1, niGrid
1644        if(numFilledByAvg(lonIndexGrid,latIndexGrid) > 0) then
1645          fieldGrid(lonIndexGrid,latIndexGrid) = fieldGrid(lonIndexGrid,latIndexGrid)/ &
1646                                                 real(numFilledByAvg(lonIndexGrid,latIndexGrid))
1647        end if
1648      end do
1649    end do
1650
1651    ! Now do something for grid points that don't have any value assigned
1652    fieldGrid_tmp(:,:) = fieldGrid(:,:)
1653    nfill(:) = 0
1654    nhole(:) = 0
1655    boxSizeLoop: do boxSize = 1, maxBoxSize
1656      filledByInterp(:,:) = 0
1657      !$OMP PARALLEL DO PRIVATE(latIndexGrid,lonIndexGrid,myThreadNum,top,bottom,left,right,numBoxIndexes,lonBoxIndex,latBoxIndex,lonBoxIndexes,latBoxIndexes,ngp,boxIndex)
1658      do latIndexGrid = 1, njGrid
1659        myThreadNum = 1 + omp_get_thread_num()
1660        do lonIndexGrid = 1, niGrid
1661          if (numFilledByAvg(lonIndexGrid,latIndexGrid) > 0) cycle
1662
1663          if (boxSize == 1) nhole(myThreadNum) = nhole(myThreadNum) + 1
1664
1665          top    = latIndexGrid + boxSize
1666          bottom = latIndexGrid - boxSize
1667          left   = lonIndexGrid - boxSize
1668          right  = lonIndexGrid + boxSize
1669
1670          numBoxIndexes = 0
1671          latBoxIndex = bottom
1672          do lonBoxIndex = left, right
1673            numBoxIndexes = numBoxIndexes + 1
1674            lonBoxIndexes(numBoxIndexes) = lonBoxIndex
1675            latBoxIndexes(numBoxIndexes) = latBoxIndex
1676          end do
1677          lonBoxIndex = right
1678          do latBoxIndex = bottom + 1, top
1679            numBoxIndexes = numBoxIndexes + 1
1680            lonBoxIndexes(numBoxIndexes) = lonBoxIndex
1681            latBoxIndexes(numBoxIndexes) = latBoxIndex
1682          end do
1683          latBoxIndex = top
1684          do lonBoxIndex = right - 1, left, -1
1685            numBoxIndexes = numBoxIndexes + 1
1686            lonBoxIndexes(numBoxIndexes) = lonBoxIndex
1687            latBoxIndexes(numBoxIndexes) = latBoxIndex
1688          end do
1689          lonBoxIndex = left
1690          do latBoxIndex = top - 1, bottom + 1, -1
1691            numBoxIndexes = numBoxIndexes + 1
1692            lonBoxIndexes(numBoxIndexes) = lonBoxIndex
1693            latBoxIndexes(numBoxIndexes) = latBoxIndex
1694          end do
1695
1696          ngp = 0
1697          do boxIndex = 1, numBoxIndexes
1698
1699            if (lonBoxIndexes(boxIndex) >= 1 .and. lonBoxIndexes(boxIndex) <= niGrid .and.    &
1700                latBoxIndexes(boxIndex) >= 1 .and. latBoxIndexes(boxIndex) <= njGrid) then
1701              if ( numFilledByAvg(lonBoxIndexes(boxIndex),latBoxIndexes(boxIndex)) > 0 ) then
1702
1703                fieldGrid_tmp(lonIndexGrid,latIndexGrid) = &
1704                     fieldGrid_tmp(lonIndexGrid,latIndexGrid) +  &
1705                     fieldGrid(lonBoxIndexes(boxIndex),latBoxIndexes(boxIndex))
1706                ngp = ngp + 1
1707
1708              end if
1709            end if
1710
1711          end do
1712
1713          if (ngp /= 0) then
1714            ! mark the grid point as being filled by interpolation on the grid
1715            filledByInterp(lonIndexGrid,latIndexGrid) = 1
1716            fieldGrid_tmp(lonIndexGrid,latIndexGrid) = fieldGrid_tmp(lonIndexGrid,latIndexGrid)/real(ngp)
1717            nfill(myThreadNum) = nfill(myThreadNum) + 1
1718          end if
1719
1720        end do
1721      end do
1722      !$OMP END PARALLEL DO
1723
1724      fieldGrid(:,:) = fieldGrid_tmp(:,:)
1725      numFilledByAvg(:,:) = numFilledByAvg(:,:) + filledByInterp(:,:)
1726    end do boxSizeLoop
1727
1728    ! find any remaining grid points that are likely water and assign value
1729    if ( checkCloudToGridUnassigned .and. &
1730         stateVectorCloud%oceanMask%maskPresent .and. &
1731         .not. stateVectorGrid%oceanMask%maskPresent ) then
1732
1733      ! create a kdtree to index all cloud locations
1734      allocate(positionArray(3,niCloud*njCloud))
1735      gridIndex = 0
1736      do lonIndexCloud = 1, niCloud
1737        do latIndexCloud = 1, njCloud
1738          gridIndex = gridIndex + 1
1739          positionArray(:,gridIndex) = &
1740               kdtree2_3dPosition(real(stateVectorCloud%hco%lon2d_4(lonIndexCloud,latIndexCloud),8), &
1741                                  real(stateVectorCloud%hco%lat2d_4(lonIndexCloud,latIndexCloud),8))
1742        end do
1743      end do
1744      tree => kdtree2_create(positionArray, sort=.true., rearrange=.true.) 
1745      
1746      ! assign grid mask to value of mask at nearest cloud location
1747      do latIndexGrid = 1, njGrid
1748        do lonIndexGrid = 1, niGrid
1749          if (numFilledByAvg(lonIndexGrid,latIndexGrid) > 0) cycle
1750
1751          ! find nearest cloud location
1752          refPosition(:) = kdtree2_3dPosition(real(stateVectorGrid%hco%lon2d_4(lonIndexGrid,latIndexGrid),8), &
1753                                              real(stateVectorGrid%hco%lat2d_4(lonIndexGrid,latIndexGrid),8))
1754
1755          searchRadiusSquared = (50.0D0*1000.0D0)**2
1756          call kdtree2_r_nearest(tp=tree, qv=refPosition, r2=searchRadiusSquared, &
1757                                 nfound=numLocalGridPointsFound, &
1758                                 nalloc=maxNumLocalGridPointsSearch, results=searchResults)
1759          if (numLocalGridPointsFound > maxNumLocalGridPointsSearch) then
1760            call utl_abort('The parameter maxNumLocalGridPointsSearch must be increased')
1761          end if
1762          if (numLocalGridPointsFound == 0) then
1763            ! very far from a cloud location, assume it is land
1764            maskGrid(lonIndexGrid,latIndexGrid) = 0
1765          else
1766            gridIndex = searchResults(1)%idx
1767            lonIndexCloud = 1 + ((gridIndex-1)/njCloud)
1768            latIndexCloud = gridIndex - ((gridIndex-1)/njCloud)*njCloud
1769            if(lonIndexCloud < 1 .or. lonIndexCloud > niCloud) call utl_abort('lonIndexCloud wrong')
1770            if(latIndexCloud < 1 .or. latIndexCloud > njCloud) call utl_abort('latIndexCloud wrong')
1771            maskGrid(lonIndexGrid,latIndexGrid) = maskCloud(lonIndexCloud,latIndexCloud)
1772          end if
1773
1774        end do
1775      end do
1776
1777      deallocate(positionArray)
1778      call kdtree2_destroy(tree)
1779
1780    end if
1781
1782    ! fill in remaining grid points
1783    if (count(numFilledByAvg(:,:) > 0) > 0) then
1784
1785      ! compute spatial mean of assigned grid values
1786      extrapValue = 0.0d0
1787      do latIndexGrid = 1, njGrid
1788        do lonIndexGrid = 1, niGrid
1789          if (numFilledByAvg(lonIndexGrid,latIndexGrid) == 0) cycle
1790          extrapValue = extrapValue + real(fieldGrid(lonIndexGrid,latIndexGrid),8)
1791        end do
1792      end do
1793      extrapValue = extrapValue / real(count(numFilledByAvg(:,:) > 0),8)
1794
1795      ! set unassigned grid points and count them
1796      nextrap0 = 0
1797      nextrap1 = 0
1798      do latIndexGrid = 1, njGrid
1799        do lonIndexGrid = 1, niGrid
1800
1801          if (numFilledByAvg(lonIndexGrid,latIndexGrid) > 0) cycle
1802
1803          fieldGrid(lonIndexGrid,latIndexGrid) = extrapValue
1804
1805          if (maskGrid(lonIndexGrid,latIndexGrid) == 1) then
1806            nextrap1 = nextrap1 + 1
1807          else if (maskGrid(lonIndexGrid,latIndexGrid) == 0) then
1808            nextrap0 = nextrap0 + 1
1809          else
1810            call msg('int_sintCloudToGrid_gsv', &
1811                 'Expecting 0 or 1 for the mask field at grid point: ' &
1812                 //str(lonIndexGrid)//', '//str(latIndexGrid)//', ' &
1813                 //str(maskGrid(lonIndexGrid,latIndexGrid)))
1814            call utl_abort('int_sintCloudToGrid_gsv')
1815          end if
1816
1817        end do
1818      end do
1819    end if
1820
1821    if ( checkCloudToGridUnassigned ) then
1822      call msg('int_sintCloudToGrid_gsv', & 
1823             new_line('')//'Total number of grid points:                                   '//str(niGrid*njGrid) &
1824           //new_line('')//'Number of grid points not covered by the cloud of points:      '//str(sum(nhole(:))) &
1825           //new_line('')//'Number of grid points filled by neighbours:                    '//str(sum(nfill(:))) &
1826           //new_line('')//'Number of grid points with extrapolated value in masked area:  '//str(nextrap0) &
1827           //new_line('')//'Number of grid points with extrapolated value in visible area: '//str(nextrap1))
1828
1829      if ( nextrap1 > 0 ) then
1830        call utl_abort('int_sintCloudToGrid_gsv: Values at some unmasked grid points were not assigned')
1831      end if
1832    end if
1833
1834    deallocate(fieldGrid_tmp)
1835    deallocate(xCloud)
1836    deallocate(yCloud)
1837    deallocate(numFilledByAvg)
1838    deallocate(filledByInterp)
1839    deallocate(maskCloud)
1840    deallocate(maskGrid)
1841
1842    ierr = 0
1843
1844    call msg('int_sintCloudToGrid_gsv', 'END', verb_opt=2)
1845    call utl_tmg_stop(176)
1846
1847  end function int_sintCloudToGrid_gsv
1848
1849  !--------------------------------------------------------------------------
1850  ! int_hInterpScalar_r8_2d
1851  !--------------------------------------------------------------------------
1852  function int_hInterpScalar_r8_2d(fieldOut_r8, fieldIn_r8, interpDegree, extrapDegree_opt) result(ierr)
1853    !
1854    !:Purpose: Horizontal interpolation of 2D scalar field that use real(8) arrays
1855    !           for input and output. Accessed through int_hInterpScalar.
1856    !
1857    implicit none
1858
1859    ! Arguments:
1860    real(8),                    intent(inout) :: fieldOut_r8(:,:)
1861    real(8),                    intent(in)    :: fieldIn_r8(:,:)
1862    character(len=*),           intent(in)    :: interpDegree
1863    character(len=*), optional, intent(in)    :: extrapDegree_opt
1864    ! Result:
1865    integer :: ierr
1866
1867    ! Locals:
1868    integer :: nii, nji, nio, njo     
1869    integer :: jk1, jk2
1870    real(4), allocatable :: bufferi4(:,:), buffero4(:,:)
1871    integer :: ezsint
1872
1873    call msg('int_hInterpScalar_r8_2d', 'START', verb_opt=4)
1874    ! read the namelist
1875    call int_readNml()
1876
1877    ! do the standard interpolation
1878
1879    call int_setezopt(interpDegree, extrapDegree_opt)   
1880
1881    nii = size(fieldIn_r8,1)
1882    nji = size(fieldIn_r8,2)
1883
1884    nio = size(fieldOut_r8,1)
1885    njo = size(fieldOut_r8,2)
1886
1887    allocate(bufferi4(nii,nji))
1888    allocate(buffero4(nio,njo))
1889
1890    do jk2 = 1,nji
1891      do jk1 = 1,nii
1892        bufferi4(jk1,jk2) = fieldIn_r8(jk1,jk2)
1893      end do
1894    end do
1895
1896    ierr = ezsint(buffero4,bufferi4)
1897
1898    do jk2 = 1,njo
1899      do jk1 = 1,nio
1900        fieldOut_r8(jk1,jk2) = buffero4(jk1,jk2)
1901      end do
1902    end do
1903
1904    deallocate(bufferi4)
1905    deallocate(buffero4)
1906
1907    call msg('int_hInterpScalar_r8_2d', 'END', verb_opt=4)
1908  end function int_hInterpScalar_r8_2d
1909
1910  !--------------------------------------------------------------------------
1911  ! int_hInterpUV_gsv
1912  !--------------------------------------------------------------------------
1913  function int_hInterpUV_gsv(stateVectorOut, stateVectorIn, varName, levIndex, stepIndex, &
1914                             interpDegree, extrapDegree_opt) result(ierr)
1915    !
1916    !:Purpose: Horizontal interpolation of 2D vector field that use stateVector objects
1917    !           for input and output. Accessed through int_hInterpUV.
1918    !
1919    implicit none
1920
1921    ! Arguments:
1922    type(struct_gsv),           intent(inout) :: stateVectorOut
1923    type(struct_gsv),           intent(inout) :: stateVectorIn
1924    character(len=*),           intent(in)    :: varName
1925    integer         ,           intent(in)    :: levIndex
1926    integer         ,           intent(in)    :: stepIndex
1927    character(len=*),           intent(in)    :: interpDegree
1928    character(len=*), optional, intent(in)    :: extrapDegree_opt
1929    ! Result:
1930    integer :: ierr
1931
1932    ! Locals:
1933    real(4), pointer :: UUout4(:,:,:,:), VVout4(:,:,:,:), UUin4(:,:,:,:), VVin4(:,:,:,:)
1934    real(8), pointer :: UUout8(:,:,:,:), VVout8(:,:,:,:), UUin8(:,:,:,:), VVin8(:,:,:,:)
1935    real(4), pointer :: UVout4(:,:,:), UVin4(:,:,:)
1936    real(8), pointer :: UVout8(:,:,:), UVin8(:,:,:)
1937    integer :: ezuvint, ezdefset
1938
1939    call msg('int_hInterpUV_gsv', 'START', verb_opt=4)
1940
1941    ! read the namelist
1942    call int_readNml()
1943
1944    ! check if special interpolation is required
1945    if (stateVectorIn%hco%initialized .and. stateVectorOut%hco%initialized) then
1946      if (stateVectorIn%hco%grtyp == 'Y') then
1947        call utl_abort('int_hInterpUV_gsv: cloudToGrid not implemented')
1948      end if
1949    end if
1950
1951    ! do the standard interpolation
1952
1953    ierr = ezdefset(stateVectorOut%hco%EZscintID, stateVectorIn%hco%EZscintID)
1954    call int_setezopt(interpDegree, extrapDegree_opt)   
1955
1956    if ( gsv_getDataKind(stateVectorOut) == 4 .and. gsv_getDataKind(stateVectorIn) == 4) then
1957
1958      if (trim(varName) == 'BOTH') then
1959        call gsv_getField(stateVectorOut, UUout4, 'UU')
1960        call gsv_getField(stateVectorOut, VVout4, 'VV')
1961        call gsv_getField(stateVectorIn,  UUin4,  'UU')
1962        call gsv_getField(stateVectorIn,  VVin4,  'VV')
1963        ierr = ezuvint(UUout4(:,:,levIndex,stepIndex),VVout4(:,:,levIndex,stepIndex), &
1964                       UUin4(:,:,levIndex,stepIndex), VVin4(:,:,levIndex,stepIndex))
1965      else if (trim(varName) == 'UU') then
1966        call gsv_getField  (stateVectorIn,  UUin4)
1967        call gsv_getField  (stateVectorOut, UUout4)
1968        call gsv_getFieldUV(stateVectorIn,  UVin4,  levIndex)
1969        call gsv_getFieldUV(stateVectorOut, UVout4, levIndex)
1970        ierr = ezuvint(UUout4(:,:,levIndex,stepIndex),UVout4(:,:,stepIndex), &
1971                       UUin4(:,:,levIndex,stepIndex), UVin4(:,:,stepIndex))
1972      else if (trim(varName) == 'VV') then
1973        call gsv_getField  (stateVectorIn,  VVin4)
1974        call gsv_getField  (stateVectorOut, VVout4)
1975        call gsv_getFieldUV(stateVectorIn,  UVin4,  levIndex)
1976        call gsv_getFieldUV(stateVectorOut, UVout4, levIndex)
1977        ierr = ezuvint(UVout4(:,:,stepIndex),VVout4(:,:,levIndex,stepIndex), &
1978                       UVin4(:,:,stepIndex), VVin4(:,:,levIndex,stepIndex))
1979      else
1980        call utl_abort('int_hInterpUV_gsv: unexpected varName: '//trim(varName))
1981      end if
1982
1983    else if ( gsv_getDataKind(stateVectorOut) == 8 .and. gsv_getDataKind(stateVectorIn) == 8) then
1984
1985      ! allocate real(4) buffers for copying to/from for interpolation
1986      allocate(UUin4(stateVectorIn%hco%ni,stateVectorIn%hco%nj,1,1))
1987      allocate(VVin4(stateVectorIn%hco%ni,stateVectorIn%hco%nj,1,1))
1988      allocate(UUout4(stateVectorOut%hco%ni,stateVectorOut%hco%nj,1,1))
1989      allocate(VVout4(stateVectorOut%hco%ni,stateVectorOut%hco%nj,1,1))
1990
1991      if (trim(varName) == 'BOTH') then
1992        call gsv_getField(stateVectorOut, UUout8, 'UU')
1993        call gsv_getField(stateVectorOut, VVout8, 'VV')
1994        call gsv_getField(stateVectorIn,  UUin8,  'UU')
1995        call gsv_getField(stateVectorIn,  VVin8,  'VV')
1996        UUin4(:,:,1,1) = UUin8(:,:,levIndex,stepIndex)
1997        VVin4(:,:,1,1) = VVin8(:,:,levIndex,stepIndex)
1998        ierr = ezuvint(UUout4(:,:,1,1),VVout4(:,:,1,1),UUin4(:,:,1,1),VVin4(:,:,1,1))
1999        UUout8(:,:,levIndex,stepIndex) = UUout4(:,:,1,1)
2000        VVout8(:,:,levIndex,stepIndex) = VVout4(:,:,1,1)
2001      else if (trim(varName) == 'UU') then
2002        call gsv_getField  (stateVectorIn,  UUin8)
2003        call gsv_getField  (stateVectorOut, UUout8)
2004        call gsv_getFieldUV(stateVectorIn,  UVin8,  levIndex)
2005        call gsv_getFieldUV(stateVectorOut, UVout8, levIndex)
2006        UUin4(:,:,1,1) = UUin8(:,:,levIndex,stepIndex)
2007        VVin4(:,:,1,1) = UVin8(:,:,stepIndex)
2008        ierr = ezuvint(UUout4(:,:,1,1),VVout4(:,:,1,1),UUin4(:,:,1,1),VVin4(:,:,1,1))
2009        UUout8(:,:,levIndex,stepIndex) = UUout4(:,:,1,1)
2010        UVout8(:,:,stepIndex)          = VVout4(:,:,1,1)
2011      else if (trim(varName) == 'VV') then
2012        call gsv_getField  (stateVectorIn,  VVin8)
2013        call gsv_getField  (stateVectorOut, VVout8)
2014        call gsv_getFieldUV(stateVectorIn,  UVin8,  levIndex)
2015        call gsv_getFieldUV(stateVectorOut, UVout8, levIndex)
2016        UUin4(:,:,1,1) = UVin8(:,:,stepIndex)
2017        VVin4(:,:,1,1) = VVin8(:,:,levIndex,stepIndex)
2018        ierr = ezuvint(UUout4(:,:,1,1),VVout4(:,:,1,1),UUin4(:,:,1,1),VVin4(:,:,1,1))
2019        UVout8(:,:,stepIndex)          = UUout4(:,:,1,1)
2020        VVout8(:,:,levIndex,stepIndex) = VVout4(:,:,1,1)
2021      else
2022        call utl_abort('int_hInterpUV_gsv: unexpected varName: '//trim(varName))
2023      end if
2024
2025      deallocate(UUin4,VVin4,UUout4,VVout4)
2026
2027    else
2028
2029      call utl_abort('int_hInterpUV_gsv: not implemented for mixed dataKind')
2030
2031    end if
2032
2033    call msg('int_hInterpUV_gsv', 'END', verb_opt=4)
2034  end function int_hInterpUV_gsv
2035
2036  !--------------------------------------------------------------------------
2037  ! int_hInterpUV_r4_2d
2038  !--------------------------------------------------------------------------
2039  function int_hInterpUV_r4_2d(uuout, vvout, uuin, vvin, interpDegree, extrapDegree_opt) result(ierr)
2040    !
2041    !:Purpose: Horizontal interpolation of 2D vector field that use real(4) arrays
2042    !           for input and output. Accessed through int_hInterpUV.
2043    !
2044    implicit none
2045
2046    ! Arguments:
2047    real(4),                    intent(inout) :: uuout(:,:)
2048    real(4),                    intent(inout) :: vvout(:,:)
2049    real(4),                    intent(in)    :: uuin(:,:)
2050    real(4),                    intent(in)    :: vvin(:,:)
2051    character(len=*),           intent(in)    :: interpDegree
2052    character(len=*), optional, intent(in)    :: extrapDegree_opt
2053    ! Result:
2054    integer :: ierr
2055
2056    ! Locals:
2057    integer :: ezuvint
2058
2059    call msg('int_hInterpUV_r4_2d', 'START', verb_opt=4)
2060    ! read the namelist
2061    call int_readNml()
2062
2063    ! do the standard interpolation
2064    call int_setezopt(interpDegree, extrapDegree_opt)   
2065    ierr = ezuvint(uuout, vvout, uuin, vvin)
2066
2067    call msg('int_hInterpUV_r4_2d', 'END', verb_opt=4)
2068  end function int_hInterpUV_r4_2d
2069
2070  !--------------------------------------------------------------------------
2071  ! int_hInterpUV_r8_2d
2072  !--------------------------------------------------------------------------
2073  function int_hInterpUV_r8_2d(uuout, vvout, uuin, vvin, interpDegree, extrapDegree_opt) result(ierr)
2074    !
2075    !:Purpose: Horizontal interpolation of 2D vector field that use real(8) arrays
2076    !           for input and output. Accessed through int_hInterpUV.
2077    !
2078    implicit none
2079
2080    ! Arguments:
2081    real(8),                    intent(inout) :: uuout(:,:)
2082    real(8),                    intent(inout) :: vvout(:,:)
2083    real(8),                    intent(in)    :: uuin(:,:)
2084    real(8),                    intent(in)    :: vvin(:,:)
2085    character(len=*),           intent(in)    :: interpDegree
2086    character(len=*), optional, intent(in)    :: extrapDegree_opt
2087    ! Result:
2088    integer :: ierr
2089
2090    ! Locals:
2091    integer :: nio, njo, nii, nji
2092    integer :: jk1, jk2
2093    real, allocatable :: bufuuout4(:,:), bufvvout4(:,:)
2094    real, allocatable :: bufuuin4(:,:), bufvvin4(:,:)
2095    integer :: ezuvint
2096
2097    call msg('int_hInterpUV_r8_2d', 'START', verb_opt=4)
2098    ! read the namelist
2099    call int_readNml()
2100
2101    ! do the standard interpolation
2102
2103    call int_setezopt(interpDegree, extrapDegree_opt)   
2104
2105    nii = size(uuin,1)
2106    nji = size(uuin,2)
2107
2108    nio = size(uuout,1)
2109    njo = size(uuout,2)
2110
2111    allocate(bufuuout4(nio,njo))
2112    allocate(bufvvout4(nio,njo))
2113    allocate(bufuuin4(nii,nji))
2114    allocate(bufvvin4(nii,nji))
2115
2116    do jk2 = 1,nji
2117      do jk1 = 1,nii
2118        bufuuin4(jk1,jk2) = uuin(jk1,jk2)
2119        bufvvin4(jk1,jk2) = vvin(jk1,jk2)
2120      end do
2121    end do
2122
2123    ierr = ezuvint(bufuuout4, bufvvout4, bufuuin4, bufvvin4)
2124
2125    do jk2 = 1,njo
2126      do jk1 = 1,nio
2127        uuout(jk1,jk2) = bufuuout4(jk1,jk2)
2128        vvout(jk1,jk2) = bufvvout4(jk1,jk2)
2129      end do
2130    end do
2131
2132    deallocate(bufuuin4)
2133    deallocate(bufvvin4)
2134    deallocate(bufuuout4)
2135    deallocate(bufvvout4)
2136
2137    call msg('int_hInterpUV_r8_2d', 'END', verb_opt=4)
2138  end function int_hInterpUV_r8_2d
2139
2140  !--------------------------------------------------------------------------
2141  ! int_ezgdef
2142  !--------------------------------------------------------------------------
2143  function int_ezgdef(ni, nj, grtyp, grtypref, ig1, ig2, ig3, ig4, ax, ay) result(vezgdef)
2144    !
2145    !:Purpose: Subroutine wrapper for rmnlib procedure ezgdef.
2146    !
2147    implicit none
2148
2149    ! Arguments:
2150    integer,          intent(in) :: ni
2151    integer,          intent(in) :: nj
2152    integer,          intent(in) :: ig1
2153    integer,          intent(in) :: ig2
2154    integer,          intent(in) :: ig3
2155    integer,          intent(in) :: ig4
2156    real(8),          intent(in) :: ax(:)
2157    real(8),          intent(in) :: ay(:)
2158    character(len=*), intent(in) :: grtyp
2159    character(len=*), intent(in) :: grtypref
2160    ! Result:
2161    integer :: vezgdef
2162
2163    ! Locals:
2164    integer :: ier2, jk, ilenx, ileny
2165    real(4), allocatable :: bufax4(:), bufay4(:)
2166    integer :: ezgdef
2167
2168    if (grtyp .eq. 'Y') then
2169      ilenx = max(1,ni*nj)
2170      ileny = ilenx
2171    else if (grtyp .eq. 'Z') then
2172      ilenx = max(1,ni)
2173      ileny = max(1,nj)
2174    else
2175      call utl_abort('VEZGDEF: Grid type not supported')
2176    end if
2177
2178    allocate(bufax4(ilenx))
2179    allocate(bufay4(ileny))
2180
2181    do jk = 1,ilenx
2182      bufax4(jk) = ax(jk)
2183    end do
2184    do jk = 1,ileny
2185      bufay4(jk) = ay(jk)
2186    end do
2187
2188    ier2 = ezgdef(ni, nj, grtyp, grtypref, ig1, ig2, ig3, ig4, &
2189                  bufax4, bufay4)
2190
2191    deallocate(bufax4)
2192    deallocate(bufay4)
2193
2194    vezgdef = ier2
2195
2196  end function int_ezgdef
2197
2198  !--------------------------------------------------------------------------
2199  ! int_cxgaig
2200  !--------------------------------------------------------------------------
2201  subroutine int_cxgaig(grtyp, ig1, ig2, ig3, ig4, xlat0, xlon0, dlat, dlon) 
2202    !
2203    !:Purpose: Subroutine wrapper for rmnlib procedure cxgaig.
2204    !
2205    implicit none
2206
2207    ! Arguments:
2208    integer,          intent(in) :: ig1
2209    integer,          intent(in) :: ig2
2210    integer,          intent(in) :: ig3
2211    integer,          intent(in) :: ig4   
2212    real(8),          intent(in) :: xlat0
2213    real(8),          intent(in) :: xlon0
2214    real(8),          intent(in) :: dlat
2215    real(8),          intent(in) :: dlon 
2216    character(len=*), intent(in) :: grtyp 
2217
2218    ! Locals:
2219    real(4) :: xlat04, xlon04, dlat4, dlon4
2220
2221    xlat04 = xlat0
2222    xlon04 = xlon0
2223    dlat4 = dlat
2224    dlon4 = dlon
2225
2226    call cxgaig(grtyp, ig1, ig2, ig3, ig4, xlat04, xlon04, dlat4, dlon4)
2227
2228  end subroutine int_cxgaig
2229
2230end module interpolation_mod