varQC_mod sourceΒΆ

  1module varQC_mod
  2  ! MODULE varQC_mod (prefix='vqc' category='1. High-level functionality')
  3  !
  4  !:Purpose:  Procedures related to variational quality control including
  5  !           hard-coded values that determine how quickly the observation
  6  !           weight begins to be reduced.
  7  !
  8  use MathPhysConstants_mod
  9  use earthConstants_mod
 10  use codtyp_mod
 11  use bufr_mod
 12  use obsSpaceData_mod
 13  use columnData_mod
 14  use rmatrix_mod ,only : rmat_lnondiagr
 15  use varNameList_mod
 16  use obsFamilyList_mod
 17  
 18  implicit none
 19  save
 20  private
 21
 22  ! public procedures
 23  public :: vqc_setup, vqc_NlTl, vqc_ad, vqc_listrej
 24
 25
 26  contains
 27
 28  subroutine vqc_setup(obsSpaceData)
 29    !
 30    !:Purpose: To set certain parameters for the asymmetric check
 31    !           and for variational quality control
 32    !
 33    implicit none
 34
 35    ! Arguments:
 36    type(struct_obs), intent(inout) :: obsSpaceData ! obsSpaceData object
 37
 38    ! Locals:
 39    integer jdata, jjo, idata, idatend, idburp
 40    integer ityp, iass, iother, jj, istyp, ilev
 41    real(8) zagz, zahu, zatt, zauv, zabt, zabtb, zapn, zaps, zazd, zach, zatm, zaice, zapr
 42    real(8) zdgz, zdhu, zdtt, zduv, zdbt, zdbtb, zdpn, zdps, zdzd, zdch, zdtm, zdice, zdpr
 43    real(8) zattra, zauvra, zattsym, zdvis, zavis
 44    real(8) zslev, zlev, zval, zspdo, zspdf, zofcst, zoval, zdiff, zaasym, zoer
 45    real(8) zfcst, zlat, zlon, zprior
 46    logical llok
 47
 48    !
 49    !_____prior probabilities for winds:ZAUV
 50    !     prior probabilities for scalar variables: zagz and zahu
 51    !     standard deviation multiple for background check for winds: zduv
 52    !     standard deviation multiple for background check for heights: zdgz
 53    !     standard deviation multiple for background check for humidity: zdhu
 54    !
 55    zagz  = 1.0d-12
 56!    zahu  = 1.0d-2
 57    zahu  = 5.0d-2
 58    zatt  = 5.0d-2
 59!    zauv  = 1.0d-2
 60    zauv  = 2.0d-2
 61    zabt  = 1.0d-1
 62    zabtb = 1.0d-1
 63    zapn  = 1.0d-4
 64    zaps  = 1.0d-4
 65    zazd  = 2.0d-2
 66    zach  = 1.0d-3
 67    zatm  = 1.0d-12
 68    zaice = 0.0d0
 69    zapr  = 0.0d0
 70
 71    zattra = 0.005d0
 72    zattsym = 1.d-1
 73    zauvra = 1.d-5
 74    zavis= 1.0d-3
 75
 76    zdgz  = 5.d0
 77    zdhu  = 5.d0
 78    zdtt  = 5.d0
 79    zduv  = 5.d0
 80    zdbt  = 3.d0
 81    zdbtb = 3.d0
 82    zdpn  = 5.d0
 83    zdps  = 5.d0
 84    zdzd  = 3.d0
 85    zdch  = 1.d1
 86    zdtm  = 5.d0
 87    zdice = 5.d2
 88    zdpr  = 5.d2
 89    zdvis= 5.d0
 90
 91    do jjo = 1, obs_numheader(obsSpaceData)
 92
 93       IDATA   = obs_headElem_i( obsSpaceData, OBS_RLN, jjo )
 94       IDATEND = obs_headElem_i( obsSpaceData, OBS_NLV, jjo ) + IDATA - 1
 95       IDBURP  = obs_headElem_i( obsSpaceData, OBS_ITY, jjo )
 96       ZLAT    = obs_headElem_r( obsSpaceData, OBS_LAT, jjo ) * MPC_DEGREES_PER_RADIAN_R8
 97       ZLON    = obs_headElem_r( obsSpaceData, OBS_LON, jjo ) * MPC_DEGREES_PER_RADIAN_R8
 98
 99       do JDATA = IDATA, IDATEND
100
101          ityp  = obs_bodyElem_i( obsSpaceData, OBS_VNM, JDATA )
102          IASS  = obs_bodyElem_i( obsSpaceData, OBS_ASS, JDATA )
103          ZLEV  = obs_bodyElem_r( obsSpaceData, OBS_PPP, JDATA ) * MPC_MBAR_PER_PA_R8
104          ZOER  = obs_bodyElem_r( obsSpaceData, OBS_OER, JDATA )
105          ZVAL  = obs_bodyElem_r( obsSpaceData, OBS_VAR, JDATA )
106
107          ZFCST = ZVAL - obs_bodyElem_r( obsSpaceData, OBS_OMP,JDATA)
108
109          if (ityp == BUFR_NETS .or. ityp == BUFR_NEPS .or.  &
110              ityp == BUFR_NEPN .or. ityp == BUFR_NESS .or.  &
111              ityp == BUFR_NEUS .or. ityp == BUFR_NEVS .or.  &
112              ityp == BUFR_NEZD .or. ityp == bufr_vis  .or.  &
113              ityp == bufr_logVis .or. ityp == bufr_gust .or. &
114              ityp == bufr_radarPrecip .or. ityp == bufr_logRadarPrecip ) then
115             LLOK = (obs_bodyElem_i(obsSpaceData,OBS_ASS,JDATA) == obs_assimilated)
116          else
117             LLOK = (IASS == obs_assimilated) .and. ((obs_bodyElem_i(obsSpaceData,OBS_XTR,JDATA) ==0) &
118                .or. ((obs_bodyElem_i(obsSpaceData,OBS_XTR,JDATA) == 2) .and. &
119                      (obs_bodyElem_i(obsSpaceData,OBS_VNM,JDATA) == BUFR_NEGZ)))
120          end if
121
122          if (LLOK) then
123             if (ityp == BUFR_NEUU .or. ityp == BUFR_NEVV .or.  &
124                 ityp == BUFR_NEUS .or. ityp == BUFR_NEVS) then
125                ZAASYM = 1.0d0
126                IOTHER = -1
127                if (ityp == BUFR_NEUU .or. ityp == BUFR_NEUS) then
128                  do JJ = IDATA, JDATA
129                    ISTYP = obs_bodyElem_i( obsSpaceData, OBS_VNM, JJ )
130                    ZSLEV = obs_bodyElem_r( obsSpaceData, OBS_PPP, JJ ) * MPC_MBAR_PER_PA_R8
131                    if ((ISTYP == BUFR_NEVV .or. ISTYP == BUFR_NEVS)  &
132                         .and. ZLEV == ZSLEV) then
133                      IOTHER = JJ
134                    end if
135                  end do
136                else
137                  do JJ=IDATA,JDATA
138                    ISTYP = obs_bodyElem_i(obsSpaceData,OBS_VNM,JJ)
139                    ZSLEV = obs_bodyElem_r(obsSpaceData,OBS_PPP,JJ)*MPC_MBAR_PER_PA_R8
140                    if ((ISTYP == BUFR_NEUU .or. ISTYP == BUFR_NEUS)  &
141                         .and. ZLEV == ZSLEV) then
142                      IOTHER = JJ
143                    end if
144                  end do
145                end if
146                if (IOTHER /= -1) then
147                  ZOER = obs_bodyElem_r(obsSpaceData,OBS_OER,JDATA)
148                  ZOVAL = obs_bodyElem_r(obsSpaceData,OBS_VAR,IOTHER)
149                  ZOFCST = ZOVAL-obs_bodyElem_r(obsSpaceData,OBS_OMP,IOTHER)
150                  ZSPDO = SQRT(ZOVAL*ZOVAL + ZVAL*ZVAL)
151                  ZSPDF = SQRT(ZOFCST*ZOFCST + ZFCST*ZFCST)
152                  ZDIFF = ZSPDO - ZSPDF
153                  ILEV = NINT(ZLEV)
154                  !
155                  !___tighten rejection criterion for satob winds
156                  !
157                  if (IDBURP == 88 .or. IDBURP == 188) then
158                    if (ZDIFF < 0.0d0 .and. ABS(ZLAT) > 20.d0 .and.  &
159                         ILEV < 550) then
160                       ZAASYM = 0.7d0*MAX(ZSPDF,1.0d0)
161                       ZPRIOR = ZAASYM*ZAUV
162                       if (ZPRIOR > 0.99d0) ZAASYM = 0.99d0/ZAUV
163                    else
164                       !
165                       !___zaasym used to specify new criterion for satwinds
166                       !   than are not included in the asymmetric test
167                       !
168                       ZAASYM = 10.d0
169                    end if
170                  end if
171
172                end if
173                !
174                !__INITIAL VALUE OF GAMMA FOR QCVAR (WINDS)
175                !
176                call obs_bodySet_r(obsSpaceData,OBS_POB,JDATA,(1.d0 -  &
177                   (1.d0-(ZAUV*ZAASYM))*(1.d0-(ZAUV*ZAASYM)))*(2.d0*MPC_PI_r8)/  &
178                  ((1.d0-(ZAUV*ZAASYM))*(1.d0-(ZAUV*ZAASYM))*  &
179                                    (2.d0*ZDUV)*(2.d0*ZDUV)))
180                if ((IDBURP >= 32  .and. IDBURP <= 38) .or.  &
181                   (IDBURP >= 135 .and. IDBURP <= 142) .or.  &
182                   (IDBURP == 132) )  &
183                call obs_bodySet_r(obsSpaceData,OBS_POB,JDATA,  &
184                       (1.d0 - (1.d0-ZAUVRA)*(1.d0-ZAUVRA))  &
185                       *(2.d0*MPC_PI_R8)/((1.d0-ZAUVRA)*(1.d0-ZAUVRA)*  &
186                                   (2.d0*ZDUV)*(2.d0*ZDUV)))
187             else if (ityp == BUFR_NEGZ) then
188                !
189                ! INITIAL VALUE OF GAMMA FOR QCVAR (HEIGHTS)
190                !
191                call obs_bodySet_r(obsSpaceData,OBS_POB,JDATA,(ZAGZ*  &
192                    SQRT(2.d0*MPC_PI_R8))/((1.d0-ZAGZ)*(2.d0*ZDGZ)))
193             else if (ityp == BUFR_NETT) then
194                !
195                ! INITIAL VALUE OF GAMMA FOR QCVAR (TEMPERATURES)
196                !
197                call obs_bodySet_r(obsSpaceData,OBS_POB,JDATA,(ZATT*  &
198                    SQRT(2.d0*MPC_PI_R8))/((1.d0-ZATT)*(2.d0*ZDTT)))
199                if ((IDBURP >= 32  .and. IDBURP <= 38) .or.  &
200                    (IDBURP >= 135 .and. IDBURP <= 142) .or.  &
201                    (IDBURP == 132) )  &
202                  call obs_bodySet_r(obsSpaceData,OBS_POB,JDATA,(ZATTRA*  &
203                    SQRT(2.d0*MPC_PI_R8))/((1.d0-ZATTRA)*(2.d0*ZDTT)))
204             else if (ityp == BUFR_NETS) then
205                !
206                ! INITIAL VALUE OF GAMMA FOR QCVAR (SCREEN-LEVEL TEMPERATURES)
207                !
208                call obs_bodySet_r(obsSpaceData,OBS_POB,JDATA,(ZATT*  &
209                    SQRT(2.d0*MPC_PI_R8))/((1.d0-ZATT)*(2.d0*ZDTT)))
210                !
211                ! ASYMMETRIC TEST FOR SHIP TEMPERATURES
212                !
213                if ((IDBURP == 13) .and. (ZVAL > ZFCST)) then
214                  call obs_bodySet_r(obsSpaceData,OBS_POB,JDATA,(ZATTSYM*  &
215                      SQRT(2.d0*MPC_PI_R8))/((1.d0-ZATTSYM)*(2.d0*ZDTT)))
216                end if
217             else if (ityp == BUFR_NEPN) then
218                !
219                ! INITIAL VALUE OF GAMMA FOR QCVAR (MSL PRESSURE)
220                !
221                call obs_bodySet_r(obsSpaceData,OBS_POB,JDATA,(ZAPN*  &
222                    SQRT(2.d0*MPC_PI_R8))/((1.d0-ZAPN)*(2.d0*ZDPN)))
223             else if (ityp == BUFR_NEPS) then
224                !
225                ! INITIAL VALUE OF GAMMA FOR QCVAR (STATION PRESSURE)
226                !
227                call obs_bodySet_r(obsSpaceData,OBS_POB,JDATA,(ZAPS*  &
228                    SQRT(2.d0*MPC_PI_R8))/((1.d0-ZAPS)*(2.d0*ZDPS)))
229             else if ( ityp == 12062 .or. ityp == 12063 .or.  &
230                      ityp == 12163) then
231                !
232                ! INITIAL VALUE OF GAMMA FOR BRIGHTNESS TEMPERATURES
233                ! TOVS AMSU-A + AIRS + IASI + GEORAD !!!!! 
234                !
235                call obs_bodySet_r(obsSpaceData,OBS_POB,JDATA,(ZABT*  &
236                    SQRT(2.d0*MPC_PI_R8))/((1.d0-ZABT)*(2.d0*ZDBT)))
237                if (IDBURP == 181 .or. IDBURP == 182 .or. &
238                    IDBURP == 200) then
239                  !
240                  ! INITIAL VALUE OF GAMMA FOR TOVS AMSU-B (181) AND MHS (182)
241                  ! AND MWHS2(200)
242                  !
243                  call obs_bodySet_r(obsSpaceData,OBS_POB,JDATA,(ZABTB*  &
244                        SQRT(2.d0*MPC_PI_R8))/((1.d0-ZABTB)*(2.d0*ZDBTB)))
245                end if
246             else if (ityp == BUFR_NEZD) then
247                !
248                ! INITIAL VALUE OF GAMMA FOR QCVAR (GPS ZENITH DELAY)
249                !
250                call obs_bodySet_r(obsSpaceData,OBS_POB,JDATA,(ZAZD*  &
251                    SQRT(2.d0*MPC_PI_R8))/((1.d0-ZAZD)*(2.d0*ZDZD)))
252             else if (bufr_IsAtmosConstituent(ityp)) then
253                !
254                ! INITIAL VALUE OF GAMMA FOR THE CH (constituents) family
255                !
256                  call obs_bodySet_r(obsSpaceData,OBS_POB,JDATA,(ZACH* &
257                       SQRT(2.d0*MPC_PI_R8))/((1.d0-ZACH)*(2.d0*ZDCH)))
258             else if (ityp == bufr_sst ) then
259                !
260                ! INITIAL VALUE OF GAMMA FOR SST
261                !
262                call obs_bodySet_r( obsSpaceData, OBS_POB, JDATA, ( zatm *  &
263                    sqrt( 2.d0 * MPC_PI_R8 )) / (( 1.d0 - zatm ) * ( 2.d0 * zdtm )))
264             else if (ityp == BUFR_ICEC ) then
265                !
266                ! INITIAL VALUE OF GAMMA FOR SEA ICE
267                !
268                call obs_bodySet_r( obsSpaceData, OBS_POB, JDATA, ( zaice *  &
269                    sqrt( 2.d0 * MPC_PI_R8 )) / (( 1.d0 - zaice ) * ( 2.d0 * zdice )))
270             else if (ityp == BUFR_radarPrecip .or. ityp == BUFR_logRadarPrecip ) then
271                !
272                ! INITIAL VALUE OF GAMMA FOR RADAR PRECIPITATION
273                !
274                call obs_bodySet_r( obsSpaceData, OBS_POB, JDATA, ( zapr *  &
275                    sqrt( 2.d0 * MPC_PI_R8 )) / (( 1.d0 - zapr ) * ( 2.d0 * zdpr )))
276             else if (ityp == bufr_gust ) then
277                !
278                ! INITIAL VALUE OF GAMMA FOR WIND GUST
279                !
280                call obs_bodySet_r( obsSpaceData, OBS_POB, JDATA, ( zauv *  &
281                    sqrt( 2.d0 * MPC_PI_R8 )) / (( 1.d0 - zauv ) * ( 2.d0 * zduv )))
282              else if (ityp == bufr_vis .or. ityp == bufr_logVis) then
283                !
284                ! INITIAL VALUE OF GAMMA FOR VISIBILITY
285                !
286                call obs_bodySet_r( obsSpaceData, OBS_POB, JDATA, ( zavis *  &
287                    sqrt( 2.d0 * MPC_PI_R8 )) / (( 1.d0 - zavis ) * ( 2.d0 * zdvis )))
288             else
289                !
290                ! INITIAL VALUE OF GAMMA FOR QCVAR (OTHERS)
291                !
292                call obs_bodySet_r(obsSpaceData,OBS_POB,JDATA,(ZAHU*  &
293                    SQRT(2.d0*MPC_PI_R8))/((1.d0-ZAHU)*(2.d0*ZDHU)))
294             end if
295          end if
296       end do
297
298    end do
299
300  end subroutine vqc_setup
301
302
303  subroutine vqc_NlTl(obsSpaceData)
304    !
305    !:Purpose: 1) Modify Jo [OBS_JOBS] according to
306    !              Andersson and Jarvinen 1999, Variational quality control,
307    !              Q.J.R., 125, pp. 697-722.
308    !           2) Save the values of (1-Wqc) in OBS_QCV
309    !              for gradient factorization and postalt flag criterion.
310    !
311    implicit none
312
313    ! Arguments:
314    type(struct_obs), intent(inout) :: obsSpaceData ! obsSpaceData object
315
316    ! Locals:
317    integer :: index_body,istyp,jj,index_header,ityp,index_body_start
318    real*8 :: zgami,zjon,zqcarg,zppost,zlev,zslev
319    logical :: lluv
320    logical :: includeFlag
321
322    BODY: do index_body = 1, obs_numbody(obsSpaceData)
323      includeFlag = (obs_bodyElem_i(obsSpaceData,OBS_ASS,index_body) == obs_assimilated).and.  &
324                    (obs_getFamily(obsSpaceData,bodyIndex_opt=index_body).ne.'RO')
325      ! pas de qcvar pour  les radiances en mode matrice R non diagonale
326      if (rmat_lnondiagr) includeFlag = includeFlag .and.  &
327        (obs_getFamily(obsSpaceData,bodyIndex_opt=index_body) /= 'TO') 
328
329      if (includeFlag) then
330        index_header = obs_bodyElem_i(obsSpaceData,OBS_HIND,index_body)
331        index_body_start = obs_headElem_i(obsSpaceData,OBS_RLN,INDEX_HEADER)
332        ZLEV = obs_bodyElem_r(obsSpaceData,OBS_PPP,INDEX_BODY)
333        zgami = obs_bodyElem_r(obsSpaceData,OBS_POB,index_body)
334        ityp = obs_bodyElem_i(obsSpaceData,OBS_VNM,INDEX_BODY)
335        LLUV = ((ityp == BUFR_NEUU .or. ityp == BUFR_NEUS) .and.  &
336               col_varExist(varName='UU')) .or. ((ityp == BUFR_NEVV .or.  &
337               ityp == BUFR_NEVS).and.col_varExist(varName='VV'))
338        if (LLUV) then
339          if (ityp == BUFR_NEUU .or. ityp == BUFR_NEUS)then
340            !
341            ! In order to calculate the contribution to Jo from a wind, the o-a
342            ! must be available for both u and v components. Hence, loop over only
343            ! data for which o-a has already been calculated
344            !
345            do JJ=INDEX_BODY_START, INDEX_BODY
346              ISTYP = obs_bodyElem_i(obsSpaceData,OBS_VNM,JJ)
347              ZSLEV = obs_bodyElem_r(obsSpaceData,OBS_PPP,JJ)
348              if ((ISTYP == BUFR_NEVV .or.  &
349                   ISTYP == BUFR_NEVS) .and.  &
350                   ZSLEV == ZLEV) then
351                ZJON=obs_bodyElem_r(obsSpaceData,OBS_JOBS,INDEX_BODY)+  &
352                     obs_bodyElem_r(obsSpaceData,OBS_JOBS,JJ)
353                ZQCARG = ZGAMI + EXP(-1.0D0*ZJON)
354                ZPPOST = ZGAMI/ZQCARG
355                !
356                ! Store the value of o-a multiplied by one minus the posterior
357                ! probability of gross error (needed for the adjoint calculations)
358                !
359                call obs_bodySet_r(obsSpaceData,OBS_QCV,INDEX_BODY, ZPPOST)
360                call obs_bodySet_r(obsSpaceData,OBS_QCV,JJ, ZPPOST)
361
362                call obs_bodySet_r(obsSpaceData,OBS_JOBS,INDEX_BODY,-LOG(ZQCARG/(ZGAMI+1.D0))/2.D0)
363                call obs_bodySet_r(obsSpaceData,OBS_JOBS,JJ, -LOG(ZQCARG/(ZGAMI+1.D0))/2.D0)
364              end if
365            end do
366          else ! ityp
367            do JJ=INDEX_BODY_START, INDEX_BODY
368              ISTYP = obs_bodyElem_i(obsSpaceData,OBS_VNM,JJ)
369              ZSLEV = obs_bodyElem_r(obsSpaceData,OBS_PPP,JJ)
370              if ((ISTYP == BUFR_NEUU .or.  &
371                   ISTYP == BUFR_NEUS) .and.  &
372                   ZSLEV == ZLEV) then
373                ZJON=obs_bodyElem_r(obsSpaceData,OBS_JOBS,INDEX_BODY)+  &
374                     obs_bodyElem_r(obsSpaceData,OBS_JOBS,JJ)
375                ZQCARG = ZGAMI + EXP(-1.0D0*ZJON)
376                ZPPOST = ZGAMI/ZQCARG
377                call obs_bodySet_r(obsSpaceData,OBS_QCV,INDEX_BODY, ZPPOST)
378                call obs_bodySet_r(obsSpaceData,OBS_QCV,JJ, ZPPOST)
379                call obs_bodySet_r(obsSpaceData,OBS_JOBS,INDEX_BODY,-LOG(ZQCARG/(ZGAMI+1.D0))/2.D0)
380                call obs_bodySet_r(obsSpaceData,OBS_JOBS,JJ, -LOG(ZQCARG/(ZGAMI+1.D0))/2.D0)
381              end if
382            enddo
383          endif !ityp
384        else ! LLUV
385          zjon = obs_bodyElem_r(obsSpaceData,OBS_JOBS,index_body)
386          zqcarg = zgami + exp(-1.0D0*zjon)
387          zppost = zgami/zqcarg
388          call obs_bodySet_r(obsSpaceData,OBS_QCV,index_body, zppost)
389          call obs_bodySet_r(obsSpaceData,OBS_JOBS,INDEX_BODY, - log(zqcarg/(zgami+1.D0)))
390        endif ! LLUV
391
392      endif ! includeFlag
393
394    enddo BODY
395
396  end subroutine vqc_NlTl
397
398
399  subroutine vqc_ad(obsSpaceData)
400    !
401    !:Purpose: Factorizes Grad(Jo) according to Andersson and Jarvinen
402    !           1999, Variational quality control, Q.J.R., 125, pp. 697-722.
403    !           It uses the value of (1-Wqc) saved in OBS_QCV in vqc_NlTl
404    !
405    implicit none
406
407    ! Arguments:
408    type(struct_obs), intent(inout) :: obsSpaceData ! obsSpaceData object
409
410    ! Locals:
411    integer :: index_body
412    logical :: includeFlag
413
414    do index_body=1,obs_numbody(obsSpaceData)
415      includeFlag = (obs_bodyElem_i(obsSpaceData,OBS_ASS,index_body) == obs_assimilated) .and.  &
416                    (obs_getFamily(obsSpaceData,bodyIndex_opt=index_body) /= 'RO')
417      ! pas de qcvar pour les radiances en mode matrice R non diagonale
418      if (rmat_lnondiagr) includeFlag = includeFlag .and.  &
419         (obs_getFamily(obsSpaceData,bodyIndex_opt=index_body) /= 'TO')
420
421      if (includeFlag) then
422        call obs_bodySet_r(obsSpaceData,OBS_WORK,index_body,  &
423               obs_bodyElem_r(obsSpaceData,OBS_WORK,index_body)  &
424               *(1.d0 - obs_bodyElem_r(obsSpaceData,OBS_QCV,index_body)))
425      endif
426    enddo
427
428  end subroutine vqc_ad
429
430
431  subroutine vqc_listrej(lobsSpaceData)
432    !
433    !:Purpose: List all observations rejected by the variational QC
434    !           Set QC flags consistent with VARQC decisions
435    !           Set global flag indicating report contains rejected observations
436    !           as required
437    !
438    implicit none
439
440    ! Arguments:
441    type(struct_obs), intent(inout) :: lobsSpaceData
442
443    ! Locals:
444    integer, parameter :: numitem = 16
445    integer :: icount( numitem, ofl_numFamily )
446    integer :: jfam, jitem, headerIndex
447    integer :: bodyIndex, bodyIndex2, ISTART,ityp,ISTYP
448    integer :: ISPDO,IDIRO,ISPDF,IDIRF,ISPDA,IDIRA,ILEV
449    integer :: IDBURP,IOTHER,obsONM
450    real(8) :: ZVAR,ZFCST,ZANA,ZPOST,ZLAT,ZLON,ZUU,ZVV
451    real(8) :: SPD,DEG,ZLEV,ZSLEV,ZCUT
452    character(len=4)  :: CLITM(NUMITEM),CLDESC
453    character(len=2)  :: CLUNITS
454    character(len=21) :: CODTYPNAME
455    character(len=12) :: stnId
456    logical :: LLOK,LLELREJ
457
458    !  ------NOTE----------
459    ! CURRENTLY SUPPORTED FAMILIES OF DATA 'UA' 'AI' 'SF' 'HU' 'TO' 'GO' 'GP'
460
461    DATA ZCUT /0.75D0/
462    DATA CLITM(1), CLITM(2), CLITM(3), CLITM(4), CLITM(5), CLITM(6)  &
463           /'WND',    'WND',    'HGT',    'TMP',    'DPD',   'STNP'/
464    DATA CLITM(7), CLITM(8), CLITM(9), CLITM(10), CLITM(11), CLITM(12)  &
465          /'MSLP',   'TSFC',   'SDPD',   'SWND',   'SWND',   'BTMP'/
466    DATA CLITM(13), CLITM(14), CLITM(15) , CLITM(16)  &
467           /'ZTD',   'CHM',     'SST',    'ICEC'/
468
469
470    do jfam = 1, ofl_numFamily
471      do jitem = 1, numitem
472        icount( jitem, jfam ) = 0
473      end do
474    end do
475    write(*,*) 'LIST OF DATA REJECTED BY VARIATIONAL QUALITY CONTROL'
476    !
477    !_____LOOP OVER ALL REPORTS, ONE FAMILY AT A TIME
478    !
479    FAMILY: do jfam = 1, ofl_numFamily
480
481      write(*,*) ' '
482      if (ofl_familyList(jfam) /= 'TO' ) then
483        write(*,*) 'IDENT     TYPE DESCRIPTION           LAT   LONG    LEVEL  ITEM   OBSVD     BKGND     ANAL  POST PROB REPORT'
484      else
485        write(*,*) 'IDENT     TYPE DESCRIPTION           LAT   LONG    CHNL   ITEM   OBSVD     BKGND     ANAL  POST PROB REPORT'
486      end if
487
488      ! loop over all header indices of the family
489      call obs_set_current_header_list( lobsSpaceData, ofl_familyList( jfam ) )
490
491      HEADER: do
492        headerIndex = obs_getHeaderIndex( lobsSpaceData )
493        if (headerIndex < 0) exit HEADER
494
495        llelrej = .false.
496        idburp  = obs_headElem_i( lobsSpaceData, OBS_ITY, headerIndex )
497        zlat    = obs_headElem_r( lobsSpaceData, OBS_LAT, headerIndex ) * MPC_DEGREES_PER_RADIAN_R8
498        zlon    = obs_headElem_r( lobsSpaceData, OBS_LON, headerIndex ) * MPC_DEGREES_PER_RADIAN_R8
499
500        ! loop over all body indices for this headerIndex
501        call obs_set_current_body_list( lobsSpaceData, headerIndex )
502
503        BODY: do 
504           bodyIndex = obs_getBodyIndex( lobsSpaceData )
505           if ( bodyIndex < 0) exit BODY
506           ityp = obs_bodyElem_i( lobsSpaceData, OBS_VNM, bodyIndex )
507           if (ityp == BUFR_NETS .or. ityp == BUFR_NEPS .or.  &
508               ityp == BUFR_NEPN .or. ityp == BUFR_NESS .or.  &
509               ityp == BUFR_NEUS .or. ityp == BUFR_NEVS .or.  &
510               ityp == BUFR_NEZD .or. ityp == bufr_sst  .or. ityp == BUFR_ICEC .or.  &
511               ityp == bufr_vis  .or. ityp == bufr_logVis  .or. ityp == bufr_gust .or.  &
512               ityp == bufr_radarPrecip .or. ityp == bufr_logRadarPrecip ) then
513              llok = (obs_bodyElem_i( lobsSpaceData, OBS_ASS, bodyIndex ) == obs_assimilated )
514           else
515              llok = (obs_bodyElem_i( lobsSpaceData, OBS_ASS, bodyIndex ) == obs_assimilated .and.  &
516                      obs_bodyElem_i( lobsSpaceData, OBS_XTR, bodyIndex ) == 0) .or.  &
517                     (obs_bodyElem_i( lobsSpaceData, OBS_XTR, bodyIndex ) == 2 .and.  &
518                      obs_bodyElem_i( lobsSpaceData, OBS_VNM, bodyIndex ) == BUFR_NEGZ)
519           end if
520           if ( llok ) then
521             zpost = obs_bodyElem_r( lobsSpaceData, OBS_QCV, bodyIndex )
522             zlev  = obs_bodyElem_r( lobsSpaceData, OBS_PPP, bodyIndex ) * MPC_MBAR_PER_PA_R8
523             if ( obs_bodyElem_i( lobsSpaceData, OBS_VCO, bodyIndex ) == 2) then
524               zlev = obs_bodyElem_r( lobsSpaceData, OBS_PPP, bodyIndex ) * MPC_MBAR_PER_PA_R8
525               CLUNITS = 'MB'
526             else if (obs_bodyElem_i(lobsSpaceData,OBS_VCO,bodyIndex) == 1) then
527               !
528               ! VERTICAL COORDINATE IS HEIGHT
529               !
530               ZLEV = obs_bodyElem_r(lobsSpaceData,OBS_PPP,bodyIndex)
531               CLUNITS = ' M'
532               if (ofl_familyList(JFAM)=='CH') then
533                  ZLEV = ZLEV*0.01
534                  CLUNITS = 'HM'
535               end if
536             else if (obs_bodyElem_i(lobsSpaceData,OBS_VCO,bodyIndex) == -1) then
537               !
538               ! TOVS CHANNEL NUMBER
539               !
540               ZLEV = obs_bodyElem_r(lobsSpaceData,OBS_PPP,bodyIndex)
541               CLUNITS = '  '
542             else 
543               ZLEV = obs_bodyElem_r(lobsSpaceData,OBS_PPP,bodyIndex)
544               CLUNITS = '  '
545             end if
546
547             ZVAR = obs_bodyElem_r(lobsSpaceData,OBS_VAR,bodyIndex)
548
549             ZFCST= ZVAR - obs_bodyElem_r(lobsSpaceData,OBS_OMP,bodyIndex)
550             ZANA = ZVAR - obs_bodyElem_r(lobsSpaceData,OBS_OMA,bodyIndex)
551             !
552             !_____________TREAT WINDS AS SPECIAL CASE
553             !              BUFR_NEUU       = 011003 (U COMPONENT)           (m/s)
554             !              BUFR_NEVV       = 011004 (V COMPONENT)           (m/s)
555             !              BUFR_NEUS       = 011215 (U COMPONENT AT 10 M)   (m/s)
556             !              BUFR_NEVS       = 011216 (V COMPONENT AT 10 M)   (m/s)
557             !
558             if (((ityp==BUFR_NEUU .or. ityp == BUFR_NEUS) .and.  &
559                  col_varExist(varName='UU')).or.  &
560                 ((ityp==BUFR_NEVV .or. ityp == BUFR_NEVS) .and.  &
561                  col_varExist(varName='VV'))) then
562               IOTHER = -1
563               if (ityp == BUFR_NEUU .or. ityp == BUFR_NEUS) then
564                 ISTART=obs_headElem_i(lobsSpaceData,OBS_RLN,headerIndex)
565                 do bodyIndex2=ISTART,bodyIndex
566                   ISTYP = obs_bodyElem_i(lobsSpaceData,OBS_VNM,bodyIndex2)
567                   if (obs_bodyElem_i(lobsSpaceData,OBS_VCO,bodyIndex2) == 2) then
568                     ZSLEV = obs_bodyElem_r(lobsSpaceData,OBS_PPP,bodyIndex2)*MPC_MBAR_PER_PA_R8
569                   end if
570                   if (obs_bodyElem_i(lobsSpaceData,OBS_VCO,bodyIndex2) == 1) then
571                     ZSLEV = obs_bodyElem_r(lobsSpaceData,OBS_PPP,bodyIndex2)
572                   end if
573                   if ((ISTYP == BUFR_NEVV .or. ISTYP == BUFR_NEVS) .and. &
574                        ZLEV == ZSLEV) then
575                     IOTHER = bodyIndex2
576                   end if
577                 end do
578               else
579                 ISTART=obs_headElem_i(lobsSpaceData,OBS_RLN,headerIndex)
580                 do bodyIndex2=ISTART,bodyIndex
581                   ISTYP = obs_bodyElem_i(lobsSpaceData,OBS_VNM,bodyIndex2)
582                   if (obs_bodyElem_i(lobsSpaceData,OBS_VCO,bodyIndex2) == 2) then
583                     ZSLEV = obs_bodyElem_r(lobsSpaceData,OBS_PPP,bodyIndex2)*MPC_MBAR_PER_PA_R8
584                   end if
585                   if (obs_bodyElem_i(lobsSpaceData,OBS_VCO,bodyIndex2) == 1) then
586                     ZSLEV = obs_bodyElem_r(lobsSpaceData,OBS_PPP,bodyIndex2)
587                   end if
588                   if ((ISTYP == BUFR_NEUU .or. ISTYP == BUFR_NEUS) .and.  &
589                        ZLEV == ZSLEV) then
590                     IOTHER = bodyIndex2
591                   end if
592                 end do
593               end if
594               if (IOTHER /= -1) then
595                 if (ityp == BUFR_NEVV .or. ityp == BUFR_NEUS) then
596                   ZUU = ZVAR
597                   ZVV = obs_bodyElem_r(lobsSpaceData,OBS_VAR,IOTHER)
598                 else
599                   ZVV = ZVAR
600                   ZUU = obs_bodyElem_r(lobsSpaceData,OBS_VAR,IOTHER)
601                 end if
602                 SPD = SQRT(ZUU*ZUU + ZVV*ZVV)
603                 ISPDO = NINT(SPD)
604                 if (ZUU==0.D0 .and. ZVV==0.D0)then
605                   IDIRO = 999
606                 else
607                   DEG = 270. - ATAN2(ZVV,ZUU)*MPC_DEGREES_PER_RADIAN_R8
608                   if (DEG > 360.D0)DEG = DEG - 360.D0
609                   if (DEG < 0.D0)  DEG = DEG + 360.D0
610                   IDIRO = NINT(DEG)
611                 end if
612                 if (ityp == BUFR_NEUU .or. ityp == BUFR_NEUS) then
613                   ZUU = ZFCST
614                   ZVV = obs_bodyElem_r(lobsSpaceData,OBS_VAR,IOTHER) - &
615                         obs_bodyElem_r(lobsSpaceData,OBS_OMP,IOTHER)
616                 else
617                   ZVV = ZFCST
618                   ZUU = obs_bodyElem_r(lobsSpaceData,OBS_VAR,IOTHER) - &
619                         obs_bodyElem_r(lobsSpaceData,OBS_OMP,IOTHER)
620                 end if
621                 SPD=SQRT(ZUU*ZUU + ZVV*ZVV)
622                 ISPDF = NINT(SPD)
623                 if (ZUU == 0.D0 .and. ZVV == 0.D0) then
624                   IDIRF = 999
625                 else
626                   DEG = 270.D0 - ATAN2(ZVV,ZUU)*MPC_DEGREES_PER_RADIAN_R8
627                   if (DEG > 360.D0)DEG = DEG - 360.D0
628                   if (DEG < 0.D0)  DEG = DEG + 360.D0
629                   IDIRF = NINT(DEG)
630                 end if
631                 if (ityp == BUFR_NEUU .or. ityp == BUFR_NEUS) then
632                   ZUU = ZANA
633                   ZVV = obs_bodyElem_r(lobsSpaceData,OBS_VAR,IOTHER) -  &
634                         obs_bodyElem_r(lobsSpaceData,OBS_OMA,IOTHER)
635                 else
636                   ZVV = ZANA
637                   ZUU = obs_bodyElem_r(lobsSpaceData,OBS_VAR,IOTHER) -  &
638                         obs_bodyElem_r(lobsSpaceData,OBS_OMA,IOTHER)
639                 end if
640                 SPD=SQRT(ZUU*ZUU + ZVV*ZVV)
641                 ISPDA = NINT(SPD)
642                 if (ZUU==0.D0 .and. ZVV==0.D0) then
643                   IDIRA = 999
644                 else
645                   DEG = 270.D0 - ATAN2(ZVV,ZUU)*MPC_DEGREES_PER_RADIAN_R8
646                   if (DEG > 360.D0)DEG = DEG - 360.D0
647                   if (DEG < 0.D0)  DEG = DEG + 360.D0
648                   IDIRA = NINT(DEG)
649                 end if
650                 ILEV = NINT(ZLEV)
651                 if (ZPOST > ZCUT) then
652                   LLELREJ = .TRUE.
653                   call obs_bodySet_i(lobsSpaceData,OBS_FLG,bodyIndex,  &
654                     IBSET(obs_bodyElem_i(lobsSpaceData,OBS_FLG,bodyIndex),9))
655                   call obs_bodySet_i(lobsSpaceData,OBS_FLG,IOTHER,  &
656                     IBSET(obs_bodyElem_i(lobsSpaceData,OBS_FLG,IOTHER),9))
657                   call obs_bodySet_i(lobsSpaceData,OBS_FLG,bodyIndex,  &
658                     IBSET(obs_bodyElem_i(lobsSpaceData,OBS_FLG,bodyIndex),17))
659                   call obs_bodySet_i(lobsSpaceData,OBS_FLG,IOTHER,  &
660                     IBSET(obs_bodyElem_i(lobsSpaceData,OBS_FLG,IOTHER),17))
661                   if (ityp == BUFR_NEUU .or.  &
662                       ityp == BUFR_NEVV) then
663                     ICOUNT(1,JFAM) = ICOUNT(1,JFAM) + 1
664                   end if
665                   if (ityp == BUFR_NEUS .or. ityp == BUFR_NEVS) then
666                     ICOUNT(10,JFAM) = ICOUNT(10,JFAM) + 1
667                   end if
668                   codtypname=codtyp_get_name(IDBURP)
669                   stnId = obs_elem_c(lobsSpaceData,'STID',headerIndex)
670                   obsONM = obs_headElem_i(lobsSpaceData,OBS_ONM,headerIndex)
671                   write(*,620) stnId,IDBURP,codtypname,ZLAT,ZLON,  &
672                     ILEV,CLUNITS,IDIRO,ISPDO,IDIRF,ISPDF,IDIRA,  &
673                     ISPDA,ZPOST,obsONM
674 620               FORMAT(A9,1X,i3,1x,A21,1X,F5.1,2X,F5.1,2X,I4,A2,2X,  &
675                     'WND',3X,I3,'/',I3,3X,I3,'/',I3,3X,I3,  &
676                     '/',I3,2X,F7.4,1X,I8)
677
678                 end if
679               end if
680             else
681               ILEV = NINT(ZLEV)
682               if (ZPOST > ZCUT) then
683                 LLELREJ = .TRUE.
684                 if (ofl_familyList(JFAM)=='CH') then
685                    ICOUNT(14,JFAM)=ICOUNT(14,JFAM)+1
686                    CLDESC=CLITM(14)
687                    if (obs_headElem_i(lobsSpaceData,OBS_CHM,headerIndex).ge.0) then
688
689                       ! CONVERT TO PERCENTAGE DIFFERENCE FROM THE FORECAST
690
691                       if (abs(ZFCST).lt.1.D-10*max(1.D-30,abs(ZVAR-ZFCST))) then
692                          write(*,*) 'vqc_listrej: WARNING for CH obs. BKGND and or OBSVD value out of bounds for output.'
693                          write(*,*) 'OBSVD and ANAL set to 0.0 below in addition to BKGND.'
694                          ZVAR=0.0D0
695                          ZANA=0.0D0
696                       else
697                          ZVAR=(ZVAR-ZFCST)/ZFCST*100.
698                          ZANA=(ZANA-ZFCST)/ZFCST*100.
699                       end if
700                       ZFCST=0.0
701                       CLDESC=vnl_varnameFromVarnum(ityp,obs_headElem_i(lobsSpaceData,OBS_CHM,headerIndex))
702                         
703                    else if (ityp == BUFR_NETT) then
704
705                       ! CONVERT FROM KELVIN TO CELCIUS
706
707                       CLDESC = CLITM(4)
708                       ZVAR = ZVAR - MPC_K_C_DEGREE_OFFSET_R8
709                       ZFCST = ZFCST - MPC_K_C_DEGREE_OFFSET_R8
710                       ZANA  = ZANA - MPC_K_C_DEGREE_OFFSET_R8
711                    end if
712                 else if (ityp == BUFR_NEGZ) then
713                   CLDESC = CLITM(3)
714                   ICOUNT(3,JFAM) = ICOUNT(3,JFAM) + 1
715                   !
716                   ! CONVERT FROM GEOPOTENTIAL TO GEOPOTENTIAL HEIGHT
717                   !
718                   ZVAR = ZVAR/ec_rg
719                   ZFCST = ZFCST/ec_rg
720                   ZANA  = ZANA/ec_rg
721                 else if (ityp == BUFR_NETT) then
722                   CLDESC = CLITM(4)
723                   ICOUNT(4,JFAM) = ICOUNT(4,JFAM) + 1
724                   !
725                   ! CONVERT FROM KELVIN TO CELCIUS
726                   !
727                   ZVAR = ZVAR - MPC_K_C_DEGREE_OFFSET_R8
728                   ZFCST = ZFCST - MPC_K_C_DEGREE_OFFSET_R8
729                   ZANA  = ZANA - MPC_K_C_DEGREE_OFFSET_R8
730                 else if (ityp == BUFR_NEES) then
731                   CLDESC = CLITM(5)
732                   ICOUNT(5,JFAM) = ICOUNT(5,JFAM) + 1
733                 else if (ityp == BUFR_NEPS) then
734                   CLDESC = CLITM(6)
735                   ICOUNT(6,JFAM) = ICOUNT(6,JFAM) + 1
736                   !
737                   ! CONVERT FROM PASCALS TO MILLIBARS
738                   !
739                   ZVAR = ZVAR*MPC_MBAR_PER_PA_R8
740                   ZANA = ZANA*MPC_MBAR_PER_PA_R8
741                   ZFCST = ZFCST*MPC_MBAR_PER_PA_R8
742                 else if (ityp == BUFR_NEPN) then
743                   CLDESC = CLITM(7)
744                   ICOUNT(7,JFAM) = ICOUNT(7,JFAM) + 1
745                   !
746                   ! CONVERT FROM PASCALS TO MILLIBARS
747                   !
748                   ZVAR = ZVAR*MPC_MBAR_PER_PA_R8
749                   ZANA = ZANA*MPC_MBAR_PER_PA_R8
750                   ZFCST = ZFCST*MPC_MBAR_PER_PA_R8
751                 else if (ityp == BUFR_NETS) then
752                   CLDESC = CLITM(8)
753                   ICOUNT(8,JFAM) = ICOUNT(8,JFAM) + 1
754                   !
755                   ! CONVERT FROM KELVIN TO CELCIUS
756                   !
757                   ZVAR = ZVAR - MPC_K_C_DEGREE_OFFSET_R8
758                   ZFCST = ZFCST - MPC_K_C_DEGREE_OFFSET_R8
759                   ZANA  = ZANA - MPC_K_C_DEGREE_OFFSET_R8
760                 else if (ityp == BUFR_NESS) then
761                   CLDESC = CLITM(9)
762                   ICOUNT(9,JFAM) = ICOUNT(9,JFAM) + 1
763                 else if ( ityp==BUFR_NBT1 .or. ityp==BUFR_NBT2 .or.  &
764                      ityp==BUFR_NBT3      ) then
765                   CLDESC = CLITM(12)
766                   ICOUNT(12,JFAM) = ICOUNT(12,JFAM) + 1
767                 else if (ityp == BUFR_NEZD) then
768                   CLDESC = CLITM(13)
769                   ICOUNT(13,JFAM) = ICOUNT(13,JFAM) + 1
770                   !
771                   ! CONVERT ZTD FROM METRES TO MILLIMETRES
772                   !
773                   ZVAR = ZVAR * 1000.D0
774                   ZFCST = ZFCST * 1000.D0
775                   ZANA  = ZANA * 1000.D0
776                 else if (ityp == bufr_sst) then
777                   CLDESC = CLITM(15)
778                   ICOUNT(15,JFAM) = ICOUNT(15,JFAM) + 1
779                 else if (ityp == BUFR_ICEC) then
780                   CLDESC = CLITM(16)
781                   ICOUNT(16,JFAM) = ICOUNT(16,JFAM) + 1
782                 end if
783
784                 call obs_bodySet_i(lobsSpaceData,OBS_FLG,bodyIndex,  &
785                   IBSET(obs_bodyElem_i(lobsSpaceData,OBS_FLG,bodyIndex),9))
786                 call obs_bodySet_i(lobsSpaceData,OBS_FLG,bodyIndex,  &
787                   IBSET(obs_bodyElem_i(lobsSpaceData,OBS_FLG,bodyIndex),17))
788                 codtypname=codtyp_get_name(IDBURP)
789                 stnId = obs_elem_c(lobsSpaceData,'STID',headerIndex)
790                 obsONM = obs_headElem_i(lobsSpaceData,OBS_ONM,headerIndex)
791                 write(*,630) stnId,IDBURP,  &
792                   codtypname,ZLAT,ZLON,ILEV,CLUNITS,CLDESC, &
793                   ZVAR,ZFCST,ZANA,ZPOST,obsONM
794 630               FORMAT(A9,1X,I3,1X,A21,1X,F5.1,2X,F5.1,2X,I4,A2,2X,  &
795                    A4,2X,F7.1,3X,F7.1,3X,F7.1,2X,F7.4,1X,I8,1X)
796               end if
797             end if
798           end if
799        end do BODY
800        !
801        ! NOW SET THE GLOBAL FLAGS IN THE BURP RECORD HEADER
802        !
803        if (LLELREJ) then
804          call obs_headSet_i(lobsSpaceData,OBS_ST1,headerIndex, IBSET( obs_headElem_i(lobsSpaceData,OBS_ST1,headerIndex), 06))
805        end if
806      end do HEADER
807    end do FAMILY
808
809
810    write(*,640)
811 640  FORMAT(//)
812    write(*,*) ' REJECTED DATA ACCORDING TO FAMILY OF REPORT.'
813    write(*,670)(ofl_familyList(JFAM),JFAM=1,ofl_numFamily)
814 670  FORMAT(5X,11(7X,A2))
815    do JITEM=1,NUMITEM
816      if ( .NOT. (JITEM == 2 .or. JITEM == 11)) then
817        write(*,680)CLITM(JITEM),(ICOUNT(JITEM,JFAM),JFAM=1,ofl_numFamily)
818 680      FORMAT(1X,A4,11(4X,I5))
819      end if
820    end do
821    write(*,640)
822
823  end subroutine vqc_listrej
824
825
826end module varQC_mod