thinning_mod sourceΒΆ

   1module thinning_mod
   2  ! MODULE thinning_mod (prefix='thn' category='1. High-level functionality')
   3  !
   4  !:Purpose:  Using observation-type-specific algorithms, set bit 11 of 'flag'
   5  !           on data that are not to be assimilated.
   6  !
   7  !:Note:     This module is intended to group all of the thinning methods in a
   8  !           single fortran module.
   9  !
  10  use midasMpi_mod
  11  use bufr_mod
  12  use mathPhysConstants_mod
  13  use earthConstants_mod
  14  use obsSpaceData_mod
  15  use horizontalCoord_mod
  16  use verticalCoord_mod
  17  use timeCoord_mod
  18  use gridStateVector_mod
  19  use gridStateVectorFileIO_mod
  20  use codtyp_mod
  21  use physicsFunctions_mod
  22  use utilities_mod
  23  use kdTree2_mod
  24  implicit none
  25  private
  26
  27  public :: thn_thinHyper, thn_thinTovs, thn_thinCSR
  28  public :: thn_thinRaobs, thn_thinAircraft, thn_thinScat, thn_thinSatWinds
  29  public :: thn_thinSurface, thn_thinGbGps, thn_thinGpsRo, thn_thinAladin
  30  public :: thn_thinSatSST
  31  integer, external :: get_max_rss
  32
  33contains
  34
  35  !--------------------------------------------------------------------------
  36  ! thn_thinSurface
  37  !--------------------------------------------------------------------------
  38  subroutine thn_thinSurface(obsdat, obsFamily)
  39    !
  40    !:Purpose: Main subroutine for thinning of surface obs.
  41    !
  42    implicit none
  43
  44    ! Arguments:
  45    type(struct_obs), intent(inout) :: obsdat
  46    character(len=2), intent(in)    :: obsFamily
  47
  48    ! Locals:
  49    integer :: nulnam
  50    integer :: fnom, fclos, ierr
  51
  52    ! Namelist variables:
  53    logical :: doThinning        ! if false, we return immediately
  54    real(8) :: step              ! time resolution (in hours)
  55    integer :: deltmax           ! maximum time difference (in minutes)
  56    logical :: useBlackList      ! signal if blacklist file should be read and used
  57    logical :: considerSHIPstnID ! signal if SHIP stn ID should be considered in thinning
  58
  59    namelist /thin_surface/doThinning, step, deltmax, useBlackList, considerSHIPstnID
  60    
  61    ! set default values for namelist variables
  62    doThinning        = .false. 
  63    step              = 6.0d0   
  64    deltmax           = 90      
  65    useBlackList      = .false. 
  66    considerSHIPstnID = .true.
  67    
  68    ! return if no surface obs
  69    if (.not. obs_famExist(obsdat, obsFamily)) return
  70
  71    ! Read the namelist for Surface observations (if it exists)
  72    if (utl_isNamelistPresent('thin_surface', './flnml')) then
  73      nulnam = 0
  74      ierr = fnom(nulnam, './flnml','FTN+SEQ+R/O', 0)
  75      if (ierr /= 0) call utl_abort('thn_thinSurface: Error opening file flnml')
  76      read(nulnam,nml = thin_surface, iostat = ierr)
  77      if (ierr /= 0) call utl_abort('thn_thinSurface: Error reading namelist')
  78      if (mmpi_myid == 0) write(*, nml = thin_surface)
  79      ierr = fclos(nulnam)
  80    else
  81      write(*,*)
  82      write(*,*) 'thn_thinSurface: Namelist block thin_surface is missing in the namelist.'
  83      write(*,*) '                 The default value will be taken.'
  84      if (mmpi_myid == 0) write(*, nml = thin_surface)
  85    end if    
  86
  87    if (.not. doThinning) return
  88
  89    call utl_tmg_start(114,'--ObsThinning')
  90    call thn_surfaceInTime(obsdat, obsFamily, step, deltmax, useBlackList, considerSHIPstnID)
  91    call utl_tmg_stop(114)
  92
  93  end subroutine thn_thinSurface
  94
  95  !--------------------------------------------------------------------------
  96  ! thn_thinRaobs
  97  !--------------------------------------------------------------------------
  98  subroutine thn_thinRaobs(obsdat)
  99    !
 100    !:Purpose: Main thinning subroutine Radiosonde obs.
 101    !
 102    implicit none
 103
 104    ! Arguments:
 105    type(struct_obs), intent(inout) :: obsdat
 106
 107    ! Locals:
 108    integer :: nulnam
 109    integer :: fnom, fclos, ierr
 110
 111    ! Namelist variables:
 112    logical :: verticalThinningES ! choose to do vertical thinning of humidity obs
 113    logical :: ecmwfRejetsES      ! choose to do filtering of T-Td obs with approach similar to ECMWF
 114    real(4) :: toleranceFactor    ! Tolerance factor for TAC vs BUFR selection
 115
 116    namelist /thin_raobs/ verticalThinningES, ecmwfRejetsES, toleranceFactor
 117
 118    ! return if no aircraft obs
 119    if (.not. obs_famExist(obsdat,'UA')) return
 120
 121    ! Default values for namelist variables
 122    verticalThinningES = .true.
 123    ecmwfRejetsES = .true.
 124    toleranceFactor = 1.4
 125
 126    ! Read the namelist for Radiosonde observations (if it exists)
 127    if (utl_isNamelistPresent('thin_raobs','./flnml')) then
 128      nulnam = 0
 129      ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
 130      if (ierr /= 0) call utl_abort('thn_thinRaobs: Error opening file flnml')
 131      read(nulnam,nml=thin_raobs,iostat=ierr)
 132      if (ierr /= 0) call utl_abort('thn_thinRaobs: Error reading thin_raobs namelist')
 133      if (mmpi_myid == 0) write(*,nml=thin_raobs)
 134      ierr = fclos(nulnam)
 135    else
 136      write(*,*)
 137      write(*,*) 'thn_thinRaobs: Namelist block thin_raobs is missing in the namelist.'
 138      write(*,*) '               The default value will be taken.'
 139      if (mmpi_myid == 0) write(*,nml=thin_raobs)
 140    end if
 141
 142    call utl_tmg_start(114,'--ObsThinning')
 143    call thn_radiosonde(obsdat, verticalThinningES, ecmwfRejetsES, toleranceFactor)
 144    call utl_tmg_stop(114)
 145
 146  end subroutine thn_thinRaobs
 147
 148  !--------------------------------------------------------------------------
 149  ! thn_thinAircraft
 150  !--------------------------------------------------------------------------
 151  subroutine thn_thinAircraft(obsdat)
 152    !
 153    !:Purpose: Main thinning subroutine for aircraft obs.
 154    !
 155    implicit none
 156
 157    ! Arguments:
 158    type(struct_obs), intent(inout) :: obsdat
 159
 160    ! Locals:
 161    integer :: nulnam
 162    integer :: fnom, fclos, ierr
 163
 164    ! Namelist variables:
 165    integer :: deltmax ! maximum time difference (in minutes)
 166
 167    namelist /thin_aircraft/deltmax
 168
 169    ! return if no aircraft obs
 170    if (.not. obs_famExist(obsdat,'AI')) return
 171
 172    ! Default values for namelist variables
 173    deltmax = 90
 174
 175    ! Read the namelist for Aircraft observations (if it exists)
 176    if (utl_isNamelistPresent('thin_aircraft','./flnml')) then
 177      nulnam = 0
 178      ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
 179      if (ierr /= 0) call utl_abort('thn_thinAircraft: Error opening file flnml')
 180      read(nulnam,nml=thin_aircraft,iostat=ierr)
 181      if (ierr /= 0) call utl_abort('thn_thinAircraft: Error reading thin_aircraft namelist')
 182      if (mmpi_myid == 0) write(*,nml=thin_aircraft)
 183      ierr = fclos(nulnam)
 184    else
 185      write(*,*)
 186      write(*,*) 'thn_thinAircraft: Namelist block thin_aircraft is missing in the namelist.'
 187      write(*,*) '                  The default value will be taken.'
 188      if (mmpi_myid == 0) write(*,nml=thin_aircraft)
 189    end if
 190
 191    call utl_tmg_start(114,'--ObsThinning')
 192    call thn_aircraftByBoxes(obsdat, 'AI', deltmax)
 193    call utl_tmg_stop(114)
 194
 195  end subroutine thn_thinAircraft
 196
 197  !--------------------------------------------------------------------------
 198  ! thn_thinSatWinds
 199  !--------------------------------------------------------------------------
 200  subroutine thn_thinSatWinds(obsdat)
 201    !
 202    !:Purpose: Main thinning subroutine for satellite winds (AMVs).
 203    !
 204    implicit none
 205
 206    ! Arguments:
 207    type(struct_obs), intent(inout) :: obsdat
 208
 209    ! Locals:
 210    integer :: nulnam
 211    integer :: fnom, fclos, ierr
 212
 213    ! Namelist variables:
 214    integer :: deltemps ! number of time bins between adjacent observations
 215    integer :: deldist  ! minimal distance in km between adjacent observations
 216
 217    namelist /thin_satwind/deltemps, deldist
 218
 219    ! return if no satwind obs
 220    if (.not. obs_famExist(obsdat,'SW')) return
 221
 222    ! Default values for namelist variables
 223    deltemps = 6
 224    deldist  = 200
 225
 226    ! Read the namelist for SatWinds observations (if it exists)
 227    if (utl_isNamelistPresent('thin_satwind','./flnml')) then
 228      nulnam = 0
 229      ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
 230      if (ierr /= 0) call utl_abort('thn_thinSatWinds: Error opening file flnml')
 231      read(nulnam,nml=thin_satwind,iostat=ierr)
 232      if (ierr /= 0) call utl_abort('thn_thinSatWinds: Error reading thin_satwind namelist')
 233      if (mmpi_myid == 0) write(*,nml=thin_satwind)
 234      ierr = fclos(nulnam)
 235    else
 236      write(*,*)
 237      write(*,*) 'thn_thinSatWinds: Namelist block thin_satwind is missing in the namelist.'
 238      write(*,*) '                  The default value will be taken.'
 239      if (mmpi_myid == 0) write(*,nml=thin_satwind)
 240    end if
 241
 242    call utl_tmg_start(114,'--ObsThinning')
 243    call thn_satWindsByDistance(obsdat, 'SW', deltemps, deldist)
 244    call utl_tmg_stop(114)
 245
 246  end subroutine thn_thinSatWinds
 247
 248  !--------------------------------------------------------------------------
 249  ! thn_thinGpsRo
 250  !--------------------------------------------------------------------------
 251  subroutine thn_thinGpsRo(obsdat)
 252    !
 253    !:Purpose: Main thinning subroutine GPS radio-occultation obs.
 254    !
 255    implicit none
 256
 257    ! Arguments:
 258    type(struct_obs), intent(inout) :: obsdat
 259
 260    ! Locals:
 261    integer :: nulnam
 262    integer :: fnom, fclos, ierr
 263
 264    ! Namelist variables:
 265    real(8) :: heightMin     ! niveau a partir du quel on accepte les donnees
 266    real(8) :: heightMax     ! niveau a partir du quel on rejette les donnees
 267    real(8) :: heightSpacing ! epaisseur minimale entre deux niveaux
 268    integer :: gpsroVarNo    ! bufr element id to be used
 269
 270    namelist /thin_gpsro/heightMin, heightMax, heightSpacing, gpsroVarNo
 271
 272    ! return if no gb-gps obs
 273    if (.not. obs_famExist(obsdat,'RO')) return
 274
 275    ! Default values for namelist variables
 276    heightMin     = 1000.0d0
 277    heightMax     = 40000.0d0
 278    heightSpacing = 750.0d0
 279    gpsroVarNo    = BUFR_NERF ! default is refractivity
 280
 281    ! Read the namelist for GpsRo observations (if it exists)
 282    if (utl_isNamelistPresent('thin_gpsro','./flnml')) then
 283      nulnam = 0
 284      ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
 285      if (ierr /= 0) call utl_abort('thn_thinGpsRo: Error opening file flnml')
 286      read(nulnam,nml=thin_gpsro,iostat=ierr)
 287      if (ierr /= 0) call utl_abort('thn_thinGpsRo: Error reading thin_gpsro namelist')
 288      if (mmpi_myid == 0) write(*,nml=thin_gpsro)
 289      ierr = fclos(nulnam)
 290    else
 291      write(*,*)
 292      write(*,*) 'thn_thinGpsRo: Namelist block thin_gpsro is missing in the namelist.'
 293      write(*,*) '               The default value will be taken.'
 294      if (mmpi_myid == 0) write(*,nml=thin_gpsro)
 295    end if
 296
 297    call utl_tmg_start(114,'--ObsThinning')
 298    call thn_gpsroVertical(obsdat, heightMin, heightMax, heightSpacing, gpsroVarNo)
 299    call utl_tmg_stop(114)
 300
 301  end subroutine thn_thinGpsRo
 302
 303  !--------------------------------------------------------------------------
 304  ! thn_thinGbGps
 305  !--------------------------------------------------------------------------
 306  subroutine thn_thinGbGps(obsdat)
 307    !
 308    !:Purpose: Main thinning subroutine ground-based GPS obs.
 309    !
 310    implicit none
 311
 312    ! Arguments:
 313    type(struct_obs), intent(inout) :: obsdat
 314
 315    ! Locals:
 316    integer :: nulnam
 317    integer :: fnom, fclos, ierr
 318
 319    ! Namelist variables:
 320    integer :: deltemps     ! number of time bins between adjacent observations
 321    integer :: deldist      ! minimal distance in km between adjacent observations
 322    logical :: removeUncorrected ! remove obs that are not bias corrected (bit 6)
 323    logical :: rejectNoZTDScore ! reject GB-GPS obs if no ZTD quality score available
 324   
 325    namelist /thin_gbgps/deltemps, deldist, rejectNoZTDScore, removeUncorrected
 326
 327    ! return if no gb-gps obs
 328    if (.not. obs_famExist(obsdat,'GP')) return
 329
 330    ! Default values for namelist variables
 331    deltemps     = 8
 332    deldist      = 50
 333    removeUncorrected = .false.
 334    rejectNoZTDScore = .false.
 335
 336    ! Read the namelist for GbGps observations (if it exists)
 337    if (utl_isNamelistPresent('thin_gbgps','./flnml')) then
 338      nulnam = 0
 339      ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
 340      if (ierr /= 0) call utl_abort('thn_thinGbGps: Error opening file flnml')
 341      read(nulnam,nml=thin_gbgps,iostat=ierr)
 342      if (ierr /= 0) call utl_abort('thn_thinGbGps: Error reading thin_gbgps namelist')
 343      if (mmpi_myid == 0) write(*,nml=thin_gbgps)
 344      ierr = fclos(nulnam)
 345    else
 346      write(*,*)
 347      write(*,*) 'thn_thinGbGps: Namelist block thin_gbgps is missing in the namelist.'
 348      write(*,*) '               The default value will be taken.'
 349      if (mmpi_myid == 0) write(*,nml=thin_gbgps)
 350    end if
 351
 352    call utl_tmg_start(114,'--ObsThinning')
 353    call thn_gbgpsByDistance(obsdat, deltemps, deldist, removeUncorrected, rejectNoZTDScore)
 354    call utl_tmg_stop(114)
 355
 356  end subroutine thn_thinGbGps
 357
 358  !--------------------------------------------------------------------------
 359  ! thn_thinAladin
 360  !--------------------------------------------------------------------------
 361  subroutine thn_thinAladin(obsdat)
 362    !
 363    !:Purpose: Main thinning subroutine for Aladin winds obs.
 364    !
 365    implicit none
 366
 367    ! Arguments:
 368    type(struct_obs), intent(inout) :: obsdat
 369
 370    ! Locals:
 371    integer :: nulnam
 372    integer :: fnom, fclos, ierr
 373
 374    ! Namelist variables:
 375    integer :: keepNthVertical ! keep every nth vertical datum
 376
 377    namelist /thin_aladin/keepNthVertical
 378
 379    ! return if no Aladin obs
 380    if (.not. obs_famExist(obsdat,'AL')) return
 381
 382    ! Default values for namelist variables
 383    keepNthVertical=-1
 384
 385    ! Read the namelist for Aladin observations (if it exists)
 386    if (utl_isNamelistPresent('thin_aladin','./flnml')) then
 387      nulnam = 0
 388      ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
 389      if (ierr /= 0) call utl_abort('thn_thinAladin: Error opening file flnml')
 390      read(nulnam,nml=thin_aladin,iostat=ierr)
 391      if (ierr /= 0) call utl_abort('thn_thinAladin: Error reading thin_aladin namelist')
 392      if (mmpi_myid == 0) write(*,nml=thin_aladin)
 393      ierr = fclos(nulnam)
 394    else
 395      write(*,*)
 396      write(*,*) 'thn_thinAladin: Namelist block thin_aladin is missing in the namelist.'
 397      write(*,*) '                The default value will be taken.'
 398      if (mmpi_myid == 0) write(*,nml=thin_aladin)
 399    end if
 400
 401    if (keepNthVertical > 0) then
 402      call utl_tmg_start(114,'--ObsThinning')
 403      call thn_keepNthObs(obsdat, 'AL', keepNthVertical)
 404      call utl_tmg_stop(114)
 405    end if
 406
 407  end subroutine thn_thinAladin
 408
 409  !--------------------------------------------------------------------------
 410  ! thn_thinCSR
 411  !--------------------------------------------------------------------------
 412  subroutine thn_thinCSR(obsdat)
 413    !
 414    !:Purpose: Main thinning subroutine for geostationary radiances (CSR).
 415    !
 416    implicit none
 417
 418    ! Arguments:
 419    type(struct_obs), intent(inout) :: obsdat
 420
 421    ! Locals:
 422    integer :: nulnam
 423    integer :: fnom, fclos, ierr
 424
 425    ! Namelist variables:
 426    integer :: deltax     ! thinning (dimension of box sides) (in km)
 427    integer :: deltrad    ! radius around box center for chosen obs (in km)
 428
 429    namelist /thin_csr/deltax, deltrad
 430
 431    ! return if no TOVS obs
 432    if (.not. obs_famExist(obsdat,'TO')) return
 433
 434    ! Default namelist values
 435    deltax  = 150
 436    deltrad = 45
 437
 438    ! Read the namelist for CSR observations (if it exists)
 439    if (utl_isNamelistPresent('thin_csr','./flnml')) then
 440      nulnam = 0
 441      ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
 442      if (ierr /= 0) call utl_abort('thn_thinCSR: Error opening file flnml')
 443      read(nulnam,nml=thin_csr,iostat=ierr)
 444      if (ierr /= 0) call utl_abort('thn_thinCSR: Error reading thin_csr namelist')
 445      if (mmpi_myid == 0) write(*,nml=thin_csr)
 446      ierr = fclos(nulnam)
 447    else
 448      write(*,*)
 449      write(*,*) 'thn_thinCSR: Namelist block thin_csr is missing in the namelist.'
 450      write(*,*) '             The default value will be taken.'
 451      if (mmpi_myid == 0) write(*,nml=thin_csr)
 452    end if
 453
 454    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 455    call utl_tmg_start(114,'--ObsThinning')
 456    call thn_csrByLatLonBoxes(obsdat, deltax, deltrad)
 457    call utl_tmg_stop(114)
 458    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 459
 460  end subroutine thn_thinCSR
 461
 462  !--------------------------------------------------------------------------
 463  ! thn_thinScat
 464  !--------------------------------------------------------------------------
 465  subroutine thn_thinScat(obsdat)
 466    !
 467    !:Purpose: Main thinning subroutine for scatterometer winds.
 468    !
 469    implicit none
 470
 471    ! Arguments:
 472    type(struct_obs), intent(inout) :: obsdat
 473
 474    ! Locals:
 475    integer :: nulnam
 476    integer :: fnom, fclos, ierr
 477
 478    ! Namelist variables:
 479    integer :: deltax     ! thinning (dimension of box sides) (in km)
 480    integer :: deltmax    ! temporal thinning resolution (in minutes)
 481
 482    namelist /thin_scat/deltax, deltmax
 483
 484    ! return if no scat obs
 485    if (.not. obs_famExist(obsdat,'SC')) return
 486
 487    ! Default namelist values
 488    deltax = 100
 489    deltmax = 90
 490
 491    ! Read the namelist for Scat observations (if it exists)
 492    if (utl_isNamelistPresent('thin_scat','./flnml')) then
 493      nulnam = 0
 494      ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
 495      if (ierr /= 0) call utl_abort('thn_thinScat: Error opening file flnml')
 496      read(nulnam,nml=thin_scat,iostat=ierr)
 497      if (ierr /= 0) call utl_abort('thn_thinScat: Error reading thin_scat namelist')
 498      if (mmpi_myid == 0) write(*,nml=thin_scat)
 499      ierr = fclos(nulnam)
 500    else
 501      write(*,*)
 502      write(*,*) 'thn_thinScat: Namelist block thin_scat is missing in the namelist.'
 503      write(*,*) '              The default value will be taken.'
 504      if (mmpi_myid == 0) write(*,nml=thin_scat)
 505    end if
 506
 507    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 508    call utl_tmg_start(114,'--ObsThinning')
 509    call thn_scatByLatLonBoxes(obsdat, deltax, deltmax)
 510    call utl_tmg_stop(114)
 511    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 512
 513  end subroutine thn_thinScat
 514
 515  !--------------------------------------------------------------------------
 516  ! thn_thinTovs
 517  !--------------------------------------------------------------------------
 518  subroutine thn_thinTovs(obsdat)
 519    !
 520    !:Purpose: Main thinning subroutine for AMSU and ATMS obs.
 521    !
 522    implicit none
 523
 524    ! Arguments:
 525    type(struct_obs), intent(inout) :: obsdat
 526
 527    ! Locals:
 528    integer :: nulnam
 529    integer :: fnom, fclos, ierr
 530
 531    ! Namelist variables:
 532    integer :: delta    ! thinning (dimension of box sides) (in km)
 533    integer :: deltrad  ! radius around box center for chosen obs (in km)
 534
 535    namelist /thin_tovs/delta, deltrad
 536
 537    ! return if no TOVS obs
 538    if (.not. obs_famExist(obsdat,'TO')) return
 539
 540    ! Default namelist values
 541    delta   = 100
 542    deltrad = 75
 543
 544    ! Read the namelist for TOVS observations (if it exists)
 545    if (utl_isNamelistPresent('thin_tovs','./flnml')) then
 546      nulnam = 0
 547      ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
 548      if (ierr /= 0) call utl_abort('thn_thinTovs: Error opening file flnml')
 549      read(nulnam,nml=thin_tovs,iostat=ierr)
 550      if (ierr /= 0) call utl_abort('thn_thinTovs: Error reading thin_tovs namelist')
 551      if (mmpi_myid == 0) write(*,nml=thin_tovs)
 552      ierr = fclos(nulnam)
 553    else
 554      write(*,*)
 555      write(*,*) 'thn_thinTovs: Namelist block thin_tovs is missing in the namelist.'
 556      write(*,*) '              The default value will be taken.'
 557      if (mmpi_myid == 0) write(*,nml=thin_tovs)
 558    end if
 559
 560    call utl_tmg_start(114,'--ObsThinning')
 561    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 562    call thn_tovsFilt(obsdat, delta, deltrad, codtyp_get_codtyp('amsua'))
 563    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 564    call thn_tovsFilt(obsdat, delta, deltrad, codtyp_get_codtyp('amsub'), &
 565                      codtyp2_opt=codtyp_get_codtyp('mhs'))
 566    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 567    call thn_tovsFilt(obsdat, delta, deltrad, codtyp_get_codtyp('atms'))
 568    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 569    call thn_tovsFilt(obsdat, delta, deltrad, codtyp_get_codtyp('mwhs2'))
 570    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 571    call thn_tovsFilt(obsdat, delta, deltrad, codtyp_get_codtyp('ssmis'))
 572    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 573    call utl_tmg_stop(114)
 574
 575  end subroutine thn_thinTovs
 576
 577  !--------------------------------------------------------------------------
 578  ! thn_thinHyper
 579  !--------------------------------------------------------------------------
 580  subroutine thn_thinHyper(obsdat)
 581    !
 582    !:Purpose: Main thinning subroutine for hyperspectral infrared radiances.
 583    !
 584    implicit none
 585
 586    ! Arguments:
 587    type(struct_obs), intent(inout) :: obsdat
 588
 589    ! Locals:
 590    integer :: nulnam
 591    integer :: fnom, fclos, ierr
 592
 593    ! Namelist variables:
 594    logical :: removeUnCorrected ! indicate if obs without bias correction should be removed
 595    integer :: deltmax           ! time window by bin (from bin center to bin edge) (in minutes)
 596    integer :: deltax            ! thinning (dimension of box sides) (in km)
 597    integer :: deltrad           ! radius around box center for chosen obs (in km)
 598    namelist /thin_hyper/removeUnCorrected, deltmax, deltax, deltrad
 599
 600    ! return if no TOVS obs
 601    if (.not. obs_famExist(obsdat,'TO')) return
 602
 603    ! Default namelist values
 604    removeUnCorrected = .true.
 605    deltmax = 22
 606    deltax  = 150
 607    deltrad = 45
 608
 609    ! Read the namelist for Aladin observations (if it exists)
 610    if (utl_isNamelistPresent('thin_hyper','./flnml')) then
 611      nulnam = 0
 612      ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
 613      if (ierr /= 0) call utl_abort('thn_thinHyper: Error opening file flnml')
 614      read(nulnam,nml=thin_hyper,iostat=ierr)
 615      if (ierr /= 0) call utl_abort('thn_thinHyper: Error reading thin_hyper namelist')
 616      if (mmpi_myid == 0) write(*,nml=thin_hyper)
 617      ierr = fclos(nulnam)
 618    else
 619      write(*,*)
 620      write(*,*) 'thn_thinHyper: Namelist block thin_hyper is missing in the namelist.'
 621      write(*,*) '               The default value will be taken.'
 622      if (mmpi_myid == 0) write(*,nml=thin_hyper)
 623    end if
 624
 625    call utl_tmg_start(114,'--ObsThinning')
 626    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 627    call thn_hyperByLatLonBoxes(obsdat, removeUnCorrected, deltmax, deltax, deltrad, &
 628                                'TO', codtyp_get_codtyp('airs'))
 629    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 630    call thn_hyperByLatLonBoxes(obsdat, removeUnCorrected, deltmax, deltax, deltrad, &
 631                                'TO', codtyp_get_codtyp('iasi'))
 632    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 633    call thn_hyperByLatLonBoxes(obsdat, removeUnCorrected, deltmax, deltax, deltrad, &
 634                                'TO', codtyp_get_codtyp('cris'))
 635    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 636    call thn_hyperByLatLonBoxes(obsdat, removeUnCorrected, deltmax, deltax, deltrad, &
 637                                'TO', codtyp_get_codtyp('crisfsr'))
 638    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 639    call utl_tmg_stop(114)
 640
 641  end subroutine thn_thinHyper
 642
 643!_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
 644!_/
 645!_/ The following methods are intended to be general algorithms that may be
 646!_/ called by any of the observation-type-specific thinning methods.
 647!_/
 648!_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
 649
 650  !--------------------------------------------------------------------------
 651  ! thn_surfaceInTime
 652  !--------------------------------------------------------------------------
 653  subroutine thn_surfaceInTime(obsdat, obsFamily, step, deltmax, &
 654                               useBlackList, considerSHIPstnID)
 655    !
 656    !:Purpose: Original method for thinning surface data in time.
 657    !           Set bit 11 of OBS_FLG on observations that are to be rejected.
 658    !
 659    implicit none
 660
 661    ! Arguments:
 662    type(struct_obs), intent(inout)        :: obsdat
 663    character(len=*), intent(in)           :: obsFamily
 664    real(8),          intent(in)           :: step
 665    integer,          intent(in)           :: deltmax
 666    logical,          intent(in)           :: useBlackList
 667    logical,          intent(in)           :: considerSHIPstnID
 668
 669    ! Local paramters:
 670
 671    ! Drifter removal parameters:
 672    ! Remove incomplete DRIFTER reports (using listEleBadDrifter)?
 673    logical, parameter :: removeBadDrifters = .true.
 674    ! Minimum required number elements (1 is consistent with bextrep in ops)
 675    integer, parameter :: numEleMinBadDrifter = 1
 676    ! List of required elements for codtyp 'drifter' (see ops derivate program)
 677    integer, parameter :: listEleBadDrifter(5) = (/bufr_nepn, bufr_neds, bufr_nefs, bufr_nets, bufr_sst/)
 678
 679    ! Selection parameters:
 680    integer, parameter :: numListCodtyp = 9 ! number of elements in listCodtyp
 681    ! List of codtyps to keep (what about SYNOP mobil? SA+SYNOP?)
 682    character(len=13), parameter :: listCodtypName(numListCodtyp) = &
 683                                    (/'synopnonauto', 'asynopauto', 'shipnonauto', 'ashipauto', &
 684                                      'drifter', 'saswobnonauto', 'saswobauto', 'metar', 'satob'/)
 685    ! Codtyps to which list_ele_select will be applied
 686    integer, parameter :: numListCodtypSelect = 3
 687    character(len=13), parameter :: listCodtypNameSelect(numListCodtypSelect) = &
 688                                    (/'metar', 'saswobnonauto', 'saswobauto'/)
 689    ! Elements to select (flags for all other elements will have bit 11 set)
 690    integer, parameter :: listEleSelect(14) = (/bufr_suWindSpeed, bufr_neps, bufr_nepn, bufr_neds, &
 691                                                bufr_nefs, bufr_neus, bufr_nevs, bufr_nets, &
 692                                                bufr_dewPoint2m, bufr_ness, bufr_vis, &
 693                                                bufr_logVis, bufr_gust, bufr_sst/) 
 694    ! BlackList parameters:
 695    character(len=*), parameter :: blackListFileName = 'blacklist_sf'
 696    character(len=6), parameter :: blacklistMode = 'normal' 
 697    integer, parameter :: numColBlacklist = 5  ! number of columns in blacklist file
 698    integer, parameter :: numEleBlacklist = 11 ! number of elements in listEleBlacklist
 699    integer, parameter :: listEleBlacklist(numEleBlacklist) = (/bufr_neps, bufr_neps, bufr_nepn, &
 700                                                                bufr_nepn, bufr_neds, bufr_nefs, &
 701                                                                bufr_neus, bufr_nevs, bufr_nets, &
 702                                                                bufr_dewPoint2m, bufr_ness/)
 703    integer, parameter :: listColBlackList(numEleBlacklist) = &
 704         (/     1,     2,     1,     2,     5,     5,     5,     5,     3,     4,     4 /)
 705
 706    ! Locals:
 707    integer :: countObsIn, countObsInMpi, countObsOut
 708    integer :: countObsInAllMpi(mmpi_nprocs), countObsInMyOffset
 709    integer :: numElements, codtyp, obsDateStamp, numStep
 710    integer :: listIndex, obsIndex, obsIndex2, headerIndex, bodyIndex, procIndex
 711    integer :: ierr, istat, nulfile, numRowBlacklist
 712    integer :: elemIndex, rowIndex, colIndex, obsFlag, obsVarNo
 713    integer :: rowBlackList(numColBlacklist) ! blacklist work array
 714    integer :: countObsInPerCodtyp(numListCodtyp), countObsOutPerCodtyp(numListCodtyp)
 715    integer :: numEleInPerCodtyp(numListCodtyp), numEleOutPerCodtyp(numListCodtyp)
 716    integer :: numBit8InPerCodtyp(numListCodtyp), numBit8OutPerCodtyp(numListCodtyp)
 717    integer :: numBit11InPerCodtyp(numListCodtyp), numBit11OutPerCodtyp(numListCodtyp)
 718    integer :: numBit8In, numBit8Out, numBit11In, numBit11Out
 719    integer :: numRemovedCodtyp, numRemovedCodTypPriority
 720    integer :: numRemovedTime, numRemovedDelt
 721    integer :: numEleIn, numEleOut, numRepeat1, numRepeat2, numRemovedDrifter
 722    integer, allocatable :: dataBlacklist(:,:) ! blacklist data matrix
 723    integer, allocatable :: obsLon(:), obsLat(:), obsDate(:)
 724    integer, allocatable :: obsLonMpi(:), obsLatMpi(:)
 725    integer, allocatable :: obsTime(:), obsStepIndex(:), obsStepIndexMpi(:)
 726    integer, allocatable :: obsCodtypIndex(:), obsDelT(:)
 727    integer, allocatable :: obsCodtypIndexMpi(:), obsDelTMpi(:)
 728    real(8) :: obsStepIndex_r8, deltaHours
 729    logical, allocatable :: valid(:), validMpi(:)
 730    character(len=9), allocatable :: obsStnid(:), obsStnidMpi(:), stnidBlacklist(:)
 731    integer :: listCodtypSelect(numListCodtypSelect)
 732    integer, external :: newdate
 733
 734    ! Check if any observations to be treated
 735    countObsIn = 0
 736    call obs_set_current_header_list(obsdat, obsFamily)
 737    HEADER0: do
 738      headerIndex = obs_getHeaderIndex(obsdat)      
 739      if (headerIndex < 0) exit HEADER0
 740      codtyp = obs_headElem_i(obsdat, OBS_ITY, headerIndex)
 741      if (codtyp /= codtyp_get_codtyp('satob')) countObsIn = countObsIn + 1
 742    end do HEADER0
 743
 744    call rpn_comm_allReduce(countObsIn, countObsInMpi, 1, 'mpi_integer', 'mpi_sum', 'grid', ierr)
 745    write(*,*)
 746    if (countObsInMpi == 0) then
 747      write(*,*) 'thn_surfaceInTime: no surface observations present'
 748      return
 749    else
 750      write(*,*) 'thn_surfaceInTime: countObs initial = ', countObsIn, countObsInMpi
 751    end if
 752    write(*,*)
 753
 754    ! Compute number of time steps in the window for thinning
 755    numStep = 2 * nint(((tim_windowsize - step) / 2.d0) / step) + 1
 756    write(*,*) 'thn_surfaceInTime: step, numStep = ', real(step), numStep
 757    write(*,*)
 758 
 759    ! Print some values to the listing
 760    write(*,*) 'Codtyps to which selection will be applied:'
 761    do listIndex = 1, numListCodtypSelect
 762      listCodtypSelect(listIndex) = codtyp_get_codtyp(listCodtypNameSelect(listIndex))
 763      write(*,*) listCodtypNameSelect(listIndex),': ', listCodtypSelect(listIndex)
 764    end do
 765    write(*,*) 'Elements to select from above codtyps:'
 766    do listIndex = 1, size(listEleSelect)
 767      write(*,*) listEleSelect(listIndex)
 768    end do
 769    write(*,*) 'Remove incomplete DRIFTER reports: ', removeBadDrifters
 770    write(*,*)
 771
 772    ! Allocate arrays
 773    allocate(valid(countObsIn))
 774    allocate(obsCodtypIndex(countObsIn))
 775    allocate(obsLon(countObsIn))
 776    allocate(obsLat(countObsIn))
 777    allocate(obsDate(countObsIn))
 778    allocate(obsTime(countObsIn))
 779    allocate(obsDelT(countObsIn))
 780    allocate(obsStepIndex(countObsIn))
 781    allocate(obsStnid(countObsIn))
 782
 783    ! Initialize dynamic arrays
 784    valid(:) =    .true.                     ! array which keeps track of which reports to keep
 785    obsCodtypIndex(:) = MPC_missingValue_INT ! codtyp index array (MPC_missingValue_INT means non-existent)
 786    obsLon(:)         = 0                    ! longitude corresponding to each report
 787    obsLat(:)         = 0                    ! latitude corresponding to each report
 788    obsDate(:)        = 0                    ! DATE yyyymmdd corresponding to each report
 789    obsTime(:)        = 0                    ! time hhmm corresponding to each report
 790    obsDelT(:)        = MPC_missingValue_INT ! delta t array (departure from nearest bin time)
 791    obsStepIndex(:)   = 0                    ! temporal bin corresponding to each report
 792    obsStnid(:)       = ''                   ! stnid corresponding to each report
 793
 794    ! Initialize counters and counter arrays
 795    numEleIn                 = 0 ! number of elements in input file
 796    numBit8In                = 0 ! number of input elements flagged (blacklist)
 797    numBit11In               = 0 ! number of input elements flagged (selection)
 798    numEleOut                = 0 ! number of elements in output file
 799    numBit8Out               = 0 ! number of output elements flagged (blacklist)
 800    numBit11Out              = 0 ! number of output elements flagged (selection)
 801    numRemovedCodtyp         = 0 ! number of obs removed due to network
 802    numRemovedTime           = 0 ! number of obs removed (desired time window)
 803    numRemovedCodTypPriority = 0 ! number of obs removed due to codtyp
 804    numRemovedDelt           = 0 ! number of obs removed due to delta t
 805    numRemovedDrifter        = 0 ! number of incomplete drifter reports removed
 806    numRepeat1               = 0 ! number of obs same lon/lat/date/time
 807    numRepeat2               = 0 ! number of obs same lon/lat/date/time/codtyp
 808
 809    countObsInPerCodtyp(:)  = 0 ! number of input reports for each codtyp
 810    numEleInPerCodtyp(:)    = 0 ! number of input elements for each codtyp
 811    numBit8InPerCodtyp(:)   = 0 ! number of input flags bit 8 for each codtyp
 812    numBit11InPerCodtyp(:)  = 0 ! number of input flags bit 11 for each codtyp
 813    countObsOutPerCodtyp(:) = 0 ! number of output reports for each codtyp
 814    numEleOutPerCodtyp(:)   = 0 ! number of output elements for each codtyp
 815    numBit8OutPerCodtyp(:)  = 0 ! number of output flags bit 8 for each codtyp
 816    numBit11OutPerCodtyp(:) = 0 ! number of output flags bit 11 for each codtyp
 817
 818    ! Extract needed information from obsSpaceData
 819    obsIndex = 0
 820    call obs_set_current_header_list(obsdat, obsFamily)
 821    HEADER1: do
 822      headerIndex = obs_getHeaderIndex(obsdat)
 823      if (headerIndex < 0) exit HEADER1
 824      codtyp = obs_headElem_i(obsdat, OBS_ITY, headerIndex)
 825      if (codtyp /= codtyp_get_codtyp('satob')) then
 826        obsIndex = obsIndex + 1
 827      else
 828        cycle HEADER1
 829      end if
 830
 831      ! Get index in codtyp list
 832      do listIndex = 1, numListCodtyp
 833        if (codtyp == codtyp_get_codtyp(listCodtypName(listIndex))) then
 834          obsCodtypIndex(obsIndex) = listIndex
 835        end if
 836      end do
 837
 838      ! Remove observation if not in codtyp list
 839      if (obsCodtypIndex(obsIndex) == MPC_missingValue_INT) then
 840        valid(obsIndex) = .false.
 841        numRemovedCodtyp = numRemovedCodtyp + 1
 842        cycle HEADER1
 843      end if
 844
 845      ! Check if DRIFTER reports contain required element(s), otherwise cycle
 846      if ((codtyp == codtyp_get_codtyp('drifter')) .and. removeBadDrifters) then
 847        numElements = 0
 848        call obs_set_current_body_list(obsdat, headerIndex)
 849        BODY1: do 
 850          bodyIndex = obs_getBodyIndex(obsdat)
 851          if (bodyIndex < 0) exit BODY1
 852          obsVarNo = obs_bodyElem_i(obsdat, obs_vnm, bodyIndex)
 853          if (any(listEleBadDrifter(:) == obsVarNo)) numElements = numElements + 1
 854        enddo BODY1
 855        if (numElements < numEleMinBadDrifter) then
 856          valid(obsIndex) = .false.
 857          numRemovedDrifter = numRemovedDrifter + 1
 858          cycle HEADER1
 859        end if
 860      end if
 861
 862      ! Count the number of elements
 863      numElements = 0
 864      call obs_set_current_body_list(obsdat, headerIndex)
 865      BODY2: do 
 866        bodyIndex = obs_getBodyIndex(obsdat)
 867        if (bodyIndex < 0) exit BODY2
 868
 869        obsVarNo = obs_bodyElem_i(obsdat, obs_vnm, bodyIndex)
 870        if (obsVarNo == -1) cycle BODY2
 871
 872        numElements = numElements + 1
 873
 874        obsFlag = obs_bodyElem_i(obsdat, obs_flg, bodyIndex)
 875
 876        ! Count input flags with bit 8 set
 877        if (btest(obsFlag, 8)) then
 878          numBit8InPerCodtyp(obsCodtypIndex(obsIndex)) = &
 879               numBit8InPerCodtyp(obsCodtypIndex(obsIndex)) + 1
 880          numBit8In = numBit8In + 1
 881        end if
 882
 883        ! Count input flags with bit 11 set
 884        if (btest(obsFlag, 11)) then
 885          numBit11InPerCodtyp(obsCodtypIndex(obsIndex)) = &
 886               numBit11InPerCodtyp(obsCodtypIndex(obsIndex)) + 1
 887          numBit11In = numBit11In + 1
 888        end if
 889
 890      end do BODY2
 891
 892      numEleIn = numEleIn + numElements
 893
 894      ! Counts per codtyp
 895      listIndex = obsCodtypIndex(obsIndex)
 896      countObsInPerCodtyp(listIndex) = countObsInPerCodtyp(listIndex) + 1
 897      numEleInPerCodtyp(listIndex) = numEleInPerCodtyp(listIndex) + numElements
 898
 899      obsStnid(obsIndex) = obs_elem_c(obsdat, 'STID', headerIndex)
 900
 901      obsLon(obsIndex) = nint(100.0 * MPC_DEGREES_PER_RADIAN_R8 * &
 902                              obs_headElem_r(obsdat, obs_lon, headerIndex))
 903      obsLat(obsIndex) = nint(100.0 * MPC_DEGREES_PER_RADIAN_R8 * &
 904                              obs_headElem_r(obsdat, obs_lat, headerIndex)) + 9000
 905
 906      obsDate(obsIndex) = obs_headElem_i(obsdat, obs_dat, headerIndex)
 907      obsTime(obsIndex) = obs_headElem_i(obsdat, obs_etm, headerIndex)
 908
 909      call tim_getStepObsIndex(obsStepIndex_r8, tim_getDatestamp(), &
 910                               obsDate(obsIndex), obsTime(obsIndex), numStep)
 911      obsStepIndex(obsIndex) = nint(obsStepIndex_r8)
 912      if (numStep > 1) then
 913        obsDelT(obsIndex) = nint(60.0 * step * (obsStepIndex_r8 - real(obsStepIndex(obsIndex))))
 914      else
 915        ierr = newdate(obsDateStamp, obsDate(obsIndex), obsTime(obsIndex) * 10000, 3)
 916	! Difference (in hours) between obs time
 917        call difdatr(obsDateStamp, tim_getDateStamp(), deltaHours)
 918        obsDelT(obsIndex) = nint(60.0 * deltaHours)
 919      end if
 920
 921      ! Reject if time difference larger than deltmax
 922      if (abs(obsDelT(obsIndex)) > deltmax) then
 923        valid(obsIndex) = .false.
 924        numRemovedTime = numRemovedTime + 1
 925      endif
 926
 927    end do HEADER1
 928
 929    call utl_allReduce(numRemovedDrifter)
 930    call utl_allReduce(numRemovedTime)
 931    call utl_allReduce(numRemovedCodtyp)
 932
 933    ! Read blacklist file
 934    if (useBlackList) then
 935      write(*,*) 'Opening blacklist file'
 936      numRowBlacklist = 0
 937      nulfile = 0
 938      open (unit = nulfile, file = blackListFileName, status = 'OLD', iostat = ierr)
 939      if (ierr /= 0) then
 940        write(*,*) 'Cannot open blacklist file ', trim(blackListFileName)
 941        call utl_abort('thn_surfaceInTime')
 942      end if
 943      read(nulfile, iostat = istat, fmt = '(i6)') numRowBlacklist
 944      write(*,*) 'thn_surfaceInTime: Number of stations in blacklist: ', numRowBlacklist
 945
 946      allocate(stnidBlacklist(numRowBlacklist))
 947      allocate(dataBlacklist(numRowBlacklist, numEleBlacklist))
 948      stnidBlacklist(:) = '' ! array of stnid values in blacklist file
 949      dataBlacklist(:,:) = 0 ! blacklist matrix for stnids and elements
 950
 951      do rowIndex = 1, numRowBlacklist
 952        read(nulfile, iostat = istat, fmt = '(x,a8,x,5(x,i1))') &
 953             stnidBlacklist(rowIndex), &
 954             (rowBlackList(colIndex), colIndex = 1, numColBlacklist)
 955        do elemIndex = 1, numEleBlacklist
 956          if ((blacklistMode == 'severe') .or. &
 957              (rowBlackList(listColBlackList(elemIndex))==1)) then
 958            dataBlacklist(rowIndex, elemIndex) = 1
 959          end if
 960        end do
 961      end do
 962      write(*,*) 'thn_surfaceInTime: Closing blacklist file'
 963      write(*,*)
 964      close (unit = nulfile)
 965    end if
 966
 967    ! Gather array information over all mpi tasks
 968    allocate(validMpi(countObsInMpi))
 969    allocate(obsCodtypIndexMpi(countObsInMpi))
 970    allocate(obsLonMpi(countObsInMpi))
 971    allocate(obsLatMpi(countObsInMpi))
 972    allocate(obsDelTMpi(countObsInMpi))
 973    allocate(obsStepIndexMpi(countObsInMpi))
 974    allocate(obsStnidMpi(countObsInMpi))
 975
 976    call intArrayToMpi(obsLon, obsLonMpi)
 977    call intArrayToMpi(obsLat, obsLatMpi)
 978    call intArrayToMpi(obsCodtypIndex, obsCodtypIndexMpi)
 979    call intArrayToMpi(obsStepIndex, obsStepIndexMpi)
 980    call intArrayToMpi(obsDelT, obsDelTMpi)
 981    call logicalArrayToMpi(valid, validMpi)
 982    call stringArrayToMpi(obsStnid, obsStnidMpi)
 983    
 984    ! Apply the thinning algorithm
 985    do obsIndex = 1, countObsInMpi
 986      
 987      ! If current report OK so far
 988      if (validMpi(obsIndex)) then
 989        ! Loop over previously-read reports
 990        do obsIndex2 = 1, obsIndex - 1
 991          ! If other report OK so far and both reports in same bin
 992          if ( validMpi(obsIndex2) .and. &
 993               (obsStepIndexMpi(obsIndex) == obsStepIndexMpi(obsIndex2))) then
 994            ! If reports are spatially colocated or have same stnid
 995            if ( ( (obsLonMpi(obsIndex) == obsLonMpi(obsIndex2)) .and. &
 996                   (obsLatMpi(obsIndex) == obsLatMpi(obsIndex2)) ) .or. &
 997                 ( considerSHIPstnID .and. &
 998                   (obsStnidMpi(obsIndex) == obsStnidMpi(obsIndex2)))) then
 999              ! If both reports have same codtyp
1000              if (obsCodtypIndexMpi(obsIndex) == obsCodtypIndexMpi(obsIndex2)) then
1001                ! If current report closer to bin time
1002                if (abs(obsDelTMpi(obsIndex)) < abs(obsDelTMpi(obsIndex2))) then
1003                  ! Reject other report
1004                  validMpi(obsIndex2) = .false.
1005                  numRemovedDelt = numRemovedDelt + 1
1006                else if (abs(obsDelTMpi(obsIndex)) > abs(obsDelTMpi(obsIndex2))) then
1007                  ! other report closer to bin time, so reject current report
1008                  validMpi(obsIndex) = .false.
1009                  numRemovedDelt = numRemovedDelt + 1
1010                else if (obsDelTMpi(obsIndex) >= obsDelTMpi(obsIndex2)) then
1011                  ! both reports equally far from bin time
1012                  ! If other report not more recent then reject it
1013                  validMpi(obsIndex2) = .false.
1014                  if (obsDelTMpi(obsIndex) == obsDelTMpi(obsIndex2)) then
1015                    numRepeat2 = numRepeat2 + 1
1016                  else
1017                    numRemovedDelt = numRemovedDelt + 1
1018                  end if
1019                else
1020                  ! current report less recent, reject it
1021                  validMpi(obsIndex) = .false.
1022                  numRemovedDelt = numRemovedDelt + 1
1023                end if ! delta t
1024              else
1025                ! Reports do not have same codtyp
1026                ! If current report has higher codtyp precedence
1027                if (obsCodtypIndexMpi(obsIndex) < obsCodtypIndexMpi(obsIndex2)) then
1028                  ! Reject other report
1029                  validMpi(obsIndex2) = .false.
1030                else
1031                  ! Other report has higher codtyp precedence, so reject current report
1032                  validMpi(obsIndex) = .false.
1033                end if
1034                numRemovedCodTypPriority = numRemovedCodTypPriority + 1
1035                if (obsDelTMpi(obsIndex) == obsDelTMpi(obsIndex2)) then
1036                  numRepeat1 = numRepeat1 + 1
1037                end if
1038              end if ! codtyp
1039            end if ! lon, lat, ibin
1040          end if ! other report OK
1041        end do ! obsIndex2
1042      end if ! current report OK
1043
1044    end do ! obsIndex
1045
1046    ! Transfer mpi global array 'valid' to local array
1047    call rpn_comm_allGather(countObsIn, 1, 'mpi_integer', countObsInAllMpi, 1, 'mpi_integer', 'GRID', ierr)
1048
1049    countObsInMyOffset = 0
1050    do procIndex = 1, mmpi_myid
1051      countObsInMyOffset = countObsInMyOffset + countObsInAllMpi(procIndex)
1052    end do
1053    do obsIndex = 1, countObsIn
1054      valid(obsIndex) = validMpi(obsIndex + countObsInMyOffset)
1055    end do
1056
1057    ! Do counts of kepts observations
1058    obsIndex = 0
1059    countObsOut = 0
1060    call obs_set_current_header_list(obsdat, obsFamily)
1061    HEADER2: do
1062      headerIndex = obs_getHeaderIndex(obsdat)
1063      if (headerIndex < 0) exit HEADER2
1064      codtyp = obs_headElem_i(obsdat, OBS_ITY, headerIndex)
1065      if (codtyp /= codtyp_get_codtyp('satob')) then
1066        obsIndex = obsIndex + 1
1067      else
1068        cycle HEADER2
1069      end if 
1070
1071      ! If rejected, set bit 11 for all data flags
1072      if (.not. valid(obsIndex)) then
1073        call obs_set_current_body_list(obsdat, headerIndex)
1074        BODY3: do
1075          bodyIndex = obs_getBodyIndex(obsdat)
1076          if (bodyIndex < 0) exit BODY3
1077          obsFlag  = obs_bodyElem_i(obsdat, obs_flg, bodyIndex)
1078          call obs_bodySet_i(obsdat, obs_flg, bodyIndex, ibset(obsFlag,11))
1079        end do BODY3
1080        cycle HEADER2
1081      end if
1082
1083      ! Count the number of elements
1084      numElements = 0
1085      call obs_set_current_body_list(obsdat, headerIndex)
1086      BODY4: do
1087        bodyIndex = obs_getBodyIndex(obsdat)
1088        if (bodyIndex < 0) exit BODY4
1089
1090        obsVarNo = obs_bodyElem_i(obsdat, obs_vnm, bodyIndex)
1091        if (obsVarNo == -1) cycle BODY4
1092
1093        numElements = numElements + 1
1094
1095        ! Set bit 8 according to blacklist, if blacklist present
1096        if (useBlackList) then
1097          ! Traverse rows of blacklist matrix
1098          do rowIndex = 1, numRowBlacklist
1099            ! If stnid found in blacklist, flag elements as appropriate
1100            if (obsStnid(obsIndex) == stnidBlacklist(rowIndex)) then
1101              ! Traverse columns of blacklist matrix
1102              do elemIndex = 1, numEleBlacklist
1103                ! If element is to be blacklisted, set bit 8
1104                if (obsVarNo == listEleBlacklist(elemIndex) .and. &
1105                    dataBlacklist(rowIndex,elemIndex) == 1) then
1106                  obsFlag  = obs_bodyElem_i(obsdat, obs_flg, bodyIndex)
1107                  call obs_bodySet_i(obsdat, obs_flg, bodyIndex, ibset(obsFlag,8))
1108                end if
1109              end do ! elemIndex
1110            end if ! stnid
1111          end do ! rowIndex
1112        end if ! useBlackList
1113
1114        ! Set bit 11 according to requested codtyps and elements
1115        if (any(listCodtypSelect(:) == codtyp)) then
1116          ! If current element not in select list, set bit 11
1117          if (.not. any(listEleSelect(:) == obsVarNo)) then
1118            obsFlag  = obs_bodyElem_i(obsdat, obs_flg, bodyIndex)
1119            write(*,*) 'Setting bit 11 for codtyp, elem = ', codtyp, obsVarNo
1120            call obs_bodySet_i(obsdat, obs_flg, bodyIndex, ibset(obsFlag,11))
1121          end if
1122        end if
1123
1124        obsFlag  = obs_bodyElem_i(obsdat, obs_flg, bodyIndex)
1125
1126        ! Count output flags with bit 8 set
1127        if (btest(obsFlag, 8)) then
1128          numBit8OutPerCodtyp(obsCodtypIndex(obsIndex)) = numBit8OutPerCodtyp(obsCodtypIndex(obsIndex)) + 1
1129          numBit8Out = numBit8Out + 1
1130        end if
1131
1132        ! Count output flags with bit 11 set
1133        if (btest(obsFlag, 11)) then
1134          numBit11OutPerCodtyp(obsCodtypIndex(obsIndex)) = numBit11OutPerCodtyp(obsCodtypIndex(obsIndex)) + 1
1135          numBit11Out = numBit11Out + 1
1136        end if
1137
1138      end do BODY4
1139
1140      numEleOut = numEleOut + numElements
1141      countObsOut = countObsOut + 1
1142
1143      ! Counts per codtyp
1144      listIndex = obsCodtypIndex(obsIndex)
1145      countObsOutPerCodtyp(listIndex) = countObsOutPerCodtyp(listIndex) + 1
1146      numEleOutPerCodtyp(listIndex) = numEleOutPerCodtyp(listIndex) + numElements
1147
1148    end do HEADER2
1149
1150    ! Output statistics to screen
1151
1152    ! numRepeat1 should include the case where codtyps are the same
1153    numRepeat1 = numRepeat1 + numRepeat2
1154
1155    write(*,'(a)') ' Number of reports in input file'
1156    do listIndex = 1, numListCodtyp
1157      call utl_allReduce(countObsInPerCodtyp(listIndex))
1158      write(*,'(i4,3a,i7)') codtyp_get_codtyp(listCodtypName(listIndex)), ' (', &
1159           listCodtypName(listIndex), '): ', countObsInPerCodtyp(listIndex)
1160    end do
1161
1162    write(*,*)
1163    write(*,'(a)') ' Number of elements in input file'
1164    do listIndex = 1, numListCodtyp
1165      call utl_allReduce(numEleInPerCodtyp(listIndex))
1166      call utl_allReduce(numBit8InPerCodtyp(listIndex))
1167      call utl_allReduce(numBit11InPerCodtyp(listIndex))
1168      write(*,'(i4,3a,i7,a,i7,a,i7,a)') codtyp_get_codtyp(listCodtypName(listIndex)), ' (', &
1169           listCodtypName(listIndex), '): ', numEleInPerCodtyp(listIndex), ' (', &
1170           numBit8InPerCodtyp(listIndex), ' bit 8, ', &
1171           numBit11InPerCodtyp(listIndex), ' bit 11)'
1172    end do
1173
1174    write(*,*)
1175    write(*,'(a)') ' Number of reports in output file'
1176    do listIndex = 1, numListCodtyp
1177      call utl_allReduce(countObsOutPerCodtyp(listIndex))
1178      write(*,'(i4,3a,i7)') codtyp_get_codtyp(listCodtypName(listIndex)), ' (', &
1179           listCodtypName(listIndex), '): ', countObsOutPerCodtyp(listIndex)
1180    end do
1181
1182    write(*,*)
1183    write(*,'(a)') ' Number of elements in output file'
1184    do listIndex = 1, numListCodtyp
1185      call utl_allReduce(numEleOutPerCodtyp(listIndex))
1186      call utl_allReduce(numBit8OutPerCodtyp(listIndex))
1187      call utl_allReduce(numBit11OutPerCodtyp(listIndex))
1188      write(*,'(i4,3a,i7,a,i7,a,i7,a)') codtyp_get_codtyp(listCodtypName(listIndex)), ' (', &
1189           listCodtypName(listIndex), '): ', numEleOutPerCodtyp(listIndex), ' (', &
1190           numBit8OutPerCodtyp(listIndex), ' bit 8, ', &
1191           numBit11OutPerCodtyp(listIndex), ' bit 11)'
1192    end do
1193
1194    write(*,*)
1195    call utl_allReduce(countObsIn)
1196    call utl_allReduce(countObsOut)
1197    write(*,'(a,i12)') 'Total number of reports in input file:   ', countObsIn
1198    write(*,'(a,i12)') 'Total number of reports in output file:  ', countObsOut
1199    call utl_allReduce(numEleIn)
1200    call utl_allReduce(numBit8In)
1201    call utl_allReduce(numBit11In)
1202    call utl_allReduce(numEleOut)
1203    call utl_allReduce(numBit8Out)
1204    call utl_allReduce(numBit11Out)
1205    write(*,'(a,i7,a,i7,a,i7,a)') 'Total number of elements in input file:  ', numEleIn, &
1206          ' (', numBit8In, ' bit 8, ', numBit11In, ' bit 11)'
1207    write(*,'(a,i7,a,i7,a,i7,a)') 'Total number of elements in output file: ', numEleOut, &
1208          ' (', numBit8Out, ' bit 8, ', numBit11Out, ' bit 11)'
1209    write(*,*)
1210    write(*,'(a,i7)') 'Number of repeated reports (lon/lat/date/time):        ', numRepeat1
1211    write(*,'(a,f6.2)') 'Above count as a percentage of total reports in:        ', 100.0 * numRepeat1 / countObsIn
1212    write(*,'(a,i7)') 'Number of repeated reports (lon/lat/date/time/codtyp): ', numRepeat2
1213    write(*,'(a,f6.2)') 'Above count as a percentage of total reports in:        ', 100.00 * numRepeat2 / countObsIn
1214    write(*,*)
1215    write(*,'(a,i7)') 'Number of reports removed due to codtyp:               ', numRemovedCodtyp
1216    write(*,'(a,i7)') 'Number of reports removed due to time:                 ', numRemovedTime
1217    write(*,'(a,i7)') 'Number of reports removed using codtyp precedence:     ', numRemovedCodTypPriority
1218    write(*,'(a,i7)') 'Number of reports removed using delta t:               ', numRemovedDelt
1219    write(*,'(a,i7)') 'Number of incomplete drifter reports removed:          ', numRemovedDrifter
1220    write(*,*)
1221 
1222  end subroutine thn_surfaceInTime
1223  
1224  !--------------------------------------------------------------------------
1225  ! thn_gpsroVertical
1226  !--------------------------------------------------------------------------
1227  subroutine thn_gpsroVertical(obsdat, heightMin, heightMax, heightSpacing, &
1228                               gpsroVarNo)
1229    !
1230    !:Purpose: Original method for thinning GPSRO data by vertical distance.
1231    !           Set bit 11 of OBS_FLG on observations that are to be rejected.
1232    !
1233    implicit none
1234
1235    ! Arguments:
1236    type(struct_obs), intent(inout) :: obsdat
1237    real(8),          intent(in)    :: heightMin
1238    real(8),          intent(in)    :: heightMax
1239    real(8),          intent(in)    :: heightSpacing
1240    integer,          intent(in)    :: gpsroVarNo
1241
1242    ! Locals:
1243    integer :: countObs, countObsMpi
1244    integer :: countObsReject, countObsRejectMpi, countObsTotal, countObsTotalMpi
1245    integer :: headerIndex, bodyIndex, ierr
1246    integer :: numLev
1247    integer :: levIndex, obsVarNo, obsFlag
1248    real(8) :: nextHeightMin
1249    logical :: rejectObs
1250    integer, allocatable :: bodyIndexList(:)
1251    real(8), allocatable :: obsHeights(:)
1252
1253    write(*,*)
1254    write(*,*) 'thn_gpsroVertical: Starting'
1255    write(*,*)
1256
1257    ! Check if any observations to be treated
1258    countObs = 0
1259    call obs_set_current_header_list(obsdat,'RO')
1260    HEADER0: do
1261      headerIndex = obs_getHeaderIndex(obsdat)
1262      if (headerIndex < 0) exit HEADER0
1263      countObs = countObs + 1
1264    end do HEADER0
1265
1266    call rpn_comm_allReduce(countObs, countObsMpi, 1, 'mpi_integer', &
1267                            'mpi_sum','grid',ierr)
1268    if (countObsMpi == 0) then
1269      write(*,*) 'thn_gpsroVertical: no gpsro observations present'
1270      return
1271    end if
1272
1273    countObsTotal  = 0
1274    countObsReject = 0
1275    call obs_set_current_header_list(obsdat,'RO')
1276    HEADER1: do
1277      headerIndex = obs_getHeaderIndex(obsdat)
1278      if (headerIndex < 0) exit HEADER1
1279
1280      ! count number of levels for this profile
1281      numLev = 0
1282      call obs_set_current_body_list(obsdat, headerIndex)
1283      BODY1: do 
1284        bodyIndex = obs_getBodyIndex(obsdat)
1285        if (bodyIndex < 0) exit BODY1
1286
1287        obsVarNo = obs_bodyElem_i(obsdat, OBS_VNM, bodyIndex)
1288        if (obsVarNo /= gpsroVarNo) cycle BODY1
1289
1290        numLev = numLev + 1
1291        countObsTotal = countObsTotal + 1
1292
1293      end do BODY1
1294
1295      allocate(obsHeights(numLev))
1296      allocate(bodyIndexList(numLev))
1297
1298      ! extract altitudes for this profile
1299      levIndex = 0
1300      call obs_set_current_body_list(obsdat, headerIndex)
1301      BODY2: do 
1302        bodyIndex = obs_getBodyIndex(obsdat)
1303        if (bodyIndex < 0) exit BODY2
1304        
1305        obsVarNo = obs_bodyElem_i(obsdat, OBS_VNM, bodyIndex)
1306        if (obsVarNo /= gpsroVarNo) cycle BODY2
1307
1308        levIndex = levIndex + 1
1309        obsHeights(levIndex) = obs_bodyElem_r(obsdat, OBS_PPP, bodyIndex)
1310        bodyIndexList(levIndex) = bodyIndex
1311
1312      end do BODY2
1313
1314      ! ensure altitudes are in ascending order
1315      call thn_QsortReal8(obsHeights,bodyIndexList)
1316
1317      ! apply vertical thinning
1318      nextHeightMin = heightMin
1319      LEVELS: do levIndex = 1, numLev
1320        
1321        if ( obsHeights(levIndex) >= nextHeightMin .and. &
1322             obsHeights(levIndex) < heightMax ) then
1323          nextHeightMin = obsHeights(levIndex) + heightSpacing
1324          rejectObs = .false.
1325        else
1326          rejectObs = .true.
1327        end if
1328
1329        if (rejectObs) then
1330          bodyIndex = bodyIndexList(levIndex)
1331          obsFlag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
1332          call obs_bodySet_i(obsdat, OBS_FLG, bodyIndex, ibset(obsFlag,11))
1333          countObsReject = countObsReject + 1
1334        end if
1335        
1336      end do LEVELS
1337
1338      deallocate(obsHeights)
1339      deallocate(bodyIndexList)
1340
1341    end do HEADER1
1342
1343    call rpn_comm_allReduce(countObsTotal, countObsTotalMpi, 1, 'mpi_integer', &
1344                            'mpi_sum','grid',ierr)
1345    call rpn_comm_allReduce(countObsReject, countObsRejectMpi, 1, 'mpi_integer', &
1346                            'mpi_sum','grid',ierr)
1347    write(*,*)' Number of GPS-RO elements, total     --->', countObsTotalMpi
1348    write(*,*)' Number of GPS-RO elements, rejected  --->', countObsRejectMpi
1349    write(*,*)' Number of GPS-RO elements, kept      --->', countObsTotalMpi - &
1350                                                            countObsRejectMpi
1351
1352    write(*,*)
1353    write(*,*) 'thn_gpsroVertical: Finished'
1354    write(*,*)
1355
1356  end subroutine thn_gpsroVertical
1357
1358  !--------------------------------------------------------------------------
1359  ! thn_radiosonde
1360  !--------------------------------------------------------------------------
1361  subroutine thn_radiosonde(obsdat, verticalThinningES, ecmwfRejetsES, toleranceFactor)
1362    !
1363    !:Purpose: Original method for thinning radiosonde data vertically.
1364    !           We assume that each vertical level is stored in obsSpaceData
1365    !           with a separate headerIndex. That is, the 4D representation.
1366    !           Set bit 11 of OBS_FLG on observations that are to be rejected.
1367    !
1368    implicit none
1369
1370    ! Arguments:
1371    type(struct_obs), intent(inout) :: obsdat
1372    logical,          intent(in)    :: verticalThinningES
1373    logical,          intent(in)    :: ecmwfRejetsES
1374    real(4),          intent(in)    :: toleranceFactor
1375
1376    ! Locals:
1377    type(struct_hco), pointer :: hco_sfc
1378    type(struct_vco), pointer :: vco_sfc
1379    type(struct_gsv)          :: stateVectorPsfc
1380    integer :: fnom, fclos, ezgdef, ezsint, ezdefset, ezsetopt
1381    integer :: ierr, nulnam, numLevStn, numLevStnMpi, countLevel, numLevStnMax
1382    integer :: numStation, numStationMpi, stationIndex, stationIndexMpi, lastProfileIndex
1383    integer :: profileIndex, headerIndex, bodyIndex, levIndex, stepIndex, varIndex
1384    integer :: levStnIndex, levStnIndexMpi, obsFlag
1385    integer :: ig1obs, ig2obs, ig3obs, ig4obs, obsGridID
1386    real(4) :: obsValue, obsOmp, obsStepIndex
1387    real(4) :: zig1, zig2, zig3, zig4, zpresa, zpresb
1388    real(8) :: obsStepIndex_r8
1389    integer, allocatable :: obsLevOffset(:), obsType(:), obsHeadDate(:), obsHeadTime(:)
1390    integer, allocatable :: obsDate(:), obsTime(:), obsLaunchTime(:), stationFlags(:)
1391    integer, allocatable :: trajFlags(:,:), obsFlags(:,:)
1392    integer, allocatable :: obsLevOffsetMpi(:), obsHeadDateMpi(:)
1393    integer, allocatable :: obsLaunchTimeMpi(:), stationFlagsMpi(:)
1394    integer, allocatable :: trajFlagsMpi(:,:), obsFlagsMpi(:,:)
1395    integer, allocatable :: procIndexes(:), procIndexesMpi(:)
1396    real(4), allocatable :: obsLat(:), obsLon(:), surfPresInterp(:,:)
1397    real(4), allocatable :: obsValues(:,:), oMinusB(:,:), presInterp(:,:)
1398    real(4), allocatable :: obsLatMpi(:), obsLonMpi(:)
1399    real(4), allocatable :: obsValuesMpi(:,:), oMinusBMpi(:,:)
1400    real(4), pointer     :: surfPressure(:,:,:,:)
1401    character(len=4), allocatable :: varNamesPsfc(:)
1402    character(len=9), allocatable :: stnId(:), stnIdMpi(:)
1403    character(len=20) :: trlmFileName
1404    character(len=2)  :: fileNumber
1405    logical :: upperAirObs
1406    integer :: countAcc_dd, countRej_dd, countAccMpi_dd, countRejMpi_dd
1407    integer :: countAcc_ff, countRej_ff, countAccMpi_ff, countRejMpi_ff
1408    integer :: countAcc_tt, countRej_tt, countAccMpi_tt, countRejMpi_tt
1409    integer :: countAcc_es, countRej_es, countAccMpi_es, countRejMpi_es
1410    integer, parameter :: numVars=5, numTraj=2, maxNumLev=300
1411
1412    ! Namelist variables:
1413    real(8) :: rprefinc        ! parameter for defining fixed set of model levels for vertical thinning
1414    real(8) :: rptopinc        ! parameter for defining fixed set of model levels for vertical thinning
1415    real(8) :: rcoefinc        ! parameter for defining fixed set of model levels for vertical thinning
1416    real(4) :: vlev(maxNumLev) ! parameter for defining fixed set of model levels for vertical thinning
1417    integer :: numlev          ! MUST NOT BE INCLUDED IN NAMELIST!
1418    namelist /namgem/rprefinc, rptopinc, rcoefinc, numlev, vlev
1419
1420    ! Check if any observations to be treated and count number of "profiles"
1421    numLevStn = 0
1422    numStation = 0
1423    lastProfileIndex = -1
1424    call obs_set_current_header_list(obsdat,'UA')
1425    HEADER0: do
1426      headerIndex = obs_getHeaderIndex(obsdat)
1427      if (headerIndex < 0) exit HEADER0
1428
1429      ! skip if this headerIndex doesn't contain upper air obs
1430      upperAirObs = .false.
1431      call obs_set_current_body_list(obsdat, headerIndex)
1432      BODY0: do 
1433        bodyIndex = obs_getBodyIndex(obsdat)
1434        if (bodyIndex < 0) exit BODY0
1435
1436        select case (obs_bodyElem_i(obsdat,OBS_VNM,bodyIndex))
1437        case (bufr_neuu, bufr_nevv, bufr_nett, bufr_nees)
1438          upperAirObs = .true.
1439        end select
1440      end do BODY0
1441      profileIndex = obs_headElem_i(obsdat, obs_prfl, headerIndex)
1442      if (.not. upperAirObs) cycle HEADER0
1443
1444      numLevStn = numLevStn + 1
1445
1446      profileIndex = obs_headElem_i(obsdat, obs_prfl, headerIndex)
1447      if (profileIndex /= lastProfileIndex) then
1448        lastProfileIndex = profileIndex
1449        numStation = numStation + 1
1450      end if
1451
1452    end do HEADER0
1453
1454    call rpn_comm_allReduce(numLevStn, numLevStnMpi, 1, 'mpi_integer', &
1455                            'mpi_sum','grid',ierr)
1456    if (numLevStnMpi == 0) then
1457      write(*,*) 'thn_radiosonde: no UA obs observations present'
1458      return
1459    end if
1460
1461    call rpn_comm_allReduce(numStation, numStationMpi, 1, 'mpi_integer', &
1462                            'mpi_sum','grid',ierr)
1463
1464    write(*,*) 'thn_radiosonde: number of obs initial = ', &
1465               numLevStn, numLevStnMpi
1466    write(*,*) 'thn_radiosonde: number of profiles    = ', &
1467               numStation, numStationMpi
1468
1469    ! Allocate some quanitities needed for each profile
1470    allocate(obsLevOffset(numStation+1))
1471    allocate(obsType(numStation))
1472    allocate(obsHeadDate(numStation))
1473    allocate(obsHeadTime(numStation))
1474    allocate(obsDate(numStation))
1475    allocate(obsTime(numStation))
1476    allocate(obsLaunchTime(numStation))
1477    allocate(stationFlags(numStation))
1478    allocate(obsLat(numStation))
1479    allocate(obsLon(numStation))
1480    allocate(stnId(numStation))
1481    allocate(presInterp(numStation,maxNumLev))
1482    allocate(trajFlags(numTraj,numLevStn))
1483    allocate(obsFlags(numVars,numLevStn))
1484    allocate(obsValues(numVars,numLevStn))
1485    allocate(oMinusB(numVars,numLevStn))
1486    obsLevOffset(:) = 0
1487    obsDate(:) = -1
1488    trajFlags(:,:) = -1
1489    obsFlags(:,:) = 0
1490    obsValues(:,:) = -999.0
1491    oMinusB(:,:) = -999.0
1492
1493    ! Fill in the arrays for each profile
1494    levStnIndex = 0
1495    stationIndex = 0
1496    lastProfileIndex = -1
1497    numLevStnMax = -1
1498    call obs_set_current_header_list(obsdat,'UA')
1499    HEADER1: do
1500      headerIndex = obs_getHeaderIndex(obsdat)
1501      if (headerIndex < 0) exit HEADER1
1502
1503      ! skip if this headerIndex doesn't contain upper air obs
1504      upperAirObs = .false.
1505      call obs_set_current_body_list(obsdat, headerIndex)
1506      BODY1: do 
1507        bodyIndex = obs_getBodyIndex(obsdat)
1508        if (bodyIndex < 0) exit BODY1
1509
1510        select case (obs_bodyElem_i(obsdat,OBS_VNM,bodyIndex))
1511        case (bufr_neuu, bufr_nevv, bufr_nett, bufr_nees)
1512          upperAirObs = .true.
1513        end select
1514      end do BODY1
1515      if (.not. upperAirObs) cycle HEADER1
1516
1517      levStnIndex = levStnIndex + 1
1518
1519      profileIndex = obs_headElem_i(obsdat, obs_prfl, headerIndex)
1520      if (profileIndex /= lastProfileIndex) then
1521        lastProfileIndex = profileIndex
1522        stationIndex = stationIndex + 1
1523        countLevel = 0
1524      end if
1525
1526      countLevel = countLevel + 1
1527      obsLevOffset(stationIndex+1) = obsLevOffset(stationIndex) + countLevel
1528
1529      if (countLevel > numLevStnMax) numLevStnMax = countLevel
1530
1531      ! Get some information from the first header in this profile
1532      if (countLevel == 1) then
1533        obsDate(stationIndex)  = obs_headElem_i(obsdat,obs_dat,headerIndex)
1534        obsTime(stationIndex)  = obs_headElem_i(obsdat,obs_etm,headerIndex)
1535        obsLat(stationIndex)   = obs_headElem_r(obsdat,obs_lat,headerIndex) * &
1536                                 MPC_DEGREES_PER_RADIAN_R8
1537        obsLon(stationIndex)   = obs_headElem_r(obsdat,obs_lon,headerIndex) * &
1538                                 MPC_DEGREES_PER_RADIAN_R8
1539        obsLat(stationIndex)   = 0.01*nint(100.0*obsLat(stationIndex))
1540        obsLon(stationIndex)   = 0.01*nint(100.0*obsLon(stationIndex))
1541      end if
1542      obsHeadDate(stationIndex)   = obs_headElem_i(obsdat,obs_hdd,headerIndex)
1543      obsHeadTime(stationIndex)   = obs_headElem_i(obsdat,obs_hdt,headerIndex)
1544      obsLaunchTime(stationIndex) = obs_headElem_i(obsdat,obs_lch,headerIndex)
1545      if (obsLaunchTime(stationIndex) == mpc_missingValue_int) then
1546        obsLaunchTime(stationIndex) = obsHeadTime(stationIndex)
1547      end if
1548      obsType(stationIndex)      = obs_headElem_i(obsdat,obs_rtp,headerIndex)
1549      stationFlags(stationIndex) = obs_headElem_i(obsdat,obs_st1,headerIndex)
1550      stnId(stationIndex)        = obs_elem_c(obsdat,'STID',headerIndex)
1551
1552      trajFlags(1,levStnIndex) = obs_headElem_i(obsdat,obs_tflg,headerIndex)
1553      trajFlags(2,levStnIndex) = obs_headElem_i(obsdat,obs_lflg,headerIndex)
1554
1555      call obs_set_current_body_list(obsdat, headerIndex)
1556      BODY2: do 
1557        bodyIndex = obs_getBodyIndex(obsdat)
1558        if (bodyIndex < 0) exit BODY2
1559
1560        obsFlag  = obs_bodyElem_i(obsdat,obs_flg,bodyIndex)
1561        obsValue = obs_bodyElem_r(obsdat,obs_var,bodyIndex)
1562        obsOmp   = obs_bodyElem_r(obsdat,obs_omp,bodyIndex)
1563        select case (obs_bodyElem_i(obsdat,OBS_VNM,bodyIndex))
1564        case (bufr_nedd)
1565          obsFlags(1,levStnIndex)  = obsFlag
1566          obsValues(1,levStnIndex) = obsValue
1567        case (bufr_neuu)
1568          oMinusB(1,levStnIndex)   = obsOmp
1569        case (bufr_neff)
1570          obsFlags(2,levStnIndex)  = obsFlag
1571          obsValues(2,levStnIndex) = obsValue
1572        case (bufr_nevv)
1573          oMinusB(2,levStnIndex)   = obsOmp
1574        case (bufr_nett)
1575          obsFlags(3,levStnIndex)  = obsFlag
1576          obsValues(3,levStnIndex) = obsValue
1577          oMinusB(3,levStnIndex)   = obsOmp
1578        case (bufr_nees)
1579          obsFlags(4,levStnIndex)  = obsFlag
1580          obsValues(4,levStnIndex) = obsValue
1581          oMinusB(4,levStnIndex)   = obsOmp
1582        end select
1583        obsValues(5,levStnIndex) = 0.01 * obs_bodyElem_r(obsdat,obs_ppp,bodyIndex)
1584      end do BODY2
1585
1586    end do HEADER1
1587
1588    ! Default namelist values
1589    numlev = MPC_missingValue_INT
1590    vlev(:) = MPC_missingValue_R4
1591    rprefinc = 0.0d0
1592    rptopinc = 0.0d0
1593    rcoefinc = 0.0d0
1594    ! Read the namelist defining the vertical levels
1595    if (utl_isNamelistPresent('namgem','./flnml')) then
1596      nulnam = 0
1597      ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
1598      if (ierr /= 0) call utl_abort('thn_radiosonde: Error opening file flnml')
1599      read(nulnam,nml=namgem,iostat=ierr)
1600      if (ierr /= 0) call utl_abort('thn_radiosonde: Error reading namgem namelist')
1601      if (numlev /= MPC_missingValue_INT) then
1602        call utl_abort('thn_radiosonde: check NAMGEM namelist section: numlev should be removed')
1603      end if
1604      numlev = 0
1605      do levIndex = 1, maxNumLev
1606        if (vlev(levIndex) == MPC_missingValue_R4) exit
1607        numlev = numlev + 1
1608      end do
1609      if (mmpi_myid == 0) write(*,nml=namgem)
1610      ierr = fclos(nulnam)
1611    else
1612      call utl_abort('thn_radiosonde: Namelist block namgem is missing in the namelist.')
1613    end if
1614
1615    ! Read the trial surface pressure
1616    nullify(vco_sfc)
1617    nullify(hco_sfc)
1618    trlmFileName = './trlm_01'
1619    call vco_setupFromFile(vco_sfc, trlmFileName)
1620    call hco_setupFromFile(hco_sfc, trlmFileName, ' ')
1621    if (vco_sfc%Vcode == 5100) then
1622      allocate(varNamesPsfc(2))
1623      varNamesPsfc = (/'P0','P0LS'/)
1624    else
1625      allocate(varNamesPsfc(1))
1626      varNamesPsfc = (/'P0'/)
1627    end if
1628    call gsv_allocate( stateVectorPsfc, tim_nstepobs, hco_sfc, vco_sfc, &
1629                       datestamp_opt=tim_getDatestamp(), mpi_local_opt=.false., &
1630                       dataKind_opt=4, varNames_opt=varNamesPsfc, &
1631                       hInterpolateDegree_opt='LINEAR' )
1632    deallocate(varNamesPsfc)
1633    do stepIndex = 1, tim_nstepobs
1634      write(fileNumber,'(I2.2)') stepIndex
1635      trlmFileName = './trlm_' // trim(fileNumber)
1636      call gio_readFromFile( stateVectorPsfc, trlmFileName, ' ', ' ',  &
1637                             stepIndex_opt=stepIndex, containsFullField_opt=.true. )
1638    end do
1639    call gsv_getField(stateVectorPsfc,surfPressure)
1640
1641    ! Setup the interpolation to obs locations of surface pressure
1642    zig1 = 0.0
1643    zig2 = 0.0
1644    zig3 = 1.0
1645    zig4 = 1.0
1646    ierr = ezsetopt('INTERP_DEGREE', 'LINEAR')
1647    call cxgaig('L',ig1obs,ig2obs,ig3obs,ig4obs,zig1,zig2,zig3,zig4)
1648    obsGridID = ezgdef(numStation,1,'Y','L',ig1obs,ig2obs,ig3obs,ig4obs,obsLon,obsLat)
1649    ierr = ezdefset(obsGridID,hco_sfc%ezScintId)
1650
1651    ! Do the interpolation of surface pressure to obs locations
1652    allocate(surfPresInterp(numStation,tim_nstepobs))
1653    do stepIndex = 1, tim_nstepobs
1654      ierr = ezsint(surfPresInterp(:,stepIndex),surfPressure(:,:,1,stepIndex))
1655    end do
1656    call gsv_deallocate(stateVectorPsfc)
1657
1658    ! Compute pressure profile at each obs location
1659    do stationIndex = 1, numStation
1660
1661      ! Calculate the stepIndex corresponding to the launch time
1662      call tim_getStepObsIndex(obsStepIndex_r8, tim_getDatestamp(), &
1663                               obsDate(stationIndex), obsTime(stationIndex), tim_nstepobs)
1664      obsStepIndex = nint(obsStepIndex_r8)
1665      if (obsStepIndex < 0) then
1666        obsStepIndex = (tim_nstepobs+1)/2
1667        write(*,*) 'thn_radiosonde: Obs outside the assimilation window, set to middle of window'
1668      end if
1669
1670      ! Calculate pressure levels for each station based on GEM3 vertical coordinate
1671      do levIndex  = 1, numLev
1672        zpresb = ((vlev(levIndex) - rptopinc/rprefinc) /  &
1673                 (1.0-rptopinc/rprefinc))**rcoefinc
1674        zpresa = rprefinc * (vlev(levIndex)-zpresb)
1675        presInterp(stationIndex,levIndex) =  &
1676             0.01 * (zpresa + zpresb*surfPresInterp(stationIndex,obsStepIndex))
1677      end do
1678
1679    end do
1680
1681    ! communicate several arrays to all MPI tasks for raobs_check_duplicated_stations
1682    allocate(obsLevOffsetMpi(numStationMpi+1))
1683    allocate(obsHeadDateMpi(numStationMpi))
1684    allocate(obsLaunchTimeMpi(numStationMpi))
1685    allocate(stationFlagsMpi(numStationMpi))
1686    allocate(obsLatMpi(numStationMpi))
1687    allocate(obsLonMpi(numStationMpi))
1688    allocate(trajFlagsMpi(numTraj,numLevStnMpi))
1689    allocate(obsFlagsMpi(numVars,numLevStnMpi))
1690    allocate(obsValuesMpi(numVars,numLevStnMpi))
1691    allocate(oMinusBMpi(numVars,numLevStnMpi))
1692    allocate(stnIdMpi(numStationMpi))
1693
1694    call intArrayToMpi(obsLevOffset, obsLevOffsetMpi, is_obsLevOffset_opt=.true.)
1695    call intArrayToMpi(obsHeadDate, obsHeadDateMpi)
1696    call intArrayToMpi(obsLaunchTime, obsLaunchTimeMpi)
1697    call intArrayToMpi(stationFlags, stationFlagsMpi)
1698    call realArrayToMpi(obsLat, obsLatMpi)
1699    call realArrayToMpi(obsLon, obsLonMpi)
1700    do varIndex = 1, numTraj
1701      call intArrayToMpi(trajFlags(varIndex,:), trajFlagsMpi(varIndex,:))
1702    end do
1703    do varIndex = 1, numVars
1704      call intArrayToMpi(obsFlags(varIndex,:), obsFlagsMpi(varIndex,:))
1705      call realArrayToMpi(obsValues(varIndex,:), obsValuesMpi(varIndex,:))
1706      call realArrayToMpi(oMinusB(varIndex,:), oMinusBMpi(varIndex,:))
1707    end do
1708    call stringArrayToMpi(stnId,stnIdMpi)
1709
1710    ! set stnIdMpi to 'NOT_VALID' for rejected duplicate stations
1711    call raobs_check_duplicated_stations( stnIdMpi, obsLevOffsetMpi, obsLatMpi, obsLonMpi, &
1712                                          obsHeadDateMpi, trajFlagsMpi, &
1713                                          obsFlagsMpi, obsValuesMpi, oMinusBMpi, &
1714                                          obsLaunchTimeMpi, stationFlagsMpi, &
1715                                          numVars, numStationMpi, toleranceFactor )
1716
1717    ! modify obs flags based on stnIdMpi
1718    do stationIndexMpi = 1, numStationMpi
1719      if (stnIdMpi(stationIndexMpi) == 'NOT_VALID') then
1720        do levStnIndex = obsLevOffsetMpi(stationIndexMpi)+1, &
1721                         obsLevOffsetMpi(stationIndexMpi+1)
1722          do varIndex = 1, numVars
1723            obsFlagsMpi(varIndex,levStnIndex) =  &
1724                 ibset(obsFlagsMpi(varIndex,levStnIndex),11)
1725          end do
1726        end do
1727      end if
1728    end do
1729
1730    ! copy global mpi flags to local copy
1731    allocate(procIndexes(numStation))
1732    allocate(procIndexesMpi(numStationMpi))
1733    procIndexes(:) = mmpi_myid
1734    call intArrayToMpi(procIndexes, procIndexesMpi)
1735    levStnIndex = 0
1736    do stationIndexMpi = 1, numStationMpi
1737      if (procIndexesMpi(stationIndexMpi) == mmpi_myid) then
1738        do levStnIndexMpi = obsLevOffsetMpi(stationIndexMpi)+1, &
1739                            obsLevOffsetMpi(stationIndexMpi+1)
1740          levStnIndex = levStnIndex + 1
1741          obsFlags(:,levStnIndex) = obsFlagsMpi(:,levStnIndexMpi)
1742        end do
1743      end if
1744    end do
1745    deallocate(procIndexes)
1746    deallocate(procIndexesMpi)
1747
1748    call raobs_thinning_model( obsFlags, obsValues, presInterp, numVars, numlev, &
1749                               numStation, numLevStnMax, obsLevOffset )
1750
1751    if ( verticalThinningES ) then
1752      call raobs_thinning_es( obsFlags, obsValues, numStation, numLevStnMax, obsLevOffset )
1753    end if
1754
1755    if ( ecmwfRejetsES ) then
1756      call raobs_blacklisting_ecmwf( obsFlags, obsValues, obsType, numStation, obsLevOffset )
1757    end if
1758
1759    countAcc_dd=0;  countRej_dd=0
1760    countAcc_ff=0;  countRej_ff=0
1761    countAcc_tt=0;  countRej_tt=0
1762    countAcc_es=0;  countRej_es=0
1763    do stationIndex = 1, numStation
1764      do levStnIndex = obsLevOffset(stationIndex)+1, obsLevOffset(stationIndex+1)
1765        if (btest(obsFlags(1,levStnIndex),11)) then
1766          countRej_dd = countRej_dd + 1
1767        else
1768          countAcc_dd = countAcc_dd + 1
1769        end if
1770        if (btest(obsFlags(2,levStnIndex),11)) then
1771          countRej_ff = countRej_ff + 1
1772        else
1773          countAcc_ff = countAcc_ff + 1
1774        end if
1775        if (btest(obsFlags(3,levStnIndex),11)) then
1776          countRej_tt = countRej_tt + 1
1777        else
1778          countAcc_tt = countAcc_tt + 1
1779        end if
1780        if (btest(obsFlags(4,levStnIndex),11) .or. btest(obsFlags(4,levStnIndex),8)) then
1781          countRej_es = countRej_es + 1
1782        else
1783          countAcc_es = countAcc_es + 1
1784        end if
1785      end do
1786    end do
1787
1788    call rpn_comm_allReduce(countRej_dd, countRejMpi_dd, 1, 'mpi_integer', &
1789                            'mpi_sum','grid',ierr)
1790    call rpn_comm_allReduce(countRej_ff, countRejMpi_ff, 1, 'mpi_integer', &
1791                            'mpi_sum','grid',ierr)
1792    call rpn_comm_allReduce(countRej_tt, countRejMpi_tt, 1, 'mpi_integer', &
1793                            'mpi_sum','grid',ierr)
1794    call rpn_comm_allReduce(countRej_es, countRejMpi_es, 1, 'mpi_integer', &
1795                            'mpi_sum','grid',ierr)
1796    call rpn_comm_allReduce(countAcc_dd, countAccMpi_dd, 1, 'mpi_integer', &
1797                            'mpi_sum','grid',ierr)
1798    call rpn_comm_allReduce(countAcc_ff, countAccMpi_ff, 1, 'mpi_integer', &
1799                            'mpi_sum','grid',ierr)
1800    call rpn_comm_allReduce(countAcc_tt, countAccMpi_tt, 1, 'mpi_integer', &
1801                            'mpi_sum','grid',ierr)
1802    call rpn_comm_allReduce(countAcc_es, countAccMpi_es, 1, 'mpi_integer', &
1803                            'mpi_sum','grid',ierr)
1804
1805    write(*,*)
1806    write(*,*) 'DD Rej/Acc = ',countRejMpi_dd, countAccMpi_dd
1807    write(*,*) 'FF Rej/Acc = ',countRejMpi_ff, countAccMpi_ff
1808    write(*,*) 'TT Rej/Acc = ',countRejMpi_tt, countAccMpi_tt
1809    write(*,*) 'ES Rej/Acc = ',countRejMpi_es, countAccMpi_es
1810    write(*,*)
1811
1812    ! Replace obs flags in obsSpaceData with obsFlags
1813    levStnIndex = 0
1814    call obs_set_current_header_list(obsdat,'UA')
1815    HEADER2: do
1816      headerIndex = obs_getHeaderIndex(obsdat)
1817      if (headerIndex < 0) exit HEADER2
1818
1819      ! skip if this headerIndex doesn't contain upper air obs
1820      upperAirObs = .false.
1821      call obs_set_current_body_list(obsdat, headerIndex)
1822      BODY3: do 
1823        bodyIndex = obs_getBodyIndex(obsdat)
1824        if (bodyIndex < 0) exit BODY3
1825
1826        select case (obs_bodyElem_i(obsdat,OBS_VNM,bodyIndex))
1827        case (bufr_neuu, bufr_nevv, bufr_nett, bufr_nees)
1828          upperAirObs = .true.
1829        end select
1830      end do BODY3
1831      if (.not. upperAirObs) cycle HEADER2
1832
1833      levStnIndex = levStnIndex + 1
1834
1835      call obs_set_current_body_list(obsdat, headerIndex)
1836      BODY4: do 
1837        bodyIndex = obs_getBodyIndex(obsdat)
1838        if (bodyIndex < 0) exit BODY4
1839
1840        select case (obs_bodyElem_i(obsdat,OBS_VNM,bodyIndex))
1841        case (bufr_neuu)
1842          call obs_bodySet_i(obsdat, OBS_FLG, bodyIndex, obsFlags(1,levStnIndex))
1843        case (bufr_nevv)
1844          call obs_bodySet_i(obsdat, OBS_FLG, bodyIndex, obsFlags(2,levStnIndex))
1845        case (bufr_nett)
1846          call obs_bodySet_i(obsdat, OBS_FLG, bodyIndex, obsFlags(3,levStnIndex))
1847        case (bufr_nees)
1848          call obs_bodySet_i(obsdat, OBS_FLG, bodyIndex, obsFlags(4,levStnIndex))
1849        end select
1850      end do BODY4
1851
1852    end do HEADER2
1853
1854    ! Deallocate arrays
1855    deallocate(surfPresInterp)
1856    deallocate(obsLevOffset)
1857    deallocate(obsType)
1858    deallocate(obsHeadDate)
1859    deallocate(obsHeadTime)
1860    deallocate(obsDate)
1861    deallocate(obsTime)
1862    deallocate(obsLaunchTime)
1863    deallocate(stationFlags)
1864    deallocate(obsLat)
1865    deallocate(obsLon)
1866    deallocate(stnId)
1867    deallocate(presInterp)
1868    deallocate(trajFlags)
1869    deallocate(obsFlags)
1870    deallocate(obsValues)
1871    deallocate(oMinusB)
1872    deallocate(obsLevOffsetMpi)
1873    deallocate(obsHeadDateMpi)
1874    deallocate(obsLaunchTimeMpi)
1875    deallocate(stationFlagsMpi)
1876    deallocate(obsLatMpi)
1877    deallocate(obsLonMpi)
1878    deallocate(trajFlagsMpi)
1879    deallocate(obsFlagsMpi)
1880    deallocate(obsValuesMpi)
1881    deallocate(oMinusBMpi)
1882    deallocate(stnIdMpi)
1883
1884  end subroutine thn_radiosonde
1885
1886  !---------------------------------------------------------------------
1887  ! Following are several subroutines needed by thn_radiosonde
1888  !---------------------------------------------------------------------
1889
1890  !--------------------------------------------------------------------------
1891  ! stringArrayToMpi
1892  !--------------------------------------------------------------------------
1893  subroutine stringArrayToMpi(array, arrayMpi)
1894    !
1895    !:Purpose: Do the equivalent of mpi_allgatherv for a string array
1896    !
1897    implicit none
1898
1899    ! Arguments:
1900    character(len=*), intent(in)  :: array(:)
1901    character(len=*), intent(out) :: arrayMpi(:)
1902
1903    ! Locals:
1904    integer :: ierr, arrayIndex, charIndex, lenString
1905    integer :: nsize, nsizeMpi, allnsize(mmpi_nprocs)
1906    integer, allocatable :: stringInt(:), stringIntMpi(:)
1907
1908    nsize = size(array)
1909    call rpn_comm_allgather( nsize,    1, 'mpi_integer',  &
1910                             allnsize, 1, 'mpi_integer', &
1911                             'GRID', ierr )
1912    nsizeMpi = sum(allnsize(:))
1913
1914    allocate(stringInt(nsize))
1915    allocate(stringIntMpi(nsizeMpi))
1916
1917    lenString = len(array(1))
1918    do charIndex = 1, lenString
1919      do arrayIndex = 1, nsize
1920        stringInt(arrayIndex) = iachar(array(arrayIndex)(charIndex:charIndex))
1921      end do
1922
1923      call intArrayToMpi(stringInt, stringIntMpi)
1924
1925      do arrayIndex = 1, nsizeMpi
1926        arrayMpi(arrayIndex)(charIndex:charIndex) = achar(stringIntMpi(arrayIndex))
1927      end do
1928    end do
1929
1930    deallocate(stringInt)
1931    deallocate(stringIntMpi)
1932    
1933  end subroutine stringArrayToMpi
1934
1935  !--------------------------------------------------------------------------
1936  ! intArrayToMpi
1937  !--------------------------------------------------------------------------
1938  subroutine intArrayToMpi(array, arrayMpi, is_obsLevOffset_opt)
1939    !
1940    !:Purpose: Do the equivalent of mpi_allgatherv for an integer array,
1941    !           but with special treatment if array of obsLevOffset.
1942    !
1943    implicit none
1944
1945    ! Arguments:
1946    integer,           intent(in)  :: array(:)
1947    integer,           intent(out) :: arrayMpi(:)
1948    logical, optional, intent(in)  :: is_obsLevOffset_opt
1949
1950    ! Locals:
1951    integer :: ierr, procIndex, arrayIndex
1952    integer :: nsize, nsizeMpi, allnsize(mmpi_nprocs), displs(mmpi_nprocs)
1953    logical :: is_obsLevOffset
1954    integer, allocatable :: numLevels(:), numLevelsMpi(:)
1955
1956    if (present(is_obsLevOffset_opt)) then
1957      is_obsLevOffset = is_obsLevOffset_opt
1958    else
1959      is_obsLevOffset = .false.
1960    end if
1961
1962    if (is_obsLevOffset) then
1963
1964      ! special treatment is requirement for the variable "obsLevOffset"
1965
1966      nsize = size(array) - 1
1967      call rpn_comm_allgather( nsize,    1, 'mpi_integer',  &
1968                               allnsize, 1, 'mpi_integer', &
1969                               'GRID', ierr )
1970      nsizeMpi = sum(allnsize(:))
1971
1972      if ( mmpi_myid == 0 ) then
1973        displs(1) = 0
1974        do procIndex = 2, mmpi_nprocs
1975          displs(procIndex) = displs(procIndex-1) + allnsize(procIndex-1)
1976        end do
1977      else
1978        displs(:) = 0
1979      end if
1980
1981      allocate(numLevels(nsize))
1982      allocate(numLevelsMpi(nsizeMpi))
1983      do arrayIndex = 1, nsize
1984        numLevels(arrayIndex) = array(arrayIndex+1) - array(arrayIndex)
1985      end do
1986      call rpn_comm_gatherv( numLevels   , nsize, 'mpi_integer', &
1987                             numLevelsMpi, allnsize, displs, 'mpi_integer',  &
1988                             0, 'GRID', ierr )
1989
1990      call rpn_comm_bcast(numLevelsMpi, nsizeMpi, 'mpi_integer',  &
1991                          0, 'GRID', ierr)
1992      arrayMpi(1) = 0
1993      do arrayIndex = 1, nsizeMpi
1994        arrayMpi(arrayIndex+1) = arrayMpi(arrayIndex) + numLevelsMpi(arrayIndex)
1995      end do
1996      deallocate(numLevels)
1997      deallocate(numLevelsMpi)
1998
1999    else
2000
2001      nsize = size(array)
2002      call rpn_comm_allgather( nsize,    1, 'mpi_integer',  &
2003                               allnsize, 1, 'mpi_integer', &
2004                               'GRID', ierr )
2005      nsizeMpi = sum(allnsize(:))
2006
2007      if ( mmpi_myid == 0 ) then
2008        displs(1) = 0
2009        do procIndex = 2, mmpi_nprocs
2010          displs(procIndex) = displs(procIndex-1) + allnsize(procIndex-1)
2011        end do
2012      else
2013        displs(:) = 0
2014      end if
2015
2016      call rpn_comm_gatherv( array   , nsize, 'mpi_integer', &
2017                             arrayMpi, allnsize, displs, 'mpi_integer',  &
2018                             0, 'GRID', ierr )
2019
2020      call rpn_comm_bcast(arrayMpi, nsizeMpi, 'mpi_integer',  &
2021                          0, 'GRID', ierr)      
2022    end if
2023
2024  end subroutine intArrayToMpi
2025
2026  !--------------------------------------------------------------------------
2027  ! realArrayToMpi
2028  !--------------------------------------------------------------------------
2029  subroutine realArrayToMpi(array, arrayMpi)
2030    !
2031    !:Purpose: Do the equivalent of mpi_allgatherv for a real array,
2032    !
2033    implicit none
2034
2035    ! Arguments:
2036    real(4), intent(in)  :: array(:)
2037    real(4), intent(out) :: arrayMpi(:)
2038
2039    ! Locals:
2040    integer :: ierr, procIndex
2041    integer :: nsize, nsizeMpi, allnsize(mmpi_nprocs), displs(mmpi_nprocs)
2042
2043    nsize = size(array)
2044    call rpn_comm_allgather( nsize,    1, 'mpi_integer',  &
2045                             allnsize, 1, 'mpi_integer', &
2046                             'GRID', ierr )
2047    nsizeMpi = sum(allnsize(:))
2048
2049    if ( mmpi_myid == 0 ) then
2050      displs(1) = 0
2051      do procIndex = 2, mmpi_nprocs
2052        displs(procIndex) = displs(procIndex-1) + allnsize(procIndex-1)
2053      end do
2054    else
2055      displs(:) = 0
2056    end if
2057    
2058    call rpn_comm_gatherv( array   , nsize, 'mpi_real4', &
2059                           arrayMpi, allnsize, displs, 'mpi_real4',  &
2060                           0, 'GRID', ierr )
2061
2062    call rpn_comm_bcast(arrayMpi, nsizeMpi, 'mpi_real4',  &
2063                        0, 'GRID', ierr)
2064    
2065  end subroutine realArrayToMpi
2066
2067  !--------------------------------------------------------------------------
2068  ! logicalArrayToMpi
2069  !--------------------------------------------------------------------------
2070  subroutine logicalArrayToMpi(array, arrayMpi)
2071    !
2072    !:Purpose: Do the equivalent of mpi_allgatherv for a logical array,
2073    !
2074    implicit none
2075
2076    ! Arguments:
2077    logical, intent(in)  :: array(:)
2078    logical, intent(out) :: arrayMpi(:)
2079
2080    ! Locals:
2081    integer :: ierr, procIndex
2082    integer :: nsize, nsizeMpi, allnsize(mmpi_nprocs), displs(mmpi_nprocs)
2083
2084    nsize = size(array)
2085    call rpn_comm_allgather( nsize,    1, 'mpi_integer',  &
2086                             allnsize, 1, 'mpi_integer', &
2087                             'GRID', ierr )
2088    nsizeMpi = sum(allnsize(:))
2089
2090    if ( mmpi_myid == 0 ) then
2091      displs(1) = 0
2092      do procIndex = 2, mmpi_nprocs
2093        displs(procIndex) = displs(procIndex-1) + allnsize(procIndex-1)
2094      end do
2095    else
2096      displs(:) = 0
2097    end if
2098    
2099    call rpn_comm_gatherv( array   , nsize, 'mpi_logical', &
2100                           arrayMpi, allnsize, displs, 'mpi_logical',  &
2101                           0, 'GRID', ierr )
2102
2103    call rpn_comm_bcast(arrayMpi, nsizeMpi, 'mpi_logical',  &
2104                        0, 'GRID', ierr)
2105    
2106  end subroutine logicalArrayToMpi
2107
2108  !--------------------------------------------------------------------------
2109  ! raobs_check_duplicated_stations
2110  !--------------------------------------------------------------------------
2111  subroutine raobs_check_duplicated_stations ( stnId, obsLevOffset, obsLat, obsLon,  &
2112                                               obsHeadDate, trajFlags, obsFlags, obsValues, &
2113                                               oMinusB, obsLaunchTime, stationFlags, &
2114                                               numVars, numStation, toleranceFactor )
2115    !
2116    !:Purpose: Check duplicated stations and select the best TAC/BUFR profiles
2117    !
2118    implicit none
2119
2120    ! Arguments:
2121    integer,          intent(in)    :: numVars, numStation
2122    integer,          intent(in)    :: obsHeadDate(:), obsLaunchTime(:), stationFlags(:)
2123    real(4),          intent(in)    :: obsLat(:), obsLon(:)
2124    real(4),          intent(in)    :: obsValues(:,:), oMinusB(:,:), toleranceFactor
2125    integer,          intent(in)    :: trajFlags(:,:)
2126    integer,          intent(in)    :: obsFlags(:,:)
2127    integer,          intent(in)    :: obsLevOffset(:)
2128    character(len=9), intent(inout) :: stnId(:)
2129
2130    ! Locals:
2131    integer, parameter :: maxNumStnid  = 5000
2132    logical :: condition, sameProfile, stnidNotFound
2133    integer :: stationIndex, stationIndex2, stationIndex3, catIndex
2134    integer :: greaterNumVal, numDuplicate, numDuplicateTotal, selectStationIndex 
2135    integer :: bufrStationIndex, tacStationIndex, numChecked, numStnid, numSame
2136    character (len=9)  :: stnidList(maxNumStnid)
2137    integer            :: stationIndexList(maxNumStnid)
2138    integer :: numCriteria(5), cloche(30), selectCriteria
2139
2140    numCriteria(:) = 0
2141    cloche(:) = 0
2142
2143    ! Check for duplication of TAC or BUFR profiles
2144
2145    numDuplicateTotal = 0
2146    do stationIndex = 1, numStation
2147
2148      if ( stnId(stationIndex) /= 'NOT_VALID' ) then
2149
2150        greaterNumVal = obsLevOffset(stationIndex+1) - obsLevOffset(stationIndex) + 1
2151        selectStationIndex = stationIndex
2152        numDuplicate = 0
2153
2154        ! Verify if there exists two records with the same stnid, date, time, flgs.
2155        ! Keep the one with the greatest number of vertical levels
2156        do stationIndex2 = stationIndex+1, numStation
2157
2158          if ( stnId(stationIndex2) /= 'NOT_VALID' ) then
2159            condition = stnId(stationIndex2) == stnId(stationIndex) .and. &
2160                        obsHeadDate(stationIndex2) == obsHeadDate(stationIndex) .and. &
2161                        obsLaunchTime(stationIndex2) == obsLaunchTime(stationIndex) .and. &
2162                        obsLat(stationIndex2) == obsLat(stationIndex) .and. &
2163                        obsLon(stationIndex2) == obsLon(stationIndex) .and. &
2164                        stationFlags(stationIndex2) == stationFlags(stationIndex)
2165
2166            if ( condition ) then
2167              numDuplicate = numDuplicate + 1
2168              if ( (obsLevOffset(stationIndex2+1) - obsLevOffset(stationIndex2) + 1) > &
2169                  greaterNumVal ) then
2170                greaterNumVal = obsLevOffset(stationIndex2+1) - &
2171                                obsLevOffset(stationIndex2) + 1
2172                selectStationIndex  = stationIndex2
2173              end if
2174            end if
2175          end if
2176
2177        end do ! stationIndex2
2178
2179        ! Invalid all duplicated station except one with greatest number of levels
2180        if (numDuplicate > 0) then
2181          do stationIndex2 = stationIndex, numStation
2182            if ( stnId(stationIndex2) /= 'NOT_VALID' .and. &
2183                stnId(stationIndex2) == stnId(stationIndex) .and. &
2184                selectStationIndex /= stationIndex2 ) then
2185              write(*,'(2A20,I10,2F9.2,I10)') 'Station duplique ', stnId(stationIndex2), &
2186                   obsLevOffset(stationIndex2+1)-obsLevOffset(stationIndex2), &
2187                   obsLat(stationIndex2), obsLon(stationIndex2), &
2188                   obsLaunchTime(stationIndex2)
2189              stnId(stationIndex2) = 'NOT_VALID'
2190            end if
2191          end do
2192          numDuplicateTotal = numDuplicateTotal + numDuplicate
2193        end if
2194
2195      end if
2196
2197    end do ! stationIndex
2198
2199    ! Select best profile for collocated TAC or BUFR reports
2200    if (mmpi_myid == 0) then
2201      open(unit=11, file='./selected_stations_tac_bufr.txt', status='UNKNOWN')
2202    end if
2203
2204    numChecked = 0
2205    do stationIndex = 1, numStation
2206
2207      if ( stnId(stationIndex) /= 'NOT_VALID' ) then
2208
2209        do stationIndex2 = stationIndex+1, numStation
2210
2211          if ( stnId(stationIndex2) /= 'NOT_VALID' ) then
2212            condition = (stnId(stationIndex2) == stnId(stationIndex)) .and. &
2213                        ( btest(stationFlags(stationIndex),23) .neqv. &
2214                          btest(stationFlags(stationIndex2),23) )
2215
2216            if ( condition ) then
2217
2218              if (obsLaunchTime(stationIndex2) == obsLaunchTime(stationIndex)) then
2219                sameProfile = .true.
2220              else
2221                call raobs_check_if_same_profile(stationIndex,stationIndex2, &
2222                                                 obsValues,obsLevOffset,sameProfile)
2223              end if
2224
2225              if ( sameProfile ) then
2226
2227                numChecked  = numChecked + 1
2228
2229                call raobs_compare_profiles(stationIndex, stationIndex2, &
2230                                            stationFlags, trajFlags, obsFlags, obsValues, &
2231                                            oMinusB, obsLevOffset, &
2232                                            numVars, cloche, selectCriteria, &
2233                                            selectStationIndex, toleranceFactor)
2234
2235                numCriteria(selectCriteria) = numCriteria(selectCriteria) + 1
2236
2237                bufrStationIndex = stationIndex2
2238                if (      btest(stationFlags(stationIndex),23) ) bufrStationIndex = stationIndex
2239                tacStationIndex  = stationIndex2
2240                if ( .not.btest(stationFlags(stationIndex),23) ) tacStationIndex = stationIndex
2241
2242                if (mmpi_myid == 0) write(11,'(A9,2F10.3,3I10)') stnId(stationIndex), &
2243                     obsLat(stationIndex), obsLon(stationIndex), &
2244                     obsLevOffset(bufrStationIndex+1)-obsLevOffset(bufrStationIndex)+1, &
2245                     obsLevOffset(tacStationIndex+1)-obsLevOffset(tacStationIndex)+1, selectCriteria
2246
2247                if ( selectStationIndex == stationIndex) stnId(stationIndex2) = 'NOT_VALID'
2248                if ( selectStationIndex == stationIndex2) stnId(stationIndex) = 'NOT_VALID'
2249
2250              end if ! sameProfile
2251
2252            end if ! condition 
2253
2254          end if ! stnId(stationIndex2) /= 'NOT_VALID'
2255
2256        end do ! stationIndex2
2257
2258      end if ! stnId(stationIndex) /= 'NOT_VALID' 
2259
2260    end do ! stationIndex
2261
2262    write(*,*)
2263    write(*,*)
2264    write(*,'(a30,I10)') 'nb of total duplicates '  ,numDuplicateTotal
2265    write(*,'(a30,I10)') 'nb TAC vs BUFR checked'   ,numChecked
2266    write(*,'(a30,I10)') 'nb not selected'          ,numCriteria(1)
2267    write(*,'(a30,I10)') 'nb suspicious traj BUFR'  ,numCriteria(2)
2268    write(*,'(a30,I10)') 'nb Energy higher   BUFR'  ,numCriteria(3)
2269    write(*,'(a30,I10)') 'nb variables lower BUFR'  ,numCriteria(4)
2270    write(*,'(a30,I10)') 'nb TAC  selected'         ,numCriteria(2) + &
2271                                    numCriteria(3) + numCriteria(4)
2272    write(*,'(a30,I10)') 'nb BUFR selected'         ,numCriteria(5)
2273    write(*,*)
2274    write(*,*)
2275
2276    do catIndex = 1, 29
2277      write(*,'(a15,f4.2,a3,f4.2,I5)') 'nb e ratio ',(catIndex/10.)-.05,' - ', &
2278                                       (catIndex/10.)+.05,cloche(catIndex)
2279    end do
2280    write(*,*)
2281    write(*,'(a30,I5)') 'nb of energyTot(2)) very small  ',cloche(30)
2282    write(*,*)
2283
2284    if (mmpi_myid == 0) close(unit=11)
2285
2286    ! Check whether there is still duplications
2287    numStnid = 0
2288    do stationIndex = 1, numStation
2289
2290      if ( stnId(stationIndex) /= 'NOT_VALID' ) then
2291
2292        if (numStnid < maxNumStnid ) then
2293          stnidNotFound=.true.
2294          if ( numStnid == 0) then
2295            numStnid = numStnid + 1
2296            stnidList(numStnid) = stnId(stationIndex)
2297            stationIndexList(numStnid) = stationIndex
2298          else
2299            numSame = 0
2300            do stationIndex2 = 1, numStnid
2301              if ( stnidList(stationIndex2) == stnId(stationIndex) ) then
2302                stnidNotFound=.false.
2303                numSame = numSame + 1
2304                stationIndex3 = stationIndexList(stationIndex2)
2305              end if
2306            end do
2307            if ( stnidNotFound ) then
2308              numStnid = numStnid + 1
2309              stnidList(numStnid) = stnId(stationIndex)
2310              stationIndexList(numStnid) = stationIndex
2311            else
2312              write(*,*) 'Multi profiles found : ',stnId(stationIndex), &
2313                   stnId(stationIndex3),numSame,stationIndex,stationIndex3, &
2314                   btest(stationFlags(stationIndex),23),btest(stationFlags(stationIndex3),23)
2315              write(*,'(a30,2i10,2f10.2,i10)') 'date, lch, lat lon ', &
2316                   obsHeadDate(stationIndex),obsLaunchTime(stationIndex), &
2317                   obsLat(stationIndex), obsLon(stationIndex), &
2318                   obsLevOffset(stationIndex+1)-obsLevOffset(stationIndex)+1
2319              write(*,'(a30,2i10,2f10.2,i10)') 'date, lch, lat lon ', &
2320                   obsHeadDate(stationIndex3),obsLaunchTime(stationIndex3), &
2321                   obsLat(stationIndex3), obsLon(stationIndex3), &
2322                   obsLevOffset(stationIndex3+1)-obsLevOffset(stationIndex3)+1
2323              write(*,*)
2324
2325            end if
2326          end if
2327        else
2328          write(*,*) 'numStnid >= ',maxNumStnid
2329          call utl_abort('raobs_check_duplicated_stations')
2330        end if
2331
2332      end if
2333
2334    end do
2335
2336  end subroutine raobs_check_duplicated_stations
2337
2338  !--------------------------------------------------------------------------
2339  ! raobs_check_if_same_profile
2340  !--------------------------------------------------------------------------
2341  subroutine raobs_check_if_same_profile( stationIndex, stationIndex2, &
2342                                          obsValues, obsLevOffset, sameProfile )
2343    !
2344    !:Purpose: Check if two raobs profiles are the same.
2345    !
2346    implicit none
2347
2348    ! Arguments:
2349    integer, intent(in)    :: stationIndex, stationIndex2
2350    real(4), intent(in)    :: obsValues(:,:)
2351    integer, intent(in)    :: obsLevOffset(:)
2352    logical, intent(out)   :: sameProfile
2353
2354    ! Locals:
2355    integer :: varIndex, levIndex, levStnIndex, levStnIndex1, levStnIndex2, numSum
2356    real(4) :: valSum, minDeltaP1, minDeltaP2
2357    integer, parameter :: numStdLevels = 16
2358    real(4) :: standardLevels(numStdLevels)
2359    standardLevels = (/ 1000.,925.,850.,700.,500.,400.,300.,250.,200.,150., &
2360                        100.,70.,50.,30.,20.,10./) 
2361
2362    valSum = 0.0
2363    numSum   = 0
2364
2365    sameProfile = .false.
2366
2367    do levIndex = 1, numStdLevels
2368
2369      minDeltaP1 = 1000.
2370      do levStnIndex = obsLevOffset(stationIndex)+1, obsLevOffset(stationIndex+1)
2371        if ( abs(standardLevels(levIndex) - obsValues(5,levStnIndex)) < minDeltaP1 ) then
2372          levStnIndex1 = levStnIndex
2373          minDeltaP1 = abs(standardLevels(levIndex) - obsValues(5,levStnIndex))
2374        end if
2375      end do
2376      minDeltaP2 = 1000.
2377      do levStnIndex = obsLevOffset(stationIndex2)+1, obsLevOffset(stationIndex2+1)
2378        if ( abs(standardLevels(levIndex) - obsValues(5,levStnIndex)) < minDeltaP2 ) then
2379          levStnIndex2 = levStnIndex
2380          minDeltaP2 = abs(standardLevels(levIndex) - obsValues(5,levStnIndex))
2381        end if
2382      end do
2383
2384      if ( minDeltaP1 < 1.0 .and. minDeltaP2 < 1.0 ) then
2385        do varIndex = 2, 4, 2
2386
2387          if ( obsValues(varIndex,levStnIndex1) /= -999.0 .and. &
2388               obsValues(varIndex,levStnIndex2) /= -999.0 ) then
2389            valSum = valSum + abs(obsValues(varIndex,levStnIndex1) - &
2390                                  obsValues(varIndex,levStnIndex2))
2391            numSum = numSum + 1
2392          end if
2393
2394        end do
2395
2396      end if
2397
2398    end do !levIndex
2399
2400    if (numSum > 0) then
2401      if (valSum/numSum < 0.5) sameProfile = .true.
2402    end if
2403
2404  end subroutine raobs_check_if_same_profile
2405
2406  !--------------------------------------------------------------------------
2407  ! raobs_compare_profiles
2408  !--------------------------------------------------------------------------
2409  subroutine raobs_compare_profiles( stationIndex, stationIndex2, stationFlags, trajFlags, &
2410                                     obsFlags, obsValues, oMinusB, obsLevOffset, numVars, &
2411                                     cloche, selectCriteria, selectStationIndex, toleranceFactor )
2412    !
2413    !:Purpose: Perform a comparison between two raobs profiles.
2414    !
2415    implicit none
2416
2417    ! Arguments:
2418    integer,           intent(in)    :: stationIndex, stationIndex2, numVars
2419    integer,           intent(in)    :: trajFlags(:,:)
2420    integer,           intent(in)    :: obsFlags(:,:)
2421    real(4),           intent(in)    :: obsValues(:,:), oMinusB(:,:), toleranceFactor
2422    integer,           intent(in)    :: obsLevOffset(:), stationFlags(:)
2423    integer,           intent(inout) :: cloche(:)
2424    integer,           intent(out)   :: selectCriteria, selectStationIndex
2425
2426    ! Locals:
2427    logical :: condition, tacAndBufr, trajInfoOk
2428    integer :: raobFormatIndex, varIndex, levStnIndex, levStnIndex2, catIndex
2429    integer :: bufrStationIndex, tacStationIndex, countTimeFlag, countLatFlag, countTraj
2430    integer :: countValues(2), thisStationIndex
2431    real(4) :: presBottom, ombBottom, deltaPres, sumDeltaPres, oMinusBavg
2432    real(4) :: sumEnergy, presLower, presUpper
2433    real(4) :: percentTrajBad
2434    real(4) :: sumEnergyVar(numVars,2), energyTot(2)
2435    real(4) :: presLowerTacBufr(numVars,2), presUpperTacBufr(numVars,2)
2436
2437    percentTrajBad = 10.0
2438    catIndex = 1
2439    selectCriteria = 1
2440
2441    tacAndBufr       = .true.
2442    trajInfoOk = .true.
2443
2444    if ( btest(stationFlags(stationIndex),23) .and. &
2445         btest(stationFlags(stationIndex2),23) ) tacAndBufr = .false.
2446    if ( .not.btest(stationFlags(stationIndex),23) .and. &
2447         .not.btest(stationFlags(stationIndex2),23) ) tacAndBufr = .false.
2448
2449    if ( tacAndBufr ) then
2450
2451      bufrStationIndex = stationIndex2
2452      if (      btest(stationFlags(stationIndex),23) ) bufrStationIndex = stationIndex
2453      tacStationIndex  = stationIndex2
2454      if ( .not.btest(stationFlags(stationIndex),23) ) tacStationIndex  = stationIndex
2455      selectStationIndex = tacStationIndex
2456
2457      ! 1. Evalue si la trajectoire native est correct
2458
2459      if ( btest(stationFlags(bufrStationIndex),14) ) then
2460
2461        countTimeFlag = 0
2462        countLatFlag = 0
2463        countTraj = obsLevOffset(bufrStationIndex+1) - obsLevOffset(bufrStationIndex) + 1
2464
2465        do levStnIndex = obsLevOffset(bufrStationIndex)+1, obsLevOffset(bufrStationIndex+1)
2466          if ( btest(trajFlags(1,levStnIndex),4) ) countTimeFlag = countTimeFlag + 1
2467          if ( btest(trajFlags(2,levStnIndex),4) ) countLatFlag  = countLatFlag + 1
2468        end do
2469
2470        if (100.*countTimeFlag/countTraj > percentTrajBad .or.  &
2471            100.*countLatFlag/countTraj  > percentTrajBad) then
2472          trajInfoOk = .false.
2473          selectStationIndex = tacStationIndex
2474          selectCriteria = 2
2475        end if
2476
2477      end if
2478
2479      if (trajInfoOk) then
2480
2481        ! Cherche les pressions (bas et haut) des extremites des profils (TAC et BUFR)
2482
2483        do raobFormatIndex = 1, 2
2484
2485          if (raobFormatIndex == 1) thisStationIndex = bufrStationIndex
2486          if (raobFormatIndex == 2) thisStationIndex = tacStationIndex
2487
2488          do varIndex = 1, 3
2489
2490            presLowerTacBufr(varIndex,raobFormatIndex) = 1000.0
2491            do levStnIndex = obsLevOffset(thisStationIndex)+1, &
2492                             obsLevOffset(thisStationIndex+1)
2493              condition = oMinusB(varIndex,levStnIndex) /= -999.0 .and. &
2494                   .not.btest(obsFlags(varIndex,levStnIndex),18) .and. &
2495                   .not.btest(obsFlags(varIndex,levStnIndex),16) .and. &
2496                   .not.btest(obsFlags(varIndex,levStnIndex),9)  .and. &
2497                   .not.btest(obsFlags(varIndex,levStnIndex),8)  .and. &
2498                   .not.btest(obsFlags(varIndex,levStnIndex),2)  .and. &
2499                   .not.btest(obsFlags(varIndex,levStnIndex),11)
2500              if ( condition ) then
2501                presLowerTacBufr(varIndex,raobFormatIndex) = obsValues(5,levStnIndex)
2502                exit
2503              end if
2504 
2505            end do
2506            presUpperTacBufr(varIndex,raobFormatIndex) = &
2507                 presLowerTacBufr(varIndex,raobFormatIndex)
2508 
2509            if (levStnIndex < obsLevOffset(thisStationIndex+1) ) then
2510
2511              do levStnIndex2 = levStnIndex+1, obsLevOffset(thisStationIndex+1)
2512
2513                condition = oMinusB(varIndex,levStnIndex2) /= -999.0 .and. &
2514                     .not.btest(obsFlags(varIndex,levStnIndex2),18) .and. &
2515                     .not.btest(obsFlags(varIndex,levStnIndex2),16) .and. &
2516                     .not.btest(obsFlags(varIndex,levStnIndex2),9)  .and. &
2517                     .not.btest(obsFlags(varIndex,levStnIndex2),8)  .and. &
2518                     .not.btest(obsFlags(varIndex,levStnIndex2),2)  .and. &
2519                     .not.btest(obsFlags(varIndex,levStnIndex2),11)
2520                if ( condition ) then
2521                  presUpperTacBufr(varIndex,raobFormatIndex) = obsValues(5,levStnIndex2)
2522                end if
2523
2524              end do
2525
2526            end if
2527
2528          end do !varIndex
2529
2530        end do !raobFormatIndex
2531
2532        ! Calcul de la norme energie equivalente (NE)
2533
2534        do raobFormatIndex = 1, 2
2535
2536          if (raobFormatIndex == 1) thisStationIndex = bufrStationIndex
2537          if (raobFormatIndex == 2) thisStationIndex = tacStationIndex
2538
2539          countValues(raobFormatIndex) = 0
2540
2541          do varIndex = 1, 3
2542
2543            presLower =  presLowerTacBufr(varIndex,1)
2544            if ( presLower > presLowerTacBufr(varIndex,2) ) then
2545              presLower = presLowerTacBufr(varIndex,2)
2546            end if
2547            presUpper =  presUpperTacBufr(varIndex,1)
2548            if ( presUpper < presUpperTacBufr(varIndex,2) ) then
2549              presUpper = presUpperTacBufr(varIndex,2)
2550            end if
2551
2552            sumDeltaPres = 0.0
2553            sumEnergy = 0.0
2554            sumEnergyVar(varIndex,raobFormatIndex) = 0.0
2555
2556            do levStnIndex = obsLevOffset(thisStationIndex)+1, &
2557                             obsLevOffset(thisStationIndex+1)
2558
2559              condition = oMinusB(varIndex,levStnIndex) /= -999.0 .and. &
2560                   .not.btest(obsFlags(varIndex,levStnIndex),18) .and. &
2561                   .not.btest(obsFlags(varIndex,levStnIndex),16) .and. &
2562                   .not.btest(obsFlags(varIndex,levStnIndex),9)  .and. &
2563                   .not.btest(obsFlags(varIndex,levStnIndex),8)  .and. &
2564                   .not.btest(obsFlags(varIndex,levStnIndex),2)  .and. &
2565                   .not.btest(obsFlags(varIndex,levStnIndex),11)
2566
2567              if ( condition ) then
2568                countValues(raobFormatIndex) = countValues(raobFormatIndex) + 1
2569                if ( obsValues(5,levStnIndex) <= presLower ) then
2570                  presBottom = obsValues(   5,levStnIndex)
2571                  ombBottom = oMinusB(varIndex,levStnIndex)
2572                  exit
2573                end if
2574              end if
2575
2576            end do
2577
2578            if (levStnIndex < obsLevOffset(thisStationIndex+1) ) then
2579
2580              do levStnIndex2 = levStnIndex+1, obsLevOffset(thisStationIndex+1)
2581
2582                condition = oMinusB(varIndex,levStnIndex2) /= -999.0 .and. &
2583                     .not.btest(obsFlags(varIndex,levStnIndex2),18) .and. &
2584                     .not.btest(obsFlags(varIndex,levStnIndex2),16) .and. &
2585                     .not.btest(obsFlags(varIndex,levStnIndex2),9)  .and. &
2586                     .not.btest(obsFlags(varIndex,levStnIndex2),8)  .and. &
2587                     .not.btest(obsFlags(varIndex,levStnIndex2),2)  .and. &
2588                     .not.btest(obsFlags(varIndex,levStnIndex2),11)
2589                if ( condition ) then
2590                  countValues(raobFormatIndex) = countValues(raobFormatIndex) + 1
2591                  if ( obsValues(5,levStnIndex2) >= presUpper ) then
2592                    deltaPres = log(presBottom) - log(obsValues(5,levStnIndex2))
2593                    oMinusBavg = ( ombBottom + oMinusB(varIndex,levStnIndex2) ) / 2 
2594                    sumEnergy = sumEnergy + deltaPres * oMinusBavg**2
2595                    sumDeltaPres = sumDeltaPres + deltaPres
2596                    presBottom = obsValues(   5,levStnIndex2)
2597                    ombBottom = oMinusB(varIndex,levStnIndex2)
2598                  end if
2599                end if
2600
2601              end do
2602
2603            end if
2604            if (sumDeltaPres > 0.) then
2605              sumEnergyVar(varIndex,raobFormatIndex) = sumEnergy/sumDeltaPres
2606            end if
2607
2608          end do ! varIndex
2609
2610          energyTot(raobFormatIndex) = sumEnergyVar(1,raobFormatIndex) + &
2611                                       sumEnergyVar(2,raobFormatIndex) + &
2612                                       (1005./300.)*sumEnergyVar(3,raobFormatIndex)
2613
2614        end do ! raobFormatIndex
2615
2616        ! 2. Choisi le profil TAC (raobFormatIndex==2) si le nombre de niveaux ou
2617        !    il y a des donnees est plus grand ou egal au nombre dans le profil
2618        !    BUFR (raobFormatIndex==1)
2619
2620        if ( countValues(2) >= countValues(1) ) then
2621
2622          selectStationIndex = tacStationIndex
2623          selectCriteria = 4
2624
2625        else
2626
2627          ! 3. Choisi le profil BUFR si la norme energie equivalente du profil
2628          !    ne depasse pas 'toleranceFactor'
2629
2630          if (abs(energyTot(2)) > 1.e-6) catIndex = nint(10.*energyTot(1)/energyTot(2)) + 1
2631          if (catIndex > 30) catIndex = 30 
2632
2633          cloche(catIndex) = cloche(catIndex) + 1
2634
2635          if ( energyTot(1) <= toleranceFactor*energyTot(2) ) then
2636
2637            selectStationIndex = bufrStationIndex
2638            selectCriteria = 5
2639
2640          else
2641
2642            selectStationIndex = tacStationIndex
2643            selectCriteria = 3
2644
2645          end if
2646
2647        end if
2648
2649      end if ! trajInfoOk
2650
2651    else
2652
2653      write(*,*) 'MEME TYPE DE FICHIER' 
2654
2655    end if !tacAndBufr
2656
2657  end subroutine raobs_compare_profiles
2658
2659  !--------------------------------------------------------------------------
2660  ! raobs_thinning_model
2661  !--------------------------------------------------------------------------
2662  subroutine raobs_thinning_model( obsFlags, obsValues, presInterp, numVars, numLev, &
2663                                   numStation, numLevStnMax, obsLevOffset )
2664    !
2665    !:Purpose: Perform raobs thinning by comparing with a set of model levels.
2666    !
2667    implicit none
2668
2669    ! Arguments:
2670    integer,           intent(in)    :: numVars, numLev, numStation, numLevStnMax
2671    integer,           intent(in)    :: obsLevOffset(:)
2672    integer,           intent(inout) :: obsFlags(:,:)
2673    real(4),           intent(in)    :: obsValues(:,:)
2674    real(4),           intent(in)    :: presInterp(:,:)
2675
2676    ! Locals:
2677    logical :: condition, debug
2678    integer :: stationIndex, levIndex, levStnIndex, stdLevelIndex, varIndex
2679    integer :: varIndexDD, varIndexFF, varIndexPres
2680    integer :: levSelectIndex, levSelectIndex2, numLevSelect, levStnIndexValid
2681    integer :: numLevSelectBest, tempInt
2682    real(4) :: presTop, presBottom, deltaPres, deltaPresMin
2683    integer, allocatable :: levStnIndexList(:), numValidObs(:), listIndex(:)
2684    ! Standard levels including 925 hPa
2685    integer, parameter :: numStdLevels = 16
2686    real(4) :: standardLevels(numStdLevels)
2687    standardLevels(1:numStdLevels) = (/ 1000.,925.,850.,700.,500.,400.,300.,250.,200.,150., &
2688                                        100.,70.,50.,30.,20.,10./) 
2689
2690    varIndexDD = 1
2691    varIndexFF = 2
2692    varIndexPres = 5
2693    debug = .false.
2694
2695    allocate(levStnIndexList(numLevStnMax))
2696    allocate(numValidObs(numLevStnMax))
2697    allocate(listIndex(numLevStnMax))
2698
2699    do stationIndex = 1, numStation
2700
2701      ! If one obsFlags is set to 0 but not the other then make the obsFlagss consistent
2702      ! and obsFlags wind direction and module if one of these wind variables is missing
2703
2704      do levStnIndex = obsLevOffset(stationIndex)+1, obsLevOffset(stationIndex+1)
2705
2706        if ( obsFlags(varIndexDD,levStnIndex) == 0  .and. &
2707             obsFlags(varIndexFF,levStnIndex) /= 0 ) then
2708          obsFlags(varIndexDD,levStnIndex) = obsFlags(varIndexFF,levStnIndex)
2709        end if
2710        if ( obsFlags(varIndexDD,levStnIndex) /= 0  .and. &
2711             obsFlags(varIndexFF,levStnIndex) == 0 ) then
2712          obsFlags(varIndexFF,levStnIndex) = obsFlags(varIndexDD,levStnIndex)
2713        end if
2714
2715        if ( (obsValues(varIndexDD,levStnIndex)  < 0. .and. &
2716              obsValues(varIndexFF,levStnIndex) >= 0.) .or. &
2717             (obsValues(varIndexDD,levStnIndex) >= 0. .and. &
2718              obsValues(varIndexFF,levStnIndex)  < 0.) ) then
2719          obsFlags(varIndexDD,levStnIndex) = ibset(obsFlags(varIndexDD,levStnIndex),11)
2720          obsFlags(varIndexFF,levStnIndex) = ibset(obsFlags(varIndexFF,levStnIndex),11)
2721        end if
2722
2723      end do
2724
2725      do levIndex = 1, numLev
2726
2727        ! Pressures at the bottom and top of the model layer
2728
2729        if (levIndex == 1) then
2730          presTop = presInterp(stationIndex,levIndex)
2731          presBottom = exp( 0.5*alog(presInterp(stationIndex,levIndex+1) * &
2732                       presInterp(stationIndex,levIndex)) )
2733        else if (levIndex == numLev) then
2734          presTop = exp( 0.5*alog(presInterp(stationIndex,levIndex-1) * &
2735                       presInterp(stationIndex,levIndex)) )
2736          presBottom = presInterp(stationIndex,levIndex)
2737        else
2738          presTop = exp( 0.5*alog(presInterp(stationIndex,levIndex-1) * &
2739                       presInterp(stationIndex,levIndex)) )
2740          presBottom = exp( 0.5*alog(presInterp(stationIndex,levIndex+1) * &
2741                       presInterp(stationIndex,levIndex)) )
2742        end if
2743
2744        ! Select the observation levels between presTop and presBottom
2745
2746        numLevSelect = 0
2747        do levStnIndex = obsLevOffset(stationIndex)+1, obsLevOffset(stationIndex+1)
2748
2749          if (obsValues(varIndexPres,levStnIndex) > presTop  .and. &
2750              obsValues(varIndexPres,levStnIndex) <= presBottom) then
2751
2752            numLevSelect = numLevSelect + 1
2753            levStnIndexList(numLevSelect) = levStnIndex
2754            numValidObs(numLevSelect) = 0
2755
2756            ! numValidObs(numLevSelect) contains number of valid obs at each level selected
2757
2758            do varIndex = 1, numVars
2759              condition = obsValues(varIndex,levStnIndex) >= 0. .and. &
2760                   .not.btest(obsFlags(varIndex,levStnIndex),18) .and. &
2761                   .not.btest(obsFlags(varIndex,levStnIndex),16) .and. &
2762                   .not.btest(obsFlags(varIndex,levStnIndex),9)  .and. &
2763                   .not.btest(obsFlags(varIndex,levStnIndex),8)  .and. &
2764                   .not.btest(obsFlags(varIndex,levStnIndex),2)  .and. &
2765                   .not.btest(obsFlags(varIndex,levStnIndex),11)
2766              if ( condition ) numValidObs(numLevSelect) = numValidObs(numLevSelect) + 1
2767            end do
2768
2769          end if
2770
2771        end do ! levStnIndex
2772
2773        if ( numLevSelect > 0 ) then
2774
2775          ! Sort the observation levels by greatest numbers of valid observations
2776
2777          do levSelectIndex = 1, numLevSelect
2778            listIndex(levSelectIndex) = levSelectIndex
2779          end do
2780          do levSelectIndex = 1, numLevSelect
2781            do levSelectIndex2 = levSelectIndex, numLevSelect
2782              if ( numValidObs(levSelectIndex2) > numValidObs(levSelectIndex) ) then
2783                tempInt     = numValidObs(levSelectIndex)
2784                numValidObs(levSelectIndex) = numValidObs(levSelectIndex2)
2785                numValidObs(levSelectIndex2) = tempInt
2786                tempInt     = listIndex(levSelectIndex)
2787                listIndex(levSelectIndex) = listIndex(levSelectIndex2)
2788                listIndex(levSelectIndex2) = tempInt 
2789              end if
2790            end do
2791          end do
2792          numLevSelectBest = 1
2793          if ( numLevSelect > 1 ) then
2794            do levSelectIndex = 2, numLevSelect
2795              if (numValidObs(levSelectIndex) == numValidObs(1)) then
2796                numLevSelectBest = numLevSelectBest + 1
2797              end if
2798            end do
2799          end if
2800
2801          do varIndex = 1, numVars
2802
2803            levStnIndexValid = 0
2804
2805            ! Rule #1 : get valid the observation at the standard level if one
2806            !           is found in the layer
2807
2808            do levSelectIndex = 1, numLevSelect
2809
2810              levStnIndex = levStnIndexList(levSelectIndex)
2811
2812              condition = obsValues(varIndex,levStnIndex) >= 0. .and. &
2813                   .not.btest(obsFlags(varIndex,levStnIndex),18) .and. &
2814                   .not.btest(obsFlags(varIndex,levStnIndex),16) .and. &
2815                   .not.btest(obsFlags(varIndex,levStnIndex),9)  .and. &
2816                   .not.btest(obsFlags(varIndex,levStnIndex),8)  .and. &
2817                   .not.btest(obsFlags(varIndex,levStnIndex),2)  .and. &
2818                   .not.btest(obsFlags(varIndex,levStnIndex),11)
2819
2820              do stdLevelIndex = 1, numStdLevels
2821                if ( (obsValues(varIndexPres,levStnIndex) == &
2822                      standardLevels(stdLevelIndex)) .and. &
2823                     condition ) then
2824                  levStnIndexValid = levStnIndex
2825                end if
2826              end do
2827
2828            end do ! levSelectIndex
2829
2830            if ( levStnIndexValid == 0 ) then
2831
2832              ! Rule #2 : get the closest valid observation to the model level
2833              !           and most complete
2834
2835              deltaPresMin = presBottom - presTop
2836              do levSelectIndex = 1, numLevSelectBest
2837
2838                levStnIndex = levStnIndexList(listIndex(levSelectIndex))
2839
2840                deltaPres = abs(obsValues(varIndexPres,levStnIndex) - &
2841                                presInterp(stationIndex,levIndex))
2842                condition = obsValues(varIndex,levStnIndex) >= 0. .and. &
2843                     .not.btest(obsFlags(varIndex,levStnIndex),18) .and. &
2844                     .not.btest(obsFlags(varIndex,levStnIndex),16) .and. &
2845                     .not.btest(obsFlags(varIndex,levStnIndex),9)  .and. &
2846                     .not.btest(obsFlags(varIndex,levStnIndex),8)  .and. &
2847                     .not.btest(obsFlags(varIndex,levStnIndex),2)  .and. &
2848                     .not.btest(obsFlags(varIndex,levStnIndex),11)
2849
2850                if ( deltaPres < deltaPresMin .and. condition) then
2851                  deltaPresMin = deltaPres
2852                  levStnIndexValid = levStnIndex
2853                end if
2854
2855              end do ! levSelectIndex
2856
2857            end if  ! levStnIndexValid == 0
2858
2859            if ( levStnIndexValid == 0 ) then
2860
2861              ! Rule #3 : get the closest valid observation if not previously selected
2862
2863              deltaPresMin = presBottom - presTop
2864              do levSelectIndex = 1, numLevSelect
2865
2866                levStnIndex = levStnIndexList(levSelectIndex)
2867
2868                deltaPres = abs(obsValues(varIndexPres,levStnIndex) - &
2869                                presInterp(stationIndex,levIndex))
2870                condition = obsValues(varIndex,levStnIndex) >= 0. .and. &
2871                     .not.btest(obsFlags(varIndex,levStnIndex),18) .and. &
2872                     .not.btest(obsFlags(varIndex,levStnIndex),16) .and. &
2873                     .not.btest(obsFlags(varIndex,levStnIndex),9)  .and. &
2874                     .not.btest(obsFlags(varIndex,levStnIndex),8)  .and. &
2875                     .not.btest(obsFlags(varIndex,levStnIndex),2)  .and. &
2876                     .not.btest(obsFlags(varIndex,levStnIndex),11)
2877
2878                if ( deltaPres < deltaPresMin .and. condition) then
2879                  deltaPresMin = deltaPres
2880                  levStnIndexValid = levStnIndex
2881                end if
2882
2883              end do ! levSelectIndex
2884
2885            end if  ! levStnIndexValid == 0
2886
2887            ! Apply thinning obsFlags to all obs except the one on level levStnIndexValid
2888
2889            do levSelectIndex = 1, numLevSelect
2890
2891              levStnIndex = levStnIndexList(levSelectIndex)
2892
2893              if ( levStnIndex /= levStnIndexValid .and. &
2894                   obsValues(varIndex,levStnIndex) >= 0. ) then
2895                obsFlags(varIndex,levStnIndex) = ibset(obsFlags(varIndex,levStnIndex),11)
2896              end if
2897
2898            end do
2899
2900          end do !varIndex
2901
2902        end if !numLevSelect > 0
2903
2904      end do !levIndex
2905
2906      ! Following lines for debugging purposes
2907
2908      if ( debug ) then
2909
2910        levIndex = numLev
2911        presBottom = presInterp(stationIndex,levIndex)
2912        write (*,*) ' '
2913        write (*,'(a40,I8,a40)') '      ==================== Station no. ', &
2914             stationIndex,' =============================='
2915        write (*,*) ' '
2916        write (*,'(a80,f10.2,i10)') 'lowest model layer-------------> ',presBottom,levIndex
2917        do while(obsValues(varIndexPres,obsLevOffset(stationIndex)+1) < presBottom)
2918          levIndex = levIndex - 1
2919          presBottom = exp(0.5 * alog(presInterp(stationIndex,levIndex+1) * &
2920                           presInterp(stationIndex,levIndex)))
2921        end do
2922        if (levIndex == numLev) levIndex = numLev - 1
2923
2924        do levStnIndex = obsLevOffset(stationIndex)+1, obsLevOffset(stationIndex+1)
2925
2926          presBottom = exp( 0.5*alog(presInterp(stationIndex,levIndex+1) * &
2927                       presInterp(stationIndex,levIndex)) )
2928          do while(obsValues(varIndexPres,levStnIndex) < presBottom)
2929            write (*,'(a80,f10.2,i10)') 'model layer---------------> ',presBottom,levIndex
2930            levIndex = levIndex - 1
2931            presBottom = exp( 0.5*alog(presInterp(stationIndex,levIndex+1) * &
2932                         presInterp(stationIndex,levIndex)) )
2933          end do
2934          write (*,'(5f8.2,4i8)') obsValues(varIndexPres,levStnIndex), &
2935               obsValues(1,levStnIndex), obsValues(2,levStnIndex), &
2936               obsValues(3,levStnIndex), obsValues(4,levStnIndex), &
2937               obsFlags(1,levStnIndex), obsFlags(2,levStnIndex), &
2938               obsFlags(3,levStnIndex), obsFlags(4,levStnIndex)
2939
2940        end do
2941
2942      end if
2943
2944    end do !stationIndex
2945
2946    deallocate(levStnIndexList, numValidObs, listIndex)
2947
2948  end subroutine raobs_thinning_model
2949
2950  !--------------------------------------------------------------------------
2951  ! raobs_thinning_es
2952  !--------------------------------------------------------------------------
2953  subroutine raobs_thinning_es( obsFlags, obsValues, numStation, &
2954                                numLevStnMax, obsLevOffset )
2955    !
2956    !:Purpose: Perform thinning of T-Td raobs observations.
2957    !
2958    implicit none
2959
2960    ! Arguments:
2961    integer,           intent(in)    :: numStation, numLevStnMax
2962    integer,           intent(in)    :: obsLevOffset(:)
2963    integer,           intent(inout) :: obsFlags(:,:)
2964    real(4),           intent(in)    :: obsValues(:,:)
2965
2966    ! Locals:
2967    integer, parameter  :: numLevES = 42
2968    logical :: condition
2969    integer :: stationIndex, levIndex, levStnIndex, levSelectIndex, varIndexES, varIndexPres
2970    integer :: numLevSelect, levStnIndexValid
2971    real(4) :: levelsES(numLevES), deltaPres, deltaPresMin, presBottom, presTop
2972    integer, allocatable :: levStnIndexList(:)
2973
2974    ! Selected pressure levels for additional thinning to humitidy observations
2975
2976    levelsES(1:numLevES) = (/ 1025.,1000.,975.,950.,925.,900.,875.,850.,825., &
2977                              800.,775.,750.,725.,700.,675.,650.,625.,600.,575., &
2978                              550.,525.,500.,475.,450.,425.,400.,375.,350.,325., &
2979                              300.,275.,250.,225.,200.,175.,150.,100., 70., 50., &
2980                              30., 20., 10./)
2981
2982    varIndexES = 4
2983    varIndexPres = 5
2984
2985    allocate(levStnIndexList(numLevStnMax))
2986
2987    do stationIndex = 1, numStation
2988
2989      ! Retain nearest observations to selected pressure levels in levelsES
2990
2991      do levIndex = 1, numLevES
2992
2993        ! Pressures at the bottom and top of the ES layer
2994
2995        if (levIndex == 1) then
2996          presBottom = levelsES(levIndex)
2997          presTop = exp(0.5*alog(levelsES(levIndex+1)*levelsES(levIndex)))
2998        else if (levIndex == numLevES) then
2999          presBottom = exp(0.5*alog(levelsES(levIndex-1)*levelsES(levIndex)))
3000          presTop = levelsES(levIndex)
3001        else
3002          presBottom = exp(0.5*alog(levelsES(levIndex-1)*levelsES(levIndex)))
3003          presTop = exp(0.5*alog(levelsES(levIndex+1)*levelsES(levIndex)))
3004        end if
3005   
3006        ! Select the levels between presTop and presBottom
3007
3008        numLevSelect = 0
3009
3010        do levStnIndex = obsLevOffset(stationIndex)+1, obsLevOffset(stationIndex+1)
3011
3012          if ( obsValues(varIndexPres,levStnIndex) > presTop .and. &
3013               obsValues(varIndexPres,levStnIndex) <= presBottom ) then
3014            numLevSelect  = numLevSelect + 1
3015            levStnIndexList(numLevSelect) = levStnIndex
3016          end if
3017
3018        end do ! levStnIndex
3019
3020        ! Get the closest valid observation to the selected pressure level
3021
3022        levStnIndexValid = 0
3023        deltaPresMin = presBottom - presTop
3024
3025        do levSelectIndex = 1, numLevSelect
3026
3027          levStnIndex = levStnIndexList(levSelectIndex)
3028
3029          deltaPres = abs(obsValues(varIndexPres,levStnIndex) - levelsES(levIndex))
3030          condition = .not.btest(obsFlags(varIndexES,levStnIndex),18) .and. &
3031               .not.btest(obsFlags(varIndexES,levStnIndex),16) .and. &
3032               .not.btest(obsFlags(varIndexES,levStnIndex),9)  .and. &
3033               .not.btest(obsFlags(varIndexES,levStnIndex),8)  .and. &
3034               .not.btest(obsFlags(varIndexES,levStnIndex),2)  .and. &
3035               .not.btest(obsFlags(varIndexES,levStnIndex),11) .and. &
3036               obsValues(varIndexES,levStnIndex) >= 0.         .and. &
3037               deltaPres < deltaPresMin
3038
3039          if ( condition ) then
3040            deltaPresMin = deltaPres
3041            levStnIndexValid = levStnIndex
3042          end if
3043
3044        end do !levSelectIndex
3045
3046        ! Apply thinning obsFlags to all obs except the one selected with levStnIndexValid
3047
3048        do levSelectIndex = 1, numLevSelect
3049
3050          levStnIndex = levStnIndexList(levSelectIndex)
3051
3052          if ( (levStnIndex /= levStnIndexValid) .and. &
3053               (obsValues(varIndexES,levStnIndex) >= 0.) ) then
3054            obsFlags(varIndexES,levStnIndex) = ibset(obsFlags(varIndexES,levStnIndex),11)
3055          end if
3056
3057        end do
3058
3059      end do !levIndex
3060
3061    end do !stationIndex
3062
3063    deallocate(levStnIndexList)
3064
3065  end subroutine raobs_thinning_es
3066
3067  !--------------------------------------------------------------------------
3068  ! raobs_blacklisting_ecmwf
3069  !--------------------------------------------------------------------------
3070  subroutine raobs_blacklisting_ecmwf( obsFlags, obsValues, obsType, numStation, obsLevOffset )
3071    !
3072    !:Purpose: Perform filtering of T-Td raobs observations based on
3073    !           approach inspired by ECMWF approach
3074    !
3075    implicit none
3076
3077    ! Arguments:
3078    integer, intent(in)    :: numStation
3079    integer, intent(in)    :: obsLevOffset(:), obsType(:)
3080    integer, intent(inout) :: obsFlags(:,:)
3081    real(4), intent(in)    :: obsValues(:,:)
3082
3083    ! Locals:
3084    logical :: conditionTT, conditionES, rejectES
3085    integer :: ierr, stationIndex, levStnIndex, varIndexES, varIndexTT, varIndexPres
3086    integer :: type, countReject0p, countReject0t, countReject1p, countReject1t
3087    integer :: countReject0pMpi, countReject0tMpi, countReject1pMpi, countReject1tMpi
3088    integer :: countTotalES, countTotalESMpi
3089
3090    countReject0p = 0
3091    countReject0t = 0
3092    countReject1p = 0
3093    countReject1t = 0
3094    countTotalES = 0
3095
3096    varIndexTT = 3
3097    varIndexES = 4
3098    varIndexPres = 5
3099
3100    do stationIndex = 1, numStation
3101      type = 0
3102
3103      if ( obsType(stationIndex) /= -1 ) then
3104
3105        if ( obsType(stationIndex) == 14  .or.  &
3106             obsType(stationIndex) == 24  .or.  &
3107             obsType(stationIndex) == 41  .or.  &
3108             obsType(stationIndex) == 42  .or.  &
3109             obsType(stationIndex) == 52  .or.  &
3110             obsType(stationIndex) == 79  .or.  &
3111             obsType(stationIndex) == 80  .or.  &
3112             obsType(stationIndex) == 81  .or.  &
3113             obsType(stationIndex) == 83  .or.  &
3114             obsType(stationIndex) == 141 .or.  &
3115             obsType(stationIndex) == 142 ) then
3116
3117          type = 1
3118
3119        end if
3120
3121      end if
3122
3123      do levStnIndex = obsLevOffset(stationIndex)+1, obsLevOffset(stationIndex+1)
3124
3125        conditionTT =  &
3126             .not.btest(obsFlags(varIndexTT,levStnIndex),18) .and. &
3127             .not.btest(obsFlags(varIndexTT,levStnIndex),16) .and. &
3128             .not.btest(obsFlags(varIndexTT,levStnIndex),9)  .and. &
3129             .not.btest(obsFlags(varIndexTT,levStnIndex),8)  .and. &
3130             .not.btest(obsFlags(varIndexTT,levStnIndex),2)  .and. &
3131             .not.btest(obsFlags(varIndexTT,levStnIndex),11) .and. &
3132             obsValues(varIndexTT,levStnIndex) >= 0.0
3133        conditionES =  &
3134             .not.btest(obsFlags(varIndexES,levStnIndex),18) .and. &
3135             .not.btest(obsFlags(varIndexES,levStnIndex),16) .and. &
3136             .not.btest(obsFlags(varIndexES,levStnIndex),9)  .and. &
3137             .not.btest(obsFlags(varIndexES,levStnIndex),8)  .and. &
3138             .not.btest(obsFlags(varIndexES,levStnIndex),2)  .and. &
3139             .not.btest(obsFlags(varIndexES,levStnIndex),11) .and. &
3140             obsValues(varIndexES,levStnIndex) >= 0.0
3141
3142        if ( conditionTT ) then
3143
3144          rejectES = .false. 
3145          if (conditionES) countTotalES = countTotalES + 1
3146
3147          if ( type == 0 ) then
3148
3149            if ( .not.rejectES .and. obsValues(varIndexPres,levStnIndex) < 300.0 ) then
3150              if (conditionES) countReject0p = countReject0p + 1
3151              rejectES = .true. 
3152            end if
3153            if ( .not.rejectES .and. obsValues(varIndexTT,levStnIndex) < 233.15 ) then
3154              if (conditionES) countReject0t = countReject0t + 1
3155              rejectES = .true. 
3156            end if
3157    
3158          else if  ( type == 1 ) then
3159
3160            if ( .not.rejectES .and. obsValues(varIndexPres,levStnIndex) < 100.0 ) then
3161              if (conditionES) countReject1p = countReject1p + 1
3162              rejectES = .true. 
3163            end if
3164            if ( .not.rejectES .and. obsValues(varIndexTT,levStnIndex) < 213.15 ) then
3165              if (conditionES)  countReject1t = countReject1t + 1
3166              rejectES = .true. 
3167            end if
3168
3169          end if
3170
3171          if ( rejectES ) then
3172            obsFlags(varIndexES,levStnIndex) = ibset(obsFlags(varIndexES,levStnIndex),11)
3173          end if
3174
3175        else
3176
3177          obsFlags(varIndexES,levStnIndex) = obsFlags(varIndexTT,levStnIndex)
3178
3179        end if
3180
3181      end do !levStnIndex
3182
3183    end do !stationIndex
3184
3185    
3186    call rpn_comm_allReduce(countTotalES, countTotalESMpi, 1, 'mpi_integer', &
3187                            'mpi_sum','grid',ierr)
3188    call rpn_comm_allReduce(countReject0p, countReject0pMpi, 1, 'mpi_integer', &
3189                            'mpi_sum','grid',ierr)
3190    call rpn_comm_allReduce(countReject0t, countReject0tMpi, 1, 'mpi_integer', &
3191                            'mpi_sum','grid',ierr)
3192    call rpn_comm_allReduce(countReject1p, countReject1pMpi, 1, 'mpi_integer', &
3193                            'mpi_sum','grid',ierr)
3194    call rpn_comm_allReduce(countReject1t, countReject1tMpi, 1, 'mpi_integer', &
3195                            'mpi_sum','grid',ierr)
3196    write(*,*)
3197    write(*,*) ' Rejet des donnees ES inspire de ECMWF'
3198    write(*,*)
3199    write(*,'(a30,I10)') 'nb total donnees es        ',countTotalESMpi
3200    write(*,'(a30,I10)') 'nb rejet type 0 pression   ',countReject0pMpi
3201    write(*,'(a30,I10)') 'nb rejet type 0 temperature',countReject0tMpi
3202    write(*,'(a30,I10)') 'nb rejet type 1 pression   ',countReject1pMpi
3203    write(*,'(a30,I10)') 'nb rejet type 1 temperature',countReject1tMpi
3204    write(*,*)
3205    if (countTotalESMpi > 0) then
3206      write(*,'(a30,I10,f10.2)') 'nb rejet total et %', &
3207           countReject0pMpi + countReject0tMpi + countReject1pMpi + countReject1tMpi, &
3208           100.0 * (countReject0pMpi + countReject0tMpi + countReject1pMpi + &
3209                    countReject1tMpi) / countTotalESMpi
3210    end if
3211    write(*,*)
3212
3213  end subroutine raobs_blacklisting_ecmwf
3214  
3215  !--------------------------------------------------------------------------
3216  ! thn_gbGpsByDistance
3217  !--------------------------------------------------------------------------
3218  subroutine thn_gbGpsByDistance(obsdat, deltemps, deldist, removeUncorrected, rejectNoZTDScore)
3219    !
3220    !:Purpose: Original method for thinning GB-GPS data by the distance method.
3221    !           Set bit 11 of OBS_FLG on observations that are to be rejected.
3222    !
3223    implicit none
3224
3225    ! Arguments:
3226    type(struct_obs), intent(inout) :: obsdat
3227    integer,          intent(in)    :: deltemps
3228    integer,          intent(in)    :: deldist
3229    logical,          intent(in)    :: removeUncorrected
3230    logical,          intent(in)    :: rejectNoZTDScore
3231
3232    ! Locals:
3233    real(4), parameter :: normZtdScore = 50.0 ! normalization factor for zdscores
3234    integer, parameter :: nullValue = 9999 ! Value representing a non-value or null Value
3235    character(len=3), parameter :: winpos='mid' ! Preference to obs close to middle of window
3236    integer :: ierr, numHeader, numHeaderMpi, numHeaderMaxMpi, bodyIndex, headerIndex
3237    integer :: countObs, countObsOutMpi, countObsInMpi
3238    integer :: obsDate, obsTime, obsVarno, ztdObsFlag, obsFlag, nsize
3239    integer :: bgckCount, bgckCountMpi, blackListCount, blackListCountMpi
3240    integer :: unCorrectCount, unCorrectCountMpi, badTimeCount, badTimeCountMpi
3241    integer :: ztdScoreCount, ztdScoreCountMpi
3242    integer :: numSelected, middleStep
3243    integer :: obsIndex1, obsIndex2, headerIndex1, headerIndex2
3244    integer :: headerIndexBeg, headerIndexEnd
3245    real(4) :: thinDistance, deltaLat, deltaLon, obsLat1, obsLat2
3246    real(4) :: normFormalErr, missingFormalErr, formalError, finalZtdScore, ztdScore
3247    real(8) :: obsLonInDegrees, obsLatInDegrees, obsStepIndex_r8
3248    character(len=12)  :: stnId
3249    logical :: skipThisObs, thisStnIdNoaa
3250    integer, allocatable :: quality(:), qualityMpi(:)
3251    integer, allocatable :: obsLonBurpFile(:), obsLatBurpFile(:)
3252    integer, allocatable :: obsLonBurpFileMpi(:), obsLatBurpFileMpi(:)
3253    integer, allocatable :: obsStepIndex(:), obsStepIndexMpi(:)
3254    integer, allocatable :: headerIndexSelected(:), headerIndexSorted(:)
3255    logical, allocatable :: valid(:), validMpi(:)
3256
3257    write(*,*)
3258    write(*,*) 'thn_gbGpsByDistance: Starting'
3259    write(*,*)
3260
3261    numHeader = obs_numHeader(obsdat)
3262    call rpn_comm_allReduce(numHeader, numHeaderMaxMpi, 1, 'mpi_integer', &
3263                            'mpi_max', 'grid', ierr)
3264    numHeaderMpi = numHeaderMaxMpi * mmpi_nprocs
3265
3266    ! Check if any observations to be treated
3267    countObs = 0
3268    call obs_set_current_header_list(obsdat,'GP')
3269    HEADER0: do
3270      headerIndex = obs_getHeaderIndex(obsdat)
3271      if (headerIndex < 0) exit HEADER0
3272      countObs = countObs + 1
3273    end do HEADER0
3274
3275    call rpn_comm_allReduce(countObs, countObsInMpi, 1, 'mpi_integer', &
3276                            'mpi_sum','grid',ierr)
3277    if (countObsInMpi == 0) then
3278      write(*,*) 'thn_gbGpsByDistance: no gb-gps observations present'
3279      return
3280    end if
3281
3282    write(*,*) 'thn_gbGpsByDistance: number of obs initial = ', &
3283               countObs, countObsInMpi
3284
3285    thinDistance = real(deldist)
3286    write(*,*)
3287    write(*,*) 'Minimum thinning distance ', thinDistance
3288
3289    middleStep   = nint( ((tim_windowSize/2.0d0) - tim_dstepobs/2.d0) / &
3290                   tim_dstepobs) + 1
3291
3292    write(*,*)
3293    write(*,*) 'Number of time bins                     = ', tim_nstepobs
3294    write(*,*) 'Minimum number of time bins between obs = ', deltemps
3295    write(*,*) 'Central time bin                        = ', middleStep
3296    write(*,*)
3297
3298    ! Allocations: 
3299    allocate(obsLatBurpFile(numHeaderMaxMpi))
3300    allocate(obsLonBurpFile(numHeaderMaxMpi))
3301    allocate(obsStepIndex(numHeaderMaxMpi))
3302    allocate(quality(numHeaderMaxMpi))
3303    allocate(valid(numHeaderMaxMpi))
3304
3305    allocate(obsLatBurpFileMpi(numHeaderMpi))
3306    allocate(obsLonBurpFileMpi(numHeaderMpi))
3307    allocate(obsStepIndexMpi(numHeaderMpi))
3308    allocate(qualityMpi(numHeaderMpi))
3309
3310    allocate(headerIndexSorted(numHeaderMpi))
3311    allocate(headerIndexSelected(numHeaderMpi))
3312    allocate(validMpi(numHeaderMpi))
3313
3314    validMpi(:) = .false.
3315
3316    quality(:)        = nullValue
3317    obsLatBurpFile(:) = 0
3318    obsLonBurpFile(:) = 0
3319    obsStepIndex(:)   = 0
3320
3321    qualityMpi(:)        = nullValue
3322    obsLatBurpFileMpi(:) = 0
3323    obsLonBurpFileMpi(:) = 0
3324    obsStepIndexMpi(:)   = 0
3325
3326    badTimeCount = 0
3327    bgckCount = 0
3328    blackListCount = 0
3329    unCorrectCount = 0
3330    ztdScoreCount = 0
3331
3332    ! First pass through observations
3333    call obs_set_current_header_list(obsdat,'GP')
3334    HEADER1: do
3335      headerIndex = obs_getHeaderIndex(obsdat)
3336      if (headerIndex < 0) exit HEADER1
3337
3338      ! convert and store stnId as integer array
3339      stnId = obs_elem_c(obsdat,'STID',headerIndex)
3340
3341      ! get latitude and longitude
3342      obsLonInDegrees = MPC_DEGREES_PER_RADIAN_R8 * obs_headElem_r(obsdat, OBS_LON, headerIndex)
3343      obsLatInDegrees = MPC_DEGREES_PER_RADIAN_R8 * obs_headElem_r(obsdat, OBS_LAT, headerIndex)
3344      obsLonBurpFile(headerIndex) = nint(100.0*obsLonInDegrees)
3345      obsLatBurpFile(headerIndex) = 9000 + nint(100.0*obsLatInDegrees)
3346      if (obsLonBurpFile(headerIndex) >= 18000) then
3347        obsLonBurpFile(headerIndex) = obsLonBurpFile(headerIndex) - 18000
3348      else
3349        obsLonBurpFile(headerIndex) = obsLonBurpFile(headerIndex) + 18000
3350      end if
3351
3352      ! get step bin
3353      obsDate = obs_headElem_i(obsdat, OBS_DAT, headerIndex)
3354      obsTime = obs_headElem_i(obsdat, OBS_ETM, headerIndex)
3355      call tim_getStepObsIndex(obsStepIndex_r8, tim_getDatestamp(), &
3356                               obsDate, obsTime, tim_nstepobs)
3357      obsStepIndex(headerIndex) = nint(obsStepIndex_r8)
3358
3359      ! thisStnIdNoaa==TRUE means obs has collocated GPS met (Psfc) observations
3360      thisStnIdNoaa = ((index(stnId,'FSL_') + index(stnId,'-NOAA') + index(stnId,'-UCAR')) > 0)
3361      ! normalization factor (Units = mm) for ZTD formal error (formalError) 
3362      if (thisStnIdNoaa) then
3363        normFormalErr  = 15.0
3364        missingFormalErr =  7.0
3365      else
3366        normFormalErr  = 5.0
3367        missingFormalErr = 3.0
3368      end if
3369
3370      ! get ztd flag and formal error value, element 15032
3371      formalError = -1.0
3372      ztdScore = -1.0
3373      ztdObsFlag = -1
3374      call obs_set_current_body_list(obsdat, headerIndex)
3375      BODY1: do 
3376        bodyIndex = obs_getBodyIndex(obsdat)
3377        if (bodyIndex < 0) exit BODY1
3378        obsVarno = obs_bodyElem_i(obsdat, OBS_VNM, bodyIndex)
3379        if (obsVarno == bufr_nezd) then
3380          ! convert units from m to mm
3381          formalError = 1000.0*obs_bodyElem_r(obsdat, OBS_OER, bodyIndex)
3382        end if
3383        if (obsVarno == bufr_ztdScore) then
3384          ztdScore = obs_bodyElem_r(obsdat, OBS_VAR, bodyIndex)
3385        end if
3386        if (obsVarno == bufr_nezd) then
3387          ztdObsFlag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
3388        end if
3389      end do BODY1
3390      if (formalError == -1.0 .or. formalError == 1000.0*MPC_missingValue_R4) then
3391        formalError = missingFormalErr
3392      end if
3393      if (ztdScore == -1.0) then
3394        ztdScore = 999.0
3395      end if
3396      if (ztdObsFlag == -1) then
3397        call utl_abort('thn_gbGpsByDistance: ztd not found')
3398      end if
3399
3400      ! ZTD quality estimate using monitoring zdscore and the formal error
3401      finalZtdScore = 80*(ztdScore/normZtdScore) + 20*(formalError/normFormalErr)
3402    
3403      ! Give preference to FSL/UCAR ZTD observations (usually include collocated GPS met Psfc)
3404      if (.not. thisStnIdNoaa) finalZtdScore = finalZtdScore + 5.0
3405    
3406      ! Give preference to obs near middle or end of the assimilation window
3407      if (winpos == 'mid') then
3408        finalZtdScore = finalZtdScore + 25.0*float(abs(middleStep-obsStepIndex(headerIndex))) / &
3409                        float(middleStep-1)
3410      else if (winpos == 'end') then
3411        finalZtdScore = finalZtdScore + 25.0*float(tim_nstepobs-obsStepIndex(headerIndex)) / &
3412                        float(tim_nstepobs-1)
3413      end if
3414
3415      ! Quality (lower is better), typical values 20->100, if no zdscore then > 1600
3416      quality(headerIndex) = nint(finalZtdScore)
3417
3418      ! obs is outside time window
3419      if(obsStepIndex(headerIndex) == -1.0d0) then
3420        badTimeCount = badTimeCount + 1
3421        quality(headerIndex) = nullValue
3422      end if
3423
3424      ! ZTD O-P failed background/topography checks, ZTD is blacklisted, ZTD is not bias corrected
3425      if (       btest(ztdObsFlag,16) ) bgckCount = bgckCount + 1
3426      if (       btest(ztdObsFlag,8) )  blackListCount = blackListCount + 1
3427      if ( btest(ztdObsFlag,16) .or. btest(ztdObsFlag,18) .or. &
3428           btest(ztdObsFlag,8) ) then
3429        quality(headerIndex) = nullValue
3430      end if
3431      if (removeUncorrected) then
3432        if ( .not. btest(ztdObsflag,6) ) then
3433          unCorrectCount = unCorrectCount + 1
3434          quality(headerIndex) = nullValue
3435        end if
3436      end if
3437
3438      ! ZTD quality is unknown (missing ztd score)
3439      if (ztdScore == 999.0) then
3440        ztdScoreCount = ztdScoreCount + 1
3441        if ( rejectNoZTDScore ) then
3442          quality(headerIndex) = 9999
3443        end if
3444      end if
3445
3446    end do HEADER1
3447
3448    ! Gather needed information from all MPI tasks
3449    nsize = numHeaderMaxMpi
3450    call rpn_comm_allgather(quality,    nsize, 'mpi_integer',  &
3451                            qualityMpi, nsize, 'mpi_integer', 'grid', ierr)
3452    call rpn_comm_allgather(obsLatBurpFile,    nsize, 'mpi_integer',  &
3453                            obsLatBurpFileMpi, nsize, 'mpi_integer', 'grid', ierr)
3454    call rpn_comm_allgather(obsLonBurpFile,    nsize, 'mpi_integer',  &
3455                            obsLonBurpFileMpi, nsize, 'mpi_integer', 'grid', ierr)
3456    call rpn_comm_allgather(obsStepIndex,    nsize, 'mpi_integer',  &
3457                            obsStepIndexMpi, nsize, 'mpi_integer', 'grid', ierr)
3458
3459    do obsIndex1 = 1, numHeaderMpi
3460      headerIndexSorted(obsIndex1)  = obsIndex1
3461    end do
3462
3463    call thn_QsortIntIgnoringNullValues(qualityMpi,headerIndexSorted,nullValue)
3464
3465    numSelected       = 0   ! number of obs selected so far
3466    OBS_LOOP: do obsIndex1 = 1, numHeaderMpi
3467
3468      if ( qualityMpi(obsIndex1) /= nullValue ) then
3469
3470        headerIndex1 = headerIndexSorted(obsIndex1)
3471
3472        ! Check if any of the obs already selected are close in space/time to this obs
3473        ! If no, then keep (select) this obs        
3474        if( numSelected >= 1 ) then
3475          skipThisObs = .false.
3476          LOOP2: do obsIndex2 = 1, numSelected
3477            headerIndex2 = headerIndexSelected(obsIndex2)
3478            if ( abs(obsStepIndexMpi(headerIndex1) -  &
3479                     obsStepIndexMpi(headerIndex2)) < deltemps  ) then
3480              deltaLat = abs(obsLatBurpFileMpi(headerIndex1) -  &
3481                             obsLatBurpFileMpi(headerIndex2))/100.
3482              deltaLon = abs(obsLonBurpFileMpi(headerIndex1) -  &
3483                             obsLonBurpFileMpi(headerIndex2))/100.
3484              if (deltaLon > 180.) deltaLon = 360. - deltaLon
3485              obsLat1 = ((obsLatBurpFileMpi(headerIndex1) - 9000)/100.)
3486              obsLat2  = ((obsLatBurpFileMpi(headerIndex2) - 9000)/100.)
3487              if ( thn_distanceArc(deltaLat,deltaLon,obsLat1,obsLat2) < thinDistance ) then
3488                skipThisObs = .true.
3489                exit LOOP2
3490              end if
3491            end if
3492          end do LOOP2
3493        else
3494          skipThisObs = .false.
3495        end if
3496
3497        if (.not. skipThisObs) then
3498          numSelected = numSelected + 1
3499          headerIndexSelected(numSelected) = headerIndex1
3500        end if
3501
3502      end if
3503
3504    end do OBS_LOOP
3505
3506    do obsIndex1 = 1, numSelected
3507      obsIndex2 = headerIndexSelected(obsIndex1)
3508      validMpi(obsIndex2) = .true.
3509    end do
3510
3511    ! Update local copy of valid from global mpi version
3512    headerIndexBeg = 1 + mmpi_myid * numHeaderMaxMpi
3513    headerIndexEnd = headerIndexBeg + numHeaderMaxMpi - 1
3514    valid(:) = validMpi(headerIndexBeg:headerIndexEnd)
3515
3516    countObs = count(valid)
3517    call rpn_comm_allReduce(countObs, countObsOutMpi, 1, 'mpi_integer', &
3518                            'mpi_sum','grid',ierr)
3519    write(*,*) 'thn_gbGpsByDistance: number of obs after thinning = ', &
3520               countObs, countObsOutMpi
3521
3522    ! modify the obs flags and count number of obs kept for each stnId
3523    call obs_set_current_header_list(obsdat,'GP')
3524    HEADER3: do
3525      headerIndex = obs_getHeaderIndex(obsdat)
3526      if (headerIndex < 0) exit HEADER3
3527
3528      ! do not keep this obs: set bit 11 and jump to the next obs
3529      if (.not. valid(headerIndex)) then
3530        call obs_set_current_body_list(obsdat, headerIndex)
3531        BODY3: do 
3532          bodyIndex = obs_getBodyIndex(obsdat)
3533          if (bodyIndex < 0) exit BODY3
3534          obsFlag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
3535          call obs_bodySet_i(obsdat, OBS_FLG, bodyIndex, ibset(obsFlag,11))
3536        end do BODY3
3537        cycle HEADER3
3538      end if
3539
3540    end do HEADER3
3541
3542    call rpn_comm_allReduce(badTimeCount, badTimeCountMpi, 1, 'mpi_integer', &
3543                            'mpi_sum','grid',ierr)
3544    call rpn_comm_allReduce(unCorrectCount, unCorrectCountMpi, 1, 'mpi_integer', &
3545                            'mpi_sum','grid',ierr)
3546    call rpn_comm_allReduce(blackListCount, blackListCountMpi, 1, 'mpi_integer', &
3547                            'mpi_sum','grid',ierr)
3548    call rpn_comm_allReduce(bgckCount, bgckCountMpi, 1, 'mpi_integer', &
3549                            'mpi_sum','grid',ierr)
3550    call rpn_comm_allReduce(ztdScoreCount, ztdScoreCountMpi, 1, 'mpi_integer', &
3551                            'mpi_sum','grid',ierr)
3552
3553    write(*,*)
3554    write(*,'(a50,i10)') 'Number of input obs                  = ', countObsInMpi
3555    write(*,'(a50,i10)') 'Total number of rejected/thinned obs = ', countObsInMpi - countObsOutMpi
3556    write(*,'(a50,i10)') 'Number of output obs                 = ', countObsOutMpi
3557    write(*,*)
3558    write(*,*)
3559    write(*,'(a50,i10)') 'Number of blacklisted obs            = ', blackListCountMpi
3560    write(*,'(a50,i10)') 'Number of obs without bias correction= ', unCorrectCountMpi
3561    write(*,'(a50,i10)') 'Number of obs with no ztdScore       = ', ztdScoreCountMpi
3562    write(*,'(a60,4i10)') 'Number of rejects outside time window, BGCK, thinning ', &
3563         badTimeCountMpi, bgckCountMpi, &
3564         countObsInMpi - countObsOutMpi - &
3565         bgckCountMpi - badTimeCountMpi - blackListCountMpi - unCorrectCountMpi - ztdScoreCountMpi
3566
3567    write(*,*)
3568    write(*,*) 'thn_gbGpsByDistance: Finished'
3569    write(*,*)
3570
3571  end subroutine thn_gbGpsByDistance
3572
3573  !--------------------------------------------------------------------------
3574  ! thn_satWindsByDistance
3575  !--------------------------------------------------------------------------
3576  subroutine thn_satWindsByDistance(obsdat, familyType, deltemps, deldist)
3577    !
3578    !:Purpose: Original method for thinning SatWinds data by the distance method.
3579    !           Set bit 11 of OBS_FLG on observations that are to be rejected.
3580    !
3581    implicit none
3582
3583    ! Arguments:
3584    type(struct_obs), intent(inout) :: obsdat
3585    character(len=*), intent(in)    :: familyType
3586    integer,          intent(in)    :: deltemps
3587    integer,          intent(in)    :: deldist
3588
3589    ! Locals:
3590    integer, parameter :: numStnIdMax = 100
3591    integer, parameter :: numLayers = 11
3592    integer, parameter :: nullValue = -1
3593    real(4), parameter :: layer(numLayers) = (/ 100000., 92500., 85000., 70000., &
3594                                                50000., 40000., 30000., 25000., &
3595                                                20000., 15000., 10000. /)
3596    integer :: ierr, numHeader, numHeaderMaxMpi, bodyIndex, headerIndex, stnIdIndex
3597    integer :: numStnId, stnIdIndexFound, lenStnId, charIndex
3598    integer :: obsDate, obsTime, layerIndex, obsVarno, obsFlag, uObsFlag, vObsFlag
3599    integer :: bgckCount, bgckCountMpi, missingCount, missingCountMpi, nsize
3600    integer :: countObs, countObsOutMpi, countObsInMpi, numSelected, numHeaderMpi
3601    integer :: obsIndex1, obsIndex2, headerIndex1, headerIndex2
3602    integer :: headerIndexBeg, headerIndexEnd, mpiTaskId
3603    real(4) :: thinDistance, deltaLat, deltaLon, obsLat1, obsLat2
3604    real(4) :: obsPressure
3605    real(8) :: obsLonInDegrees, obsLatInDegrees
3606    real(8) :: obsStepIndex_r8, deltaPress, deltaPressMin
3607    character(len=12)  :: stnId, stnidList(numStnIdMax)
3608    logical :: obsAlreadySameStep, skipThisObs
3609    integer :: numObsStnIdOut(numStnIdMax)
3610    integer :: numObsStnIdInMpi(numStnIdMax), numObsStnIdOutMpi(numStnIdMax)
3611    integer, allocatable :: stnIdInt(:,:), stnIdIntMpi(:,:), obsMethod(:), obsMethodMpi(:)
3612    integer, allocatable :: quality(:), qualityMpi(:)
3613    integer, allocatable :: obsLonBurpFile(:), obsLatBurpFile(:)
3614    integer, allocatable :: obsLonBurpFileMpi(:), obsLatBurpFileMpi(:)
3615    integer, allocatable :: obsStepIndex(:), obsStepIndexMpi(:)
3616    integer, allocatable :: obsLayerIndex(:), obsLayerIndexMpi(:)
3617    integer, allocatable :: headerIndexSorted(:), headerIndexSelected(:)
3618    logical, allocatable :: valid(:), validMpi(:), validMpi2(:)
3619
3620    write(*,*)
3621    write(*,*) 'thn_satWindsByDistance: Starting'
3622    write(*,*)
3623
3624    numHeader = obs_numHeader(obsdat)
3625    call rpn_comm_allReduce(numHeader, numHeaderMaxMpi, 1, 'mpi_integer', &
3626                            'mpi_max', 'grid', ierr)
3627
3628    ! Check if any observations to be treated
3629    countObs = 0
3630    call obs_set_current_header_list(obsdat,trim(familyType))
3631    HEADER0: do
3632      headerIndex = obs_getHeaderIndex(obsdat)
3633      if (headerIndex < 0) exit HEADER0
3634      countObs = countObs + 1
3635    end do HEADER0
3636
3637    call rpn_comm_allReduce(countObs, countObsInMpi, 1, 'mpi_integer', &
3638                            'mpi_sum','grid',ierr)
3639    if (countObsInMpi == 0) then
3640      write(*,*) 'thn_satWindsByDistance: no satwind observations present'
3641      return
3642    end if
3643
3644    write(*,*) 'thn_satWindsByDistance: number of obs initial = ', &
3645               countObs, countObsInMpi
3646
3647    thinDistance = real(deldist)
3648    write(*,*) 'Minimun thinning distance ',thinDistance
3649
3650    ! Allocations:
3651    allocate(valid(numHeaderMaxMpi))
3652    allocate(quality(numHeaderMaxMpi))
3653    allocate(obsLatBurpFile(numHeaderMaxMpi))
3654    allocate(obsLonBurpFile(numHeaderMaxMpi))
3655    allocate(obsStepIndex(numHeaderMaxMpi))
3656    allocate(obsLayerIndex(numHeaderMaxMpi))
3657    allocate(obsMethod(numHeaderMaxMpi))
3658    lenStnId = len(stnId)
3659    allocate(stnIdInt(lenStnId,numHeaderMaxMpi))
3660
3661    ! Initializations:
3662    valid(:) = .false.
3663    quality(:) = nullValue
3664    obsLatBurpFile(:) = 0
3665    obsLonBurpFile(:) = 0
3666    obsLayerIndex(:) = 0
3667    obsStepIndex(:) = 0
3668    obsMethod(:) = 0
3669    stnIdInt(:,:) = 0
3670    numObsStnIdInMpi(:) = 0
3671    numObsStnIdOut(:) = 0
3672    numObsStnIdOutMpi(:) = 0
3673    bgckCount = 0
3674    missingCount = 0
3675
3676    ! First pass through observations
3677    numStnId = 0
3678    call obs_set_current_header_list(obsdat,trim(familyType))
3679    HEADER1: do
3680      headerIndex = obs_getHeaderIndex(obsdat)
3681      if (headerIndex < 0) exit HEADER1
3682
3683      ! convert and store stnId as integer array
3684      stnId = obs_elem_c(obsdat,'STID',headerIndex)
3685      do charIndex = 1, lenStnId
3686        stnIdInt(charIndex,headerIndex) = iachar(stnId(charIndex:charIndex))
3687      end do
3688
3689      ! get latitude and longitude
3690      obsLonInDegrees = MPC_DEGREES_PER_RADIAN_R8 * obs_headElem_r(obsdat, OBS_LON, headerIndex)
3691      obsLatInDegrees = MPC_DEGREES_PER_RADIAN_R8 * obs_headElem_r(obsdat, OBS_LAT, headerIndex)
3692      obsLonBurpFile(headerIndex) = nint(100.0*obsLonInDegrees)
3693      obsLatBurpFile(headerIndex) = 9000 + nint(100.0*obsLatInDegrees)
3694
3695      ! get step bin
3696      obsDate = obs_headElem_i(obsdat, OBS_DAT, headerIndex)
3697      obsTime = obs_headElem_i(obsdat, OBS_ETM, headerIndex)
3698      call tim_getStepObsIndex(obsStepIndex_r8, tim_getDatestamp(), &
3699                               obsDate, obsTime, tim_nstepobs)
3700      obsStepIndex(headerIndex) = nint(obsStepIndex_r8)
3701
3702      ! find layer (assumes 1 level only per headerIndex)
3703      obsPressure = -1.0
3704      call obs_set_current_body_list(obsdat, headerIndex)
3705      BODY1: do 
3706        bodyIndex = obs_getBodyIndex(obsdat)
3707        if (bodyIndex < 0) exit BODY1
3708        
3709        if (obsPressure <= 0.0) then
3710          obsPressure = obs_bodyElem_r(obsdat, OBS_PPP, bodyIndex)
3711          exit BODY1
3712        end if
3713      end do BODY1
3714      ! modify obsPressure to be consistent with operational pgm
3715      obsPressure = 100.0*nint(obsPressure/100.0)
3716      deltaPressMin = abs( log(obsPressure) - log(layer(1)) )
3717      obsLayerIndex(headerIndex) = 1
3718      do layerIndex = 2, numLayers
3719        deltaPress = abs( log(obsPressure) - log(layer(layerIndex)) )
3720        if ( deltaPress < deltaPressMin ) then
3721          deltaPressMin = deltaPress
3722          obsLayerIndex(headerIndex) = layerIndex
3723        end if
3724      end do
3725
3726      ! extract additional information
3727      obsMethod(headerIndex) = obs_headElem_i(obsdat, OBS_SWMT, headerIndex)
3728
3729      ! set the observation quality based on QI1
3730      quality(headerIndex) = obs_headElem_i(obsdat, OBS_SWQ1, headerIndex)
3731
3732      ! find observation flags (assumes 1 level only per headerIndex)
3733      uObsFlag = nullValue
3734      vObsFlag = nullValue
3735      call obs_set_current_body_list(obsdat, headerIndex)
3736      BODY2: do 
3737        bodyIndex = obs_getBodyIndex(obsdat)
3738        if (bodyIndex < 0) exit BODY2
3739        obsVarno = obs_bodyElem_i(obsdat, OBS_VNM, bodyIndex)
3740        if (obsVarno == bufr_neuu) then
3741          uObsFlag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
3742        else if (obsVarno == bufr_nevv) then
3743          vObsFlag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
3744        end if
3745      end do BODY2
3746
3747      ! modify quality based on flags
3748      if (uObsFlag /= nullValue .and. vObsFlag /= nullValue ) then
3749        if ( btest(uObsFlag,16) .or. btest(vObsFlag,16) ) bgckCount = bgckCount + 1
3750        if ( btest(uObsFlag,16) .or. btest(vObsFlag,16) .or. &
3751             btest(uObsFlag,18) .or. btest(vObsFlag,18) ) then
3752          quality(headerIndex) = 0
3753        end if
3754      else
3755        quality(headerIndex) = 0
3756        MissingCount = MissingCount + 1
3757      end if
3758
3759    end do HEADER1
3760
3761    ! Gather needed information from all MPI tasks
3762    allocate(validMpi(numHeaderMaxMpi*mmpi_nprocs))
3763    allocate(validMpi2(numHeaderMaxMpi*mmpi_nprocs))
3764    allocate(qualityMpi(numHeaderMaxMpi*mmpi_nprocs))
3765    allocate(obsLatBurpFileMpi(numHeaderMaxMpi*mmpi_nprocs))
3766    allocate(obsLonBurpFileMpi(numHeaderMaxMpi*mmpi_nprocs))
3767    allocate(obsStepIndexMpi(numHeaderMaxMpi*mmpi_nprocs))
3768    allocate(obsLayerIndexMpi(numHeaderMaxMpi*mmpi_nprocs))
3769    allocate(obsMethodMpi(numHeaderMaxMpi*mmpi_nprocs))
3770    allocate(stnIdIntMpi(lenStnId,numHeaderMaxMpi*mmpi_nprocs))
3771
3772    nsize = numHeaderMaxMpi
3773    call rpn_comm_allgather(quality,    nsize, 'mpi_integer',  &
3774                            qualityMpi, nsize, 'mpi_integer', 'grid', ierr)
3775    call rpn_comm_allgather(obsLatBurpFile,    nsize, 'mpi_integer',  &
3776                            obsLatBurpFileMpi, nsize, 'mpi_integer', 'grid', ierr)
3777    call rpn_comm_allgather(obsLonBurpFile,    nsize, 'mpi_integer',  &
3778                            obsLonBurpFileMpi, nsize, 'mpi_integer', 'grid', ierr)
3779    call rpn_comm_allgather(obsStepIndex,    nsize, 'mpi_integer',  &
3780                            obsStepIndexMpi, nsize, 'mpi_integer', 'grid', ierr)
3781    call rpn_comm_allgather(obsLayerIndex,    nsize, 'mpi_integer',  &
3782                            obsLayerIndexMpi, nsize, 'mpi_integer', 'grid', ierr)
3783    call rpn_comm_allgather(obsMethod,    nsize, 'mpi_integer',  &
3784                            obsMethodMpi, nsize, 'mpi_integer', 'grid', ierr)
3785    nsize = lenStnId * numHeaderMaxMpi
3786    call rpn_comm_allgather(stnIdInt,    nsize, 'mpi_integer',  &
3787                            stnIdIntMpi, nsize, 'mpi_integer', 'grid', ierr)
3788
3789    ! build a global list of stnId over all mpi tasks
3790    numHeaderMpi = numHeaderMaxMpi * mmpi_nprocs
3791    HEADER: do headerIndex = 1, numHeaderMpi
3792      if (all(stnIdIntMpi(:,headerIndex) == 0)) cycle HEADER
3793
3794      ! Station ID converted back to character string
3795      do charIndex = 1, lenStnId
3796        stnId(charIndex:charIndex) = achar(stnIdIntMpi(charIndex,headerIndex))
3797      end do
3798
3799      if (numStnId < numStnIdMax ) then
3800        stnIdIndexFound = nullValue
3801        do stnIdIndex = 1, numStnId
3802          if ( stnidList(stnIdIndex) == stnid ) stnIdIndexFound = stnIdIndex
3803        end do
3804        if ( stnIdIndexFound == nullValue ) then
3805          numStnId = numStnId + 1
3806          stnidList(numStnId) = stnid
3807          stnIdIndexFound = numStnId
3808        end if
3809        numObsStnIdInMpi(stnIdIndexFound) = numObsStnIdInMpi(stnIdIndexFound) + 1
3810      else
3811        call utl_abort('thn_satWindsByDistance: numStnId too large')
3812      end if
3813    end do HEADER
3814
3815    allocate(headerIndexSorted(numHeaderMaxMpi*mmpi_nprocs))
3816    do headerIndex = 1, numHeaderMaxMpi*mmpi_nprocs
3817      headerIndexSorted(headerIndex) = headerIndex
3818    end do
3819    allocate(headerIndexSelected(numHeaderMaxMpi*mmpi_nprocs))
3820    headerIndexSelected(:) = 0
3821
3822    ! Thinning procedure
3823    call thn_QsortIntIgnoringNullValues(qualityMpi,headerIndexSorted,nullValue)
3824
3825    validMpi(:) = .false.
3826    STNIDLOOP: do stnIdIndex = 1, numStnId
3827      write(*,*) 'thn_satWindsByDistance: applying thinning for: ', &
3828                 trim(stnidList(stnIdIndex))
3829
3830      LAYERLOOP: do layerIndex = 1, numLayers
3831
3832        ! do selection of obs for 1 satellite and layer at a time, on separate mpi tasks
3833        mpiTaskId = mod((stnIdIndex-1)*numLayers + layerIndex - 1, mmpi_nprocs)
3834        if (mmpi_myid /= mpiTaskId) cycle LAYERLOOP
3835
3836        numSelected       = 0
3837        OBSLOOP1: do obsIndex1 = 1, numHeaderMpi
3838          headerIndex1 = headerIndexSorted(numHeaderMpi-obsIndex1+1)
3839
3840          ! only consider obs with current layer being considered
3841          if (obsLayerIndexMpi(headerIndex1) /= layerIndex) cycle OBSLOOP1
3842
3843          ! only consider obs with high quality
3844          if (qualityMpi(numHeaderMpi-obsIndex1+1) <= 10) cycle OBSLOOP1
3845
3846          ! only consider obs from current satellite
3847          do charIndex = 1, lenStnId
3848            stnId(charIndex:charIndex) = achar(stnIdIntMpi(charIndex,headerIndex1))
3849          end do
3850          if (stnidList(stnIdIndex) /= stnId) cycle OBSLOOP1
3851
3852          ! On compte le nombre d'observations qui sont deja
3853          ! selectionnees avec les memes parametres 'obsStepIndex' et 'obsLayerIndex'
3854          ! que l'observation consideree ici.
3855          obsAlreadySameStep = .false.
3856          OBSLOOP2: do obsIndex2 = 1, numSelected
3857            headerIndex2 = headerIndexSelected(obsIndex2)
3858            if ( obsStepIndexMpi(headerIndex1) == obsStepIndexMpi(headerIndex2) ) then
3859              if ( (obsLatBurpFileMpi(headerIndex1) == obsLatBurpFileMpi(headerIndex2)) .and. &
3860                   (obsLonBurpFileMpi(headerIndex1) == obsLonBurpFileMpi(headerIndex2)) ) then
3861                ! Si une observation selectionnee porte deja le meme lat, lon, layer et step.
3862                cycle OBSLOOP1
3863              end if
3864              obsAlreadySameStep = .true.
3865              exit OBSLOOP2
3866            end if
3867          end do OBSLOOP2
3868
3869          if ( obsAlreadySameStep ) then
3870            ! Calcule les distances entre la donnee courante et toutes celles choisies 
3871            ! precedemment.
3872            skipThisObs = .false.
3873            OBSLOOP3: do obsIndex2 = 1, numSelected
3874              headerIndex2 = headerIndexSelected(obsIndex2)
3875              if ( abs( obsStepIndexMpi(headerIndex1) - &
3876                        obsStepIndexMpi(headerIndex2) ) < deltemps ) then
3877                deltaLat = abs( obsLatBurpFileMpi(headerIndex1) - &
3878                                obsLatBurpFileMpi(headerIndex2) ) / 100.
3879                deltaLon = abs( obsLonBurpFileMpi(headerIndex1) - &
3880                                obsLonBurpFileMpi(headerIndex2) ) / 100.
3881                if(deltaLon > 180.) deltaLon = 360. - deltaLon
3882                obsLat1 = ((obsLatBurpFileMpi(headerIndex1) - 9000)/100.)
3883                obsLat2 = ((obsLatBurpFileMpi(headerIndex2) - 9000)/100.)
3884                if ( thn_distanceArc(deltaLat,deltaLon,obsLat1,obsLat2) < thinDistance ) then
3885                  skipThisObs = .true.
3886                  exit OBSLOOP3
3887                end if
3888              end if
3889            end do OBSLOOP3
3890
3891            if ( .not. skipThisObs ) then
3892
3893              ! On selectionne la donnee si toutes celles choisies sont au-dela
3894              ! de thinDistance. Cet evaluation est faite dan la boucle
3895              ! 'check_list' precedante.
3896              numSelected = numSelected + 1
3897              headerIndexSelected(numSelected) = headerIndex1
3898
3899            end if
3900
3901          else
3902
3903            ! On selectionne la donnee s'il y en a aucune choisie dans les intervalles 
3904            ! layer et step.
3905            numSelected = numSelected + 1
3906            headerIndexSelected(numSelected) = headerIndex1
3907
3908          end if
3909
3910        end do OBSLOOP1
3911
3912        do obsIndex1 = 1, numSelected
3913          validMpi(headerIndexSelected(obsIndex1)) = .true.
3914        end do
3915      
3916      end do LAYERLOOP
3917    end do STNIDLOOP
3918
3919    ! communicate values of validMpi computed on each mpi task
3920    nsize = numHeaderMaxMpi * mmpi_nprocs
3921    call rpn_comm_allReduce(validMpi, validMpi2, nsize, 'mpi_logical', &
3922                            'mpi_lor','grid',ierr)
3923
3924    ! Update local copy of valid from global mpi version
3925    headerIndexBeg = 1 + mmpi_myid * numHeaderMaxMpi
3926    headerIndexEnd = headerIndexBeg + numHeaderMaxMpi - 1
3927    valid(:) = validMpi2(headerIndexBeg:headerIndexEnd)
3928    
3929    countObs = count(valid)
3930    call rpn_comm_allReduce(countObs, countObsOutMpi, 1, 'mpi_integer', &
3931                            'mpi_sum','grid',ierr)
3932    write(*,*) 'thn_satWindsByDistance: number of obs after thinning = ', &
3933               countObs, countObsOutMpi
3934
3935    ! modify the obs flags and count number of obs kept for each stnId
3936    call obs_set_current_header_list(obsdat,trim(familyType))
3937    HEADER3: do
3938      headerIndex = obs_getHeaderIndex(obsdat)
3939      if (headerIndex < 0) exit HEADER3
3940
3941      ! do not keep this obs: set bit 11 and jump to the next obs
3942      if (.not. valid(headerIndex)) then
3943        call obs_set_current_body_list(obsdat, headerIndex)
3944        BODY3: do 
3945          bodyIndex = obs_getBodyIndex(obsdat)
3946          if (bodyIndex < 0) exit BODY3
3947          obsFlag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
3948          call obs_bodySet_i(obsdat, OBS_FLG, bodyIndex, ibset(obsFlag,11))
3949        end do BODY3
3950        cycle HEADER3
3951      end if
3952
3953      stnId = obs_elem_c(obsdat,'STID',headerIndex)
3954      stnIdIndexFound = nullValue
3955      do stnIdIndex = 1, numStnId
3956        if (stnidList(stnIdIndex) == stnId) stnIdIndexFound = stnIdIndex
3957      end do
3958      if (stnIdIndexFound == nullValue) call utl_abort('stnid not found in list')
3959      numObsStnIdOut(stnIdIndexFound) = numObsStnIdOut(stnIdIndexFound) + 1
3960    end do HEADER3
3961
3962    call rpn_comm_allReduce(numObsStnIdOut, numObsStnIdOutMpi, &
3963                            numStnIdMax, 'mpi_integer', 'mpi_sum', 'grid', ierr)
3964    call rpn_comm_allReduce(bgckCount, bgckCountMpi, 1, &
3965                            'mpi_integer', 'mpi_sum', 'grid', ierr)
3966    call rpn_comm_allReduce(missingCount, missingCountMpi, 1, &
3967                            'mpi_integer', 'mpi_sum', 'grid', ierr)
3968
3969    ! Print counts
3970    write(*,*)
3971    write(*,'(a30, i10)') ' Number of obs in  = ', countObsInMpi
3972    write(*,'(a30, i10)') ' Total number of reject = ', countObsInMpi - countObsOutMpi
3973    write(*,'(a30, i10)') ' Number of obs out = ', countObsOutMpi
3974    write(*,*)
3975    write(*,'(a30,4i10)') ' Number of rejects from bgck, thinned, missing obs ', &
3976         bgckCountMpi, &
3977         countObsInMpi - countObsOutMpi - bgckCountMpi - missingCountMpi, &
3978         missingCountMpi
3979    write(*,*)
3980    write(*,'(a30,i10)') 'Number of satellites found = ',numStnId
3981    write(*,*)
3982
3983    write(*,'(a30,2a15)') 'Satellite', 'nb AMVs in'
3984    write(*,*)
3985    do stnIdIndex = 1, numStnId
3986      write(*,'(a30,2i15)') stnidList(stnIdIndex), numObsStnIdInMpi(stnIdIndex)
3987    end do
3988    write(*,*)
3989    write(*,'(a30,2i10,f10.4)') 'Total number of obs in : ',sum(numObsStnIdInMpi)
3990
3991    write(*,*)
3992    write(*,'(a30,2a15)') 'Satellite', 'nb AMVs out'
3993    write(*,*)
3994    do stnIdIndex = 1, numStnId
3995      write(*,'(a30,2i15)') stnidList(stnIdIndex), numObsStnIdOutMpi(stnIdIndex)
3996    end do
3997    write(*,*)
3998    write(*,'(a30,2i10,f10.4)') 'Total number of obs out : ',sum(numObsStnIdOutMpi)
3999
4000    ! Deallocations:
4001    deallocate(valid)
4002    deallocate(quality)
4003    deallocate(obsLatBurpFile)
4004    deallocate(obsLonBurpFile)
4005    deallocate(obsStepIndex)
4006    deallocate(obsLayerIndex)
4007    deallocate(obsMethod)
4008    deallocate(stnIdInt)
4009    deallocate(validMpi)
4010    deallocate(validMpi2)
4011    deallocate(qualityMpi)
4012    deallocate(obsLatBurpFileMpi)
4013    deallocate(obsLonBurpFileMpi)
4014    deallocate(obsStepIndexMpi)
4015    deallocate(obsLayerIndexMpi)
4016    deallocate(obsMethodMpi)
4017    deallocate(stnIdIntMpi)
4018    deallocate(headerIndexSorted)
4019    deallocate(headerIndexSelected)
4020
4021    write(*,*)
4022    write(*,*) 'thn_satWindsByDistance: Finished'
4023    write(*,*)
4024
4025  end subroutine thn_satWindsByDistance
4026
4027  !--------------------------------------------------------------------------
4028  ! thn_QsortIntIgnoringNullValues
4029  !--------------------------------------------------------------------------
4030  subroutine thn_QsortIntIgnoringNullValues(A,B,nullValue)
4031    !
4032    !:Purpose: Quick sort algorithm for integer data
4033    !           Calling 'thn_QsortInt' on array without missing values (-1)
4034    !           The 'QuickSort' algorithm gives different results
4035    !           depending on the size of the input array.  The same
4036    !           values won't be put in the same order if null values
4037    !           are inserted in the array.  So this routine filters
4038    !           the input array and asks for 'thn_QsortInt' to sort
4039    !           the filtered array and then insert back the values to
4040    !           respect the order of the original input array.
4041    !
4042    implicit none
4043
4044    ! Arguments:
4045    integer, intent(inout) :: A(:)
4046    integer, intent(inout) :: B(:)
4047    integer, intent(in)    :: nullValue
4048
4049    ! Locals:
4050    integer :: numSelected, indexSelected, index
4051    integer, allocatable :: buffer(:), indices(:)
4052
4053    ! Compute the number of non-null values in array 'A'
4054    indexSelected = 0
4055    do index = 1, size(A)
4056      if ( A(index) /= nullValue ) then
4057        indexSelected = indexSelected + 1
4058      end if
4059    end do
4060    numSelected = indexSelected
4061
4062    ! Allocate temporary arrays
4063    allocate(buffer(numSelected))
4064    allocate(indices(numSelected))
4065
4066    ! Initialize the temporary arrays
4067    indexSelected = 0
4068    do index = 1, size(A)
4069      if ( A(index) /= nullValue ) then
4070        indexSelected = indexSelected + 1
4071        buffer(indexSelected) = A(index)
4072        ! keep the index of the value in the original array
4073        indices(indexSelected) = index
4074      end if
4075    end do
4076
4077    call thn_QsortInt(buffer,indices)
4078
4079    ! Populate again the array 'A' with the data from 'buffer'
4080    ! Populate again the array 'B' with the data from 'indices'
4081    indexSelected = 0
4082    do index = 1, size(A)
4083      if ( A(index) /= nullValue ) then
4084        indexSelected = indexSelected + 1
4085        A(index) = buffer(indexSelected)
4086        B(index) = indices(indexSelected)
4087      else
4088        B(index) = index
4089      end if
4090    end do
4091
4092    deallocate(buffer)
4093    deallocate(indices)
4094
4095  end subroutine thn_QsortIntIgnoringNullValues
4096
4097  !--------------------------------------------------------------------------
4098  ! thn_QsortInt
4099  !--------------------------------------------------------------------------
4100  recursive subroutine thn_QsortInt(A,B)
4101    !
4102    !:Purpose: Quick sort algorithm for integer data.
4103    !
4104    implicit none
4105
4106    ! Arguments:
4107    integer, intent(inout) :: A(:)
4108    integer, intent(inout) :: B(:)
4109
4110    ! Locals:
4111    integer :: iq
4112
4113    if (size(A) > 1) then
4114      call thn_QsortIntpartition(A,B,iq)
4115      call thn_QsortInt(A(:iq-1),B(:iq-1))
4116      call thn_QsortInt(A(iq:),B(iq:))
4117    end if
4118
4119  end subroutine thn_QsortInt
4120
4121  !--------------------------------------------------------------------------
4122  ! thn_QsortIntpartition
4123  !--------------------------------------------------------------------------
4124  subroutine thn_QsortIntpartition(A,B,marker)
4125    !
4126    !:Purpose: Subroutine called in quick sort for integers.
4127    !
4128    implicit none
4129
4130    ! Arguments:
4131    integer, intent(inout) :: A(:)
4132    integer, intent(inout) :: B(:)
4133    integer, intent(out)   :: marker
4134
4135    ! Locals:
4136    integer :: i, j, tmpi
4137    integer :: temp
4138    integer :: x      ! pivot point
4139
4140    x = A(1)
4141    i= 0
4142    j= size(A) + 1
4143
4144    do
4145      j = j-1
4146      do
4147        if (A(j) <= x) exit
4148        j = j-1
4149      end do
4150      i = i+1
4151      do
4152        if (A(i) >= x) exit
4153        i = i+1
4154      end do
4155      if (i < j) then
4156        ! exchange A(i) and A(j)
4157        temp = A(i)
4158        A(i) = A(j)
4159        A(j) = temp
4160        tmpi = B(i)
4161        B(i) = B(j)
4162        B(j) = tmpi
4163      else if (i == j) then
4164        marker = i+1
4165        return
4166      else
4167        marker = i
4168        return
4169      end if
4170    end do
4171
4172  end subroutine thn_QsortIntpartition
4173
4174  !--------------------------------------------------------------------------
4175  ! thn_QsortReal8
4176  !--------------------------------------------------------------------------
4177  recursive subroutine thn_QsortReal8(A,B)
4178    !
4179    !:Purpose: Quick sort algorithm for real8 data.
4180    !
4181    implicit none
4182
4183    ! Arguments:
4184    real(8), intent(inout) :: A(:)
4185    integer, intent(inout) :: B(:)
4186
4187    ! Locals:
4188    integer :: iq
4189
4190    if (size(A) > 1) then
4191      call thn_QsortReal8partition(A,B,iq)
4192      call thn_QsortReal8(A(:iq-1),B(:iq-1))
4193      call thn_QsortReal8(A(iq:),B(iq:))
4194    end if
4195
4196  end subroutine thn_QsortReal8
4197
4198  !--------------------------------------------------------------------------
4199  ! thn_QsortReal8partition
4200  !--------------------------------------------------------------------------
4201  subroutine thn_QsortReal8partition(A,B,marker)
4202    !
4203    !:Purpose: Subroutine called for quick sort of real8 data.
4204    !
4205    implicit none
4206
4207    ! Arguments:
4208    real(8), intent(inout) :: A(:)
4209    integer, intent(inout) :: B(:)
4210    integer, intent(out)   :: marker
4211
4212    ! Locals:
4213    integer :: i, j, tmpi
4214    real(8) :: temp
4215    real(8) :: x      ! pivot point
4216
4217    x = A(1)
4218    i= 0
4219    j= size(A) + 1
4220
4221    do
4222      j = j-1
4223      do
4224        if (A(j) <= x) exit
4225        j = j-1
4226      end do
4227      i = i+1
4228      do
4229        if (A(i) >= x) exit
4230        i = i+1
4231      end do
4232      if (i < j) then
4233        ! exchange A(i) and A(j)
4234        temp = A(i)
4235        A(i) = A(j)
4236        A(j) = temp
4237        tmpi = B(i)
4238        B(i) = B(j)
4239        B(j) = tmpi
4240      else if (i == j) then
4241        marker = i+1
4242        return
4243      else
4244        marker = i
4245        return
4246      end if
4247    end do
4248
4249  end subroutine thn_QsortReal8partition
4250
4251  !--------------------------------------------------------------------------
4252  ! thn_distanceArc
4253  !--------------------------------------------------------------------------
4254  real(4) function thn_distanceArc( deltaLat, deltaLon, lat1, lat2 )
4255    !
4256    !:Purpose: Compute arc distance.
4257    !
4258    implicit none
4259
4260    ! Arguments:
4261    real(4), intent(in) :: deltaLat
4262    real(4), intent(in) :: deltaLon
4263    real(4), intent(in) :: lat1
4264    real(4), intent(in) :: lat2
4265
4266    ! Locals:
4267    real(4), parameter :: PI = 3.141592654
4268    real(4), parameter :: RT = 6374.893
4269    real(4) :: lat1_r, lat2_r, deltaLat_r, deltaLon_r, term_a
4270
4271    lat1_r = lat1*PI/180.
4272    lat2_r = lat2*PI/180.
4273    deltaLat_r = deltaLat*PI/180.
4274    deltaLon_r = deltaLon*PI/180.
4275
4276    term_a  = sin(deltaLat_r/2)*sin(deltaLat_r/2) +  &
4277              cos(lat1_r)*cos(lat2_r)*sin(deltaLon_r/2)*sin(deltaLon_r/2)
4278    if(term_a < 0.0) term_a = 0.0
4279    if(term_a > 1.0) term_a = 1.0
4280
4281    thn_distanceArc = 2*RT*asin(sqrt(term_a))
4282
4283  end function thn_distanceArc
4284
4285  !--------------------------------------------------------------------------
4286  ! thn_aircraftByBoxes
4287  !--------------------------------------------------------------------------
4288  subroutine thn_aircraftByBoxes(obsdat, familyType, deltmax)
4289    !
4290    !:Purpose: Original method for thinning aircraft data by lat-lon boxes.
4291    !           Set bit 11 of OBS_FLG on observations that are to be rejected.
4292    !
4293    implicit none
4294
4295    ! Arguments:
4296    type(struct_obs), intent(inout) :: obsdat
4297    character(len=*), intent(in)    :: familyType
4298    integer,          intent(in)    :: deltmax
4299
4300    ! Locals:
4301    character(len=4), allocatable :: varNamesPsfc(:)
4302    character(len=20) :: trlmFileName
4303    character(len=2)  :: fileNumber
4304    type(struct_hco), pointer :: hco_thinning
4305    type(struct_vco), pointer :: vco_sfc
4306    type(struct_gsv)          :: stateVectorPsfc
4307    integer :: numLon, numLat, nsize, headerIndexBeg, headerIndexEnd
4308    integer :: nulnam, ierr, lonIndex, latIndex, levIndex, stepIndex, codtyp
4309    integer :: obsLonIndex, obsLatIndex, obsLevIndex, obsStepIndex
4310    integer :: numHeader, numHeaderMaxMpi, headerIndex, bodyIndex
4311    integer :: aiTypeCount(4), aiTypeCountMpi(4)
4312    integer :: obsFlag, obsVarno, obsDate, obsTime, countObs, countObsMpi
4313    logical :: ttMissing, huMissing, uuMissing, vvMissing, ddMissing, ffMissing
4314    real(8) :: zpresa, zpresb, obsPressure, delMinutes, obsStepIndex_r8
4315    real(8) :: obsLonInRad, obsLatInRad, obsLonInDegrees, obsLatInDegrees
4316    real(8) :: deltaLon, deltaLat, deltaPress, midDistance, score
4317    real(4), allocatable :: gridLat(:), gridLon(:)
4318    integer, allocatable :: rejectCount(:), rejectCountMpi(:)
4319    real(8), allocatable :: gridPressure(:,:,:,:)
4320    real(8), pointer     :: surfPressure(:,:,:,:)
4321    logical, allocatable :: valid(:), validMpi(:), isAircraft(:)
4322    integer, allocatable :: obsLatIndexVec(:), obsLonIndexVec(:)
4323    integer, allocatable :: obsTimeIndexVec(:), obsLevIndexVec(:)
4324    integer, allocatable :: obsLatIndexMpi(:), obsLonIndexMpi(:)
4325    integer, allocatable :: obsTimeIndexMpi(:), obsLevIndexMpi(:)
4326    logical, allocatable :: obsUVPresent(:), obsTTPresent(:)
4327    logical, allocatable :: obsUVPresentMpi(:), obsTTPresentMpi(:)
4328    real(4), allocatable :: obsDistance(:), obsUU(:), obsVV(:), obsTT(:)
4329    real(4), allocatable :: obsDistanceMpi(:), obsUUMpi(:), obsVVMpi(:), obsTTMpi(:)
4330    integer, allocatable :: handlesGrid(:,:,:), numObsGrid(:,:,:)
4331    real(4), allocatable :: minScoreGrid(:,:,:), minDistGrid(:,:,:), maxDistGrid(:,:,:)
4332    real(4), allocatable :: uuSumGrid(:,:,:), vvSumGrid(:,:,:), ttSumGrid(:,:,:)
4333    integer, external :: fnom, fclos
4334    integer, parameter :: maxLev = 500
4335
4336    ! Namelist variables:
4337    real(8) :: rprefinc      ! parameter for defining fixed set of model levels for vertical thinning
4338    real(8) :: rptopinc      ! parameter for defining fixed set of model levels for vertical thinning
4339    real(8) :: rcoefinc      ! parameter for defining fixed set of model levels for vertical thinning
4340    real(4) :: vlev(maxLev)  ! parameter for defining fixed set of model levels for vertical thinning
4341    integer :: numlev        ! MUST NOT BE INCLUDED IN NAMELIST!
4342    namelist /namgem/rprefinc, rptopinc, rcoefinc, numlev, vlev
4343
4344    write(*,*)
4345    write(*,*) 'thn_aircraftByBoxes: Starting'
4346    write(*,*)
4347
4348    numHeader = obs_numHeader(obsdat)
4349    call rpn_comm_allReduce(numHeader, numHeaderMaxMpi, 1, 'mpi_integer', &
4350                            'mpi_max','grid',ierr)
4351
4352    allocate(valid(numHeaderMaxMpi))
4353    allocate(isAircraft(numHeaderMaxMpi))
4354    valid(:) = .false.
4355    aiTypeCount(:) = 0
4356
4357    call obs_set_current_header_list(obsdat,trim(familyType))
4358    HEADER0: do
4359      headerIndex = obs_getHeaderIndex(obsdat)
4360      if (headerIndex < 0) exit HEADER0
4361
4362      ! check observation type
4363      codtyp = obs_headElem_i(obsdat, OBS_ITY, headerIndex)
4364      if ( codtyp == codtyp_get_codtyp('airep') ) then
4365        aiTypeCount(1) = aiTypeCount(1) + 1
4366        valid(headerIndex) = .true.
4367      else if ( codtyp == codtyp_get_codtyp('amdar')  ) then
4368        aiTypeCount(2) = aiTypeCount(2) + 1
4369        valid(headerIndex) = .true.
4370      else if ( codtyp == codtyp_get_codtyp('acars') ) then
4371        aiTypeCount(3) = aiTypeCount(3) + 1
4372        valid(headerIndex) = .true.
4373      else if ( codtyp == codtyp_get_codtyp('ads') ) then
4374        aiTypeCount(4) = aiTypeCount(4) + 1
4375        valid(headerIndex) = .true.
4376      end if
4377    end do HEADER0
4378    isAircraft(:) = valid(:)
4379
4380    ! Return if no aircraft obs to thin
4381    allocate(validMpi(numHeaderMaxMpi*mmpi_nprocs))
4382    nsize = numHeaderMaxMpi
4383    call rpn_comm_allgather(valid,    nsize, 'mpi_logical',  &
4384                            validMpi, nsize, 'mpi_logical', 'grid', ierr)
4385    if (count(validMpi(:)) == 0) then
4386      write(*,*) 'thn_aircraftByBoxes: no aircraft observations present'
4387      return
4388    end if
4389
4390    write(*,*) 'thn_aircraftByBoxes: numHeader, numHeaderMaxMpi = ', &
4391         numHeader, numHeaderMaxMpi
4392
4393    countObs = count(valid(:))
4394    call rpn_comm_allReduce(countObs, countObsMpi, 1, 'mpi_integer', &
4395                            'mpi_sum','grid',ierr)
4396    write(*,*) 'thn_aircraftByBoxes: number of obs initial = ', countObs, countObsMpi
4397
4398    ! Setup horizontal thinning grid
4399    nullify(hco_thinning)
4400    call hco_SetupFromFile(hco_thinning, './analysisgrid_thinning_ai', &
4401                           'ANALYSIS', 'Analysis')
4402
4403    ! Default namelist values
4404    numlev = MPC_missingValue_INT
4405    vlev(:) = -1
4406    rprefinc = 0.0d0
4407    rptopinc = 0.0d0
4408    rcoefinc = 0.0d0
4409    ! Read the namelist defining the vertical levels
4410    if (utl_isNamelistPresent('namgem','./flnml')) then
4411      nulnam = 0
4412      ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
4413      if (ierr /= 0) call utl_abort('thn_aircraftByBoxes: Error opening file flnml')
4414      read(nulnam,nml=namgem,iostat=ierr)
4415      if (ierr /= 0) call utl_abort('thn_aircraftByBoxes: Error reading namgem namelist')
4416      if (mmpi_myid == 0) write(*,nml=namgem)
4417      ierr = fclos(nulnam)
4418      if (numlev /= MPC_missingValue_INT) then
4419        call utl_abort('thn_aircraftByBoxes: check NAMGEM namelist section: numlev should be removed')
4420      end if
4421      numlev = 0
4422      do levIndex = 1, maxLev
4423        if (vlev(levIndex) == -1) exit
4424        numlev = numlev + 1
4425      end do
4426    else
4427      call utl_abort('thn_aircraftByBoxes: Namelist block namgem is missing in the namelist.')
4428    end if
4429
4430    ! Setup thinning grid parameters
4431    numLon = hco_thinning%ni
4432    numLat = hco_thinning%nj
4433    allocate(gridLat(numLat))
4434    allocate(gridLon(numLon))
4435    gridLon(:) = hco_thinning%lon(:) * MPC_DEGREES_PER_RADIAN_R8
4436    gridLat(:) = hco_thinning%lat(:) * MPC_DEGREES_PER_RADIAN_R8
4437    write(*,*) 'thinning grid vlev = '
4438    write(*,*) vlev(1:numLev)
4439
4440    ! Allocate vectors
4441    allocate(rejectCount(tim_nstepObs))
4442    allocate(obsLatIndexVec(numHeaderMaxMpi))
4443    allocate(obsLonIndexVec(numHeaderMaxMpi))
4444    allocate(obsLevIndexVec(numHeaderMaxMpi))
4445    allocate(obsTimeIndexVec(numHeaderMaxMpi))
4446    allocate(obsDistance(numHeaderMaxMpi))
4447    allocate(obsUU(numHeaderMaxMpi))
4448    allocate(obsVV(numHeaderMaxMpi))
4449    allocate(obsTT(numHeaderMaxMpi))
4450    allocate(obsUVPresent(numHeaderMaxMpi))
4451    allocate(obsTTPresent(numHeaderMaxMpi))
4452
4453    ! Allocate mpi global vectors
4454    allocate(rejectCountMpi(tim_nstepObs))
4455    allocate(obsLatIndexMpi(numHeaderMaxMpi*mmpi_nprocs))
4456    allocate(obsLonIndexMpi(numHeaderMaxMpi*mmpi_nprocs))
4457    allocate(obsLevIndexMpi(numHeaderMaxMpi*mmpi_nprocs))
4458    allocate(obsTimeIndexMpi(numHeaderMaxMpi*mmpi_nprocs))
4459    allocate(obsDistanceMpi(numHeaderMaxMpi*mmpi_nprocs))
4460    allocate(obsUUMpi(numHeaderMaxMpi*mmpi_nprocs))
4461    allocate(obsVVMpi(numHeaderMaxMpi*mmpi_nprocs))
4462    allocate(obsTTMpi(numHeaderMaxMpi*mmpi_nprocs))
4463    allocate(obsUVPresentMpi(numHeaderMaxMpi*mmpi_nprocs))
4464    allocate(obsTTPresentMpi(numHeaderMaxMpi*mmpi_nprocs))
4465
4466    ! Initialize vectors
4467    rejectCount(:) = 0
4468    obsLatIndexVec(:) = 0
4469    obsLonIndexVec(:) = 0
4470    obsLevIndexVec(:) = 0
4471    obsTimeIndexVec(:) = 0
4472    obsDistance(:) = 0.
4473    obsUU(:) = 0.
4474    obsVV(:) = 0.
4475    obsTT(:) = 0.
4476    obsUVPresent(:) = .true.
4477    obsTTPresent(:) = .true.
4478
4479    ! Read and interpolate the trial surface pressure
4480    nullify(vco_sfc)
4481    trlmFileName = './trlm_01'
4482    call vco_setupFromFile(vco_sfc, trlmFileName)
4483    if (vco_sfc%Vcode == 5100) then
4484      allocate(varNamesPsfc(2))
4485      varNamesPsfc = (/'P0','P0LS'/)
4486    else
4487      allocate(varNamesPsfc(1))
4488      varNamesPsfc = (/'P0'/)
4489    end if
4490    call gsv_allocate( stateVectorPsfc, tim_nstepobs, hco_thinning, vco_sfc, &
4491                       datestamp_opt=tim_getDatestamp(), mpi_local_opt=.false., &
4492                       varNames_opt=varNamesPsfc, hInterpolateDegree_opt='LINEAR' )
4493    deallocate(varNamesPsfc)
4494    do stepIndex = 1, tim_nstepobs
4495      write(fileNumber,'(I2.2)') stepIndex
4496      trlmFileName = './trlm_' // trim(fileNumber)
4497      call gio_readFromFile( stateVectorPsfc, trlmFileName, ' ', ' ',  &
4498                             stepIndex_opt=stepIndex, containsFullField_opt=.true. )
4499    end do
4500
4501    ! compute pressure of each model level using p0
4502    allocate(gridPressure(numLon,numLat,numLev,tim_nstepobs))
4503    gridPressure(:,:,:,:) = -1.0
4504    call gsv_getField(stateVectorPsfc,surfPressure)
4505    do stepIndex = 1, tim_nstepobs
4506      do levIndex  = 1, numLev
4507        zpresb = ( (vlev(levIndex) - rptopinc/rprefinc) /  &
4508                   (1.0D0-rptopinc/rprefinc) )**rcoefinc
4509        zpresa = rprefinc * (vlev(levIndex)-zpresb)
4510        do latIndex = 1, numLat
4511          do lonIndex = 1, numLon
4512            gridPressure(lonIndex,latIndex,levIndex,stepIndex) =  &
4513                 zpresa + zpresb*surfPressure(lonIndex,latIndex,1,stepIndex)
4514          end do
4515        end do
4516      end do
4517    end do
4518    call gsv_deallocate(stateVectorPsfc)
4519
4520    call obs_set_current_header_list(obsdat,trim(familyType))
4521    HEADER1: do
4522      headerIndex = obs_getHeaderIndex(obsdat)
4523      if (headerIndex < 0) exit HEADER1
4524
4525      ! find time difference
4526      obsDate = obs_headElem_i(obsdat, OBS_DAT, headerIndex)
4527      obsTime = obs_headElem_i(obsdat, OBS_ETM, headerIndex)
4528      call tim_getStepObsIndex(obsStepIndex_r8, tim_getDatestamp(), &
4529                               obsDate, obsTime, tim_nstepobs)
4530      obsStepIndex = nint(obsStepIndex_r8)
4531      delMinutes = abs(nint(60.0 * tim_dstepobs * abs(real(obsStepIndex) - obsStepIndex_r8)))
4532
4533      ! check time window
4534      if ( delMinutes > deltmax ) then
4535        valid(headerIndex) = .false.
4536        rejectCount(obsStepIndex) = rejectCount(obsStepIndex) + 1
4537      end if
4538
4539      ! Only accept obs below 175hPa
4540      obsPressure = -1.0d0
4541      call obs_set_current_body_list(obsdat, headerIndex)
4542      BODY: do 
4543        bodyIndex = obs_getBodyIndex(obsdat)
4544        if (bodyIndex < 0) exit BODY
4545        
4546        if (obsPressure < 0.0d0) then
4547          obsPressure = obs_bodyElem_r(obsdat, OBS_PPP, bodyIndex)
4548          if ( obsPressure < 17500.0 .or. obsPressure > 110000.0 ) valid(headerIndex) = .false.
4549        end if
4550      end do BODY
4551
4552      ttMissing = .true.
4553      huMissing = .true.
4554      uuMissing = .true.
4555      vvMissing = .true.
4556      ddMissing = .true.
4557      ffMissing = .true.      
4558      call obs_set_current_body_list(obsdat, headerIndex)
4559      BODY2: do 
4560        bodyIndex = obs_getBodyIndex(obsdat)
4561        if (bodyIndex < 0) exit BODY2
4562        
4563        obsFlag  = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
4564        obsVarno = obs_bodyElem_i(obsdat, OBS_VNM, bodyIndex)
4565
4566        ! find number of elements availables
4567        if (obsVarno == BUFR_NETT) then
4568          if ( .not. (btest(obsFlag,18) .or. btest(obsFlag,16) .or. &
4569                      btest(obsFlag,9)  .or. btest(obsFlag,8)  .or. &
4570                      btest(obsFlag,2)) ) then
4571            ttMissing = .false.
4572            obsTT(headerIndex) = obs_bodyElem_r(obsdat, OBS_VAR, bodyIndex)
4573          end if
4574        else if (obsVarno == BUFR_NEES) then
4575          if ( .not. (btest(obsFlag,18) .or. btest(obsFlag,16) .or. &
4576                      btest(obsFlag,9)  .or. btest(obsFlag,8)  .or. &
4577                      btest(obsFlag,2)) ) huMissing = .false.
4578        else if (obsVarno == BUFR_NEUU) then
4579          if ( .not. (btest(obsFlag,18) .or. btest(obsFlag,16) .or. &
4580                      btest(obsFlag,9)  .or. btest(obsFlag,8)  .or. &
4581                      btest(obsFlag,2)) ) then
4582            uuMissing = .false.
4583            obsUU(headerIndex) = obs_bodyElem_r(obsdat, OBS_VAR, bodyIndex)
4584          end if
4585        else if (obsVarno == BUFR_NEVV) then
4586          if ( .not. (btest(obsFlag,18) .or. btest(obsFlag,16) .or. &
4587                      btest(obsFlag,9)  .or. btest(obsFlag,8)  .or. &
4588                      btest(obsFlag,2)) ) then
4589            vvMissing = .false.
4590            obsVV(headerIndex) = obs_bodyElem_r(obsdat, OBS_VAR, bodyIndex)
4591          end if
4592        else if (obsVarno == BUFR_NEDD) then
4593          if ( .not. (btest(obsFlag,18) .or. btest(obsFlag,16) .or. &
4594                      btest(obsFlag,9)  .or. btest(obsFlag,8)  .or. &
4595                      btest(obsFlag,2)) ) ddMissing = .false.
4596        else if (obsVarno == BUFR_NEFF) then
4597          if ( .not. (btest(obsFlag,18) .or. btest(obsFlag,16) .or. &
4598                      btest(obsFlag,9)  .or. btest(obsFlag,8)  .or. &
4599                      btest(obsFlag,2)) ) ffMissing = .false.
4600        end if
4601
4602      end do BODY2
4603
4604      ! wind components are rejected if speed or direction 
4605      if (ddMissing .or. ffMissing) then
4606        uuMissing = .true.
4607        vvMissing = .true.
4608      end if
4609
4610      ! eliminate records with nothing to assimilate
4611      if (ttMissing .and. huMissing .and. uuMissing .and. vvMissing) then
4612        rejectCount(obsStepIndex) = rejectCount(obsStepIndex) + 1
4613        valid(headerIndex) = .false.
4614      end if
4615
4616      if( uuMissing .or. vvMissing ) obsUVPresent(headerIndex) = .false.
4617      if( ttMissing )                obsTTPresent(headerIndex) = .false.
4618
4619      ! Lat and Lon for each observation
4620      obsLonInRad = obs_headElem_r(obsdat, OBS_LON, headerIndex)
4621      obsLatInRad = obs_headElem_r(obsdat, OBS_LAT, headerIndex)
4622      obsLonInDegrees = MPC_DEGREES_PER_RADIAN_R8 * obsLonInRad
4623      obsLatInDegrees = MPC_DEGREES_PER_RADIAN_R8 * obsLatInRad
4624
4625      ! latitude index
4626      deltaLat = abs(gridLat(1) - obsLatInDegrees)
4627      obsLatIndex = 1
4628      do latIndex = 2, numLat
4629        if (abs(gridLat(latIndex) - obsLatInDegrees) < deltaLat) then
4630          deltaLat = abs(gridLat(latIndex) - obsLatInDegrees)
4631          obsLatIndex = latIndex
4632        end if
4633      end do
4634
4635      ! longitude index
4636      deltaLon = abs(gridLon(1) - obsLonInDegrees)
4637      obsLonIndex = 1
4638      do lonIndex = 2, numLon
4639        if ( abs(gridLon(lonIndex) - obsLonInDegrees) < deltaLon ) then
4640          deltaLon = abs(gridLon(lonIndex) - obsLonInDegrees)
4641          obsLonIndex = lonIndex
4642        end if
4643      end do
4644
4645      ! layer index
4646      deltaPress = abs(gridPressure(obsLonIndex,obsLatIndex,1,obsStepIndex) - obsPressure)
4647      obsLevIndex = 1
4648      do levIndex = 2, numLev
4649        if ( abs(gridPressure(obsLonIndex,obsLatIndex,levIndex,obsStepIndex) - obsPressure) < &
4650             deltaPress ) then
4651          deltaPress = abs( gridPressure(obsLonIndex,obsLatIndex,levIndex,obsStepIndex) - &
4652                            obsPressure )
4653          obsLevIndex = levIndex
4654        end if
4655      end do
4656
4657      obsLatIndexVec(headerIndex) = obsLatIndex
4658      obsLonIndexVec(headerIndex) = obsLonIndex
4659      obsLevIndexVec(headerIndex) = obsLevIndex
4660      obsTimeIndexVec(headerIndex) = obsStepIndex
4661      obsDistance(headerIndex) = sqrt((deltaLon*3)**2 + (deltaLat*3)**2 + (deltaPress/100.0)**2)
4662
4663    end do HEADER1
4664
4665    call rpn_comm_allReduce(aiTypeCount, aiTypeCountMpi, 4, 'mpi_integer', &
4666                            'mpi_sum','grid',ierr)
4667    call rpn_comm_allReduce(rejectCount, rejectCountMpi, tim_nstepObs, 'mpi_integer', &
4668                            'mpi_sum','grid',ierr)
4669
4670    write(*,*)
4671    write(*,'(a50,i10)') ' Total number of obs = ', sum(aiTypeCountMpi(:))
4672    write(*,*)
4673    do stepIndex = 1, tim_nstepobs
4674      write(*,'(a50,2i10)')' Number of rejects for bin = ', stepIndex, rejectCountMpi(stepIndex)
4675    end do
4676    write(*,'(a50,i10)')' Total number of rejects = ', sum(rejectCountMpi)
4677    write(*,*)
4678    write(*,'(a50,i10)') '====nb AIREP = ', aiTypeCountMpi(1)
4679    write(*,'(a50,i10)') '====nb AMDAR = ', aiTypeCountMpi(2)
4680    write(*,'(a50,i10)') '====nb ACARS = ', aiTypeCountMpi(3)
4681    write(*,'(a50,i10)') '====nb ADS = ',   aiTypeCountMpi(4)
4682    write(*,*)
4683
4684    allocate(handlesGrid(numLat,numLon,numLev))
4685    allocate(minScoreGrid(numLat,numLon,numLev))
4686    allocate(minDistGrid(numLat,numLon,numLev))
4687    allocate(maxDistGrid(numLat,numLon,numLev))
4688    allocate(numObsGrid(numLat,numLon,numLev))
4689    allocate(uuSumGrid(numLat,numLon,numLev))
4690    allocate(vvSumGrid(numLat,numLon,numLev))
4691    allocate(ttSumGrid(numLat,numLon,numLev))
4692
4693    ! Make all inputs to the following tests mpiglobal
4694    nsize = numHeaderMaxMpi
4695    call rpn_comm_allgather(valid,    nsize, 'mpi_logical',  &
4696                            validMpi, nsize, 'mpi_logical', 'grid', ierr)
4697    call rpn_comm_allgather(obsLatIndexVec, nsize, 'mpi_integer',  &
4698                            obsLatIndexMpi, nsize, 'mpi_integer', 'grid', ierr)
4699    call rpn_comm_allgather(obsLonIndexVec, nsize, 'mpi_integer',  &
4700                            obsLonIndexMpi, nsize, 'mpi_integer', 'grid', ierr)
4701    call rpn_comm_allgather(obsLevIndexVec, nsize, 'mpi_integer',  &
4702                            obsLevIndexMpi, nsize, 'mpi_integer', 'grid', ierr)
4703    call rpn_comm_allgather(obsTimeIndexVec,nsize, 'mpi_integer',  &
4704                            obsTimeIndexMpi,nsize, 'mpi_integer', 'grid', ierr)
4705    call rpn_comm_allgather(obsDistance,    nsize, 'mpi_real4',  &
4706                            obsDistanceMpi, nsize, 'mpi_real4', 'grid', ierr)
4707    call rpn_comm_allgather(obsUU,    nsize, 'mpi_real4',  &
4708                            obsUUMpi, nsize, 'mpi_real4', 'grid', ierr)
4709    call rpn_comm_allgather(obsVV,    nsize, 'mpi_real4',  &
4710                            obsVVMpi, nsize, 'mpi_real4', 'grid', ierr)
4711    call rpn_comm_allgather(obsTT,    nsize, 'mpi_real4',  &
4712                            obsTTMpi, nsize, 'mpi_real4', 'grid', ierr)
4713    call rpn_comm_allgather(obsUVPresent,    nsize, 'mpi_logical',  &
4714                            obsUVPresentMpi, nsize, 'mpi_logical', 'grid', ierr)
4715    call rpn_comm_allgather(obsTTPresent,    nsize, 'mpi_logical',  &
4716                            obsTTPresentMpi, nsize, 'mpi_logical', 'grid', ierr)
4717
4718    STEP: do stepIndex = 1, tim_nstepobs
4719      handlesGrid(:,:,:) = -1
4720      minScoreGrid(:,:,:) = 1000000.
4721      minDistGrid(:,:,:) = 1000000.
4722      maxDistGrid(:,:,:) = 0.
4723      numObsGrid(:,:,:) = 0 
4724      uuSumGrid(:,:,:) = 0. 
4725      vvSumGrid(:,:,:) = 0. 
4726      ttSumGrid(:,:,:) = 0. 
4727
4728      ! Calcul des distances min et max du centre la boite des rapports 
4729      ! contenus dans les boites
4730      do headerIndex = 1, numHeaderMaxMpi*mmpi_nprocs
4731        if( .not. validMpi(headerIndex) ) cycle
4732        if( obsTimeIndexMpi(headerIndex) /= stepIndex ) cycle
4733        latIndex = obsLatIndexMpi(headerIndex)
4734        lonIndex = obsLonIndexMpi(headerIndex)
4735        levIndex = obsLevIndexMpi(headerIndex)
4736        if ( obsDistanceMpi(headerIndex) < minDistGrid(latIndex,lonIndex,levIndex) ) then
4737          minDistGrid(latIndex,lonIndex,levIndex) = obsDistanceMpi(headerIndex)
4738        end if
4739        if ( obsDistanceMpi(headerIndex) > maxDistGrid(latIndex,lonIndex,levIndex) ) then
4740          maxDistGrid(latIndex,lonIndex,levIndex) = obsDistanceMpi(headerIndex)
4741        end if
4742      end do
4743
4744      ! Calcul des sommes de u, v et t des observations situees a une distance midDistance
4745      ! du centre de la boite
4746      do headerIndex = 1, numHeaderMaxMpi*mmpi_nprocs
4747        if( .not. validMpi(headerIndex) ) cycle
4748        if( obsTimeIndexMpi(headerIndex) /= stepIndex ) cycle
4749        latIndex = obsLatIndexMpi(headerIndex)
4750        lonIndex = obsLonIndexMpi(headerIndex)
4751        levIndex = obsLevIndexMpi(headerIndex)
4752        midDistance = ( minDistGrid(latIndex,lonIndex,levIndex) + &
4753                        maxDistGrid(latIndex,lonIndex,levIndex) )/2.
4754        if ( (obsDistanceMpi(headerIndex) < midDistance) .and. &
4755             obsTTPresentMpi(headerIndex) .and. &
4756             obsUVPresentMpi(headerIndex) ) then
4757          numObsGrid(latIndex,lonIndex,levIndex) =  &
4758               numObsGrid(latIndex,lonIndex,levIndex) + 1
4759          uuSumGrid(latIndex,lonIndex,levIndex) =  &
4760               uuSumGrid(latIndex,lonIndex,levIndex) + obsUUMpi(headerIndex)
4761          vvSumGrid(latIndex,lonIndex,levIndex) =  &
4762               vvSumGrid(latIndex,lonIndex,levIndex) + obsVVMpi(headerIndex)
4763          ttSumGrid(latIndex,lonIndex,levIndex) =  &
4764               ttSumGrid(latIndex,lonIndex,levIndex) + obsTTMpi(headerIndex)
4765        end if
4766      end do
4767
4768      ! Calcul la moyenne de u, v et t s'il y a plus de 3 rapports dans la boite
4769      do latIndex = 1, numLat
4770        do lonIndex = 1, numLon
4771          do levIndex = 1, numLev
4772            if(numObsGrid(latIndex,lonIndex,levIndex) >= 3) then
4773              uuSumGrid(latIndex,lonIndex,levIndex) =  &
4774                   uuSumGrid(latIndex,lonIndex,levIndex)/numObsGrid(latIndex,lonIndex,levIndex)
4775              vvSumGrid(latIndex,lonIndex,levIndex) =  &
4776                   vvSumGrid(latIndex,lonIndex,levIndex)/numObsGrid(latIndex,lonIndex,levIndex)
4777              ttSumGrid(latIndex,lonIndex,levIndex) =  &
4778                   ttSumGrid(latIndex,lonIndex,levIndex)/numObsGrid(latIndex,lonIndex,levIndex)
4779            end if
4780          end do
4781        end do
4782      end do
4783
4784      ! S'il y a plus de 3 rapports dans la boite, le rapport dont le score est le plus
4785      ! petit est retenu. S'il y a 2 rapports ou moins, le rapport le plus pres du centre
4786      ! de la boite est retenu.
4787      do headerIndex = 1, numHeaderMaxMpi*mmpi_nprocs
4788        if( .not. validMpi(headerIndex) ) cycle
4789        if( obsTimeIndexMpi(headerIndex) /= stepIndex ) cycle
4790        latIndex = obsLatIndexMpi(headerIndex)
4791        lonIndex = obsLonIndexMpi(headerIndex)
4792        levIndex = obsLevIndexMpi(headerIndex)
4793
4794        if(numObsGrid(latIndex,lonIndex,levIndex) >= 3) then
4795
4796          midDistance = ( minDistGrid(latIndex,lonIndex,levIndex) + &
4797                          maxDistGrid(latIndex,lonIndex,levIndex) )/2.
4798          if ((obsDistanceMpi(headerIndex) < midDistance) .and. &
4799               obsTTPresentMpi(headerIndex) .and. &
4800               obsUVPresentMpi(headerIndex) ) then
4801            score = sqrt( (uuSumGrid(latIndex,lonIndex,levIndex) - &
4802                           obsUUMpi(headerIndex))**2/(1.4**2) +   &
4803                          (vvSumGrid(latIndex,lonIndex,levIndex) - &
4804                           obsVVMpi(headerIndex))**2/(1.4**2) ) + &
4805                    (ttSumGrid(latIndex,lonIndex,levIndex) - &
4806                     obsTTMpi(headerIndex))**2/(0.9**2)
4807
4808            if ( handlesGrid(latIndex,lonIndex,levIndex) /= -1 ) then
4809              if ( score >= minScoreGrid(latIndex,lonIndex,levIndex) ) then
4810                validMpi(headerIndex) = .false.
4811              end if
4812            end if
4813          
4814            if ( validMpi(headerIndex) ) then
4815              if ( handlesGrid(latIndex,lonIndex,levIndex) /= -1 ) then
4816                validMpi(handlesGrid(latIndex,lonIndex,levIndex)) = .false.
4817              end if
4818              minScoreGrid(latIndex,lonIndex,levIndex) = score
4819              validMpi(headerIndex) = .true.
4820              handlesGrid(latIndex,lonIndex,levIndex) = headerIndex
4821            end if
4822
4823          else
4824            validMpi(headerIndex) = .false.
4825          end if
4826
4827        else ! if(numObsGrid(latIndex,lonIndex,levIndex) < 3)
4828
4829          if ( handlesGrid(latIndex,lonIndex,levIndex) /= -1 ) then
4830            if ( obsDistanceMpi(headerIndex) > minDistGrid(latIndex,lonIndex,levIndex) ) then
4831              validMpi(headerIndex) = .false.
4832            end if
4833          end if
4834
4835          if ( validMpi(headerIndex) ) then
4836            if ( handlesGrid(latIndex,lonIndex,levIndex) /= -1 ) then
4837              validMpi(handlesGrid(latIndex,lonIndex,levIndex)) = .false.
4838            end if
4839            validMpi(headerIndex) = .true.
4840            handlesGrid(latIndex,lonIndex,levIndex) = headerIndex
4841          end if
4842
4843        end if
4844
4845      end do ! headerIndex
4846
4847    end do  STEP
4848
4849    ! Update local copy of valid from global mpi version
4850    headerIndexBeg = 1 + mmpi_myid * numHeaderMaxMpi
4851    headerIndexEnd = headerIndexBeg + numHeaderMaxMpi - 1
4852    valid(:) = validMpi(headerIndexBeg:headerIndexEnd)
4853
4854    deallocate(handlesGrid)
4855    deallocate(minScoreGrid)
4856    deallocate(minDistGrid)
4857    deallocate(maxDistGrid)
4858    deallocate(numObsGrid)
4859    deallocate(uuSumGrid)
4860    deallocate(vvSumGrid)
4861    deallocate(ttSumGrid)
4862
4863    write(*,*)
4864    write(*,'(a50,i10)') ' Number of obs in  = ', sum(aiTypeCountMpi(:))
4865    write(*,'(a50,i10)') ' Number of obs out = ', count(validMpi(:))
4866    write(*,'(a50,i10)') ' Number of obs not out = ', &
4867         sum(aiTypeCountMpi(:)) - count(validMpi(:))
4868    write(*,*)
4869
4870    ! Modify the flags for rejected observations
4871    do headerIndex = 1, numHeader
4872      ! skip observation if we're not supposed to consider it
4873      if (.not. isAirCraft(headerIndex)) cycle
4874     
4875      if (.not. valid(headerIndex)) then
4876        call obs_set_current_body_list(obsdat, headerIndex)
4877        BODY3: do 
4878          bodyIndex = obs_getBodyIndex(obsdat)
4879          if (bodyIndex < 0) exit BODY3
4880        
4881          obsFlag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
4882          call obs_bodySet_i(obsdat, OBS_FLG, bodyIndex, ibset(obsFlag,11))
4883
4884        end do BODY3
4885      end if
4886    end do
4887
4888    ! Deallocation
4889    deallocate(valid)
4890    deallocate(isAircraft)
4891    deallocate(validMpi)
4892    deallocate(gridLat)
4893    deallocate(gridLon)
4894    deallocate(rejectCount)
4895    deallocate(obsLatIndexVec)
4896    deallocate(obsLonIndexVec)
4897    deallocate(obsLevIndexVec)
4898    deallocate(obsTimeIndexVec)
4899    deallocate(obsDistance)
4900    deallocate(obsUU)
4901    deallocate(obsVV)
4902    deallocate(obsTT)
4903    deallocate(obsUVPresent)
4904    deallocate(obsTTPresent)
4905    deallocate(rejectCountMpi)
4906    deallocate(obsLatIndexMpi)
4907    deallocate(obsLonIndexMpi)
4908    deallocate(obsLevIndexMpi)
4909    deallocate(obsTimeIndexMpi)
4910    deallocate(obsDistanceMpi)
4911    deallocate(obsUUMpi)
4912    deallocate(obsVVMpi)
4913    deallocate(obsTTMpi)
4914    deallocate(obsUVPresentMpi)
4915    deallocate(obsTTPresentMpi)
4916    deallocate(gridPressure)
4917
4918    write(*,*)
4919    write(*,*) 'thn_aircraftByBoxes: Finished'
4920    write(*,*)
4921
4922  end subroutine thn_aircraftByBoxes
4923
4924  !--------------------------------------------------------------------------
4925  ! thn_keepNthObs
4926  !--------------------------------------------------------------------------
4927  subroutine thn_keepNthObs(obsdat, familyType, keepNthVertical)
4928    !
4929    !:Purpose: Of the observations in a column that have not already been
4930    !           rejected, keep every nth observation and throw out the rest.
4931    !           Set bit 11 of OBS_FLG on observations that are to be rejected.
4932    !
4933    implicit none
4934
4935    ! Arguments:
4936    type(struct_obs), intent(inout) :: obsdat
4937    character(len=*), intent(in)    :: familyType
4938    integer,          intent(in)    :: keepNthVertical
4939
4940    ! Locals:
4941    integer, parameter :: PROFILE_NOT_FOUND=-1
4942    integer :: headerIndex, bodyIndex
4943    integer :: flag
4944    integer :: countKeepN ! count to keep every Nth observation in the column
4945    integer :: newProfileId
4946
4947    write(*,*)
4948    write(*,*) 'thn_keepNthObs: Starting'
4949    write(*,*)
4950
4951    countKeepN=0
4952
4953    ! Loop over all body indices (columns) of the family of interest and
4954    ! thin each column independently of the others
4955    call obs_set_current_body_list(obsdat, familyType)
4956    BODY: do 
4957      bodyIndex = obs_getBodyIndex(obsdat)
4958      if (bodyIndex < 0) exit BODY
4959
4960      ! If datum already rejected, ignore it
4961      flag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
4962      if ( btest(flag,9) .or. &
4963           btest(flag,11) ) cycle BODY
4964
4965      headerIndex  = obs_bodyElem_i(obsdat, OBS_HIND, bodyIndex  )
4966      newProfileId = obs_headElem_i(obsdat, OBS_PRFL, headerIndex)
4967
4968      countKeepN=countKeepN + 1
4969      if ( countKeepN == keepNthVertical .or. &
4970           new_column() ) then
4971        ! Reset the counter and keep this observation
4972        countKeepN=0
4973      else
4974        ! Reject this observation
4975        call obs_bodySet_i(obsdat, OBS_FLG, bodyIndex, ibset(flag,11))
4976      end if
4977
4978    end do BODY
4979
4980    write(*,*)
4981    write(*,*) 'thn_keepNthObs: Finished'
4982    write(*,*)
4983
4984  contains
4985    function new_column()
4986      !
4987      !:Purpose: Determine whether the current observation begins a new vertical column
4988      !           (Assume that observations are in chronological order)
4989      !
4990      !:Note:  This method has been written with aladin observations in mind.
4991      !         It might be necessary to generalize the method.
4992      !
4993      implicit none
4994
4995      ! Result:
4996      logical :: new_column
4997
4998      ! Locals:
4999      integer, save :: previousProfileId=huge(previousProfileId)
5000
5001      if (newProfileId == PROFILE_NOT_FOUND) then
5002        ! The profile ID for this element is missing.
5003        ! Assume that it is the same as the previous element
5004        newProfileId = previousProfileId
5005      end if
5006
5007      if (newProfileId /= previousProfileId) then
5008        previousProfileId = newProfileId
5009        new_column=.true.
5010      else
5011        new_column=.false.
5012      end if
5013    end function new_column
5014
5015  end subroutine thn_keepNthObs
5016
5017  !--------------------------------------------------------------------------
5018  ! thn_tovsFilt
5019  !--------------------------------------------------------------------------
5020  subroutine thn_tovsFilt(obsdat, delta, deltrad, codtyp, codtyp2_opt)
5021    !
5022    !:Purpose: Thinning algorithm used for AMSU and ATMS radiance obs.
5023    !           Set bit 11 of OBS_FLG on observations that are to be rejected.
5024    !
5025    implicit none
5026
5027    ! Arguments:
5028    type(struct_obs),  intent(inout) :: obsdat
5029    integer,           intent(in)    :: delta
5030    integer,           intent(in)    :: deltrad
5031    integer,           intent(in)    :: codtyp
5032    integer, optional, intent(in)    :: codtyp2_opt
5033
5034    ! Locals:
5035    integer :: numLat, numLon, headerIndex, headerIndexKeep, latIndex, lonIndex, latIndex2
5036    integer :: gridIndex, numGridLonsTotal, obsTime, obsDate, numHeader, numHeaderMaxMpi, ierr
5037    integer :: bodyIndex, stepIndex, obsIndex, obsFov
5038    integer :: loscan, hiscan, obsFlag, numObs, minLonBurpFileMpi(mmpi_nprocs)
5039    integer :: procIndex, procIndexKeep, minLonBurpFile, countObs, countObsMpi
5040    integer :: countQc, countKept, countOther, countKeptMpi, countQcMpi, countGridPoints
5041    real(4) :: obsLatInRad, obsLonInRad, obsLat, obsLon, distance
5042    real(4) :: obsLatInDegrees, obsLonInDegrees, minDistance, minDistanceMpi(mmpi_nprocs)
5043    real(4) :: rejectRate, gridLat, gridLon
5044    real(4) :: percentTotal, percentQc, percentOther, percentKept
5045    real(8), allocatable :: stepObsIndex(:)
5046    real(4), allocatable :: gridLats(:), gridLatsAll(:), gridLonsAll(:), obsDistance(:)
5047    logical, allocatable :: valid(:)
5048    integer, allocatable :: numGridLons(:), numObsGrid(:), obsGridIndex(:)
5049    integer, allocatable :: obsLonBurpFile(:), obsLatBurpFile(:), numObsAssim(:)
5050    integer, allocatable :: headerIndexList(:), headerIndexList2(:)
5051    integer, allocatable :: obsIndexGrid(:), obsIndexLink(:)
5052    character(len=codtyp_name_length) :: instrumName
5053    integer, parameter :: latLength=10000
5054    integer, parameter :: lonLength=40000
5055    integer, parameter :: mxscanamsua=30
5056    integer, parameter :: mxscanamsub=90
5057    integer, parameter :: mxscanatms =96
5058    integer, parameter :: mxscanmwhs2=98
5059    integer, parameter :: mxscanssmis=90
5060
5061    instrumName = codtyp_get_name(codtyp)
5062    write(*,*)
5063    write(*,*) 'thn_tovsFilt: Starting, ', trim(instrumName)
5064    write(*,*)
5065
5066    numHeader = obs_numHeader(obsdat)
5067    call rpn_comm_allReduce(numHeader, numHeaderMaxMpi, 1, 'mpi_integer', &
5068                            'mpi_max','grid',ierr)
5069
5070    ! Check if we have any observations to process
5071    allocate(valid(numHeaderMaxMpi))
5072    valid(:) = .false.
5073    do headerIndex = 1, numHeader
5074      if (obs_headElem_i(obsdat, OBS_ITY, headerIndex) == codtyp) then
5075        valid(headerIndex) = .true.
5076      else if (present(codtyp2_opt)) then
5077        if (obs_headElem_i(obsdat, OBS_ITY, headerIndex) == codtyp2_opt) then
5078          valid(headerIndex) = .true.
5079        end if
5080      end if
5081    end do
5082
5083    countObs = count(valid(:))
5084    call rpn_comm_allReduce(countObs, countObsMpi, 1, 'mpi_integer', &
5085                            'mpi_sum','grid',ierr)
5086    if (countObsMpi == 0) then
5087      write(*,*) 'thn_tovsFilt: no observations for this instrument'
5088      deallocate(valid)
5089      return
5090    end if
5091
5092    write(*,*) 'thn_tovsFilt: countObs initial                        = ', &
5093               countObs, countObsMpi
5094
5095    ! Remove RARS obs that are also present from a global originating centre
5096    call thn_removeRarsDuplicates(obsdat, valid)
5097
5098    countObs = count(valid(:))
5099    call rpn_comm_allReduce(countObs, countObsMpi, 1, 'mpi_integer', &
5100                            'mpi_sum','grid',ierr)
5101    write(*,*) 'thn_tovsFilt: countObs after thn_removeRarsDuplicates = ', &
5102               countObs, countObsMpi
5103
5104    numLat = 2*latLength/delta
5105    numLon = lonLength/delta
5106
5107    ! Allocations
5108    allocate(gridLats(numLat))
5109    allocate(gridLatsAll(numLat*numLon))
5110    allocate(gridLonsAll(numLat*numLon))
5111    allocate(numObsGrid(numLat*numLon))
5112    allocate(obsIndexGrid(numLat*numLon))
5113    allocate(obsIndexLink(numHeader))
5114    allocate(headerIndexList(numHeader))
5115    allocate(headerIndexList2(numHeader))
5116    allocate(numGridLons(numLat))
5117    allocate(obsGridIndex(numHeader))
5118    allocate(obsLonBurpFile(numHeader))
5119    allocate(obsLatBurpFile(numHeader))
5120    allocate(numObsAssim(numHeader))
5121    allocate(obsDistance(numHeader))
5122    allocate(stepObsIndex(numHeader))
5123
5124    ! Initialize arrays
5125    gridLats(:) = 0.0
5126    gridLatsAll(:) = 0.0
5127    gridLonsAll(:) = 0.0
5128    obsDistance(:) = 0.0
5129    numObsGrid(:) = 0
5130    obsIndexGrid(:) = 0
5131    obsIndexLink(:) = 0
5132    headerIndexList(:) = 0
5133    headerIndexList2(:) = 0
5134    numGridLons(:) = 0
5135    obsGridIndex(:) = 0
5136    obsLonBurpFile(:) = 0
5137    obsLatBurpFile(:) = 0
5138    numObsAssim(:) = 0
5139    stepObsIndex(:) = 0.0d0
5140
5141    ! Set up the grid used for thinning
5142    numGridLonsTotal = 0
5143    do latIndex = 1, numLat
5144      gridLats(latIndex)    = (latIndex*180./numLat) - 90.
5145      distance              = lonLength * cos(gridLats(latIndex) * mpc_pi_r4 / 180.)
5146      numGridLons(latIndex) = nint(distance/delta)
5147      numGridLons(latIndex) = max(numGridLons(latIndex),1)
5148      numGridLonsTotal      = numGridLonsTotal + numGridLons(latIndex)
5149    end do
5150
5151    gridIndex = 0
5152    do latIndex = 1, numLat
5153      do lonIndex = 1, numGridLons(latIndex)
5154        gridLatsAll(gridIndex+lonIndex) = gridLats(latIndex)
5155        gridLonsAll(gridIndex+lonIndex) = (lonIndex-1) * 360. / numGridLons(latIndex)
5156      end do
5157      gridIndex = gridIndex + numGridLons(latIndex)
5158    end do
5159
5160    ! Loop over all observation locations
5161    do headerIndex = 1, numHeader
5162      if ( .not. valid(headerIndex) ) cycle
5163
5164      ! Lat and Lon for each observation
5165      obsLonInRad = obs_headElem_r(obsdat, OBS_LON, headerIndex)
5166      obsLatInRad = obs_headElem_r(obsdat, OBS_LAT, headerIndex)
5167
5168      obsLonInDegrees = MPC_DEGREES_PER_RADIAN_R8 * obsLonInRad
5169      obsLatInDegrees = MPC_DEGREES_PER_RADIAN_R8 * obsLatInRad
5170      obsLonBurpFile(headerIndex) = nint(100.0*(obsLonInDegrees - 180.0))
5171      if(obsLonBurpFile(headerIndex) < 0) then
5172        obsLonBurpFile(headerIndex) = obsLonBurpFile(headerIndex) + 36000
5173      end if
5174      obsLatBurpFile(headerIndex) = 9000+nint(100.0*obsLatInDegrees)
5175
5176      obsDate = obs_headElem_i(obsdat, OBS_DAT, headerIndex)
5177      obsTime = obs_headElem_i(obsdat, OBS_ETM, headerIndex)
5178      call tim_getStepObsIndex(stepObsIndex(headerIndex), tim_getDatestamp(), &
5179                               obsDate, obsTime, tim_nstepobs)
5180
5181      ! Associate each observation to a grid point
5182      obsLat = (obsLatBurpFile(headerIndex) - 9000.) / 100.
5183      obsLon = obsLonBurpFile(headerIndex) / 100.
5184      do latIndex = 1, numLat-1
5185        if (obsLat <  gridLats(1))      obsLat = gridLats(1)
5186        if (obsLat >= gridLats(numLat)) obsLat = gridLats(numLat) - 0.5
5187        if (obsLat >= gridLats(latIndex) .and. obsLat < gridLats(latIndex+1)) then
5188          gridIndex = 1
5189          do latIndex2 = 1, latIndex-1
5190            gridIndex = gridIndex + numGridLons(latIndex2)
5191          end do
5192          gridIndex = gridIndex + ifix(obsLon/(360./numGridLons(latIndex)))
5193          exit
5194        end if
5195      end do
5196      obsGridIndex(headerIndex) = gridIndex
5197      numObsGrid(gridIndex) = numObsGrid(gridIndex) + 1
5198
5199    end do ! headerIndex
5200
5201    if      ( codtyp == codtyp_get_codtyp('amsua') ) then
5202      loscan   = 1
5203      hiscan   = mxscanamsua
5204    else if ( codtyp == codtyp_get_codtyp('amsub') ) then
5205      loscan   = 1
5206      hiscan   = mxscanamsub
5207    else if ( codtyp == codtyp_get_codtyp('atms') ) then
5208      loscan   = 2
5209      hiscan   = mxscanatms - 1
5210    else if ( codtyp == codtyp_get_codtyp('mwhs2') ) then
5211      loscan   = 1
5212      hiscan   = mxscanmwhs2
5213    else if ( codtyp == codtyp_get_codtyp('ssmis') ) then
5214      loscan   = 1
5215      hiscan   = mxscanssmis
5216   else
5217      write(*,*) 'codtyp = ', codtyp
5218      call utl_abort('thn_tovsFilt: Invalid codtyp')
5219    end if
5220
5221    countQc = 0
5222    numObsAssim(:) = 0
5223    do headerIndex = 1, numHeader
5224
5225      if ( .not. valid(headerIndex) ) cycle
5226
5227      ! Look at the obs flags
5228      rejectRate = 0.
5229
5230      call obs_set_current_body_list(obsdat, headerIndex)
5231      BODY: do 
5232        bodyIndex = obs_getBodyIndex(obsdat)
5233        if (bodyIndex < 0) exit BODY
5234        
5235        ! If not a blacklisted channel (note that bit 11 is set in 
5236        ! satqc_amsu*.f for blacklisted channels)
5237        obsFlag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
5238        if ( .not. btest(obsFlag,11) ) then
5239          numObsAssim(headerIndex) = numObsAssim(headerIndex) + 1
5240          if ( btest(obsFlag,9) ) then
5241            rejectRate = rejectRate + 1.0
5242          end if
5243        end if
5244      end do BODY
5245
5246      ! fixer le % de rejets a 100% si aucun canal n'est assimilable         
5247      if ( rejectRate == 0. .and. numObsAssim(headerIndex) == 0 ) then
5248        rejectRate = 1.
5249      else
5250        rejectRate = rejectRate / max(numObsAssim(headerIndex),1)  
5251      end if
5252
5253      obsFov = obs_headElem_i(obsdat, OBS_FOV, headerIndex)
5254      if ( rejectRate >= 0.80 ) then
5255        countQc = countQc + 1
5256        valid(headerIndex) = .false.
5257      else if (obsFov < loscan .or.  &
5258               obsFov > hiscan ) then
5259        countQc = countQc + 1
5260        valid(headerIndex) = .false.
5261      end if
5262
5263    end do
5264
5265    countObs = count(valid(:))
5266    call rpn_comm_allReduce(countObs, countObsMpi, 1, 'mpi_integer', &
5267                            'mpi_sum','grid',ierr)
5268    write(*,*) 'thn_tovsFilt: countObs after QC                       = ', &
5269               countObs, countObsMpi
5270
5271    ! Calculate distance of obs. from center of its grid box center
5272    do headerIndex = 1, numHeader
5273      if ( .not. valid(headerIndex) ) cycle
5274
5275      gridIndex = obsGridIndex(headerIndex)
5276      if (numObsGrid(gridIndex) /= 0) then
5277        latIndex = (gridLatsAll(gridIndex)+90.)/(180./numLat)
5278        gridLat = gridLatsAll(gridIndex) + 0.5*(180./numLat)
5279        gridLon = gridLonsAll(gridIndex) + 0.5*360./numGridLons(latIndex)
5280        obsLat = (obsLatBurpFile(headerIndex) - 9000.) / 100.
5281        obsLon = obsLonBurpFile(headerIndex) / 100.
5282        obsDistance(headerIndex) = thn_separation(obsLon,obsLat,gridLon,gridLat) * &
5283             float(latLength) / 90.
5284      end if
5285    end do
5286
5287    ! Create a linked list of observations (link to grid point)
5288    obsIndexGrid(:) = 0
5289    obsIndexLink(:) = 0
5290    countObs = 0
5291    do headerIndex = 1, numHeader
5292      if ( .not. valid(headerIndex) ) cycle
5293
5294      gridIndex = obsGridIndex(headerIndex)
5295      if (numObsGrid(gridIndex) /= 0) then
5296        countObs = countObs + 1
5297        headerIndexList(countObs) = headerIndex
5298        obsIndexLink(countObs) = obsIndexGrid(gridIndex)
5299        obsIndexGrid(gridIndex) = countObs
5300      end if
5301    end do
5302
5303    ! Loop over stepObs
5304    do stepIndex = 1, tim_nstepobs
5305
5306      ! Loop over all grid points
5307      countGridPoints = 0
5308      do gridIndex = 1, numGridLonsTotal
5309        if (numObsGrid(gridIndex) /= 0) then
5310          countGridPoints = countGridPoints + 1
5311        end if
5312        numObs = 0
5313        obsIndex = obsIndexGrid(gridIndex)
5314        do
5315          if (obsIndex == 0) exit
5316          headerIndex = headerIndexList(obsIndex)
5317          if ( obsGridIndex(headerIndex) == gridIndex  .and. &
5318               valid(headerIndex)               .and. &
5319               nint(stepObsIndex(headerIndex)) == stepIndex ) then
5320            numObs = numObs + 1
5321            headerIndexList2(numObs) = headerIndex
5322          end if
5323
5324          obsIndex = obsIndexLink(obsIndex)
5325        end do
5326
5327        minDistance = 1000000.             
5328
5329        ! Choose the obs closest to the grid point
5330        do obsIndex = 1, numObs
5331          if (obsDistance(headerIndexList2(obsIndex)) < minDistance) then
5332            minDistance = obsDistance(headerIndexList2(obsIndex))
5333            minLonBurpFile = obsLonBurpFile(headerIndexList2(obsIndex))
5334            headerIndexKeep = headerIndexList2(obsIndex)
5335          end if
5336        end do
5337
5338        ! Check for multiple obs with same distance to grid point
5339        if (numObs > 0) then
5340          if ( count(obsDistance(headerIndexList2(1:numObs)) == minDistance) > 1 ) then
5341            ! resolve ambiguity by choosing obs with min value of lon
5342            minLonBurpFile = 10000000
5343            do obsIndex = 1, numObs
5344              if (obsDistance(headerIndexList2(obsIndex)) == minDistance) then
5345                if (obsLonBurpFile(headerIndexList2(obsIndex)) < minLonBurpFile) then
5346                  minLonBurpFile = obsLonBurpFile(headerIndexList2(obsIndex))
5347                  headerIndexKeep = headerIndexList2(obsIndex)
5348                end if
5349              end if
5350            end do
5351          end if
5352        end if
5353
5354        do obsIndex = 1, numObs
5355          valid(headerIndexList2(obsIndex)) = .false.
5356        end do
5357        if (numObs > 0 .and. minDistance <= real(deltRad) ) then
5358          valid(headerIndexKeep) = .true.
5359        end if
5360
5361        ! Communicate the distance of chosen observation among all mpi tasks
5362        call rpn_comm_allgather(minDistance,    1, 'mpi_real4',  &
5363                                minDistanceMpi, 1, 'mpi_real4', 'grid', ierr)
5364
5365        ! Choose the closest to the center of the box among all mpi tasks
5366        minDistance = 1000000.
5367        do procIndex = 1, mmpi_nprocs
5368          if (minDistanceMpi(procIndex) < minDistance) then
5369            minDistance = minDistanceMpi(procIndex)
5370            procIndexKeep = procIndex
5371          end if
5372        end do
5373
5374        ! Adjust flags to only keep 1 observation among all mpi tasks
5375        if (minDistance < 1000000.) then
5376          if ( count(minDistanceMpi(:) == minDistance) > 1 ) then
5377            ! resolve ambiguity by choosing obs with min value of lon
5378            call rpn_comm_allgather(minLonBurpFile,    1, 'mpi_integer',  &
5379                                    minLonBurpFileMpi, 1, 'mpi_integer', 'grid', ierr)
5380            minLonBurpFile = 10000000
5381            do procIndex = 1, mmpi_nprocs
5382              if (minDistanceMpi(procIndex) == minDistance) then
5383                if (minLonBurpFileMpi(procIndex) < minLonBurpFile) then
5384                  minLonBurpFile = minLonBurpFileMpi(procIndex)
5385                  procIndexKeep = procIndex
5386                end if
5387              end if
5388            end do            
5389          end if
5390          if (numObs > 0) then
5391            if (mmpi_myid /= (procIndexKeep-1)) then
5392              valid(headerIndexKeep) = .false.
5393            end if
5394          end if
5395        end if
5396
5397      end do ! gridIndex
5398    end do ! stepIndex
5399
5400    countObs = count(valid(:))
5401    call rpn_comm_allReduce(countObs, countObsMpi, 1, 'mpi_integer', &
5402                            'mpi_sum','grid',ierr)
5403    write(*,*) 'thn_tovsFilt: countObs after thinning                 = ', &
5404               countObs, countObsMpi
5405
5406    ! modify the observation flags in obsSpaceData
5407    countObs = 0
5408    do headerIndex = 1, numHeader
5409      ! skip observation if we're not supposed to consider it
5410      if (obs_headElem_i(obsdat, OBS_ITY, headerIndex) /= codtyp) then
5411        if (present(codtyp2_opt)) then
5412          if (obs_headElem_i(obsdat, OBS_ITY, headerIndex) /= codtyp2_opt) then
5413            cycle
5414          end if
5415        else
5416          cycle
5417        end if
5418      end if
5419     
5420      countObs = countObs + 1
5421
5422      if (.not. valid(headerIndex)) then
5423        call obs_set_current_body_list(obsdat, headerIndex)
5424        BODY2: do 
5425          bodyIndex = obs_getBodyIndex(obsdat)
5426          if (bodyIndex < 0) exit BODY2
5427        
5428          obsFlag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
5429          call obs_bodySet_i(obsdat, OBS_FLG, bodyIndex, ibset(obsFlag,11))
5430
5431        end do BODY2
5432      end if
5433    end do
5434
5435    ! print a summary to the listing
5436    countKept = count(valid)
5437    call rpn_comm_allReduce(countKept, countKeptMpi, 1, 'mpi_integer', &
5438                            'mpi_sum','grid',ierr)
5439    call rpn_comm_allReduce(countQc,   countQcMpi,   1, 'mpi_integer', &
5440                            'mpi_sum','grid',ierr)
5441    call rpn_comm_allReduce(countObs, countObsMpi, 1, 'mpi_integer', &
5442                            'mpi_sum','grid',ierr)
5443
5444    countOther = countObsMpi - countKeptMpi - countQcMpi
5445
5446    percentTotal = 100.0
5447    percentQc    = (float(countQcMpi)   / float(countObsMpi)) * 100.0
5448    percentOther = (float(countOther)   / float(countObsMpi)) * 100.0
5449    percentKept  = (float(countKeptMpi) / float(countObsMpi)) * 100.0
5450         
5451    write(*,100)
5452100 format(/,' SOMMAIRE DES RESULTATS',/)
5453    write(*,200) countObsMpi, percentTotal, countQcMpi, percentQc, &
5454                 countOther, percentOther, countKeptMpi, percentKept, &
5455                 delta, numGridLonsTotal, countGridPoints
5456200 format(' NB.STNS TOTAL AU DEBUT EN ENTREE        =    ',I7, &
5457           '  =  ',F6.1,'%',/, &
5458           ' NB.STNS REJETEES AU CONTROLE QUALITATIF =  - ',I7, &
5459           '  =  ',F6.1,'%',/, &
5460           ' NB.STNS REJETEES POUR LES AUTRES RAISONS=  - ',I7, &
5461           '  =  ',F6.1,'%',/, &
5462           '                                              ', &
5463           '-------- = -------',/, &
5464           ' NB.STNS GARDEES A LA FIN                =    ',I7, &
5465           '  =  ',F6.1,'%',/////, &
5466           ' NB.PTS GRILLE AVEC LA RESOLUTION ',I5,' KM   =    ', &
5467           I7,/, &
5468           ' NB.PTS GRILLE TRAITES                       =    ',I7)
5469
5470    ! end of sommair
5471
5472    deallocate(valid)
5473    deallocate(gridLats)
5474    deallocate(gridLatsAll)
5475    deallocate(gridLonsAll)
5476    deallocate(numObsGrid)
5477    deallocate(obsIndexGrid)
5478    deallocate(obsIndexLink)
5479    deallocate(headerIndexList)
5480    deallocate(headerIndexList2)
5481    deallocate(numGridLons)
5482    deallocate(obsGridIndex)
5483    deallocate(obsLonBurpFile)
5484    deallocate(obsLatBurpFile)
5485    deallocate(numObsAssim)
5486    deallocate(obsDistance)
5487    deallocate(stepObsIndex)
5488
5489    write(*,*)
5490    write(*,*) 'thn_tovsFilt: Finished'
5491    write(*,*)
5492
5493  end subroutine thn_tovsFilt
5494
5495  !--------------------------------------------------------------------------
5496  ! thn_removeRarsDuplicates
5497  !--------------------------------------------------------------------------
5498  subroutine thn_removeRarsDuplicates(obsdat, valid)
5499    !
5500    !:Purpose: Remove duplicate TOVS observations due to RARS.
5501    !
5502    implicit none
5503
5504    ! Arguments:
5505    type(struct_obs), intent(inout) :: obsdat
5506    logical,          intent(inout) :: valid(:)
5507
5508    ! Locals:
5509    integer :: nsize, ierr, lenStnId, headerIndex, headerIndex1, headerIndex2
5510    integer :: numHeader, numHeaderMaxMpi, charIndex, headerIndexBeg, headerIndexEnd
5511    integer :: obsDate, obsTime
5512    real(4) :: obsLatInRad, obsLonInRad
5513    real(8) :: dlhours
5514    logical :: global1, global2
5515    integer, allocatable :: centreOrig(:), centreOrigMpi(:)
5516    integer, allocatable :: obsFov(:), obsFovMpi(:)
5517    integer, allocatable :: obsDateStamp(:), obsDateStampMpi(:)
5518    integer, allocatable :: stnIdInt(:,:), stnIdIntMpi(:,:)
5519    logical, allocatable :: validMpi(:)
5520    character(len=12)    :: stnId
5521    type(kdtree2), pointer            :: tree
5522    integer, parameter                :: maxNumSearch = 100
5523    integer                           :: numFoundSearch, resultIndex
5524    type(kdtree2_result)              :: searchResults(maxNumSearch)
5525    real(kdkind)                      :: maxRadius = 100.d6
5526    real(kdkind)                      :: refPosition(3)
5527    real(kdkind), allocatable         :: obsPosition3d(:,:)
5528    real(kdkind), allocatable         :: obsPosition3dMpi(:,:)
5529    integer, parameter :: centreOrigGlobal(3)=(/53, 74, 160/)
5530    integer, external  :: newdate
5531
5532    numHeader = obs_numHeader(obsdat)
5533    call rpn_comm_allReduce(numHeader, numHeaderMaxMpi, 1, 'mpi_integer', &
5534                            'mpi_max','grid',ierr)
5535
5536    ! Allocations
5537    allocate(obsPosition3d(3,numHeaderMaxMpi))
5538    allocate(obsPosition3dMpi(3,numHeaderMaxMpi*mmpi_nprocs))
5539    allocate(centreOrig(numHeaderMaxMpi))
5540    allocate(centreOrigMpi(numHeaderMaxMpi*mmpi_nprocs))
5541    allocate(obsFov(numHeaderMaxMpi))
5542    allocate(obsFovMpi(numHeaderMaxMpi*mmpi_nprocs))
5543    allocate(obsDateStamp(numHeaderMaxMpi))
5544    allocate(obsDateStampMpi(numHeaderMaxMpi*mmpi_nprocs))
5545    allocate(validMpi(numHeaderMaxMpi*mmpi_nprocs))
5546    lenStnId = len(stnId)
5547    allocate(stnIdInt(lenStnId,numHeaderMaxMpi))
5548    allocate(stnIdIntMpi(lenStnId,numHeaderMaxMpi*mmpi_nprocs))
5549
5550    ! Some initializations
5551    centreOrig(:) = 0
5552    obsPosition3d(:,:) = 0.0
5553
5554    ! Loop over all observation locations
5555    do headerIndex = 1, numHeader
5556      if ( .not. valid(headerIndex) ) cycle
5557
5558      ! Originating centre of data
5559      centreOrig(headerIndex) = obs_headElem_i(obsdat, OBS_ORI, headerIndex)
5560
5561      ! Station ID converted to integer array
5562      stnId = obs_elem_c(obsdat,'STID',headerIndex)
5563      do charIndex = 1, lenStnId
5564        stnIdInt(charIndex,headerIndex) = iachar(stnId(charIndex:charIndex))
5565      end do
5566
5567      ! Date stamp for each observation
5568      obsDate = obs_headElem_i(obsdat, OBS_DAT, headerIndex)
5569      obsTime = obs_headElem_i(obsdat, OBS_ETM, headerIndex)
5570      ierr = newdate(obsDateStamp(headerIndex), obsDate, obsTime*10000+2900, 3)
5571
5572      ! Field of View for each observation
5573      obsFov(headerIndex) = obs_headElem_i(obsdat, OBS_FOV, headerIndex)
5574
5575      ! Lat and Lon for each observation
5576      obsLonInRad = obs_headElem_r(obsdat, OBS_LON, headerIndex)
5577      obsLatInRad = obs_headElem_r(obsdat, OBS_LAT, headerIndex)
5578
5579      ! 3D location array for kdtree
5580      obsPosition3d(1,headerIndex) = ec_ra * sin(obsLonInRad) * cos(obsLatInRad)
5581      obsPosition3d(2,headerIndex) = ec_ra * cos(obsLonInRad) * cos(obsLatInRad)
5582      obsPosition3d(3,headerIndex) = ec_ra *                    sin(obsLatInRad)
5583    end do
5584
5585    nsize = 3 * numHeaderMaxMpi
5586    call rpn_comm_allgather(obsPosition3d,    nsize, 'mpi_real8',  &
5587                            obsPosition3dMpi, nsize, 'mpi_real8', 'grid', ierr)
5588    nsize = numHeaderMaxMpi
5589    call rpn_comm_allgather(valid,    nsize, 'mpi_logical',  &
5590                            validMpi, nsize, 'mpi_logical', 'grid', ierr)
5591    call rpn_comm_allgather(centreOrig,    nsize, 'mpi_integer',  &
5592                            centreOrigMpi, nsize, 'mpi_integer', 'grid', ierr)
5593    call rpn_comm_allgather(obsFov,    nsize, 'mpi_integer',  &
5594                            obsFovMpi, nsize, 'mpi_integer', 'grid', ierr)
5595    call rpn_comm_allgather(obsDateStamp,    nsize, 'mpi_integer',  &
5596                            obsDateStampMpi, nsize, 'mpi_integer', 'grid', ierr)
5597    nsize = lenStnId * numHeaderMaxMpi
5598    call rpn_comm_allgather(stnIdInt,    nsize, 'mpi_integer',  &
5599                            stnIdIntMpi, nsize, 'mpi_integer', 'grid', ierr)
5600    nullify(tree)
5601
5602    tree => kdtree2_create(obsPosition3dMpi, sort=.true., rearrange=.true.)
5603    HEADER1: do headerIndex1 = 1, mmpi_nprocs*numHeaderMaxMpi
5604        
5605      if ( .not. validMpi(headerIndex1) ) cycle HEADER1
5606
5607      ! Find all obs within 10km
5608      refPosition(:) = obsPosition3dMpi(:,headerIndex1)
5609      call kdtree2_r_nearest(tp=tree, qv=refPosition, r2=maxRadius, nfound=numFoundSearch, &
5610                             nalloc=maxNumSearch, results=searchResults)
5611      if (numFoundSearch >= maxNumSearch) then
5612        call utl_abort('thn_tovsFilt: the parameter maxNumSearch must be increased')
5613      end if
5614      if (numFoundSearch == 0) then
5615        call utl_abort('thn_tovsFilt: no match found. This should not happen!!!')
5616      end if
5617
5618      ! Loop over all of these nearby locations
5619      HEADER2: do resultIndex = 1, numFoundSearch
5620        headerIndex2 = searchResults(resultIndex)%idx
5621
5622        if ( .not. validMpi(headerIndex2) ) cycle HEADER2
5623
5624        ! Certaines stations locales nous envoient 
5625        ! le mauvais numero d'orbite. On ne peut donc
5626        ! pas s'y fier.
5627        ! Il faut comparer le temps de la reception des
5628        ! donnees
5629        if ( centreOrigMpi(headerIndex1) /= centreOrigMpi(headerIndex2) ) then
5630          if ( obsFovMpi(headerIndex1) == obsFovMpi(headerIndex2) ) then
5631            if ( all(stnIdIntMpi(:,headerIndex1) ==  stnIdIntMpi(:,headerIndex2)) ) then
5632            
5633              ! Difference (in hours) between obs time
5634              call difdatr(obsDateStampMpi(headerIndex1),obsDateStampMpi(headerIndex2),dlhours)
5635
5636              ! Si la difference est moins de 6 minutes,
5637              ! on peut avoir affaire a un rars
5638
5639              if ( abs(dlhours) <= 0.1 ) then
5640
5641                ! si l'element_i est global, on doit le garder et rejeter l'element_j
5642                global1 = any(centreOrigGlobal(:) == centreOrigMpi(headerIndex1))
5643                if (global1) then 
5644                  validMpi(headerIndex2) = .false.
5645                else
5646                  ! toutefois, ca ne signifie pas que l'element_j est un rars
5647                  ! VERIFIER SI LA STATION 2 EST RARS
5648                  global2 = any(centreOrigGlobal(:) == centreOrigMpi(headerIndex2))
5649
5650                  ! Si l'element_j est global, rejeter l'element_i
5651                  ! Si les 2 elements sont rars, garder le 1er
5652                  if (global2) then 
5653                    validMpi(headerIndex1) = .false.
5654                    cycle HEADER1
5655                  else
5656                    validMpi(headerIndex2) = .false.
5657                  end if
5658                end if
5659
5660              end if ! abs(dlhours) <= 0.1
5661              
5662            end if ! STID1 == STID2
5663          end if ! FOV1 == FOV2
5664        end if ! centreOrig1 /= centreOrig2
5665
5666      end do HEADER2
5667    end do HEADER1
5668    call kdtree2_destroy(tree)
5669
5670    ! update local copy of 'valid' array
5671    headerIndexBeg = 1 + mmpi_myid * numHeaderMaxMpi
5672    headerIndexEnd = headerIndexBeg + numHeaderMaxMpi - 1
5673    valid(:) = validMpi(headerIndexBeg:headerIndexEnd)
5674
5675    deallocate(obsPosition3d)
5676    deallocate(obsPosition3dMpi)
5677    deallocate(centreOrig)
5678    deallocate(centreOrigMpi)
5679    deallocate(obsFov)
5680    deallocate(obsFovMpi)
5681    deallocate(obsDateStamp)
5682    deallocate(obsDateStampMpi)
5683    deallocate(validMpi)
5684    deallocate(stnIdInt)
5685    deallocate(stnIdIntMpi)
5686
5687  end subroutine thn_removeRarsDuplicates
5688
5689  !--------------------------------------------------------------------------
5690  ! thn_scatByLatLonBoxes
5691  !--------------------------------------------------------------------------
5692  subroutine thn_scatByLatLonBoxes(obsdat, deltax, deltmax)
5693    !
5694    !:Purpose: Only keep the observation closest to the center of each
5695    !           lat-lon (and time) box for SCAT observations.
5696    !           Set bit 11 of OBS_FLG on observations that are to be rejected.
5697    !
5698    implicit none
5699
5700    ! Arguments:
5701    type(struct_obs), intent(inout) :: obsdat
5702    integer,          intent(in)    :: deltax
5703    integer,          intent(in)    :: deltmax
5704
5705    ! Locals:
5706    integer, parameter :: latLength = 10000 ! Earth dimension parameters
5707    integer, parameter :: lonLength = 40000 ! Earth dimension parameters
5708    integer, parameter :: numStnIdMax = 100
5709    integer :: bodyIndex, charIndex, nsize, lenStnId
5710    integer :: timeRejectCount, flagRejectCount, timeRejectCountMpi, flagRejectCountMpi
5711    integer :: uObsFlag, vObsFlag, obsVarno, stnIdIndex, numStnId, stnIdIndexFound
5712    integer :: numLat, numLon, latIndex, lonIndex, stepIndex, obsFlag
5713    integer :: ierr, headerIndex, numHeader, numHeaderMaxMpi
5714    integer :: headerIndexBeg, headerIndexEnd
5715    integer :: countObs, countObsInMpi, countObsOutMpi
5716    integer :: obsLonBurpFile, obsLatBurpFile, obsDate, obsTime
5717    integer :: numObsStnIdOut(numStnIdMax)
5718    integer :: numObsStnIdInMpi(numStnIdMax), numObsStnIdOutMpi(numStnIdMax)
5719    real(4) :: latInRadians, distance, obsLat, obsLon
5720    real(8) :: obsLatInDegrees, obsLonInDegrees, obsStepIndex_r8
5721    logical :: change
5722    real(4), allocatable :: gridLats(:), gridLatsMid(:), gridLonsMid(:,:)
5723    integer, allocatable :: headerIndexGrid(:,:,:), delMinutesGrid(:,:,:)
5724    real(4), allocatable :: distanceGrid(:,:,:)
5725    integer, allocatable :: obsLatIndex(:), obsLonIndex(:), obsStepIndex(:), numGridLons(:)
5726    real(4), allocatable :: obsDistance(:)
5727    integer, allocatable :: obsLatIndexMpi(:), obsLonIndexMpi(:), obsStepIndexMpi(:)
5728    integer, allocatable :: obsDelMinutes(:), obsDelMinutesMpi(:)
5729    integer, allocatable :: stnIdInt(:,:), stnIdIntMpi(:,:)
5730    real(4), allocatable :: obsDistanceMpi(:)
5731    logical, allocatable :: valid(:), validMpi(:)
5732    character(len=5)     :: stnIdTrim
5733    character(len=12)    :: stnId, stnidList(numStnIdMax)
5734    character(len=12), allocatable :: stnIdGrid(:,:,:)
5735
5736    write(*,*)
5737    write(*,*) 'thn_scatByLatLonBoxes: Starting'
5738    write(*,*)
5739
5740    numHeader = obs_numHeader(obsdat)
5741    call rpn_comm_allReduce(numHeader, numHeaderMaxMpi, 1, 'mpi_integer', &
5742                            'mpi_max','grid',ierr)
5743    write(*,*) 'thn_scatByLatLonBoxes: numHeader, numHeaderMaxMpi = ', &
5744               numHeader, numHeaderMaxMpi
5745
5746    ! Check if we have any observations to process
5747    allocate(valid(numHeaderMaxMpi))
5748    valid(:) = .false.
5749    call obs_set_current_header_list(obsdat,'SC')
5750    HEADER0: do
5751      headerIndex = obs_getHeaderIndex(obsdat)
5752      if (headerIndex < 0) exit HEADER0
5753      valid(headerIndex) = .true.
5754    end do HEADER0
5755    countObs = count(valid(:))
5756    call rpn_comm_allReduce(countObs, countObsInMpi, 1, 'mpi_integer', &
5757                            'mpi_sum','grid',ierr)
5758    if (countObsInMpi == 0) then
5759      write(*,*) 'thn_scatByLatLonBoxes: no observations for this instrument'
5760      deallocate(valid)
5761      return
5762    end if
5763
5764    numLat = nint(2.*real(latLength)/real(deltax))
5765    numLon = nint(real(lonLength)/real(deltax))
5766
5767    write(*,*)
5768    write(*,*) 'Number of horizontal boxes : ', numLon
5769    write(*,*) 'Number of vertical boxes   : ', numLat
5770    write(*,*) 'Number of temporal bins    : ', tim_nstepobs
5771    write(*,*)
5772
5773    write(*,*) 'thn_scatByLatLonBoxes: countObs initial                   = ', &
5774               countObs, countObsInMpi
5775
5776    ! Allocate arrays
5777    allocate(gridLats(numLat))
5778    allocate(gridLatsMid(numLat))
5779    allocate(gridLonsMid(numLat,numLon))
5780    allocate(numGridLons(numLat))
5781    allocate(stnIdGrid(numLat,numLon,tim_nstepobs))
5782    allocate(distanceGrid(numLat,numLon,tim_nstepobs))
5783    allocate(headerIndexGrid(numLat,numLon,tim_nstepobs))
5784    allocate(delMinutesGrid(numLat,numLon,tim_nstepobs))
5785
5786    allocate(obsLatIndex(numHeaderMaxMpi))
5787    allocate(obsLonIndex(numHeaderMaxMpi))
5788    allocate(obsStepIndex(numHeaderMaxMpi))
5789    allocate(obsDistance(numHeaderMaxMpi))
5790    allocate(obsDelMinutes(numHeaderMaxMpi))
5791    lenStnId = len(stnId)
5792    allocate(stnIdInt(lenStnId,numHeaderMaxMpi))
5793
5794    ! Allocation for MPI gather
5795    allocate(validMpi(numHeaderMaxMpi*mmpi_nprocs))
5796    allocate(obsLatIndexMpi(numHeaderMaxMpi*mmpi_nprocs))
5797    allocate(obsLonIndexMpi(numHeaderMaxMpi*mmpi_nprocs))
5798    allocate(obsStepIndexMpi(numHeaderMaxMpi*mmpi_nprocs))
5799    allocate(obsDistanceMpi(numHeaderMaxMpi*mmpi_nprocs))
5800    allocate(obsDelMinutesMpi(numHeaderMaxMpi*mmpi_nprocs))
5801    allocate(stnIdIntMpi(lenStnId,numHeaderMaxMpi*mmpi_nprocs))
5802
5803    gridLats(:)            = 0.
5804    gridLatsMid(:)         = 0.
5805    gridLonsMid(:,:)       = 0.
5806    numGridLons(:)         = 0
5807    stnIdGrid(:,:,:)       = ''
5808    distanceGrid(:,:,:)    = -1.0
5809    headerIndexGrid(:,:,:) = -1
5810
5811    timeRejectCount = 0
5812    flagRejectCount = 0
5813
5814    ! set spatial boxes properties
5815    do latIndex = 1, numLat
5816      gridLats(latIndex) = (latIndex*180./numLat) - 90.
5817      gridLatsMid(latIndex) = gridLats(latIndex) - (90./numLat)
5818      if (gridLats(latIndex) <= 0.0) then
5819        latInRadians = gridLats(latIndex) * MPC_PI_R8 / 180.
5820      else
5821        latInRadians = gridLats(latIndex-1) * MPC_PI_R8 / 180.
5822      end if
5823      distance = lonLength * cos(latInRadians)
5824      numGridLons(latIndex) = nint(distance/deltax)
5825      do lonIndex = 1, numGridLons(latIndex)
5826        gridLonsMid(latIndex,lonIndex) =  &
5827             (lonIndex * 36000 /  numGridLons(latIndex)) -  &
5828             (18000 / numGridLons(latIndex))
5829        gridLonsMid(latIndex,lonIndex) = 0.01 * gridLonsMid(latIndex,lonIndex)
5830      end do
5831    end do
5832
5833    ! Station ID converted to integer array
5834    stnIdInt(:,:) = 0
5835    HEADER1: do headerIndex = 1, numHeader
5836      if (.not. valid(headerIndex)) cycle HEADER1
5837
5838      stnId = obs_elem_c(obsdat,'STID',headerIndex)
5839      do charIndex = 1, lenStnId
5840        stnIdInt(charIndex,headerIndex) = iachar(stnId(charIndex:charIndex))
5841      end do
5842    end do HEADER1
5843
5844    nsize = numHeaderMaxMpi
5845    call rpn_comm_allgather(valid,    nsize, 'mpi_logical',  &
5846                            validMpi, nsize, 'mpi_logical', 'grid', ierr)
5847    nsize = lenStnId * numHeaderMaxMpi
5848    call rpn_comm_allgather(stnIdInt,    nsize, 'mpi_integer',  &
5849                            stnIdIntMpi, nsize, 'mpi_integer', 'grid', ierr)
5850
5851    ! build a global list of stnId over all mpi tasks
5852    numStnId = 0
5853    numObsStnIdInMpi(:) = 0
5854    HEADER2: do headerIndex = 1, numHeaderMaxMpi * mmpi_nprocs
5855      if (all(stnIdIntMpi(:,headerIndex) == 0)) cycle HEADER2
5856      if (.not.validMpi(headerIndex)) cycle HEADER2
5857
5858      ! Station ID converted back to character string
5859      do charIndex = 1, lenStnId
5860        stnId(charIndex:charIndex) = achar(stnIdIntMpi(charIndex,headerIndex))
5861      end do
5862
5863      if (numStnId < numStnIdMax ) then
5864        stnIdIndexFound = -1
5865        do stnIdIndex = 1, numStnId
5866          if ( stnidList(stnIdIndex) == stnid ) stnIdIndexFound = stnIdIndex
5867        end do
5868        if ( stnIdIndexFound == -1 ) then
5869          numStnId = numStnId + 1
5870          stnidList(numStnId) = stnid
5871          stnIdIndexFound = numStnId
5872        end if
5873        numObsStnIdInMpi(stnIdIndexFound) = numObsStnIdInMpi(stnIdIndexFound) + 1
5874      else
5875        call utl_abort('thn_scatByLatLonBoxes: numStnId too large')
5876      end if
5877    end do HEADER2
5878
5879    ! Initial pass through all observations
5880    HEADER3: do headerIndex = 1, numHeader
5881      if (.not. valid(headerIndex)) cycle HEADER3
5882
5883      ! Station ID converted to integer array
5884      stnId = obs_elem_c(obsdat,'STID',headerIndex)
5885      do charIndex = 1, lenStnId
5886        stnIdInt(charIndex,headerIndex) = iachar(stnId(charIndex:charIndex))
5887      end do
5888
5889      ! Obs lat-lon
5890      obsLonInDegrees = MPC_DEGREES_PER_RADIAN_R8 * obs_headElem_r(obsdat, OBS_LON, headerIndex)
5891      obsLatInDegrees = MPC_DEGREES_PER_RADIAN_R8 * obs_headElem_r(obsdat, OBS_LAT, headerIndex)
5892      obsLonBurpFile = nint(100.0*(obsLonInDegrees - 180.0))
5893      if(obsLonBurpFile < 0) obsLonBurpFile = obsLonBurpFile + 36000
5894      obsLatBurpFile = 9000+nint(100.0*obsLatInDegrees)
5895
5896      ! compute box indices
5897      obsLat = (obsLatBurpFile - 9000.)/100.
5898      do latIndex = 1, numLat
5899        if ( obsLat <= (gridLats(latIndex) + 0.000001) ) then
5900          obsLatIndex(headerIndex) = latIndex
5901          exit
5902        end if
5903      end do
5904
5905      if (obsLonBurpFile >= 18000) then
5906        obsLonBurpFile = obsLonBurpFile - 18000
5907      else
5908        obsLonBurpFile = obsLonBurpFile + 18000
5909      end if
5910      obsLonIndex(headerIndex) =  &
5911           int( obsLonBurpFile /  &
5912                (36000. / real(numGridLons(obsLatIndex(headerIndex)))) ) + 1
5913      if ( obsLonIndex(headerIndex) > numGridLons(obsLatIndex(headerIndex)) ) then
5914        obsLonIndex(headerIndex) = numGridLons(obsLatIndex(headerIndex))
5915      end if
5916
5917      ! compute spatial distances
5918      obsLat = (obsLatBurpFile - 9000.)/100.
5919      obsLon = obsLonBurpFile/100.
5920      obsDistance(headerIndex) =  100.0 * &
5921           ( ((gridLatsMid(obsLatIndex(headerIndex))-obsLat))**2 +  &
5922             ((gridLonsMid(obsLatIndex(headerIndex),obsLonIndex(headerIndex))-obsLon))**2)**0.5
5923
5924      ! calcul de la bin temporelle dans laquelle se trouve l'observation
5925      obsDate = obs_headElem_i(obsdat, OBS_DAT, headerIndex)
5926      obsTime = obs_headElem_i(obsdat, OBS_ETM, headerIndex)
5927      call tim_getStepObsIndex(obsStepIndex_r8, tim_getDatestamp(), &
5928                               obsDate, obsTime, tim_nstepobs)
5929      obsStepIndex(headerIndex) = nint(obsStepIndex_r8)
5930      obsDelMinutes(headerIndex) = nint( 60.0 * tim_dstepobs *  &
5931           abs(real(obsStepIndex(headerIndex)) - obsStepIndex_r8) )
5932
5933      ! check time window
5934      if ( obsDelMinutes(headerIndex) > deltmax ) then
5935        timeRejectCount = timeRejectCount + 1
5936        valid(headerIndex) = .false.
5937      end if
5938
5939      ! find observation flags (assumes 1 level only per headerIndex)
5940      uObsFlag = -1
5941      vObsFlag = -1
5942      call obs_set_current_body_list(obsdat, headerIndex)
5943      BODY3: do 
5944        bodyIndex = obs_getBodyIndex(obsdat)
5945        if (bodyIndex < 0) exit BODY3
5946        obsVarno = obs_bodyElem_i(obsdat, OBS_VNM, bodyIndex)
5947        if (obsVarno == bufr_neus) then
5948          uObsFlag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
5949        else if (obsVarno == bufr_nevs) then
5950          vObsFlag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
5951        end if
5952      end do BODY3
5953
5954      ! modify valid based on flags
5955      if (uObsFlag /= -1 .and. vObsFlag /= -1) then
5956        if ( btest(uObsFlag,16) .or. btest(vObsFlag,16) .or. &
5957             btest(uObsFlag,18) .or. btest(vObsFlag,18) ) then
5958          flagRejectCount = flagRejectCount + 1
5959          valid(headerIndex) = .false.
5960        end if
5961      else
5962        valid(headerIndex) = .false.
5963      end if
5964
5965    end do HEADER3
5966
5967    countObs = count(valid(:))
5968    call rpn_comm_allReduce(countObs, countObsOutMpi, 1, 'mpi_integer', &
5969                            'mpi_sum','grid',ierr)
5970    write(*,*) 'thn_scatByLatLonBoxes: countObs after QC and time tests   = ', &
5971               countObs, countObsOutMpi
5972
5973    ! Gather data from all MPI tasks
5974    nsize = numHeaderMaxMpi
5975    call rpn_comm_allgather(valid,    nsize, 'mpi_logical',  &
5976                            validMpi, nsize, 'mpi_logical', 'grid', ierr)
5977    call rpn_comm_allgather(obsLatIndex,    nsize, 'mpi_integer',  &
5978                            obsLatIndexMpi, nsize, 'mpi_integer', 'grid', ierr)
5979    call rpn_comm_allgather(obsLonIndex,    nsize, 'mpi_integer',  &
5980                            obsLonIndexMpi, nsize, 'mpi_integer', 'grid', ierr)
5981    call rpn_comm_allgather(obsStepIndex,    nsize, 'mpi_integer',  &
5982                            obsStepIndexMpi, nsize, 'mpi_integer', 'grid', ierr)
5983    call rpn_comm_allgather(obsDelMinutes,    nsize, 'mpi_integer',  &
5984                            obsDelMinutesMpi, nsize, 'mpi_integer', 'grid', ierr)
5985    call rpn_comm_allgather(obsDistance,    nsize, 'mpi_real4',  &
5986                            obsDistanceMpi, nsize, 'mpi_real4', 'grid', ierr)
5987    
5988    ! Apply thinning algorithm
5989    HEADER4: do headerIndex = 1, numHeaderMaxMpi*mmpi_nprocs
5990      if (.not. validMpi(headerIndex)) cycle HEADER4
5991
5992      change = .true.
5993
5994      latIndex  = obsLatIndexMpi(headerIndex)
5995      lonIndex  = obsLonIndexMpi(headerIndex)
5996      stepIndex = obsStepIndexMpi(headerIndex)
5997
5998      ! Station ID converted back to character string
5999      do charIndex = 1, lenStnId
6000        stnId(charIndex:charIndex) = achar(stnIdIntMpi(charIndex,headerIndex))
6001      end do
6002      stnIdTrim = stnId(2:6)
6003
6004      if ( stnIdGrid(latIndex,lonIndex,stepIndex) /= '' ) then
6005
6006        ! This is an ASCAT observation
6007        if (stnIdTrim == 'METOP') then
6008
6009          ! si l'obs retenue precedemment etait un ASCAT, on poursuit l'investigation
6010          if ( stnIdTrim == stnIdGrid(latIndex,lonIndex,stepIndex) ) then
6011          
6012            ! si la difference temporelle est plus grande que celle deja retenue
6013            if ( obsDelMinutesMpi(headerIndex) >  &
6014                 delMinutesGrid(latIndex,lonIndex,stepIndex) ) then
6015              change = .false.
6016            else
6017              ! si la distance au centre de la boite est plus grande que celle retenue 
6018              if ( (obsDelMinutesMpi(headerIndex) ==  &
6019                    delMinutesGrid(latIndex,lonIndex,stepIndex)) .and. &
6020                   (obsDistanceMpi(headerIndex) >=  &
6021                    distanceGrid(latIndex,lonIndex,stepIndex)) ) then
6022                change = .false.
6023              end if                    
6024            end if
6025
6026          else
6027
6028            ! si l'obs retenue precedemment etait autre que METOP
6029            change = .true.
6030
6031          end if
6032
6033        else ! satellites autre que METOP
6034
6035          ! si l'obs retenue precedemment etait autre qu'un METOP, on poursuit l'investigation
6036          if ( stnIdTrim == stnIdGrid(latIndex,lonIndex,stepIndex) ) then
6037                 
6038            ! si la difference temporelle est plus grande que celle deja retenue, skip it
6039            if ( obsDelMinutesMpi(headerIndex) >  &
6040                 delMinutesGrid(latIndex,lonIndex,stepIndex) ) then
6041              change = .false.
6042            else
6043              ! si la distance au centre de la boite est plus grande que celle retenue, skip it 
6044              if ( (obsDelMinutesMpi(headerIndex) ==  &
6045                    delMinutesGrid(latIndex,lonIndex,stepIndex)) .and. &
6046                   (obsDistanceMpi(headerIndex) >=  &
6047                    distanceGrid(latIndex,lonIndex,stepIndex)) ) then
6048                change = .false.
6049              end if
6050            end if
6051
6052          else
6053
6054            ! si l'obs retenue precedemment etait un METOP
6055            change = .false.
6056
6057          end if
6058
6059        end if ! METOP
6060
6061      end if
6062
6063      ! update list of data to save
6064      if ( .not. change ) then
6065        ! keep previously accepted obs, so reject current obs
6066        validMpi(headerIndex) = .false.
6067      else
6068        ! reject previously accepted obs
6069        if ( headerIndexGrid(latIndex,lonIndex,stepIndex) /= -1 ) then
6070          validMpi(headerIndexGrid(latIndex,lonIndex,stepIndex)) = .false.
6071        end if
6072
6073        ! keep current obs
6074        validMpi(headerIndex) = .true.
6075        headerIndexGrid(latIndex,lonIndex,stepIndex) = headerIndex
6076        stnIdGrid(latIndex,lonIndex,stepIndex) = stnIdTrim
6077        delMinutesGrid(latIndex,lonIndex,stepIndex) = obsDelMinutesMpi(headerIndex)
6078        distanceGrid(latIndex,lonIndex,stepIndex) = obsDistanceMpi(headerIndex)
6079      end if
6080
6081    end do HEADER4
6082
6083    ! update local copy of 'valid' array
6084    headerIndexBeg = 1 + mmpi_myid * numHeaderMaxMpi
6085    headerIndexEnd = headerIndexBeg + numHeaderMaxMpi - 1
6086    valid(:) = validMpi(headerIndexBeg:headerIndexEnd)
6087
6088    countObs = count(valid(:))
6089    call rpn_comm_allReduce(countObs, countObsOutMpi, 1, 'mpi_integer', &
6090                            'mpi_sum','grid',ierr)
6091    write(*,*) 'thn_scatByLatLonBoxes: countObs after choosing 1 per box  = ', &
6092               countObs, countObsOutMpi
6093
6094    ! modify the observation flags in obsSpaceData and count obs for each stnId
6095    numObsStnIdOut(:) = 0
6096    call obs_set_current_header_list(obsdat,'SC')
6097    HEADER5: do
6098      headerIndex = obs_getHeaderIndex(obsdat)
6099      if (headerIndex < 0) exit HEADER5
6100     
6101      if (.not. valid(headerIndex)) then
6102        call obs_set_current_body_list(obsdat, headerIndex)
6103        BODY5: do 
6104          bodyIndex = obs_getBodyIndex(obsdat)
6105          if (bodyIndex < 0) exit BODY5
6106        
6107          obsFlag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
6108          call obs_bodySet_i(obsdat, OBS_FLG, bodyIndex, ibset(obsFlag,11))
6109
6110        end do BODY5
6111        cycle HEADER5
6112      end if
6113
6114      ! count number of obs kept for each stnId
6115      stnId = obs_elem_c(obsdat,'STID',headerIndex)
6116      stnIdIndexFound = -1
6117      do stnIdIndex = 1, numStnId
6118        if (stnidList(stnIdIndex) == stnId) stnIdIndexFound = stnIdIndex
6119      end do
6120      if (stnIdIndexFound == -1) call utl_abort('stnid not found in list')
6121      numObsStnIdOut(stnIdIndexFound) = numObsStnIdOut(stnIdIndexFound) + 1
6122    end do HEADER5
6123
6124    call rpn_comm_allReduce(numObsStnIdOut, numObsStnIdOutMpi, &
6125                            numStnIdMax, 'mpi_integer', 'mpi_sum', 'grid', ierr)
6126    call rpn_comm_allReduce(timeRejectCount, timeRejectCountMpi, 1, &
6127                            'mpi_integer', 'mpi_sum', 'grid', ierr)
6128    call rpn_comm_allReduce(flagRejectCount, flagRejectCountMpi, 1, &
6129                            'mpi_integer', 'mpi_sum', 'grid', ierr)
6130
6131    write(*,*)
6132    write(*,'(a,i6)') 'scatByLatLonBoxes: Number of obs in input  = ', countObsInMpi
6133    write(*,'(a,i6)') 'scatByLatLonBoxes: Number of obs in output = ', countObsOutMpi
6134    write(*,'(a,i6)') 'scatByLatLonBoxes: Number of obs not selected due to time = ', &
6135         timeRejectCountMpi
6136    write(*,'(a,i6)') 'scatByLatLonBoxes: Number of obs not selected due to topo = ', &
6137         flagRejectCountMpi
6138    write(*,*)
6139    
6140    write(*,'(a40,i10)' ) 'Number of satellites found = ', numStnId
6141    write(*,*)
6142  
6143    write(*,'(a40,a15)' ) 'Satellite', 'nb SCAT in'
6144    write(*,*)
6145    do stnIdIndex = 1, numStnId
6146      write(*,'(a40,i15)') stnidList(stnIdIndex), numObsStnIdInMpi(stnIdIndex)
6147    end do
6148    write(*,*)
6149    write(*,'(a40,i15)' ) 'Total number of obs in : ', sum(numObsStnIdInMpi(:))
6150  
6151    write(*,*)
6152    write(*,'(a40,a15)' ) 'Satellite', 'nb SCAT out'
6153    write(*,*)
6154    do stnIdIndex = 1, numStnId
6155      write(*,'(a40,i15)') stnidList(stnIdIndex), numObsStnIdOutMpi(stnIdIndex)
6156    end do
6157    write(*,*)
6158    write(*,'(a40,i15)' ) 'Total number of obs out : ', sum(numObsStnIdOutMpi(:))
6159
6160    ! Deallocations:
6161    deallocate(valid)
6162    deallocate(gridLats)
6163    deallocate(gridLatsMid)
6164    deallocate(gridLonsMid)
6165    deallocate(numGridLons)
6166    deallocate(stnIdGrid)
6167    deallocate(distanceGrid)
6168    deallocate(headerIndexGrid)
6169    deallocate(delMinutesGrid)
6170
6171    deallocate(obsLatIndex)
6172    deallocate(obsLonIndex)
6173    deallocate(obsStepIndex)
6174    deallocate(obsDistance)
6175    deallocate(obsDelMinutes)
6176    deallocate(stnIdInt)
6177
6178    deallocate(validMpi)
6179    deallocate(obsLatIndexMpi)
6180    deallocate(obsLonIndexMpi)
6181    deallocate(obsStepIndexMpi)
6182    deallocate(obsDistanceMpi)
6183    deallocate(obsDelMinutesMpi)
6184    deallocate(stnIdIntMpi)
6185
6186    write(*,*)
6187    write(*,*) 'thn_scatByLatLonBoxes: Finished'
6188    write(*,*)
6189
6190  end subroutine thn_scatByLatLonBoxes
6191
6192  !--------------------------------------------------------------------------
6193  ! thn_csrByLatLonBoxes
6194  !--------------------------------------------------------------------------
6195  subroutine thn_csrByLatLonBoxes(obsdat, deltax, deltrad)
6196    !
6197    !:Purpose: Only keep the observation closest to the center of each
6198    !           lat-lon (and time) box for CSR observations.
6199    !           Set bit 11 of OBS_FLG on observations that are to be rejected.
6200    !
6201    implicit none
6202
6203    ! Arguments:
6204    type(struct_obs), intent(inout) :: obsdat
6205    integer,          intent(in)    :: deltax
6206    integer,          intent(in)    :: deltrad
6207
6208    ! Locals:
6209    integer, parameter :: latLength = 10000 ! Earth dimension parameters
6210    integer, parameter :: lonLength = 40000 ! Earth dimension parameters
6211    integer, parameter :: maxNumChan = 15    ! nb max de canaux
6212    integer :: bodyIndex, channelIndex, charIndex, nsize, lenStnId
6213    integer :: numLat, numLon, latIndex, lonIndex, stepIndex, obsFlag
6214    integer :: ierr, headerIndex, numHeader, numHeaderMaxMpi, channelList(maxNumChan)
6215    integer :: headerIndexBeg, headerIndexEnd, countObs, countObsMpi
6216    integer :: obsLonBurpFile, obsLatBurpFile, obsDate, obsTime
6217    real(4) :: latInRadians, distance, obsLat, obsLon, gridLat, gridLon
6218    real(8) :: obsLatInDegrees, obsLonInDegrees, obsStepIndex_r8
6219    logical :: change
6220    real(4), allocatable :: gridLats(:)
6221    integer, allocatable :: numChanAssimGrid(:,:,:), headerIndexGrid(:,:,:)
6222    real(4), allocatable :: angleGrid(:,:,:), distanceGrid(:,:,:), cloudGrid(:,:,:,:)
6223    integer, allocatable :: obsLatIndex(:), obsLonIndex(:), obsStepIndex(:), numGridLons(:)
6224    real(4), allocatable :: obsCloud(:,:), obsAngle(:), obsDistance(:)
6225    integer, allocatable :: obsLatIndexMpi(:), obsLonIndexMpi(:), obsStepIndexMpi(:)
6226    integer, allocatable :: stnIdInt(:,:), stnIdIntMpi(:,:), numChannel(:), numChannelMpi(:)
6227    real(4), allocatable :: obsCloudMpi(:,:), obsAngleMpi(:), obsDistanceMpi(:)
6228    logical, allocatable :: valid(:), validMpi(:), channelAssim(:,:), channelAssimMpi(:,:)
6229    character(len=12) :: stnId
6230    character(len=12), allocatable :: stnIdGrid(:,:,:)
6231
6232    write(*,*)
6233    write(*,*) 'thn_csrByLatLonBoxes: Starting'
6234    write(*,*)
6235
6236    numHeader = obs_numHeader(obsdat)
6237    call rpn_comm_allReduce(numHeader, numHeaderMaxMpi, 1, 'mpi_integer', &
6238                            'mpi_max','grid',ierr)
6239    write(*,*) 'thn_csrByLatLonBoxes: numHeader, numHeaderMaxMpi = ', &
6240               numHeader, numHeaderMaxMpi
6241
6242    ! Check if we have any observations to process
6243    allocate(valid(numHeaderMaxMpi))
6244    valid(:) = .false.
6245    do headerIndex = 1, numHeader
6246      if ( obs_headElem_i(obsdat, OBS_ITY, headerIndex) == &
6247           codtyp_get_codtyp('radianceclear') ) then
6248        valid(headerIndex) = .true.
6249      end if
6250    end do
6251    countObs = count(valid(:))
6252    call rpn_comm_allReduce(countObs, countObsMpi, 1, 'mpi_integer', &
6253                            'mpi_sum','grid',ierr)
6254    if (countObsMpi == 0) then
6255      write(*,*) 'thn_csrByLatLonBoxes: no observations for this instrument'
6256      deallocate(valid)
6257      return
6258    end if
6259
6260    numLat = nint(2.*real(latLength)/real(deltax))
6261    numLon = nint(real(lonLength)/real(deltax))
6262
6263    write(*,*)
6264    write(*,*) 'Number of horizontal boxes : ', numLon
6265    write(*,*) 'Number of vertical boxes   : ', numLat
6266    write(*,*) 'Number of temporal bins    : ', tim_nstepobs
6267    write(*,*)
6268
6269    write(*,*) 'thn_csrByLatLonBoxes: countObs initial                   = ', &
6270               countObs, countObsMpi
6271
6272    ! Allocate arrays
6273    allocate(gridLats(numLat))
6274    allocate(numGridLons(numLat))
6275    allocate(stnIdGrid(numLat,numLon,tim_nstepobs))
6276    allocate(numChanAssimGrid(numLat,numLon,tim_nstepobs))
6277    allocate(angleGrid(numLat,numLon,tim_nstepobs))
6278    allocate(distanceGrid(numLat,numLon,tim_nstepobs))
6279    allocate(headerIndexGrid(numLat,numLon,tim_nstepobs))
6280    allocate(cloudGrid(maxNumChan,numLat,numLon,tim_nstepobs))
6281    allocate(obsLatIndex(numHeaderMaxMpi))
6282    allocate(obsLonIndex(numHeaderMaxMpi))
6283    allocate(obsStepIndex(numHeaderMaxMpi))
6284    allocate(numChannel(numHeaderMaxMpi))
6285    allocate(channelAssim(maxNumChan,numHeaderMaxMpi))
6286    allocate(obsAngle(numHeaderMaxMpi))
6287    allocate(obsCloud(maxNumChan,numHeaderMaxMpi))
6288    allocate(obsDistance(numHeaderMaxMpi))
6289
6290    gridLats(:)             = 0.
6291    numGridLons(:)          = 0
6292    stnIdGrid(:,:,:)        = ''
6293    numChanAssimGrid(:,:,:) = -1
6294    angleGrid(:,:,:)        = -1.0
6295    distanceGrid(:,:,:)     = -1.0
6296    headerIndexGrid(:,:,:)  = -1
6297    cloudGrid(:,:,:,:)      = -1.0
6298    channelAssim(:,:)       = .false.
6299    numChannel(:)           = 0
6300    obsAngle(:)             = 0.0
6301    obsCloud(:,:)           = 0.0
6302    obsDistance(:)          = 0.0
6303
6304    ! set spatial boxes properties
6305
6306    do latIndex = 1, numLat
6307      gridLats(latIndex) = (latIndex*180./numLat) - 90.
6308      if (gridLats(latIndex) <= 0.0) then
6309        latInRadians = gridLats(latIndex) * MPC_PI_R8 / 180.
6310      else
6311        latInRadians = gridLats(latIndex-1) * MPC_PI_R8 / 180.
6312      end if
6313      distance = lonLength * cos(latInRadians)
6314      numGridLons(latIndex) = nint(distance/deltax)
6315    end do
6316
6317    ! Initial pass through all observations
6318    HEADER1: do headerIndex = 1, numHeader
6319      if (.not. valid(headerIndex)) cycle HEADER1
6320
6321      obsLonInDegrees = MPC_DEGREES_PER_RADIAN_R8 * obs_headElem_r(obsdat, OBS_LON, headerIndex)
6322      obsLatInDegrees = MPC_DEGREES_PER_RADIAN_R8 * obs_headElem_r(obsdat, OBS_LAT, headerIndex)
6323      obsLonBurpFile = nint(100.0*(obsLonInDegrees - 180.0))
6324      if(obsLonBurpFile < 0) obsLonBurpFile = obsLonBurpFile + 36000
6325      obsLatBurpFile = 9000+nint(100.0*obsLatInDegrees)
6326
6327      ! compute box indices
6328      do latIndex = 1, numLat
6329        if ( (obsLatBurpFile - 9000.)/100. <= (gridLats(latIndex) + 0.000001) ) then
6330          obsLatIndex(headerIndex) = latIndex
6331          exit
6332        end if
6333      end do
6334
6335      obsLonIndex(headerIndex) = int(obsLonBurpFile /  &
6336           (36000. / numGridLons(obsLatIndex(headerIndex)))) + 1
6337      if ( obsLonIndex(headerIndex) > numGridLons(obsLatIndex(headerIndex)) ) then
6338        obsLonIndex(headerIndex) = numGridLons(obsLatIndex(headerIndex))
6339      end if
6340
6341      ! compute spatial distances
6342      ! position of the observation
6343      obsLat = (obsLatBurpFile - 9000.) / 100.
6344      obsLon = obsLonBurpFile / 100.
6345
6346      ! position of the box center
6347      gridLat = gridLats(obsLatIndex(headerIndex)) - 0.5 * (180./numLat)
6348      gridLon = (360. / numGridLons(obsLatIndex(headerIndex))) *  &
6349                (obsLonIndex(headerIndex) - 0.5)
6350
6351      ! spatial separation
6352      obsDistance(headerIndex) = thn_separation(obsLon,obsLat,gridLon,gridLat) * &
6353                                 latLength / 90.
6354
6355      ! calcul de la bin temporelle dans laquelle se trouve l'observation
6356      obsDate = obs_headElem_i(obsdat, OBS_DAT, headerIndex)
6357      obsTime = obs_headElem_i(obsdat, OBS_ETM, headerIndex)
6358      call tim_getStepObsIndex(obsStepIndex_r8, tim_getDatestamp(), &
6359                               obsDate, obsTime, tim_nstepobs)
6360      obsStepIndex(headerIndex) = nint(obsStepIndex_r8)
6361
6362      ! check if distance too far from box center
6363      if (obsDistance(headerIndex) > real(deltrad)) then
6364        valid(headerIndex) = .false.
6365        cycle HEADER1
6366      end if
6367
6368    end do HEADER1
6369
6370    countObs = count(valid(:))
6371    call rpn_comm_allReduce(countObs, countObsMpi, 1, 'mpi_integer', &
6372                            'mpi_sum','grid',ierr)
6373    write(*,*) 'thn_csrByLatLonBoxes: countObs after deltrad test        = ', &
6374               countObs, countObsMpi
6375
6376    ! Second pass through all observations
6377    HEADER2: do headerIndex = 1, numHeader
6378      if (.not. valid(headerIndex)) cycle HEADER2
6379      
6380      ! get the zenith angle
6381      obsAngle(headerIndex) = obs_headElem_r(obsdat, OBS_SZA, headerIndex)
6382
6383      ! Keep obs only if at least one channel not rejected based on tests in suprep
6384      valid(headerIndex) = .false.
6385      channelIndex = 0
6386      call obs_set_current_body_list(obsdat, headerIndex)
6387      BODY1: do 
6388        bodyIndex = obs_getBodyIndex(obsdat)
6389        if (bodyIndex < 0) exit BODY1
6390
6391        if (obs_bodyElem_i(obsdat, OBS_VNM, bodyIndex) /= bufr_nbt3) then
6392          cycle BODY1
6393        end if
6394
6395        numChannel(headerIndex) = numChannel(headerIndex) + 1
6396        channelIndex = channelIndex + 1
6397        channelList(channelIndex) = nint(obs_bodyElem_r(obsdat, OBS_PPP, bodyIndex))
6398
6399        obsFlag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
6400        if ( .not.btest(obsFlag,8) .and. &
6401             .not.btest(obsFlag,9) .and. &
6402             .not.btest(obsFlag,11) ) then
6403          valid(headerIndex) = .true.
6404          channelAssim(channelIndex,headerIndex) = .true.
6405        end if
6406      end do BODY1
6407
6408      ! read the new element 20081 in obsspacedata OBS_CLA
6409      CHANNELS: do channelIndex = 1, numChannel(headerIndex)
6410        obsCloud(channelIndex, headerIndex) = -1.0
6411
6412        ! search for the cloud information for this channel
6413        call obs_set_current_body_list(obsdat, headerIndex)
6414        BODY2: do
6415          bodyIndex = obs_getBodyIndex(obsdat)
6416          if (bodyIndex < 0) exit BODY2
6417
6418          ! check if channel number matches
6419          if (nint(obs_bodyElem_r(obsdat, OBS_PPP, bodyIndex)) == channelList(channelIndex)) then
6420            obsCloud(channelIndex, headerIndex) = real(obs_bodyElem_i(obsdat, OBS_CLA, bodyIndex))
6421            cycle CHANNELS
6422          end if
6423        end do BODY2
6424        if (obsCloud(channelIndex, headerIndex) == -1.0) then
6425          call utl_abort('thn_csrByLatLonBoxes: could not find cloud fraction in obsSpaceData')
6426        end if
6427      end do CHANNELS
6428
6429    end do HEADER2
6430
6431    countObs = count(valid(:))
6432    call rpn_comm_allReduce(countObs, countObsMpi, 1, 'mpi_integer', &
6433                            'mpi_sum','grid',ierr)
6434    write(*,*) 'thn_csrByLatLonBoxes: countObs after rejection flag test = ', &
6435               countObs, countObsMpi
6436
6437    ! Allocation for MPI gather
6438    allocate(validMpi(numHeaderMaxMpi*mmpi_nprocs))
6439    allocate(obsLatIndexMpi(numHeaderMaxMpi*mmpi_nprocs))
6440    allocate(obsLonIndexMpi(numHeaderMaxMpi*mmpi_nprocs))
6441    allocate(obsStepIndexMpi(numHeaderMaxMpi*mmpi_nprocs))
6442    allocate(numChannelMpi(numHeaderMaxMpi*mmpi_nprocs))
6443    allocate(obsAngleMpi(numHeaderMaxMpi*mmpi_nprocs))
6444    allocate(obsDistanceMpi(numHeaderMaxMpi*mmpi_nprocs))
6445    allocate(channelAssimMpi(maxNumChan,numHeaderMaxMpi*mmpi_nprocs))
6446    allocate(obsCloudMpi(maxNumChan,numHeaderMaxMpi*mmpi_nprocs))
6447    lenStnId = len(stnId)
6448    allocate(stnIdInt(lenStnId,numHeaderMaxMpi))
6449    allocate(stnIdIntMpi(lenStnId,numHeaderMaxMpi*mmpi_nprocs))
6450
6451    ! Initialize arrays
6452    obsLatIndexMpi(:)    = 0
6453    obsLonIndexMpi(:)    = 0
6454    obsStepIndexMpi(:)   = 0
6455    numChannelMpi(:)     = 0
6456    obsAngleMpi(:)       = 0.0
6457    obsDistanceMpi(:)    = 0.0
6458    channelAssimMpi(:,:) = 0
6459    obsCloudMpi(:,:)     = 0.0
6460    stnIdInt(:,:)        = 0
6461    stnIdIntMpi(:,:)     = 0
6462
6463    ! Station ID converted to integer array
6464    HEADER3: do headerIndex = 1, numHeader
6465      if (.not. valid(headerIndex)) cycle HEADER3
6466
6467      stnId = obs_elem_c(obsdat,'STID',headerIndex)
6468      do charIndex = 1, lenStnId
6469        stnIdInt(charIndex,headerIndex) = iachar(stnId(charIndex:charIndex))
6470      end do
6471    end do HEADER3
6472
6473    ! Gather data from all MPI tasks
6474    nsize = numHeaderMaxMpi
6475    call rpn_comm_allgather(valid,    nsize, 'mpi_logical',  &
6476                            validMpi, nsize, 'mpi_logical', 'grid', ierr)
6477    call rpn_comm_allgather(obsLatIndex,    nsize, 'mpi_integer',  &
6478                            obsLatIndexMpi, nsize, 'mpi_integer', 'grid', ierr)
6479    call rpn_comm_allgather(obsLonIndex,    nsize, 'mpi_integer',  &
6480                            obsLonIndexMpi, nsize, 'mpi_integer', 'grid', ierr)
6481    call rpn_comm_allgather(obsStepIndex,    nsize, 'mpi_integer',  &
6482                            obsStepIndexMpi, nsize, 'mpi_integer', 'grid', ierr)
6483    call rpn_comm_allgather(numChannel,    nsize, 'mpi_integer',  &
6484                            numChannelMpi, nsize, 'mpi_integer', 'grid', ierr)
6485    call rpn_comm_allgather(obsAngle,    nsize, 'mpi_real4',  &
6486                            obsAngleMpi, nsize, 'mpi_real4', 'grid', ierr)
6487    call rpn_comm_allgather(obsDistance,    nsize, 'mpi_real4',  &
6488                            obsDistanceMpi, nsize, 'mpi_real4', 'grid', ierr)
6489
6490    nsize = maxNumChan * numHeaderMaxMpi
6491    call rpn_comm_allgather(channelAssim,    nsize, 'mpi_integer',  &
6492                            channelAssimMpi, nsize, 'mpi_integer', 'grid', ierr)
6493    call rpn_comm_allgather(obsCloud,    nsize, 'mpi_real4',  &
6494                            obsCloudMpi, nsize, 'mpi_real4', 'grid', ierr)
6495
6496    nsize = lenStnId * numHeaderMaxMpi
6497    call rpn_comm_allgather(stnIdInt,    nsize, 'mpi_integer',  &
6498                            stnIdIntMpi, nsize, 'mpi_integer', 'grid', ierr)
6499    
6500    ! Apply thinning algorithm
6501    HEADER4: do headerIndex = 1, numHeaderMaxMpi*mmpi_nprocs
6502      if (.not. validMpi(headerIndex)) cycle HEADER4
6503
6504      change = .true.
6505
6506      latIndex  = obsLatIndexMpi(headerIndex)
6507      lonIndex  = obsLonIndexMpi(headerIndex)
6508      stepIndex = obsStepIndexMpi(headerIndex)
6509
6510      ! Station ID converted back to character string
6511      do charIndex = 1, lenStnId
6512        stnId(charIndex:charIndex) = achar(stnIdIntMpi(charIndex,headerIndex))
6513      end do
6514
6515      if ( stnIdGrid(latIndex,lonIndex,stepIndex) /= '' ) then
6516
6517        ! on veut previlegier les profils avec le plus de canaux assimiles
6518        if ( count(channelAssimMpi(:,headerIndex)) <  &
6519             numChanAssimGrid(latIndex,lonIndex,stepIndex) ) change = .false.
6520
6521        ! en cas d'egalite, on doit regarder d'autres conditions pour faire un choix
6522        if ( count(channelAssimMpi(:,headerIndex)) ==  &
6523             numChanAssimGrid(latIndex,lonIndex,stepIndex) ) then
6524
6525          ! si le profil actuel est d'un autre instrument que celui deja considere
6526          ! choisir celui qui a le plus petit angle satellite
6527          if ( stnid /= stnIdGrid(latIndex,lonIndex,stepIndex) ) then
6528            if ( obsAngleMpi(headerIndex)  >  &
6529                 angleGrid(latIndex,lonIndex,stepIndex) ) change = .false.
6530
6531            ! en cas d'egalite de l'angle,
6532            ! choisir le profil le plus pres du centre de la boite
6533            if ( ( obsAngleMpi(headerIndex) ==  &
6534                   angleGrid(latIndex,lonIndex,stepIndex) ) .and. &
6535                 ( obsDistanceMpi(headerIndex) >  &
6536                   distanceGrid(latIndex,lonIndex,stepIndex) ) ) change = .false.
6537
6538          ! si le profil actuel est du meme instrument que celui deja considere
6539          ! choisir celui dont tous les canaux assimiles ont respectivement
6540          ! moins de fraction nuageuse que celui deja considere
6541          else
6542            do channelIndex = 1, numChannelMpi(headerIndex)
6543              if ( channelAssimMpi(channelIndex,headerIndex) .and. &
6544                   ( obsCloudMpi(channelIndex,headerIndex) >  &
6545                     cloudGrid(channelIndex,latIndex,lonIndex,stepIndex) ) ) change = .false.
6546            end do
6547
6548            ! en cas d'egalite de la fraction nuageuse pour chaque canal present,
6549            ! choisir le profil le plus pres du centre de la boite
6550            do channelIndex = 1, numChannelMpi(headerIndex)
6551              if ( channelAssimMpi(channelIndex,headerIndex) .and. &
6552                   ( obsCloudMpi(channelIndex,headerIndex) <  &
6553                     cloudGrid(channelIndex,latIndex,lonIndex,stepIndex) ) ) exit
6554              if ( ( channelIndex == numChannelMpi(headerIndex) ) .and. &
6555                   ( obsDistanceMpi(headerIndex) >  &
6556                     distanceGrid(latIndex,lonIndex,stepIndex) ) ) change = .false.
6557            end do
6558          end if
6559        end if
6560      end if
6561
6562      ! update list of data to save
6563      if ( .not. change ) then
6564        ! keep previously accepted obs, so reject current obs
6565        validMpi(headerIndex) = .false.
6566      else
6567        ! reject previously accepted obs
6568        if ( headerIndexGrid(latIndex,lonIndex,stepIndex) /= -1 ) then
6569          validMpi(headerIndexGrid(latIndex,lonIndex,stepIndex)) = .false.
6570        end if
6571
6572        ! keep current obs
6573        validMpi(headerIndex) = .true.
6574        headerIndexGrid(latIndex,lonIndex,stepIndex) = headerIndex
6575        numChanAssimGrid(latIndex,lonIndex,stepIndex) =  &
6576             count(channelAssimMpi(:,headerIndex))
6577        stnIdGrid(latIndex,lonIndex,stepIndex) = stnid
6578        angleGrid(latIndex,lonIndex,stepIndex) = obsAngleMpi(headerIndex)
6579        cloudGrid(:,latIndex,lonIndex,stepIndex) = obsCloudMpi(:,headerIndex)
6580        distanceGrid(latIndex,lonIndex,stepIndex) = obsDistanceMpi(headerIndex)
6581      end if
6582
6583    end do HEADER4
6584
6585    ! update local copy of 'valid' array
6586    headerIndexBeg = 1 + mmpi_myid * numHeaderMaxMpi
6587    headerIndexEnd = headerIndexBeg + numHeaderMaxMpi - 1
6588    valid(:) = validMpi(headerIndexBeg:headerIndexEnd)
6589
6590    countObs = count(valid(:))
6591    call rpn_comm_allReduce(countObs, countObsMpi, 1, 'mpi_integer', &
6592                            'mpi_sum','grid',ierr)
6593    write(*,*) 'thn_csrByLatLonBoxes: countObs after choosing 1 per box  = ', &
6594               countObs, countObsMpi
6595
6596    ! modify the observation flags in obsSpaceData
6597    HEADER5: do headerIndex = 1, numHeader
6598      ! skip observation if we're not supposed to consider it
6599      if ( obs_headElem_i(obsdat, OBS_ITY, headerIndex) /= &
6600           codtyp_get_codtyp('radianceclear') ) then
6601        cycle HEADER5
6602      end if
6603     
6604      if (.not. valid(headerIndex)) then
6605        call obs_set_current_body_list(obsdat, headerIndex)
6606        BODY3: do 
6607          bodyIndex = obs_getBodyIndex(obsdat)
6608          if (bodyIndex < 0) exit BODY3
6609        
6610          obsFlag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
6611          call obs_bodySet_i(obsdat, OBS_FLG, bodyIndex, ibset(obsFlag,11))
6612
6613        end do BODY3
6614      end if
6615    end do HEADER5
6616
6617    deallocate(valid)
6618    deallocate(gridLats)
6619    deallocate(numGridLons)
6620    deallocate(stnIdGrid)
6621    deallocate(numChanAssimGrid)
6622    deallocate(angleGrid)
6623    deallocate(distanceGrid)
6624    deallocate(headerIndexGrid)
6625    deallocate(cloudGrid)
6626    deallocate(obsLatIndex)
6627    deallocate(obsLonIndex)
6628    deallocate(obsStepIndex)
6629    deallocate(numChannel)
6630    deallocate(channelAssim)
6631    deallocate(obsAngle)
6632    deallocate(obsCloud)
6633    deallocate(obsDistance)
6634    deallocate(validMpi)
6635    deallocate(obsLatIndexMpi)
6636    deallocate(obsLonIndexMpi)
6637    deallocate(obsStepIndexMpi)
6638    deallocate(numChannelMpi)
6639    deallocate(obsAngleMpi)
6640    deallocate(obsDistanceMpi)
6641    deallocate(channelAssimMpi)
6642    deallocate(obsCloudMpi)
6643    deallocate(stnIdInt)
6644    deallocate(stnIdIntMpi)
6645
6646    write(*,*)
6647    write(*,*) 'thn_csrByLatLonBoxes: Finished'
6648    write(*,*)
6649
6650  end subroutine thn_csrByLatLonBoxes
6651
6652  !--------------------------------------------------------------------------
6653  ! thn_hyperByLatLonBoxes
6654  !--------------------------------------------------------------------------
6655  subroutine thn_hyperByLatLonBoxes(obsdat, removeUnCorrected, &
6656                                    deltmax, deltax, deltrad,  &
6657                                    familyType, codtyp)
6658    !
6659    !:Purpose: Only keep the observation closest to the center of each
6660    !           lat-lon (and time) box.
6661    !           Set bit 11 of OBS_FLG on observations that are to be rejected.
6662    !
6663    implicit none
6664
6665    ! Arguments:
6666    type(struct_obs), intent(inout) :: obsdat
6667    logical,          intent(in)    :: removeUnCorrected
6668    integer,          intent(in)    :: deltmax
6669    integer,          intent(in)    :: deltax
6670    integer,          intent(in)    :: deltrad
6671    character(len=*), intent(in)    :: familyType
6672    integer,          intent(in)    :: codtyp
6673
6674    ! Locals:
6675    integer :: headerIndex, bodyIndex, obsDate, obsTime, obsFlag
6676    integer :: numHeader, numHeaderMpi, numHeaderMaxMpi, lenStnId 
6677    integer :: numLat, numLon, latIndex, numChannels, delMinutes
6678    integer :: lonBinIndex, latBinIndex, timeBinIndex, charIndex
6679    integer :: ierr, nsize, procIndex, countHeader, countHeaderMpi
6680    real(4) :: latInRadians, length, distance
6681    real(8) :: lonBoxCenterInDegrees, latBoxCenterInDegrees
6682    real(8) :: obsLatInRad, obsLonInRad, obsLat, obsLon
6683    real(8) :: obsLatInDegrees, obsLonInDegrees, obsStepIndex_r8
6684    integer, parameter :: latLength = 10000
6685    integer, parameter :: lonLength = 40000
6686    real(4), allocatable :: gridLats(:)
6687    integer, allocatable :: numGridLons(:)
6688    integer, allocatable :: stnIdInt(:,:), stnIdIntMpi(:,:)
6689    integer, allocatable :: headerIndexKeep(:,:,:), numChannelsKeep(:,:,:)
6690    integer, allocatable :: headerIndexKeepMpi(:,:,:,:), numChannelsKeepMpi(:,:,:,:)
6691    integer, allocatable :: delMinutesKeep(:,:,:), delMinutesKeepMpi(:,:,:,:)
6692    integer, allocatable :: procIndexKeep(:,:,:)
6693    real(4), allocatable :: distanceKeep(:,:,:), distanceKeepMpi(:,:,:,:)
6694    logical, allocatable :: rejectThisHeader(:)
6695    logical :: keepThisObs, stnIdFoundInList
6696    integer :: obsLonBurpFile, obsLatBurpFile
6697    integer, parameter :: numStnIdMax = 10
6698    integer :: numStnId, stnIdIndexFound, stnIdIndex, countMpi, numObsStnId(numStnIdMax)
6699    character(len=12) :: stnid, stnidList(numStnIdMax)
6700    character(len=codtyp_name_length) :: instrumName
6701
6702    instrumName = codtyp_get_name(codtyp)
6703    write(*,*)
6704    write(*,*) 'thn_hyperByLatLonBoxes: Starting, ', trim(instrumName)
6705    write(*,*)
6706
6707    numHeader = obs_numHeader(obsdat)
6708    call rpn_comm_allReduce(numHeader, numHeaderMaxMpi, 1, 'mpi_integer', &
6709                            'mpi_max', 'grid', ierr)
6710
6711    lenStnId = len(stnId)
6712    allocate(stnIdInt(lenStnId,numHeaderMaxMpi))
6713    allocate(stnIdIntMpi(lenStnId,numHeaderMaxMpi*mmpi_nprocs))
6714    stnIdInt(:,:) = 0
6715    stnIdIntMpi(:,:) = 0
6716
6717    ! loop over all header indices of the specified family and get integer stnId
6718    countHeader = 0
6719    call obs_set_current_header_list(obsdat,trim(familyType))
6720    HEADER: do
6721      headerIndex = obs_getHeaderIndex(obsdat)
6722      if (headerIndex < 0) exit HEADER
6723      if (obs_headElem_i(obsdat, OBS_ITY, headerIndex) /= codtyp) cycle HEADER
6724
6725      countHeader = countHeader + 1
6726
6727      ! convert and store stnId as integer array
6728      stnId = obs_elem_c(obsdat,'STID',headerIndex)
6729      do charIndex = 1, lenStnId
6730        stnIdInt(charIndex,headerIndex) = iachar(stnId(charIndex:charIndex))
6731      end do
6732    end do HEADER
6733
6734    ! return if no observations for this instrument
6735    call rpn_comm_allReduce(countHeader, countHeaderMpi, 1, 'mpi_integer', &
6736                            'mpi_sum', 'grid', ierr)
6737    if (countHeaderMpi == 0) then
6738      write(*,*) 'thn_hyperByLatLonBoxes: no observations for this instrument'
6739      return
6740    end if
6741
6742    ! Gather stnIdInt from all MPI tasks
6743    nsize = lenStnId * numHeaderMaxMpi
6744    call rpn_comm_allgather(stnIdInt,    nsize, 'mpi_integer',  &
6745                            stnIdIntMpi, nsize, 'mpi_integer', 'grid', ierr)
6746
6747    ! build a global stnIdList
6748    numStnId = 0
6749    numHeaderMpi = numHeaderMaxMpi * mmpi_nprocs
6750    HEADER4: do headerIndex = 1, numHeaderMpi
6751      if (all(stnIdIntMpi(:,headerIndex) == 0)) cycle HEADER4
6752
6753      ! Station ID converted back to character string
6754      do charIndex = 1, lenStnId
6755        stnId(charIndex:charIndex) = achar(stnIdIntMpi(charIndex,headerIndex))
6756      end do
6757
6758      stnIdFoundInList = .false.
6759      if ( numStnId == 0 ) then
6760        stnIdFoundInList = .true.
6761        numStnId = numStnId + 1
6762        stnidList(numStnId) = stnId
6763      else
6764        do stnIdIndex = 1, numStnId
6765          if ( stnidList(stnIdIndex) == stnId ) cycle HEADER4
6766        end do
6767      end if
6768
6769      if ( .not. stnIdFoundInList ) then
6770        numStnId = numStnId + 1
6771        stnidList(numStnId) = stnId
6772      end if
6773
6774      if ( numStnId >= numStnIdMax ) then
6775        call utl_abort('thn_hyperByLatLonBoxes: numStnId too large')
6776      end if
6777
6778    end do HEADER4
6779
6780    ! loop over local headers to find numObs for each stnId
6781    call obs_set_current_header_list(obsdat,trim(familyType))
6782    HEADER0: do
6783      headerIndex = obs_getHeaderIndex(obsdat)
6784      if (headerIndex < 0) exit HEADER0
6785      if (obs_headElem_i(obsdat, OBS_ITY, headerIndex) /= codtyp) cycle HEADER0
6786
6787      stnId = obs_elem_c(obsdat,'STID',headerIndex)
6788
6789      do stnIdIndex = 1, numStnId
6790        if ( stnidList(stnIdIndex) == stnId ) then
6791          numObsStnId(stnIdIndex) = numObsStnId(stnIdIndex) + 1
6792        end if
6793      end do
6794
6795    end do HEADER0
6796
6797    ! Initial setup
6798    numLat = nint( 2. * real(latLength) / real(deltax) )
6799    numLon = nint(      real(lonLength) / real(deltax) )
6800    allocate(headerIndexKeep(numLat,numLon,tim_nstepobs))
6801    allocate(numChannelsKeep(numLat,numLon,tim_nstepobs))
6802    allocate(distanceKeep(numLat,numLon,tim_nstepobs))
6803    allocate(delMinutesKeep(numLat,numLon,tim_nstepobs))
6804    allocate(gridLats(numLat))
6805    allocate(numGridLons(numLat))
6806    headerIndexKeep(:,:,:) = -1
6807    numChannelsKeep(:,:,:) = 0
6808    distanceKeep(:,:,:)    = 0.0
6809    delMinutesKeep(:,:,:)  = deltmax
6810    gridLats(:)              = 0.0
6811    numGridLons(:)                = 0
6812
6813    write(*,'(a)') ' '
6814    write(*,'(a)') ' == Gridbox properties == '
6815    write(*,'(a)') ' '
6816    write(*,'(a,i8)') ' Number of horizontal boxes : ', numLon
6817    write(*,'(a,i8)') ' Number of vertical boxes   : ', numLat
6818    write(*,'(a,i8)') ' Number of temporal bins    : ', tim_nstepobs
6819
6820    ! print some statistics before thinning
6821
6822    instrumName = codtyp_get_name(codtyp)
6823    write(*,*)
6824    write(*,'(a)')        ' == Input file == '
6825    write(*,*)
6826    write(*,'(a,a4)')     ' Instrument = ', trim(instrumName)
6827    write(*,'(a,i4)')     ' Codtyp     = ', codtyp
6828
6829    write(*,*)
6830    write(*,'(a,2i8)')     ' Number of valid satellite profiles  = ', countHeader, countHeaderMpi
6831    write(*,*)
6832
6833    do stnIdIndex = 1, numStnId
6834      call rpn_comm_allReduce(numObsStnId(stnIdIndex), countMpi, 1, 'mpi_integer', &
6835                              'mpi_sum', 'grid', ierr)
6836      write(*,'(a9,a,2i8)')  stnidList(stnIdIndex), ' :  ', numObsStnId(stnIdIndex), countMpi
6837    end do
6838    
6839    allocate(headerIndexKeepMpi(numLat,numLon,tim_nstepobs,mmpi_nprocs))
6840    allocate(numChannelsKeepMpi(numLat,numLon,tim_nstepobs,mmpi_nprocs))
6841    allocate(distanceKeepMpi(numLat,numLon,tim_nstepobs,mmpi_nprocs))
6842    allocate(delMinutesKeepMpi(numLat,numLon,tim_nstepobs,mmpi_nprocs))
6843    allocate(procIndexKeep(numLat,numLon,tim_nstepobs))
6844    procIndexKeep(:,:,:) = -1
6845
6846    ! set spatial boxes properties
6847    ! gridLats(:) : latitude (deg) of northern side of the box
6848    ! numGridLons(:)   : number of longitudinal boxes at this latitude
6849    do latIndex = 1, numLat
6850      gridLats(latIndex) = (latIndex*180./numLat) - 90.
6851      if ( gridLats(latIndex) <= 0.0 ) then
6852        latInRadians = gridLats(latIndex) * MPC_PI_R8 / 180.
6853      else
6854        latInRadians = gridLats(latIndex-1) * MPC_PI_R8 / 180.
6855      end if
6856      length = lonLength * cos(latInRadians)
6857      numGridLons(latIndex)   = nint(length/deltax)
6858    end do
6859
6860    ! loop over all header indices of the specified family
6861    call obs_set_current_header_list(obsdat,trim(familyType))
6862    HEADER1: do
6863      headerIndex = obs_getHeaderIndex(obsdat)
6864      if (headerIndex < 0) exit HEADER1
6865
6866      if (obs_headElem_i(obsdat, OBS_ITY, headerIndex) /= codtyp) cycle HEADER1
6867
6868      obsLonInRad = obs_headElem_r(obsdat, OBS_LON, headerIndex)
6869      obsLatInRad = obs_headElem_r(obsdat, OBS_LAT, headerIndex)
6870      obsDate = obs_headElem_i(obsdat, OBS_DAT, headerIndex)
6871      obsTime = obs_headElem_i(obsdat, OBS_ETM, headerIndex)
6872
6873      obsLonInDegrees = MPC_DEGREES_PER_RADIAN_R8 * obsLonInRad
6874      obsLatInDegrees = MPC_DEGREES_PER_RADIAN_R8 * obsLatInRad
6875      obsLonBurpFile = nint(100.0*(obsLonInDegrees - 180.0))
6876      if(obsLonBurpFile < 0) obsLonBurpFile = obsLonBurpFile + 36000
6877      obsLatBurpFile = 9000+nint(100.0*obsLatInDegrees)
6878
6879      numChannels = 0
6880
6881      ! loop over all body indices for this headerIndex
6882      call obs_set_current_body_list(obsdat, headerIndex)
6883      BODY: do 
6884        bodyIndex = obs_getBodyIndex(obsdat)
6885        if (bodyIndex < 0) exit BODY
6886
6887        ! mark for rejection if not bias corrected (bit 6 not set)
6888        if (removeUnCorrected) then
6889          obsFlag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
6890          if (.not. btest(obsFlag,6)) then
6891            call obs_bodySet_i(obsdat, OBS_FLG, bodyIndex, ibset(obsFlag,11))
6892          end if
6893        end if
6894
6895        ! count the number of accepted channels
6896        obsFlag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
6897        if ( .not. btest(obsFlag,8) .and. &
6898             .not. btest(obsFlag,9) .and. &
6899             .not. btest(obsFlag,11) ) then
6900          numChannels = numChannels + 1
6901        end if
6902
6903      end do BODY
6904
6905      ! Determine the lat and lon bin indexes
6906      do latIndex = 1, numLat
6907        if ( obsLatInDegrees <= (gridLats(latIndex)+0.000001) ) then
6908          latBinIndex = latIndex
6909          exit
6910        end if
6911      end do
6912      lonBinIndex = int( obsLonBurpFile/(36000.0/numGridLons(latBinIndex)) ) + 1
6913      if ( lonBinIndex > numGridLons(latBinIndex) ) lonBinIndex = numGridLons(latBinIndex)
6914
6915      ! Determine the time bin index
6916      call tim_getStepObsIndex(obsStepIndex_r8, tim_getDatestamp(), &
6917                               obsDate, obsTime, tim_nstepobs)
6918      timeBinIndex = nint(obsStepIndex_r8)
6919      delMinutes = nint(60.0 * tim_dstepobs * abs(real(timeBinIndex) - obsStepIndex_r8))
6920
6921      ! Determine distance from box center
6922      latBoxCenterInDegrees = gridLats(latBinIndex) - 0.5 * (180./numLat)
6923      lonBoxCenterInDegrees = (360. / numGridLons(latBinIndex)) * (lonBinIndex - 0.5)
6924      obsLat = (obsLatBurpFile - 9000.) / 100.
6925      obsLon = obsLonBurpFile / 100.
6926      distance = 1.0d-3 * phf_calcDistance(MPC_RADIANS_PER_DEGREE_R8 * latBoxCenterInDegrees, &
6927                                           MPC_RADIANS_PER_DEGREE_R8 * lonBoxCenterInDegrees, &
6928                                           MPC_RADIANS_PER_DEGREE_R8 * obsLat, &
6929                                           MPC_RADIANS_PER_DEGREE_R8 * obsLon )
6930
6931      ! Apply thinning criteria
6932      keepThisObs = .false.
6933
6934      ! keep if distance to box center smaller than limit and 
6935      ! time under limit fixed at input and maximise number of channels 
6936      if ( (distance < deltrad)                                              .and. &
6937           (numChannels >= numChannelsKeep(latBinIndex,lonBinIndex,timeBinIndex)) .and. &
6938           (delMinutes <= deltmax) ) keepThisObs = .true.
6939     
6940      ! keep the closest to bin central time
6941      if ( numChannels == numChannelsKeep(latBinIndex,lonBinIndex,timeBinIndex) ) then
6942        if ( headerIndexKeep(latBinIndex,lonBinIndex,timeBinIndex) == -1 ) then
6943          keepThisObs = .false.
6944        else
6945          if ( delMinutes > delMinutesKeep(latBinIndex,lonBinIndex,timeBinIndex) ) then
6946            keepThisObs = .false.
6947          end if
6948         
6949          if ( delMinutes == delMinutesKeep(latBinIndex,lonBinIndex,timeBinIndex) ) then
6950            if ( distance > distanceKeep(latBinIndex,lonBinIndex,timeBinIndex) ) then
6951              keepThisObs = .false.
6952            end if
6953          end if
6954
6955        end if
6956      end if
6957
6958      ! save the observation if thinning criteria is satisfied
6959      if ( keepThisObs ) then
6960        headerIndexKeep(latBinIndex,lonBinIndex,timeBinIndex) = headerIndex
6961        distanceKeep(latBinIndex,lonBinIndex,timeBinIndex)    = distance
6962        delMinutesKeep(latBinIndex,lonBinIndex,timeBinIndex)  = delMinutes
6963        numChannelsKeep(latBinIndex,lonBinIndex,timeBinIndex) = numChannels
6964      end if
6965
6966    end do HEADER1
6967
6968    ! communicate results to all other mpi tasks
6969    nsize = numLat * numLon * tim_nstepobs
6970    call rpn_comm_allgather(distanceKeep,     nsize, 'mpi_real4',  &
6971                            distanceKeepMpi,  nsize, 'mpi_real4', 'grid', ierr)
6972    call rpn_comm_allgather(delMinutesKeep,     nsize, 'mpi_integer',  &
6973                            delMinutesKeepMpi,  nsize, 'mpi_integer', 'grid', ierr)
6974    call rpn_comm_allgather(numChannelsKeep,     nsize, 'mpi_integer',  &
6975                            numChannelsKeepMpi,  nsize, 'mpi_integer', 'grid', ierr)
6976    call rpn_comm_allgather(headerIndexKeep,     nsize, 'mpi_integer',  &
6977                            headerIndexKeepMpi,  nsize, 'mpi_integer', 'grid', ierr)
6978
6979    ! reset arrays that store info about kept obs
6980    headerIndexKeep(:,:,:) = -1
6981    numChannelsKeep(:,:,:) = 0
6982    distanceKeep(:,:,:)    = 0.0
6983    delMinutesKeep(:,:,:)  = deltmax
6984
6985    do timeBinIndex = 1, tim_nstepobs
6986      do lonBinIndex = 1, numLon
6987        do latBinIndex = 1, numLat
6988
6989          ! Apply thinning criteria to results from all mpi tasks
6990          do procIndex = 1, mmpi_nprocs
6991
6992            headerIndex = headerIndexKeepMpi(latBinIndex,lonBinIndex,timeBinIndex,procIndex)
6993            distance    = distanceKeepMpi(latBinIndex,lonBinIndex,timeBinIndex,procIndex)
6994            delMinutes  = delMinutesKeepMpi(latBinIndex,lonBinIndex,timeBinIndex,procIndex)
6995            numChannels = numChannelsKeepMpi(latBinIndex,lonBinIndex,timeBinIndex,procIndex)
6996            
6997            keepThisObs = .false.
6998
6999            ! keep if distance to box center smaller than limit and 
7000            ! time under limit fixed at input and maximise number of channels 
7001            if ( (distance < deltrad)                                              .and. &
7002                 (numChannels >= numChannelsKeep(latBinIndex,lonBinIndex,timeBinIndex)) .and. &
7003                 (delMinutes <= deltmax) ) keepThisObs = .true.
7004     
7005            ! keep the closest to bin central time
7006            if ( numChannels == numChannelsKeep(latBinIndex,lonBinIndex,timeBinIndex) ) then
7007              if ( headerIndexKeep(latBinIndex,lonBinIndex,timeBinIndex) == -1 ) then
7008                keepThisObs = .false.
7009              else
7010                if ( delMinutes > delMinutesKeep(latBinIndex,lonBinIndex,timeBinIndex) ) then
7011                  keepThisObs = .false.
7012                end if
7013         
7014                if ( delMinutes == delMinutesKeep(latBinIndex,lonBinIndex,timeBinIndex) ) then
7015                  if ( distance > distanceKeep(latBinIndex,lonBinIndex,timeBinIndex) ) then
7016                    keepThisObs = .false.
7017                  end if
7018                end if
7019
7020              end if
7021            end if
7022
7023            ! save the observation if thinning criteria is satisfied
7024            if ( keepThisObs ) then
7025              procIndexKeep(latBinIndex,lonBinIndex,timeBinIndex)   = procIndex
7026              headerIndexKeep(latBinIndex,lonBinIndex,timeBinIndex) = headerIndex
7027              distanceKeep(latBinIndex,lonBinIndex,timeBinIndex)    = distance
7028              delMinutesKeep(latBinIndex,lonBinIndex,timeBinIndex)  = delMinutes
7029              numChannelsKeep(latBinIndex,lonBinIndex,timeBinIndex) = numChannels
7030            end if
7031
7032          end do ! procIndex
7033
7034        end do
7035      end do
7036    end do
7037
7038    ! determine which headerIndex values are rejected
7039    allocate(rejectThisHeader(obs_numheader(obsdat)))
7040    rejectThisHeader(:) = .true.
7041    do timeBinIndex = 1, tim_nstepobs
7042      do lonBinIndex = 1, numLon
7043        do latBinIndex = 1, numLat
7044          if (procIndexKeep(latBinIndex,lonBinIndex,timeBinIndex) == mmpi_myid+1) then
7045            headerIndex = headerIndexKeep(latBinIndex,lonBinIndex,timeBinIndex)
7046            rejectThisHeader(headerIndex) = .false.
7047          end if
7048        end do
7049      end do
7050    end do
7051
7052    ! modify flags, by setting bit 11 for those rejected, and count obs
7053    countHeader = 0
7054    numObsStnId(:) = 0
7055    call obs_set_current_header_list(obsdat,trim(familyType))
7056    HEADER2: do
7057      headerIndex = obs_getHeaderIndex(obsdat)
7058      if (headerIndex < 0) exit HEADER2
7059
7060      if (obs_headElem_i(obsdat, OBS_ITY, headerIndex) /= codtyp) cycle HEADER2
7061
7062      if (.not. rejectThisHeader(headerIndex)) then
7063
7064        stnid = obs_elem_c(obsdat,'STID',headerIndex)
7065        stnIdIndexFound = -1
7066        do stnIdIndex = 1, numStnId
7067          if ( stnidList(stnIdIndex) == stnid ) stnIdIndexFound = stnIdIndex
7068        end do
7069        if ( stnIdIndexFound == -1 ) then
7070          call utl_abort('thn_hyperByLatLonBoxes: Problem with stnId')
7071        end if
7072        numObsStnId(stnIdIndexFound) = numObsStnId(stnIdIndexFound) + 1
7073        countHeader = countHeader + 1
7074
7075        cycle HEADER2
7076      end if
7077
7078      ! loop over all body indices for this headerIndex
7079      call obs_set_current_body_list(obsdat, headerIndex)
7080      BODY2: do 
7081        bodyIndex = obs_getBodyIndex(obsdat)
7082        if (bodyIndex < 0) exit BODY2
7083
7084        obsFlag = obs_bodyElem_i(obsdat, OBS_FLG, bodyIndex)
7085        call obs_bodySet_i(obsdat, OBS_FLG, bodyIndex, ibset(obsFlag,11))
7086      end do BODY2
7087
7088    end do HEADER2
7089
7090    call rpn_comm_allReduce(countHeader, countHeaderMpi, 1, 'mpi_integer', &
7091                            'mpi_sum', 'grid', ierr)
7092
7093    write(*,*)
7094    write(*,'(a)')        ' == Output file == '
7095
7096    write(*,*)
7097    write(*,'(a,2i8)')     ' Number of valid satellite profiles  = ', countHeader, countHeaderMpi
7098    write(*,*)
7099
7100    do stnIdIndex = 1, numStnId
7101      call rpn_comm_allReduce(numObsStnId(stnIdIndex), countMpi, 1, 'mpi_integer', &
7102                              'mpi_sum', 'grid', ierr)
7103      write(*,'(a9,a,2i8)')  stnidList(stnIdIndex), ' :  ', numObsStnId(stnIdIndex), countMpi
7104    end do
7105    
7106    deallocate(rejectThisHeader)
7107    deallocate(headerIndexKeep)
7108    deallocate(numChannelsKeep)
7109    deallocate(distanceKeep)
7110    deallocate(delMinutesKeep)
7111    deallocate(gridLats)
7112    deallocate(numGridLons)
7113    deallocate(headerIndexKeepMpi)
7114    deallocate(numChannelsKeepMpi)
7115    deallocate(distanceKeepMpi)
7116    deallocate(delMinutesKeepMpi)
7117    deallocate(procIndexKeep)
7118    deallocate(stnIdIntMpi)
7119    deallocate(stnIdInt)
7120
7121    write(*,*)
7122    write(*,*) 'thn_hyperByLatLonBoxes: Finished'
7123    write(*,*)
7124
7125  end subroutine thn_hyperByLatLonBoxes
7126
7127  !--------------------------------------------------------------------------
7128  ! thn_separation
7129  !--------------------------------------------------------------------------
7130  function thn_separation(xlon1, xlat1, xlon2, xlat2)
7131    !
7132    !:Purpose: Compute the separation distance for some thinning algorithms.
7133    !
7134    implicit none
7135
7136    ! Arguments:
7137    real(4), intent(in) :: xlat1
7138    real(4), intent(in) :: xlat2
7139    real(4), intent(in) :: xlon1
7140    real(4), intent(in) :: xlon2
7141    ! Result:
7142    real(4) :: thn_separation
7143
7144    ! Locals:
7145    real(4) :: cosval, degrad, raddeg
7146
7147    raddeg = 180.0 / 3.14159265358979
7148    degrad = 1.0 / raddeg
7149    cosval = sin(xlat1 * degrad) * sin(xlat2 * degrad) + &
7150             cos(xlat1 * degrad) * cos(xlat2 * degrad) * &
7151             cos((xlon1 - xlon2) * degrad)
7152
7153    if (cosval < -1.0d0) then
7154      cosval = -1.0d0
7155    else if (cosval > 1.0d0) then
7156      cosval = 1.0d0
7157    end if
7158    thn_separation = acos(cosval) * raddeg
7159
7160  end function thn_separation
7161
7162  !--------------------------------------------------------------------------
7163  ! thn_thinSatSST
7164  !--------------------------------------------------------------------------
7165  subroutine thn_thinSatSST(obsData)
7166    !
7167    !:Purpose: Main subroutine for thinning of satellite SST obs.
7168    !
7169    implicit none
7170
7171    ! Arguments:
7172    type(struct_obs), intent(inout) :: obsData
7173
7174    ! Locals:
7175    integer :: nulnam
7176    integer :: fnom, fclos, ierr
7177    integer, parameter :: maxNumDataSetSST = 10 ! maximum number of SST datasets considered in surface thinning 
7178    integer :: dataSetSSTIndex, numberDataSetSST
7179
7180    ! Namelist variables:
7181    logical :: doThinning                             ! if false, we return immediately
7182    integer :: numTimesteps                           ! thinning number of timesteps
7183    integer :: deltmax                                ! maximum time difference (in minutes)
7184    real(4) :: fractionGridCell                       ! keep data only inside a fraction of the grid cell size
7185    character(len=10) :: dataSetSST(maxNumDataSetSST) ! array of SST dataset names considered in thinning
7186
7187    namelist /thin_satSST/doThinning, numTimesteps, deltmax, fractionGridCell, dataSetSST
7188    
7189    ! set default values for namelist variables
7190    doThinning        = .false. 
7191    numTimesteps      = 5
7192    deltmax           = 90      
7193    fractionGridCell  = 1.0
7194    dataSetSST(:)     = ''
7195    
7196    ! return if no SST obs
7197    if (.not. obs_famExist(obsData, 'TM')) return
7198
7199    ! Read the namelist for Surface observations (if it exists)
7200    if (utl_isNamelistPresent('thin_satSST', './flnml')) then
7201      nulnam = 0
7202      ierr = fnom(nulnam, './flnml','FTN+SEQ+R/O', 0)
7203      if (ierr /= 0) call utl_abort('thn_thinSatSST: Error opening file flnml')
7204      read(nulnam,nml = thin_satSST, iostat = ierr)
7205      if (ierr /= 0) call utl_abort('thn_thinSatSST: Error reading namelist')
7206      if (mmpi_myid == 0) write(*, nml = thin_satSST)
7207      ierr = fclos(nulnam)
7208    else
7209      write(*,*)
7210      write(*,*) 'thn_thinSatSST: Namelist block thin_satSST is missing in the namelist.'
7211      write(*,*) '                The default value will be taken.'
7212      if (mmpi_myid == 0) write(*, nml = thin_satSST)
7213    end if 
7214       
7215    if (.not. doThinning) return
7216
7217    numberDataSetSST = 0
7218    do dataSetSSTIndex = 1, maxNumDataSetSST
7219      if (trim(dataSetSST(dataSetSSTIndex)) == '') exit
7220      numberDataSetSST = numberDataSetSST + 1
7221    end do
7222
7223    write(*,*)''
7224    if (numberDataSetSST > 0) then
7225      write(*,*) 'thn_thinSurface: satellite SST datasets considered in thinning: '
7226      do dataSetSSTIndex = 1, numberDataSetSST
7227        write(*,'(i5,a)') dataSetSSTIndex, trim(dataSetSST(dataSetSSTIndex))
7228      end do
7229    end if
7230
7231    call utl_tmg_start(114,'--ObsThinning')
7232    do dataSetSSTIndex = 1, numberDataSetSST
7233      call thn_satelliteSSTByGridCell(obsData, dataSetSST(dataSetSSTIndex), &
7234                                      numTimesteps, deltmax, fractionGridCell)
7235    end do
7236    call utl_tmg_stop(114)
7237
7238  end subroutine thn_thinSatSST
7239
7240  !--------------------------------------------------------------------------
7241  ! thn_satelliteSSTByGridCell
7242  !--------------------------------------------------------------------------
7243  subroutine thn_satelliteSSTByGridCell(obsData, dataSet, numTimesteps, deltmax, fractionGridCell)
7244    !
7245    !:Purpose: thinning satellite SST data by grid cells.
7246    !          Set bit 11 of obs_flg on observations that are to be rejected.
7247    !          The algorithm consists in keeping median satellite SST data
7248    !          within one grid cell.
7249    !          The grid is provided in the input file analysisgrid_thinning_satSST.
7250    !
7251    implicit none
7252
7253    ! Arguments:
7254    type(struct_obs), intent(inout) :: obsData          ! obsSpace data object
7255    character(len=*), intent(in)    :: dataSet          ! station ID (id_stn variable in SQLite obs.files)
7256    integer         , intent(in)    :: numTimesteps     ! thinning number of timesteps
7257    integer         , intent(in)    :: deltmax          ! maximum time difference (in minutes)
7258    real(4)         , intent(in)    :: fractionGridCell ! between 0. and 1., keep data only inside a fraction of the grid cell size
7259    
7260    ! Locals:
7261    type(struct_hco), pointer :: hco_thinning
7262    integer :: headerIndexBeg, headerIndexEnd
7263    integer :: ierr, lonIndex, latIndex, stepIndex, codeType
7264    integer :: obsLonIndex, obsLatIndex, obsStepIndex
7265    integer :: numHeader, numHeaderMaxMpi, headerIndex, bodyIndex
7266    integer :: satSSTCount, satSSTCountMpi
7267    integer :: obsFlag, obsVarno, obsDate, obsTime
7268    real(8) :: delMinutes, obsStepIndex_r8
7269    real(8) :: obsLon, obsLat
7270    real(8) :: deltaLon, deltaLat, deltaLatCell, deltaLonCell
7271    real(4) :: obsDistance, sizeGridCell
7272    integer :: medianIndex
7273    logical :: llok
7274    real(4), allocatable :: lonGrid(:), latGrid(:)
7275    logical, allocatable :: valid(:), validMpi(:)
7276    integer, allocatable :: obsLonIndexVec(:), obsLonIndexMpi(:)
7277    integer, allocatable :: obsLatIndexVec(:), obsLatIndexMpi(:) 
7278    integer, allocatable :: obsTimeIndexVec(:), obsTimeIndexMpi(:)
7279    real(4), allocatable :: obsSST(:), obsSSTMpi(:)
7280    type countSatSSTdataType
7281      integer              :: numObs         ! number of data inside each grid cell
7282      real(4), allocatable :: dataVec(:)     ! vector of data inside each grid cell
7283      integer, allocatable :: headerIndex(:) ! header indexes inside each grid cell
7284    end type countSatSSTdataType
7285    type(countSatSSTdataType), allocatable :: dataGrid(:,:) ! for each lon/lat of the grid
7286
7287    write(*,*)
7288    write(*,*) 'thn_satelliteSSTByGridCell: Starting satellite SST data thinning for sensor: ', trim(dataSet)
7289    write(*,*)
7290
7291    numHeader = obs_numHeader(obsData)
7292    call rpn_comm_allReduce(numHeader, numHeaderMaxMpi, 1, 'mpi_integer', 'mpi_max','grid',ierr)
7293
7294    allocate(valid(numHeaderMaxMpi))
7295    valid(:) = .false.
7296
7297    ! count satellite SST data of the current sensor (id_stn)
7298    satSSTCount = 0
7299    do headerIndex = 1, numHeader      
7300      bodyIndex = obs_headElem_i(obsData, obs_rln, headerIndex)
7301      llok = (obs_bodyElem_i(obsData, obs_ass, bodyIndex) == obs_assimilated)
7302      if (.not. llok) cycle
7303      obsVarno  = obs_bodyElem_i(obsData, obs_vnm, bodyIndex)
7304      if (obsVarno /= bufr_sst) cycle
7305      codeType = obs_headElem_i(obsData, obs_ity, headerIndex)
7306      if (codeType /= codtyp_get_codtyp('satob')) cycle
7307
7308      if (trim(obs_elem_c(obsData, 'STID' , headerIndex)) == trim(dataSet)) then
7309        satSSTCount = satSSTCount + 1
7310        valid(headerIndex) = .true.
7311      end if
7312    end do
7313
7314    ! Return if no satellite SST obs to thin
7315    allocate(validMpi(numHeaderMaxMpi * mmpi_nprocs))
7316    validMpi(:) = .false.
7317    call rpn_comm_allgather(valid,    numHeaderMaxMpi, 'mpi_logical',  &
7318                            validMpi, numHeaderMaxMpi, 'mpi_logical', 'grid', ierr)
7319    if (count(validMpi(:)) == 0) then
7320      write(*,*) 'thn_satelliteSSTByGridCell: no ', trim(dataSet), ' SST data present'
7321      return
7322    end if
7323
7324    write(*,*) 'thn_satelliteSSTByGridCell: ', trim(dataSet), ': numHeader, numHeaderMaxMpi: ', &
7325               numHeader, numHeaderMaxMpi
7326
7327    call rpn_comm_allReduce(satSSTCount, satSSTCountMpi, 1, 'mpi_integer', 'mpi_sum','grid', ierr)
7328    write(*,*) 'thn_satelliteSSTByGridCell: ', trim(dataSet),' data: total number of initial data: ', satSSTCountMpi
7329    write(*,*) 'thn_satelliteSSTByGridCell: ', trim(dataSet),' data: number of thinning timesteps: ', numTimesteps
7330
7331    ! Setup horizontal thinning grid
7332    nullify(hco_thinning)
7333    call hco_SetupFromFile(hco_thinning, './analysisgrid_thinning_satSST', 'ANALYSIS', 'Analysis')
7334
7335    ! Setup thinning grid parameters
7336    allocate(lonGrid(hco_thinning%ni))
7337    allocate(latGrid(hco_thinning%nj))
7338    lonGrid(:) = hco_thinning%lon(:) * MPC_DEGREES_PER_RADIAN_R8
7339    latGrid(:) = hco_thinning%lat(:) * MPC_DEGREES_PER_RADIAN_R8
7340    allocate(dataGrid(hco_thinning%nj, hco_thinning%ni))
7341
7342    ! Allocate vectors
7343    allocate(obsLatIndexVec(numHeaderMaxMpi))
7344    allocate(obsLonIndexVec(numHeaderMaxMpi))
7345    allocate(obsTimeIndexVec(numHeaderMaxMpi))
7346    allocate(obsSST(numHeaderMaxMpi))
7347
7348    ! Allocate mpi global vectors
7349    allocate(obsLatIndexMpi(numHeaderMaxMpi * mmpi_nprocs))
7350    allocate(obsLonIndexMpi(numHeaderMaxMpi * mmpi_nprocs))
7351    allocate(obsTimeIndexMpi(numHeaderMaxMpi * mmpi_nprocs))
7352    allocate(obsSSTMpi(numHeaderMaxMpi * mmpi_nprocs))
7353
7354    ! Initialize vectors
7355    obsLatIndexVec(:) = 0
7356    obsLonIndexVec(:) = 0
7357    obsTimeIndexVec(:) = 0
7358    obsSST(:) = 0.
7359
7360    HEADER: do headerIndex = 1, numHeader      
7361      bodyIndex = obs_headElem_i(obsData, obs_rln, headerIndex)
7362      llok = (obs_bodyElem_i(obsData, obs_ass, bodyIndex) == obs_assimilated)
7363      if (.not. llok) cycle HEADER
7364      obsVarno  = obs_bodyElem_i(obsData, obs_vnm, bodyIndex)
7365      if (obsVarno /= bufr_sst) cycle HEADER
7366      codeType = obs_headElem_i(obsData, obs_ity, headerIndex)
7367      if (codeType /= codtyp_get_codtyp('satob')) cycle HEADER
7368      if (trim(obs_elem_c(obsData, 'STID' , headerIndex)) /= trim(dataSet)) cycle HEADER
7369
7370      ! find time difference
7371      obsDate = obs_headElem_i(obsData, obs_dat, headerIndex)
7372      obsTime = obs_headElem_i(obsData, obs_etm, headerIndex)
7373      call tim_getStepObsIndex(obsStepIndex_r8, tim_getDatestamp(), obsDate, obsTime, numTimesteps)
7374      obsStepIndex = nint(obsStepIndex_r8)
7375      
7376      ! reject observations that are outside the assimilation window
7377      if (obsStepIndex < 1 .or. obsStepIndex > numTimesteps) then
7378        valid(headerIndex) = .false.
7379	cycle HEADER
7380      end if	
7381
7382      if (numTimesteps == 1) then
7383        delMinutes = abs(nint(60.0 * tim_windowsize * abs(real(obsStepIndex) - obsStepIndex_r8)))
7384      else
7385        delMinutes = abs(nint(60.0 * tim_windowsize / (numTimesteps - 1) * &
7386	                                              abs(real(obsStepIndex) - obsStepIndex_r8)))
7387      end if
7388
7389      ! check time window
7390      if (delMinutes > deltmax) then
7391        valid(headerIndex) = .false.
7392	cycle HEADER
7393      end if	
7394
7395      obsFlag  = obs_bodyElem_i(obsData, obs_flg, bodyIndex)
7396      obsVarno = obs_bodyElem_i(obsData, obs_vnm, bodyIndex)
7397
7398      if (btest(obsFlag, 9)) cycle HEADER
7399
7400      obsSST(headerIndex) = obs_bodyElem_r(obsData, obs_var, bodyIndex)
7401
7402      ! obs lat and lon in degrees
7403      obsLon = obs_headElem_r(obsData, obs_lon, headerIndex) * MPC_DEGREES_PER_RADIAN_R8
7404      obsLat = obs_headElem_r(obsData, obs_lat, headerIndex) * MPC_DEGREES_PER_RADIAN_R8
7405      ! latitude index
7406      deltaLat = abs(latGrid(1) - obsLat)
7407      obsLatIndex = 1
7408      do latIndex = 2, hco_thinning%nj
7409        if (abs(latGrid(latIndex) - obsLat) < deltaLat) then
7410          deltaLat     = abs(latGrid(latIndex) - obsLat)
7411          deltaLatCell = abs(latGrid(latIndex) - latGrid(latIndex - 1))
7412          obsLatIndex = latIndex
7413        end if
7414      end do
7415
7416      ! longitude index
7417      deltaLon = abs(lonGrid(1) - obsLon)
7418      obsLonIndex = 1
7419      do lonIndex = 2, hco_thinning%ni
7420        if (abs(lonGrid(lonIndex) - obsLon) < deltaLon) then
7421          deltaLon     = abs(lonGrid(lonIndex) - obsLon)
7422          deltaLonCell = abs(lonGrid(lonIndex) - lonGrid(lonIndex - 1))
7423          obsLonIndex = lonIndex
7424        end if
7425      end do
7426
7427      obsLatIndexVec(headerIndex) = obsLatIndex
7428      obsLonIndexVec(headerIndex) = obsLonIndex
7429      obsTimeIndexVec(headerIndex) = obsStepIndex
7430      obsDistance  = sqrt(deltaLon**2 + deltaLat**2)
7431      sizeGridCell = sqrt(deltaLonCell**2 + deltaLatCell**2)
7432      
7433      ! reject data that are farther than the given fraction of the size of grid cell
7434      if (obsDistance > sizeGridCell * fractionGridCell) valid(headerIndex) = .false.
7435
7436    end do HEADER
7437
7438    ! Make all inputs to the following tests mpiglobal
7439    call rpn_comm_allgather(valid          , numHeaderMaxMpi, 'mpi_logical',  &
7440                            validMpi       , numHeaderMaxMpi, 'mpi_logical', 'grid', ierr)
7441    call rpn_comm_allgather(obsLatIndexVec , numHeaderMaxMpi, 'mpi_integer',  &
7442                            obsLatIndexMpi , numHeaderMaxMpi, 'mpi_integer', 'grid', ierr)
7443    call rpn_comm_allgather(obsLonIndexVec , numHeaderMaxMpi, 'mpi_integer',  &
7444                            obsLonIndexMpi , numHeaderMaxMpi, 'mpi_integer', 'grid', ierr)
7445    call rpn_comm_allgather(obsTimeIndexVec, numHeaderMaxMpi, 'mpi_integer',  &
7446                            obsTimeIndexMpi, numHeaderMaxMpi, 'mpi_integer', 'grid', ierr)
7447    call rpn_comm_allgather(obsSST         , numHeaderMaxMpi, 'mpi_real4'  ,  &
7448                            obsSSTMpi      , numHeaderMaxMpi, 'mpi_real4'  , 'grid', ierr)
7449
7450    TIMESTEP: do stepIndex = 1, numTimesteps 
7451
7452      dataGrid(:,:)%numObs = 0
7453      ! Computation of number of data inside each grid cell 
7454      do headerIndex = 1, numHeaderMaxMpi * mmpi_nprocs
7455        if (.not. validMpi(headerIndex)) cycle
7456        if (obsTimeIndexMpi(headerIndex) /= stepIndex) cycle
7457        latIndex = obsLatIndexMpi(headerIndex)
7458        lonIndex = obsLonIndexMpi(headerIndex)
7459        dataGrid(latIndex, lonIndex)%numObs = dataGrid(latIndex, lonIndex)%numObs + 1
7460      end do
7461     
7462      ! Allocation of vector data inside each grid cell
7463      do lonIndex = 1, hco_thinning%ni
7464        do latIndex = 1, hco_thinning%nj
7465          allocate(dataGrid(latIndex, lonIndex)%dataVec(dataGrid(latIndex, lonIndex)%numObs))
7466          allocate(dataGrid(latIndex, lonIndex)%headerIndex(dataGrid(latIndex, lonIndex)%numObs))
7467        end do
7468      end do
7469
7470      ! Fill out vectors of data and header indexes inside each grid cell
7471      dataGrid(:,:)%numObs = 0 ! to reuse it as a counter that should be differentiated for each lat-lon cell
7472      do headerIndex = 1, numHeaderMaxMpi * mmpi_nprocs
7473        if (.not. validMpi(headerIndex)) cycle
7474        if (obsTimeIndexMpi(headerIndex) /= stepIndex) cycle
7475        latIndex = obsLatIndexMpi(headerIndex)
7476        lonIndex = obsLonIndexMpi(headerIndex)
7477        dataGrid(latIndex, lonIndex)%numObs = dataGrid(latIndex, lonIndex)%numObs + 1
7478        dataGrid(latIndex, lonIndex)%dataVec(dataGrid(latIndex, lonIndex)%numObs) = obsSSTMpi(headerIndex)
7479        dataGrid(latIndex, lonIndex)%headerIndex(dataGrid(latIndex, lonIndex)%numObs) = headerIndex       
7480      end do
7481
7482      ! Compute median inside each grid cell and keep only this observation, rejecting all the others 
7483      do lonIndex = 1, hco_thinning%ni
7484        do latIndex = 1, hco_thinning%nj
7485          if(dataGrid(latIndex, lonIndex)%numObs <= 1) cycle
7486          medianIndex = utl_medianIndex(dataGrid(latIndex, lonIndex)%dataVec(:))
7487          validMpi(dataGrid(latIndex, lonIndex)%headerIndex(:)) = .false.
7488          validMpi(dataGrid(latIndex, lonIndex)%headerIndex(medianIndex)) = .true.
7489        end do       
7490      end do
7491     
7492      ! Deallocation of vector data inside each grid cell      
7493      do lonIndex = 1, hco_thinning%ni
7494        do latIndex = 1, hco_thinning%nj
7495          deallocate(dataGrid(latIndex, lonIndex)%dataVec)
7496          deallocate(dataGrid(latIndex, lonIndex)%headerIndex)
7497        end do
7498      end do
7499
7500    end do TIMESTEP
7501
7502    ! Update local copy of valid from global mpi version
7503    headerIndexBeg = 1 + mmpi_myid * numHeaderMaxMpi
7504    headerIndexEnd = headerIndexBeg + numHeaderMaxMpi - 1
7505    valid(:) = validMpi(headerIndexBeg:headerIndexEnd)
7506
7507    deallocate(dataGrid)
7508
7509    write(*,*)
7510    write(*,*) 'thn_satelliteSSTByGridCell: results for ', trim(dataSet), ' data ****************'
7511    write(*,'(a30,i10)') ' Number of obs initially: ', satSSTCountMpi
7512    write(*,'(a30,i10)') ' Number of obs kept     : ', count(validMpi(:))
7513    write(*,'(a30,i10)') ' Number of obs rejected : ', satSSTCountMpi - count(validMpi(:))
7514    write(*,*)
7515
7516    ! Modify the flags for rejected observations
7517    do headerIndex = 1, numHeader
7518      ! skip observation if we're not supposed to consider it
7519      bodyIndex = obs_headElem_i(obsData, obs_rln, headerIndex)
7520      llok = (obs_bodyElem_i(obsData, obs_ass, bodyIndex) == obs_assimilated)
7521      if (.not. llok) cycle
7522      obsVarno  = obs_bodyElem_i(obsData, obs_vnm, bodyIndex)
7523      if (obsVarno /= bufr_sst) cycle
7524      codeType = obs_headElem_i(obsData, obs_ity, headerIndex)
7525      if (codeType /= codtyp_get_codtyp('satob')) cycle
7526      if (trim(obs_elem_c(obsData, 'STID' , headerIndex)) /= trim(dataSet)) cycle
7527
7528      if (.not. valid(headerIndex)) then
7529        obsFlag = obs_bodyElem_i(obsData, obs_flg, bodyIndex)
7530        call obs_bodySet_i(obsData, obs_flg, bodyIndex, ibset(obsFlag, 11))
7531      end if
7532    end do
7533
7534    ! Deallocation
7535    deallocate(valid)
7536    deallocate(validMpi)
7537    deallocate(latGrid)
7538    deallocate(lonGrid)
7539    deallocate(obsTimeIndexVec)
7540    deallocate(obsTimeIndexMpi)
7541    deallocate(obsLatIndexVec)
7542    deallocate(obsLatIndexMpi)
7543    deallocate(obsLonIndexVec)
7544    deallocate(obsLonIndexMpi)
7545    deallocate(obsSST)
7546    deallocate(obsSSTMpi)
7547
7548    write(*,*)
7549    write(*,*) 'thn_satelliteSSTByGridCell: thinning for ', trim(dataSet) ,' data completed.'
7550    write(*,*)
7551
7552  end subroutine thn_satelliteSSTByGridCell
7553
7554end module thinning_mod