obsErrors_mod sourceΒΆ

   1module obsErrors_mod
   2  ! MODULE obsErrors_mod (prefix='oer' category='2. B and R matrices')
   3  !
   4  !:Purpose: Subroutines to set up the observation-error standard deviations.
   5  !
   6  use midasMpi_mod
   7  use mathPhysConstants_mod
   8  use obsSpaceData_mod
   9  use obsSubSpaceData_mod
  10  use tovsNL_mod
  11  use codtyp_mod
  12  use bufr_mod
  13  use utilities_mod
  14  use earthConstants_mod
  15  use gps_mod
  16  use horizontalCoord_mod
  17  use verticalCoord_mod
  18  use columnData_mod
  19  use gridStateVector_mod
  20  use gridStateVectorFileIO_mod
  21  use rmatrix_mod
  22  use varNameList_mod
  23  use obsfiles_mod
  24  use burp_module
  25  use rttov_const, only: surftype_sea
  26  use statetocolumn_mod
  27  implicit none
  28  save
  29  private
  30
  31  ! public procedures
  32  public :: oer_setObsErrors, oer_SETERRGPSGB, oer_SETERRGPSRO, oer_setErrBackScatAnisIce, oer_sw
  33  public :: oer_setInterchanCorr, oer_inflateErrAllsky
  34  
  35  ! public functions
  36  public :: oer_getSSTdataParam_char, oer_getSSTdataParam_int, oer_getSSTdataParam_R8
  37
  38  ! public variables (parameters)
  39  public :: oer_ascatAnisOpenWater, oer_ascatAnisIce
  40  
  41 ! Arrays for QC purpose
  42  public :: oer_toverrst, oer_cldPredThresh, oer_tovutil
  43  public :: oer_errThreshAllsky, oer_useStateDepSigmaObs 
  44  ! TOVS OBS ERRORS
  45  real(8) :: toverrst(tvs_maxChannelNumber,tvs_maxNumberOfSensors)
  46  real(8) :: cldPredThresh(tvs_maxChannelNumber,tvs_maxNumberOfSensors,2)
  47  real(8) :: errThreshAllsky(tvs_maxChannelNumber,tvs_maxNumberOfSensors,2)
  48  integer :: tovutil(tvs_maxChannelNumber,tvs_maxNumberOfSensors)
  49  logical :: useStateDepSigmaObs(tvs_maxChannelNumber,tvs_maxNumberOfSensors)
  50  real(8) :: oer_toverrst(tvs_maxChannelNumber,tvs_maxNumberOfSensors)
  51  real(8) :: oer_cldPredThresh(tvs_maxChannelNumber,tvs_maxNumberOfSensors,2)
  52  real(8) :: oer_errThreshAllsky(tvs_maxChannelNumber,tvs_maxNumberOfSensors,2)
  53  real(8) :: clearCldPredThresh(tvs_maxChannelNumber,tvs_maxNumberOfSensors)
  54  real(8) :: inflateErrAllskyCoeff(tvs_maxNumberOfSensors)
  55  integer :: oer_tovutil(tvs_maxChannelNumber,tvs_maxNumberOfSensors)
  56  logical :: oer_useStateDepSigmaObs(tvs_maxChannelNumber,tvs_maxNumberOfSensors)
  57
  58  ! SST data 
  59  type SSTdataParamsType
  60    character(len=20) :: dataType   = '' ! type of data: insitu, satellite, pseudo
  61    character(len=20) :: instrument = '' ! instrument: drifts, bouys, ships, AVHRR, VIIRS, AMSR2
  62    character(len=20) :: sensor     = '' ! sensor of satellite data: NOAA19, NOAA20,...
  63    character(len=20) :: sensorType = '' ! type of satellite data sensors: infrared, microwave,..
  64    integer           :: codeType   = MPC_missingValue_INT ! data codtype
  65    real(8)           :: dayError   = MPC_missingValue_R8  ! data error for daytime 
  66    real(8)           :: nightError = MPC_missingValue_R8  ! data error for nighttime
  67  end type SSTdataParamsType
  68  integer, parameter :: maxNumberSSTDatasets = 15
  69
  70  ! SST namelist variables
  71  integer :: numberSSTDatasets = MPC_missingValue_INT            ! MUST NOT BE INCLUDED IN NAMELIST!
  72  type(SSTdataParamsType) :: SSTdataParams(maxNumberSSTDatasets) ! list of SSTdataParamsType defining SST obs errors
  73
  74  ! CONVENTIONAL OBS ERRORS
  75  real(8) :: xstd_ua_ai_sw(20,11)
  76  real(8) :: xstd_sf(9,6)
  77  real(8) :: xstd_pr(2)
  78  real(8) :: xstd_sc(1)
  79  real(8) :: LVLVALUE(9), HGT_ERR(200,9)
  80
  81  ! CONSTITUENT/CHEMISTRY OBS ERROR STD DEV
  82  ! Associated below to routines beginning with the prefix 'chm'
  83  type :: struct_chm_std
  84     !
  85     ! Structure containing information retrieved from auxiliary obs data file 
  86     ! holding observation std dev information for constituent obs
  87     !
  88     !  Variable               Description
  89     !  --------               -----------
  90     !  n_stnid                Number of sub-families (identified via STNIDs)
  91     !  stnids                 Sub-families (STNIDs; * are wild cards)
  92     !  element                BUFR element in data block 
  93     !  source                 0: Set entirely from the auxiliary file being read. No 
  94     !                            initial values read from observation files
  95     !                         1: Initial values in observation files 
  96     !                            (may be adjusted after input)
  97     !                         2: Initial values in observation files for variable number
  98     !                            of vertical levels (for error std deviations only)
  99     !  std_type               Index of setup approach (used in combination with source)
 100     !                         For source value 0 or 1, 
 101     !                         0: std1 or observation file values (sigma)
 102     !                         1: max(std3,std2*ZVAL)  if source=0
 103     !                            max(std3,std2*sigma) otherwise
 104     !                         2: sqrt(std3**2+(std2*ZVAL)**2))  if source=0
 105     !                            sqrt(std3**2+(std2*sigma)**2)) otherwise
 106     !                         3: min(std3,max(std2,std1_chm*ZVAL)) if source=0
 107     !                            min(std3,max(std2,std1_chm*sigma))  otherwise
 108     !                         4: sqrt(std2**2+(std1*ZVAL)**2))  if source=0 
 109     !                            sqrt(std2**2+(std1*sigma)**2)) otherwise
 110     !  ibegin                 Position index of start of data for given
 111     !                         sub-family in the arrays std1,levels,lat
 112     !  n_lvl                  Number of vertical levels (max number when source=2)
 113     !  levels                 Vertical levels (in coordinate of sub-family data)
 114     !  n_lat                  Number of latitudes
 115     !  lat                    Latitudes (degrees; ordered in increasing size)
 116     !  std1                   See std_type for usage (dependent on vertical level)
 117     !  std2                   See std_type for usage (independent of vertical level)
 118     !  std3                   See std_type for usage (independent of vertical level)
 119
 120     integer ::  n_stnid
 121     character(len=12), allocatable :: stnids(:)
 122     integer, allocatable :: element(:),std_type(:),n_lat(:)
 123     integer, allocatable :: source(:),ibegin(:),n_lvl(:)
 124     real(8), allocatable :: std1(:),std2(:),std3(:)
 125     real(8), allocatable :: levels(:),lat(:)
 126
 127     ! Array to hold std dev's read from auxiliary obs data/info file
 128     type(struct_oss_obsdata), allocatable :: obsStdDev(:)
 129     
 130  end type struct_chm_std
 131
 132  type(struct_chm_std)  :: chm_std
 133
 134  ! Sea Ice Concentration obs-error standard deviation
 135  real(8) :: xstd_sic(9)
 136  ! tiepoint standard deviation for ASCAT backscatter anisotropy
 137  integer, parameter :: ncells = 21
 138  real(8) :: ascatAnisSigmaOpenWater(ncells,12), ascatAnisSigmaIce(ncells,12)
 139  real    :: oer_ascatAnisOpenWater(ncells,12), oer_ascatAnisIce(ncells,12)
 140
 141  ! Hydrology
 142  real(8) :: xstd_hydro(1)
 143
 144  integer :: n_sat_type, n_categorie
 145  integer :: tbl_m(200), tbl_h(200), tbl_t(200), tbl_g(200)
 146  integer :: surfaceObsTypeNumber
 147
 148  character(len=9) :: SAT_AMV(200,10), SAT_LIST(200), MET_LIST(200)
 149  character(len=9) :: HTM_LIST(200), TMG_LIST(200), NSW_LIST(200)
 150
 151  logical :: useTovsUtil
 152  character(len=48) :: obserrorMode
 153
 154  ! Namelist variables:
 155  logical :: new_oer_sw                 ! use the 'new' method to compute errors for AMV observations
 156  logical :: obsfile_oer_sw             ! choose to read errors for AMV from the obs files
 157  logical :: visAndGustAdded            ! choose to read visibility and gust errors in addition to other conv variables
 158  logical :: mwAllskyTtInflateByOmp     ! choose to inflate all sky TT radiance errors by an amount related to O-P
 159  logical :: mwAllskyTtInflateByClwDiff ! choose to inflate all sky TT radiance errors by an amount related to cloud O-P
 160  logical :: mwAllskyHuInflateByOmp     ! choose to inflate all sky HU radiance errors by an amount related to O-P
 161  logical :: mwAllskyHuInflateBySiDiff  ! choose to inflate all sky HU radiance errors by an amount related to cloud O-P
 162  real(8) :: amsuaClearCldPredThresh(5) ! cloud threshold for considering obs as clear sky
 163  real(8) :: amsuaInflateErrAllskyCoeff ! state dependent obs error inflation factor
 164  logical :: readOldSymmetricObsErrFile ! choose to read 'old style' obs error file, when only AMSU-A was all sky
 165
 166contains
 167
 168  !--------------------------------------------------------------------------
 169  ! oer_setInterchanCorr
 170  !--------------------------------------------------------------------------
 171  subroutine oer_setInterchanCorr()
 172    !
 173    !:Purpose: Setup of interchannel observation errors correlations
 174    !    
 175    use rmatrix_mod
 176    IMPLICIT NONE
 177
 178    ! Locals:
 179    INTEGER ::  ISENS
 180
 181    if (tvs_nsensors == 0) then
 182      write(*,*) 'oer_setInterchanCorr: tvs_nsensors is zero, skipping setup'
 183      return
 184    end if
 185
 186! Initialization of the correlation matrices
 187    call rmat_init(tvs_nsensors,tvs_nobtov)
 188    if (rmat_lnondiagr) then
 189      do isens = 1, tvs_nsensors
 190        if (tvs_isReallyPresent(isens)) call rmat_readCMatrix(tvs_listSensors(:,isens), isens, tvs_ichan(1:tvs_nchan(isens),isens))
 191      end do
 192    end if
 193
 194  END SUBROUTINE oer_setInterchanCorr
 195
 196  !--------------------------------------------------------------------------
 197  ! oer_setObsErrors
 198  !--------------------------------------------------------------------------
 199  subroutine oer_setObsErrors(obsSpaceData, obserrorMode_in, useTovsUtil_opt)
 200    !
 201    !:Purpose: read and set observation errors (from former sucovo subroutine).
 202    !
 203    type(struct_obs)             :: obsSpaceData
 204    character(len=*), intent(in) :: obserrorMode_in
 205    logical, optional            :: useTovsUtil_opt
 206
 207    namelist /namoer/ new_oer_sw, obsfile_oer_sw, visAndGustAdded
 208    namelist /namoer/ mwAllskyTtInflateByOmp, mwAllskyTtInflateByClwDiff
 209    namelist /namoer/ mwAllskyHuInflateByOmp, mwAllskyHuInflateBySiDiff
 210    namelist /namoer/ amsuaClearCldPredThresh
 211    namelist /namoer/ amsuaInflateErrAllskyCoeff
 212    namelist /namoer/ readOldSymmetricObsErrFile
 213    integer :: fnom, fclos, nulnam, ierr
 214
 215    !
 216    !- 1.  Setup Mode
 217    !
 218    obserrorMode = obserrorMode_in
 219
 220    ! Additional key to allow the use of 'util' column in stats_tovs file
 221    if (present(useTovsUtil_opt)) then
 222      useTovsUtil = useTovsUtil_opt
 223    else
 224      useTovsUtil = .false.
 225    end if
 226
 227    ! read namelist namoer
 228    new_oer_sw = .false.
 229    obsfile_oer_sw  = .false.
 230    visAndGustAdded = .false.
 231    mwAllskyTtInflateByOmp = .false.
 232    mwAllskyTtInflateByClwDiff = .false.
 233    mwAllskyHuInflateByOmp = .false.
 234    mwAllskyHuInflateBySiDiff = .false.
 235    amsuaClearCldPredThresh(:) = 0.03D0
 236    amsuaClearCldPredThresh(1) = 0.05D0
 237    amsuaClearCldPredThresh(4) = 0.02D0
 238    amsuaInflateErrAllskyCoeff = 13.0D0
 239    readOldSymmetricObsErrFile = .true.
 240
 241    if (utl_isNamelistPresent('namoer','./flnml')) then
 242      nulnam = 0
 243      ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
 244      read (nulnam, nml = NAMOER, iostat = ierr)
 245      if (ierr /= 0) call utl_abort('oer_setObsErrors: Error reading namelist')
 246      if (mmpi_myid == 0) write(*,nml=namoer)
 247      ierr = fclos(nulnam)
 248    else
 249      write(*,*)
 250      write(*,*) 'oer_setObsErrors: namoer is missing in the namelist. The default value will be taken.'
 251    end if
 252
 253    !
 254    !- 2.  Read in the observation std dev errors
 255    !
 256
 257    !- 2.1 Radiance data
 258    if (obs_famexist(obsSpaceData,'TO')) then
 259      call oer_readObsErrorsTOVS
 260    else 
 261      write(*,*) "oer_setObsErrors: No brightness temperature observations found."
 262    end if
 263    
 264    !- 2.2 Conventional data
 265    if (obs_famExist(obsSpaceData, 'UA') .or. obs_famExist(obsSpaceData, 'AI') .or. obs_famExist(obsSpaceData, 'SW') .or. &
 266        obs_famExist(obsSpaceData, 'SF') .or. obs_famExist(obsSpaceData, 'GP') .or. obs_famExist(obsSpaceData, 'SC') .or. &
 267        obs_famExist(obsSpaceData, 'PR')) then
 268
 269      call oer_readObsErrorsCONV()
 270
 271    else
 272
 273      write(*,*) "oer_setObsErrors: No conventional weather observations found."
 274
 275    end if
 276
 277    !- 2.3 Constituent data
 278    if (obs_famexist(obsSpaceData,'CH')) then
 279      call chm_read_obs_err_stddev
 280    else
 281      write(*,*) "oer_setObsErrors: No CH observations found."
 282    end if
 283
 284    !- 2.4 Sea ice concentration
 285    if (obs_famexist(obsSpaceData,'GL')) then
 286      call oer_readObsErrorsIce
 287    else
 288      write(*,*) "oer_setObsErrors: No GL observations found."
 289    end if
 290
 291    !- 2.5 SST
 292    if (obs_famexist(obsSpaceData,'TM')) then    
 293      call oer_readObsErrorsSST
 294    else
 295      write(*,*) "oer_setObsErrors: No TM observations found."
 296    end if
 297
 298    !- 2.6 Hydrology
 299    if (obs_famexist(obsSpaceData,'HY')) then
 300      call oer_readObsErrorsHydro
 301    else
 302      write(*,*) "oer_setObsErrors: No HY observations found."
 303    end if
 304
 305    !
 306    !- 3.  Set obs error information in obsSpaceData object
 307    !
 308    call oer_fillObsErrors(obsSpaceData)
 309
 310    !
 311    !- 4.  Deallocate temporary storage
 312    !
 313    if (obs_famExist(obsSpaceData,'CH')) call chm_dealloc_obs_err_stddev
 314
 315  end subroutine oer_setObsErrors
 316
 317  !--------------------------------------------------------------------------
 318  ! oer_readObsErrorsTOVS
 319  !--------------------------------------------------------------------------
 320  subroutine oer_readObsErrorsTOVS
 321    !
 322    !:Purpose: Read the observation error statistics and
 323    !           utilization flag for TOVS processing. This information
 324    !           resides on an ASCII file and is read using a free format.
 325    !
 326    implicit none
 327
 328    ! Locals:
 329    integer, parameter :: bgckColumnIndex = 1
 330    integer, parameter :: analysisColumnIndex = 2
 331    integer,external  :: FNOM, FCLOS
 332    integer :: IER, ILUTOV, ILUTOV2, JI, obsErrorColumnIndex, JL, JM 
 333    integer :: INUMSAT, INUMSAT2, ISAT, IPLF
 334    integer :: IPLATFORM(tvs_maxNumberOfSensors), ISATID(tvs_maxNumberOfSensors)
 335    integer :: IINSTRUMENT(tvs_maxNumberOfSensors), NUMCHN(tvs_maxNumberOfSensors)
 336    integer :: NUMCHNIN(tvs_maxNumberOfSensors)
 337    integer :: IPLATFORM2, ISATID2, IINSTRUMENT2, NUMCHNIN2
 338    integer :: IUTILST(tvs_maxChannelNumber,tvs_maxNumberOfSensors)
 339    integer :: ICHN(tvs_maxChannelNumber,tvs_maxNumberOfSensors)
 340    integer :: ICHNIN(tvs_maxChannelNumber,tvs_maxNumberOfSensors)
 341    integer :: ICHNIN2(tvs_maxChannelNumber)
 342    real(8) :: TOVERRIN(tvs_maxChannelNumber,2,tvs_maxNumberOfSensors)
 343    real(8) :: cldPredThreshInput(tvs_maxChannelNumber,tvs_maxNumberOfSensors,2)
 344    real(8) :: errThreshAllskyInput(tvs_maxChannelNumber,tvs_maxNumberOfSensors,2)
 345    real(8) :: tovsObsInflation(tvs_maxChannelNumber,tvs_maxNumberOfSensors)
 346    real(8) :: clearCldPredThreshInput(tvs_maxChannelNumber,tvs_maxNumberOfSensors)
 347    real(8) :: inflateErrAllskyCoeffInput(tvs_maxNumberOfSensors)
 348    integer :: useStateDepSigmaObsInput(tvs_maxChannelNumber,tvs_maxNumberOfSensors)
 349    integer :: amsuaChannelOffset, amsuaChannelNum  
 350    character (len=132) :: CLDUM,CPLATF,CINSTR
 351
 352    write(*,*) 'oer_readObsErrorsTOVS: reading observation error statistics required for TOVS processing'
 353
 354    !
 355    !    1. Initialize
 356    !       ----------
 357    !
 358    TOVERRST(:,:) = 0.0D0
 359    TOVERRIN(:,:,:) = 0.0D0
 360    cldPredThresh(:,:,:) = 0.0d0
 361    cldPredThreshInput(:,:,:) = 0.0d0
 362    errThreshAllsky(:,:,:) = 0.0d0
 363    errThreshAllskyInput(:,:,:) = 0.0d0
 364    tovsObsInflation(:,:) = 0.0d0
 365    IUTILST(:,:) = 0
 366    useStateDepSigmaObs(:,:) = .false.
 367    useStateDepSigmaObsInput(:,:) = 0
 368    clearCldPredThresh(:,:) = 0.0d0
 369    clearCldPredThreshInput(:,:) = 0.0d0
 370    inflateErrAllskyCoeff(:) = 0.0d0
 371    inflateErrAllskyCoeffInput(:) = 0.0d0
 372
 373    IPLATFORM(:) = 0
 374    NUMCHN(:) = 0
 375    NUMCHNIN(:) = 0
 376    ICHN(:,:) = 0
 377    ICHNIN(:,:) = 0
 378    ICHNIN2(:) = 0
 379
 380    if (tvs_nobtov == 0) return
 381
 382    !
 383    !     2. Open the file
 384    !        -------------
 385    !
 386    ilutov = 0
 387    IER =  FNOM(ILUTOV,'stats_tovs','SEQ+FMT',0)
 388    if (IER < 0) call utl_abort ('oer_readObsErrorsTOVS: Problem opening file stats_tovs')
 389
 390    !
 391    !     3. Read number of satellites
 392    !        -------------------------
 393    !
 394    read(ILUTOV,*)
 395    read(ILUTOV,*) INUMSAT
 396    read(ILUTOV,*)
 397
 398    if (inumsat > tvs_maxNumberOfSensors) then
 399      write(*,'(A)') ' Number of sensors in stats_tovs file is greater than maximum allowed (tvs_maxNumberOfSensors)'
 400      call utl_abort('oer_readObsErrorsTOVS')
 401    end if
 402
 403    !
 404    !     4. Read the satellite identification, the number of channels,
 405    !        the observation errors and the utilization flags
 406    !        ----------------------------------------------------------
 407    !
 408    write(*,*) 'oer_readObsErrorsTOVS: Reading stats_tovs file.'
 409
 410    DO JL = 1, INUMSAT
 411
 412      read(ILUTOV,*)
 413      read(ILUTOV,'(A)') CLDUM
 414      write(*,'(A)') CLDUM
 415      CINSTR=CLDUM
 416      call split(CINSTR," ",CPLATF)
 417      write(*,*) "CINSTR: ",CINSTR
 418      write(*,*) "CPLATF: ",CPLATF
 419      read(ILUTOV,*)
 420      read(ILUTOV,*) ISATID(JL), NUMCHNIN(JL)
 421
 422      do JI = 1, 3
 423        read(ILUTOV,*)
 424      end do
 425
 426      if (CPLATF == 'FY-3C') THEN 
 427         CPLATF = 'FY3-3'
 428         CINSTR = 'MWHS2'
 429      end if
 430
 431      IPLATFORM(JL) =  tvs_getPlatformId(CPLATF)
 432
 433      if (IPLATFORM(JL) == -1) call utl_abort ('oer_readObsErrorsTOVS: Unknown platform!')
 434
 435      IINSTRUMENT(JL) = tvs_getInstrumentId(CINSTR)
 436      
 437      if (IINSTRUMENT(JL) == -1) call utl_abort ('oer_readObsErrorsTOVS: Unknown instrument!')
 438
 439      do JI = 1, NUMCHNIN(JL)
 440        read(ILUTOV,*) ICHNIN(JI,JL), TOVERRIN(ICHNIN(JI,JL),1,JL), TOVERRIN(ICHNIN(JI,JL),2,JL), IUTILST(ICHNIN(JI,JL),JL), tovsObsInflation(ICHNIN(JI,JL),JL)
 441      end do
 442      read(ILUTOV,*)
 443
 444    end do
 445
 446    ! read in the parameters to define the user-defined symmetric TOVS errors
 447    if (tvs_mwAllskyAssim) then
 448      ilutov2 = 10
 449      IER =  FNOM(ILUTOV2,'stats_tovs_symmetricObsErr','SEQ+FMT',0)
 450      if (IER < 0) call utl_abort ('oer_readObsErrorsTOVS: Problem opening symmetricObsErr file.')
 451
 452      read(ILUTOV2,*)
 453      read(ILUTOV2,*) INUMSAT2
 454      read(ILUTOV2,*)
 455
 456      write(*,*) 'oer_readObsErrorsTOVS: Reading symmetricObsErr file.'
 457
 458      DO JL = 1, INUMSAT2
 459
 460        read(ILUTOV2,*)
 461        read(ILUTOV2,'(A)') CLDUM
 462        write(*,'(A)') CLDUM
 463        CINSTR = CLDUM
 464        call split(CINSTR," ",CPLATF)
 465        write(*,*) "CINSTR: ",CINSTR
 466        write(*,*) "CPLATF: ",CPLATF
 467        read(ILUTOV2,*)
 468
 469        ! If reading the old style stats_tovs_symmetricObsErr, the the all-sky parameters are available only for AMSUA.
 470        if (readOldSymmetricObsErrFile) then
 471          read(ILUTOV2,*) ISATID2, NUMCHNIN2
 472          if (CINSTR == "AMSUA") inflateErrAllskyCoeffInput(JL) = amsuaInflateErrAllskyCoeff
 473        else
 474          read(ILUTOV2,*) ISATID2, NUMCHNIN2, inflateErrAllskyCoeffInput(JL)
 475        end if
 476
 477        if (ISATID2 /= ISATID(JL) .or. NUMCHNIN2 /= NUMCHNIN(JL)) &
 478          call utl_abort ('oer_readObsErrorsTOVS: problem with ISATID2, NUMCHNIN2 in symmetricObsErr')
 479
 480        do JI = 1, 3
 481          read(ILUTOV2,*)
 482        end do
 483
 484        IPLATFORM2 = tvs_getPlatformId(CPLATF)
 485        IINSTRUMENT2 = tvs_getInstrumentId(CINSTR)
 486        if (IPLATFORM2 /= IPLATFORM(JL) .or. IINSTRUMENT2 /= IINSTRUMENT(JL)) & 
 487          call utl_abort ('oer_readObsErrorsTOVS: problem with IPLATFORM2, IINSTRUMENT2 in symmetricObsErr')
 488
 489        do JI = 1, NUMCHNIN2
 490          ! If reading the old style stats_tovs_symmetricObsErr, then the all-sky parameters are available only for AMSUA.
 491          if (readOldSymmetricObsErrFile) then
 492            read(ILUTOV2,*) ICHNIN2(JI), &
 493                  cldPredThreshInput(ICHNIN2(JI),JL,1), cldPredThreshInput(ICHNIN2(JI),JL,2), &
 494                  errThreshAllskyInput(ICHNIN2(JI),JL,1), errThreshAllskyInput(ICHNIN2(JI),JL,2), &
 495                  useStateDepSigmaObsInput(ICHNIN2(JI),JL)
 496            if (CINSTR == "AMSUA") then
 497              amsuaChannelOffset = 27
 498              amsuaChannelNum = ICHNIN2(JI) - amsuaChannelOffset
 499              if (amsuaChannelNum >= 1 .and.  amsuaChannelNum <= 5) then
 500                clearCldPredThreshInput(ICHNIN2(JI),JL) = amsuaClearCldPredThresh(amsuaChannelNum)
 501              end if
 502            end if
 503          else
 504            read(ILUTOV2,*) ICHNIN2(JI), &
 505                  cldPredThreshInput(ICHNIN2(JI),JL,1), cldPredThreshInput(ICHNIN2(JI),JL,2), &
 506                  errThreshAllskyInput(ICHNIN2(JI),JL,1), errThreshAllskyInput(ICHNIN2(JI),JL,2), &
 507                  clearCldPredThreshInput(ICHNIN2(JI),JL), &
 508                  useStateDepSigmaObsInput(ICHNIN2(JI),JL)
 509          end if
 510
 511          if (ICHNIN2(JI) /= ICHNIN(JI,JL)) & 
 512            call utl_abort ('oer_readObsErrorsTOVS: problem with ICHNIN2 in symmetricObsErr')
 513
 514        end do
 515        read(ILUTOV2,*)
 516
 517      end do
 518
 519      IER = FCLOS(ILUTOV2)
 520      if (IER /= 0) call utl_abort ('oer_readObsErrorsTOVS')
 521
 522    end if
 523
 524    !
 525    !   Select input error to use: if ANAL mode, use ERRANAL (obsErrorColumnIndex=2);
 526    !   otherwise use ERRBGCK (obsErrorColumnIndex=1)
 527    !
 528    if (trim(obserrorMode) == 'analysis' .or. trim(obserrorMode) == 'FSO') THEN
 529      obsErrorColumnIndex = analysisColumnIndex
 530    ELSE
 531      obsErrorColumnIndex = bgckColumnIndex
 532    end if
 533
 534    !
 535    !   Fill the observation error array TOVERRST
 536    !
 537    write(*,*) 'oer_readObsErrorsTOVS: Fill error array TOVERRST.'
 538    do JM = 1, INUMSAT
 539      do JL = 1, tvs_nsensors
 540        if (tvs_platforms (JL) == IPLATFORM(JM) .AND. tvs_satellites(JL) == ISATID(JM)) THEN
 541          if (tvs_instruments (JL) == IINSTRUMENT(JM)) THEN
 542            NUMCHN(JL)=NUMCHNIN(JM)
 543            do JI = 1, tvs_maxChannelNumber
 544              TOVERRST(JI,JL) = TOVERRIN(JI,obsErrorColumnIndex,JM)
 545              ICHN(JI,JL) = ICHNIN(JI,JM)
 546
 547              if (tvs_mwAllskyAssim) then
 548                cldPredThresh(JI,JL,:) = cldPredThreshInput(JI,JM,:)
 549                errThreshAllsky(JI,JL,:) = errThreshAllskyInput(JI,JM,:)
 550                clearCldPredThresh(JI,JL) = clearCldPredThreshInput(JI,JM)
 551                useStateDepSigmaObs(JI,JL) = (useStateDepSigmaObsInput(JI,JM) == 1)
 552
 553                if (JI == 1) inflateErrAllskyCoeff(JL) = inflateErrAllskyCoeffInput(JM)
 554
 555                ! inflate the errThreshAllsky in analysis mode
 556                if (obsErrorColumnIndex == analysisColumnIndex) then
 557                  errThreshAllsky(JI,JL,1) = errThreshAllsky(JI,JL,1) * tovsObsInflation(JI,JM)
 558                  errThreshAllsky(JI,JL,2) = errThreshAllsky(JI,JL,2) * tovsObsInflation(JI,JM)
 559                end if
 560              end if
 561
 562            end do
 563            if (trim(obserrorMode) == 'bgck' .or. useTovsUtil) THEN
 564              do JI = 1, tvs_maxChannelNumber
 565                ! channel Selection using array IUTILST(chan,sat):
 566                !   0 (blacklisted)
 567                !   1 (assmilate)
 568                !   2 (assimilate over open water only)
 569                tovutil(JI,JL) =  IUTILST(JI,JM)
 570              end do
 571            end if
 572          end if
 573        end if
 574      end do
 575    end do
 576
 577    !
 578    !  Check that oberservation error statistics have been defined for
 579    !  all the satellites specified in the namelist.
 580    !
 581    do JL = 1, tvs_nsensors
 582      IPLF = utl_findloc(IPLATFORM(:) , tvs_platforms(JL))
 583      ISAT =  utl_findloc(ISATID(:), tvs_satellites(JL))
 584      if (IPLF == 0 .OR. ISAT == 0) THEN
 585        write(*,'(A,I3)') 'oer_readObsErrorsTOVS: Observation errors not defined for sensor #', JL
 586        call utl_abort ('oer_readObsErrorsTOVS')
 587      end if
 588      if (NUMCHN(JL) == 0) THEN 
 589        write(*,'(A,I3)') 'oer_readObsErrorsTOVS: Problem setting errors for sensor #', JL
 590        call utl_abort ('oer_readObsErrorsTOVS')
 591      end if
 592    end do
 593
 594    !
 595    !    5. Print out observation errors for each sensor
 596    !       --------------------------------------------
 597    !
 598    if (mmpi_myid == 0) THEN
 599      write(*,*) 'Radiance observation errors read from file'
 600      write(*,*) '------------------------------------------'
 601      do JL = 1, tvs_nsensors
 602        write(*,'(A,I2,4(A))') 'SENSOR #', JL, ', Platform: ', tvs_satelliteName(JL), &
 603                                ', Instrument: ',tvs_instrumentName(JL)
 604        if (tvs_mwAllskyAssim .and. any(useStateDepSigmaObs(ICHN(1:NUMCHN(JL),JL),JL))) then
 605          write(*,'(A,4(2X,A8),(1X,A9),(2X,A3))') 'Channel','cld1','cld2','sigmaO1','sigmaO2','anlErrInf','use'
 606          do JI = 1, NUMCHN(JL)
 607            write(*,'(I7,5(2X,F8.4),(2X,L3))') ICHN(JI,JL), &
 608              cldPredThresh(ICHN(JI,JL),JL,1), cldPredThresh(ICHN(JI,JL),JL,2), &
 609              errThreshAllsky(ICHN(JI,JL),JL,1), errThreshAllsky(ICHN(JI,JL),JL,2), &
 610              clearCldPredThresh(ICHN(JI,JL),JL), &
 611              useStateDepSigmaObs(ICHN(JI,JL),JL)
 612          end do
 613          write(*,'(A,(2X,F8.4))') 'inflateErrAllskyCoeff=', &
 614                inflateErrAllskyCoeff(JL) 
 615        else
 616          write(*,'(A,2X,A8)') 'Channel','sigmaO'
 617          do JI = 1, NUMCHN(JL)
 618            write(*,'(I7,1(2X,F8.4))') ICHN(JI,JL),TOVERRST(ICHN(JI,JL),JL)
 619          end do
 620        end if
 621      end do
 622    end if
 623
 624    !
 625    !    6. Close the file
 626    !       --------------
 627    !
 628    IER = FCLOS(ILUTOV)
 629    if (IER /= 0) call utl_abort ('oer_readObsErrorsTOVS')
 630
 631    !
 632    !    7. Fill the publics variables for QC purpose
 633    !       --------------
 634    oer_toverrst(:,:) = toverrst(:,:)
 635    oer_tovutil (:,:) = tovutil(:,:)
 636    oer_cldPredThresh(:,:,:) = cldPredThresh(:,:,:)
 637    oer_errThreshAllsky(:,:,:) = errThreshAllsky(:,:,:)
 638    oer_useStateDepSigmaObs(:,:) = useStateDepSigmaObs(:,:)
 639
 640  contains
 641
 642    !--------------------------------------------------------------------------
 643    ! compact
 644    !--------------------------------------------------------------------------
 645    subroutine compact(str)
 646      ! Code from Benthien's module: http://www.gbenthien.net/strings/index.html
 647      ! Converts multiple spaces and tabs to single spaces; deletes control characters;
 648      ! removes initial spaces.
 649      implicit none
 650
 651      ! Arguments:
 652      character(len=*), intent(inout) :: str
 653
 654      ! Locals:
 655      character(len=1):: ch
 656      character(len=len_trim(str)):: outstr
 657      integer isp,k,lenstr,i,ich
 658
 659      str=adjustl(str)
 660      lenstr=len_trim(str)
 661      outstr=' '
 662      isp=0
 663      k=0
 664
 665      do i=1,lenstr
 666        ch=str(i:i)
 667        ich=iachar(ch)
 668
 669        select case(ich)
 670        case(9,32)    ! space or tab character         
 671          if(isp==0) then
 672            k=k+1
 673            outstr(k:k)=' '
 674          end if
 675          isp=1
 676        case(33:)              ! not a space, quote, or control character
 677          k=k+1
 678          outstr(k:k)=ch
 679          isp=0
 680        end select
 681
 682      end do
 683
 684      str=adjustl(outstr)
 685
 686    end subroutine compact
 687    
 688    !--------------------------------------------------------------------------
 689    ! split
 690    !--------------------------------------------------------------------------
 691    subroutine split(str, delims, before)
 692      !
 693      !:Comment:
 694      ! Code extracted from Benthien's module: http://www.gbenthien.net/strings/index.html
 695      ! Routine finds the first instance of a character from 'delims' in the
 696      ! the string 'str'. The characters before the found delimiter are
 697      ! output in 'before'. The characters after the found delimiter are
 698      ! output in 'str'. 
 699      !
 700      implicit none
 701
 702      ! Arguments:
 703      character(len=*), intent(inout) :: str
 704      character(len=*), intent(in)    :: delims
 705      character(len=*), intent(out)   :: before
 706
 707      ! Locals:
 708      character :: ch,cha
 709      integer lenstr,i,k,ipos,iposa
 710      str=adjustl(str)
 711      call compact(str)
 712      lenstr=len_trim(str)
 713
 714      if(lenstr == 0) return ! string str is empty
 715      k=0
 716      before=' '
 717      do i=1,lenstr
 718        ch=str(i:i)
 719        
 720        ipos=index(delims,ch)
 721
 722        if(ipos == 0) then ! character is not a delimiter
 723          k=k+1
 724          before(k:k)=ch
 725          cycle
 726        end if
 727        if(ch /= ' ') then ! character is a delimiter that is not a space
 728          str=str(i+1:)
 729          exit
 730        end if
 731
 732        cha=str(i+1:i+1)  ! character is a space delimiter
 733        iposa=index(delims,cha)
 734        if(iposa > 0) then   ! next character is a delimiter 
 735          str=str(i+2:)
 736          exit
 737        else
 738          str=str(i+1:)
 739          exit
 740        end if
 741      end do
 742      if(i >= lenstr) str=''
 743
 744      str=adjustl(str) ! remove initial spaces
 745
 746    end subroutine split
 747
 748  end subroutine oer_readObsErrorsTOVS
 749
 750  !--------------------------------------------------------------------------
 751  ! oer_readObsErrorsCONV
 752  !--------------------------------------------------------------------------
 753  subroutine oer_readObsErrorsCONV()
 754    ! 
 755    !:Purpose: read observation errors (modification of former readcovo subroutine) of conventional data 
 756    !
 757    implicit none
 758
 759    ! Locals:
 760    integer :: fnom, fclos, ierr, jlev, jelm, jcat, icodtyp, nulstat
 761    logical             :: LnewExists
 762    character (len=128) :: ligne
 763
 764    if (visAndGustAdded) then
 765      surfaceObsTypeNumber = 6
 766    else
 767      surfaceObsTypeNumber = 4
 768    end if
 769
 770    ! CHECK THE EXISTENCE OF THE NEW FILE WITH STATISTICS
 771    inquire(file = 'obserr', exist = LnewExists)
 772    if (LnewExists) then
 773      write(*,*) '--------------------------------------------------------'
 774      write(*,*) 'oer_readObsErrorsCONV: reads observation errors in obserr'
 775      write(*,*) '--------------------------------------------------------'
 776    else
 777      call utl_abort('oer_readObsErrorsCONV: NO OBSERVATION STAT FILE FOUND!!')     
 778    end if
 779
 780    ! Read observation errors from file obserr for conventional data
 781    nulstat=0
 782    ierr=fnom(nulstat, 'obserr', 'SEQ', 0)
 783    if (ierr == 0) then
 784      write(*,*) 'oer_readObsErrorsCONV: File =  ./obserr'
 785      write(*,*) ' opened as unit file ',nulstat
 786      open(unit=nulstat, file='obserr', status='OLD')
 787    else
 788      call utl_abort('oer_readObsErrorsCONV: COULD NOT OPEN FILE obserr!!!')
 789    end if
 790
 791    write(*, '(A)') ' '
 792
 793    do jlev = 1,3
 794      read(nulstat, '(A)') ligne
 795      write(*, '(A)') ligne
 796    end do
 797
 798    do jlev = 1, 19
 799      read(nulstat, *) (xstd_ua_ai_sw(jlev,jelm), jelm=1,11)
 800      write(*, '(f6.0,10f6.1)')  (xstd_ua_ai_sw(jlev,jelm), jelm=1,11)
 801    end do
 802
 803    do jlev = 1,5
 804      read(nulstat, '(A)') ligne
 805      write(*, '(A)') ligne
 806    end do
 807
 808    read(nulstat, *) xstd_pr(1),xstd_pr(2)
 809    write(*, '(2f6.1)')  xstd_pr(1),xstd_pr(2)
 810
 811    do jlev = 1,5
 812      read(nulstat, '(A)') ligne
 813      write(*, '(A)') ligne
 814    end do
 815
 816    read(nulstat, *) xstd_sc(1)
 817    write(*, '(f8.3)')  xstd_sc(1)
 818
 819    read(nulstat, '(A)') ligne
 820    write(*, '(A)') ligne
 821
 822    do icodtyp = 1,9
 823      do jlev = 1,4
 824        read(nulstat, '(A)') ligne
 825        write(*, '(A)') ligne
 826      end do
 827      read(nulstat, *) (xstd_sf(icodtyp,jelm), jelm=1,surfaceObsTypeNumber)
 828      write(*, '(f6.2,2f6.1,f8.3)')  (xstd_sf(icodtyp,jelm), jelm=1,surfaceObsTypeNumber)
 829    end do
 830
 831    !
 832    !     Read height assignment errors
 833    !     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 834    !
 835
 836    if(new_oer_sw) then
 837
 838      do jlev = 1,5
 839        read(nulstat, '(A)') ligne
 840        write(*, '(A)') ligne
 841      end do
 842
 843      read(nulstat, *) (LVLVALUE(jelm), jelm=1,9)
 844      write(*, '(9f6.1)')  (LVLVALUE(jelm), jelm=1,9)
 845
 846      do jlev = 1,2
 847        read(nulstat, '(A)') ligne
 848        write(*, '(A)') ligne
 849      end do
 850
 851      read(nulstat, '(i10)') n_sat_type
 852      write(*,'(i10)') n_sat_type
 853      do jlev = 1,n_sat_type
 854        read(nulstat, '(10A10)') (SAT_AMV(jlev,jelm), jelm=1,10)
 855        write(*, '(10A10)') (SAT_AMV(jlev,jelm), jelm=1,10)
 856      end do
 857
 858      read(nulstat, '(A)') ligne
 859      write(*, '(A)') ligne
 860
 861      tbl_m(:) = 0
 862      tbl_h(:) = 0
 863      tbl_t(:) = 0
 864      tbl_g(:) = 0
 865      read(nulstat, '(i10)') n_categorie
 866      write(*,'(i10)') n_categorie
 867      do jcat = 1,n_categorie
 868        read(nulstat, *) SAT_LIST(jcat),MET_LIST(jcat),HTM_LIST(jcat),TMG_LIST(jcat),NSW_LIST(jcat),(HGT_ERR(jcat,jelm), jelm=1,9)
 869        write(*, '(5A10,9f6.1)') SAT_LIST(jcat),MET_LIST(jcat),HTM_LIST(jcat),TMG_LIST(jcat),NSW_LIST(jcat),(HGT_ERR(jcat,jelm), jelm=1,9)
 870        if(trim(MET_LIST(jcat)) == 'ir')   tbl_m(jcat) = 1
 871        if(trim(MET_LIST(jcat)) == 'vis')  tbl_m(jcat) = 2
 872        if(trim(MET_LIST(jcat)) == 'wv')   tbl_m(jcat) = 3
 873        if(trim(HTM_LIST(jcat)) == 'ebbt') tbl_h(jcat) = 1
 874        if(trim(HTM_LIST(jcat)) == 'co2')  tbl_h(jcat) = 4
 875        if(trim(TMG_LIST(jcat)) == 'sea')  tbl_t(jcat) = 1
 876        if(trim(TMG_LIST(jcat)) == 'land') tbl_t(jcat) = 2 
 877        if(trim(TMG_LIST(jcat)) == 'ice')  tbl_t(jcat) = 3
 878        if(trim(NSW_LIST(jcat)) == 'NH')   tbl_g(jcat) = 1
 879        if(trim(NSW_LIST(jcat)) == 'SH')   tbl_g(jcat) = 2
 880      end do
 881
 882    end if
 883
 884    write(*, '(A)') ' '
 885
 886    close(unit = nulstat)
 887    ierr = fclos(nulstat)
 888
 889  end subroutine oer_readObsErrorsCONV
 890
 891  !--------------------------------------------------------------------------
 892  ! oer_readObsErrorsIce
 893  !--------------------------------------------------------------------------
 894  subroutine oer_readObsErrorsIce
 895    !
 896    !:Purpose: read observation errors for sea ice concentration analysis
 897    !
 898    implicit none
 899
 900    ! Locals:
 901    external fnom, fclos
 902    integer :: fnom, fclos, ierr, jlev, jelm, nulstat
 903    logical            :: fileExists
 904    character(len=128) :: ligne
 905    character(len=15), parameter :: fileName = 'sea_ice_obs-err'
 906    ! Variables for the ASCAT backscatter anisotropy
 907    integer :: jcell_no, icell_no, imonth
 908    real :: tiePoint12, tiePoint13, tiePoint23
 909
 910    ! CHECK THE EXISTENCE OF THE FILE WITH STATISTICS
 911    inquire(file = fileName, exist = fileExists)
 912    if (fileExists) then
 913      write(*,*) '--------------------------------------------------------'
 914      write(*,*) 'oer_readObsErrorsICE: reads observation errors in ',fileName
 915      write(*,*) '--------------------------------------------------------'
 916    else
 917      call utl_abort('oer_readObsErrorsICE: NO OBSERVATION STAT FILE FOUND!!')     
 918    end if
 919
 920    ! Read observation errors from file
 921    nulstat=0
 922    ierr=fnom(nulstat, fileName, 'SEQ', 0)
 923    if (ierr == 0) then
 924      write(*,*) 'oer_readObsErrorsICE: File = ./',fileName
 925      write(*,*) ' opened as unit file ',nulstat
 926      open(unit=nulstat, file=fileName, status='OLD')
 927    else
 928      call utl_abort('oer_readObsErrorsICE: COULD NOT OPEN FILE '//fileName//'!!!')
 929    end if
 930
 931    if (mmpi_myid == 0) write(*, '(A)') ' '
 932
 933    do jelm = 1, 9
 934
 935      do jlev = 1, 3
 936        read(nulstat, '(A)') ligne
 937        if (mmpi_myid == 0) write(*, '(A)') ligne
 938      end do
 939
 940      read(nulstat, *) xstd_sic(jelm)
 941      if (mmpi_myid == 0) write(*,*) xstd_sic(jelm)
 942
 943      do jlev = 1, 2
 944        read(nulstat, '(A)') ligne
 945        if (mmpi_myid == 0) write(*, '(A)') ligne
 946      end do
 947
 948    end do
 949
 950    ! Read coefficients for ASCAT backscatter anisotropy
 951    do jlev = 1, 5
 952      read(nulstat, '(A)') ligne
 953      if (mmpi_myid == 0) write(*, '(A)') ligne
 954    end do
 955
 956    do imonth = 1, 12
 957      do jlev = 1, 3
 958        read(nulstat, '(A)') ligne
 959        if (mmpi_myid == 0) write(*, '(A)') ligne
 960      end do
 961      do jcell_no = 1, ncells
 962         read(nulstat, *) icell_no, tiePoint12, tiePoint13, tiePoint23, &
 963                           oer_ascatAnisIce(jcell_no,imonth), oer_ascatAnisOpenWater(jcell_no,imonth), &
 964                           ascatAnisSigmaIce(jcell_no,imonth), ascatAnisSigmaOpenWater(jcell_no,imonth)
 965         if (mmpi_myid == 0) write(*,*) icell_no, tiePoint12, tiePoint13, tiePoint23, &
 966                           oer_ascatAnisIce(jcell_no,imonth), oer_ascatAnisOpenWater(jcell_no,imonth), &
 967                           ascatAnisSigmaIce(jcell_no,imonth), ascatAnisSigmaOpenWater(jcell_no,imonth)
 968      end do
 969    end do
 970
 971    if (mmpi_myid == 0) write(*, '(A)') ' '
 972
 973    close(unit = nulstat)
 974    ierr = fclos(nulstat)
 975
 976  end subroutine oer_readObsErrorsIce
 977
 978  !--------------------------------------------------------------------------
 979  ! oer_readObsErrorsSST
 980  !--------------------------------------------------------------------------
 981  subroutine oer_readObsErrorsSST
 982    !
 983    !:Purpose: read observation errors for SST data
 984    !
 985    implicit none
 986
 987    ! Locals:
 988    integer :: fnom, fclos, nulnam, ierr, indexDataset
 989    namelist /namSSTObsErrors/ numberSSTDatasets, SSTdataParams
 990    
 991    if (utl_isNamelistPresent('namSSTObsErrors','./flnml')) then
 992      nulnam = 0
 993      ierr = fnom(nulnam, './flnml', 'FTN+SEQ+R/O', 0)
 994      read (nulnam, nml = namSSTObsErrors, iostat = ierr)
 995      if (ierr /= 0) call utl_abort('oer_readObsErrorsSST: Error reading namelist')
 996      if (mmpi_myid == 0) write(*,nml=namSSTObsErrors)
 997      ierr = fclos(nulnam)
 998      if (numberSSTDatasets /= MPC_missingValue_INT) then
 999        call utl_abort('oer_readObsErrorsSST: check namSSTObsErrors namelist section: numberSSTDatasets should be removed')
1000      end if
1001      numberSSTDatasets = 0
1002      do indexDataset = 1, maxNumberSSTDatasets
1003        if (trim(SSTdataParams(indexDataset)%dataType)   == ""                   .and. &
1004            trim(SSTdataParams(indexDataset)%instrument) == ""                   .and. &
1005            trim(SSTdataParams(indexDataset)%sensor)     == ""                   .and. &
1006            trim(SSTdataParams(indexDataset)%sensorType) == ""                   .and. &
1007                 SSTdataParams(indexDataset)%codeType    == MPC_missingValue_INT .and. &
1008                 SSTdataParams(indexDataset)%dayError    == MPC_missingValue_R8  .and. &
1009                 SSTdataParams(indexDataset)%nightError  == MPC_missingValue_R8 ) exit
1010        numberSSTDatasets = numberSSTDatasets + 1
1011      end do
1012    else
1013       call utl_abort('oer_readObsErrorsSST: namSSTObsErrors is missing in the namelist.')
1014    end if
1015    
1016  end subroutine oer_readObsErrorsSST
1017
1018  !--------------------------------------------------------------------------
1019  ! oer_readObsErrorsHydro
1020  !--------------------------------------------------------------------------
1021  subroutine oer_readObsErrorsHydro
1022    implicit none
1023
1024    ! Locals:
1025    external :: fnom, fclos
1026    integer                      :: fnom, fclos, ierr, nulstat
1027    logical                      :: fileExists
1028    character(len=15), parameter :: fileName = 'obserr_hydro'    
1029    character(len=*) , parameter :: myName   = 'oer_readObsErrorsHydro'
1030
1031    inquire(file = fileName, exist = fileExists)
1032    if (fileExists) then
1033      write(*,*) '--------------------------------------------------------'
1034      write(*,*) myName//': reads observation errors in ', fileName
1035      write(*,*) '--------------------------------------------------------'
1036    else
1037      call utl_abort(myName//': NO OBSERVATION STAT FILE FOUND!!')     
1038    end if
1039
1040    nulstat=0
1041    ierr=fnom(nulstat, fileName, 'SEQ', 0)
1042    if (ierr == 0) then
1043      write(*,*) myName//': File = ./',fileName
1044      write(*,*) myName//' opened as unit file ',nulstat
1045      open(unit=nulstat, file=fileName, status='OLD')
1046    else
1047      call utl_abort(myName//': COULD NOT OPEN FILE '//fileName//'!!!')
1048    end if
1049
1050    read(nulstat, *)    xstd_hydro(1)
1051    write(*, '(f8.3)')  xstd_hydro(1)
1052
1053    close(unit = nulstat)
1054    ierr = fclos(nulstat)
1055
1056  end subroutine oer_readObsErrorsHydro
1057
1058  !--------------------------------------------------------------------------
1059  ! oer_fillObsErrors
1060  !--------------------------------------------------------------------------
1061  subroutine oer_fillObsErrors(obsSpaceData)
1062    !
1063    !:Purpose: fill observation errors in obsSpaceData
1064    !
1065    implicit none
1066
1067    ! Arguments:
1068    type(struct_obs), intent(inout) :: obsSpaceData
1069
1070    ! Locals:
1071    integer :: jn, JI, bodyIndex, headerIndex, ityp, iass, idata, idatend, codeType
1072    integer :: sensorIndex 
1073    integer :: isat, channelNumber, iplatf, instr, iplatform, instrum
1074    integer :: ilev, nlev, idate, itime
1075    integer :: ielem, icodtyp, header_prev, indexDataset, indexSensor
1076    real(8) :: zlat, zlon, zlev, zval, zwb, zwt, obs_err_stddev, solarZenith
1077    real(8) :: obsValue, obsStdDevError, obsPPP, obsOER
1078    real(8) :: cldPredThresh1, cldPredThresh2, cldPredUsed
1079    real(8) :: errThresh1, errThresh2, sigmaObsErrUsed
1080    real(4), parameter :: minRetrievableClwValue_r4 = 0.0
1081    real(4), parameter :: maxRetrievableClwValue_r4 = 3.0
1082    real(4), parameter :: minRetrievableSiValue_r4 = -10.0
1083    real(4), parameter :: maxRetrievableSiValue_r4 = 30.0
1084    logical :: ifirst, surfTypeIsWater, unsupportedCodeType, unsupportedSensor
1085    character(len=2)  :: cfam
1086    character(len=12) :: cstnid
1087
1088    write(*,'(10X, "***********************************")')
1089    write(*,'(10X, "oer_fillObsErrors: starting", /)')
1090    write(*,'(10X, "***********************************")')
1091
1092    header_prev=-1
1093
1094    ! SET STANDARD DEVIATION ERRORS FOR EACH DATA FAMILY
1095    HEADER: do headerIndex = 1, obs_numheader(obsSpaceData)
1096
1097      idata    = obs_headElem_i(obsSpaceData, OBS_RLN, headerIndex)
1098      idatend  = obs_headElem_i(obsSpaceData, OBS_NLV, headerIndex) + idata - 1
1099      cfam     = obs_getFamily (obsSpaceData, headerIndex_opt=headerIndex)
1100      zlat     = obs_headElem_r(obsSpaceData, OBS_LAT, headerIndex)
1101      zlon     = obs_headElem_r(obsSpaceData, OBS_LON, headerIndex)
1102      codeType = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
1103      iplatf   = obs_headElem_i(obsSpaceData, OBS_SAT, headerIndex)
1104      instr    = obs_headElem_i(obsSpaceData, OBS_INS, headerIndex)
1105      cstnid   = obs_elem_c    (obsSpaceData, 'STID' , headerIndex)
1106      idate    = obs_headElem_i(obsSpaceData, OBS_DAT, headerIndex) 
1107      itime    = obs_headElem_i(obsSpaceData, OBS_ETM, headerIndex)
1108
1109      surfTypeIsWater = (tvs_ChangedStypValue(obsSpaceData,headerIndex) == surftype_sea)
1110
1111      nlev = idatend - idata + 1
1112
1113      BODY: do bodyIndex  = idata, idatend
1114
1115        ityp  = obs_bodyElem_i(obsSpaceData, OBS_VNM, bodyIndex)
1116        iass  = obs_bodyElem_i(obsSpaceData, OBS_ASS, bodyIndex)
1117        zval  = obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex)
1118
1119        if (iass == obs_assimilated) then
1120
1121             !***********************************************************************
1122             !                           TOVS DATA
1123             !***********************************************************************
1124
1125          if (cfam == 'TO') then
1126
1127            if (ityp == BUFR_NBT1 .or. &
1128                ityp == BUFR_NBT2 .or. &
1129                ityp == BUFR_NBT3) then
1130
1131              channelNumber = NINT(obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex))
1132              call tvs_mapSat(iplatf, iplatform, isat)
1133              call tvs_mapInstrum(instr, instrum)
1134
1135              do sensorIndex = 1, tvs_nsensors
1136                if (iplatform ==  tvs_platforms(sensorIndex)  .and. &
1137                    isat      ==  tvs_satellites(sensorIndex) .and. &
1138                    instrum   == tvs_instruments(sensorIndex)) then
1139
1140                  ! decide whether or not use the state dependent sigmaObsErrUsed for OBS_OER
1141                  if (tvs_mwAllskyAssim .and. &
1142                      useStateDepSigmaObs(channelNumber,sensorIndex) .and. &
1143                      surfTypeIsWater) then
1144
1145                    ! set dummy value for OBS_OER in bgck mode
1146                    if (trim(obserrorMode) == 'bgck') then
1147                      sigmaObsErrUsed = 1.0d0
1148                    else
1149                      cldPredThresh1 = cldPredThresh(channelNumber,sensorIndex,1)
1150                      cldPredThresh2 = cldPredThresh(channelNumber,sensorIndex,2)
1151                      errThresh1 = errThreshAllsky(channelNumber,sensorIndex,1)
1152                      errThresh2 = errThreshAllsky(channelNumber,sensorIndex,2)
1153
1154                      ! Compute cloudPredictor to use
1155                      cldPredUsed = computeCloudPredictor(sensorIndex, headerIndex)
1156
1157                      ! Use cloud predictor to compute state-dependent obs error
1158                      sigmaObsErrUsed = calcStateDepObsErr(cldPredThresh1, cldPredThresh2, &
1159                                                           errThresh1, errThresh2, cldPredUsed)
1160                    end if
1161                  else
1162                    sigmaObsErrUsed = TOVERRST(channelNumber, sensorIndex)
1163                  end if
1164                  call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, sigmaObsErrUsed)
1165
1166                  !   Utilization flag for AIRS,IASI and CrIS channels (bgck mode only)
1167                  if (trim(obserrorMode) == 'bgck' .or. useTovsUtil) then
1168                    if  (tovutil(channelNumber, sensorIndex) == 0) &
1169                      call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, ibset(obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex), 8))
1170                  end if
1171                end if
1172              end do
1173
1174            end if
1175
1176                !***********************************************************************
1177                !                      RADIOSONDE DATA
1178                !***********************************************************************
1179
1180          else if (cfam == 'UA') then
1181
1182            zlev = obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex)
1183
1184            if ((ityp == BUFR_NEUS) .or. (ityp == BUFR_NEVS)) then
1185              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(1,4))
1186            else if (ityp == BUFR_NETS) then
1187              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(1,2))
1188            else if (ityp == BUFR_NESS) then
1189              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(1,3))
1190            else if (ityp == BUFR_NEPS) then
1191              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(1,1))
1192            else if (ityp == BUFR_NEPN) then
1193              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(1,1))
1194            else
1195
1196              if ((ityp == BUFR_NEUU) .or. (ityp == BUFR_NEVV)) then
1197                ielem = 4
1198              else if (ityp == BUFR_NETT) then
1199                ielem = 2
1200              else if (ityp == BUFR_NEES) then
1201                ielem = 3
1202              else if (ityp == BUFR_NEGZ) then
1203                ielem = 5
1204              end if
1205
1206              if ((zlev * MPC_MBAR_PER_PA_R8) >= xstd_ua_ai_sw(1,1)) then
1207
1208                call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_ua_ai_sw(1, ielem))
1209
1210              else if ((zlev * MPC_MBAR_PER_PA_R8) <= xstd_ua_ai_sw(19, 1)) then
1211
1212                call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_ua_ai_sw(19, ielem))
1213
1214              else
1215
1216                do jn = 1, 18
1217                  if ((zlev * MPC_MBAR_PER_PA_R8) >= xstd_ua_ai_sw(jn + 1, 1)) exit
1218                end do
1219
1220                zwb = log((zlev * MPC_MBAR_PER_PA_R8) / xstd_ua_ai_sw(jn, 1)) / log(xstd_ua_ai_sw(jn + 1, 1) / xstd_ua_ai_sw(jn, 1))
1221                zwt = 1.0D0 - zwb
1222                
1223                call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex,  &
1224                                    zwt * xstd_ua_ai_sw(jn, ielem) +  &
1225                                    zwb * xstd_ua_ai_sw(jn + 1, ielem))
1226
1227              end if
1228                   
1229            end if
1230
1231                !***********************************************************************
1232                !                          AMV, AIREP, AMDAR DATA
1233                !***********************************************************************
1234
1235          else if (cfam == 'AI' .or. cfam == 'SW') then
1236
1237            zlev=obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex)
1238
1239            if (codeType == codtyp_get_codtyp('windsbufr')) then ! AMV
1240          
1241              if ((ityp == BUFR_NEUU) .or. (ityp == BUFR_NEVV)) ielem = 11
1242       
1243            else if (codeType == codtyp_get_codtyp('airep')) then ! AIREP
1244              if ((ityp == BUFR_NEUU) .or. (ityp == BUFR_NEVV)) then
1245                ielem = 7
1246              else if (ityp == BUFR_NETT) then
1247                ielem = 6
1248              end if
1249              
1250            else if (codeType == codtyp_get_codtyp('amdar') .or. &
1251                     codeType == codtyp_get_codtyp('acars') .or. &
1252                     codeType == codtyp_get_codtyp('ads')) then
1253              if (ityp == BUFR_NEUU .or. ityp == BUFR_NEVV) then
1254                ielem = 10
1255              else if (ityp == BUFR_NETT) then
1256                ielem = 8
1257              else if (ityp == BUFR_NEES) then
1258                ielem = 9
1259              end if
1260            end if
1261
1262            if ((zlev * MPC_MBAR_PER_PA_R8) >= xstd_ua_ai_sw(1, 1)) then
1263              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_ua_ai_sw(1, ielem))
1264            else if ((zlev * MPC_MBAR_PER_PA_R8) <= xstd_ua_ai_sw(19, 1)) then
1265              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_ua_ai_sw(19, ielem))
1266            else
1267              do jn = 1, 18
1268                if ((zlev * MPC_MBAR_PER_PA_R8) >= xstd_ua_ai_sw(jn + 1, 1)) exit
1269              end do
1270
1271              zwb = log((zlev * MPC_MBAR_PER_PA_R8) / xstd_ua_ai_sw(jn, 1)) / log(xstd_ua_ai_sw(jn + 1, 1) / xstd_ua_ai_sw(jn, 1))
1272              zwt = 1.0D0 - zwb 
1273
1274              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex,  &
1275                                  zwt * xstd_ua_ai_sw(jn, ielem) +  &
1276                                  zwb * xstd_ua_ai_sw(jn + 1, ielem))
1277
1278            end if
1279
1280                !***********************************************************************
1281                !                         SURFACE DATA
1282                !***********************************************************************
1283
1284          else if (cfam == 'SF') then
1285            icodtyp = 1   ! Default values
1286            if (codeType == codtyp_get_codtyp('synopnonauto')) icodtyp = 2 ! SYNOP
1287            if (codeType == codtyp_get_codtyp('shipnonauto'))  icodtyp = 3 ! SHIP NON-AUTOMATIQUE
1288            if (codeType == codtyp_get_codtyp('synopmobil'))   icodtyp = 4 ! DRIBU
1289            if (codeType == codtyp_get_codtyp('drifter'))      icodtyp = 5 ! DRifTER
1290            if (codeType == codtyp_get_codtyp('synoppatrol'))  icodtyp = 6 ! STATION AUTOMATIQUE
1291            if (codeType == codtyp_get_codtyp('asynopauto'))   icodtyp = 7 ! ASYNOP
1292            if (codeType == codtyp_get_codtyp('ashipauto'))    icodtyp = 8 ! ASHIP
1293            if (ityp == BUFR_NEUU .or. ityp == BUFR_NEVV .or. &
1294                ityp == BUFR_NEGZ .or. ityp == BUFR_NETT .or. ityp == BUFR_NEES) icodtyp = 9  ! Others
1295            if (ityp == BUFR_NEUS .or. ityp == BUFR_NEVS) then
1296              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(icodtyp, 4))
1297            else if (ityp == BUFR_NETS) then
1298              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(icodtyp, 2))
1299            else if (ityp == BUFR_NESS) then
1300              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(icodtyp, 3))
1301            else if (ityp == BUFR_NEPS) then
1302              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(icodtyp, 1))
1303            else if (ityp == BUFR_NEPN) then
1304              if (icodtyp == 2  .or. icodtyp == 7) then
1305                call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(1, 1))
1306              else
1307                call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(icodtyp, 1))
1308              end if
1309            else if ((ityp == BUFR_NEUU) .or. (ityp == BUFR_NEVV))then
1310              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(icodtyp, 4))
1311            else if (ityp == BUFR_NEGZ) then
1312              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(icodtyp, 1))
1313            else if (ityp == BUFR_NETT) then
1314              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(icodtyp, 2))
1315            else if (ityp == BUFR_NEES) then
1316              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(icodtyp, 3))
1317            else if (ityp == bufr_vis .or. ityp == bufr_logVis) then
1318              if (surfaceObsTypeNumber >= 5) then
1319                call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(icodtyp, 5))
1320              else
1321                call utl_abort('oer_fillObsErrors: observation error missing for visibility')
1322              end if
1323            else if (ityp == bufr_gust) then
1324              if (surfaceObsTypeNumber >= 6) then
1325                call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(icodtyp, 6))
1326              else
1327                call utl_abort('oer_fillObsErrors: observation error missing for wind gust')
1328              end if
1329            else if (ityp == BUFR_NEFS) then ! SAR wind speed
1330              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, 2.0d0)
1331            end if
1332
1333                !***********************************************************************
1334                !                             GPS RO DATA
1335                !***********************************************************************
1336
1337          else if (cfam == 'RO') then
1338            ! Process only refractivity data (codtyp 169)
1339            if (codeType == codtyp_get_codtyp('ro')) then
1340              if (ityp == BUFR_NEPS) then
1341                call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, 50.D0)
1342              end if
1343              if (ityp == BUFR_NETT) then
1344                call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, 10.D0)
1345              end if
1346              if (ityp == BUFR_NERF) then
1347                call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, 500.D0)
1348              end if
1349              if (ityp == BUFR_NEBD) then
1350                call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, 0.08D0)
1351              end if
1352            end if
1353
1354          !***********************************************************************
1355          !                   GB-GPS SFC MET DATA
1356          !***********************************************************************
1357
1358          !              ERRORS ARE SET TO SYNO SFC OBS ERRORS FROM S/R SUCOVO
1359          !              AND WEIGHTED BY FACTOR YSFERRWGT FOR 3D-VAR FGAT OR 4D-VAR ASSIM.
1360          !              OF TIME-SERIES (YSFERRWGT = 1.0 FOR 3D THINNING) 
1361          !
1362          else if (cfam == 'GP') then
1363
1364            if (ityp == BUFR_NEPS) then ! Psfc Error (Pa)
1365              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(2, 1))
1366            end if
1367            if (ityp == BUFR_NETS) then ! Tsfc Error (K)
1368              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(2, 2))
1369            end if
1370            if (ityp == BUFR_NESS) then ! T-Td Error (K)
1371              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sf(2, 3))
1372            end if
1373                ! ZTD Error (m) (value is formal error, real error set later in s/r seterrgpsgb)
1374                ! If error is missing, set to dummy value (1 m).
1375            if (ityp == BUFR_NEZD) then
1376              if (obs_bodyElem_r(obsSpaceData, OBS_OER, bodyIndex) <=  0.0D0) then
1377                call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, 1.0D0)
1378              end if
1379            end if
1380
1381                !***********************************************************************
1382                !        SCATTEROMETER, WIND PROFILER DATA
1383                !***********************************************************************
1384
1385          else if (cfam == 'SC') then
1386
1387            call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sc(1))
1388
1389          else if (cfam == 'PR') then
1390
1391            zlev= obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex) - obs_headElem_r(obsSpaceData, OBS_ALT, headerIndex)
1392
1393            if (zlev  >=  6000.) then
1394              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_pr(2))
1395            else
1396              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_pr(1))
1397            end if
1398
1399                !***********************************************************************
1400                !        ALADIN HORIZONTAL LINE-OF-SIGHT WIND DATA
1401                !***********************************************************************
1402
1403          else if (cfam == 'AL') then
1404
1405            ! TEMPORARILY, hard-code the observation error of AL winds to 2 m/s
1406            call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, 2.0d0)
1407              
1408                !***********************************************************************
1409                !               CONSTITUENT DATA (OZONE AND OTHER CHEMICALS)
1410                !***********************************************************************
1411
1412          else if (cfam == 'CH') then
1413
1414                !        Process only retrieved constituent data
1415                !        Also, exclude BUFR_SCALE_EXPONENT element as a data value!
1416
1417            if (codeType == codtyp_get_codtyp('CHEMREMOTE') .or. codeType == codtyp_get_codtyp('CHEMINSITU')) then
1418
1419              ifirst = headerIndex /= header_prev
1420              if (ifirst) then
1421
1422                header_prev = headerIndex
1423
1424                ! Subtract the number of occurences of code BUFR_SCALE_EXPONENT from number of levels
1425                do ji = 0, nlev - 1
1426                  if (obs_bodyElem_i(obsSpaceData, OBS_VNM, idata + ji) == BUFR_SCALE_EXPONENT) nlev = nlev-1
1427                end do
1428              end if
1429
1430              zlev   = obs_bodyElem_r(obsSpaceData,OBS_PPP,bodyIndex)
1431
1432              ilev = 0
1433              do ji = idata, bodyIndex
1434                if (obs_bodyElem_i(obsSpaceData, OBS_VNM, ji) /= BUFR_SCALE_EXPONENT) ilev = ilev+1
1435              end do
1436
1437              obs_err_stddev = chm_get_obs_err_stddev(cstnid, nlev, ityp, zlat, zlon, idate, itime, zval, zlev, ilev, ifirst)
1438              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, obs_err_stddev)
1439
1440            end if     
1441
1442                !***********************************************************************
1443                !               Sea Surface Temperature
1444                !***********************************************************************
1445
1446          else if (cfam == 'TM') then
1447
1448            if (obs_bodyElem_i(obsSpaceData, OBS_VNM, bodyIndex) /= bufr_sst) cycle BODY
1449
1450            if (codeType == codtyp_get_codtyp('drifter')   .or. codeType == codtyp_get_codtyp('shipnonauto') .or. &
1451                codeType == codtyp_get_codtyp('ashipauto') .or. codeType == codtyp_get_codtyp('pseudosfc')        ) then
1452
1453              unsupportedCodeType = .true.
1454              dataset_loop: do indexDataset = 1, numberSSTDatasets
1455                if(codeType == SSTdataParams(indexDataset)%codeType) then
1456                  unsupportedCodeType = .false.
1457                  call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, SSTdataParams(indexDataset)%dayError)
1458                  exit dataset_loop
1459                end if
1460              end do dataset_loop
1461
1462              if(unsupportedCodeType) then
1463                write(*,'(a,i5,2a)') 'oer_fillObsErrors: unsupported SST data, codtype ', codeType,', cstnid ', cstnid,' found in dataset_loop!' 
1464                call utl_abort('oer_fillObsErrors: unsupported codeType!')
1465              end if
1466
1467            else if (codeType == codtyp_get_codtyp('satob')) then
1468
1469              solarZenith = obs_headElem_r(obsSpaceData, obs_sun, headerIndex)
1470              if (solarZenith == MPC_missingValue_R8) then
1471                write(*,*) 'oer_fillObsErrors: Solar zenith value is missing for satellite SST data ', &
1472                           cstnid, ', headerIndex: ', headerIndex,', bodyIndex: ', bodyIndex
1473                call utl_abort('oer_fillObsErrors: Solar zenith value is missing!')
1474              end if
1475              
1476              unsupportedSensor = .true.
1477              sensor_loop: do indexSensor = 1, numberSSTDatasets
1478                if (cstnid == trim(SSTdataParams(indexSensor)%sensor)) then
1479                  unsupportedSensor = .false.
1480                  if (solarZenith <= 90.) then ! day
1481                    call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, SSTdataParams(indexSensor)%dayError)
1482                  else ! night
1483                    call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, SSTdataParams(indexSensor)%nightError)
1484                  end if
1485                  exit sensor_loop
1486                end if
1487              end do sensor_loop
1488
1489              if(unsupportedSensor) then
1490                write(*,'(3a)') 'oer_fillObsErrors: unsupported satellite SST data sensor ', cstnid,' found in sensor_loop!' 
1491                call utl_abort('oer_fillObsErrors: unsupported satellite SST data sensor!')
1492              end if
1493
1494            else
1495
1496              write(*,'(a,i5,2a)') 'oer_fillObsErrors: unsupported SST data, codtype ', codeType,', cstnid ', cstnid,' found!' 
1497              call utl_abort('oer_fillObsErrors: unsupported codeType!')
1498
1499            end if
1500
1501                !***********************************************************************
1502                !               Sea Ice Concentration
1503                !***********************************************************************
1504
1505          else if (cfam == 'GL') then
1506
1507            if (index(cstnid,'DMSP') == 1) then
1508              select case(cstnid)
1509              case('DMSP15')
1510                call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sic(1))
1511              case('DMSP16','DMSP17','DMSP18')
1512                call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sic(2))
1513              case DEFAULT
1514                call utl_abort('oer_fillObsErrors: UNKNOWN station id: '//cstnid)
1515              end select
1516            else if (cstnid == 'GCOM-W1') then
1517              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sic(3))
1518            else if (cstnid(1:6) == 'METOP-') then
1519              if (ityp == BUFR_ICEP) then
1520                 call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sic(4))
1521              else if (ityp == BUFR_ICES) then
1522                 ! This is backscatter anisotropy, obs-error will be set in oer_setErrBackScatAnisIce
1523                 ! because the need of background ice concentration.
1524                 ! For now just put a large positive value
1525                 call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, 1000.0)
1526              else
1527                 call utl_abort('oer_fillObsErrors: UNKNOWN varno: '// trim(utl_str(ityp)) // 'station id: '//cstnid)
1528              end if
1529            else if (cstnid == 'noaa-19') then
1530              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sic(5))
1531            else if (cstnid == 'CIS_DAILY') then
1532              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sic(6))
1533            else if (cstnid == 'RS1_IMG') then
1534              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sic(7))
1535            else if (codeType ==  178) then ! lake ice
1536              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sic(8))
1537            else if (cstnid == 'CIS_REGIONAL') then
1538              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, xstd_sic(9))
1539            else if (index(cstnid,'RCM') == 1) then
1540              ! For RCM, obs-error comes from SQLite file
1541            else
1542              call utl_abort('oer_fillObsErrors: UNKNOWN station id: '//cstnid)
1543            end if
1544            
1545            !***********************************************************************
1546            !               Hydrology
1547            !***********************************************************************
1548          else if (cfam == 'HY') then
1549
1550            if (obs_bodyElem_i(obsSpaceData, OBS_VNM, bodyIndex) /= bufr_riverFlow) cycle BODY
1551
1552            if (codeType == 12) then ! pseudo-SYNOP
1553              obsValue = obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex)
1554              obsStdDevError = obsValue * xstd_hydro(1)
1555              write(*,*) 'Hydro observation std dev error: ', bodyIndex, obsValue, xstd_hydro(1), obsStdDevError
1556              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, obsStdDevError)
1557            else
1558              write(*,*) 'oer_fillObsErrors: unsupported codeType for hydro data found in the observations: ', codeType 
1559              call utl_abort('oer_fillObsErrors: unsupported codeType')
1560            end if  
1561
1562          else if (cfam == 'RA') then
1563
1564            if (ityp == BUFR_radarPrecip .or. ityp == BUFR_logRadarPrecip) then
1565              ! Temporary hardcoded value for log-transformed radar precipitation
1566              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, 1.0D0)
1567            else if (ityp == bufr_radvel) then
1568              ! Temporary hardcoded value for radar Doppler velocity
1569              call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, 1.0D0)
1570            else
1571              write(*,*) 'varnum = ', ityp
1572              call utl_abort('oer_fillObsErrors: unknown varnum for RA family')
1573            end if
1574
1575          else
1576
1577            write(*,*) 'oer_fillObsErrors: UNKNOWN DATA FAMILY:',cfam
1578
1579          end if
1580
1581          !***********************************************************************
1582          !              Check for case where error should have been set but was
1583          !              not. 3dvar will abort in this case.
1584          !***********************************************************************
1585
1586          obsOER = obs_bodyElem_r(obsSpaceData,OBS_OER,bodyIndex)
1587          if (obsOER  <=  0.0D0) then
1588
1589            write(*,*)'  PROBLEM OBSERR VARIANCE FAM= ',cfam
1590
1591            write(*,'(1X,"STNID= ",A10,"codeType= ",I5," LAT= ",F10.2," LON = ",F10.2)') &
1592                 cstnid,    &
1593                 codeType,                                           &
1594                 zlat*MPC_DEGREES_PER_RADIAN_R8,                   &
1595                 zlon*MPC_DEGREES_PER_RADIAN_R8
1596
1597            obsPPP = obs_bodyElem_r(obsSpaceData,OBS_PPP,bodyIndex)
1598            write(*,'(1X,"ELEMENT= ",I6," LEVEL= ",F10.2," OBSERR = ",E10.2)')         &
1599                 ityp, obsPPP, obsOER
1600
1601            call utl_abort('oer_fillObsErrors: PROBLEM OBSERR VARIANCE.')
1602
1603          end if
1604
1605        else
1606
1607          call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, MPC_missingValue_R8)
1608
1609        end if ! end of iass == obs_assimilated
1610
1611      end do BODY
1612
1613    end do HEADER
1614
1615    write(*,'(10X,"Fill_obs_errors: Done")')
1616    write(*,'(10X,"---------------",/)')
1617
1618  contains
1619
1620    !--------------------------------------------------------------------------
1621    ! calcStateDepObsErr
1622    !--------------------------------------------------------------------------
1623    function calcStateDepObsErr(cldPredThresh1, cldPredThresh2, &
1624                                errThresh1, errThresh2, cldPredUsed) result(sigmaObsErrUsed)
1625      !
1626      !:Purpose: Calculate state-dependent observation error.
1627      !
1628      implicit none
1629
1630      ! Arguments:
1631      real(8), intent(in) :: cldPredThresh1 ! first cloud predictor threshold
1632      real(8), intent(in) :: cldPredThresh2 ! second cloud predictor threshold
1633      real(8), intent(in) :: errThresh1     ! sigmaO corresponding to first cloud predictor threshold
1634      real(8), intent(in) :: errThresh2     ! sigmaO corresponding to second cloud predictor threshold
1635      real(8), intent(in) :: cldPredUsed    ! cloud predictor for the obs
1636      ! Result:
1637      real(8) :: sigmaObsErrUsed            ! estimated sigmaO for the obs
1638
1639      if (cldPredUsed <= cldPredThresh1) then
1640        sigmaObsErrUsed = errThresh1
1641      else if (cldPredUsed >  cldPredThresh1 .and. & 
1642               cldPredUsed <= cldPredThresh2) then
1643        sigmaObsErrUsed = errThresh1 + &
1644                          (errThresh2 - errThresh1) / &
1645                          (cldPredThresh2 - cldPredThresh1) * &
1646                          (cldPredUsed - cldPredThresh1) 
1647      else
1648        sigmaObsErrUsed = errThresh2
1649      end if
1650
1651    end function calcStateDepObsErr
1652
1653    !--------------------------------------------------------------------------
1654    ! computeCloudPredictor
1655    !--------------------------------------------------------------------------
1656    function computeCloudPredictor(sensorIndex, headerIndex) result(cldPredUsed)
1657      !
1658      !:Purpose: Compute cloud predictor to use for state-dependent observation error.
1659      !
1660      implicit none
1661
1662      ! Arguments:
1663      integer, intent(in) :: sensorIndex ! index of sensor in tvs_nsensors
1664      integer, intent(in) :: headerIndex
1665      ! Result:
1666      real(8) :: cldPredUsed             ! cloud predictor. CLW/SI for all-sky temperature/humidity
1667
1668      ! Locals:
1669      integer :: platformId
1670      integer :: satelliteId
1671      integer :: instrumId
1672      real(8) :: clwObs, clwFG
1673      real(8) :: siObs, siFG
1674      real(4) :: cldPredUsed_r4
1675
1676      platformId = tvs_platforms(sensorIndex)
1677      satelliteId = tvs_satellites(sensorIndex)
1678      instrumId = tvs_instruments(sensorIndex)
1679
1680      if (tvs_isInstrumAllskyTtAssim(instrumId)) then
1681        clwObs = obs_headElem_r(obsSpaceData, OBS_CLWO, headerIndex)
1682        clwFG  = obs_headElem_r(obsSpaceData, OBS_CLWB, headerIndex)
1683        cldPredUsed = 0.5D0 * (clwObs + clwFG)
1684        cldPredUsed_r4 = real(cldPredUsed)
1685
1686        ! check to ensure CLW is retrieved and properly set
1687        if (cldPredUsed_r4 < minRetrievableClwValue_r4 .or. &
1688            cldPredUsed_r4 > maxRetrievableClwValue_r4) then
1689          write(*,*) 'This observation should have been rejected ', &
1690                     'for all-sky temperature in background check!' 
1691          write(*,*) 'computeCloudPredictor: platformId=', platformId, &
1692                     ', satelliteId=', satelliteId, ', instrumId=', instrumId
1693          write(*,*) 'computeCloudPredictor: clwObs=', clwObs, &
1694                    ', clwFG=', clwFG
1695          call utl_abort('computeCloudPredictor: not usable to define obs error with CLW')
1696        end if
1697
1698      else if (tvs_isInstrumAllskyHuAssim(instrumId)) then
1699        siObs = obs_headElem_r(obsSpaceData, OBS_SIO, headerIndex)
1700        siFG  = obs_headElem_r(obsSpaceData, OBS_SIB, headerIndex)
1701        cldPredUsed = 0.5D0 * (siObs + siFG)
1702        cldPredUsed_r4 = real(cldPredUsed)
1703
1704        ! check to ensure SI is retrieved and properly set
1705        if (cldPredUsed_r4 < minRetrievableSiValue_r4 .or. &
1706            cldPredUsed_r4 > maxRetrievableSiValue_r4) then
1707          write(*,*) 'This observation should have been rejected ', &
1708                     'for all-sky humidity in background check!' 
1709          write(*,*) 'computeCloudPredictor: platformId=', platformId, &
1710                     ', satelliteId=', satelliteId, ', instrumId=', instrumId
1711          write(*,*) 'computeCloudPredictor: siObs=', siObs, &
1712                    ', siFG=', siFG
1713          call utl_abort('computeCloudPredictor: not usable to define obs error with SI')
1714        end if        
1715
1716      else
1717        call utl_abort('computeCloudPredictor: instrum is not TT or HU allSky assimilation.')
1718      end if
1719
1720    end function computeCloudPredictor
1721
1722  end subroutine oer_fillObsErrors
1723
1724  !--------------------------------------------------------------------------
1725  ! oer_inflateErrAllsky
1726  !--------------------------------------------------------------------------
1727  subroutine oer_inflateErrAllsky(obsSpaceData, bodyIndex, ompOmaObsColumn, beSilent_opt)
1728    !
1729    !:Purpose: Update OBS_OER with inflated state dependant observation error for all-sky
1730    !           temperature/humidity assimilation.
1731    !
1732    implicit none
1733
1734    ! Arguments:
1735    type(struct_obs),  intent(inout) :: obsSpaceData
1736    integer,           intent(in)    :: bodyIndex
1737    integer,           intent(in)    :: ompOmaObsColumn  ! obsSpaceData OBS_OMP or OBS_OMA column
1738    logical, optional, intent(in)    :: beSilent_opt     ! prints extra info to listing if .true.
1739    
1740    ! Locals:
1741    integer :: headerIndex
1742    integer :: channelNumber_withOffset
1743    integer :: channelNumber, channelIndex
1744    integer :: tovsIndex, sensorIndex
1745    logical :: surfTypeIsWater 
1746    real(8) :: clwObs
1747    real(8) :: clwFG
1748    real(8) :: siObs
1749    real(8) :: siFG
1750    real(8) :: deltaE1
1751    real(8) :: deltaE2
1752    real(8) :: ompValue
1753    real(8) :: sigmaObsBeforeInflation
1754    real(8) :: sigmaObsAfterInflation
1755    logical :: chanIsAllskyTt, chanIsAllskyHu
1756    logical :: beSilent
1757
1758    if (present(beSilent_opt)) then
1759      beSilent = beSilent_opt
1760    else
1761      beSilent = .true.
1762    end if
1763
1764    headerIndex = obs_bodyElem_i(obsSpaceData, OBS_HIND, bodyIndex)
1765    tovsIndex = tvs_tovsIndex(headerIndex)
1766    sensorIndex = tvs_lsensor(tovsIndex)
1767
1768    call tvs_getChannelNumIndexFromPPP(obsSpaceData, headerIndex, bodyIndex, &
1769                                        channelNumber, channelIndex)
1770    channelNumber_withOffset = channelNumber + tvs_channelOffset(sensorIndex)
1771
1772    surfTypeIsWater = (tvs_ChangedStypValue(obsSpaceData,headerIndex) == surftype_sea)
1773
1774    call chanIsAllsky(obsSpaceData, bodyIndex, chanIsAllskyTt, chanIsAllskyHu)
1775
1776    if (chanIsAllskyTt) then
1777      if (.not. surfTypeIsWater .or. &
1778          (.not. mwAllskyTtInflateByOmp .and. .not. mwAllskyTtInflateByClwDiff)) then
1779        return
1780      end if
1781
1782      clwObs = obs_headElem_r(obsSpaceData, OBS_CLWO, headerIndex)
1783      clwFG  = obs_headElem_r(obsSpaceData, OBS_CLWB, headerIndex)
1784
1785      sigmaObsBeforeInflation = obs_bodyElem_r(obsSpaceData, OBS_OER, bodyIndex)
1786      ompValue                = obs_bodyElem_r(obsSpaceData, ompOmaObsColumn, bodyIndex)
1787
1788      if (.not. beSilent) then
1789        write(*,*) 'oer_inflateErrAllsky: chanIsAllskyTt', &
1790                          ', headerIndex=', headerIndex, &
1791                          ', sensorIndex=', sensorIndex, &
1792                          ', chan_noOff=', channelNumber_withOffset, &
1793                          ', chan_no=', channelNumber
1794        write(*,*) 'oer_inflateErrAllsky: clwObs=', clwObs, &
1795                          ', clwFG=', clwFG, ', OMP=', ompValue                          
1796      end if
1797
1798      ! error inflation for cloud placement 
1799      deltaE1 = 0.0D0
1800      if (mwAllskyTtInflateByOmp .and. &
1801          ((clwObs - clearCldPredThresh(channelNumber_withOffset,sensorIndex)) * &
1802           (clwFG  - clearCldPredThresh(channelNumber_withOffset,sensorIndex)) < 0) .and. &
1803          abs(clwObs - clwFG) >= 0.005) then
1804        deltaE1 = abs(ompValue)
1805      end if
1806
1807      ! error inflation due to cloud liquid water difference
1808      deltaE2 = 0.0D0
1809      if (mwAllskyTtInflateByClwDiff) then
1810        deltaE2 = inflateErrAllskyCoeff(sensorIndex) * abs(clwObs - clwFG) * &
1811                  sigmaObsBeforeInflation
1812      end if
1813      deltaE2 = min(deltaE2,3.5D0 * sigmaObsBeforeInflation)
1814
1815    else if (chanIsAllskyHu) then
1816      if (.not. surfTypeIsWater .or. &
1817          (.not. mwAllskyHuInflateByOmp .and. .not. mwAllskyHuInflateBySiDiff)) then
1818        return
1819      end if
1820
1821      siObs = obs_headElem_r(obsSpaceData, OBS_SIO, headerIndex)
1822      siFG  = obs_headElem_r(obsSpaceData, OBS_SIB, headerIndex)
1823  
1824      sigmaObsBeforeInflation = obs_bodyElem_r(obsSpaceData, OBS_OER, bodyIndex)
1825      ompValue                = obs_bodyElem_r(obsSpaceData, ompOmaObsColumn, bodyIndex)
1826
1827      if (.not. beSilent) then
1828        write(*,*) 'oer_inflateErrAllsky: chanIsAllskyHu', &
1829                          ', headerIndex=', headerIndex, &
1830                          ', sensorIndex=', sensorIndex, &
1831                          ', chan_noOff=', channelNumber_withOffset, &
1832                          ', chan_no=', channelNumber
1833        write(*,*) 'oer_inflateErrAllsky: siObs=', siObs, &
1834                          ', siFG=', siFG, ', OMP=', ompValue                          
1835      end if      
1836  
1837      ! error inflation for cloud placement 
1838      deltaE1 = 0.0D0
1839      if (mwAllskyHuInflateByOmp .and. &
1840          ((siObs - clearCldPredThresh(channelNumber_withOffset,sensorIndex)) * &
1841           (siFG  - clearCldPredThresh(channelNumber_withOffset,sensorIndex)) < 0) .and. &
1842          abs(siObs - siFG) >= 1.0) then
1843        deltaE1 = abs(ompValue)
1844      end if
1845      
1846      ! error inflation due to scattering-index difference
1847      deltaE2 = 0.0D0
1848      if (mwAllskyHuInflateBySiDiff) then
1849        deltaE2 = inflateErrAllskyCoeff(sensorIndex) * abs(siObs - siFG) * &
1850                  sigmaObsBeforeInflation
1851      end if
1852      deltaE2 = min(deltaE2,3.5D0 * sigmaObsBeforeInflation)      
1853
1854    else
1855      return
1856    end if
1857
1858    sigmaObsAfterInflation = sqrt(sigmaObsBeforeInflation ** 2 + &
1859                             (deltaE1 + deltaE2) ** 2)
1860
1861    if (.not. beSilent) then
1862      write(*,*) 'oer_inflateErrAllsky: deltaE1=', deltaE1, &
1863                        ', deltaE2=', deltaE2
1864      write(*,*) 'oer_inflateErrAllsky: sigmaObs=', &
1865          sigmaObsBeforeInflation, ', sigmaObsInflated=', sigmaObsAfterInflation
1866    end if                             
1867
1868    call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, sigmaObsAfterInflation)
1869
1870  end subroutine oer_inflateErrAllsky
1871
1872  !--------------------------------------------------------------------------
1873  ! chanIsAllsky
1874  !--------------------------------------------------------------------------
1875  subroutine chanIsAllsky(obsSpaceData, bodyIndex, chanIsAllskyTt, chanIsAllskyHu)
1876    !
1877    !:Purpose: Determine if the tovs instrument/channel combination is all-sky.
1878    !
1879    implicit none
1880    
1881    ! Arguments:
1882    type(struct_obs), intent(in)  :: obsSpaceData
1883    integer,          intent(in)  :: bodyIndex
1884    logical,          intent(out) :: chanIsAllskyTt ! .true. if channel is all-sky temperature
1885    logical,          intent(out) :: chanIsAllskyHu ! .true. if channel is all-sky humidity
1886
1887    ! Locals:
1888    integer :: headerIndex
1889    integer :: channelNumber_withOffset
1890    integer :: channelNumber, channelIndex
1891    integer :: tovsIndex, sensorIndex, instrumId
1892
1893    headerIndex = obs_bodyElem_i(obsSpaceData, OBS_HIND, bodyIndex)
1894    tovsIndex = tvs_tovsIndex(headerIndex)
1895    sensorIndex = tvs_lsensor(tovsIndex)
1896    instrumId = tvs_instruments(sensorIndex)
1897
1898    call tvs_getChannelNumIndexFromPPP(obsSpaceData, headerIndex, bodyIndex, &
1899                                       channelNumber, channelIndex)
1900    channelNumber_withOffset = channelNumber + tvs_channelOffset(sensorIndex)
1901
1902    chanIsAllskyTt = .false.
1903    chanIsAllskyHu = .false.
1904    if (.not. tvs_mwAllskyAssim .or. &
1905        .not. useStateDepSigmaObs(channelNumber_withOffset,sensorIndex)) return
1906    
1907    if (tvs_isInstrumAllskyTtAssim(instrumId)) then
1908      chanIsAllskyTt = .true.
1909    end if
1910    if (tvs_isInstrumAllskyHuAssim(instrumId)) then
1911      chanIsAllskyHu = .true.
1912    end if
1913    if (tvs_isInstrumAllskyTtAssim(instrumId) .and. tvs_isInstrumAllskyHuAssim(instrumId)) then
1914      call utl_abort('chanIsAllsky: all-sky TtHu is not yet implemented')
1915    end if
1916
1917  end subroutine chanIsAllsky
1918
1919  !--------------------------------------------------------------------------
1920  ! readOerFromObsFileForSW
1921  !--------------------------------------------------------------------------
1922  subroutine readOerFromObsFileForSW(obsSpaceData)
1923    !
1924    !:Purpose: Read observation errors for AMVs from the obsFiles so as to use
1925    !           the values computed by a previous MIDAS execution (e.g. GDPS).
1926    !
1927    implicit none
1928
1929    ! Arguments:
1930    type(struct_obs), intent(inout) :: obsSpaceData
1931
1932    ! Locals:
1933    character(len=1060) :: filename
1934    type(BURP_FILE)  :: fileIn
1935    type(BURP_BLOCK) :: blkoer
1936    type(BURP_RPT)   :: report
1937    character(len=9)      :: stnid
1938    integer(kind=int_def) :: error, ref_rpt
1939    integer  :: numLevels, numValues, numReports, obsCount
1940    logical  :: fileFound
1941    integer  :: levelIndex, reportIndex, obsIndex
1942    integer  :: uuIndex, vvIndex, headerIndex, bodyIndex, blockIndex, g_btyp_oer
1943    integer  :: bodyIndexBeg, bodyIndexEnd
1944    real(8), allocatable :: uu_oer(:), vv_oer(:)
1945
1946    filename = obsf_getFileName('SW',fileFound)
1947    if (fileFound) then
1948      write(*,*) 'oer_readOerFromObsFileForSW: reading OER from the file: ', trim(filename)
1949    else
1950      write(*,*) 'oer_readOerFromObsFileForSW: no obsfile with SW family, returning'
1951      return
1952    end if
1953
1954    g_btyp_oer  = 1134
1955
1956    call burp_init(report)
1957    call burp_init(blkoer)
1958    call burp_init(fileIn)
1959    call burp_new(fileIn, FILENAME=filename, MODE=FILE_ACC_READ, IOSTAT=error)
1960
1961    !
1962    ! Scans reports
1963    !
1964    call burp_get_property(fileIn, NRPTS = numReports, FILENAME = filename, IOSTAT = error)
1965    ref_rpt  = 0
1966    obsCount = 0
1967
1968    do reportIndex = 1, numReports
1969      ref_rpt = burp_find_report(fileIn, REPORT=report, SEARCH_FROM=ref_rpt, IOSTAT=error)
1970      call burp_get_property(report, ELEV=numLevels, IOSTAT=error)
1971      obsCount = obsCount + numLevels
1972    end do
1973
1974    write(*,*) 'readOerFromObsFileForSW: numReports, obsCount  = ',numReports, obsCount
1975
1976    allocate (uu_oer(obsCount))
1977    allocate (vv_oer(obsCount))
1978
1979    !
1980    ! Read observation errors from file
1981    !
1982    ref_rpt  = 0
1983    obsIndex  = 0
1984
1985    records_in: do
1986
1987      ref_rpt = burp_find_report(fileIn, REPORT=report, SEARCH_FROM=ref_rpt, IOSTAT=error)
1988      if (ref_rpt <0) exit
1989      
1990      call burp_get_property(report, STNID=stnid,ELEV=numLevels, IOSTAT=error)
1991
1992      if (stnid(1:2) == ">>") cycle records_in
1993
1994      blockIndex = 0
1995      blockIndex = burp_find_block(report, block=blkoer, search_from=blockIndex, btyp=g_btyp_oer, bfam=10, convert=.false., iostat=error)    
1996
1997      call burp_get_property(blkoer, NVAL=numValues, IOSTAT=error) 
1998
1999      uuIndex  = burp_find_element(blkoer, ELEMENT=BUFR_NEUU, IOSTAT=error) 
2000      vvIndex  = burp_find_element(blkoer, ELEMENT=BUFR_NEVV, IOSTAT=error) 
2001
2002      if (uuIndex == -1) then
2003        write(*,*) 'readOerFromObsFileForSW: WARNING: wind element not found, skipping report'
2004        cycle records_in
2005      end if
2006      
2007      do levelIndex = 1, numLevels
2008
2009        obsIndex = obsIndex + 1
2010        if (obsIndex > obsCount) call abort('readOerFromObsFileForSW: Something went wrong')
2011        uu_oer(obsIndex) = (burp_get_tblval(blkoer, NELE_IND=uuIndex, NVAL_IND=numValues, NT_IND=levelIndex, IOSTAT=error) - 4096)/10.0
2012        vv_oer(obsIndex) = (burp_get_tblval(blkoer, NELE_IND=vvIndex, NVAL_IND=numValues, NT_IND=levelIndex, IOSTAT=error) - 4096)/10.0
2013
2014      end do
2015
2016    end do records_in
2017
2018    !
2019    ! Clean-up
2020    !
2021    call burp_free(report)
2022    call burp_free(blkoer)
2023    call burp_free(fileIn)
2024
2025    !
2026    ! Fill obsSpaceData with observation errors from SW file
2027    !
2028    obsIndex = 0
2029    HEADER_UU: do headerIndex = 1, obs_numheader(obsSpaceData)
2030      if (obs_getFamily(obsSpaceData,headerIndex_opt=headerIndex) /= 'SW') cycle HEADER_UU
2031      bodyIndexBeg   = obs_headElem_i(obsSpaceData,OBS_RLN,headerIndex)
2032      bodyIndexEnd = obs_headElem_i(obsSpaceData,OBS_NLV,headerIndex) + bodyIndexBeg - 1
2033      do bodyIndex = bodyIndexBeg, bodyIndexEnd
2034        if (obs_bodyElem_i(obsSpaceData,OBS_VNM,bodyIndex) == BUFR_NEUU) then
2035          obsIndex = obsIndex + 1
2036          if (obsIndex > obsCount) call abort('readOerFromObsFileForSW: Something went wrong')
2037          call obs_bodySet_r(obsSpaceData,OBS_OER,bodyIndex,uu_oer(obsIndex))
2038        end if
2039      end do
2040    end do HEADER_UU
2041
2042    obsIndex = 0
2043    HEADER_VV: do headerIndex = 1, obs_numheader(obsSpaceData)
2044      if (obs_getFamily(obsSpaceData,headerIndex_opt=headerIndex) /= 'SW') cycle HEADER_VV
2045      bodyIndexBeg   = obs_headElem_i(obsSpaceData,OBS_RLN,headerIndex)
2046      bodyIndexEnd = obs_headElem_i(obsSpaceData,OBS_NLV,headerIndex) + bodyIndexBeg - 1
2047      do bodyIndex = bodyIndexBeg, bodyIndexEnd
2048        if (obs_bodyElem_i(obsSpaceData,OBS_VNM,bodyIndex) == BUFR_NEVV) then
2049          obsIndex = obsIndex + 1
2050          if (obsIndex > obsCount) call abort('readOerFromObsFileForSW: Something went wrong')
2051          call obs_bodySet_r(obsSpaceData,OBS_OER,bodyIndex,vv_oer(obsIndex))
2052        end if
2053      end do
2054    end do HEADER_VV
2055
2056    deallocate (uu_oer)
2057    deallocate (vv_oer)
2058
2059  end subroutine readOerFromObsFileForSW
2060
2061  !--------------------------------------------------------------------------
2062  ! oer_sw
2063  !--------------------------------------------------------------------------
2064  subroutine oer_sw(columnTrlOnTrlLev,obsSpaceData)
2065    !
2066    !:Purpose: Calculate observation errors for AMVs according to the Met-Office 
2067    !           situation dependant approach.
2068    !
2069    implicit none
2070
2071    ! Arguments:
2072    type(struct_columnData), intent(in)    :: columnTrlOnTrlLev
2073    type(struct_obs),        intent(inout) :: obsSpaceData
2074
2075    ! Locals:
2076    integer :: headerIndex,bodyIndex,ilyr,jlev
2077    integer :: iass,ixtr,ivco,ivnm,iqiv,iqiv1,iqiv2,imet,ilsv,igav,ihav,itrn,J_SAT
2078    real(8) :: zvar,zoer
2079    real(8) :: zwb,zwt,ZOTR,ZMOD
2080    real(8) :: zlat,zlon,zlev,zpt,zpb,zpc
2081    real(8) :: SP_WGH,TO_WGH,TO_DSP,E_VHGT,E_DRIFT,E_HEIGHT
2082    character(len=4) :: varName
2083    character(len=4) :: varLevel
2084    character(len=9) :: cstnid
2085    real(8), pointer :: col_ptr_uv(:)
2086    logical :: passe_once, valeurs_defaut, print_debug
2087    logical, save :: firstCall=.true.
2088
2089    ! If requested, just read oer from the burp file (only 1st time)
2090    if(obsfile_oer_sw) then
2091      if (firstCall) then
2092        call readOerFromObsFileForSW(obsSpaceData)
2093        firstCall = .false.
2094      end if
2095      return
2096    end if
2097    
2098    if(.not. new_oer_sw) return
2099
2100    valeurs_defaut = .false.
2101    passe_once     = .true.
2102    print_debug    = .false.
2103
2104    if (firstCall) write(*,*) "Entering subroutine oer_sw"
2105    firstCall = .false.
2106
2107    call obs_set_current_body_list(obsSpaceData, 'SW')
2108    BODY: do
2109      bodyIndex = obs_getBodyIndex(obsSpaceData)
2110      if (bodyIndex < 0) exit BODY
2111
2112      ! Only process pressure level observations flagged to be assimilated
2113      iass=obs_bodyElem_i (obsSpaceData,OBS_ASS,bodyIndex)
2114      ivco=obs_bodyElem_i (obsSpaceData,OBS_VCO,bodyIndex)
2115      if(iass /= obs_assimilated .or. ivco /= 2) cycle BODY
2116
2117      ixtr = obs_bodyElem_i (obsSpaceData,OBS_XTR,bodyIndex)
2118      ivnm = obs_bodyElem_i (obsSpaceData,OBS_VNM,bodyIndex)
2119      zvar = obs_bodyElem_r (obsSpaceData,OBS_VAR,bodyIndex)
2120      zlev = obs_bodyElem_r (obsSpaceData,OBS_PPP,bodyIndex)
2121      zoer = obs_bodyElem_r (obsSpaceData,OBS_OER,bodyIndex)
2122      headerIndex = obs_bodyElem_i (obsSpaceData,OBS_HIND,bodyIndex)
2123
2124      iqiv1 = obs_headElem_i (obsSpaceData,OBS_SWQ1,headerIndex)
2125      iqiv2 = obs_headElem_i (obsSpaceData,OBS_SWQ2,headerIndex)
2126      imet =  obs_headElem_i (obsSpaceData,OBS_SWMT,headerIndex)
2127      ilsv =  obs_headElem_i (obsSpaceData,OBS_SWLS,headerIndex)
2128      igav =  obs_headElem_i (obsSpaceData,OBS_SWGA,headerIndex)
2129      ihav =  obs_headElem_i (obsSpaceData,OBS_SWHA,headerIndex)
2130      zlat =  obs_headElem_r (obsSpaceData,OBS_LAT,headerIndex)*MPC_DEGREES_PER_RADIAN_R8
2131      zlon =  obs_headElem_r (obsSpaceData,OBS_LON,headerIndex)*MPC_DEGREES_PER_RADIAN_R8
2132      cstnid = obs_elem_c (obsSpaceData,'STID' ,headerIndex)
2133
2134      itrn = ilsv
2135      if(igav == 1) itrn = 2
2136      if(imet >  3) imet = 3
2137      if(ihav /= 4) ihav = 1
2138      ! Use the quality score qiv2, but if it is missing then use qiv1
2139      iqiv = iqiv2
2140      if(iqiv < 0) iqiv = iqiv1
2141
2142      if(valeurs_defaut) then
2143        E_DRIFT  = 2.5
2144        E_HEIGHT = 8000.0
2145      else
2146        E_DRIFT = 7.5 - 0.05*iqiv
2147        call get_height_error(cstnid,imet,itrn,ihav,zlat,zlev,E_HEIGHT,J_SAT)
2148      end if
2149
2150      varLevel = vnl_varLevelFromVarnum(ivnm)
2151      varName  = vnl_varnameFromVarnum(ivnm)
2152
2153      col_ptr_uv=>col_getColumn(columnTrlOnTrlLev,headerIndex,varName)
2154      ilyr = obs_bodyElem_i (obsSpaceData,OBS_LYR,bodyIndex)
2155      ZPT  = col_getPressure(columnTrlOnTrlLev,ilyr  ,headerIndex,varLevel)
2156      ZPB  = col_getPressure(columnTrlOnTrlLev,ilyr+1,headerIndex,varLevel)
2157      ZWB  = LOG(zlev/ZPT)/LOG(ZPB/ZPT)
2158      ZWT  = 1. - ZWB
2159
2160      ZMOD = ZWB*col_ptr_uv(ilyr+1) + ZWT*col_ptr_uv(ilyr)
2161
2162      TO_WGH = 0.0D0
2163      TO_DSP = 0.0D0
2164      if(zlev > 20000. .and. zlev < 30000. .and. passe_once .and. print_debug) then
2165        write(*,'(3a10,2i10,4f12.3)') 'stlchka',cstnid,varName,iqiv,imet,zlev,ZMOD,ZVAR,E_DRIFT
2166      end if
2167      do jlev = 2, col_getNumLev(columnTrlOnTrlLev,'MM') - 1
2168        ZPT= col_getPressure(columnTrlOnTrlLev,jlev-1,headerIndex,varLevel)
2169        ZPC= col_getPressure(columnTrlOnTrlLev,jlev  ,headerIndex,varLevel)
2170        ZPB= col_getPressure(columnTrlOnTrlLev,jlev+1,headerIndex,varLevel)
2171        ZOTR = col_ptr_uv(jlev)
2172        SP_WGH = exp(-0.5*((ZPC - zlev)**2)/(E_HEIGHT**2))*((ZPB - ZPT)/2)
2173        TO_DSP = TO_DSP + SP_WGH*((ZOTR - ZMOD)**2)
2174        TO_WGH = TO_WGH + SP_WGH 
2175        if(zlev > 20000. .and. zlev < 30000. .and. passe_once .and. print_debug) then
2176          write(*,'(a10,i10,4f12.3)') 'stlchk',jlev,ZPT,ZPC,ZPB,ZOTR
2177        end if
2178
2179      end do
2180
2181      E_VHGT = sqrt(TO_DSP/TO_WGH)
2182      zoer = sqrt(E_VHGT**2 + E_DRIFT**2)
2183       
2184      if(zlev > 20000. .and. zlev < 30000. .and. passe_once .and. print_debug) then
2185        write(*,'(a10,4f10.2)') 'stlchkb',zoer,E_VHGT,E_DRIFT,E_HEIGHT
2186        passe_once = .false.
2187      end if
2188
2189      call obs_bodySet_r(obsSpaceData,OBS_OER,bodyIndex,zoer)
2190
2191      if(print_debug) write(*,'(2a10,6f12.3,4i10)') 'hgterr',cstnid,zlat,zlon,zlev/100.,E_HEIGHT/100.0,E_DRIFT,zoer,imet,itrn,ihav,J_SAT
2192      
2193    end do BODY
2194
2195  end subroutine oer_sw
2196
2197  !--------------------------------------------------------------------------
2198  ! get_height_error
2199  !--------------------------------------------------------------------------
2200  subroutine get_height_error(stnid, methode, terrain, htasmet, zlat, zlev, E_HEIGHT, J_SAT)
2201    implicit none
2202
2203    ! Arguments:
2204    character(len=9), intent(in)     :: stnid
2205    integer,          intent(in)     :: methode
2206    integer,          intent(in)     :: terrain
2207    integer,          intent(in)     :: htasmet
2208    integer,          intent(out)    :: J_SAT 
2209    real(8),          intent(in)     :: zlat
2210    real(8),          intent(in)     :: zlev
2211    real(8),          intent(out)    :: E_HEIGHT
2212
2213    ! Locals:
2214    integer, parameter :: NLVLVALUE=9
2215    integer :: I_HGT, jelm, jlev, jcat, i_typ, hemisphere
2216    real(8) :: zlev_hpa, ZWB, ZWT
2217    logical :: interpole
2218
2219    if(zlat >= 0) hemisphere = 1
2220    if(zlat <  0) hemisphere = 2
2221
2222
2223    J_SAT = 1 ! default value
2224
2225    i_typ = 0
2226    list1: do jlev = 1,n_sat_type
2227      do jelm = 2, 10
2228        if(trim(stnid(2:9)) == trim(SAT_AMV(jlev,jelm))) then
2229          i_typ = jlev
2230          exit list1
2231        end if
2232      end do
2233    end do list1
2234
2235    if (i_typ /= 0) then
2236      list2:   do jcat = 1,n_categorie
2237        if(trim(SAT_AMV(i_typ,1)) == trim(SAT_LIST(jcat))) then
2238          if(tbl_m(jcat) == 0 .or. tbl_m(jcat) == methode) then
2239            if(tbl_h(jcat) == 0 .or. tbl_h(jcat) == htasmet) then
2240              if(tbl_t(jcat) == 0 .or. tbl_t(jcat) == terrain+1) then
2241                if(tbl_g(jcat) == 0 .or. tbl_g(jcat) == hemisphere) then
2242                  J_SAT = jcat
2243                  exit list2
2244                end if
2245              end if
2246            end if
2247          end if
2248        end if
2249      end do list2
2250    end if
2251
2252    zlev_hpa  = zlev/100.
2253    interpole = .true.
2254    do I_HGT=1,NLVLVALUE
2255      if(zlev_hpa >= LVLVALUE(I_HGT)) exit
2256    end do
2257    if(I_HGT == 1)            interpole = .false.
2258    if(I_HGT == NLVLVALUE + 1) interpole = .false.
2259    if(I_HGT == NLVLVALUE + 1) I_HGT     = NLVLVALUE
2260
2261    if(interpole) then
2262
2263      ZWB      = LOG(zlev_hpa/LVLVALUE(I_HGT-1))/LOG(LVLVALUE(I_HGT)/LVLVALUE(I_HGT-1))
2264      ZWT      = 1. - ZWB
2265      E_HEIGHT = ZWT*HGT_ERR(J_SAT,I_HGT-1) + ZWB*HGT_ERR(J_SAT,I_HGT)
2266
2267    else
2268
2269      E_HEIGHT = HGT_ERR(J_SAT,I_HGT)
2270      
2271    end if
2272
2273!
2274!   Retourne l'erreur en (Pa)
2275!
2276    E_HEIGHT = E_HEIGHT*100.0
2277
2278  end subroutine get_height_error
2279
2280  !--------------------------------------------------------------------------
2281  ! oer_SETERRGPSRO
2282  !--------------------------------------------------------------------------
2283  SUBROUTINE oer_SETERRGPSRO(columnTrlOnTrlLev, obsSpaceData, beSilent)
2284    !
2285    !:Purpose: Compute estimated errors for GPSRO observations
2286    !
2287    IMPLICIT NONE
2288
2289    ! Arguments:
2290    type(struct_columnData), intent(in)    :: columnTrlOnTrlLev
2291    type(struct_obs),        intent(inout) :: obsSpaceData
2292    logical,                 intent(in)    :: beSilent
2293
2294    ! Locals:
2295    integer headerIndex, IDATYP, bodyIndex, iProfile, varNum
2296    REAL*8 zLat, Lat, sLat
2297    REAL*8 zLon, Lon
2298    REAL*8 zAzm
2299    REAL*8, allocatable :: ZPP(:)
2300    REAL*8, allocatable :: ZTT(:)
2301    REAL*8, allocatable :: ZHU(:)
2302    REAL*8, allocatable :: zHeight(:)
2303    REAL*8, allocatable :: ZUU(:)
2304    REAL*8, allocatable :: ZVV(:)
2305    REAL*8 DH,DDH
2306    integer JL, isat, JH, NGPSLEV, NWNDLEV
2307    REAL*8 zMT, Rad, Geo, zP0
2308    REAL*8 HNH1, HJH, SUM0, SUM1, ZMIN, WFGPS, H1, F2, F3, F4
2309    LOGICAL  ASSIM, L1, L2, L3
2310    integer NH, NH1
2311    TYPE(GPS_PROFILE)           :: PRF
2312    REAL*8       , allocatable :: H   (:),AZMV(:)
2313    REAL*8       , allocatable :: ZOBS(:),ZREF(:),ZOFF(:),ZERR(:), ZMHX(:)
2314    TYPE(GPS_DIFF), allocatable :: RSTV(:)
2315
2316    if (.not. beSilent) write(*,*)'ENTER SETERRGPSRO'
2317    !
2318    !     * 1.  Initializations
2319    !     *     ---------------
2320    !
2321    NGPSLEV=col_getNumLev(columnTrlOnTrlLev,'TH')
2322    NWNDLEV=col_getNumLev(columnTrlOnTrlLev,'MM')
2323    allocate(ZPP (NGPSLEV))
2324    allocate(ZTT (NGPSLEV))
2325    allocate(ZHU (NGPSLEV))
2326    allocate(zHeight (NGPSLEV))
2327    allocate(ZUU (NGPSLEV))
2328    allocate(ZVV (NGPSLEV))
2329    !
2330    allocate(H    (gps_RO_MAXPRFSIZE))
2331    allocate(AZMV (gps_RO_MAXPRFSIZE))
2332    allocate(ZOBS (gps_RO_MAXPRFSIZE))
2333    allocate(ZREF (gps_RO_MAXPRFSIZE))
2334    allocate(ZOFF (gps_RO_MAXPRFSIZE))
2335    allocate(ZERR (gps_RO_MAXPRFSIZE))
2336    allocate(RSTV (gps_RO_MAXPRFSIZE))
2337    allocate(ZMHX (gps_RO_MAXPRFSIZE))
2338    !
2339    !     Loop over all header indices of the 'RO' family:
2340    !
2341    call obs_set_current_header_list(obsSpaceData,'RO')
2342    HEADER: do
2343      headerIndex = obs_getHeaderIndex(obsSpaceData)
2344      if (headerIndex < 0) exit HEADER
2345       !
2346       !     *  Process only refractivity data (codtyp 169)
2347       !
2348      IDATYP = obs_headElem_i(obsSpaceData,OBS_ITY,headerIndex)
2349      IF (IDATYP == 169) then
2350          !
2351          !     *     Scan for requested data values of the profile, and count them
2352          !
2353        ASSIM = .FALSE.
2354        NH = 0
2355        call obs_set_current_body_list(obsSpaceData, headerIndex)
2356        BODY: do 
2357          bodyIndex = obs_getBodyIndex(obsSpaceData)
2358          if (bodyIndex < 0) exit BODY
2359          IF (obs_bodyElem_i(obsSpaceData, OBS_ASS, bodyIndex) == obs_assimilated) then
2360            ASSIM = .TRUE.
2361            NH = NH + 1
2362          end if
2363        end do BODY
2364          !
2365          !     *     If assimilations are requested, prepare and apply the observation operator
2366          !
2367        IF (ASSIM) then
2368          iProfile=gps_iprofile_from_index(headerIndex)
2369          varNum = gps_vRO_IndexPrf(iProfile, 2)
2370             !
2371             !     *        Basic geometric variables of the profile:
2372             !
2373          isat = obs_headElem_i(obsSpaceData,OBS_SAT,headerIndex)
2374          Rad  = obs_headElem_r(obsSpaceData,OBS_TRAD,headerIndex)
2375          Geo  = obs_headElem_r(obsSpaceData,OBS_GEOI,headerIndex)
2376          zAzm = obs_headElem_r(obsSpaceData,OBS_AZA,headerIndex) / MPC_DEGREES_PER_RADIAN_R8
2377          zMT  = col_getHeight(columnTrlOnTrlLev,0,headerIndex,'SF')
2378             !     
2379             !     *        Profile at the observation location:
2380             !
2381          zLat = obs_headElem_r(obsSpaceData,OBS_LAT,headerIndex)
2382          zLon = obs_headElem_r(obsSpaceData,OBS_LON,headerIndex)
2383          Lat  = zLat * MPC_DEGREES_PER_RADIAN_R8
2384          Lon  = zLon * MPC_DEGREES_PER_RADIAN_R8
2385          sLat = sin(zLat)
2386          zMT  = zMT * ec_rg / gps_gravitysrf(sLat)
2387          zP0  = col_getElem(columnTrlOnTrlLev,1,headerIndex,'P0')
2388          DO JL = 1, NGPSLEV
2389                !
2390                !     *           Profile x
2391                !
2392            ZPP(JL) = col_getPressure(columnTrlOnTrlLev,JL,headerIndex,'TH')
2393            ZTT(JL) = col_getElem(columnTrlOnTrlLev,JL,headerIndex,'TT') - MPC_K_C_DEGREE_OFFSET_R8
2394            ZHU(JL) = col_getElem(columnTrlOnTrlLev,JL,headerIndex,'HU')
2395            ZUU(JL) = 0.d0
2396            ZVV(JL) = 0.d0
2397            zHeight(jl) = col_getHeight(columnTrlOnTrlLev,jl,headerIndex,'TH')
2398          end do
2399
2400          if((col_getPressure(columnTrlOnTrlLev,1,headerIndex,'TH') + 1.0d-4)  <  &
2401               col_getPressure(columnTrlOnTrlLev,1,headerIndex,'MM')) then
2402                ! case with top thermo level above top momentum level (Vcode=5002)
2403            do jl = 1, nwndlev
2404              zuu(jl) = col_getElem(columnTrlOnTrlLev,jl, headerIndex, 'UU')
2405              zvv(jl) = col_getElem(columnTrlOnTrlLev,jl, headerIndex, 'VV')
2406            end do
2407          else
2408                ! case without top thermo above top momentum level or unstaggered (Vcode=5001/4/5)
2409            do jl = 1, nwndlev - 1
2410              zuu(jl) = col_getElem(columnTrlOnTrlLev, jl + 1, headerIndex, 'UU')
2411              zvv(jl) = col_getElem(columnTrlOnTrlLev, jl + 1, headerIndex, 'VV')
2412            end do
2413            zuu(nwndlev) = zuu(nwndlev - 1)
2414            zvv(nwndlev) = zuu(nwndlev - 1)
2415          end if
2416          zuu(ngpslev) = zuu(nwndlev)
2417          zvv(ngpslev) = zuu(nwndlev)
2418             !     
2419             !     *        GPS profile structure:
2420             !
2421          call gps_struct1sw_v2(ngpslev,zLat,zLon,zAzm,zMT,Rad,geo,zP0,zPP,zTT,zHU,zUU,zVV,zHeight,prf)
2422             !
2423             !     *        Prepare the vector of all the observations:
2424             !
2425          NH1 = 0
2426             !
2427             !     *        Loop over all body indices for this index_header:
2428             !     *        (start at the beginning of the list)
2429             !
2430          call obs_set_current_body_list(obsSpaceData, headerIndex)
2431          BODY_2: do 
2432            bodyIndex = obs_getBodyIndex(obsSpaceData)
2433            if (bodyIndex < 0) exit BODY_2
2434            IF (obs_bodyElem_i(obsSpaceData, OBS_ASS, bodyIndex) == obs_assimilated) then
2435              NH1      = NH1 + 1
2436              H(NH1)   = obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex)
2437              AZMV(NH1)= zAzm
2438              ZOBS(NH1)= obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex)
2439                   !     *              Reference value:
2440              IF (varNum == bufr_nebd) then
2441                ZREF(NH1) = 0.025d0*exp(-(H(NH1)-Rad)/6500.d0)
2442              ELSE
2443                ZREF(NH1) = 300.d0*exp(-H(NH1)/6500.d0)
2444              end if
2445            end if
2446          end do BODY_2
2447             !
2448             !     *        Apply the observation operator:
2449             !
2450          IF (varNum == bufr_nebd) then
2451            call GPS_BNDOPV1(H, AZMV, NH, PRF, RSTV)
2452          ELSE
2453            call GPS_REFOPV (H,       NH, PRF, RSTV)
2454          end if
2455             !
2456             !     *        Perform the (H(x)-Y)/R operation:
2457             !
2458          DO NH1 = 1, NH
2459            ZMHX(NH1) = RSTV(NH1)%VAR
2460                !
2461                !     *           Normalized offset:
2462                !
2463            IF (.NOT.gps_roBNorm) then
2464              ZOFF(NH1) = (ZOBS(NH1) - ZMHX(NH1)) / ZREF(NH1)
2465            ELSE
2466              ZOFF(NH1) = (ZOBS(NH1) - ZMHX(NH1)) / ZMHX(NH1)
2467            end if
2468          end do
2469             !
2470             !     *        The procedure below is well tested to collectively
2471             !     *        create error profiles from the data profile, and
2472             !     *        intended to be used for these data.
2473             !
2474          DH = 5000.d0
2475          if (varNum == bufr_nebd) then
2476            ZMIN=0.01D0
2477          else
2478            ZMIN=0.002D0
2479          end if
2480
2481          if (varNum == bufr_nerf) then
2482            if (trim(gps_roError) == 'DYNAMIC') then
2483              do NH1 = 1, NH
2484                SUM0=1.d-30
2485                SUM1=0.d0
2486                do JH = 1, NH
2487                  if (H(JH) <= gps_HtpMaxEr) then
2488                    DDH=H(JH)-H(NH1)
2489                    SUM0=SUM0+EXP(-(DDH/DH)**2)
2490                    SUM1=SUM1+EXP(-(DDH/DH)**2)*ZOFF(JH)**2
2491                  end if
2492                end do
2493                ZERR(NH1)=(SUM1/SUM0)**0.5D0
2494                if (ZERR(NH1) < ZMIN) ZERR(NH1) = ZMIN
2495              end do
2496            else if (trim(gps_roError) == 'STATIC_2018') then
2497              ! this was introduced by Maziar in late 2018 on advice by Josep
2498              do NH1 = 1, NH
2499                HNH1 = H(NH1)
2500                ZERR(NH1) = 0.05d0
2501                L1=(HNH1 <= 10000.d0)
2502                L2=(HNH1 > 10000.d0 .and. HNH1 < 30000.d0)
2503                L3=(HNH1 > 30000.d0)
2504                IF (L1) ZERR(NH1)=0.005d0+0.020d0*(10000.d0-HNH1)/10000.d0
2505                IF (L2) ZERR(NH1)=0.005d0
2506                IF (L3) ZERR(NH1)=0.005d0+0.030d0*(HNH1-30000.d0)/30000.d0
2507                if (ZERR(NH1) < ZMIN) ZERR(NH1) = ZMIN
2508              end do
2509            else if (trim(gps_roError) == 'STATIC_2014') then
2510              ! recipe used in EnKF from Josep by email on February 25 2014 
2511              do NH1 = 1, NH
2512                HNH1 = H(NH1)
2513                select case (nint(hnh1))
2514                case(:10000)
2515                  ZERR(NH1) = 0.005 + 0.015 * ((10000.0-hnh1)/10000.0)
2516                case (10001:30000)
2517                  ZERR(NH1) = 0.005
2518                case (30001:)
2519                  ZERR(NH1) = 0.005 + 0.010 * ((hnh1-30000.0)/10000.0)
2520                end select
2521              end do
2522            else
2523              call utl_abort('oer_setErrGPSro: Invalid value for gps_roError')
2524            endif
2525          else
2526            if (trim(gps_roError) == 'DYNAMIC') then
2527              ZMIN = 0.01D0
2528              do NH1 = 1, NH
2529                HNH1=H(NH1)-Rad
2530                SUM0=1.d-30
2531                SUM1=0.d0
2532                DH = 1000.d0 + 0.1d0 * HNH1
2533                do JH = 1, NH
2534                  HJH=H(JH)-Rad
2535                  if (HJH <= gps_HtpMaxEr) then
2536                    DDH=HJH-HNH1
2537                    SUM0=SUM0+EXP(-(DDH/DH)**2+(DDH/DH))
2538                    SUM1=SUM1+EXP(-(DDH/DH)**2+(DDH/DH))*ZOFF(JH)**2
2539                  end if
2540                end do
2541                ZERR(NH1)=(SUM1/SUM0)**0.5D0
2542                if (ZERR(NH1) < ZMIN) ZERR(NH1) = ZMIN
2543                if (H(NH1) < PRF%ast(ngpslev)%Var) ZERR(NH1)=0.08
2544              end do
2545            else if (trim(gps_roError) == 'STATIC_2018') then
2546              do NH1 = 1, NH
2547                ZERR(NH1)=0.05d0
2548                HNH1=H(NH1)-Rad
2549                L1=(HNH1 <= 10000.d0)
2550                L2=(HNH1 > 10000.d0 .and. HNH1 < 30000.d0)
2551                L3=(HNH1 > 30000.d0)
2552                IF (L1) ZERR(NH1)=0.02d0+0.08d0*(10000.d0-HNH1)/10000.d0
2553                IF (L2) ZERR(NH1)=0.02d0
2554                IF (L3) ZERR(NH1)=0.02d0+0.13d0*(HNH1-30000.d0)/30000.d0
2555                IF (isat==3 .or. isat==4 .or. isat==5) ZERR(NH1) = 2*ZERR(NH1)
2556                IF (ZERR(NH1) < ZMIN) ZERR(NH1) = ZMIN
2557              end do
2558            end if
2559          end if
2560
2561          NH1 = 0
2562             !
2563             !     *        Loop over all body indices for this index_header:
2564             !     *        (start at the beginning of the list)
2565             !
2566          call obs_set_current_body_list(obsSpaceData, headerIndex)
2567          BODY_4: do 
2568            bodyIndex = obs_getBodyIndex(obsSpaceData)
2569            if (bodyIndex < 0) exit BODY_4
2570            IF (obs_bodyElem_i(obsSpaceData, OBS_ASS, bodyIndex) == obs_assimilated) then
2571              NH1 = NH1 + 1
2572              H1 = H(NH1)
2573              if (varNum == bufr_nebd) then
2574                 H1 = H1 - Rad
2575                 F2 = 0.5d0*(erf((H1-22000.d0)/5000.d0)+1.d0)
2576              else
2577                 F2 = exp(0.5d0*H1/6500.d0)
2578              endif
2579              F3 = exp(-((H1- 6500.d0)/6500.d0)**2)
2580              F4 = exp(-((H1-14500.d0)/6500.d0)**2)
2581              WFGPS = gps_WGPS(ISAT,1) + gps_WGPS(ISAT,2) * F2 + gps_WGPS(ISAT,3) * F3 + gps_WGPS(ISAT,4) * F4
2582              IF (WFGPS < 1.D0) WFGPS = 1.D0
2583              WFGPS = sqrt(WFGPS)
2584              !
2585                   !     *              Observation error    S
2586                   !
2587              if (.NOT.gps_roBNorm) then
2588                call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, ZERR(NH1)*ZREF(NH1)*WFGPS)
2589              else
2590                call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, ZERR(NH1)*ZMHX(NH1)*WFGPS)
2591              end if
2592            end if
2593          end do BODY_4
2594        end if
2595      end if
2596    end do HEADER
2597
2598    deallocate(RSTV)
2599    deallocate(ZERR)
2600    deallocate(ZOFF)
2601    deallocate(ZREF)
2602    deallocate(ZOBS)
2603    deallocate(AZMV)
2604    deallocate(H)
2605    deallocate(ZMHX)
2606
2607    deallocate(zVV)
2608    deallocate(zUU)
2609    deallocate(zHU)
2610    deallocate(zHeight)
2611    deallocate(zTT)
2612    deallocate(zPP)
2613
2614    if (.not.beSilent) write(*,*)'oer_setErrGPSRO: done'
2615
2616  END SUBROUTINE OER_SETERRGPSRO
2617
2618  !--------------------------------------------------------------------------
2619  ! oer_SETERRGPSGB
2620  !--------------------------------------------------------------------------
2621  SUBROUTINE oer_SETERRGPSGB(columnTrlOnTrlLev, obsSpaceData, beSilent, ldata, analysisMode)
2622    !
2623    !:Purpose:
2624    !
2625    !             - Set the observation errors [OBS_OER] and Std(O-P) [OBS_HPHT] for GB-GPS ZTD data.
2626    !               (GPS surface met obs errors are set before in observation_erreurs_mod.ftn90.
2627    !               The ZTD error is also initialized to the "formal error" or to 1.0 if missing.)
2628    !             - Returns ldata=.false. if there are no GPS ZTD data to assimilate
2629    !               and also sets the modgpsztd_mod variable gps_gb_numZTD = 0.
2630    !
2631    IMPLICIT NONE
2632    !!
2633    !! NOTE: gps_gb_YZDERRWGT IN modgpsztd_mod (FROM NML FILE) IS USED FOR ERROR WEIGHTING
2634    !!       OF TIME SERIES (FGAT) GPS ZTD OBSERVATIONS TO ACCOUNT FOR TEMPORAL ERROR
2635    !!       CORRELATIONS.
2636    !!
2637
2638    ! Arguments:
2639    type(struct_columnData), intent(in)    :: columnTrlOnTrlLev
2640    type(struct_obs)       , intent(inout) :: obsSpaceData
2641    logical                , intent(in)    :: beSilent
2642    logical                , intent(out)   :: ldata
2643    logical                , intent(in)    :: analysisMode
2644
2645    ! Locals:
2646    integer bodyIndex, headerIndex, ityp, iass, IZTDJ, NBRPDATE, ICOUNT, ICOUNT2
2647    integer nlev_T
2648    LOGICAL LLCZTDE, LLFER, LLFZTDE, LLZTD, LLRZTDE, ASSIM, ERRSET, DEBUG, LESTP
2649    LOGICAL LLZWD
2650    character(len=12) :: cstnid
2651    REAL*8  ZTDERR, ZZTD, ZMINZDE, ZPSFC, ZHD, ZWD, ZTDOER, zlev, zval, ZZWD
2652    REAL*8  ZBTSFC, ZBPSFC, ZBZSFC, ZDZ, ZSTDOMP
2653    !
2654    !     ZZDERMIN = MIN ZTD OER VALUE (M)
2655    !     ZZDERMAX = MAX ZTD OER VALUE (M)
2656    !     ZTDERFAC = MULTIPLICATION FACTOR FOR FORMAL ZTD MEASUREMENT ERROR
2657    !     ZOPEFAC  = FRACTION OF REGRESSION EQUATION SD(O-P) TO USE AS ZTD OBSERVATION ERROR
2658    !     ----------------------------------------------------------------------------------
2659    !
2660    REAL*8 ZZDERMIN, ZZDERMAX, ZTDERFAC, ZOPEFAC
2661    DATA ZZDERMIN /0.004D0/
2662    DATA ZZDERMAX /0.030D0/
2663    DATA ZTDERFAC /3.0D0/
2664    DATA ZOPEFAC  /1.0D0/
2665    !
2666    !     FOR ESTIMATION OF PSFC (IF MISSING)
2667    !       ZGAMMA = (NEG. OF) TEMPERATURE LAPSE RATE (K/M)
2668    !
2669    REAL*8 ZGAMMA
2670    DATA ZGAMMA /0.0065D0/
2671
2672    !     ----------------------------------------------------------------------------------
2673    !     LINEAR REGRESSION EQUATION CONSTANTS AND COEFFS FOR ZTD ERROR AND STD(O-P):
2674
2675    !     ZRCONST, ZRCOEFF:
2676    !       - From linear regression of Desroziers error estimates binned by observed ZWD.
2677    !       - Gives ZTDerror (mm) as function of ZWD (m):
2678    !            ZTDerror(mm) = ZRCONST + ZRCOEFF*ZWD(m)
2679    !     ZRCONST2, ZRCOEFF2:
2680    !       - From linear regression of Std(O-P) binned by observed ZWD.
2681    !       - Gives Std(O-P) (mm) as function of ZWD (m):
2682    !            Std(O-P)(mm) = ZRCONST2 + ZRCOEFF2*ZWD(m)
2683    !     ----------------------------------------------------------------------------------
2684    REAL*8 ZRCONST, ZRCOEFF, ZRCONST2, ZRCOEFF2
2685    DATA  ZRCONST  /5.12D0/
2686    DATA  ZRCOEFF  /26.4D0/
2687    DATA  ZRCONST2 /6.67D0/
2688    DATA  ZRCOEFF2 /42.6D0/
2689
2690    if (.not.beSilent) write(*,*) 'ENTER SETERRGPSGB'
2691    !
2692    DEBUG = .FALSE.
2693
2694    LLCZTDE = .FALSE.
2695    LLRZTDE = .FALSE.
2696    LLFZTDE = .FALSE.
2697    IF (gps_gb_YZTDERR  <  0.0D0) then
2698      LLFZTDE = .TRUE.
2699    else if (gps_gb_YZTDERR  >  0.0D0) then
2700      LLCZTDE = .TRUE.
2701    ELSE
2702      LLRZTDE = .TRUE.
2703    end if
2704
2705    nlev_T = col_getNumLev(columnTrlOnTrlLev,'TH')
2706
2707    ldata = .false.
2708    ICOUNT  = 0
2709    ICOUNT2 = 0
2710    !
2711    !     Loop over all header indices of the 'GP' family:
2712    !
2713    call obs_set_current_header_list(obsSpaceData,'GP')
2714    HEADER: do
2715      headerIndex = obs_getHeaderIndex(obsSpaceData)
2716      if (headerIndex < 0) exit HEADER
2717      NBRPDATE  = obs_headElem_i(obsSpaceData, OBS_DAT, headerIndex)
2718      LLZTD     = .FALSE.
2719      LLFER     = .FALSE.
2720      LLZWD     = .FALSE.
2721      ASSIM     = .FALSE.
2722      ERRSET    = .FALSE.
2723      ZZTD      = -1.0D0
2724      ZZWD      = -1.0D0
2725      ZPSFC     = -1.0D0
2726      LESTP     = .FALSE.
2727      ZSTDOMP   = 15.0D0*0.001D0
2728
2729       !   Get Psfc (Pa), Tsfc (K) and model surface height (m) from background profile
2730
2731      ZBPSFC = col_getElem(columnTrlOnTrlLev, 1, headerIndex, 'P0')
2732      ZBTSFC = col_getElem(columnTrlOnTrlLev, nlev_T, headerIndex, 'TT')
2733      ZBZSFC = col_getHeight(columnTrlOnTrlLev, nlev_T, headerIndex, 'TH')
2734       !
2735       !    Loop over all body indices of current report; Set the ZTD error if
2736       !    constant value specified (LLCZTDE=true). Get GPS height and Psfc obs (if any).
2737       !    Get ZTD obs, ZTD formal error and ZWD observation.
2738       !
2739      call obs_set_current_body_list(obsSpaceData, headerIndex)
2740      BODY: do
2741        bodyIndex = obs_getBodyIndex(obsSpaceData)
2742        if (bodyIndex < 0) exit BODY
2743        ityp   = obs_bodyElem_i(obsSpaceData, OBS_VNM, bodyIndex)
2744        iass   = obs_bodyElem_i(obsSpaceData, OBS_ASS, bodyIndex)
2745        zval   = obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex)
2746          !         Store Psfc
2747        IF (ityp == BUFR_NEPS) then
2748          IF (zval  >  0.0D0) ZPSFC = zval
2749        end if
2750          !         Set ZTDOER to constant value (if LLCZTDE); get value of ZTD, 
2751          !         ZTD formal error (OBS_OER) and antenna height (OBS_PPP).
2752        IF (ityp == BUFR_NEZD) then
2753          IF (LLCZTDE) then
2754            ZTDOER = gps_gb_YZTDERR
2755            ERRSET = .TRUE.
2756          end if
2757          zlev   = obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex)
2758          ZTDERR = obs_bodyElem_r(obsSpaceData, OBS_OER, bodyIndex)
2759          IF (ZTDERR  /=  1.0D0) LLFER = .TRUE.
2760          IZTDJ = bodyIndex
2761          IF (zval  >  0.0D0) then
2762            ZZTD = zval
2763            LLZTD = .TRUE.
2764          end if
2765          IF (iass == obs_assimilated) ASSIM = .TRUE.
2766        end if
2767        IF (ityp == BUFR_NEZW) then
2768          IF (zval  >  0.0D0) then
2769            ZZWD = zval
2770            LLZWD = .TRUE.
2771          end if
2772        end if
2773      end do BODY
2774
2775       !      Initialize Std(O-P) to 15 mm  for ZTD observation (for bgck mode)
2776      IF (LLZTD .and. .NOT.analysisMode) &
2777           call obs_bodySet_r(obsSpaceData,OBS_HPHT,IZTDJ,ZSTDOMP)
2778
2779       !      Replace formal ZTD error with real error for all ZTD to be assimilated.
2780       !      Set Std(O-P) as function of ZWD for ZTD observation and store in OBS_HPHT. 
2781
2782      if (ASSIM) then
2783        if (LLZTD) then
2784          ldata = .true.
2785          ICOUNT = ICOUNT + 1
2786          if (LLZWD) then
2787            ZWD = ZZWD
2788          else
2789                !               If Psfc obs is missing, estimate the pressure from background state
2790            if (ZPSFC  <  0.0D0) then
2791              LESTP = .TRUE.
2792              ZDZ = zlev - ZBZSFC
2793              ZPSFC  = ZBPSFC * &
2794                   (1.0D0-(ZGAMMA/ZBTSFC)*ZDZ)**(ec_rg/(MPC_RGAS_DRY_AIR_R8*ZGAMMA))
2795              ICOUNT2 = ICOUNT2 + 1
2796            end if
2797                !                Compute the hydrostatic delay ZHD (m) from Psfc (Pa)
2798            ZHD = 2.2766D-05 * ZPSFC
2799                !               Compute the wet delay (m) from ZTD and ZHD. Avoid negative ZWD.
2800            if (ZHD  >  ZZTD) then
2801              ZWD = 0.0D0
2802            else
2803              ZWD = ZZTD - ZHD
2804            end if
2805          end if
2806             !              Std(O-P) for background check. Limit to 30 mm in case ZTD obs is bad (too high).             
2807          ZSTDOMP = (ZRCONST2 + ZRCOEFF2*ZWD)*0.001D0
2808          ZSTDOMP = MIN(ZZDERMAX, ZSTDOMP)
2809             !             Compute ZTD error as a function of ZWD using regression coeff (SD(O-P) vs ZWD).
2810             !             Take fraction ZOPEFAC of computed error and convert from mm to m.
2811             !             Ensure error is > ZZDERMIN and < ZZDERMAX
2812          IF (.NOT. ERRSET) then 
2813            ZMINZDE = ZRCONST + ZRCOEFF*ZWD
2814            ZMINZDE = ZMINZDE * ZOPEFAC * 0.001D0
2815            IF (LLRZTDE) then
2816              ZTDOER = MAX(ZZDERMIN, ZMINZDE)
2817              ZTDOER = MIN(ZZDERMAX, ZTDOER)
2818            ELSE
2819              IF (LLFER) then
2820                ZTDOER = MAX(ZZDERMIN, ZTDERR*ZTDERFAC)
2821              ELSE
2822                ZTDOER = MAX(ZZDERMIN, ZMINZDE)
2823                ZTDOER = MIN(ZZDERMAX, ZTDOER)
2824              end if
2825            end if
2826                !  Ensure that error is not less than formal error ZTDERR
2827            IF (LLFER) then
2828              IF (DEBUG .and. ICOUNT  <=  50) then
2829                write(*,*) cstnid, &
2830                     ' FORMAL ERR, OBS ERROR (mm) = ', &
2831                     ZTDERR*1000.D0, ZTDOER*1000.D0
2832              end if
2833              ZTDOER = MAX(ZTDOER, ZTDERR)
2834            end if
2835          end if
2836             !  *** APPLY TIME-SERIES WEIGHTING FACTOR TO OBSERVATION ERROR (gps_gb_YZDERRWGT=1 FOR 3D THINNING)
2837          call obs_bodySet_r(obsSpaceData,OBS_OER,IZTDJ, ZTDOER*gps_gb_YZDERRWGT)
2838          IF (.NOT.analysisMode) call obs_bodySet_r(obsSpaceData,OBS_HPHT,IZTDJ, ZSTDOMP)
2839          IF (DEBUG .and. (ICOUNT2  <=  50) .and. LESTP) then
2840            write(*,*) 'TAG    SITE    ZTD    ERROR    ELEV    PSFC    ZWD     STDOMP'
2841            write(*,*) 'ERRDEBUG ', cstnid, &
2842                 ZZTD*1000.D0, ZTDOER*1000.D0, zlev, ZPSFC/100.D0, ZWD*1000.D0, ZSTDOMP*1000.D0
2843          end if
2844        ELSE
2845          call utl_abort('SETERRGPSGB: ERROR:NEGATIVE ZTD VALUE!')
2846        end if
2847      end if
2848
2849       !
2850    end do  HEADER
2851
2852    !      IF (DEBUG) call utl_abort('******DEBUG STOP*******')
2853
2854    IF (.not.ldata) gps_gb_numZTD = 0
2855
2856    IF (ldata .and. .not.beSilent) write(*,*) ' gps_gb_numZTD = ', ICOUNT
2857
2858    if (.not.beSilent) write(*,*) 'EXIT SETERRGPSGB'
2859
2860  END SUBROUTINE OER_SETERRGPSGB
2861
2862  !--------------------------------------------------------------------------
2863  ! oer_setErrBackScatAnisIce
2864  !--------------------------------------------------------------------------
2865  subroutine oer_setErrBackScatAnisIce(obsSpaceData, beSilent, columnTrlOnTrlLev_opt)
2866    !
2867    !:Purpose: Compute estimated errors for ASCAT backscatter anisotropy observations
2868    !
2869    implicit none
2870
2871    ! Arguments:
2872    type(struct_obs),                  intent(inout) :: obsSpaceData
2873    logical,                           intent(in)    :: beSilent
2874    type(struct_columnData), optional, intent(in)    :: columnTrlOnTrlLev_opt
2875
2876    ! Locals:
2877    type(struct_columnData)   :: columnTrlOnTrlLev
2878    type(struct_hco), pointer :: hco_trl => null()
2879    type(struct_vco), pointer :: vco_trl => null()
2880    type(struct_gsv) :: stateVector
2881    integer :: headerIndex, bodyIndex
2882    integer :: idate, imonth, varno
2883    integer :: trackCellNum
2884    real(8) :: conc, obsErrStdDev
2885    character(len=*), parameter :: myName = 'oer_setErrBackScatAnisIce'
2886    character(len=8)            :: ccyymmdd
2887
2888    if (.not. beSilent) write(*,*) 'ENTER '//myName
2889
2890    if (.not.present(columnTrlOnTrlLev_opt)) then
2891      ! Need background ice concentration
2892      call hco_SetupFromFile( hco_trl, './bgSeaIceConc', '', varName_opt='GL' )
2893      call vco_SetupFromFile( vco_trl, './bgSeaIceConc')
2894      call gsv_allocate( stateVector, 1, hco_trl, vco_trl, dateStamp_opt=-1, &
2895                         mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
2896                         dataKind_opt=4, hInterpolateDegree_opt='LINEAR', &
2897                         varNames_opt=(/'GL'/) )
2898      call gsv_zero( stateVector )
2899      call gio_readFromFile( stateVector, './bgSeaIceConc', '', 'P@', &
2900                             unitConversion_opt = .false. )
2901      call col_setVco(columnTrlOnTrlLev, vco_trl)
2902      call col_allocate(columnTrlOnTrlLev, obs_numHeader(obsSpaceData), mpiLocal_opt=.true.)
2903      call s2c_nl( stateVector, obsSpaceData, columnTrlOnTrlLev, hco_trl, &
2904                   timeInterpType='NEAREST' )
2905      call gsv_deallocate( stateVector )
2906    end if
2907
2908    !
2909    !     Loop over all header indices of the 'GL' family:
2910    !
2911    call obs_set_current_header_list(obsSpaceData,'GL')
2912    HEADER: do
2913      headerIndex = obs_getHeaderIndex(obsSpaceData)
2914      if (headerIndex < 0) exit HEADER
2915      call obs_set_current_body_list(obsSpaceData, headerIndex)
2916      BODY: do 
2917        bodyIndex = obs_getBodyIndex(obsSpaceData)
2918        if (bodyIndex < 0) exit BODY
2919        !
2920        !     *  Process only ASCAT backscatter anisotropy observations
2921        !
2922        varno = obs_bodyElem_i(obsSpaceData, OBS_VNM , bodyIndex)
2923        if (varno == BUFR_ICES) then
2924
2925          if (present(columnTrlOnTrlLev_opt)) then
2926            conc = col_getElem(columnTrlOnTrlLev_opt,1,headerIndex,'GL')
2927          else
2928            conc = col_getElem(columnTrlOnTrlLev,1,headerIndex,'GL')
2929          end if
2930          trackCellNum = obs_headElem_i(obsSpaceData, OBS_FOV, headerIndex)
2931          idate = obs_headElem_i(obsSpaceData, OBS_DAT, headerIndex)
2932          write(ccyymmdd, FMT='(i8.8)') idate
2933          read(ccyymmdd(5:6), FMT='(i2)') imonth
2934          obsErrStdDev = SQRT(((1.0-conc)*ascatAnisSigmaOpenWater(trackCellNum,imonth))**2 + &
2935                                          (conc*ascatAnisSigmaIce(trackCellNum,imonth))**2)
2936
2937          call obs_bodySet_r(obsSpaceData, OBS_OER, bodyIndex, obsErrStdDev)
2938
2939        end if
2940      end do BODY
2941    end do HEADER
2942
2943    if (.not.present(columnTrlOnTrlLev_opt)) then
2944      call col_deallocate(columnTrlOnTrlLev)
2945    end if
2946 
2947    if (.not. beSilent) write(*,*) myName//': done'
2948
2949  end subroutine oer_setErrBackScatAnisIce
2950
2951  !--------------------------------------------------------------------------
2952  ! chm_read_obs_err_stddev
2953  !--------------------------------------------------------------------------
2954  subroutine chm_read_obs_err_stddev
2955    !
2956    !:Purpose: To read observation errors from auxiliary file or observation
2957    !          file.
2958    !
2959    implicit none
2960
2961    ! Locals:
2962    integer, parameter :: ndim=1
2963    integer :: istnid
2964
2965    ! read the error std. dev. information from the auxiliary file
2966    call chm_read_obs_err_stddev_file
2967
2968    ! set size of observation sub-space std array
2969    allocate(chm_std%obsStdDev(chm_std%n_stnid))
2970
2971    ! read from the observation file if requested
2972    do istnid=1,chm_std%n_stnid
2973       if (chm_std%source(istnid).ge.1) then
2974          
2975          ! retrieve data from stats blocks (with bkstp=14 and block_type='DATA')
2976          chm_std%obsStdDev(istnid) = obsf_obsSub_read('CH',chm_std%stnids(istnid),chm_std%element(istnid), &
2977                                 chm_std%n_lvl(istnid),ndim,bkstp_opt=14,block_opt='DATA', &
2978                                 match_nlev_opt=chm_std%source(istnid).eq.1)
2979
2980       end if
2981    end do
2982
2983  end subroutine chm_read_obs_err_stddev
2984
2985  !--------------------------------------------------------------------------
2986  ! chm_read_obs_err_stddev_file
2987  !--------------------------------------------------------------------------
2988  subroutine chm_read_obs_err_stddev_file
2989    !
2990    !:Purpose:  To read and store observation error std. dev. as needed for CH
2991    !           family obs.
2992    !
2993    implicit none
2994
2995    ! Locals:
2996    integer :: FNOM, FCLOS
2997    integer :: IERR, JLEV, JELM, nulstat, ios, isize, icount
2998    logical :: LnewExists
2999    character(len=11) :: chemAuxObsDataFile = 'obsinfo_chm'
3000    character (len=128) :: ligne
3001    EXTERNAL FNOM,FCLOS
3002
3003    ! Initialization
3004
3005    chm_std%n_stnid=0
3006
3007    ! CHECK THE EXISTENCE OF THE NEW FILE WITH STATISTICS
3008
3009    INQUIRE(FILE=trim(chemAuxObsDataFile),EXIST=LnewExists)
3010    IF (.not.LnewExists) then
3011      WRITE(*,*) '---------------------------------------------------------------'
3012      WRITE(*,*) 'WARNING! chm_read_obs_err_stddev: auxiliary file ' // trim(chemAuxObsDataFile) 
3013      WRITE(*,*) 'WARNING! not available. Default CH family stddev to be applied if needed.'
3014      WRITE(*,*) '---------------------------------------------------------------'
3015      return
3016    ENDIF
3017
3018    ! Read observation error std dev. from auxiliary file for constituent data
3019
3020    NULSTAT=0
3021    IERR=FNOM(NULSTAT,trim(chemAuxObsDataFile),'SEQ',0)
3022    IF (IERR .EQ. 0) THEN
3023      open(unit=nulstat, file=trim(chemAuxObsDataFile), status='OLD')
3024    ELSE
3025      CALL utl_abort('chm_read_obs_err_stddev_file: COULD NOT OPEN AUXILIARY FILE ' // trim(chemAuxObsDataFile))
3026    ENDIF
3027
3028    ! Read error standard deviations for constituents if available.
3029    ! (CH family; ozone and others)
3030
3031    ios=0
3032    read(nulstat,'(A)',iostat=ios,err=10,end=10) ligne
3033    do while (trim(adjustl(ligne(1:12))).ne.'SECTION I:') 
3034      read(nulstat,'(A)',iostat=ios,err=10,end=10) ligne
3035    end do
3036
3037    ! Read number of observation set sub-families (STNIDs and ...) and allocate space
3038
3039    read(nulstat,*,iostat=ios,err=10,end=10) chm_std%n_stnid
3040    read(nulstat,*,iostat=ios,err=10,end=10) isize
3041
3042    allocate(chm_std%stnids(chm_std%n_stnid))
3043    allocate(chm_std%std_type(chm_std%n_stnid),chm_std%n_lat(chm_std%n_stnid))
3044    allocate(chm_std%source(chm_std%n_stnid),chm_std%ibegin(chm_std%n_stnid))
3045    allocate(chm_std%element(chm_std%n_stnid),chm_std%n_lvl(chm_std%n_stnid))
3046    allocate(chm_std%std1(isize),chm_std%std2(chm_std%n_stnid),chm_std%std3(chm_std%n_stnid))
3047    allocate(chm_std%levels(isize),chm_std%lat(isize))
3048
3049    chm_std%element(:)=0
3050    chm_std%source(:)=0
3051    chm_std%std_type(:)=0
3052    chm_std%n_lvl(:)=1
3053    chm_std%n_lat(:)=1
3054
3055    ! Begin reading for each sub-family
3056    ! Important: Combination of STNID, BUFR element and number of vertical levels
3057    !            to determine association to the observations.
3058
3059    icount=0
3060    STNIDLOOP: do jelm=1,chm_std%n_stnid
3061      chm_std%ibegin(jelm)=icount+1
3062
3063      ! disregard line of dashes
3064      read(nulstat,'(A)',iostat=ios,err=10,end=10) ligne
3065
3066      ! Read STNID (* as wildcard)    
3067      read(nulstat,'(2X,A9)',iostat=ios,err=10,end=10) chm_std%stnids(jelm) 
3068
3069      !   Read (1) BUFR element,
3070      !        (2) Flag indication if EOR provided from this auxiliary file or
3071      !            to be read from the observation file,
3072      !        (3) Index specifying OER setup method,
3073      !        (4) Number of vertical levels
3074      !        (5) Number of latitudes
3075      !
3076      !   Important: Combination of STNID, BUFR element and number of vertical levels
3077      !              to determine association to the observations.
3078
3079      read(nulstat,*,iostat=ios,err=10,end=10) chm_std%element(jelm),chm_std%source(jelm),  &
3080           chm_std%std_type(jelm), chm_std%n_lvl(jelm), chm_std%n_lat(jelm),  &
3081           chm_std%std2(jelm), chm_std%std3(jelm)
3082
3083      if (chm_std%n_lvl(jelm).lt.1) chm_std%n_lvl(jelm)=1
3084      if (chm_std%n_lat(jelm).lt.1) chm_std%n_lat(jelm)=1
3085
3086      if (icount+chm_std%n_lvl(jelm)*chm_std%n_lat(jelm).gt.isize) then
3087        write(*,'(10X,"Max array size exceeded: ",I6)') isize
3088        CALL utl_abort('chm_read_obs_err_stddev_file: PROBLEM READING OBSERR STD DEV.')    
3089      end if
3090
3091      ! disregard line of dashes
3092      read(nulstat,'(A)',iostat=ios,err=10,end=10) ligne
3093
3094      ! disregard data section if not needed
3095      if (chm_std%std_type(jelm).eq.1.or.chm_std%std_type(jelm).eq.2.or.(chm_std%source(jelm).ge.1.and.chm_std%std_type(jelm).eq.0)) &
3096           cycle STNIDLOOP 
3097
3098      if (chm_std%n_lvl(jelm).eq.1.and.chm_std%n_lat(jelm).eq.1) then
3099
3100        ! Read one value only (independent of level and latitude)
3101
3102        icount=icount+1
3103        read(nulstat,*,iostat=ios,err=10,end=10) chm_std%std1(icount)
3104
3105      else if (chm_std%n_lvl(jelm).eq.1.and.chm_std%n_lat(jelm).gt.1) then
3106
3107        !      Value dependent on latitude only
3108
3109        ! Read reference latitudes (must be in order of increasing size)
3110
3111        read(nulstat,*,iostat=ios,err=10,end=10)                      &
3112             chm_std%lat(icount+1:icount+chm_std%n_lat(jelm))
3113
3114        ! Read OER-related values
3115
3116        read(nulstat,*,iostat=ios,err=10,end=10)                 &
3117             chm_std%std1(icount+1:icount+chm_std%n_lat(jelm))
3118
3119        icount=icount+chm_std%n_lat(jelm)
3120
3121      else if (chm_std%n_lvl(jelm).gt.1.and.chm_std%n_lat(jelm).eq.1) then
3122
3123        ! Value dependent on vertical level only
3124
3125        do jlev=1,chm_std%n_lvl(jelm)
3126          icount=icount+1
3127
3128          ! Read vertical level and OER-related value.
3129
3130          read(nulstat,*,iostat=ios,err=10,end=10)                 &
3131               chm_std%levels(icount),chm_std%std1(icount)
3132
3133        end do
3134
3135      else if (chm_std%n_lvl(jelm).gt.1.and.chm_std%n_lat(jelm).gt.1) then
3136
3137        ! Value dependent on vertical level and latitude 
3138
3139        ! Read reference latitudes (must be in order of increasing size)
3140        read(nulstat,*,iostat=ios,err=10,end=10)                      &
3141             chm_std%lat(icount+1:icount+chm_std%n_lat(jelm))
3142        ! write(*, '(10X,20F9.3)') chm_std%lat(icount+1:icount+chm_std%n_lat(jelm))
3143
3144        do jlev=1,chm_std%n_lvl(jelm)
3145
3146          ! Read vertical level and OER-related lat-dependent values.
3147
3148          read(nulstat,*,iostat=ios,err=10,end=10)                   &
3149               chm_std%levels(icount+jlev),                           &
3150               chm_std%std1(icount+(jlev-1)*chm_std%n_lat(jelm)+1:icount+jlev*chm_std%n_lat(jelm))
3151
3152        end do
3153        icount=icount+chm_std%n_lat(jelm)*chm_std%n_lvl(jelm)
3154      end if
3155    end do STNIDLOOP
3156
315710  if (ios.gt.0) then
3158      WRITE(*,*) 'File read error message number: ',ios
3159      CALL utl_abort('chm_read_obs_err_stddev_file: PROBLEM READING OBSERR STD DEV.')    
3160    end if
3161
316211  CLOSE(UNIT=NULSTAT)
3163    IERR=FCLOS(NULSTAT)    
3164
3165  end subroutine chm_read_obs_err_stddev_file
3166
3167  !--------------------------------------------------------------------------
3168  ! chm_obs_err_stddev_index
3169  !--------------------------------------------------------------------------
3170  subroutine chm_obs_err_stddev_index(CSTNID,NLEV,VARNO,ZLAT,ISTNID,JINT)
3171    ! 
3172    !:Purpose: To return the station ID and latitude indices corresponding to a
3173    !          measurement.
3174    !
3175    implicit none
3176
3177    ! Arguments:
3178    character(len=*), intent(in)  :: CSTNID
3179    integer,          intent(in)  :: NLEV
3180    integer,          intent(in)  :: VARNO
3181    real(8),          intent(in)  :: ZLAT
3182    integer,          intent(out) :: ISTNID
3183    integer,          intent(out) :: JINT
3184
3185    ! Locals:
3186    integer                       :: JN,ibegin
3187    real(8)                       :: lat
3188
3189    !  Important: Combination of STNID, BUFR element and number of vertical levels
3190    !             to determine association to the observations.
3191
3192    !             Find stnid with same number of vertical levels and same BUFR element.
3193    !             Note: * in chm_std%stnids stands for a wildcard
3194     
3195    ISTNID=0
3196    DO JN=1,chm_std%n_stnid
3197
3198       ! First compare STNID values allowing for * and blanks in 
3199       ! chm_std%stnids(JN) as wildcards
3200
3201       IF (utl_stnid_equal(chm_std%stnids(JN),CSTNID)) THEN
3202          IF ((NLEV.EQ.chm_std%n_lvl(JN) .OR. chm_std%source(JN).eq.2) .AND. VARNO.EQ.chm_std%element(JN)) THEN
3203             ISTNID=JN
3204             exit
3205          END IF
3206       END IF
3207
3208    END DO
3209
3210    IF (ISTNID.EQ.0) THEN
3211       write(*,*) 'chm_obs_err_stddev_index: Error std. dev. is unavailable for STNID ' // trim(CSTNID) // & 
3212                  ' and NLEV = ' // trim(utl_str(NLEV)) // ' and VARNO = ' // trim(utl_str(VARNO))
3213       write(*,*)
3214       write(*,*) ' Contents of chm_std (n_stnid = ' // trim(utl_str(chm_std%n_stnid)) // '):'
3215       if (chm_std%n_stnid.gt.0) then
3216          write(*,'(A)') '  STNID                 BUFR     NLEVELS'
3217          do jn=1,chm_std%n_stnid
3218             write(*,'(2X,A,2X,I12,2X,I10)') chm_std%stnids(jn),chm_std%element(jn),chm_std%n_lvl(jn)
3219          end do
3220       end if
3221       write(*,*)
3222       call utl_abort('chm_obs_err_stddev_index: Check section I of the auxiliary file.')
3223    ELSE
3224
3225       IF (chm_std%n_lat(ISTNID) .GT. 1) THEN
3226
3227          ! Find latitude index for interpolation.
3228          ! Assuming increasing latitudes in chm_std%lat
3229
3230          lat = zlat / MPC_RADIANS_PER_DEGREE_R8  ! radians to degrees
3231
3232          ibegin=chm_std%ibegin(ISTNID)-1
3233          IF (lat .GE. chm_std%lat(ibegin+chm_std%n_lat(ISTNID))) THEN
3234             JINT=chm_std%n_lat(ISTNID)+1
3235          ELSE
3236             DO JINT=1,chm_std%n_lat(ISTNID)
3237                IF (lat .LE. chm_std%lat(ibegin+JINT)) exit
3238             END DO
3239          END IF
3240                                           
3241       END IF       
3242    END IF         
3243
3244  end subroutine chm_obs_err_stddev_index
3245
3246  !--------------------------------------------------------------------------
3247  ! chm_get_obs_err_stddev
3248  !--------------------------------------------------------------------------
3249  function chm_get_obs_err_stddev(cstnid,nlev,varno,zlat,zlon,idate,itime,zval,&
3250                                  zlev,ilev,ifirst) result(obs_err_stddev) 
3251    ! 
3252    !:Purpose: To return the observational error std dev for a CH family
3253    !          measurement
3254    implicit none
3255   
3256    ! Arguments:
3257    character(len=*), intent(in) :: CSTNID ! station ID
3258    integer,          intent(in) :: NLEV   ! number of levels
3259    integer,          intent(in) :: VARNO  ! BUFR number
3260    real(8),          intent(in) :: ZLAT   ! latitude (radians)
3261    real(8),          intent(in) :: ZLON   ! longitude (radians)
3262    integer,          intent(in) :: IDATE  ! date in YYYYMMDD format
3263    integer,          intent(in) :: ITIME  ! time in HHMM format
3264    real(8),          intent(in) :: ZVAL   ! observation values
3265    real(8),          intent(in) :: ZLEV   ! vertical coordinate value
3266    integer,          intent(in) :: ILEV   ! observation number in the profile
3267    logical,          intent(in) :: IFIRST ! true:  first call for a profile
3268    ! Result:
3269    real(8)  :: obs_err_stddev 
3270
3271    ! Locals:
3272    real(8) :: wgt,zwb,sigma
3273    integer :: ibegin,JLEV,JN,stat
3274    integer, save :: ISTNID,JINT
3275
3276    ! If this call is for the first level for this measurement, get
3277    ! the station ID and latitude indices corresponding to this measurement
3278    if (ifirst) call chm_obs_err_stddev_index(CSTNID,NLEV,VARNO,ZLAT,ISTNID,JINT)                  
3279            
3280    ! Get weighting of error std. dev. if required
3281
3282    if (chm_std%std_type(ISTNID).gt.2 .or. &
3283       (chm_std%source(ISTNID).eq.0 .and. chm_std%std_type(ISTNID).eq.0)) then
3284
3285       IF (chm_std%n_lvl(ISTNID) .GT. 1) THEN
3286                 
3287          ! Find nearest vertical level (no interpolation)
3288                 
3289          zwb=1.E10
3290          ibegin=chm_std%ibegin(ISTNID)-1
3291          DO JN=1,chm_std%n_lvl(ISTNID)
3292             IF (zwb .GT. abs(ZLEV-chm_std%levels(ibegin+JN))) THEN
3293                JLEV=JN
3294                zwb=abs(ZLEV-chm_std%levels(ibegin+JN))
3295             END IF
3296          END DO
3297          JLEV=ibegin+(JLEV-1)*chm_std%n_lat(ISTNID)+1
3298       ELSE
3299          JLEV=chm_std%ibegin(ISTNID)
3300       END IF
3301
3302       IF (chm_std%n_lat(ISTNID) .GT. 1) THEN
3303                
3304          ! Apply interpolation
3305
3306          JLEV=JLEV+JINT-1
3307          ibegin=chm_std%ibegin(ISTNID)-1
3308          IF (JINT.EQ.1.OR.JINT.GT.chm_std%n_lat(ISTNID)) THEN
3309             wgt=chm_std%std1(JLEV)
3310          ELSE
3311             wgt=(chm_std%std1(JLEV-1)*(chm_std%lat(ibegin+JINT)-ZLAT)+ &
3312                  chm_std%std1(JLEV)*(ZLAT-chm_std%lat(ibegin+JINT-1)))/ &
3313                  (chm_std%lat(ibegin+JINT)-chm_std%lat(ibegin+JINT-1))
3314          END IF
3315       ELSE
3316          wgt=chm_std%std1(JLEV)             
3317       END IF
3318         
3319    end if
3320             
3321    ! Set the error std. dev.
3322                   
3323    IF (chm_std%source(ISTNID).EQ.0) THEN
3324               
3325       ! Set error standard deviations from scratch using content of
3326       ! previously read content of the auxiliary file.
3327                
3328       select case(chm_std%std_type(ISTNID))
3329       case(0)
3330          obs_err_stddev = wgt
3331       case(1)
3332          obs_err_stddev = max(chm_std%std3(ISTNID),chm_std%std2(ISTNID)*ZVAL)
3333       case(2)
3334          obs_err_stddev = sqrt(chm_std%std3(ISTNID)**2+(chm_std%std2(ISTNID)*ZVAL)**2)
3335       case(3)
3336          obs_err_stddev = min(chm_std%std3(ISTNID),max(chm_std%std2(ISTNID),wgt*ZVAL))
3337       case(4)
3338          obs_err_stddev = sqrt(chm_std%std2(ISTNID)**2+(wgt*ZVAL)**2)
3339       case default
3340          call utl_abort('chm_get_obs_err_stddev: std_type = ' // trim(utl_str(chm_std%std_type(ISTNID))) // &
3341               ' for STNID = ' // trim(CSTNID) // ' is not recognized.')
3342       end select
3343
3344    ELSE
3345
3346       ! Adjust error standard deviations read from observation file if requested.
3347
3348       sigma = oss_obsdata_get_element(chm_std%obsStdDev(istnid), oss_obsdata_get_header_code(zlon,zlat,idate,itime,cstnid), ilev, stat_opt=stat)
3349
3350       select case(stat)
3351       case(1)
3352          call utl_abort("chm_get_obs_err_stddev: No reports available for STNID = " // trim(cstnid) // &
3353                       ", nlev = " // trim(utl_str(nlev)) // ", varno = " // trim(utl_str(varno)))
3354       case(2)
3355          call utl_abort("chm_get_obs_err_stddev: Report not found for STNID = " // trim(cstnid) // &
3356                       ", nlev = " // trim(utl_str(nlev)) // ", varno = " // trim(utl_str(varno)))
3357       end select
3358
3359       select case(chm_std%std_type(ISTNID))
3360       case(0)
3361          obs_err_stddev = sigma
3362       case(1)
3363          obs_err_stddev = max(chm_std%std3(ISTNID),chm_std%std2(ISTNID)*sigma)
3364       case(2)
3365          obs_err_stddev = sqrt(chm_std%std3(ISTNID)**2+(chm_std%std2(ISTNID)*sigma)**2)
3366       case(3)
3367          obs_err_stddev = min(chm_std%std3(ISTNID),max(chm_std%std2(ISTNID),wgt*sigma))
3368       case(4)
3369          obs_err_stddev = sqrt(chm_std%std2(ISTNID)**2+(wgt*sigma)**2)
3370       case default
3371          call utl_abort('chm_get_obs_err_stddev: std_type = ' // trim(utl_str(chm_std%std_type(ISTNID))) // &
3372               ' for STNID = ' // trim(CSTNID) // ' is not recognized.')
3373       end select
3374       
3375    END IF
3376    
3377  end function chm_get_obs_err_stddev
3378
3379  !--------------------------------------------------------------------------
3380  ! chm_dealloc_obs_err_stddev
3381  !--------------------------------------------------------------------------
3382  subroutine chm_dealloc_obs_err_stddev
3383    ! 
3384    !:Purpose: To deallocate temporary storage space used for observation errors
3385    !          for the CH family.
3386    !
3387    implicit none
3388
3389    ! Locals:
3390    integer :: istnid
3391
3392    if (chm_std%n_stnid.eq.0) return
3393    
3394    if (allocated(chm_std%obsStdDev)) then
3395       do istnid=1,chm_std%n_stnid
3396          if (chm_std%source(istnid).ge.1) call oss_obsdata_dealloc(chm_std%obsStdDev(istnid))
3397       end do
3398       deallocate(chm_std%obsStdDev)       
3399    end if
3400
3401    if (allocated(chm_std%stnids))   deallocate(chm_std%stnids)
3402    if (allocated(chm_std%n_lvl))    deallocate(chm_std%n_lvl)
3403    if (allocated(chm_std%std_type)) deallocate(chm_std%std_type)
3404    if (allocated(chm_std%ibegin))   deallocate(chm_std%ibegin)
3405    if (allocated(chm_std%element))  deallocate(chm_std%element)
3406    if (allocated(chm_std%source))   deallocate(chm_std%source)
3407    if (allocated(chm_std%n_lat))    deallocate(chm_std%n_lat)
3408    if (allocated(chm_std%std1))     deallocate(chm_std%std1)
3409    if (allocated(chm_std%std2))     deallocate(chm_std%std2)
3410    if (allocated(chm_std%std3))     deallocate(chm_std%std3)
3411    if (allocated(chm_std%levels))   deallocate(chm_std%levels)
3412    if (allocated(chm_std%lat))      deallocate(chm_std%lat)
3413
3414  end subroutine chm_dealloc_obs_err_stddev
3415
3416  !--------------------------------------------------------------------------
3417  ! oer_getSSTdataParam_char
3418  !--------------------------------------------------------------------------
3419  function oer_getSSTdataParam_char(item, itemIndex) result(value)
3420    !
3421    !:Purpose: get character item value from SSTdataParams derived type
3422    !
3423    implicit none
3424    
3425
3426    ! Arguments:
3427    character(len=*), intent(in) :: item
3428    integer         , intent(in) :: itemIndex
3429    ! Result:
3430    character(len=20) :: value 
3431
3432    select case(trim(item))      
3433      case('dataType')
3434        value = SSTdataParams(itemIndex)%dataType
3435      case('instrument')
3436        value = SSTdataParams(itemIndex)%instrument
3437      case('sensor')
3438        value = SSTdataParams(itemIndex)%sensor
3439      case('sensorType')
3440        value = SSTdataParams(itemIndex)%sensorType
3441      case default
3442        call utl_abort('oer_getSSTdataParam_char: invalid item '//(trim(item)))
3443    end select
3444
3445  end function oer_getSSTdataParam_char
3446
3447  !--------------------------------------------------------------------------
3448  ! oer_getSSTdataParam_int
3449  !--------------------------------------------------------------------------
3450  function oer_getSSTdataParam_int(item, itemIndex_opt) result(value)
3451    !
3452    !:Purpose: get integer item value from SSTdataParams derived type
3453    !
3454    implicit none
3455
3456    ! Arguments:
3457    character(len=*),  intent(in) :: item
3458    integer, optional, intent(in) :: itemIndex_opt
3459    ! Result:
3460    integer :: value 
3461
3462    if (present(itemIndex_opt)) then
3463      select case(trim(item))      
3464        case('codeType')
3465          value = SSTdataParams(itemIndex_opt)%codeType
3466        case default
3467          call utl_abort('oer_getSSTdataParam_int: invalid item '//(trim(item)))
3468      end select
3469    else
3470      select case(trim(item))
3471        case('maxNumberSSTDatasets')
3472          value = maxNumberSSTDatasets
3473        case('numberSSTDatasets')
3474          value = numberSSTDatasets
3475        case default
3476          call utl_abort('oer_getSSTdataParam_int: invalid item '//(trim(item)))
3477      end select
3478
3479    end if
3480
3481  end function oer_getSSTdataParam_int
3482
3483  !--------------------------------------------------------------------------
3484  ! oer_getSSTdataParam_R8
3485  !--------------------------------------------------------------------------
3486  function oer_getSSTdataParam_R8(item, itemIndex) result(value)
3487    !
3488    !:Purpose: get real(8) item value from SSTdataParams derived type
3489    !
3490    implicit none
3491
3492    ! Arguments:
3493    character(len=*), intent(in) :: item
3494    integer         , intent(in) :: itemIndex
3495    ! Result:
3496    real(8) :: value 
3497
3498    select case(trim(item))      
3499      case('dayError')
3500        value = SSTdataParams(itemIndex)%dayError
3501      case('nightError')
3502        value = SSTdataParams(itemIndex)%nightError
3503      case default
3504        call utl_abort('oer_getSSTdataParam_R8: invalid item '//(trim(item)))
3505    end select
3506
3507  end function oer_getSSTdataParam_R8
3508
3509end module obsErrors_mod