stateToColumn_mod sourceΒΆ

   1module stateToColumn_mod
   2  ! MODULE stateToColumn_mod (prefix='s2c' category='4. Data Object transformations')
   3  !
   4  !:Purpose:  Non-linear, tangent-linear and adjoint versions of
   5  !           horizontal-temporal interpolation between a gridStateVector object
   6  !           and a columnData object.
   7  !
   8  use mathPhysConstants_mod
   9  use earthConstants_mod
  10  use mpi, only : mpi_status_size ! this is the mpi library module
  11  use midasMpi_mod
  12  use codePrecision_mod
  13  use gridStateVector_mod
  14  use obsSpaceData_mod
  15  use columnData_mod
  16  use horizontalCoord_mod
  17  use obsTimeInterp_mod
  18  use windRotation_mod
  19  use utilities_mod
  20  use gridVariableTransforms_mod
  21  use varNameList_mod
  22  use slantProfileLatLon_mod
  23  use tovsNL_mod
  24  use codtyp_mod
  25  use getGridPosition_mod
  26  use kdTree2_mod
  27  use calcHeightAndPressure_mod
  28  use humidityLimits_mod
  29
  30  implicit none
  31  save
  32  private
  33  
  34  ! public routines
  35  public :: s2c_tl, s2c_ad, s2c_nl
  36  public :: s2c_bgcheck_bilin, s2c_getFootprintRadius, s2c_getWeightsAndGridPointIndexes
  37  public :: s2c_deallocInterpInfo
  38
  39  ! private module variables and derived types
  40
  41  type struct_stepProcData
  42    ! lat-lon location of observations to be interpolated
  43    real(8), pointer          :: allLat(:,:) => null()         ! (headerUsed, kIndex)
  44    real(8), pointer          :: allLon(:,:) => null()         ! (headerUsed, kIndex)
  45    ! lat-lon location on rotated grid of observations to be interpolated
  46    real(8), pointer          :: allLatRot(:,:,:) => null()    ! (subGrid, headerUsed, kIndex)
  47    real(8), pointer          :: allLonRot(:,:,:) => null()    ! (subGrid, headerUsed, kIndex)
  48    ! actual headerIndex, since the headerUsed is only for those obs with a non-zero interp weight
  49    integer, pointer          :: allHeaderIndex(:) => null()   ! (headerUsed)
  50    ! depotIndexBeg/End contain first/last indices into depots of interpolation weights and lat/lon indices
  51    integer, pointer          :: depotIndexBeg(:,:,:) => null() ! (subGrid, headerUsed, kIndex)
  52    integer, pointer          :: depotIndexEnd(:,:,:) => null() ! (subGrid, headerUsed, kIndex)
  53  end type struct_stepProcData
  54
  55  type struct_interpInfo
  56    logical                   :: initialized = .false.
  57    type(struct_hco), pointer :: hco => null() ! horizontal grid object
  58    type(struct_uvr), pointer :: uvr => null() ! windRotation object
  59    type(struct_oti), pointer :: oti => null() ! obsTimeInterp object
  60
  61    ! number of obs headers on each proc having a non-zero interp weight for each stepIndex (headerUsed)
  62    integer, pointer          :: allNumHeaderUsed(:,:) => null()    ! (step, proc)
  63
  64    ! structure containing all interpolation information that depends on (proc, step)
  65    type(struct_stepProcData), allocatable :: stepProcData(:,:) ! (proc, step)
  66
  67    ! interpolation weights and lat/lon indices are accessed via the 'stepProcData%depotIndexBeg/End'
  68    real(8), allocatable      :: interpWeightDepot(:)                ! (depotIndex)
  69    integer, pointer          :: latIndexDepot(:)                    ! (depotIndex)
  70    integer, pointer          :: lonIndexDepot(:)                    ! (depotIndex)
  71    character(len=2)          :: inputStateVectorType
  72  end type struct_interpInfo
  73
  74  type(struct_interpInfo), target :: interpInfo_tlad, interpInfo_nl
  75  type(kdtree2), pointer  :: tree_nl => null()
  76  type(kdtree2), pointer  :: tree_tlad => null()
  77
  78  character(len=20), parameter :: timeInterpType_tlad = 'LINEAR' ! hardcoded type of time interpolation for increment
  79
  80  integer, parameter :: maxNumWrites = 50
  81  logical, parameter :: verbose = .false.
  82
  83  ! "special" values of the footprint radius
  84  real(4), parameter :: nearestNeighbourFootprint = -3.0
  85  real(4), parameter ::             lakeFootprint = -2.0
  86  real(4), parameter ::         bilinearFootprint = -1.0
  87  integer, parameter :: maxNumLocalGridptsSearch = 1000
  88  integer, parameter :: minNumLocalGridptsSearch = 8
  89
  90  ! namelist variables
  91  logical :: slantPath_TO_nl               ! choose to use slant path for non-linear radiance operator
  92  logical :: slantPath_TO_tlad             ! choose to use slant path for linearized radiance operators
  93  logical :: slantPath_RO_nl               ! choose to use slant path for non-linear GPS-RO operator
  94  logical :: slantPath_RA_nl               ! choose to use slant path for non-linear radar operator
  95  logical :: calcHeightPressIncrOnColumn   ! choose to compute Z and P increment on column, instead of grid
  96  logical :: useFootprintForTovs           ! choose to use a horizontal footprint operator for radiance obs
  97  logical :: rejectObsNonMonotonicPressure ! choose to reject obs when interpolated column pressure is non-monotonic
  98
  99  integer, external :: get_max_rss
 100
 101contains 
 102
 103
 104  !---------------------------------------------------------
 105  ! pressureProfileMonotonicityCheck
 106  !---------------------------------------------------------
 107  subroutine pressureProfileMonotonicityCheck(obsSpaceData, column)
 108    !
 109    !:Purpose: Check for non monotonic pressure profiles that can be computed in slantpathmode
 110    !
 111    implicit none
 112
 113    ! Arguments:
 114    type(struct_obs),        intent(inout) :: obsSpaceData
 115    type(struct_columnData), intent(inout) :: column
 116
 117    ! Locals:
 118    integer, parameter :: numWriteMax = 10
 119    integer :: headerIndex, bodyIndex, iterationCount, singularIndex, levelIndex
 120    integer :: pressureVarIndex
 121    integer :: nlv
 122    integer :: numWrites
 123    real(8), pointer :: pressureProfile(:)
 124    logical :: monotonicProfile
 125    integer, parameter :: nPressureVar =2
 126    character(len=4), parameter :: pressureVarList(nPressureVar)=['P_T ', 'P_M ']
 127
 128    write(*,*) ' '
 129    write(*,*) 'pressureProfileMonotonicityCheck: START'
 130    write(*,*) ' '
 131    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 132
 133    numWrites = 0
 134
 135    call obs_set_current_header_list(obsSpaceData, 'TO')
 136    HEADER: do
 137      headerIndex = obs_getHeaderIndex(obsSpaceData)
 138      if (headerIndex < 0) exit HEADER
 139      do pressureVarIndex = 1, nPressureVar
 140        pressureProfile => col_getColumn(column, headerIndex, pressureVarList(pressureVarIndex))
 141        nlv = size(pressureProfile)
 142        monotonicProfile = .true.
 143        iterationCount = 0
 144        iterationLoop:do
 145          singularIndex = -1
 146          levelSearch:do levelIndex = 1, nlv - 1
 147            if ( pressureProfile(levelIndex) > pressureProfile(levelIndex+1)) then
 148              singularIndex = levelIndex
 149              exit levelSearch
 150            end if
 151          end do levelSearch
 152          if ( singularIndex == -1 ) exit iterationLoop !regular profile or correction OK
 153          iterationCount = iterationCount + 1
 154          if (iterationCount == 1) then
 155            monotonicProfile = .false.
 156            if (numWrites < numWriteMax) then
 157              numWrites = numWrites + 1
 158              write(*,*) 'pressureProfileMonotonicityCheck: found non monotonic pressure profile:', &
 159                   pressureVarList(pressureVarIndex), pressureProfile
 160            end if
 161          end if
 162          if (singularIndex == 1) then !should never happen
 163            write(*,*) 'pressureProfileMonotonicityCheck: ', pressureProfile(1:2)
 164            call utl_abort('pressureProfileMonotonicityCheck: profile in the wrong order ?' &
 165                 // pressureVarList(pressureVarIndex))
 166          end if
 167          pressureProfile(singularIndex) = 0.5d0 * ( pressureProfile(singularIndex - 1) + pressureProfile(singularIndex + 1) )
 168          write(*,*) 'pressureProfileMonotonicityCheck: profile iteration', &
 169               pressureVarList(pressureVarIndex), iterationCount
 170        end do iterationLoop
 171
 172        ! if requested reject the corrected profile
 173        if (.not. monotonicProfile .and. rejectObsNonMonotonicPressure) then
 174          call obs_headSet_i(obsSpaceData,OBS_ST1,headerIndex, &
 175               ibset( obs_headElem_i(obsSpaceData,OBS_ST1,headerIndex), 05))
 176          call obs_set_current_body_list(obsSpaceData, headerIndex)
 177          BODY: do
 178            bodyIndex = obs_getBodyIndex(obsSpaceData)
 179            if (bodyIndex < 0) exit BODY
 180            if (rejectObsNonMonotonicPressure) then
 181              call obs_bodySet_i(obsSpaceData, OBS_ASS, bodyIndex, obs_notAssimilated)
 182              call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, &
 183                   ibset(obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex),9))
 184            end if
 185          end do BODY
 186        end if
 187      end do ! loop on pressure variables
 188     
 189    end do HEADER
 190
 191    write(*,*) 'pressureProfileMonotonicityCheck: END'
 192
 193  end subroutine pressureProfileMonotonicityCheck
 194
 195  !---------------------------------------------------------
 196  ! latlonChecksAnlGrid
 197  !---------------------------------------------------------
 198  subroutine latlonChecksAnlGrid(obsSpaceData, hco_core, moveObsAtPole)
 199    !
 200    !:Purpose: Check the lat/lon of observations and modify if necessary
 201    !
 202    implicit none
 203
 204    ! Arguments:
 205    type(struct_obs)         , intent(inout) :: obsSpaceData
 206    type(struct_hco), pointer, intent(in)    :: hco_core
 207    logical                  , intent(in)    :: moveObsAtPole
 208
 209    ! Locals:
 210    integer :: headerIndex, ierr
 211    integer :: idata, idatend, jdata, subGridIndex
 212    real(4) :: lat_r4, lon_r4, lat_deg_r4, lon_deg_r4
 213    real(4) :: xpos_r4, ypos_r4, xpos2_r4, ypos2_r4
 214    real(4) :: xposLowerBoundAnl_r4, xposUpperBoundAnl_r4
 215    real(8) :: lat_r8, lon_r8
 216    integer, save :: numWrites = 0
 217    integer :: gdllfxy
 218
 219    write(*,*) ' '
 220    write(*,*) 'latlonChecksAnlGrid: STARTING'
 221    write(*,*) ' '
 222    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 223
 224    !
 225    !-    Get the Analysis Grid structure
 226    !
 227    if ( hco_core % global ) then
 228       xposLowerBoundAnl_r4 = - huge(1.0) ! no limit since grid is global (periodic)
 229       xposUpperBoundAnl_r4 = + huge(1.0) ! no limit since grid is global (periodic)
 230    else
 231       xposLowerBoundAnl_r4 = 1.0
 232       xposUpperBoundAnl_r4 = real(hco_core % ni)
 233    end if
 234
 235    header_loop: do headerIndex=1, obs_numheader(obsSpaceData)
 236
 237      !- Get LatLon of observation location
 238      lat_r8 = obs_headElem_r(obsSpaceData,OBS_LAT,headerIndex)
 239      lon_r8 = obs_headElem_r(obsSpaceData,OBS_LON,headerIndex)
 240      lat_r4 = real(lat_r8,4)
 241      lon_r4 = real(lon_r8,4)
 242      if (lon_r4.lt.0.0         ) lon_r4 = lon_r4 + 2.0*MPC_PI_R4
 243      if (lon_r4.ge.2.*MPC_PI_R4) lon_r4 = lon_r4 - 2.0*MPC_PI_R4
 244
 245      lat_deg_r4 = lat_r4 * MPC_DEGREES_PER_RADIAN_R4 ! Radian To Degree
 246      lon_deg_r4 = lon_r4 * MPC_DEGREES_PER_RADIAN_R4
 247
 248      !
 249      !- Find the position in the analysis grid
 250      !
 251      ierr = gpos_getPositionXY( hco_core % EZscintID,  &
 252                                xpos_r4, ypos_r4, xpos2_r4, ypos2_r4, &
 253                                lat_deg_r4, lon_deg_r4, subGridIndex )
 254
 255      !- Test if the obs is outside the analysis grid
 256      if ( xpos_r4 < xposLowerBoundAnl_r4  .or. &
 257           xpos_r4 > xposUpperBoundAnl_r4  .or. &
 258           ypos_r4 < 1.0                   .or. &
 259           ypos_r4 > real(hco_core % nj) ) then
 260
 261        if ( hco_core % global ) then
 262
 263          if ( moveObsAtPole ) then
 264            ! Modify latitude if we have an observation at or near the poles
 265            write(*,*) ''
 266            write(*,*) 'latlonChecksAnlGrid: Moving OBS inside the GLOBAL ANALYSIS grid, ', headerIndex
 267            write(*,*) '  true position : ', lat_deg_r4, lon_deg_r4, ypos_r4, xpos_r4
 268
 269            !- Move the observation to the nearest grid point
 270            if ( ypos_r4 < 1.0 )                ypos_r4 = 1.0
 271            if ( ypos_r4 > real(hco_core % nj) ) ypos_r4 = real(hco_core % nj)
 272
 273            ierr = gdllfxy( hco_core % EZscintID, &    ! IN
 274                            lat_deg_r4, lon_deg_r4, & ! OUT
 275                            xpos_r4, ypos_r4, 1)      ! IN
 276
 277            write(*,*) '  new  position : ', lat_deg_r4, lon_deg_r4, ypos_r4, xpos_r4
 278
 279            lat_r8 = real(lat_deg_r4,8) * MPC_RADIANS_PER_DEGREE_R8
 280            lon_r8 = real(lon_deg_r4,8) * MPC_RADIANS_PER_DEGREE_R8
 281            call obs_headSet_r(obsSpaceData,OBS_LAT,headerIndex, lat_r8) ! IN
 282            call obs_headSet_r(obsSpaceData,OBS_LON,headerIndex, lon_r8) ! IN
 283          else
 284            write(*,*)
 285            write(*,*) 'latlonChecksAnlGrid: OBS outside the GLOBAL ANALYSIS grid, but NOT moved, ', headerIndex
 286          end if
 287
 288        else
 289          ! The observation is outside the domain
 290          ! In LAM Analysis mode we must discard this observation
 291          numWrites = numWrites + 1
 292          if (numWrites < maxNumWrites) then
 293            write(*,*) 'latlonChecksAnlGrid: Rejecting OBS outside the LAM ANALYSIS grid domain, ', headerIndex
 294            write(*,*) '  position : ', lat_deg_r4, lon_deg_r4, ypos_r4, xpos_r4
 295          else if (numWrites == maxNumWrites) then
 296            write(*,*) 'latlonChecksAnlGrid: More rejects, but reached maximum number of writes to the listing.'
 297          end if
 298
 299          idata   = obs_headElem_i(obsSpaceData,OBS_RLN,headerIndex)
 300          idatend = obs_headElem_i(obsSpaceData,OBS_NLV,headerIndex) + idata -1
 301          do jdata = idata, idatend
 302            call obs_bodySet_i(obsSpaceData,OBS_ASS,JDATA, obs_notAssimilated)
 303          end do
 304          call obs_headSet_i(obsSpaceData,OBS_ST1,headerIndex,  &
 305                             ibset( obs_headElem_i(obsSpaceData,OBS_ST1,headerIndex), 05))
 306        end if
 307
 308      end if
 309
 310    end do header_loop
 311
 312    write(*,*) 'latlonChecksAnlGrid: END'
 313
 314  end subroutine latlonChecksAnlGrid
 315
 316  !---------------------------------------------------------
 317  ! s2c_setupInterpInfo
 318  !---------------------------------------------------------
 319  subroutine s2c_setupInterpInfo( interpInfo, obsSpaceData, stateVector,  &
 320                                  headerIndexBeg, headerIndexEnd, &
 321                                  timeInterpType, rejectOutsideObs, &
 322                                  inputStateVectorType, lastCall_opt )
 323    !:Purpose: Setup all of the information needed to quickly
 324    !           perform the horizontal interpolation to the observation
 325    !           locations.
 326    !
 327    implicit none
 328
 329    ! Arguments:
 330    type(struct_interpInfo),   intent(out)   :: interpInfo
 331    type(struct_obs)        ,  intent(inout) :: obsSpaceData
 332    type(struct_gsv), target,  intent(in)    :: stateVector
 333    integer                 ,  intent(in)    :: headerIndexBeg
 334    integer                 ,  intent(in)    :: headerIndexEnd
 335    logical                 ,  intent(in)    :: rejectOutsideObs
 336    character(len=*)        ,  intent(in)    :: timeInterpType
 337    character(len=*)        ,  intent(in)    :: inputStateVectorType
 338    logical, optional       ,  intent(in)    :: lastCall_opt
 339
 340    ! Locals:
 341    type(struct_gsv)          :: stateVector_VarsLevs_1Step, stateVector_Tiles_allVar_1Step
 342    type(struct_gsv)          :: stateVector_Tiles_1Step
 343    type(struct_gsv), save    :: stateVector_1Step
 344    type(struct_gsv), pointer :: stateVector_Tiles_ptr
 345    integer :: numHeader, numHeaderUsedMax, headerIndex, headerUsedIndex
 346    integer :: kIndex, kIndexCount, myKBeg
 347    integer :: numStep, stepIndex, fnom, fclos, nulnam, ierr
 348    integer :: procIndex, niP1, numGridptTotal, numHeaderUsed
 349    integer :: subGridIndex, subGridForInterp, numSubGridsForInterp
 350    real(8) :: latRot, lonRot, lat, lon
 351    real(4) :: lon_r4, lat_r4, lon_deg_r4, lat_deg_r4
 352    real(4) :: xpos_r4, ypos_r4, xpos2_r4, ypos2_r4
 353    real(4) :: footprintRadius_r4 ! (metres)
 354    integer, allocatable :: numGridpt(:), allNumHeaderUsed(:,:)
 355    integer, allocatable :: allHeaderIndex(:,:,:), headerIndexVec(:,:)
 356    real(8), allocatable :: lat_send_r8(:,:), lat_recv_r8(:,:), lon_send_r8(:,:), lon_recv_r8(:,:)
 357    real(4), allocatable :: footprintRadiusVec_r4(:), allFootprintRadius_r4(:,:,:)
 358    real(8), allocatable :: allLatOneLev(:,:)
 359    real(8), allocatable :: allLonOneLev(:,:)
 360    character(len=4), pointer :: varNames(:)
 361    character(len=4)          :: varLevel
 362    real(8), allocatable :: latColumn(:,:), lonColumn(:,:)
 363    real(8), allocatable :: latLev_T(:), lonLev_T(:), latLev_M(:), lonLev_M(:)
 364    real(8) :: latLev_S, lonLev_S
 365    real(4), pointer :: height3D_r4_ptr1(:,:,:), height3D_r4_ptr2(:,:,:)
 366    real(4), save, pointer :: height3D_T_r4(:,:,:), height3D_M_r4(:,:,:)
 367    real(8), pointer :: height3D_r8_ptr1(:,:,:)
 368    real(kdkind), allocatable :: positionArray(:,:)
 369    integer :: sendsizes(mmpi_nprocs), recvsizes(mmpi_nprocs), senddispls(mmpi_nprocs)
 370    integer :: recvdispls(mmpi_nprocs), allkBeg(mmpi_nprocs)
 371    integer :: codeType, nlev_T, nlev_M, levIndex 
 372    integer :: lonIndex, latIndex, gridIndex
 373    integer :: maxkcount, numkToSend, numTovsUsingFootprint, numAllTovs
 374    logical :: doSlantPath, SlantTO, SlantRO, SlantRA, firstHeaderSlantPathTO, firstHeaderSlantPathRO, firstHeaderSlantPathRA
 375    logical :: doSetup3dHeights, lastCall
 376    logical, save :: nmlAlreadyRead = .false.
 377    type(kdtree2), pointer  :: tree
 378
 379    namelist /nams2c/ slantPath_TO_nl, slantPath_TO_tlad, slantPath_RO_nl, slantPath_RA_nl, calcHeightPressIncrOnColumn
 380    namelist /nams2c/ useFootprintForTovs, rejectObsNonMonotonicPressure 
 381
 382    write(*,*) 's2c_setupInterpInfo: STARTING'
 383    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 384
 385    write(*,*) 's2c_setupInterpInfo: inputStateVectorType=', inputStateVectorType
 386
 387    if ( present(lastCall_opt) ) then
 388      lastCall = lastCall_opt
 389    else
 390      lastCall = .false.
 391    end if
 392
 393    if ( .not. nmlAlreadyRead ) then
 394      nmlAlreadyRead = .true.
 395
 396      ! default values
 397      slantPath_TO_nl   = .false.
 398      slantPath_TO_tlad = .false.
 399      slantPath_RO_nl   = .false.
 400      slantPath_RA_nl   = .false.
 401      calcHeightPressIncrOnColumn = .false.
 402      useFootprintForTovs = .false.
 403      rejectObsNonMonotonicPressure =.true.
 404
 405      if ( .not. utl_isNamelistPresent('NAMS2C','./flnml') ) then
 406        if ( mmpi_myid == 0 ) then
 407          write(*,*) 's2c_setupInterpInfo: nams2c is missing in the namelist.'
 408          write(*,*) '                     The default values will be taken.'
 409        end if
 410
 411      else
 412        ! reading namelist variables
 413        nulnam = 0
 414        ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
 415        read(nulnam, nml = nams2c, iostat = ierr)
 416        if ( ierr /= 0 ) call utl_abort('s2c_setupInterpInfo: Error reading namelist')
 417        ierr = fclos(nulnam)
 418      end if
 419      if ( mmpi_myid == 0 ) write(*, nml = nams2c)
 420    end if
 421
 422    doSlantPath = .false.
 423    SlantTO     = .false.
 424    SlantRO     = .false.
 425    if ( slantPath_TO_nl   .and. inputStateVectorType == 'nl' ) then
 426      doSlantPath = .true.
 427      SlantTO     = .true.
 428    endif
 429    if ( slantPath_TO_tlad .and. inputStateVectorType /= 'nl' ) then
 430      doSlantPath = .true.
 431      SlantTO     = .true.
 432    endif
 433    if ( slantPath_RO_nl   .and. inputStateVectorType == 'nl' ) then
 434      doSlantPath = .true.
 435      SlantRO     = .true.
 436    endif
 437    if ( slantPath_RA_nl   .and. inputStateVectorType == 'nl' ) then
 438      doSlantPath = .true.
 439      SlantRA     = .true.
 440    endif
 441    write(*,*) 's2c_setupInterpInfo: doSlantPath, SlantTO, SlantRO, SlantRA = ', &
 442               doSlantPath, SlantTO, SlantRO, SlantRA
 443
 444    numStep = stateVector%numStep
 445    numHeader = headerIndexEnd - headerIndexBeg + 1
 446
 447    call oti_setup(interpInfo%oti, obsSpaceData, numStep,  &
 448                   headerIndexBeg, headerIndexEnd, &
 449                   interpType_opt=timeInterpType, flagObsOutside_opt=.true.)
 450
 451    if ((stateVector%heightSfcPresent) .and. ( mmpi_myid == 0)) then
 452      mykBeg = 0 
 453    else
 454      mykBeg = stateVector%mykBeg
 455    end if   
 456
 457    call rpn_comm_allgather(mykBeg, 1,'mpi_integer',       &
 458                            allkBeg,1,'mpi_integer','grid',ierr)
 459
 460    ! Allow for periodicity in Longitude for global Gaussian grid
 461    if ( stateVector%hco%grtyp == 'G' .or. &
 462         (stateVector%hco%grtyp == 'Z' .and. stateVector%hco%global) ) then
 463      niP1 = stateVector%ni + 1
 464    else
 465      niP1 = stateVector%ni
 466    end if
 467
 468    ! First count the number of headers for each stepIndex
 469    allocate(allNumHeaderUsed(numStep,mmpi_nprocs))
 470    allNumHeaderUsed(:,:) = 0
 471    do stepIndex = 1, numStep
 472      numHeaderUsed = 0
 473
 474      header_loop1: do headerIndex = headerIndexBeg, headerIndexEnd
 475
 476        ! if obs inside window, but zero weight for current stepIndex then skip it
 477        if ( oti_getTimeInterpWeight(interpInfo%oti,headerIndex,stepIndex) == 0.0d0 ) then
 478          cycle header_loop1
 479        end if
 480
 481        numHeaderUsed = numHeaderUsed + 1
 482
 483      end do header_loop1
 484      ! gather the number of obs over all processors for each timestep
 485      call rpn_comm_allgather(numHeaderUsed,                 1, 'MPI_INTEGER', &
 486                              allNumHeaderUsed(stepIndex,:), 1, 'MPI_INTEGER', &
 487                              'GRID',ierr)
 488
 489    end do
 490
 491    numHeaderUsedMax = maxval(allNumHeaderUsed(:,:))
 492    write(*,*) 's2c_setupInterpInfo: numHeaderUsedMax = ', numHeaderUsedMax
 493
 494    ! temporary arrays
 495    allocate(headerIndexVec(numHeaderUsedMax,numStep))
 496    allocate(footprintRadiusVec_r4(numHeaderUsedMax))
 497    headerIndexVec(:,:) = 0
 498
 499    ! copy the horizontal grid object
 500    interpInfo%hco => stateVector%hco
 501
 502    ! setup the information for wind rotation
 503    if ( (gsv_varExist(varName='UU') .or. gsv_varExist(varName='VV')) .and.  &
 504         stateVector%hco%rotated ) then
 505      call uvr_Setup( interpInfo%uvr, & ! INOUT
 506                      stateVector%hco ) ! IN 
 507    end if
 508
 509    allocate(interpInfo%stepProcData(mmpi_nprocs,numStep))
 510    do stepIndex = 1,numStep
 511      do procIndex = 1, mmpi_nprocs
 512        allocate(interpInfo%stepProcData(procIndex,stepIndex)%allLat(allNumHeaderUsed(stepIndex,procIndex),mykBeg:stateVector%mykEnd))
 513        allocate(interpInfo%stepProcData(procIndex,stepIndex)%allLon(allNumHeaderUsed(stepIndex,procIndex),mykBeg:stateVector%mykEnd))
 514        interpInfo%stepProcData(procIndex,stepIndex)%allLat(:,:) = 0.0d0
 515        interpInfo%stepProcData(procIndex,stepIndex)%allLon(:,:) = 0.0d0
 516
 517        allocate(interpInfo%stepProcData(procIndex,stepIndex)%allHeaderIndex(allNumHeaderUsed(stepIndex,procIndex)))
 518        interpInfo%stepProcData(procIndex,stepIndex)%allHeaderIndex(:) = 0
 519
 520        allocate(interpInfo%stepProcData(procIndex,stepIndex)%depotIndexBeg(interpInfo%hco%numSubGrid,numHeaderUsedMax,mykBeg:stateVector%mykEnd))
 521        allocate(interpInfo%stepProcData(procIndex,stepIndex)%depotIndexEnd(interpInfo%hco%numSubGrid,numHeaderUsedMax,mykBeg:stateVector%mykEnd))
 522        interpInfo%stepProcData(procIndex,stepIndex)%depotIndexBeg(:,:,:) = 0
 523        interpInfo%stepProcData(procIndex,stepIndex)%depotIndexEnd(:,:,:) = -1
 524      end do
 525    end do
 526
 527    ! allocate arrays that will be returned
 528    allocate(interpInfo%allNumHeaderUsed(numStep,mmpi_nprocs))
 529    allocate(allLatOneLev(numHeaderUsedMax,mmpi_nprocs))
 530    allocate(allLonOneLev(numHeaderUsedMax,mmpi_nprocs))
 531    allocate(allFootprintRadius_r4(numHeaderUsedMax,numStep,mmpi_nprocs))
 532    allocate(numGridpt(interpInfo%hco%numSubGrid))
 533    allFootprintRadius_r4(:,:,:) = bilinearFootprint
 534    interpInfo%allNumHeaderUsed(:,:) = allNumHeaderUsed(:,:)
 535
 536    if ( interpInfo%hco%rotated ) then
 537      do stepIndex = 1, numStep
 538        do procIndex = 1, mmpi_nprocs
 539          allocate(interpInfo%stepProcData(procIndex,stepIndex)%allLatRot(interpInfo%hco%numSubGrid,allNumHeaderUsed(stepIndex,procIndex),mykBeg:stateVector%mykEnd))
 540          allocate(interpInfo%stepProcData(procIndex,stepIndex)%allLonRot(interpInfo%hco%numSubGrid,allNumHeaderUsed(stepIndex,procIndex),mykBeg:stateVector%mykEnd))
 541          interpInfo%stepProcData(procIndex,stepIndex)%allLatRot(:,:,:) = 0.0d0
 542          interpInfo%stepProcData(procIndex,stepIndex)%allLonRot(:,:,:) = 0.0d0
 543        end do
 544      end do
 545    end if
 546
 547    nlev_T = gsv_getNumLev(stateVector,'TH')
 548    nlev_M = gsv_getNumLev(stateVector,'MM')
 549
 550    doSetup3dHeights = doSlantPath .and.  &
 551                       .not. gsv_isAllocated(stateVector_1Step) .and. &
 552                       gsv_varExist(stateVector,'Z_T') .and. &
 553                       gsv_varExist(stateVector,'Z_M')
 554
 555    ! prepare for extracting the 3D height for slant-path calculation
 556    if ( doSetup3dHeights ) then
 557
 558      write(*,*) 's2c_setupInterpInfo: extracting 3D heights for slant-path for ', inputStateVectorType 
 559
 560      if ( inputStateVectorType == 'nl' ) then
 561        nullify(varNames)
 562        call gsv_varNamesList(varNames, stateVector)
 563        call gsv_allocate( stateVector_VarsLevs_1Step, 1, &
 564                           stateVector%hco, stateVector%vco, &
 565                           mpi_local_opt=.true., mpi_distribution_opt='VarsLevs', &
 566                           dataKind_opt=4, varNames_opt=varNames )
 567
 568        call gsv_getField(stateVector,height3D_r4_ptr1)
 569        call gsv_getField(stateVector_VarsLevs_1Step,height3D_r4_ptr2)
 570        height3D_r4_ptr2(:,:,:) = height3D_r4_ptr1(:,:,:)
 571
 572        call gsv_allocate( stateVector_Tiles_allVar_1Step, 1, &
 573                           stateVector%hco, stateVector%vco, &
 574                           mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
 575                           dataKind_opt=4, varNames_opt=varNames )
 576
 577        call gsv_transposeVarsLevsToTiles( stateVector_VarsLevs_1Step, stateVector_Tiles_allVar_1Step )
 578        call gsv_deallocate(statevector_VarsLevs_1Step)
 579
 580        call gsv_allocate( stateVector_Tiles_1Step, 1, &
 581                           stateVector%hco, stateVector%vco, &
 582                           mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
 583                           dataKind_opt=4, varNames_opt=(/'Z_M','Z_T'/) )
 584
 585        call gsv_getField(stateVector_Tiles_allVar_1Step,height3D_r4_ptr1,'Z_T')
 586        call gsv_getField(stateVector_Tiles_1Step,height3D_r4_ptr2,'Z_T')
 587        height3D_r4_ptr2(:,:,:) = height3D_r4_ptr1(:,:,:)
 588
 589        call gsv_getField(stateVector_Tiles_allVar_1Step,height3D_r4_ptr1,'Z_M')
 590        call gsv_getField(stateVector_Tiles_1Step,height3D_r4_ptr2,'Z_M')
 591        height3D_r4_ptr2(:,:,:) = height3D_r4_ptr1(:,:,:)
 592
 593        call gsv_deallocate(stateVector_Tiles_allVar_1Step)
 594
 595      else
 596        stateVector_Tiles_ptr => gvt_getStateVectorTrial('height')
 597
 598        call gsv_allocate( stateVector_Tiles_1Step, 1, &
 599                           stateVector%hco, stateVector%vco, &
 600                           mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
 601                           dataKind_opt=4, varNames_opt=(/'Z_M','Z_T'/) )
 602
 603        call gsv_getField(stateVector_Tiles_ptr,height3D_r8_ptr1,'Z_T')
 604        call gsv_getField(stateVector_Tiles_1Step,height3D_r4_ptr2,'Z_T')
 605        height3D_r4_ptr2(:,:,:) = height3D_r8_ptr1(:,:,:)
 606
 607        call gsv_getField(stateVector_Tiles_ptr,height3D_r8_ptr1,'Z_M')
 608        call gsv_getField(stateVector_Tiles_1Step,height3D_r4_ptr2,'Z_M')
 609        height3D_r4_ptr2(:,:,:) = height3D_r8_ptr1(:,:,:)
 610
 611      end if ! inputStateVectorType 
 612
 613      ! Communicate 3D height fields onto all mpi tasks
 614      call gsv_allocate( stateVector_1Step, 1, &
 615                         stateVector%hco, stateVector%vco, &
 616                         mpi_local_opt=.false., &
 617                         dataKind_opt=4, varNames_opt=(/'Z_M','Z_T'/) )
 618      call utl_tmg_start(32,'------s2c_Slant')
 619      call gsv_transposeTilesToMpiGlobal(stateVector_1Step, stateVector_Tiles_1Step)
 620      call utl_tmg_stop(32)
 621      call gsv_getField(stateVector_1Step,height3D_T_r4,'Z_T')
 622      call gsv_getField(stateVector_1Step,height3D_M_r4,'Z_M')
 623    
 624      write(*,*) 's2c_setupInterpInfo, height3D_T_r4='
 625      write(*,*) height3D_T_r4(1,1,:)
 626      write(*,*) 's2c_setupInterpInfo, height3D_M_r4='
 627      write(*,*) height3D_M_r4(1,1,:)
 628
 629      write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 630    end if ! doSlantPath 
 631
 632    ! get observation lat-lon and footprint radius onto all mpi tasks
 633    step_loop2: do stepIndex = 1, numStep
 634      numHeaderUsed = 0
 635
 636      footprintRadiusVec_r4(:) = bilinearFootprint
 637
 638      header_loop2: do headerIndex = headerIndexBeg, headerIndexEnd
 639
 640        ! if obs inside window, but zero weight for current stepIndex then skip it
 641        if ( oti_getTimeInterpWeight(interpInfo%oti, headerIndex, stepIndex) == 0.0d0 ) then
 642          cycle header_loop2
 643        end if
 644
 645        numHeaderUsed = numHeaderUsed + 1
 646        headerIndexVec(numHeaderUsed,stepIndex) = headerIndex
 647
 648        footprintRadiusVec_r4(numHeaderUsed) = s2c_getFootprintRadius(obsSpaceData, &
 649                                                                stateVector, headerIndex)
 650
 651      end do header_loop2
 652
 653      call rpn_comm_allgather(footprintRadiusVec_r4,                numHeaderUsedMax, 'MPI_REAL4', &
 654                              allFootprintRadius_r4(:,stepIndex,:), numHeaderUsedMax, 'MPI_REAL4', &
 655                              'GRID', ierr)
 656
 657      allocate(latColumn(numHeaderUsedMax,allkBeg(1):stateVector%nk))
 658      allocate(lonColumn(numHeaderUsedMax,allkBeg(1):stateVector%nk))
 659      latColumn(:,:) = 0.0d0
 660      lonColumn(:,:) = 0.0d0
 661
 662      if ( doSlantPath .and. &
 663           gsv_varExist(stateVector,'Z_T') .and. &
 664           gsv_varExist(stateVector,'Z_M') ) then
 665
 666        allocate(latLev_T(nlev_T))
 667        allocate(lonLev_T(nlev_T))
 668        allocate(latLev_M(nlev_M))
 669        allocate(lonLev_M(nlev_M))
 670        latLev_T(:) = 0.0d0
 671        lonLev_T(:) = 0.0d0
 672        latLev_M(:) = 0.0d0
 673        lonLev_M(:) = 0.0d0
 674
 675        firstHeaderSlantPathTO = .true.
 676        firstHeaderSlantPathRO = .true.
 677        firstHeaderSlantPathRA = .true.
 678        header_loop3: do headerUsedIndex = 1, numHeaderUsed
 679          headerIndex = headerIndexVec(headerUsedIndex,stepIndex)
 680
 681          !- Get LatLon of observation location
 682          lat_r4 = real(obs_headElem_r(obsSpaceData, OBS_LAT, headerIndex), 4)
 683          lon_r4 = real(obs_headElem_r(obsSpaceData, OBS_LON, headerIndex), 4)
 684          if (lon_r4 <  0.0          ) lon_r4 = lon_r4 + 2.0*MPC_PI_R4
 685          if (lon_r4 >= 2.0*MPC_PI_R4) lon_r4 = lon_r4 - 2.0*MPC_PI_R4
 686
 687          codeType = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
 688
 689          if ( tvs_isIdBurpTovs(codeType) .and. SlantTO ) then
 690            if ( firstHeaderSlantPathTO ) then
 691              write(*,'(a,i3,a,i8)') 's2c_setupInterpInfo: start slant-path for TOVS. stepIndex = ', &
 692                   stepIndex,' and numHeaderUsed = ',numHeaderUsed
 693              firstHeaderSlantPathTO = .false.
 694            end if
 695
 696            ! calculate lat/lon along the line of sight
 697            call utl_tmg_start(32,'------s2c_Slant')
 698            call slp_calcLatLonTovs( obsSpaceData, stateVector%hco, headerIndex, & ! IN
 699                                     height3D_T_r4, height3D_M_r4,               & ! IN
 700                                     latLev_T, lonLev_T,                         & ! OUT
 701                                     latLev_M, lonLev_M,                         & ! OUT
 702                                     latLev_S, lonLev_S             )              ! OUT
 703            call utl_tmg_stop(32)
 704
 705          else if (codeType == codtyp_get_codtyp('ro') .and. SlantRO ) then
 706            if ( firstHeaderSlantPathRO ) then
 707              write(*,'(a,i3,a,i8)') 's2c_setupInterpInfo: start slant-path for RO. stepIndex = ', &
 708                   stepIndex,' and numHeaderUsed = ',numHeaderUsed
 709              firstHeaderSlantPathRO = .false.
 710            end if
 711
 712            ! Calculate lat/lon along the GPSRO obs
 713            call utl_tmg_start(32,'------s2c_Slant')
 714            call slp_calcLatLonRO( obsSpaceData, stateVector%hco, headerIndex, & ! IN
 715                                   height3D_T_r4, height3D_M_r4,               & ! IN
 716                                   latLev_T, lonLev_T,                         & ! OUT
 717                                   latLev_M, lonLev_M,                         & ! OUT
 718                                   latLev_S, lonLev_S                          ) ! OUT
 719            call utl_tmg_stop(32)
 720          else if (codeType == codtyp_get_codtyp('radar') .and. SlantRA ) then
 721            if ( firstHeaderSlantPathRA ) then
 722              write(*,'(a,i3,a,i8)') 's2c_setupInterpInfo: start slant-path for RADAR. stepIndex=', &
 723                   stepIndex,' and numHeaderUsed=',numHeaderUsed
 724              firstHeaderSlantPathRA = .false.
 725            end if
 726            
 727             ! calculate lat/lon along the radar beam obs
 728             call slp_calcLatLonRadar( obsSpaceData, stateVector%hco, headerIndex, & ! IN
 729                                     height3D_T_r4, height3D_M_r4,                 & ! IN
 730                                     latLev_T, lonLev_T,                           & ! OUT
 731                                     latLev_M, lonLev_M,                           & ! OUT
 732                                     latLev_S, lonLev_S                           ) ! OUT
 733          else
 734
 735            latLev_T(:) = real(lat_r4,8)
 736            lonLev_T(:) = real(lon_r4,8)
 737            latLev_M(:) = real(lat_r4,8)
 738            lonLev_M(:) = real(lon_r4,8)
 739            latLev_S = real(lat_r4,8)
 740            lonLev_S = real(lon_r4,8)
 741          end if
 742
 743          ! check if the slanted lat/lon is inside the domain
 744          call latlonChecks ( obsSpaceData, stateVector%hco, & ! IN
 745                              headerIndex, rejectOutsideObs, & ! IN
 746                              latLev_T, lonLev_T,            & ! IN/OUT
 747                              latLev_M, lonLev_M,            & ! IN/OUT
 748                              latLev_S, lonLev_S )             ! IN/OUT
 749
 750          ! put the lat/lon from TH/MM levels to kIndex
 751          do kIndex = allkBeg(1), stateVector%nk
 752            if ( kIndex == 0 ) then
 753              varLevel = 'SF'
 754            else
 755              levIndex = gsv_getLevFromK(stateVector,kIndex)
 756              varLevel = vnl_varLevelFromVarname(gsv_getVarNameFromK(stateVector,kIndex))
 757            end if
 758
 759            if ( varLevel == 'TH' ) then
 760              latColumn(headerUsedIndex,kIndex) = latLev_T(levIndex)
 761              lonColumn(headerUsedIndex,kIndex) = lonLev_T(levIndex)
 762            else if ( varLevel == 'MM' ) then
 763              latColumn(headerUsedIndex,kIndex) = latLev_M(levIndex)
 764              lonColumn(headerUsedIndex,kIndex) = lonLev_M(levIndex)
 765            else if ( varLevel == 'SF' ) then
 766              latColumn(headerUsedIndex,kIndex) = latLev_S
 767              lonColumn(headerUsedIndex,kIndex) = lonLev_S
 768            else
 769              call utl_abort('s2c_setupInterpInfo: unknown value of varLevel')
 770            end if
 771
 772          end do
 773
 774        end do header_loop3
 775
 776        ! MPI communication for the slant-path lat/lon
 777
 778        maxkCount = maxval(stateVector%allkCount(:) + stateVector%allkBeg(:) - allkBeg(:))
 779        numkToSend = min(mmpi_nprocs,stateVector%nk)
 780
 781        allocate(lat_recv_r8(numHeaderUsedMax,mmpi_nprocs))
 782        lat_recv_r8(:,:) = 0.0d0
 783        allocate(lat_send_r8(numHeaderUsedMax,mmpi_nprocs))
 784        lat_send_r8(:,:) = 0.0d0
 785        allocate(lon_recv_r8(numHeaderUsedMax,mmpi_nprocs))
 786        lon_recv_r8(:,:) = 0.0d0
 787        allocate(lon_send_r8(numHeaderUsedMax,mmpi_nprocs))
 788        lon_send_r8(:,:) = 0.0d0
 789
 790        ! only send the data from tasks with data, same amount to all
 791        sendsizes(:) = 0
 792        do procIndex = 1, numkToSend
 793          sendsizes(procIndex) = numHeaderUsed
 794        end do
 795        senddispls(1) = 0
 796        do procIndex = 2, mmpi_nprocs
 797          senddispls(procIndex) = senddispls(procIndex-1) + numHeaderUsedMax
 798        end do
 799
 800        recvdispls(1) = 0
 801        do procIndex = 2, mmpi_nprocs
 802          recvdispls(procIndex) = recvdispls(procIndex-1) + numHeaderUsedMax
 803        end do
 804
 805        ! loop to send (at most) 1 level to (at most) all other mpi tasks
 806        do kIndexCount = 1, maxkCount
 807
 808          sendsizes(:) = 0
 809          do procIndex = 1, mmpi_nprocs
 810            ! compute kIndex value being sent
 811            kIndex = kIndexCount + allkBeg(procIndex) - 1
 812            if ( kIndex <= stateVector%allkEnd(procIndex) ) then
 813              if( procIndex > numkToSend ) then
 814                write(*,*) 'procIndex, numkToSend = ', procIndex, numkToSend
 815                call utl_abort('ERROR: with numkToSend?')
 816              end if
 817
 818              lat_send_r8(1:numHeaderUsed,procIndex) = latColumn(1:numHeaderUsed,kIndex)
 819              lon_send_r8(1:numHeaderUsed,procIndex) = lonColumn(1:numHeaderUsed,kIndex)
 820              sendsizes(procIndex) = numHeaderUsed
 821            else
 822              sendsizes(procIndex) = 0
 823            end if
 824          end do
 825
 826          ! all tasks recv only from those with data
 827          kIndex = kIndexCount + mykBeg - 1
 828          if ( kIndex <= stateVector%mykEnd ) then
 829            do procIndex = 1, mmpi_nprocs
 830              recvsizes(procIndex) = allNumHeaderUsed(stepIndex,procIndex)
 831            end do
 832          else
 833            recvsizes(:) = 0
 834          end if
 835
 836          call mpi_alltoallv(lat_send_r8, sendsizes, senddispls, mmpi_datyp_real8,  &
 837                             lat_recv_r8, recvsizes, recvdispls, mmpi_datyp_real8,  &
 838                             mmpi_comm_grid, ierr)
 839          call mpi_alltoallv(lon_send_r8, sendsizes, senddispls, mmpi_datyp_real8,  &
 840                             lon_recv_r8, recvsizes, recvdispls, mmpi_datyp_real8,  &
 841                             mmpi_comm_grid, ierr)
 842
 843          do procIndex = 1, mmpi_nprocs
 844            ! all tasks copy the received step data into correct slot
 845            kIndex = kIndexCount + mykBeg - 1
 846            if ( kIndex <= stateVector%mykEnd ) then
 847              interpInfo%stepProcData(procIndex,stepIndex)%allLat(:,kIndex) = &
 848                   lat_recv_r8(1:allNumHeaderUsed(stepIndex,procIndex),procIndex)
 849              interpInfo%stepProcData(procIndex,stepIndex)%allLon(:,kIndex) = &
 850                   lon_recv_r8(1:allNumHeaderUsed(stepIndex,procIndex),procIndex)
 851            end if
 852          end do
 853
 854        end do ! kIndexCount
 855
 856        deallocate(lon_send_r8)
 857        deallocate(lon_recv_r8)
 858        deallocate(lat_send_r8)
 859        deallocate(lat_recv_r8)
 860
 861        deallocate(latLev_T)
 862        deallocate(lonLev_T)
 863        deallocate(latLev_M)
 864        deallocate(lonLev_M)
 865
 866      else ! not doSlantPath
 867
 868        allocate(latLev_T(1))
 869        allocate(lonLev_T(1))
 870        allocate(latLev_M(1))
 871        allocate(lonLev_M(1))
 872        latLev_T(:) = 0.0d0
 873        lonLev_T(:) = 0.0d0
 874        latLev_M(:) = 0.0d0
 875        lonLev_M(:) = 0.0d0
 876
 877        do headerUsedIndex = 1, numHeaderUsed
 878          headerIndex = headerIndexVec(headerUsedIndex,stepIndex)
 879
 880          !- Get LatLon of observation location
 881          lat_r4 = real(obs_headElem_r(obsSpaceData, OBS_LAT, headerIndex), 4)
 882          lon_r4 = real(obs_headElem_r(obsSpaceData, OBS_LON, headerIndex), 4)
 883          if (lon_r4 <  0.0          ) lon_r4 = lon_r4 + 2.0*MPC_PI_R4
 884          if (lon_r4 >= 2.0*MPC_PI_R4) lon_r4 = lon_r4 - 2.0*MPC_PI_R4
 885
 886          latLev_T(:) = real(lat_r4,8)
 887          lonLev_T(:) = real(lon_r4,8)
 888          latLev_M(:) = real(lat_r4,8)
 889          lonLev_M(:) = real(lon_r4,8)
 890
 891          ! check if the lat/lon is inside the domain
 892          call latlonChecks ( obsSpaceData, stateVector%hco, & ! IN
 893                              headerIndex, rejectOutsideObs, & ! IN
 894                              latLev_T, lonLev_T,            & ! IN/OUT
 895                              latLev_M, lonLev_M )             ! IN/OUT 
 896
 897          latColumn(headerUsedIndex,allkBeg(1)) = latLev_T(1)
 898          lonColumn(headerUsedIndex,allkBeg(1)) = lonLev_T(1)
 899        end do
 900
 901        ! gather geographical lat, lon positions of observations from all processors
 902        call rpn_comm_allgather(latColumn(:,allkBeg(1)), numHeaderUsedMax, 'MPI_REAL8', &
 903                                allLatOneLev(:,:), numHeaderUsedMax, 'MPI_REAL8', 'GRID', ierr)
 904        call rpn_comm_allgather(lonColumn(:,allkBeg(1)), numHeaderUsedMax, 'MPI_REAL8', &
 905                                allLonOneLev(:,:), numHeaderUsedMax, 'MPI_REAL8', 'GRID', ierr)
 906
 907        k_loop: do kIndex = mykBeg, statevector%mykEnd
 908          do procIndex = 1, mmpi_nprocs
 909            interpInfo%stepProcData(procIndex,stepIndex)%allLat(:,kIndex) = allLatOneLev(1:allNumHeaderUsed(stepIndex,procIndex),procIndex)
 910            interpInfo%stepProcData(procIndex,stepIndex)%allLon(:,kIndex) = allLonOneLev(1:allNumHeaderUsed(stepIndex,procIndex),procIndex)
 911          end do
 912        end do k_loop
 913
 914        deallocate(latLev_T)
 915        deallocate(lonLev_T)
 916        deallocate(latLev_M)
 917        deallocate(lonLev_M)
 918
 919      end if ! doSlantPath 
 920
 921      deallocate(lonColumn)
 922      deallocate(latColumn)
 923
 924    end do step_loop2
 925
 926    if ( gsv_isAllocated(stateVector_1Step) .and. lastCall ) then
 927      write(*,*) 's2c_setupInterpInfo: deallocate height3D fields'
 928      call gsv_deallocate(stateVector_1Step)
 929    end if
 930    deallocate(footprintRadiusVec_r4)
 931
 932    write(*,*) 's2c_setupInterpInfo: latlonChecks and lat/lon MPI comm finished.'
 933
 934    allocate(allHeaderIndex(numHeaderUsedMax,numStep,mmpi_nprocs))
 935    ! gather the headerIndexVec arrays onto all processors
 936    call rpn_comm_allgather(headerIndexVec, numHeaderUsedMax*numStep, 'MPI_INTEGER', &
 937                            allHeaderIndex, numHeaderUsedMax*numStep, 'MPI_INTEGER', &
 938                            'GRID',ierr)
 939
 940    do procIndex = 1, mmpi_nprocs
 941      do stepIndex = 1, numStep
 942        do headerIndex = 1, allNumHeaderUsed(stepIndex,procIndex)
 943          interpInfo%stepProcData(procIndex, stepIndex)%allHeaderIndex(headerIndex) = allHeaderIndex(headerIndex,stepIndex,procIndex)
 944        end do
 945      end do
 946    end do
 947
 948    ! create kdtree to use in footprint operator, if any footprint radius > 0.
 949    interpInfo%inputStateVectorType = inputStateVectorType
 950    if ( any(allFootprintRadius_r4(:,:,:) > 0.0) ) then
 951      if ( (inputStateVectorType == 'nl' .and. .not. associated(tree_nl))   .or. &
 952           (inputStateVectorType == 'tl' .and. .not. associated(tree_tlad)) .or. &
 953           (inputStateVectorType == 'ad' .and. .not. associated(tree_tlad)) ) then
 954
 955        write(*,*) 's2c_setupInterpInfo: start creating kdtree for inputStateVectorType=', &
 956                   inputStateVectorType
 957        write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 958
 959        allocate(positionArray(3,statevector%hco%ni*statevector%hco%nj))
 960
 961        gridIndex = 0
 962        do latIndex = 1, statevector%hco%nj
 963          do lonIndex = 1, statevector%hco%ni
 964            gridIndex = gridIndex + 1
 965            lat = real(stateVector % hco % lat2d_4(lonIndex,latIndex), 8)
 966            lon = real(stateVector % hco % lon2d_4(lonIndex,latIndex), 8)
 967
 968            positionArray(:,gridIndex) = kdtree2_3dPosition(lon, lat)
 969
 970          end do
 971        end do
 972
 973        nullify(tree)
 974        tree => kdtree2_create(positionArray, sort=.false., rearrange=.true.) 
 975
 976        if ( inputStateVectorType == 'nl' ) then
 977          tree_nl => tree
 978        else 
 979          tree_tlad => tree
 980        end if
 981
 982        deallocate(positionArray)
 983
 984        write(*,*) 's2c_setupInterpInfo: done creating kdtree for inputStateVectorType=', &
 985                   inputStateVectorType
 986        write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 987
 988      end if
 989    end if
 990
 991    do stepIndex = 1, numStep
 992      !$OMP PARALLEL DO PRIVATE (procIndex, kIndex, headerIndex, lat_deg_r4, lon_deg_r4, ierr, xpos_r4, ypos_r4, xpos2_r4, ypos2_r4, &
 993      !$OMP subGridIndex, numSubGridsForInterp, subGridForInterp, lat, lon, latRot, lonRot, footprintRadius_r4, numGridpt)
 994      do procIndex = 1, mmpi_nprocs
 995        do kIndex = mykBeg, statevector%mykEnd
 996          do headerIndex = 1, allNumHeaderUsed(stepIndex,procIndex)
 997
 998            ! Compute the rotated lat/lon, needed for the winds
 999
1000            lat_deg_r4 = real(interpInfo%stepProcData(procIndex,stepIndex)%allLat(headerIndex,kIndex) *  &
1001                         MPC_DEGREES_PER_RADIAN_R8)
1002            lon_deg_r4 = real(interpInfo%stepProcData(procIndex,stepIndex)%allLon(headerIndex,kIndex) *  &
1003                         MPC_DEGREES_PER_RADIAN_R8)
1004            ierr = gpos_getPositionXY( stateVector%hco%EZscintID,   &
1005                                       xpos_r4, ypos_r4, xpos2_r4, ypos2_r4, &
1006                                       lat_deg_r4, lon_deg_r4, subGridIndex )
1007
1008            if ( subGridIndex == 3 ) then
1009              ! both subGrids involved in interpolation, so first treat subGrid 1
1010              numSubGridsForInterp = 2
1011              subGridIndex = 1
1012            else
1013              ! only 1 subGrid involved in interpolation
1014              numSubGridsForInterp = 1
1015            end if
1016
1017            do subGridForInterp = 1, numSubGridsForInterp
1018
1019              if ( subGridForInterp == 1 ) then
1020                ! when only 1 subGrid involved, subGridIndex can be 1 or 2
1021              else
1022                ! when 2 subGrids, subGridIndex is set to 1 for 1st iteration, 2 for second
1023                subGridIndex = 2
1024              end if
1025
1026              if ( interpInfo%hco%rotated .and.  &
1027                   (gsv_varExist(varName='UU') .or.  &
1028                    gsv_varExist(varName='VV')) ) then
1029                lat = interpInfo%stepProcData(procIndex,stepIndex)%allLat(headerIndex,kIndex)
1030                lon = interpInfo%stepProcData(procIndex,stepIndex)%allLon(headerIndex,kIndex)
1031                call uvr_RotateLatLon( interpInfo%uvr,   & ! INOUT
1032                                       subGridIndex,     & ! IN
1033                                       latRot, lonRot,   & ! OUT (radians)
1034                                       lat, lon,         & ! IN  (radians)
1035                                       'ToLatLonRot')      ! IN
1036                interpInfo%stepProcData(procIndex,stepIndex)%allLatRot(subGridIndex,headerIndex,kIndex) = latRot
1037                interpInfo%stepProcData(procIndex,stepIndex)%allLonRot(subGridIndex,headerIndex,kIndex) = lonRot
1038              end if
1039
1040            end do ! subGridForInterp
1041
1042            ! Count total number of grid points for allocating interp depot
1043
1044            footprintRadius_r4 = allFootprintRadius_r4(headerIndex,stepIndex,procIndex)
1045
1046            call s2c_setupHorizInterp(footprintRadius_r4, interpInfo, &
1047                                      stateVector, headerIndex, kIndex, stepIndex, &
1048                                      procIndex, numGridpt)
1049
1050            ! for now, just store the number of gridpts for each obs in depotIndexEnd
1051            if ( (subGridIndex == 1) .or. (subGridIndex == 2) ) then
1052              ! indices for only 1 subgrid, other will have zeros
1053              interpInfo%stepProcData(procIndex,stepIndex)%depotIndexEnd(subGridIndex,headerIndex,kIndex) = numGridpt(subGridIndex)
1054            else
1055              ! locations on both subGrids will be averaged
1056              interpInfo%stepProcData(procIndex,stepIndex)%depotIndexEnd(1,headerIndex,kIndex) = numGridpt(1)
1057              interpInfo%stepProcData(procIndex,stepIndex)%depotIndexEnd(2,headerIndex,kIndex) = numGridpt(2)
1058            end if
1059
1060          end do ! headerIndex
1061        end do ! kIndex
1062      end do ! procIndex
1063      !$OMP END PARALLEL DO
1064    end do ! stepIndex
1065
1066    numGridptTotal = 0
1067    do stepIndex = 1, numStep
1068      do procIndex = 1, mmpi_nprocs
1069        do kIndex = mykBeg, statevector%mykEnd
1070          do headerIndex = 1, allNumHeaderUsed(stepIndex,procIndex)
1071            do subGridIndex = 1, interpInfo%hco%numSubGrid
1072              if ( interpInfo%stepProcData(procIndex,stepIndex)%depotIndexEnd(subGridIndex,headerIndex,kIndex) /= -1 ) then
1073                interpInfo%stepProcData(procIndex,stepIndex)%depotIndexBeg(subGridIndex,headerIndex,kIndex) = numGridptTotal + 1
1074                numGridptTotal = numGridptTotal + interpInfo%stepProcData(procIndex,stepIndex)%depotIndexEnd(subGridIndex,headerIndex,kIndex)
1075                interpInfo%stepProcData(procIndex,stepIndex)%depotIndexEnd(subGridIndex,headerIndex,kIndex) = numGridptTotal
1076              end if
1077            end do ! subGridIndex
1078          end do ! headerIndex
1079        end do ! kIndex
1080      end do ! procIndex
1081    end do ! stepIndex
1082
1083    deallocate(allHeaderIndex)
1084
1085    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
1086
1087    ! now that we know the size, allocate main arrays for storing interpolation information
1088    write(*,*) 's2c_setupInterpInfo: numGridptTotal = ', numGridptTotal
1089    allocate( interpInfo%latIndexDepot(numGridptTotal) )
1090    allocate( interpInfo%lonIndexDepot(numGridptTotal) )
1091    allocate( interpInfo%interpWeightDepot(numGridptTotal) )
1092
1093    call utl_tmg_start(33,'------s2c_SetupWeights')
1094    !$OMP PARALLEL DO PRIVATE (procIndex, stepIndex, kIndex, headerIndex, footprintRadius_r4, numGridpt)
1095    do procIndex = 1, mmpi_nprocs
1096      do stepIndex = 1, numStep
1097        do kIndex = mykBeg, statevector%mykEnd
1098
1099          do headerIndex = 1, allNumHeaderUsed(stepIndex,procIndex)
1100
1101            footprintRadius_r4 = allFootprintRadius_r4(headerIndex, stepIndex, procIndex)
1102
1103            call s2c_setupHorizInterp(footprintRadius_r4, interpInfo, stateVector, &
1104                                      headerIndex, kIndex, stepIndex, procIndex, numGridpt)
1105
1106          end do ! headerIndex
1107
1108        end do ! kIndex
1109      end do ! stepIndex
1110    end do ! procIndex
1111    !$OMP END PARALLEL DO
1112    call utl_tmg_stop(33)
1113
1114    ! reject obs in obsSpaceData if any processor has zero weight
1115    ! called when a mask exists to catch land contaminated ocean obs
1116    if ( stateVector%oceanMask%maskPresent ) then
1117      call s2c_rejectZeroWeightObs(interpInfo,obsSpaceData,mykBeg,stateVector%mykEnd)
1118    end if
1119
1120    ! on the last call, deallocate the tree_nl/tree_tlad
1121    if ( lastCall ) then
1122      if ( inputStateVectorType == 'nl' .and. associated(tree_nl) ) then
1123        call kdtree2_destroy(tree_nl)
1124      else if ( (inputStateVectorType == 'tl' .or. inputStateVectorType == 'ad') .and. &
1125                associated(tree_tlad) ) then
1126        call kdtree2_destroy(tree_tlad)
1127      end if
1128    end if
1129
1130    ! Count the number of TOVS using footprint operator on one level
1131    if ( useFootprintForTovs ) then 
1132      numTovsUsingFootprint = 0
1133      numAllTovs = 0
1134      procIndex = mmpi_myid + 1
1135      do stepIndex = 1, numStep
1136        do headerUsedIndex = 1, allNumHeaderUsed(stepIndex,procIndex)
1137          footprintRadius_r4 = allFootprintRadius_r4(headerUsedIndex, stepIndex, procIndex)
1138          headerIndex = headerIndexVec(headerUsedIndex,stepIndex)
1139          codeType = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
1140
1141          if ( tvs_isIdBurpTovs(codeType) ) then
1142            if ( footprintRadius_r4 > 0.0 ) numTovsUsingFootprint = numTovsUsingFootprint + 1
1143            numAllTovs = numAllTovs + 1 
1144          end if
1145        end do
1146      end do
1147
1148      if ( numAllTovs > 0 ) then 
1149        write(*,'(A,2(I5,A2),F5.1,A)') 's2c_setupInterpInfo: numTovsUsingFootprint/numAllTovs=', &
1150                       numTovsUsingFootprint, ' /', numAllTovs, ' (', &
1151                       real(numTovsUsingFootprint) / real(numAllTovs) * 100.0, '%)'
1152      end if
1153    end if
1154    
1155    deallocate(allFootprintRadius_r4)
1156    deallocate(allLonOneLev)
1157    deallocate(allLatOneLev)
1158
1159    deallocate(headerIndexVec)
1160    deallocate(allNumHeaderUsed)
1161
1162    interpInfo%initialized = .true.
1163
1164    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
1165    write(*,*) 's2c_setupInterpInfo: FINISHED'
1166
1167  end subroutine s2c_setupInterpInfo
1168
1169  !---------------------------------------------------------
1170  ! s2c_tl
1171  !---------------------------------------------------------
1172  subroutine s2c_tl(statevector_in, columnAnlInc, columnTrlOnAnlIncLev, obsSpaceData)
1173    !
1174    !:Purpose: Tangent linear version of the horizontal
1175    !           interpolation, used for the increment (or perturbations).
1176    !
1177    implicit none
1178
1179    ! Arguments:
1180    type(struct_gsv), target, intent(in)    :: stateVector_in
1181    type(struct_obs)        , intent(inout) :: obsSpaceData
1182    type(struct_columnData) , intent(inout) :: columnAnlInc
1183    type(struct_columnData) , intent(inout) :: columnTrlOnAnlIncLev
1184
1185    ! Locals:
1186    type(struct_gsv)           :: stateVector_VarsLevs
1187    type(struct_gsv), pointer  :: stateVector
1188    integer :: kIndex, kIndex2, levIndex, kCount, stepIndex, numStep, mykEndExtended
1189    integer :: headerIndex, numHeader, numHeaderMax, yourNumHeader
1190    integer :: procIndex, nsize, ierr, headerUsedIndex
1191    real(8) :: weight
1192    real(8), pointer     :: allCols_ptr(:,:)
1193    real(pre_incrReal), pointer :: ptr4d(:,:,:,:)
1194    real(pre_incrReal), pointer :: ptr3d_UV(:,:,:)
1195    real(8), allocatable :: cols_hint(:,:,:)
1196    real(8), allocatable :: cols_send(:,:)
1197    real(8), allocatable :: cols_recv(:,:)
1198    real(8), allocatable :: cols_send_1proc(:)
1199    logical              :: rejectOutsideObs
1200    character(len=4)     :: varName
1201    character(len=4), pointer :: varNames(:)
1202
1203    call utl_tmg_start(30,'--StateToColumn')
1204
1205    if ( mmpi_myid == 0 ) write(*,*) 's2c_tl: Horizontal interpolation StateVector --> ColumnData'
1206    call utl_tmg_start(38,'----s2c_TL')
1207
1208    call rpn_comm_barrier('GRID',ierr)
1209
1210    if ( .not. gsv_isAllocated(stateVector_in) ) then 
1211      call utl_abort('s2c_tl: stateVector must be allocated')
1212    end if
1213
1214    if (interpInfo_tlad%initialized) then
1215      if (.not. hco_equal(interpInfo_tlad%hco,stateVector_in%hco)) then
1216        write(*,*) 's2c_tl: WARNING! Current hco grid parameters differ from allocated interpInfo_tlad!'
1217        write(*,*) 's2c_tl: InterpInfo_tlad will be deallocated.'
1218	call s2c_deallocInterpInfo(inputStateVectorType='tlad')
1219      end if
1220    end if
1221
1222    ! check the column and statevector have same nk/varNameList
1223    call checkColumnStatevectorMatch(columnAnlInc,statevector_in)
1224
1225    ! if we only compute Height and Pressure on column, make copy without them
1226    if ( calcHeightPressIncrOnColumn ) then
1227      allocate(stateVector)
1228      call gsv_allocate( stateVector, statevector_in%numstep, &
1229                         statevector_in%hco, statevector_in%vco, &
1230                         mpi_local_opt=.true., &
1231                         dataKind_opt=gsv_getDataKind(statevector_in), &
1232                         allocHeight_opt=.false., allocPressure_opt=.false. )
1233      call gsv_copy(stateVector_in, stateVector, allowVarMismatch_opt=.true.)
1234    else
1235      stateVector => stateVector_in
1236
1237      ! calculate delP_T/delP_M and del Z_T/Z_M on the grid
1238      call gvt_transform( statevector, 'ZandP_tl' )
1239    end if
1240
1241    nullify(varNames)
1242    call gsv_varNamesList(varNames, statevector)
1243    if (statevector%mpi_distribution == 'None') then
1244      call gsv_allocate( statevector_VarsLevs, statevector%numstep, &
1245                         statevector%hco, statevector%vco,          &
1246                         mpi_local_opt=.false., mpi_distribution_opt='None', &
1247                         dataKind_opt=gsv_getDataKind(statevector), &
1248                         varNames_opt=varNames )
1249    else
1250      call gsv_allocate( statevector_VarsLevs, statevector%numstep, &
1251                         statevector%hco, statevector%vco,          &
1252                         mpi_local_opt=.true., mpi_distribution_opt='VarsLevs', &
1253                         dataKind_opt=gsv_getDataKind(statevector), &
1254                         varNames_opt=varNames )
1255    end if
1256    deallocate(varNames)
1257    call gsv_transposeTilesToVarsLevs( statevector, statevector_VarsLevs )
1258
1259    numStep = stateVector_VarsLevs%numStep
1260    numHeader = obs_numheader(obsSpaceData)
1261    call rpn_comm_allreduce(numHeader, numHeaderMax, 1,  &
1262                            'MPI_INTEGER', 'MPI_MAX', 'GRID', ierr)
1263
1264    if ( .not. interpInfo_tlad%initialized ) then
1265      rejectOutsideObs = .false.
1266      call utl_tmg_stop(38)
1267      call utl_tmg_start(31,'----s2c_Setups')
1268      call s2c_setupInterpInfo( interpInfo_tlad, obsSpaceData, stateVector_VarsLevs,  &
1269                                1, numHeader, timeInterpType_tlad,  rejectOutsideObs, &
1270                                inputStateVectorType='tl' )
1271      call utl_tmg_stop(31)
1272      call utl_tmg_start(38,'----s2c_TL')
1273    end if
1274
1275    ! arrays for interpolated column for 1 level/variable and each time step
1276    allocate(cols_hint(maxval(interpInfo_tlad%allNumHeaderUsed),numStep,mmpi_nprocs))
1277    cols_hint(:,:,:) = 0.0d0
1278
1279    ! arrays for sending/receiving time interpolated column for 1 level/variable
1280    allocate(cols_send(numHeaderMax,mmpi_nprocs))
1281    cols_send(:,:) = 0.0d0
1282
1283    allocate(cols_recv(numHeaderMax,mmpi_nprocs))
1284    cols_recv(:,:) = 0.0d0
1285
1286    allocate(cols_send_1proc(numHeaderMax))
1287
1288    ! set contents of column to zero
1289    allCols_ptr => col_getAllColumns(columnAnlInc)
1290    if ( numHeader > 0 ) allCols_ptr(:,:) = 0.0d0
1291
1292    call gsv_getField(stateVector_VarsLevs, ptr4d)
1293
1294    mykEndExtended = stateVector_VarsLevs%mykBeg + maxval(stateVector_VarsLevs%allkCount(:)) - 1
1295
1296    kCount = 0
1297    k_loop: do kIndex = stateVector_VarsLevs%mykBeg, mykEndExtended
1298
1299      kCount = kCount + 1
1300
1301      if ( kIndex <= stateVector_VarsLevs%mykEnd ) then
1302        varName = gsv_getVarNameFromK(statevector,kIndex)
1303
1304        if ( varName == 'UU' .or. varName == 'VV' ) then
1305          call gsv_getFieldUV(stateVector_VarsLevs,ptr3d_UV,kIndex)
1306        end if
1307
1308        call utl_tmg_start(39,'------s2c_TL_Hinterp')
1309        !$OMP PARALLEL DO PRIVATE (stepIndex, procIndex, yourNumHeader, headerIndex)
1310        step_loop: do stepIndex = 1, numStep
1311          if ( maxval(interpInfo_tlad%allNumHeaderUsed(stepIndex,:)) == 0 ) cycle step_loop
1312
1313          ! interpolate to the columns destined for all procs for all steps and one lev/var
1314          do procIndex = 1, mmpi_nprocs
1315            yourNumHeader = interpInfo_tlad%allNumHeaderUsed(stepIndex,procIndex)
1316            if ( yourNumHeader > 0 ) then
1317              if ( varName == 'UU' ) then
1318                call myezuvint_tl( cols_hint(1:yourNumHeader,stepIndex,procIndex), 'UU',  &
1319                                   ptr4d(:,:,kIndex,stepIndex), ptr3d_UV(:,:,stepIndex),  &
1320                                   interpInfo_tlad, kIndex, stepIndex, procIndex )
1321              else if ( varName == 'VV' ) then
1322                call myezuvint_tl( cols_hint(1:yourNumHeader,stepIndex,procIndex), 'VV',  &
1323                                   ptr3d_UV(:,:,stepIndex), ptr4d(:,:,kIndex,stepIndex),  &
1324                                   interpInfo_tlad, kIndex, stepIndex, procIndex )
1325              else
1326                call myezsint_tl( cols_hint(1:yourNumHeader,stepIndex,procIndex),  &
1327                                  ptr4d(:,:,kIndex,stepIndex), interpInfo_tlad, kIndex, &
1328                                  stepIndex, procIndex )
1329              end if
1330            end if
1331          end do
1332
1333        end do step_loop
1334        !$OMP END PARALLEL DO
1335        call utl_tmg_stop(39)
1336
1337        ! interpolate in time to the columns destined for all procs and one level/variable
1338        do procIndex = 1, mmpi_nprocs
1339          cols_send_1proc(:) = 0.0d0
1340          do stepIndex = 1, numStep
1341            !$OMP PARALLEL DO PRIVATE (headerUsedIndex, headerIndex, weight)
1342            do headerUsedIndex = 1, interpInfo_tlad%allNumHeaderUsed(stepIndex, procIndex)
1343              headerIndex = interpInfo_tlad%stepProcData(procIndex, stepIndex)%allHeaderIndex(headerUsedIndex)
1344              weight = oti_getTimeInterpWeightMpiGlobal(interpInfo_tlad%oti,  &
1345                                                        headerIndex,stepIndex,procIndex)
1346              cols_send_1proc(headerIndex) = cols_send_1proc(headerIndex) &
1347                            + weight * cols_hint(headerUsedIndex,stepIndex,procIndex)
1348
1349            end do
1350            !$OMP END PARALLEL DO
1351          end do
1352          cols_send(:,procIndex) = cols_send_1proc(:)
1353        end do
1354
1355      else
1356
1357        ! this value of k does not exist on this mpi task
1358        cols_send(:,:) = 0.0
1359
1360      end if ! if kIndex <= mykEnd
1361
1362      call rpn_comm_barrier('GRID',ierr)
1363
1364      ! mpi communication: alltoall for one level/variable
1365      nsize = numHeaderMax
1366      if(mmpi_nprocs > 1) then
1367        call rpn_comm_alltoall(cols_send, nsize, 'MPI_REAL8',  &
1368                               cols_recv, nsize, 'MPI_REAL8', 'GRID', ierr)
1369      else
1370        cols_recv(:,1) = cols_send(:,1)
1371      end if
1372
1373      ! reorganize ensemble of distributed columns
1374      !$OMP PARALLEL DO PRIVATE (procIndex, kIndex2, varName, levIndex, allCols_ptr, headerIndex)
1375      proc_loop: do procIndex = 1, mmpi_nprocs
1376        ! This is kIndex value of source (can be different for destination)
1377        kIndex2 = statevector_VarsLevs%allkBeg(procIndex) + kCount - 1
1378        if ( kIndex2 > stateVector_VarsLevs%allkEnd(procIndex) ) cycle proc_loop
1379
1380        ! Figure out which variable/level of destination
1381        varName = gsv_getVarNameFromK(statevector,kIndex2)
1382        levIndex = gsv_getLevFromK(statevector,kIndex2)
1383        allCols_ptr => col_getAllColumns(columnAnlInc,varName)
1384
1385        do headerIndex = 1, numHeader
1386          allCols_ptr(levIndex,headerIndex) = cols_recv(headerIndex,procIndex)
1387        end do
1388      end do proc_loop
1389      !$OMP END PARALLEL DO
1390
1391    end do k_loop
1392
1393    if (calcHeightPressIncrOnColumn) then
1394      ! calculate delP_T/delP_M and  del Z_T/Z_M on the columns
1395      call czp_calcZandP_tl(columnAnlInc, columnTrlOnAnlIncLev)
1396    end if
1397
1398    deallocate(cols_hint)
1399    deallocate(cols_send)
1400    deallocate(cols_recv)
1401    deallocate(cols_send_1proc)
1402
1403    call gsv_deallocate( statevector_VarsLevs )
1404    if (calcHeightPressIncrOnColumn) then
1405      call gsv_deallocate( stateVector )
1406      deallocate(stateVector)
1407    end if
1408    
1409    if (slantPath_TO_tlad) call pressureProfileMonotonicityCheck(obsSpaceData, columnTrlOnAnlIncLev)
1410
1411    call utl_tmg_stop(38)
1412
1413    call utl_tmg_stop(30)
1414
1415  end subroutine s2c_tl
1416
1417  !---------------------------------------------------------
1418  ! s2c_ad
1419  !---------------------------------------------------------
1420  subroutine s2c_ad(statevector_out, columnAnlInc, columnTrlOnAnlIncLev, obsSpaceData)
1421    !
1422    !:Purpose: Adjoint version of the horizontal interpolation,
1423    !           used for the cost function gradient with respect to the increment.
1424    !
1425    implicit none
1426
1427    ! Arguments:
1428    type(struct_gsv), target, intent(inout) :: stateVector_out
1429    type(struct_obs)        , intent(inout) :: obsSpaceData
1430    type(struct_columnData) , intent(inout) :: columnAnlInc
1431    type(struct_columnData) , intent(inout) :: columnTrlOnAnlIncLev
1432
1433    ! Locals:
1434    type(struct_gsv)           :: stateVector_VarsLevs
1435    type(struct_gsv), pointer  :: stateVector
1436    integer :: kIndex, kIndex2, kCount, levIndex, stepIndex, numStep, mykEndExtended
1437    integer :: headerIndex, numHeader, numHeaderMax, yourNumHeader
1438    integer :: procIndex, nsize, ierr, headerUsedIndex
1439    character(len=4)     :: varName
1440    real(8) :: weight
1441    real(8), pointer     :: allCols_ptr(:,:)
1442    real(pre_incrReal), pointer :: ptr4d(:,:,:,:), ptr3d_UV(:,:,:)
1443    real(8), allocatable :: cols_hint(:,:,:)
1444    real(8), allocatable :: cols_send(:,:)
1445    real(8), allocatable :: cols_recv(:,:)
1446    logical              :: rejectOutsideObs
1447    character(len=4), pointer :: varNames(:)
1448
1449    call utl_tmg_start(30,'--StateToColumn')
1450
1451    if(mmpi_myid == 0) write(*,*) 's2c_ad: Adjoint of horizontal interpolation StateVector --> ColumnData'
1452    call utl_tmg_start(40,'----s2c_AD')
1453
1454    call rpn_comm_barrier('GRID',ierr)
1455
1456    if ( .not. gsv_isAllocated(stateVector_out) ) then 
1457      call utl_abort('s2c_ad: stateVector must be allocated')
1458    end if
1459
1460    if (interpInfo_tlad%initialized) then
1461      if (.not. hco_equal(interpInfo_tlad%hco,stateVector_out%hco)) then
1462        write(*,*) 's2c_ad: WARNING! Current hco grid parameters differ from allocated interpInfo_tlad!'
1463        write(*,*) 's2c_ad: InterpInfo_tlad will be deallocated.'
1464        call s2c_deallocInterpInfo(inputStateVectorType='tlad')
1465      end if
1466    end if
1467
1468
1469    ! if we only compute Height and Pressure on column, make copy without them
1470    if (calcHeightPressIncrOnColumn) then
1471      allocate(stateVector)
1472      call gsv_allocate( stateVector, statevector_out%numstep, &
1473                         statevector_out%hco, statevector_out%vco, &
1474                         mpi_local_opt=.true., &
1475                         dataKind_opt=gsv_getDataKind(statevector_out), &
1476                         allocHeight_opt=.false., allocPressure_opt=.false. )
1477      ! Adjoint of calculate del Z_T/Z_M and delP_T/delP_M on the columns
1478      call czp_calcZandP_ad(columnAnlInc, columnTrlOnAnlIncLev)
1479    else
1480      stateVector => stateVector_out
1481    end if
1482
1483    nullify(varNames)
1484    call gsv_varNamesList(varNames, statevector)
1485    call gsv_allocate( statevector_VarsLevs, statevector%numstep, &
1486                       statevector%hco, statevector%vco,          &
1487                       mpi_local_opt=.true., mpi_distribution_opt='VarsLevs', &
1488                       dataKind_opt=gsv_getDataKind(statevector), &
1489                       varNames_opt=varNames )
1490    deallocate(varNames)
1491    call gsv_zero( statevector_VarsLevs )
1492
1493    numStep = stateVector_VarsLevs%numStep
1494    numHeader = obs_numheader(obsSpaceData)
1495    call rpn_comm_allreduce(numHeader, numHeaderMax, 1,  &
1496                            'MPI_INTEGER', 'MPI_MAX', 'GRID', ierr)
1497
1498    if ( .not. interpInfo_tlad%initialized ) then
1499      rejectOutsideObs = .false.
1500      call utl_tmg_stop(40)
1501      call utl_tmg_start(31,'----s2c_Setups')
1502      call s2c_setupInterpInfo( interpInfo_tlad, obsSpaceData, stateVector_VarsLevs,  &
1503                                1, numHeader, timeInterpType_tlad, rejectOutsideObs,  &
1504                                inputStateVectorType='ad' )
1505      call utl_tmg_stop(31)
1506      call utl_tmg_start(40,'----s2c_AD')
1507    end if
1508
1509    ! arrays for interpolated column for 1 level/variable and each time step
1510    allocate(cols_hint(maxval(interpInfo_tlad%allNumHeaderUsed),numStep,mmpi_nprocs))
1511    cols_hint(:,:,:) = 0.0d0
1512
1513    ! arrays for sending/receiving time interpolated column for 1 level/variable
1514    allocate(cols_send(numHeaderMax,mmpi_nprocs))
1515    cols_send(:,:) = 0.0d0
1516
1517    allocate(cols_recv(numHeaderMax,mmpi_nprocs))
1518    cols_recv(:,:) = 0.0d0
1519
1520    ! set contents of column to zero
1521    allCols_ptr => col_getAllColumns(columnAnlInc)
1522
1523    call gsv_getField(stateVector_VarsLevs,ptr4d)
1524    mykEndExtended = stateVector_VarsLevs%mykBeg + maxval(stateVector_VarsLevs%allkCount(:)) - 1
1525
1526    kCount = 0
1527    k_loop: do kIndex = stateVector_VarsLevs%mykBeg, mykEndExtended
1528
1529      kCount = kCount + 1
1530
1531      ! reorganize ensemble of distributed columns
1532      !$OMP PARALLEL DO PRIVATE (procIndex, kIndex2, varName, levIndex, allCols_ptr, headerIndex)
1533      proc_loop: do procIndex = 1, mmpi_nprocs
1534        ! This is kIndex value of destination (can be different for source)
1535        kIndex2 = statevector_VarsLevs%allkBeg(procIndex) + kCount - 1
1536        if ( kIndex2 > stateVector_VarsLevs%allkEnd(procIndex) ) cycle proc_loop
1537        
1538        ! Figure out which variable/level of source
1539        varName = gsv_getVarNameFromK(statevector,kIndex2)
1540        levIndex = gsv_getLevFromK(statevector,kIndex2)
1541        allCols_ptr => col_getAllColumns(columnAnlInc,varName)
1542
1543        do headerIndex = 1, numHeader
1544          cols_send(headerIndex,procIndex) = allCols_ptr(levIndex,headerIndex)
1545        end do
1546      end do proc_loop
1547      !$OMP END PARALLEL DO
1548
1549      call rpn_comm_barrier('GRID',ierr)
1550
1551      ! mpi communication: alltoall for one level/variable
1552      nsize = numHeaderMax
1553      if(mmpi_nprocs > 1) then
1554        call rpn_comm_alltoall(cols_send, nsize, 'MPI_REAL8',  &
1555                               cols_recv, nsize, 'MPI_REAL8', 'GRID', ierr)
1556      else
1557        cols_recv(:,1) = cols_send(:,1)
1558      end if
1559
1560      if ( kIndex <= stateVector_VarsLevs%mykEnd ) then
1561        varName = gsv_getVarNameFromK(statevector,kIndex)
1562
1563        if ( varName == 'UU' .or. varName == 'VV' ) then
1564          call gsv_getFieldUV(stateVector_VarsLevs, ptr3d_UV, kIndex)
1565        end if
1566
1567        ! interpolate in time to the columns destined for all procs and one level/variable
1568        do procIndex = 1, mmpi_nprocs
1569          do stepIndex = 1, numStep
1570            !$OMP PARALLEL DO PRIVATE (headerIndex, headerUsedIndex, weight)
1571            do headerUsedIndex = 1, interpInfo_tlad%allNumHeaderUsed(stepIndex, procIndex)
1572
1573              headerIndex = interpInfo_tlad%stepProcData(procIndex, stepIndex)%allHeaderIndex(headerUsedIndex)
1574              weight = oti_getTimeInterpWeightMpiGlobal(interpInfo_tlad%oti,  &
1575                                                        headerIndex,stepIndex,procIndex)
1576
1577              cols_hint(headerUsedIndex,stepIndex,procIndex) =  &
1578                   weight * cols_recv(headerIndex,procIndex)
1579
1580            end do
1581            !$OMP END PARALLEL DO
1582          end do
1583        end do
1584
1585        call utl_tmg_start(41,'------s2c_AD_Hinterp')
1586        !$OMP PARALLEL DO PRIVATE (stepIndex, procIndex, yourNumHeader)
1587        step_loop: do stepIndex = 1, numStep
1588          if ( maxval(interpInfo_tlad%allNumHeaderUsed(stepIndex,:)) == 0 ) cycle step_loop
1589
1590          ! interpolate to the columns destined for all procs for all steps and one lev/var
1591          do procIndex = 1, mmpi_nprocs
1592            yourNumHeader = interpInfo_tlad%allNumHeaderUsed(stepIndex,procIndex)
1593            if ( yourNumHeader > 0 ) then
1594              if ( varName == 'UU' ) then
1595                call myezuvint_ad( cols_hint(1:yourNumHeader,stepIndex,procIndex), 'UU',  &
1596                                   ptr4d(:,:,kIndex,stepIndex), ptr3d_UV(:,:,stepIndex),  &
1597                                   interpInfo_tlad, kIndex, stepIndex, procIndex )
1598              else if ( varName == 'VV' ) then
1599                call myezuvint_ad( cols_hint(1:yourNumHeader,stepIndex,procIndex), 'VV',  &
1600                                   ptr3d_UV(:,:,stepIndex), ptr4d(:,:,kIndex,stepIndex),  &
1601                                   interpInfo_tlad, kIndex, stepIndex, procIndex )
1602              else
1603                call myezsint_ad( cols_hint(1:yourNumHeader,stepIndex,procIndex), &
1604                                  ptr4d(:,:,kIndex,stepIndex), interpInfo_tlad, kIndex, &
1605                                  stepIndex, procIndex )
1606              end if
1607            end if
1608          end do
1609
1610        end do step_loop
1611        !$OMP END PARALLEL DO
1612        call utl_tmg_stop(41)
1613
1614      end if ! if kIndex <= mykEnd
1615
1616    end do k_loop
1617
1618    deallocate(cols_hint)
1619    deallocate(cols_send)
1620    deallocate(cols_recv)
1621
1622    call rpn_comm_barrier('GRID',ierr)
1623
1624    call gsv_transposeTilesToVarsLevsAd( statevector_VarsLevs, statevector )
1625
1626    if (calcHeightPressIncrOnColumn) then
1627      call gsv_zero(statevector_out)
1628      call gsv_copy(stateVector, stateVector_out, allowVarMismatch_opt=.true.)
1629    else
1630      ! Adjoint of calculate del Z_T/Z_M and delP_T/delP_M on the grid
1631      call gvt_transform( statevector, 'ZandP_ad' )
1632    end if
1633
1634    call gsv_deallocate( statevector_VarsLevs )
1635    if (calcHeightPressIncrOnColumn) then
1636      call gsv_deallocate( statevector )
1637      deallocate(stateVector)
1638    end if
1639
1640    if (slantPath_TO_tlad) call pressureProfileMonotonicityCheck(obsSpaceData, columnTrlOnAnlIncLev)
1641
1642    call utl_tmg_stop(40)
1643
1644    call utl_tmg_stop(30)
1645
1646  end subroutine s2c_ad
1647
1648  !---------------------------------------------------------
1649  ! s2c_nl
1650  !---------------------------------------------------------
1651  subroutine s2c_nl(stateVector, obsSpaceData, column, hco_core, timeInterpType, &
1652                   varName_opt, numObsBatches_opt, dealloc_opt, moveObsAtPole_opt, &
1653                   beSilent_opt)
1654    !
1655    !:Purpose: Non-linear version of the horizontal interpolation,
1656    !           used for a full field (usually the background state when computing
1657    !           the innovation vector).
1658    !
1659    implicit none
1660
1661    ! Arguments:
1662    type(struct_gsv)       ,    intent(inout) :: stateVector
1663    type(struct_obs)       ,    intent(inout) :: obsSpaceData
1664    type(struct_columnData),    intent(inout) :: column
1665    type(struct_hco), pointer,  intent(in)    :: hco_core
1666    character(len=*)          , intent(in)    :: timeInterpType
1667    character(len=*), optional, intent(in)    :: varName_opt
1668    integer,          optional, intent(in)    :: numObsBatches_opt
1669    logical,          optional, intent(in)    :: dealloc_opt
1670    logical,          optional, intent(in)    :: moveObsAtPole_opt
1671    logical,          optional, intent(in)    :: beSilent_opt
1672
1673    ! Locals:
1674    type(struct_gsv), save :: stateVector_VarsLevs 
1675    integer :: kIndex, kIndex2, kCount, stepIndex, numStep, mykEndExtended
1676    integer :: headerIndex, headerIndex2, numHeader, numHeaderMax, yourNumHeader
1677    integer :: headerIndexBeg, headerIndexEnd, obsBatchIndex, numObsBatches
1678    integer :: procIndex, nsize, ierr, headerUsedIndex, allHeaderIndexBeg(mmpi_nprocs)
1679    integer :: kIndexHeightSfc, varNameIndex, allNumHeader(mmpi_nprocs)
1680    real(8) :: weight
1681    character(len=4)     :: varName
1682    real(8), pointer     :: column_ptr(:), ptr2d_r8(:,:), allCols_ptr(:,:)
1683    real(4), pointer     :: ptr4d_r4(:,:,:,:), ptr3d_UV_r4(:,:,:)
1684    real(8), allocatable :: cols_hint(:,:,:)
1685    real(8), allocatable :: cols_send(:,:)
1686    real(8), allocatable :: cols_recv(:,:)
1687    real(8), allocatable :: cols_send_1proc(:)
1688    integer              :: displs(mmpi_nprocs), nsizes(mmpi_nprocs)
1689    integer              :: senddispls(mmpi_nprocs), sendsizes(mmpi_nprocs)
1690    integer              :: recvdispls(mmpi_nprocs), recvsizes(mmpi_nprocs)
1691    logical              :: dealloc, moveObsAtPole, rejectOutsideObs, beSilent
1692    logical, save        :: firstCall = .true.
1693    character(len=4), pointer :: varNames(:)
1694
1695    call utl_tmg_start(30,'--StateToColumn')
1696    call utl_tmg_start(34,'----s2c_NL')
1697
1698    call utl_tmg_start(37,'------s2c_NL_barrier')
1699    call rpn_comm_barrier('GRID',ierr)
1700    call utl_tmg_stop(37)
1701
1702    if ( present(beSilent_opt) ) then
1703      beSilent = beSilent_opt
1704    else
1705      beSilent = .false.
1706    end if
1707
1708    if ( .not. beSilent ) then
1709      write(*,*) 's2c_nl: STARTING'
1710      write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
1711    end if
1712
1713    if ( .not. gsv_isAllocated(stateVector) ) then 
1714      call utl_abort('s2c_nl: stateVector must be allocated')
1715    end if
1716
1717    if (present(dealloc_opt)) then
1718      dealloc = dealloc_opt
1719    else
1720      dealloc = .true.
1721    end if
1722
1723    ! determine number of obs batches (to reduce memory usage)
1724    if (present(numObsBatches_opt)) then
1725      numObsBatches = numObsBatches_opt
1726    else
1727      numObsBatches = 1
1728    end if
1729    if (.not. dealloc) then
1730      numObsBatches = 1 ! multiple batches only possible if dealloc=.true.
1731    end if
1732
1733    if (interpInfo_nl%initialized) then
1734      if (.not. hco_equal(interpInfo_nl%hco,stateVector%hco) .or. numObsBatches > 1) then
1735        write(*,*) 's2c_nl: WARNING! Current hco grid parameters differ from allocated interpInfo!'
1736        write(*,*) 's2c_nl: InterpInfo will be deallocated.'
1737        call s2c_deallocInterpInfo(inputStateVectorType='nl')
1738      end if	
1739    end if
1740
1741    if ( stateVector%mpi_distribution /= 'Tiles' ) then 
1742      call utl_abort('s2c_nl: stateVector must by Tiles distributed')
1743    end if
1744
1745    if ( present(moveObsAtPole_opt) ) then
1746      moveObsAtPole = moveObsAtPole_opt
1747    else
1748      moveObsAtPole = .false.
1749    end if
1750
1751    ! check the column and statevector have same nk/varNameList
1752    call checkColumnStatevectorMatch(column,statevector)
1753
1754    ! calculate delP_T/delP_M and del Z_T/Z_M on the grid
1755    call gvt_transform( statevector, 'ZandP_nl' )
1756
1757    if ( dealloc .or. firstCall ) then
1758      nullify(varNames)
1759      call gsv_varNamesList(varNames, statevector)
1760      call gsv_allocate( statevector_VarsLevs, stateVector%numstep, &
1761                         stateVector%hco, stateVector%vco, mpi_local_opt=.true., &
1762                         mpi_distribution_opt='VarsLevs', dataKind_opt=4, &
1763                         allocHeightSfc_opt=.true., varNames_opt=varNames )
1764      deallocate(varNames)
1765    else
1766      if (mmpi_myid == 0 .and. .not. beSilent) write(*,*) 's2c_nl: avoid re-allocating statevector_VarsLevs'
1767      call gsv_zero(statevector_VarsLevs)
1768    end if
1769
1770    call gsv_transposeTilesToVarsLevs( stateVector, stateVector_VarsLevs, &
1771                                       beSilent_opt=beSilent )
1772
1773    numStep = stateVector_VarsLevs%numStep
1774
1775    if ( .not. interpInfo_nl%initialized ) then
1776      call utl_tmg_stop(34)
1777      call utl_tmg_start(31,'----s2c_Setups')
1778      ! also reject obs outside (LAM) domain and optionally move obs near 
1779      ! numerical pole to first/last analysis grid latitude
1780      call latlonChecksAnlGrid( obsSpaceData, hco_core, moveObsAtPole )
1781
1782      ! Do not reject obs for global domain
1783      rejectOutsideObs = .not. stateVector_VarsLevs%hco%global
1784      write(*,*) 's2c_nl: rejectOutsideObs = ', rejectOutsideObs
1785      call utl_tmg_stop(31)
1786      call utl_tmg_start(34,'----s2c_NL')
1787
1788    end if
1789
1790    ! set contents of column to zero (1 variable or all)
1791    allCols_ptr => col_getAllColumns(column,varName_opt)
1792    if ( obs_numHeader(obsSpaceData) > 0 ) allCols_ptr(:,:) = 0.0d0
1793
1794    OBSBATCH: do obsBatchIndex = 1, numObsBatches
1795      headerIndexBeg = 1 + (obsBatchIndex - 1) * (obs_numheader(obsSpaceData) / numObsBatches)
1796      if (obsBatchIndex == numObsBatches) then
1797        headerIndexEnd = obs_numheader(obsSpaceData)
1798      else
1799        headerIndexEnd = headerIndexBeg + (obs_numheader(obsSpaceData) / numObsBatches) - 1
1800      end if
1801      numHeader = headerIndexEnd - headerIndexBeg + 1
1802      call rpn_comm_allreduce(numHeader, numHeaderMax, 1,  &
1803                              'MPI_INTEGER', 'MPI_MAX', 'GRID', ierr)
1804      if ( .not. beSilent ) then
1805        write(*,*) 's2c_nl: headerIndexBeg/End, numHeader, numHeaderMax = ',  &
1806                   headerIndexBeg, headerIndexEnd, numHeader, numHeaderMax
1807        if (mmpi_myid == 0) then
1808          write(*,*) 's2c_nl: min/max of allNumHeader = ',  &
1809                     minval(allNumHeader), maxval(allNumHeader)
1810        end if
1811      end if
1812
1813      call rpn_comm_allgather(numHeader,   1,'mpi_integer', &
1814                              allNumHeader,1,'mpi_integer','grid',ierr)
1815      call rpn_comm_allgather(headerIndexBeg,   1,'mpi_integer', &
1816                              allHeaderIndexBeg,1,'mpi_integer','grid',ierr)
1817
1818      if ( .not. interpInfo_nl%initialized ) then
1819        call utl_tmg_stop(34)
1820        call utl_tmg_start(31,'----s2c_Setups')
1821
1822        ! compute and collect all obs grids onto all mpi tasks
1823        call s2c_setupInterpInfo( interpInfo_nl, obsSpaceData, stateVector_VarsLevs,  &
1824                                  headerIndexBeg, headerIndexEnd, &
1825                                  timeInterpType, rejectOutsideObs, &
1826                                  inputStateVectorType='nl', &
1827                                  lastCall_opt=(obsBatchIndex==numObsBatches))
1828        if ( mmpi_myid == 0 .and. verbose ) then
1829          do stepIndex = 1, numStep
1830            write(*,*) 's2c_nl: stepIndex, allNumHeaderUsed = ',  &
1831                       stepIndex, interpInfo_nl%allNumHeaderUsed(stepIndex,:)
1832          end do
1833        end if
1834        call utl_tmg_stop(31)
1835        call utl_tmg_start(34,'----s2c_NL')
1836      end if
1837
1838      ! arrays for interpolated column for 1 level/variable and each time step
1839      allocate(cols_hint(maxval(interpInfo_nl%allNumHeaderUsed),numStep,mmpi_nprocs))
1840      cols_hint(:,:,:) = 0.0d0
1841
1842      ! arrays for sending/receiving time interpolated column for 1 level/variable
1843      allocate(cols_send(numHeaderMax,mmpi_nprocs))
1844      cols_send(:,:) = 0.0d0
1845
1846      allocate(cols_recv(numHeaderMax,mmpi_nprocs))
1847      cols_recv(:,:) = 0.0d0
1848
1849      allocate(cols_send_1proc(numHeaderMax))
1850
1851      call gsv_getField(stateVector_VarsLevs,ptr4d_r4)
1852
1853      mykEndExtended = stateVector_VarsLevs%mykBeg + maxval(stateVector_VarsLevs%allkCount(:)) - 1
1854
1855      kCount = 0
1856      k_loop: do kIndex = stateVector_VarsLevs%mykBeg, mykEndExtended
1857        kCount = kCount + 1
1858
1859        if ( kIndex <= stateVector_VarsLevs%mykEnd ) then
1860          varName = gsv_getVarNameFromK(stateVector_VarsLevs,kIndex)
1861
1862          call utl_tmg_start(35,'------s2c_NL_Hinterp')
1863          if ( varName == 'UU' .or. varName == 'VV' ) then
1864            call gsv_getFieldUV(stateVector_VarsLevs,ptr3d_UV_r4,kIndex)
1865          end if
1866
1867          step_loop: do stepIndex = 1, numStep
1868            if ( maxval(interpInfo_nl%allNumHeaderUsed(stepIndex,:)) == 0 ) cycle step_loop
1869
1870            ! interpolate to the columns destined for all procs for all steps and one lev/var
1871            !$OMP PARALLEL DO PRIVATE (procIndex, yourNumHeader)
1872            do procIndex = 1, mmpi_nprocs
1873              yourNumHeader = interpInfo_nl%allNumHeaderUsed(stepIndex,procIndex)
1874              if ( yourNumHeader > 0 ) then
1875                if ( varName == 'UU' ) then
1876                  call myezuvint_nl( cols_hint(1:yourNumHeader,stepIndex,procIndex), 'UU',  &
1877                                     ptr4d_r4(:,:,kIndex,stepIndex), ptr3d_UV_r4(:,:,stepIndex), &
1878                                     interpInfo_nl, kindex, stepIndex, procIndex )
1879                else if ( varName == 'VV' ) then
1880                  call myezuvint_nl( cols_hint(1:yourNumHeader,stepIndex,procIndex), 'VV',  &
1881                                     ptr3d_UV_r4(:,:,stepIndex), ptr4d_r4(:,:,kIndex,stepIndex), &
1882                                     interpInfo_nl, kindex, stepIndex, procIndex )
1883                else
1884                  call myezsint_nl( cols_hint(1:yourNumHeader,stepIndex,procIndex), &
1885                                    ptr4d_r4(:,:,kIndex,stepIndex),  &
1886                                    interpInfo_nl, kindex, stepIndex, procIndex )
1887                end if
1888              end if
1889            end do
1890            !$OMP END PARALLEL DO
1891
1892          end do step_loop
1893          call utl_tmg_stop(35)
1894
1895          ! interpolate in time to the columns destined for all procs and one level/variable
1896          do procIndex = 1, mmpi_nprocs
1897            cols_send_1proc(:) = 0.0d0
1898            do stepIndex = 1, numStep
1899              !$OMP PARALLEL DO PRIVATE (headerIndex, headerIndex2, headerUsedIndex, weight)
1900              do headerUsedIndex = 1, interpInfo_nl%allNumHeaderUsed(stepIndex, procIndex)
1901                headerIndex = interpInfo_nl%stepProcData(procIndex,stepIndex)%allHeaderIndex(headerUsedIndex)
1902                headerIndex2 = headerIndex - allHeaderIndexBeg(procIndex) + 1
1903                weight = oti_getTimeInterpWeightMpiGlobal(interpInfo_nl%oti,  &
1904                                                          headerIndex2,stepIndex,procIndex)
1905                cols_send_1proc(headerIndex2) = cols_send_1proc(headerIndex2) + &
1906                                                weight * cols_hint(headerUsedIndex,stepIndex,procIndex)
1907              end do
1908              !$OMP END PARALLEL DO
1909            end do
1910            cols_send(:,procIndex) = cols_send_1proc(:)
1911          end do
1912
1913        else
1914
1915          ! this value of k does not exist on this mpi task
1916          cols_send(:,:) = 0.0d0
1917
1918        end if ! if kIndex <= mykEnd
1919
1920        call utl_tmg_start(37,'------s2c_NL_barrier')
1921        call rpn_comm_barrier('GRID',ierr)
1922        call utl_tmg_stop(37)
1923
1924        ! mpi communication: alltoallv for one level/variable
1925        call utl_tmg_start(36,'------s2c_NL_allToAll')
1926
1927        ! only receive the data from tasks with data, same amount from all of those
1928        recvsizes(:) = 0
1929        do procIndex = 1, mmpi_nprocs
1930          kIndex2 = stateVector_VarsLevs%allkBeg(procIndex) + kCount - 1
1931          if (kIndex2 > stateVector_VarsLevs%allkEnd(procIndex)) cycle
1932          recvsizes(procIndex) = numHeader
1933        end do
1934        recvdispls(1) = 0
1935        do procIndex = 2, mmpi_nprocs
1936          recvdispls(procIndex) = recvdispls(procIndex-1) + numHeaderMax
1937        end do
1938
1939        ! tasks send only from those with data
1940        if (kIndex <= stateVector_VarsLevs%mykEnd) then
1941          do procIndex = 1, mmpi_nprocs
1942            sendsizes(procIndex) = allNumHeader(procIndex)
1943          end do
1944        else
1945          sendsizes(:) = 0
1946        end if
1947        senddispls(1) = 0
1948        do procIndex = 2, mmpi_nprocs
1949          senddispls(procIndex) = senddispls(procIndex-1) + numHeaderMax
1950        end do
1951
1952        if(mmpi_nprocs > 1) then
1953          call mpi_alltoallv(cols_send, sendsizes, senddispls, mmpi_datyp_real8,  &
1954                             cols_recv, recvsizes, recvdispls, mmpi_datyp_real8,  &
1955                             mmpi_comm_grid, ierr)
1956        else
1957          cols_recv(:,1) = cols_send(:,1)
1958        end if
1959        call utl_tmg_stop(36)
1960	
1961        ! reorganize ensemble of distributed columns
1962        !$OMP PARALLEL DO PRIVATE (procIndex, kIndex2, headerIndex, headerIndex2)
1963        proc_loop: do procIndex = 1, mmpi_nprocs
1964          kIndex2 = stateVector_VarsLevs%allkBeg(procIndex) + kCount - 1
1965          if ( kIndex2 > stateVector_VarsLevs%allkEnd(procIndex) ) cycle proc_loop
1966          do headerIndex = 1, numHeader
1967            headerIndex2 = headerIndex + headerIndexBeg - 1
1968            allCols_ptr(kIndex2,headerIndex2) = cols_recv(headerIndex,procIndex)
1969          end do
1970        end do proc_loop
1971        !$OMP END PARALLEL DO
1972
1973      end do k_loop
1974
1975      ! impose a lower limit on HU
1976      if(col_varExist(column,'HU')) then
1977        do headerIndex = headerIndexBeg, headerIndexEnd
1978          column_ptr => col_getColumn(column,headerIndex,'HU')
1979          column_ptr(:) = max(column_ptr(:),col_rhumin)
1980        end do
1981      end if
1982
1983      ! impose a lower/upper limits on ALL cloud variables
1984      do varNameIndex = 1, vnl_numvarmaxCloud
1985        if(col_varExist(column, vnl_varNameListCloud(varNameIndex))) then
1986          do headerIndex = headerIndexBeg, headerIndexEnd
1987            column_ptr => col_getColumn(column,headerIndex, vnl_varNameListCloud(varNameIndex))
1988            column_ptr(:) = max(column_ptr(:), qlim_getMinValueCloud(vnl_varNameListCloud(varNameIndex)))
1989            column_ptr(:) = min(column_ptr(:), qlim_getMaxValueCloud(vnl_varNameListCloud(varNameIndex)))
1990          end do
1991        end if
1992      end do
1993
1994      ! Interpolate surface height separately, only exists on mpi task 0
1995      HeightSfcPresent: if ( stateVector_VarsLevs%HeightSfcPresent ) then
1996
1997        if ( mmpi_myid == 0 ) then
1998          varName = 'GZ'
1999          kIndexHeightSfc = 0    
2000          step_loop_height: do stepIndex = 1, numStep
2001
2002            if ( maxval(interpInfo_nl%allNumHeaderUsed(stepIndex,:)) == 0 ) cycle step_loop_height
2003
2004            ! interpolate to the columns destined for all procs for all steps and one lev/var
2005            !$OMP PARALLEL DO PRIVATE (procIndex, yourNumHeader, ptr2d_r8)
2006            do procIndex = 1, mmpi_nprocs
2007              yourNumHeader = interpInfo_nl%allNumHeaderUsed(stepIndex,procIndex)
2008              if ( yourNumHeader > 0 ) then
2009                ptr2d_r8 => gsv_getHeightSfc(stateVector_VarsLevs)
2010                call myezsint_r8_nl( cols_hint(1:yourNumHeader,stepIndex,procIndex), &
2011                                     ptr2d_r8(:,:), interpInfo_nl, kIndexHeightSfc, stepIndex, procIndex )
2012              end if
2013            end do
2014            !$OMP END PARALLEL DO
2015
2016          end do step_loop_height
2017
2018          ! interpolate in time to the columns destined for all procs and one level/variable
2019          do procIndex = 1, mmpi_nprocs
2020            cols_send(:,procIndex) = 0.0d0
2021            do stepIndex = 1, numStep
2022              !$OMP PARALLEL DO PRIVATE (headerIndex, headerIndex2, headerUsedIndex)
2023              do headerUsedIndex = 1, interpInfo_nl%allNumHeaderUsed(stepIndex, procIndex)
2024                headerIndex = interpInfo_nl%stepProcData(procIndex, stepIndex)%allHeaderIndex(headerUsedIndex)
2025                ! just copy, since surface height same for all time steps
2026                headerIndex2 = headerIndex - allHeaderIndexBeg(procIndex) + 1
2027                cols_send(headerIndex2,procIndex) = cols_hint(headerUsedIndex,stepIndex,procIndex)
2028              end do
2029              !$OMP END PARALLEL DO
2030            end do
2031          end do
2032
2033        end if
2034
2035        ! mpi communication: scatter data from task 0
2036        nsize = numHeader
2037        if(mmpi_nprocs > 1) then
2038          do procIndex = 1, mmpi_nprocs
2039            displs(procIndex) = (procIndex - 1) * numHeaderMax
2040            nsizes(procIndex) = allNumHeader(procIndex)
2041          end do
2042          call rpn_comm_scatterv(cols_send, nsizes, displs, 'MPI_REAL8', &
2043                                 cols_recv, nsize, 'MPI_REAL8', &
2044                                 0, 'GRID', ierr)
2045        else
2046          cols_recv(:,1) = cols_send(:,1)
2047        end if
2048
2049        do headerIndex = headerIndexBeg, headerIndexEnd
2050          headerIndex2 = headerIndex - headerIndexBeg + 1
2051          call col_setHeightSfc(column, headerIndex, cols_recv(headerIndex2,1))
2052        end do
2053
2054      end if HeightSfcPresent
2055
2056      deallocate(cols_hint)
2057      deallocate(cols_send)
2058      deallocate(cols_recv)
2059      deallocate(cols_send_1proc)
2060
2061      if ( dealloc ) call s2c_deallocInterpInfo( inputStateVectorType='nl' )
2062
2063    end do OBSBATCH
2064
2065    if ( dealloc) call gsv_deallocate( statevector_VarsLevs )
2066
2067    if (slantPath_TO_nl) call pressureProfileMonotonicityCheck(obsSpaceData, column)
2068
2069    firstCall = .false.
2070
2071    if ( .not. beSilent ) then
2072      write(*,*) 's2c_nl: FINISHED'
2073      write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
2074    end if
2075
2076    call utl_tmg_stop(34)
2077
2078    call utl_tmg_stop(30)
2079
2080  end subroutine s2c_nl
2081
2082  ! -------------------------------------------------
2083  ! myezsint_nl: Scalar field horizontal interpolation
2084  ! -------------------------------------------------
2085  subroutine myezsint_nl( column_out, field_in, interpInfo, kIndex, stepIndex, procIndex )
2086    !
2087    !:Purpose: Scalar horizontal interpolation, replaces the
2088    !           ezsint routine from rmnlib.
2089    !
2090    implicit none
2091
2092    ! Arguments:
2093    real(8)                , intent(out) :: column_out(:)
2094    real(4)                , intent(in)  :: field_in(:,:)
2095    type(struct_interpInfo), intent(in)  :: interpInfo
2096    integer                , intent(in)  :: stepIndex
2097    integer                , intent(in)  :: procIndex
2098    integer                , intent(in)  :: kIndex
2099
2100    ! Locals:
2101    integer :: lonIndex, latIndex, gridptIndex, headerIndex, subGridIndex, numColumn
2102    real(8) :: interpValue, weight
2103
2104    numColumn = size( column_out )
2105
2106    do headerIndex = 1, numColumn
2107
2108      ! Interpolate the model state to the obs point
2109      interpValue = 0.0d0
2110
2111      do subGridIndex = 1, interpInfo%hco%numSubGrid
2112
2113        do gridptIndex =  &
2114             interpInfo%stepProcData(procIndex,stepIndex)%depotIndexBeg(subGridIndex, headerIndex, kIndex), &
2115             interpInfo%stepProcData(procIndex,stepIndex)%depotIndexEnd(subGridIndex, headerIndex, kIndex)
2116
2117          lonIndex = interpInfo%lonIndexDepot(gridptIndex)
2118          latIndex = interpInfo%latIndexDepot(gridptIndex)
2119          weight = interpInfo%interpWeightDepot(gridptIndex)
2120
2121          interpValue = interpValue + weight * real(field_in(lonIndex, latIndex),8)
2122
2123        end do
2124
2125      end do
2126      column_out(headerIndex) = interpValue
2127
2128    end do
2129
2130  end subroutine myezsint_nl
2131
2132  ! -------------------------------------------------
2133  ! myezsint_r8_nl: Scalar field horizontal interpolation
2134  ! -------------------------------------------------
2135  subroutine myezsint_r8_nl( column_out, field_in, interpInfo, kIndex, stepIndex, procIndex )
2136    !
2137    !:Purpose: Scalar horizontal interpolation, replaces the
2138    !           ezsint routine from rmnlib.
2139    !
2140    implicit none
2141
2142    ! Arguments:
2143    real(8)                , intent(out) :: column_out(:)
2144    real(8)                , intent(in)  :: field_in(:,:)
2145    type(struct_interpInfo), intent(in)  :: interpInfo
2146    integer                , intent(in)  :: stepIndex
2147    integer                , intent(in)  :: procIndex
2148    integer                , intent(in)  :: kIndex
2149
2150    ! Locals:
2151    integer :: lonIndex, latIndex, gridptIndex, headerIndex, subGridIndex, numColumn
2152    real(8) :: interpValue, weight
2153
2154    numColumn = size( column_out )
2155
2156    do headerIndex = 1, numColumn
2157
2158      ! Interpolate the model state to the obs point
2159      interpValue = 0.0d0
2160
2161      do subGridIndex = 1, interpInfo%hco%numSubGrid
2162
2163        do gridptIndex =  &
2164             interpInfo%stepProcData(procIndex,stepIndex)%depotIndexBeg(subGridIndex, headerIndex, kIndex), &
2165             interpInfo%stepProcData(procIndex,stepIndex)%depotIndexEnd(subGridIndex, headerIndex, kIndex)
2166
2167          lonIndex = interpInfo%lonIndexDepot(gridptIndex)
2168          latIndex = interpInfo%latIndexDepot(gridptIndex)
2169          weight = interpInfo%interpWeightDepot(gridptIndex)
2170
2171          interpValue = interpValue + weight * field_in(lonIndex, latIndex)
2172
2173        end do
2174
2175      end do
2176      column_out(headerIndex) = interpValue
2177
2178    end do
2179
2180  end subroutine myezsint_r8_nl
2181
2182  ! -------------------------------------------------
2183  ! myezsint_tl: Scalar field horizontal interpolation
2184  ! -------------------------------------------------
2185  subroutine myezsint_tl( column_out, field_in, interpInfo, kIndex, stepIndex, procIndex )
2186    !
2187    !:Purpose: Scalar horizontal interpolation, replaces the
2188    !           ezsint routine from rmnlib.
2189    !
2190    implicit none
2191
2192    ! Arguments:
2193    real(8)                , intent(out) :: column_out(:)
2194    real(pre_incrReal)     , intent(in)  :: field_in(:,:)
2195    type(struct_interpInfo), intent(in)  :: interpInfo
2196    integer                , intent(in)  :: stepIndex
2197    integer                , intent(in)  :: procIndex
2198    integer                , intent(in)  :: kIndex
2199
2200    ! Locals:
2201    integer :: lonIndex, latIndex, gridptIndex, headerIndex, subGridIndex, numColumn
2202    real(8) :: interpValue, weight
2203
2204    numColumn = size( column_out )
2205
2206    do headerIndex = 1, numColumn
2207
2208      ! Interpolate the model state to the obs point
2209      interpValue = 0.0d0
2210
2211      do subGridIndex = 1, interpInfo%hco%numSubGrid
2212
2213        do gridptIndex =  &
2214             interpInfo%stepProcData(procIndex,stepIndex)%depotIndexBeg(subGridIndex, headerIndex, kIndex), &
2215             interpInfo%stepProcData(procIndex,stepIndex)%depotIndexEnd(subGridIndex, headerIndex, kIndex)
2216
2217          lonIndex = interpInfo%lonIndexDepot(gridptIndex)
2218          latIndex = interpInfo%latIndexDepot(gridptIndex)
2219          weight = interpInfo%interpWeightDepot(gridptIndex)
2220
2221          interpValue = interpValue + weight * field_in(lonIndex, latIndex)
2222
2223        end do
2224
2225      end do
2226      column_out(headerIndex) = interpValue
2227
2228    end do
2229
2230  end subroutine myezsint_tl
2231
2232  ! -------------------------------------------------------------
2233  ! myezsint_ad: Adjoint of scalar field horizontal interpolation
2234  ! -------------------------------------------------------------
2235  subroutine myezsint_ad( column_in, field_out, interpInfo, kIndex, stepIndex, procIndex )
2236    !
2237    !:Purpose: Adjoint of the scalar horizontal interpolation.
2238    !
2239    implicit none
2240
2241    ! Arguments:
2242    real(8)                , intent(in)    :: column_in(:)
2243    real(pre_incrReal)     , intent(inout) :: field_out(:,:)
2244    type(struct_interpInfo), intent(in)    :: interpInfo
2245    integer                , intent(in)    :: stepIndex
2246    integer                , intent(in)    :: procIndex
2247    integer                , intent(in)    :: kIndex
2248
2249    ! Locals:
2250    integer :: lonIndex, latIndex, gridptIndex, headerIndex, subGridIndex, numColumn
2251    real(8) :: weight
2252
2253    numColumn = size( column_in )
2254
2255    do headerIndex = 1, numColumn
2256
2257      ! Interpolate the model state to the obs point
2258
2259      do subGridIndex = 1, interpInfo%hco%numSubGrid
2260
2261        do gridptIndex =  &
2262             interpInfo%stepProcData(procIndex,stepIndex)%depotIndexBeg(subGridIndex, headerIndex, kIndex), &
2263             interpInfo%stepProcData(procIndex,stepIndex)%depotIndexEnd(subGridIndex, headerIndex, kIndex)
2264
2265          lonIndex = interpInfo%lonIndexDepot(gridptIndex)
2266          latIndex = interpInfo%latIndexDepot(gridptIndex)
2267          weight = interpInfo%interpWeightDepot(gridptIndex)
2268
2269          field_out(lonIndex, latIndex) = field_out(lonIndex, latIndex) +  &
2270                                          weight * column_in(headerIndex)
2271
2272        end do
2273
2274      end do
2275
2276    end do
2277
2278  end subroutine myezsint_ad
2279
2280  ! -------------------------------------------------------------
2281  ! myezuvint_nl: Vector field horizontal interpolation
2282  ! -------------------------------------------------------------
2283  subroutine myezuvint_nl( column_out, varName, fieldUU_in, fieldVV_in,  &
2284                           interpInfo, kIndex, stepIndex, procIndex )
2285    !
2286    !:Purpose: Vector horizontal interpolation, replaces the
2287    !           ezuvint routine from rmnlib.
2288    !
2289    implicit none
2290
2291    ! Arguments:
2292    real(8)                , intent(out) :: column_out(:)
2293    character(len=*)       , intent(in)  :: varName
2294    real(4)                , intent(in)  :: fieldUU_in(:,:)
2295    real(4)                , intent(in)  :: fieldVV_in(:,:)
2296    type(struct_interpInfo), intent(in)  :: interpInfo
2297    integer                , intent(in)  :: stepIndex
2298    integer                , intent(in)  :: procIndex
2299    integer                , intent(in)  :: kIndex
2300
2301    ! Locals:
2302    integer :: lonIndex, latIndex, indexBeg, indexEnd, gridptIndex, headerIndex
2303    integer :: numColumn, subGridIndex
2304    real(8) :: interpUU(interpInfo%hco%numSubGrid), interpVV(interpInfo%hco%numSubGrid)
2305    real(8) :: lat, lon, latRot, lonRot, weight
2306    logical :: doUU, doVV
2307
2308    numColumn = size( column_out )
2309
2310    doUU = (trim(varName) == 'UU' .or. interpInfo%hco%rotated)
2311    doVV = (trim(varName) == 'VV' .or. interpInfo%hco%rotated)
2312
2313    header_loop: do headerIndex = 1, numColumn
2314
2315      interpUU(:) = 0.0d0
2316      interpVV(:) = 0.0d0
2317
2318      subGrid_loop: do subGridIndex = 1, interpInfo%hco%numSubGrid
2319
2320        indexBeg = interpInfo%stepProcData(procIndex,stepIndex)%depotIndexBeg(subGridIndex, headerIndex, kIndex)
2321        indexEnd = interpInfo%stepProcData(procIndex,stepIndex)%depotIndexEnd(subGridIndex, headerIndex, kIndex)
2322
2323        if ( indexEnd < IndexBeg ) cycle subGrid_loop
2324
2325        ! Interpolate the model UU to the obs point
2326        do gridptIndex = indexBeg, indexEnd
2327
2328          lonIndex = interpInfo%lonIndexDepot(gridptIndex)
2329          latIndex = interpInfo%latIndexDepot(gridptIndex)
2330          weight = interpInfo%interpWeightDepot(gridptIndex)
2331
2332          if ( doUU ) interpUU(subGridIndex) = interpUU(subGridIndex) +  &
2333                      weight * real(fieldUU_in(lonIndex, latIndex),8)
2334          if ( doVV ) interpVV(subGridIndex) = interpVV(subGridIndex) +  &
2335                      weight * real(fieldVV_in(lonIndex, latIndex),8)
2336
2337        end do
2338        ! now rotate the wind vector
2339        if ( interpInfo%hco%rotated ) then
2340          lat = interpInfo%stepProcData(procIndex, stepIndex)%allLat(headerIndex, kIndex)
2341          lon = interpInfo%stepProcData(procIndex, stepIndex)%allLon(headerIndex, kIndex)
2342          latRot = interpInfo%stepProcData(procIndex,stepIndex)%allLatRot(subGridIndex, headerIndex, kIndex)
2343          lonRot = interpInfo%stepProcData(procIndex,stepIndex)%allLonRot(subGridIndex, headerIndex, kIndex)
2344
2345          call uvr_rotateWind_nl( interpInfo%uvr,            & ! IN
2346                                  subGridIndex,              & ! IN
2347                                  interpUU(subGridIndex),    & ! INOUT
2348                                  interpVV(subGridIndex),    & ! INOUT
2349                                  lat, lon, latRot, lonRot,  & ! IN
2350                                  'ToMetWind' )                ! IN
2351        end if
2352
2353      end do subGrid_loop
2354
2355      ! return only the desired component
2356      if ( trim(varName) == 'UU' ) then
2357        column_out(headerIndex) = sum(interpUU(:))
2358      else
2359        column_out(headerIndex) = sum(interpVV(:))
2360      end if
2361
2362    end do header_loop
2363
2364  end subroutine myezuvint_nl
2365
2366  ! -------------------------------------------------------------
2367  ! myezuvint_tl: Vector field horizontal interpolation
2368  ! -------------------------------------------------------------
2369  subroutine myezuvint_tl( column_out, varName, fieldUU_in, fieldVV_in,  &
2370                           interpInfo, kIndex, stepIndex, procIndex )
2371    !:Purpose: Vector horizontal interpolation, replaces the
2372    !           ezuvint routine from rmnlib.
2373    !
2374    implicit none
2375
2376    ! Arguments:
2377    real(8)                , intent(out) :: column_out(:)
2378    character(len=*)       , intent(in)  :: varName
2379    real(pre_incrReal)     , intent(in)  :: fieldUU_in(:,:)
2380    real(pre_incrReal)     , intent(in)  :: fieldVV_in(:,:)
2381    type(struct_interpInfo), intent(in)  :: interpInfo
2382    integer                , intent(in)  :: stepIndex
2383    integer                , intent(in)  :: procIndex
2384    integer                , intent(in)  :: kIndex
2385
2386    ! Locals:
2387    integer :: lonIndex, latIndex, indexBeg, indexEnd, gridptIndex, headerIndex
2388    integer :: numColumn, subGridIndex
2389    real(8) :: interpUU(interpInfo%hco%numSubGrid), interpVV(interpInfo%hco%numSubGrid)
2390    real(8) :: lat, lon, latRot, lonRot, weight
2391    logical :: doUU, doVV
2392
2393    numColumn = size( column_out )
2394
2395    doUU = (trim(varName) == 'UU' .or. interpInfo%hco%rotated)
2396    doVV = (trim(varName) == 'VV' .or. interpInfo%hco%rotated)
2397
2398    header_loop: do headerIndex = 1, numColumn
2399
2400      interpUU(:) = 0.0d0
2401      interpVV(:) = 0.0d0
2402
2403      subGrid_loop: do subGridIndex = 1, interpInfo%hco%numSubGrid
2404
2405        indexBeg = interpInfo%stepProcData(procIndex,stepIndex)%depotIndexBeg(subGridIndex, headerIndex, kIndex)
2406        indexEnd = interpInfo%stepProcData(procIndex,stepIndex)%depotIndexEnd(subGridIndex, headerIndex, kIndex)
2407
2408        if ( indexEnd < IndexBeg ) cycle subGrid_loop
2409
2410        ! Interpolate the model UU to the obs point
2411        do gridptIndex = indexBeg, indexEnd
2412
2413          lonIndex = interpInfo%lonIndexDepot(gridptIndex)
2414          latIndex = interpInfo%latIndexDepot(gridptIndex)
2415          weight = interpInfo%interpWeightDepot(gridptIndex)
2416
2417          if ( doUU ) interpUU(subGridIndex) = interpUU(subGridIndex) +  &
2418                      weight * fieldUU_in(lonIndex, latIndex)
2419          if ( doVV ) interpVV(subGridIndex) = interpVV(subGridIndex) +  &
2420                      weight * fieldVV_in(lonIndex, latIndex)
2421
2422        end do
2423        ! now rotate the wind vector
2424        if ( interpInfo%hco%rotated ) then
2425          lat = interpInfo%stepProcData(procIndex, stepIndex)%allLat(headerIndex, kIndex)
2426          lon = interpInfo%stepProcData(procIndex, stepIndex)%allLon(headerIndex, kIndex)
2427          latRot = interpInfo%stepProcData(procIndex,stepIndex)%allLatRot(subGridIndex, headerIndex, kIndex)
2428          lonRot = interpInfo%stepProcData(procIndex,stepIndex)%allLonRot(subGridIndex, headerIndex, kIndex)
2429
2430          call uvr_rotateWind_tl( interpInfo%uvr,            & ! IN
2431                                  subGridIndex,              & ! IN
2432                                  interpUU(subGridIndex),    & ! INOUT
2433                                  interpVV(subGridIndex),    & ! INOUT
2434                                  lat, lon, latRot, lonRot,  & ! IN
2435                                  'ToMetWind' )                ! IN
2436        end if
2437
2438      end do subGrid_loop
2439
2440      ! return only the desired component
2441      if ( trim(varName) == 'UU' ) then
2442        column_out(headerIndex) = sum(interpUU(:))
2443      else
2444        column_out(headerIndex) = sum(interpVV(:))
2445      end if
2446
2447    end do header_loop
2448
2449  end subroutine myezuvint_tl
2450
2451  ! -------------------------------------------------------------
2452  ! myezuvint_ad: Adjoint of vector field horizontal interpolation
2453  ! -------------------------------------------------------------
2454  subroutine myezuvint_ad( column_in, varName, fieldUU_out, fieldVV_out, &
2455                           interpInfo, kIndex, stepIndex, procIndex )
2456    !
2457    !:Purpose: Adjoint of the vector horizontal interpolation.
2458    !
2459    implicit none
2460
2461    ! Arguments:
2462    real(8)                , intent(in)    :: column_in(:)
2463    character(len=*)       , intent(in)    :: varName
2464    real(pre_incrReal)     , intent(inout) :: fieldUU_out(:,:)
2465    real(pre_incrReal)     , intent(inout) :: fieldVV_out(:,:)
2466    type(struct_interpInfo), intent(in)    :: interpInfo
2467    integer                , intent(in)    :: stepIndex
2468    integer                , intent(in)    :: procIndex
2469    integer                , intent(in)    :: kIndex
2470
2471    ! Locals:
2472    integer :: lonIndex, latIndex, indexBeg, indexEnd, gridptIndex, headerIndex
2473    integer :: numColumn, subGridIndex
2474    real(8) :: interpUU(interpInfo%hco%numSubGrid), interpVV(interpInfo%hco%numSubGrid)
2475    real(8) :: lat, lon, latRot, lonRot, weight
2476    logical :: doUU, doVV
2477
2478    numColumn = size( column_in )
2479
2480    doUU = (trim(varName) == 'UU' .or. interpInfo%hco%rotated)
2481    doVV = (trim(varName) == 'VV' .or. interpInfo%hco%rotated)
2482
2483    header_loop: do headerIndex = 1, numColumn
2484
2485      if ( trim(varName) == 'UU' ) then
2486        interpUU(:) = column_in(headerIndex)
2487        interpVV(:) = 0.0d0
2488      else
2489        interpUU(:) = 0.0d0
2490        interpVV(:) = column_in(headerIndex)
2491      end if
2492
2493      subGrid_loop: do subGridIndex = 1, interpInfo%hco%numSubGrid
2494
2495        indexBeg = interpInfo%stepProcData(procIndex,stepIndex)%depotIndexBeg(subGridIndex, headerIndex, kIndex)
2496        indexEnd = interpInfo%stepProcData(procIndex,stepIndex)%depotIndexEnd(subGridIndex, headerIndex, kIndex)
2497
2498        if ( indexEnd < IndexBeg ) cycle subGrid_loop
2499
2500        ! now rotate the wind vector and return the desired component
2501        if ( interpInfo%hco%rotated ) then
2502          lat = interpInfo%stepProcData(procIndex, stepIndex)%allLat(headerIndex, kIndex)
2503          lon = interpInfo%stepProcData(procIndex, stepIndex)%allLon(headerIndex, kIndex)
2504          latRot = interpInfo%stepProcData(procIndex,stepIndex)%allLatRot(subGridIndex, headerIndex, kIndex)
2505          lonRot = interpInfo%stepProcData(procIndex,stepIndex)%allLonRot(subGridIndex, headerIndex, kIndex)
2506
2507          call uvr_rotateWind_ad( interpInfo%uvr,           & ! IN 
2508                                  subGridIndex,             & ! IN
2509                                  interpUU(subGridIndex),   & ! INOUT
2510                                  interpVV(subGridIndex),   & ! INOUT
2511                                  lat, lon, latRot, lonRot, & ! IN
2512                                  'ToMetWind' )               ! IN
2513        end if
2514
2515        ! Interpolate the model VV to the obs point
2516        do gridptIndex = indexBeg, indexEnd
2517
2518          lonIndex = interpInfo%lonIndexDepot(gridptIndex)
2519          latIndex = interpInfo%latIndexDepot(gridptIndex)
2520          weight = interpInfo%interpWeightDepot(gridptIndex)
2521
2522          if ( doUU ) fieldUU_out(lonIndex, latIndex) =  &
2523                      fieldUU_out(lonIndex, latIndex) + weight * interpUU(subGridIndex)
2524          if ( doVV ) fieldVV_out(lonIndex, latIndex) =  &
2525                      fieldVV_out(lonIndex, latIndex) + weight * interpVV(subGridIndex)
2526
2527        end do
2528
2529      end do subGrid_loop
2530
2531    end do header_loop
2532
2533  end subroutine myezuvint_ad
2534
2535  !---------------------------------------------------------
2536  ! s2c_bgcheck_bilin
2537  !---------------------------------------------------------
2538  subroutine s2c_bgcheck_bilin(column,statevector,obsSpaceData)
2539    !
2540    !:Purpose: Special version of s2c_tl used for background check. This should
2541    !           be replaced by direct call to s2c_tl. It is not general enough to
2542    !           be used for new analysis variables.
2543    !
2544    implicit none
2545
2546    ! Arguments:
2547    type(struct_columnData), intent(inout) :: column
2548    type(struct_gsv),        intent(in)    :: statevector
2549    type(struct_obs),        intent(in)    :: obsSpaceData
2550
2551    ! Locals:
2552    integer :: jk, jk2, jgl, headerIndex
2553    integer :: lonIndex, ila, ierr, subGridIndex
2554    integer :: extraLongitude
2555    real(8) :: lat, lon
2556    real(4) :: lat_r4, lon_r4, lat_deg_r4, lon_deg_r4, xpos_r4, ypos_r4, xpos2_r4, ypos2_r4
2557    real(8) :: dldy, dlw1, dlw2, dlw3, dlw4, dldx, ypos, xpos
2558    real(8), allocatable ::zgd(:,:,:)
2559    real(8), pointer :: field_ptr(:,:,:)
2560    real(8), pointer :: varColumn(:)
2561    integer :: varIndex
2562    character(len=4) :: varName
2563
2564    call utl_tmg_start(30,'--StateToColumn')
2565
2566    ! Note: We assume here the all the obs between the poles and the last grid points
2567    !       (i.e. outside the grid) have been moved within the grid by suprep
2568
2569    if (statevector%hco%global) then
2570      extraLongitude = 1
2571    else
2572      extraLongitude = 0
2573    end if
2574
2575    allocate(zgd(statevector%ni+extraLongitude,statevector%nj,statevector%nk))
2576  
2577    zgd(:,:,:)=0.0d0
2578    call gsv_getField(statevector,field_ptr)
2579    zgd(1:statevector%ni,1:statevector%nj,1:statevector%nk)= &
2580         field_ptr(1:statevector%ni,1:statevector%nj,1:statevector%nk)
2581
2582    !
2583    !- 1.  Expand field by repeating meridian 1 into into meridian ni+1
2584    !
2585    if (extraLongitude == 1) then
2586      do jk = 1, statevector%nk
2587        do jgl = 1, statevector%nj
2588          zgd(statevector%ni+1,jgl,jk) = zgd( 1,jgl,jk)
2589        end do
2590      end do
2591    end if
2592
2593    !
2594    !- 2.  Loop over all the headers
2595    !
2596    do headerIndex = 1, col_getNumCol(column)
2597
2598      !- 2.1 Find the obs positin within the analysis grid
2599      lat    = obs_headElem_r(obsSpaceData,OBS_LAT,headerIndex)
2600      lon    = obs_headElem_r(obsSpaceData,OBS_LON,headerIndex)
2601      lat_r4 = real(lat,4)
2602      lon_r4 = real(lon,4)
2603      if (lon_r4.lt.0.0         ) lon_r4 = lon_r4 + 2.0*MPC_PI_R4
2604      if (lon_r4.ge.2.*MPC_PI_R4) lon_r4 = lon_r4 - 2.0*MPC_PI_R4
2605      lat_deg_r4 = lat_r4 * MPC_DEGREES_PER_RADIAN_R4 ! Radian To Degree
2606      lon_deg_r4 = lon_r4 * MPC_DEGREES_PER_RADIAN_R4
2607      ierr = gpos_getPositionXY( stateVector % hco % EZscintID,   &
2608                                xpos_r4, ypos_r4, xpos2_r4, ypos2_r4, &
2609                                lat_deg_r4, lon_deg_r4, subGridIndex )
2610      xpos = real(xpos_r4,8)
2611      ypos = real(ypos_r4,8)
2612
2613      !- Make sure we are within bounds
2614      if ( ypos < 1.d0                        .or. &
2615           ypos > real(statevector%nj    , 8) .or. &
2616           xpos < 1.d0                        .or. &
2617           xpos > real(statevector%ni + extraLongitude, 8) ) then
2618        write(*,*) 's2c_bgcheck_bilin: Obs outside local domain for headerIndex = ', &
2619                   headerIndex
2620        write(*,*) '  obs    lat, lon position            = ', &
2621                   Lat*MPC_DEGREES_PER_RADIAN_R8, Lon*MPC_DEGREES_PER_RADIAN_R8
2622        write(*,*) '  obs    x, y     position            = ', &
2623                   xpos, ypos
2624        write(*,*) '  domain x_end, y_end bounds          = ', &
2625                   statevector%ni + extraLongitude, statevector%nj
2626        call utl_abort('s2c_bgcheck_bilin')
2627      end if
2628
2629      !- 2.2 Find the lower-left grid point next to the observation
2630      if ( xpos == real(statevector%ni + extraLongitude,8) ) then
2631        lonIndex = floor(xpos) - 1
2632      else
2633        lonIndex = floor(xpos)
2634      end if
2635
2636      if ( ypos == real(statevector%nj,8) ) then
2637        ILA = floor(ypos) - 1
2638      else
2639        ILA = floor(ypos)
2640      end if
2641
2642      !- 2.3 Compute the 4 weights of the bilinear interpolation
2643      dldx = xpos - real(lonIndex,8)
2644      dldy = ypos - real(ILA,8)
2645
2646      dlw1 = (1.d0-dldx) * (1.d0-dldy)
2647      dlw2 =       dldx  * (1.d0-dldy)
2648      dlw3 = (1.d0-dldx) *       dldy
2649      dlw4 =       dldx  *       dldy
2650
2651      !- 2.4 Interpolate the model state to the obs point
2652           
2653      do varIndex = 1, vnl_numvarmax
2654        if (.not. col_varExist(column,trim(vnl_varNameList(varIndex)))) cycle
2655        varName=trim(vnl_varNameList(varIndex))
2656        varColumn => col_getColumn(column,headerIndex,varName)
2657        
2658        if(gsv_varExist(statevector,varName)) then
2659          do jk = 1, gsv_getNumLevFromVarName(statevector,varName)      
2660              jk2=jk+gsv_getOffsetFromVarName(statevector,varName)
2661              varColumn(jk) =   dlw1*zgd(lonIndex  ,ila,jk2)  &
2662                                + dlw2*zgd(lonIndex+1,ila,jk2)  &
2663                                + dlw3*zgd(lonIndex  ,ila+1,jk2)  &
2664                                + dlw4*zgd(lonIndex+1,ila+1,jk2)
2665          end do
2666        end if
2667        
2668        nullify(varColumn)
2669      end do
2670      
2671    end do
2672
2673    deallocate(zgd)
2674
2675    call utl_tmg_stop(30)
2676
2677  end subroutine s2c_bgcheck_bilin
2678
2679  !--------------------------------------------------------------------------
2680  ! s2c_setupHorizInterp
2681  !--------------------------------------------------------------------------
2682  subroutine s2c_setupHorizInterp(footprintRadius_r4, interpInfo, &
2683                                  stateVector, headerIndex, kIndex, stepIndex, &
2684                                  procIndex, numGridpt)
2685    !
2686    !:Purpose: To identify the appropriate horizontal interpolation scheme based
2687    !          on footprint radius value. Then to call the corresponding
2688    !          subroutine to determine the grid points and their associated
2689    !          weights.
2690    !
2691    implicit none
2692
2693    ! Arguments:
2694    real(4)                , intent(in)    :: footprintRadius_r4 ! (metres)
2695    type(struct_interpInfo), intent(inout) :: interpInfo
2696    type(struct_gsv)       , intent(in)    :: stateVector
2697    integer                , intent(in)    :: headerIndex, kIndex, stepIndex
2698    integer                , intent(in)    :: procIndex
2699    integer                , intent(out)   :: numGridpt(interpInfo%hco%numSubGrid)
2700
2701    if ( footprintRadius_r4 > 0.0 ) then
2702
2703      call s2c_setupFootprintInterp(footprintRadius_r4, interpInfo, stateVector, headerIndex, kIndex, stepIndex, procIndex, numGridpt)
2704
2705    else if ( footprintRadius_r4 == bilinearFootprint ) then
2706
2707      call s2c_setupBilinearInterp(interpInfo, stateVector, headerIndex, kIndex, stepIndex, procIndex, numGridpt)
2708
2709    else if ( footprintRadius_r4 == lakeFootprint ) then
2710
2711      call s2c_setupLakeInterp(interpInfo, stateVector, headerIndex, kIndex, stepIndex, procIndex, numGridpt)
2712
2713    else if ( footprintRadius_r4 == nearestNeighbourFootprint ) then
2714
2715      call s2c_setupNearestNeighbor(interpInfo, stateVector, headerIndex, kIndex, stepIndex, procIndex, numGridpt)
2716
2717    else
2718
2719      write(*,*) 'footprint radius = ',footprintRadius_r4
2720      call utl_abort('s2c_setupHorizInterp: footprint radius not permitted')
2721
2722    end if
2723
2724  end subroutine s2c_setupHorizInterp
2725
2726  !------------------------------------------------------------------
2727  ! s2c_getFootprintRadius
2728  !------------------------------------------------------------------
2729  function s2c_getFootprintRadius( obsSpaceData, stateVector, headerIndex ) result(fpr)
2730    !
2731    !:Purpose: To determine the footprint radius (metres) of the observation.
2732    !          In the case of bilinear horizontal interpolation,
2733    !          the returned footprint is zero (default).
2734    !
2735    implicit none
2736
2737    ! Arguments:
2738    type(struct_obs), intent(in)  :: obsSpaceData
2739    type(struct_gsv), intent(in)  :: stateVector
2740    integer         , intent(in)  :: headerIndex
2741    ! Result:
2742    real(4)                       :: fpr
2743
2744    ! Locals:
2745    character(len=2)  :: obsFamily
2746    character(len=12) :: cstnid
2747    integer           :: codeType
2748
2749    fpr = bilinearFootprint
2750
2751    obsFamily = obs_getFamily ( obsSpaceData, headerIndex_opt=headerIndex )
2752    if ( obsFamily == 'GL' ) then
2753
2754      cstnid = obs_elem_c ( obsSpaceData, 'STID' , headerIndex )
2755      codeType = obs_headElem_i( obsSpaceData, OBS_ITY, headerIndex )
2756
2757      if (index(cstnid,'DMSP') == 1) then
2758
2759        select case(cstnid)
2760        case('DMSP15')
2761          fpr = 27.5e3
2762        case('DMSP16','DMSP17','DMSP18')
2763          fpr = 29.0e3
2764        case DEFAULT
2765          call utl_abort('s2c_getFootprintRadius: UNKNOWN station id: '//cstnid)
2766        end select
2767
2768      else if (cstnid == 'GCOM-W1') then
2769
2770        fpr = 11.0e3
2771
2772      else if (cstnid(1:6) == 'METOP-') then
2773
2774        fpr = 25.0e3
2775
2776      else if (cstnid == 'noaa-19') then
2777
2778        fpr = 2.75e3
2779
2780      else if (cstnid == 'CIS_DAILY') then
2781
2782        fpr = bilinearFootprint
2783
2784      else if (cstnid == 'RS1_IMG') then
2785
2786        fpr = bilinearFootprint
2787
2788      else if (codtyp_get_name(codeType) == 'iceclake') then
2789
2790        fpr = lakeFootprint
2791
2792      else if (cstnid == 'CIS_REGIONAL') then
2793
2794        fpr = bilinearFootprint
2795
2796      else if (cstnid(1:3) == 'RCM') then
2797
2798        fpr = 0.8e3
2799
2800      else
2801
2802        call utl_abort('s2c_getFootprintRadius: UNKNOWN station id: '//cstnid)
2803
2804      end if
2805
2806    else if (obsFamily == 'HY') then
2807
2808      fpr = nearestNeighbourFootprint
2809
2810    else if (obsFamily == 'TO' .and. useFootprintForTovs ) then
2811
2812      fpr = getTovsFootprintRadius(obsSpaceData, headerIndex, beSilent_opt=.true.)
2813
2814      ! As safety margin, add 10% to maxGridSpacing before comparing to the footprint radius.
2815      if ( fpr < 1.1 * real(stateVector%hco%maxGridSpacing,4) ) fpr = bilinearFootprint
2816
2817    else
2818
2819      fpr = bilinearFootprint
2820
2821    end if
2822
2823  end function s2c_getFootprintRadius
2824
2825  !--------------------------------------------------------------------------
2826  ! s2c_rejectZeroWeightObs
2827  !--------------------------------------------------------------------------
2828  subroutine s2c_rejectZeroWeightObs(interpInfo, obsSpaceData, mykBeg, mykEnd)
2829    !
2830    !:Purpose: To flag an observation in obsSpaceData as being rejected if
2831    !          it has zero interpolation weight (usually because an ocean
2832    !          obs is touching land) on any mpi task.
2833    !
2834    implicit none    
2835
2836    ! Arguments:
2837    type(struct_interpInfo), intent(inout) :: interpInfo
2838    type(struct_obs)       , intent(inout) :: obsSpaceData
2839    integer                , intent(in)    :: mykBeg
2840    integer                , intent(in)    :: mykEnd
2841
2842    ! Locals:
2843    integer :: numStep, procIndex, stepIndex, headerUsedIndex, headerIndex, kIndex
2844    integer :: numHeader, numHeaderMax, bodyIndexBeg, bodyIndexEnd, bodyIndex
2845    integer :: subGridIndex, gridptIndex, ierr, nsize
2846    integer, save :: numWrites = 0
2847    logical, allocatable :: allRejectObs(:,:), allRejectObsMpiGlobal(:,:)
2848
2849    write(*,*) 's2c_rejectZeroWeightObs: Starting'
2850
2851    numHeader = obs_numheader(obsSpaceData)
2852    call rpn_comm_allreduce(numHeader, numHeaderMax, 1,  &
2853                            'MPI_INTEGER', 'MPI_MAX', 'GRID', ierr)
2854
2855    allocate(allRejectObs(numHeaderMax,mmpi_nprocs))
2856    allocate(allRejectObsMpiGlobal(numHeaderMax,mmpi_nprocs))
2857    allRejectObs(:,:) = .false.
2858    allRejectObsMpiGlobal(:,:) = .false.
2859
2860    numStep = size(interpInfo%stepProcData(1,:))
2861    do procIndex = 1, mmpi_nprocs
2862      do stepIndex = 1, numStep
2863        do headerUsedIndex = 1, interpInfo%allNumHeaderUsed(stepIndex,procIndex)
2864          headerIndex = interpInfo%stepProcData(procIndex,stepIndex)%allHeaderIndex(headerUsedIndex)
2865          do kIndex = mykBeg, mykEnd
2866            if (kIndex == mykBeg) allRejectObs(headerIndex,procIndex) = .true.
2867            do subGridIndex = 1, interpInfo%hco%numSubGrid
2868              do gridptIndex =  &
2869                   interpInfo%stepProcData(procIndex,stepIndex)%depotIndexBeg(subGridIndex, headerUsedIndex, kIndex), &
2870                   interpInfo%stepProcData(procIndex,stepIndex)%depotIndexEnd(subGridIndex, headerUsedIndex, kIndex)
2871                if (interpInfo%interpWeightDepot(gridptIndex) > 0.0d0) then
2872                  allRejectObs(headerIndex,procIndex) = .false.
2873                end if
2874              end do
2875            end do
2876          end do ! kIndex
2877        end do ! headerUsedIndex
2878      end do ! stepIndex
2879    end do ! procIndex
2880
2881    ! do global communication of reject flags
2882    nsize = numHeaderMax*mmpi_nprocs
2883    call rpn_comm_allreduce(allRejectObs,allRejectObsMpiGlobal,nsize,'MPI_LOGICAL','MPI_LOR','GRID',ierr)
2884
2885    ! modify obsSpaceData based on reject flags
2886    do headerIndex = 1, obs_numHeader(obsSpaceData)
2887      if (allRejectObsMpiGlobal(headerIndex,mmpi_myid+1)) then
2888
2889        numWrites = numWrites + 1
2890        if (numWrites < maxNumWrites) then
2891          write(*,*) 's2c_rejectZeroWeightObs: Rejecting OBS with zero weight, index ', headerIndex
2892        else if (numWrites == maxNumWrites) then
2893          write(*,*) 's2c_rejectZeroWeightObs: More rejects, but reached maximum number of writes to the listing.'
2894        end if
2895
2896        bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
2897        bodyIndexEnd = obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) + bodyIndexBeg -1
2898        do bodyIndex = bodyIndexBeg, bodyIndexEnd
2899          call obs_bodySet_i(obsSpaceData, OBS_ASS, bodyIndex, obs_notAssimilated)
2900        end do
2901        call obs_headSet_i(obsSpaceData, OBS_ST1, headerIndex,  &
2902                    ibset( obs_headElem_i(obsSpaceData, OBS_ST1, headerIndex), 05))
2903      end if
2904    end do
2905
2906    deallocate(allRejectObs)
2907    deallocate(allRejectObsMpiGlobal)
2908
2909    write(*,*) 's2c_rejectZeroWeightObs: Finished'
2910
2911  end subroutine s2c_rejectZeroWeightObs
2912
2913  !--------------------------------------------------------------------------
2914  ! s2c_setupBilinearInterp
2915  !--------------------------------------------------------------------------
2916  subroutine s2c_setupBilinearInterp(interpInfo, stateVector, headerIndex, kIndex, &
2917                                     stepIndex, procIndex, numGridpt)
2918    !
2919    !:Purpose: To determine the grid points and their associated weights
2920    !          for the bilinear horizontal interpolation. If mask is present
2921    !          we currently can only handle a single 2D mask (like for sea
2922    !          ice or SST analysis). Will abort if multiple ocean levels present.
2923    !
2924    implicit none
2925
2926    ! Arguments:
2927    type(struct_interpInfo), intent(inout) :: interpInfo
2928    type(struct_gsv)       , intent(in)    :: stateVector
2929    integer                , intent(in)    :: headerIndex, kIndex, stepIndex
2930    integer                , intent(in)    :: procIndex
2931    integer                , intent(out)   :: numGridpt(interpInfo%hco%numSubGrid)
2932
2933    ! Locals:
2934    integer :: depotIndex
2935    integer :: ierr, niP1
2936    integer :: latIndex, lonIndex, latIndex2, lonIndex2, lonIndexP1
2937    integer :: subGridIndex, subGridForInterp, numSubGridsForInterp
2938    integer :: ipoint, gridptCount
2939    integer :: latIndexVec(4), lonIndexVec(4)
2940    logical :: mask(2,2)
2941    real(8) :: WeightVec(4)
2942    real(8) :: dldx, dldy
2943    real(8) :: weightsSum
2944    real(4) :: lon_deg_r4, lat_deg_r4
2945    real(4) :: xpos_r4, ypos_r4, xpos2_r4, ypos2_r4
2946    integer, parameter :: leftIndex = 1, rightIndex = 2, bottomIndex = 1, topIndex = 2
2947
2948    numGridpt(:) = 0
2949
2950    lat_deg_r4 = real(interpInfo%stepProcData(procIndex, stepIndex)%allLat(headerIndex, kIndex) *  &
2951                 MPC_DEGREES_PER_RADIAN_R8)
2952    lon_deg_r4 = real(interpInfo%stepProcData(procIndex, stepIndex)%allLon(headerIndex, kIndex) *  &
2953                 MPC_DEGREES_PER_RADIAN_R8)
2954    ierr = gpos_getPositionXY( stateVector%hco%EZscintID,   &
2955                              xpos_r4, ypos_r4, xpos2_r4, ypos2_r4, &
2956                              lat_deg_r4, lon_deg_r4, subGridIndex )
2957
2958    ! Allow for periodicity in Longitude for global Gaussian grid
2959    if ( stateVector%hco%grtyp == 'G' .or. &
2960         (stateVector%hco%grtyp == 'Z' .and. stateVector%hco%global) ) then
2961      niP1 = statevector%ni + 1
2962    else
2963      niP1 = statevector%ni
2964    end if
2965
2966    ! Find the lower-left grid point next to the observation
2967    if ( xpos_r4 >= real(niP1) ) then
2968      xpos_r4 = real(niP1)
2969      lonIndex = niP1 - 1
2970    else if ( xpos_r4 < 1.0 ) then
2971      xpos_r4 = 1.0
2972      lonIndex = 1
2973    else
2974      lonIndex = floor(xpos_r4)
2975    end if
2976    if ( xpos2_r4 >= real(niP1) ) then
2977      xpos2_r4 = real(niP1)
2978      lonIndex2 = niP1 - 1
2979    else if ( xpos2_r4 < 1.0 ) then
2980      xpos2_r4 = 1.0
2981      lonIndex2 = 1
2982    else
2983      lonIndex2 = floor(xpos2_r4)
2984    end if
2985
2986    if ( ypos_r4 >= real(statevector%nj) ) then
2987      ypos_r4 = real(statevector%nj)
2988      latIndex = statevector%nj - 1
2989    else if ( ypos_r4 < 1.0 ) then
2990      ypos_r4 = 1.0
2991      latIndex = 1
2992    else
2993      latIndex = floor(ypos_r4)
2994    end if
2995    if ( ypos2_r4 >= real(statevector%nj) ) then
2996      ypos2_r4 = real(statevector%nj)
2997      latIndex2 = statevector%nj - 1
2998    else if ( ypos2_r4 < 1.0 ) then
2999      ypos2_r4 = 1.0
3000      latIndex2 = 1
3001    else
3002      latIndex2 = floor(ypos2_r4)
3003    end if
3004
3005    if ( stateVector%hco%grtyp == 'U' ) then
3006      if ( ypos_r4 == real(stateVector%nj/2) ) then
3007        latIndex = floor(ypos_r4) - 1
3008      end if
3009      if ( ypos2_r4 == real(stateVector%nj/2) ) then
3010        latIndex2 = floor(ypos2_r4) - 1
3011      end if
3012    end if
3013
3014    ! Handle periodicity in longitude
3015    lonIndexP1 = lonIndex + 1
3016    if ( lonIndexP1 == statevector%ni + 1 ) lonIndexP1 = 1
3017
3018    ! Check if location is in between Yin and Yang (should not happen)
3019    if ( stateVector%hco%grtyp == 'U' ) then
3020      if ( ypos_r4 > real(stateVector%nj/2) .and.  &
3021           ypos_r4 < real((stateVector%nj/2)+1) ) then
3022        write(*,*) 's2c_setupBilinearInterp: WARNING, obs position in between Yin and Yang!!!'
3023        write(*,*) '   xpos, ypos = ', xpos_r4, ypos_r4
3024      end if
3025      if ( ypos2_r4 > real(stateVector%nj/2) .and.  &
3026           ypos2_r4 < real((stateVector%nj/2)+1) ) then
3027        write(*,*) 's2c_setupBilinearInterp: WARNING, obs position in between Yin and Yang!!!'
3028        write(*,*) '   xpos2, ypos2 = ', xpos2_r4, ypos2_r4
3029      end if
3030    end if
3031
3032    if ( subGridIndex == 3 ) then
3033      ! both subGrids involved in interpolation, so first treat subGrid 1
3034      numSubGridsForInterp = 2
3035      subGridIndex = 1
3036    else
3037      ! only 1 subGrid involved in interpolation
3038      numSubGridsForInterp = 1
3039    end if
3040
3041    if ( stateVector%oceanMask%maskPresent ) then
3042      ! abort if 3D mask is present, since we may not handle this situation correctly
3043      if ( stateVector%oceanMask%nLev > 1 ) then
3044        call utl_abort('s2c_setupBilinearInterp: 3D mask present - this case not properly handled')
3045      end if
3046      ! extract the ocean mask
3047      mask(leftIndex ,bottomIndex) = stateVector%oceanMask%mask(lonIndex  ,latIndex    ,1)
3048      mask(rightIndex,bottomIndex) = stateVector%oceanMask%mask(lonIndexP1,latIndex    ,1)
3049      mask(leftIndex ,topIndex   ) = stateVector%oceanMask%mask(lonIndex  ,latIndex + 1,1)
3050      mask(rightIndex,topIndex   ) = stateVector%oceanMask%mask(lonIndexP1,latIndex + 1,1)
3051    else
3052      mask(:,:) = .true.
3053    end if
3054
3055    do subGridForInterp = 1, numSubGridsForInterp
3056
3057      WeightVec(:) = 0
3058      gridptCount = 0
3059
3060      ! Compute the 4 weights of the bilinear interpolation
3061      if ( subGridForInterp == 1 ) then
3062        ! when only 1 subGrid involved, subGridIndex can be 1 or 2
3063        dldx = real(xpos_r4,8) - real(lonIndex,8)
3064        dldy = real(ypos_r4,8) - real(latIndex,8)
3065      else
3066        ! when 2 subGrids, subGridIndex is set to 1 for 1st iteration, 2 for second
3067        subGridIndex = 2
3068        lonIndex = lonIndex2
3069        latIndex = latIndex2
3070        lonIndexP1 = lonIndex2 + 1
3071        dldx = real(xpos2_r4,8) - real(lonIndex,8)
3072        dldy = real(ypos2_r4,8) - real(latIndex,8)
3073      end if
3074
3075      if ( mask(leftIndex ,bottomIndex) ) then
3076        gridptCount = gridptCount + 1
3077        latIndexVec(gridptCount) = latIndex
3078        lonIndexVec(gridptCount) = lonIndex
3079        WeightVec(gridptCount) = (1.d0-dldx) * (1.d0-dldy)
3080      end if
3081
3082      if ( mask(rightIndex,bottomIndex) ) then
3083        gridptCount = gridptCount + 1
3084        latIndexVec(gridptCount) = latIndex
3085        lonIndexVec(gridptCount) = lonIndexP1
3086        WeightVec(gridptCount) =       dldx  * (1.d0-dldy)
3087      end if
3088
3089      if ( mask(leftIndex ,topIndex   ) ) then
3090        gridptCount = gridptCount + 1
3091        latIndexVec(gridptCount) = latIndex + 1
3092        lonIndexVec(gridptCount) = lonIndex
3093        WeightVec(gridptCount) = (1.d0-dldx) *       dldy
3094      end if
3095
3096      if ( mask(rightIndex,topIndex   ) ) then
3097        gridptCount = gridptCount + 1
3098        latIndexVec(gridptCount) = latIndex + 1
3099        lonIndexVec(gridptCount) = lonIndexP1
3100        WeightVec(gridptCount) =       dldx  *       dldy
3101      end if
3102
3103      weightsSum = sum(WeightVec(1:gridptCount))
3104      if ( weightsSum > 0.d0 ) then
3105        WeightVec(1:gridptCount) = WeightVec(1:gridptCount) / weightsSum
3106      end if
3107
3108      ! divide weight by number of subGrids
3109      WeightVec(1:gridptCount) = WeightVec(1:gridptCount) / real(numSubGridsForInterp,8)
3110
3111      if ( allocated(interpInfo%interpWeightDepot) ) then
3112
3113        depotIndex = interpInfo%stepProcData(procIndex,stepIndex)%depotIndexBeg(subGridIndex, headerIndex, kIndex)
3114
3115        do ipoint=1,gridptCount
3116
3117          interpInfo%interpWeightDepot(depotIndex) = WeightVec(ipoint)
3118          interpInfo%latIndexDepot(depotIndex)     = latIndexVec(ipoint)
3119          interpInfo%lonIndexDepot(depotIndex)     = lonIndexVec(ipoint)
3120          depotIndex = depotIndex + 1
3121
3122        end do
3123
3124      end if
3125
3126      numGridpt(subGridIndex) = gridptCount
3127
3128    end do ! subGrid
3129
3130  end subroutine s2c_setupBilinearInterp
3131
3132  !--------------------------------------------------------------------------
3133  ! s2c_setupFootprintInterp
3134  !--------------------------------------------------------------------------
3135  subroutine s2c_setupFootprintInterp(fpr, interpInfo, stateVector, headerIndex, &
3136                                      kIndex, stepIndex, procIndex, numGridpt)
3137    !
3138    !:Purpose: To determine the grid points and their associated weights
3139    !          for the footprint horizontal interpolation.
3140    !
3141    implicit none
3142
3143    ! Arguments:
3144    real(4)                , intent(in)    :: fpr ! footprint radius (metres)
3145    type(struct_interpInfo), intent(inout) :: interpInfo
3146    type(struct_gsv)       , intent(in)    :: stateVector
3147    integer                , intent(in)    :: headerIndex, kIndex, stepIndex
3148    integer                , intent(in)    :: procIndex
3149    integer                , intent(out)   :: numGridpt(interpInfo%hco%numSubGrid)
3150
3151    ! Locals:
3152    integer :: depotIndex
3153    integer :: ierr
3154    integer :: latIndexCentre, lonIndexCentre, latIndexCentre2, lonIndexCentre2
3155    integer :: subGridIndex, numLocalGridptsFoundSearch
3156    real(4) :: lonObs_deg_r4, latObs_deg_r4
3157    real(8) :: lonObs, latObs
3158    real(4) :: xpos_r4, ypos_r4, xpos2_r4, ypos2_r4
3159    integer :: ipoint, gridptCount
3160    integer :: lonIndex, latIndex, resultsIndex, gridIndex
3161    integer :: lonIndexVec(maxNumLocalGridptsSearch), latIndexVec(maxNumLocalGridptsSearch)
3162    type(kdtree2_result)      :: searchResults(maxNumLocalGridptsSearch)
3163    real(kdkind)              :: refPosition(3), maxRadiusSquared
3164    type(kdtree2), pointer    :: tree
3165
3166    numGridpt(:) = 0
3167
3168    ! Determine the grid point nearest the observation.
3169
3170    latObs = interpInfo % stepProcData(procIndex, stepIndex) % allLat(headerIndex, kIndex)
3171    lonObs = interpInfo % stepProcData(procIndex, stepIndex) % allLon(headerIndex, kIndex)
3172
3173    latObs_deg_r4 = real(latObs * MPC_DEGREES_PER_RADIAN_R8)
3174    lonObs_deg_r4 = real(lonObs * MPC_DEGREES_PER_RADIAN_R8)
3175    ierr = gpos_getPositionXY( stateVector%hco%EZscintID,   &
3176                              xpos_r4, ypos_r4, xpos2_r4, ypos2_r4, &
3177                              latObs_deg_r4, lonObs_deg_r4, subGridIndex )
3178
3179    lonIndexCentre = nint(xpos_r4)
3180    latIndexCentre = nint(ypos_r4)
3181    lonIndexCentre2 = nint(xpos2_r4)
3182    latIndexCentre2 = nint(ypos2_r4)
3183
3184    if ( subGridIndex == 3 ) then
3185      write(*,*) 's2c_setupFootprintInterp: revise code'
3186      call utl_abort('s2c_setupFootprintInterp: both subGrids involved in interpolation.')
3187    end if
3188
3189    ! Return if observation is not on the grid, or masked.
3190    if ( lonIndexCentre < 1 .or. lonIndexCentre > statevector%hco%ni .or.  &
3191         latIndexCentre < 1 .or. latIndexCentre > statevector%hco%nj ) return
3192
3193    if ( stateVector%oceanMask%maskPresent ) then
3194      ! abort if 3D mask is present, since we may not handle this situation correctly
3195      if ( stateVector%oceanMask%nLev > 1 ) then
3196        call utl_abort('s2c_setupFootprintInterp: 3D mask present - this case not properly handled')
3197      end if
3198
3199      if ( .not. stateVector%oceanMask%mask(lonIndexCentre,latIndexCentre,1) ) return
3200    end if
3201
3202    ! do the search
3203    maxRadiusSquared = real(fpr,8) ** 2
3204    refPosition(:) = kdtree2_3dPosition(lonObs, latObs)
3205    nullify(tree)
3206    if ( interpInfo%inputStateVectorType == 'nl' ) then
3207      if ( associated(tree_nl) ) then
3208        tree => tree_nl
3209      else
3210        call utl_abort('s2c_setupFootprintInterp: tree_nl is not allocated!')
3211      end if
3212    else if ( interpInfo%inputStateVectorType == 'tl' .or. &
3213              interpInfo%inputStateVectorType == 'ad' ) then
3214      if ( associated(tree_tlad) ) then
3215        tree => tree_tlad
3216      else
3217        call utl_abort('s2c_setupFootprintInterp: tree_tlad is not allocated!')
3218      end if
3219    end if
3220    call kdtree2_r_nearest(tp=tree, qv=refPosition, r2=maxRadiusSquared, &
3221                           nfound=numLocalGridptsFoundSearch, &
3222                           nalloc=maxNumLocalGridptsSearch, &
3223                           results=searchResults)
3224
3225    if (numLocalGridptsFoundSearch > maxNumLocalGridptsSearch ) then
3226      call utl_abort('s2c_setupFootprintInterp: the parameter maxNumLocalGridptsSearch must be increased')
3227    else if ( numLocalGridptsFoundSearch < minNumLocalGridptsSearch .and. useFootprintForTovs ) then
3228      write(*,*) 's2c_setupFootprintInterp: Warning! For TOVS headerIndex=', headerIndex, &
3229                 ' number of grid points found within footprint radius=', fpr, ' is less than ', &
3230                 minNumLocalGridptsSearch 
3231    end if
3232
3233    ! ensure at least the nearest neighbor is included in lonIndexVec/latIndexVec
3234    ! if footprint size is smaller than the grid spacing.
3235    gridptCount = 1
3236    lonIndexVec(gridptCount) = lonIndexCentre
3237    latIndexVec(gridptCount) = latIndexCentre
3238
3239    ! fill the rest of lonIndexVec/latIndexVec
3240    gridLoop1: do resultsIndex = 1, numLocalGridptsFoundSearch
3241      gridIndex = searchResults(resultsIndex)%idx
3242      if ( gridIndex < 1 .or. gridIndex > statevector%hco%ni * statevector%hco%nj ) then
3243        write(*,*) 's2c_setupFootprintInterp: gridIndex=', gridIndex
3244        call utl_abort('s2c_setupFootprintInterp: gridIndex out of bound.')
3245      end if
3246
3247      latIndex = (gridIndex - 1) / statevector%hco%ni + 1
3248      lonIndex = gridIndex - (latIndex - 1) * statevector%hco%ni
3249      if ( lonIndex < 1 .or. lonIndex > statevector%hco%ni .or. &
3250           latIndex < 1 .or. latIndex > statevector%hco%nj ) then
3251        write(*,*) 's2c_setupFootprintInterp: lonIndex=', lonIndex, ',latIndex=', latIndex
3252        call utl_abort('s2c_setupFootprintInterp: lonIndex/latIndex out of bound.')
3253      end if
3254
3255      if ( stateVector%oceanMask%maskPresent ) then
3256        if ( .not. stateVector%oceanMask%mask(lonIndex,latIndex,1) ) cycle gridLoop1
3257      end if
3258
3259      if ( lonIndex == lonIndexCentre .and. latIndex == latIndexCentre ) cycle gridLoop1
3260
3261      gridptCount = gridptCount + 1
3262      lonIndexVec(gridptCount) = lonIndex
3263      latIndexVec(gridptCount) = latIndex
3264    end do gridLoop1
3265
3266    if ( allocated(interpInfo%interpWeightDepot) ) then
3267
3268      depotIndex = interpInfo%stepProcData(procIndex,stepIndex)%depotIndexBeg(subGridIndex, headerIndex, kIndex)
3269
3270      do ipoint = 1, gridptCount
3271
3272        interpInfo%interpWeightDepot(depotIndex) = 1.0d0 / real(gridptCount,8)
3273        interpInfo%latIndexDepot(depotIndex)     = latIndexVec(ipoint)
3274        interpInfo%lonIndexDepot(depotIndex)     = lonIndexVec(ipoint)
3275        depotIndex = depotIndex + 1
3276
3277      end do
3278
3279    end if
3280
3281    numGridpt(subGridIndex) = gridptCount
3282
3283  end subroutine s2c_setupFootprintInterp
3284
3285  !--------------------------------------------------------------------------
3286  ! s2c_setupLakeInterp
3287  !--------------------------------------------------------------------------
3288  subroutine s2c_setupLakeInterp(interpInfo, stateVector, headerIndex, kIndex, &
3289                                 stepIndex, procIndex, numGridpt)
3290    !
3291    !:Purpose: To determine the grid points and their associated weights
3292    !          for the lake horizontal interpolation.
3293    !
3294    implicit none
3295
3296    ! Arguments:
3297    type(struct_interpInfo), intent(inout) :: interpInfo
3298    type(struct_gsv)       , intent(in)    :: stateVector
3299    integer                , intent(in)    :: headerIndex, kIndex, stepIndex
3300    integer                , intent(in)    :: procIndex
3301    integer                , intent(out)   :: numGridpt(interpInfo%hco%numSubGrid)
3302
3303    ! Locals:
3304    integer :: depotIndex
3305    integer :: ierr
3306    integer :: latIndexCentre, lonIndexCentre, latIndexCentre2, lonIndexCentre2
3307    integer :: subGridIndex, subGridForInterp, numSubGridsForInterp
3308    real(4) :: lon_deg_r4, lat_deg_r4
3309    real(8) :: lon_rad, lat_rad
3310    real(4) :: xpos_r4, ypos_r4, xpos2_r4, ypos2_r4
3311    integer :: ipoint, gridptCount
3312    integer :: lakeCount
3313    integer :: lonIndex, latIndex, lakeIndex
3314    integer :: lonIndexVec(statevector%ni*statevector%nj), latIndexVec(statevector%ni*statevector%nj)
3315    logical :: reject, lake(statevector%ni,statevector%nj)
3316    integer :: k, l
3317
3318    if ( stateVector%hco%grtyp == 'U' ) then
3319      call utl_abort('s2c_setupLakeInterp: Yin-Yang grid not supported')
3320    end if
3321
3322    if ( .not.stateVector%oceanMask%maskPresent ) then
3323      call utl_abort('s2c_setupLakeInterp: Only compatible when mask present')
3324    end if
3325
3326    numGridpt(:) = 0
3327
3328    reject = .false.
3329
3330    numGridpt(:) = 0
3331
3332    ! Determine the grid point nearest the observation.
3333
3334    lat_rad = interpInfo%stepProcData(procIndex, stepIndex)%allLat(headerIndex, kIndex)
3335    lon_rad = interpInfo%stepProcData(procIndex, stepIndex)%allLon(headerIndex, kIndex)
3336    lat_deg_r4 = real(lat_rad * MPC_DEGREES_PER_RADIAN_R8)
3337    lon_deg_r4 = real(lon_rad * MPC_DEGREES_PER_RADIAN_R8)
3338    ierr = gpos_getPositionXY( stateVector%hco%EZscintID,   &
3339                               xpos_r4, ypos_r4, xpos2_r4, ypos2_r4, &
3340                               lat_deg_r4, lon_deg_r4, subGridIndex )
3341
3342    lonIndexCentre = nint(xpos_r4)
3343    latIndexCentre = nint(ypos_r4)
3344    lonIndexCentre2 = nint(xpos2_r4)
3345    latIndexCentre2 = nint(ypos2_r4)
3346
3347    if ( subGridIndex == 3 ) then
3348      write(*,*) 's2c_setupLakeInterp: revise code'
3349      call utl_abort('s2c_setupLakeInterp: both subGrids involved in interpolation.')
3350      numSubGridsForInterp = 2
3351      subGridIndex = 1
3352    else
3353      ! only 1 subGrid involved in interpolation
3354      numSubGridsForInterp = 1
3355    end if
3356
3357    do subGridForInterp = 1, numSubGridsForInterp
3358
3359      gridptCount = 0
3360
3361      ! It can happen that the lake location is closest to a grid point
3362      ! where MASK(I,J) = .false. while there are other grid points for the
3363      ! same lake where MASK(I,J) = .true.. Code needs modifications
3364      ! for this case.
3365
3366      ! If observation is not on the grid, don't use it.
3367      if ( lonIndexCentre < 1 .or. lonIndexCentre > statevector%ni .or.  &
3368           latIndexCentre < 1 .or. latIndexCentre > statevector%nj ) reject = .true.
3369
3370      if ( .not. stateVector%oceanMask%mask(lonIndexCentre,latIndexCentre,1) ) reject = .true.
3371
3372      if ( .not. reject ) then
3373
3374        lake(:,:) = .false.
3375        lake(lonIndexCentre,latIndexCentre) = .true.
3376        gridptCount = 1
3377        lonIndexVec(gridptCount) = lonIndexCentre
3378        latIndexVec(gridptCount) = latIndexCentre
3379
3380        lakeCount = 0
3381
3382        do while(lakeCount /= gridptCount)
3383
3384          do lakeIndex = lakeCount+1, gridptCount
3385
3386            if(lakeIndex == lakeCount+1) lakeCount = gridptCount
3387
3388            k = lonIndexVec(lakeIndex)
3389            l = latIndexVec(lakeIndex)
3390
3391            do latIndex = max(1,l-1),min(l+1,statevector%nj)
3392              do lonIndex = max(1,k-1),min(k+1,statevector%ni)
3393                if(stateVector%oceanMask%mask(lonIndex,latIndex,1) .and. .not. lake(lonIndex,latIndex)) then
3394                  lake(lonIndex,latIndex) = .true.
3395                  gridptCount = gridptCount + 1
3396                  lonIndexVec(gridptCount) = lonIndex
3397                  latIndexVec(gridptCount) = latIndex
3398                end if
3399              end do
3400            end do
3401
3402          end do
3403
3404        end do
3405
3406        if ( allocated(interpInfo%interpWeightDepot) ) then
3407
3408          depotIndex = interpInfo%stepProcData(procIndex,stepIndex)%depotIndexBeg(subGridIndex, headerIndex, kIndex)
3409
3410          do ipoint=1,gridptCount
3411
3412            interpInfo%interpWeightDepot(depotIndex) = 1.0d0 / real(gridptCount,8)
3413            interpInfo%latIndexDepot(depotIndex)     = latIndexVec(ipoint)
3414            interpInfo%lonIndexDepot(depotIndex)     = lonIndexVec(ipoint)
3415            depotIndex = depotIndex + 1
3416
3417          end do
3418
3419        end if
3420
3421        numGridpt(subGridIndex) = gridptCount
3422
3423      end if ! not reject
3424
3425    end do ! subGrid
3426
3427  end subroutine s2c_setupLakeInterp
3428
3429  !--------------------------------------------------------------------------
3430  ! s2c_setupNearestNeighbor
3431  !--------------------------------------------------------------------------
3432  subroutine s2c_setupNearestNeighbor(interpInfo, stateVector, headerIndex, kIndex, &
3433                                      stepIndex, procIndex, numGridpt)
3434    !
3435    !:Purpose: Determine the nearest grid points to the observations location
3436    !
3437    implicit none
3438
3439    ! Arguments:
3440    type(struct_interpInfo), intent(inout) :: interpInfo
3441    type(struct_gsv)       , intent(in)    :: stateVector
3442    integer                , intent(in)    :: headerIndex, kIndex, stepIndex, procIndex
3443    integer                , intent(out)   :: numGridpt(interpInfo%hco%numSubGrid)
3444
3445    ! Locals:
3446    integer :: depotIndex
3447    integer :: ierr
3448    integer :: latIndex, lonIndex
3449    integer :: subGridIndex
3450    real(4) :: lon_deg_r4, lat_deg_r4
3451    real(4) :: xpos_r4, ypos_r4, xpos2_r4, ypos2_r4
3452
3453    if ( stateVector%hco%grtyp == 'U' ) then
3454      call utl_abort('s2c_setupNearestNeighbor: Yin-Yang grid not supported')
3455    end if
3456
3457    numGridpt(:) = 0
3458
3459    lat_deg_r4 = real(interpInfo%stepProcData(procIndex, stepIndex)%allLat(headerIndex, kIndex) *  &
3460                 MPC_DEGREES_PER_RADIAN_R8)
3461    lon_deg_r4 = real(interpInfo%stepProcData(procIndex, stepIndex)%allLon(headerIndex, kIndex) *  &
3462                 MPC_DEGREES_PER_RADIAN_R8)
3463
3464    ierr = gpos_getPositionXY( stateVector%hco%EZscintID,   &
3465                              xpos_r4, ypos_r4, xpos2_r4, ypos2_r4, &
3466                              lat_deg_r4, lon_deg_r4, subGridIndex )
3467
3468    latIndex = nint(ypos_r4)
3469    lonIndex = nint(xpos_r4)
3470
3471    ! Handle periodicity in longitude
3472    if ( lonIndex == statevector%ni+1 .and. stateVector%hco%grtyp == 'G' ) lonIndex = 1
3473
3474    ! Test bounds
3475    if ( lonIndex < 1 .or. lonIndex > statevector%ni .or. &
3476         latIndex < 1 .or. latIndex > statevector%nj  ) then
3477
3478      write(*,*) 's2c_setupNearestNeighbor: observation out of bounds'
3479
3480    else
3481
3482      if ( allocated(interpInfo%interpWeightDepot) ) then
3483      
3484        depotIndex = interpInfo%stepProcData(procIndex,stepIndex)%depotIndexBeg(subGridIndex, headerIndex, kIndex)
3485
3486        interpInfo%interpWeightDepot(depotIndex) = 1.d0
3487        interpInfo%latIndexDepot    (depotIndex) = latIndex
3488        interpInfo%lonIndexDepot    (depotIndex) = lonIndex
3489
3490      end if
3491
3492      numGridpt(subGridIndex) = 1
3493
3494    end if
3495
3496  end subroutine s2c_setupNearestNeighbor
3497
3498  !--------------------------------------------------------------------------
3499  ! checkColumnStatevectorMatch
3500  !--------------------------------------------------------------------------
3501  subroutine checkColumnStatevectorMatch(column,statevector)
3502    !
3503    !:Purpose: To check column and statevector have identical nk and variables.
3504    !
3505    implicit none
3506
3507    ! Arguments:
3508    type(struct_gsv)       , intent(in) :: statevector
3509    type(struct_columnData), intent(in) :: column
3510
3511    ! Locals:
3512    integer :: kIndex
3513
3514    ! check column/statevector have same nk
3515    if ( column%nk /= gsv_getNumK(statevector) ) then
3516      write(*,*) 'checkColumnStatevectorMatch: column%nk, gsv_getNumK(statevector)', column%nk, gsv_getNumK(statevector)
3517      call utl_abort('checkColumnStatevectorMatch: column%nk /= gsv_getNumK(statevector)')
3518    end if
3519    
3520    ! loop through k and check varNames are same between column/statevector
3521    do kIndex = 1, column%nk
3522      if (gsv_getVarNameFromK(statevector,kIndex) /= col_getVarNameFromK(column,kIndex)) then
3523        write(*,*) 'checkColumnStatevectorMatch: kIndex, varname in statevector and column: ', kIndex, &
3524                   gsv_getVarNameFromK(statevector,kIndex), col_getVarNameFromK(column,kIndex) 
3525        call utl_abort('checkColumnStatevectorMatch: varname in column and statevector do not match')
3526      end if	
3527    end do
3528
3529  end subroutine checkColumnStatevectorMatch
3530
3531  !--------------------------------------------------------------------------
3532  ! latlonChecks
3533  !--------------------------------------------------------------------------
3534  subroutine latlonChecks( obsSpaceData, hco, headerIndex, rejectOutsideObs, &
3535    latLev_T, lonLev_T, latLev_M, lonLev_M, latLev_S_opt, lonLev_S_opt )
3536    !
3537    !:Purpose: To check if the obs are inside the domain.
3538    !
3539    implicit none
3540
3541    ! Arguments:
3542    type(struct_obs),  intent(inout) :: obsSpaceData
3543    type(struct_hco),  intent(in)    :: hco
3544    integer,           intent(in)    :: headerIndex
3545    logical,           intent(in)    :: rejectOutsideObs
3546    real(8),           intent(inout) :: latLev_T(:)
3547    real(8),           intent(inout) :: lonLev_T(:)
3548    real(8),           intent(inout) :: latLev_M(:)
3549    real(8),           intent(inout) :: lonLev_M(:)
3550    real(8), optional, intent(inout) :: latLev_S_opt
3551    real(8), optional, intent(inout) :: lonLev_S_opt
3552
3553    ! Locals:
3554    integer :: ierr
3555    integer :: bodyIndex, bodyIndexBeg, bodyIndexEnd, niP1, subGridIndex
3556    integer :: nlev_T, nlev_M
3557    real(4) :: lon_r4, lat_r4, lon_deg_r4, lat_deg_r4
3558    real(4) :: xpos_r4, ypos_r4, xpos2_r4, ypos2_r4
3559    logical :: latlonOutsideGrid, rejectHeader
3560    integer :: gdllfxy
3561
3562    ! Allow for periodicity in Longitude for global Gaussian grid
3563    if ( hco%grtyp == 'G' .or. (hco%grtyp == 'Z' .and. hco%global) ) then
3564      niP1 = hco%ni + 1
3565    else
3566      niP1 = hco%ni
3567    end if
3568
3569    nlev_T = size(latLev_T)
3570    nlev_M = size(latLev_M)
3571
3572    ! check if lat/lon of last thermo level is outside domain.
3573    rejectHeader = .false.
3574    lat_r4 = real(latLev_T(nlev_T),4)
3575    lon_r4 = real(lonLev_T(nlev_T),4)
3576
3577    lat_deg_r4 = lat_r4 * MPC_DEGREES_PER_RADIAN_R8
3578    lon_deg_r4 = lon_r4 * MPC_DEGREES_PER_RADIAN_R8
3579    ierr = gpos_getPositionXY( hco%EZscintID,   &
3580                              xpos_r4, ypos_r4, xpos2_r4, ypos2_r4, &
3581                              lat_deg_r4, lon_deg_r4, subGridIndex )
3582
3583    latlonOutsideGrid = ( xpos_r4 < 1.0        .or. &
3584                          xpos_r4 > real(niP1) .or. &
3585                          ypos_r4 < 1.0        .or. &
3586                          ypos_r4 > real(hco%nj) )
3587
3588    if ( latlonOutsideGrid .and. rejectOutsideObs ) then
3589      rejectHeader = .true.
3590    end if
3591
3592    !  check if lat/lon of last momentum level is outside domain.
3593    if ( .not. rejectHeader ) then
3594      lat_r4 = real(latLev_M(nlev_M),4)
3595      lon_r4 = real(lonLev_M(nlev_M),4)
3596
3597      lat_deg_r4 = lat_r4 * MPC_DEGREES_PER_RADIAN_R8
3598      lon_deg_r4 = lon_r4 * MPC_DEGREES_PER_RADIAN_R8
3599      ierr = gpos_getPositionXY( hco%EZscintID,   &
3600                                xpos_r4, ypos_r4, xpos2_r4, ypos2_r4, &
3601                                lat_deg_r4, lon_deg_r4, subGridIndex )
3602
3603      latlonOutsideGrid = ( xpos_r4 < 1.0        .or. &
3604                            xpos_r4 > real(niP1) .or. &
3605                            ypos_r4 < 1.0        .or. &
3606                            ypos_r4 > real(hco%nj) )
3607
3608      if ( latlonOutsideGrid .and. rejectOutsideObs ) then
3609        rejectHeader = .true.
3610      end if
3611    end if
3612
3613    !  check if lat/lon of surface level is outside domain.
3614    if ( present(latLev_S_opt) .and. present(lonLev_S_opt) .and. .not. rejectHeader ) then
3615      lat_r4 = real(latLev_S_opt,4)
3616      lon_r4 = real(lonLev_S_opt,4)
3617
3618      lat_deg_r4 = lat_r4 * MPC_DEGREES_PER_RADIAN_R8
3619      lon_deg_r4 = lon_r4 * MPC_DEGREES_PER_RADIAN_R8
3620      ierr = gpos_getPositionXY( hco%EZscintID,   &
3621                                xpos_r4, ypos_r4, xpos2_r4, ypos2_r4, &
3622                                lat_deg_r4, lon_deg_r4, subGridIndex )
3623
3624      latlonOutsideGrid = ( xpos_r4 < 1.0        .or. &
3625                            xpos_r4 > real(niP1) .or. &
3626                            ypos_r4 < 1.0        .or. &
3627                            ypos_r4 > real(hco%nj) )
3628
3629      if ( latlonOutsideGrid .and. rejectOutsideObs ) then
3630        rejectHeader = .true.
3631      end if
3632    end if
3633
3634    if ( rejectHeader ) then
3635      ! The observation is outside the domain.
3636      ! With a LAM trial field we must discard this observation
3637      write(*,*) 'latlonChecks: Rejecting OBS outside the hco domain, ', headerIndex
3638      write(*,*) '  position : ', lat_deg_r4, lon_deg_r4, ypos_r4, xpos_r4
3639
3640      bodyIndexBeg = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
3641      bodyIndexEnd = obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) + bodyIndexBeg -1
3642      do bodyIndex = bodyIndexBeg, bodyIndexEnd
3643        call obs_bodySet_i(obsSpaceData, OBS_ASS, bodyIndex, obs_notAssimilated)
3644      end do
3645      call obs_headSet_i(obsSpaceData, OBS_ST1, headerIndex,  &
3646           ibset( obs_headElem_i(obsSpaceData, OBS_ST1, headerIndex), 05))
3647
3648      ! Assign domain mid-point lat-lon to this header
3649      if ( hco%grtyp == 'Y' ) then
3650        lat_deg_r4 = hco%lat2d_4(hco%ni/2,hco%nj/2)
3651        lon_deg_r4 = hco%lon2d_4(hco%ni/2,hco%nj/2)
3652      else
3653        xpos_r4 = real(hco%ni)/2.0
3654        ypos_r4 = real(hco%nj)/2.0
3655        ierr = gdllfxy(hco%EZscintID, lat_deg_r4, lon_deg_r4, &
3656                       xpos_r4, ypos_r4, 1)
3657      end if
3658
3659      lonLev_T(:) = real(lon_deg_r4 * MPC_RADIANS_PER_DEGREE_R4,8)
3660      latLev_T(:) = real(lat_deg_r4 * MPC_RADIANS_PER_DEGREE_R4,8)
3661      lonLev_M(:) = real(lon_deg_r4 * MPC_RADIANS_PER_DEGREE_R4,8)
3662      latLev_M(:) = real(lat_deg_r4 * MPC_RADIANS_PER_DEGREE_R4,8)
3663      if (present(lonLev_S_opt) .and. present(latLev_S_opt)) then
3664        lonLev_S_opt = real(lon_deg_r4 * MPC_RADIANS_PER_DEGREE_R4,8)
3665        latLev_S_opt = real(lat_deg_r4 * MPC_RADIANS_PER_DEGREE_R4,8)
3666      end if
3667
3668    end if
3669
3670  end subroutine latlonChecks
3671
3672  !--------------------------------------------------------------------------
3673  ! getTovsFootprintRadius
3674  !--------------------------------------------------------------------------
3675  function getTovsFootprintRadius(obsSpaceData, headerIndex, beSilent_opt) result(footPrintRadius_r4)
3676    !
3677    !:Purpose: calculate foot-print radius for TOVS observations
3678    !
3679    implicit none
3680
3681    ! Arguments:
3682    type(struct_obs),  intent(in) :: obsSpaceData
3683    integer         ,  intent(in) :: headerIndex
3684    logical, optional, intent(in) :: beSilent_opt
3685    ! Result:
3686    real(4)                       :: footPrintRadius_r4
3687
3688    ! Locals:
3689    integer :: codtyp, sensorIndex 
3690    real(8) :: fovAngularDiameter, satHeight, footPrintRadius
3691    character(len=codtyp_name_length) :: instrumName
3692    logical :: beSilent
3693
3694    if ( present(beSilent_opt) ) then
3695      beSilent = beSilent_opt
3696    else
3697      beSilent = .true.
3698    end if
3699
3700    ! get nominal satellite height
3701    sensorIndex = tvs_lsensor(tvs_tovsIndex(headerIndex))
3702    satHeight = tvs_coefs(sensorIndex)%coef%fc_sat_height
3703
3704    ! FOV angular diameter  
3705    codtyp = obs_headElem_i( obsSpaceData, OBS_ITY, headerIndex )
3706    instrumName = codtyp_get_name(codtyp)
3707    select case(trim(instrumName))
3708    case('amsua')
3709      fovAngularDiameter = 3.3d0
3710    case('amsub')
3711      fovAngularDiameter = 1.1d0
3712    case('mhs')
3713      fovAngularDiameter = 10.0d0 / 9.0d0
3714    case('airs')
3715      fovAngularDiameter = 1.1d0
3716    case('iasi')
3717      fovAngularDiameter = 14.65d0 / 1000.0d0 * MPC_DEGREES_PER_RADIAN_R8
3718    case('radianceclear')
3719      fovAngularDiameter = 0.125d0
3720    case('ssmis')
3721      fovAngularDiameter = 1.2d0
3722    case('atms')
3723      fovAngularDiameter = 2.2d0
3724    case('cris')
3725      fovAngularDiameter = 14.0d0 / 824.0d0 * MPC_DEGREES_PER_RADIAN_R8
3726    case default
3727      fovAngularDiameter = -1.0d0
3728    end select
3729
3730    if ( fovAngularDiameter < 0.0d0 ) then 
3731      footPrintRadius_r4 = bilinearFootprint
3732    else
3733      ! get foot print radius (meter) from angular diameter
3734      footPrintRadius = 0.5d0 * fovAngularDiameter * MPC_RADIANS_PER_DEGREE_R8 * satHeight * 1000
3735      footPrintRadius_r4 = real(footPrintRadius,4)
3736    end if
3737
3738    if ( .not. beSilent ) then
3739      write(*,*) 'getTovsFootprintRadius: sensorIndex=', sensorIndex, &
3740                ',satHeight=', satHeight, ',fovAngularDiameter=', fovAngularDiameter, ',codtyp=', codtyp, &
3741                ',footPrintRadius=', footPrintRadius_r4
3742    end if
3743
3744  end function getTovsFootprintRadius
3745
3746  ! -------------------------------------------------------------
3747  ! s2c_getWeightsAndGridPointIndexes
3748  ! -------------------------------------------------------------
3749  subroutine s2c_getWeightsAndGridPointIndexes(headerIndex, kIndex, stepIndex, procIndex, &
3750                                               interpWeight, latIndex, lonIndex, gridptCount)
3751    !:Purpose: Returns the weights and grid point indexes for a single observation.
3752    !           
3753    !
3754    implicit none
3755
3756    ! Arguments:
3757    integer, intent(in)  :: headerIndex
3758    integer, intent(in)  :: kIndex
3759    integer, intent(in)  :: stepIndex
3760    integer, intent(in)  :: procIndex
3761    real(8), intent(out) :: interpWeight(:)
3762    integer, intent(out) :: latIndex(:)
3763    integer, intent(out) :: lonIndex(:)
3764    integer, intent(out) :: gridptCount
3765
3766    ! Locals:
3767    integer :: indexBeg, indexEnd, gridptIndex
3768    integer :: subGridIndex, maxGridpt
3769
3770    call utl_tmg_start(30,'--StateToColumn')
3771
3772    maxGridpt = size( interpWeight )
3773
3774    gridptCount = 0
3775
3776    if ( interpInfo_tlad%stepProcData(procIndex, stepIndex)%allHeaderIndex(headerIndex) /= headerIndex ) then
3777      call utl_abort('s2c_getWeightsAndGridPointIndexes: headerUsedIndex and headerIndex differ.'//    &
3778                     ' If using multiple time steps in the assimilation window,'//                     &
3779                     ' the code needs to be modified to convert values of headerIndex into headerUsedIndex.')
3780    end if
3781
3782    subGrid_loop: do subGridIndex = 1, interpInfo_tlad%hco%numSubGrid
3783
3784      indexBeg = interpInfo_tlad%stepProcData(procIndex,stepIndex)%depotIndexBeg(subGridIndex, headerIndex, kIndex)
3785      indexEnd = interpInfo_tlad%stepProcData(procIndex,stepIndex)%depotIndexEnd(subGridIndex, headerIndex, kIndex)
3786
3787      if ( indexEnd < IndexBeg ) cycle subGrid_loop
3788
3789      do gridptIndex = indexBeg, indexEnd
3790
3791        gridptCount = gridptCount + 1
3792
3793        if ( gridptCount > maxGridpt ) then
3794          call utl_abort('s2c_getWeightsAndGridPointIndexes: maxGridpt must be increased')
3795        end if
3796
3797        lonIndex(gridptCount) = interpInfo_tlad%lonIndexDepot(gridptIndex)
3798        latIndex(gridptCount) = interpInfo_tlad%latIndexDepot(gridptIndex)
3799        interpWeight(gridptCount) = interpInfo_tlad%interpWeightDepot(gridptIndex)
3800
3801      end do
3802
3803    end do subGrid_loop
3804
3805    call utl_tmg_stop(30)
3806
3807  end subroutine s2c_getWeightsAndGridPointIndexes
3808
3809  ! -------------------------------------------------------------
3810  ! s2c_deallocInterpInfo
3811  ! -------------------------------------------------------------
3812  subroutine s2c_deallocInterpInfo( inputStateVectorType )
3813    !:Purpose: Deallocate interpInfo_nl/tlad object.
3814    !
3815    implicit none
3816
3817    ! Arguments:
3818    character(len=*), intent(in) :: inputStateVectorType
3819
3820    ! Locals:
3821    type(struct_interpInfo), pointer :: interpInfo
3822    integer :: stepIndex, procIndex, numStep
3823
3824    select case( trim(inputStateVectorType) )
3825      case('nl')
3826        interpInfo => interpInfo_nl
3827      case('tlad')
3828        interpInfo => interpInfo_tlad
3829      case default
3830        call utl_abort('s2c_deallocInterpInfo: invalid input argument' // inputStateVectorType)
3831    end select
3832
3833    if ( .not. interpInfo%initialized ) return
3834
3835    write(*,*) 's2c_deallocInterpInfo: deallocating interpInfo for inputStateVectorType=', &
3836                inputStateVectorType
3837
3838    numStep = size(interpInfo%stepProcData,2)
3839
3840    deallocate(interpInfo%interpWeightDepot)
3841    deallocate(interpInfo%latIndexDepot)
3842    deallocate(interpInfo%lonIndexDepot)
3843    do stepIndex = 1, numStep
3844      do procIndex = 1, mmpi_nprocs
3845        deallocate(interpInfo%stepProcData(procIndex,stepIndex)%allLat)
3846        deallocate(interpInfo%stepProcData(procIndex,stepIndex)%allLon)
3847        deallocate(interpInfo%stepProcData(procIndex,stepIndex)%allHeaderIndex)
3848        deallocate(interpInfo%stepProcData(procIndex,stepIndex)%depotIndexBeg)
3849        deallocate(interpInfo%stepProcData(procIndex,stepIndex)%depotIndexEnd)
3850        if ( interpInfo%hco%rotated ) then
3851          deallocate(interpInfo%stepProcData(procIndex,stepIndex)%allLonRot)
3852          deallocate(interpInfo%stepProcData(procIndex,stepIndex)%allLatRot)
3853        end if
3854      end do
3855    end do
3856    deallocate(interpInfo%stepProcData)
3857    deallocate(interpInfo%allNumHeaderUsed)
3858    call oti_deallocate(interpInfo%oti)
3859
3860    interpInfo%initialized = .false.
3861
3862  end subroutine s2c_deallocInterpInfo
3863
3864end module stateToColumn_mod