1module obsFilter_mod
2 ! MODULE obsFilter_mod (prefix='filt' category='5. Observation operators')
3 !
4 !:Purpose: Various types of filters that are applied to the observations
5 ! mostly to reject them so that they will not be assimilated.
6 !
7 use codePrecision_mod
8 use midasMpi_mod
9 use earthConstants_mod
10 use obsSpaceData_mod
11 use columnData_mod
12 use bufr_mod
13 use tovsNL_mod
14 use gps_mod
15 use utilities_mod
16 use varNameList_mod
17 use physicsFunctions_mod
18 use codtyp_mod
19 use radialVelocity_mod
20 implicit none
21 save
22 private
23
24 ! public variables
25 public :: filt_rlimlvhu
26 ! public procedures
27 public :: filt_setup, filt_topo, filt_suprep
28 public :: filt_surfaceWind, filt_gpsro, filt_backScatAnisIce, filt_iceConcentration, filt_radvel
29 public :: filt_bufrCodeAssimilated, filt_getBufrCodeAssimilated, filt_nBufrCodeAssimilated
30 integer, parameter :: nelemsMax = 30
31 integer, parameter :: nflagsMax = 15
32 integer :: filt_nelems, filt_nflags
33 integer, target :: filt_nlist(nelemsMax)
34 integer :: filt_nlistflg(nflagsMax)
35
36 real(8) :: filt_rlimlvhu
37
38 ! topographic rejection criteria
39 integer, parameter :: numElem = 21
40 real(8) :: altDiffMax(numElem) = & ! default values (in metres)
41 (/ 50.d0, 50.d0, 50.d0, 50.d0, 50.d0, 800.d0, 800.d0, &
42 800.d0, 800.d0, 1000.d0, 50.d0, 50.d0, 50.d0, 50.d0, &
43 50.d0, 50.d0, 50.d0, 50.d0, 50.d0, 50.d0, 1000.d0 /)
44 integer, parameter :: elemList(numElem) = &
45 (/ BUFR_NEDS, BUFR_NEFS, BUFR_NEUS, BUFR_NEVS, BUFR_NESS, BUFR_NETS, BUFR_NEPS, &
46 BUFR_NEPN, BUFR_NEGZ, BUFR_NEZD, BUFR_NEDD, BUFR_NEFF, BUFR_NEUU, BUFR_NEVV, &
47 BUFR_NEES, BUFR_NETT, BUFR_NEAL, bufr_vis , bufr_logVis, bufr_gust, bufr_radvel /)
48
49 integer, parameter :: nTopoFiltFam = 8
50 character(len=2) :: filtTopoList(nTopoFiltFam) = ' '
51
52 character(len=48) :: filterMode
53
54 logical :: initialized = .false.
55
56 ! Namelist variables:
57 logical :: discardlandsfcwind ! choose to reject surface wind obs over land
58 real(8) :: surfaceBufferZone_Pres ! height of buffer zone (in Pa) for rejecting obs near sfc
59 real(8) :: surfaceBufferZone_Height ! height of buffer zone (in m) for rejecting obs near sfc
60 logical :: useEnkfTopoFilt ! choose to use simpler approach (originally in EnKF) for rejecting obs near sfc
61 logical :: rejectGZforAnalysis ! whether to reject geopotential height for analysis update
62
63contains
64
65 !--------------------------------------------------------------------------
66 ! findElemIndex
67 !-------------------------------------------------------------------------
68 function findElemIndex(varNum) result(listIndex)
69 implicit none
70
71 ! Arguments:
72 integer, intent(in) :: varNum
73 ! Result:
74 integer :: listIndex
75
76 ! Locals:
77 integer :: elemIndex
78
79 listIndex = -1
80 do elemIndex=1,numElem
81 if(varNum == elemList(elemIndex)) listIndex = elemIndex
82 end do
83
84 if (listIndex == -1) then
85 write(*,*) 'filterobs_mod-findElemIndex: WARNING: varNum value not found: ',varNum
86 end if
87
88 end function findElemIndex
89
90 !--------------------------------------------------------------------------
91 ! filt_setup
92 !--------------------------------------------------------------------------
93 subroutine filt_setup(filterMode_in)
94 implicit none
95
96 ! Arguments:
97 character(len=*), intent(in) :: filterMode_in
98
99 ! Locals:
100 integer :: nulnam, ierr, elem, jflag, ibit, obsFamilyIndex, elemIndex
101 integer :: fnom, fclos
102 integer :: flagIndex, elementIndex
103
104 character(len=35) :: CREASON(-12:13)
105 data creason/'PASSIVE ', &
106 'SIMULATED ', &
107 'CLOUD EFFECTED RADIANCE ', &
108 'REJECT BY RAW TO BURP ', &
109 'JACOBIAN IMPORTANT ABOVE MODEL TOP', &
110 'ABS OROGRAPH-PHI ', &
111 'MASQUE TERRE-MER ', &
112 'OROGRAPHIE ', &
113 'REJECTED BY QCVAR ', &
114 'REJECTED BY BACKGROUND CHECK ', &
115 'BACKGROUND CHECK LEVEL 3 ', &
116 'BACKGROUND CHECK LEVEL 2 ', &
117 'BACKGROUND GHECK LEVEL 1 ', &
118 'RESERVED ', &
119 'REJECTED BY SELECTION PROCESS ', &
120 'GENERATED BY OI ', &
121 'REJECTION BY OI ', &
122 'ELEMENT ON BLACK LIST ', &
123 'RESERVED ', &
124 'CORRECTED ELEMENT ', &
125 'INTERPOLATED ELEMENT ', &
126 'DOUBTFUL ELEMENT ', &
127 'POSSIBLY ERRONEOUS ELEMENT ', &
128 'ERRONEOUS ELEMENT ', &
129 'ELEMENT EXCEEDS CLIMATE EXTREME ', &
130 'ELEMENT MODIFIED OR GEN BY ADE '/
131
132 ! Namelist variables: (local)
133 integer :: nelems ! MUST NOT BE INCLUDED IN NAMELIST!
134 integer :: nlist(nelemsMax) ! list of bufr element IDs to consider for assimilation
135 integer :: nflags ! MUST NOT BE INCLUDED IN NAMELIST!
136 integer :: nlistflg(nFLagsMax) ! list of flag 'reference numbers' to use for rejecting obs
137 integer :: nelems_altDiffMax ! MUST NOT BE INCLUDED IN NAMELIST!
138 integer :: list_altDiffMax(numElem) ! list of bufr element IDs to apply maximum altitude difference
139 character(len=2) :: list_topoFilt(nTopoFiltFam) ! list of obs family names for applying max altitude
140 real(8) :: value_altDiffMax(numElem) ! value of maximum difference between model sfc and obs altitude
141 real(8) :: rlimlvhu ! pressure level (in hPa) above which humidity (ES) obs are rejected
142
143 namelist /namfilt/nelems, nlist, nflags, nlistflg, rlimlvhu, discardlandsfcwind, &
144 nelems_altDiffMax, list_altDiffMax, value_altDiffMax, surfaceBufferZone_Pres, &
145 surfaceBufferZone_Height, list_topoFilt, useEnkfTopoFilt, rejectGZforAnalysis
146
147 filterMode = filterMode_in
148
149 ! set default values for namelist variables
150 nlist(:) = MPC_missingValue_INT
151 nelems = MPC_missingValue_INT
152 nlistflg(:) = MPC_missingValue_INT
153 nflags = MPC_missingValue_INT
154 list_altDiffMax (:) = MPC_missingValue_INT
155 value_altDiffMax(:) = MPC_missingValue_R8
156 nelems_altDiffMax = MPC_missingValue_INT
157
158 list_topoFilt(:) = '**'
159
160 rlimlvhu = 300.d0
161 discardlandsfcwind = .true.
162
163 surfaceBufferZone_Pres = 5000.0d0 ! default value in Pascals
164 surfaceBufferZone_Height = 400.0d0 ! default value in Metres
165
166 useEnkfTopoFilt = .false.
167 rejectGZforAnalysis = .true.
168
169 nulnam=0
170 ierr=fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
171 read(nulnam,nml=namfilt,iostat=ierr)
172 if(ierr.ne.0) call utl_abort('filt_setup: Error reading namelist! Hint: did you replace ltopofilt by list_topoFilt?')
173 if(mmpi_myid == 0) write(*,nml=namfilt)
174 ierr=fclos(nulnam)
175
176 filt_rlimlvhu = rlimlvhu
177
178 if (nelems /= MPC_missingValue_INT) then
179 call utl_abort('filt_setup: check NAMFILT namelist section; NELEMS should be removed')
180 end if
181 filt_nelems = 0
182 do elementIndex = 1, nelemsMax
183 if (nlist(elementIndex) /= MPC_missingValue_INT) then
184 filt_nlist(elementIndex) = nlist(elementIndex)
185 filt_nelems = filt_nelems + 1
186 end if
187 end do
188
189 if (nflags /= MPC_missingValue_INT) then
190 call utl_abort('filt_setup: check NAMFILT namelist section; NFLAGS should be removed')
191 end if
192 filt_nflags = 0
193 do flagIndex = 1, nflagsMax
194 if (nlistflg(flagIndex) /= MPC_missingValue_INT) then
195 filt_nlistflg(flagIndex) = nlistflg(flagIndex)
196 filt_nflags = filt_nflags + 1
197 end if
198 end do
199
200 if(mmpi_myid == 0) then
201 write(*,'(1X,"***********************************")')
202 write(*,'(1X," ELEMENTS SELECTED FOR ASSIMILATION:",/)')
203 write(*,'(1X,"***********************************")')
204 do elem=1,filt_nelems
205 write(*,'(15X,I5)') filt_nlist(elem)
206 end do
207 write(*,'(1X,"***********************************")')
208 write(*,*) ' REJECT ELEMENTS WITH REJECT FLAG '
209 write(*,*)' BIT : '
210 do jflag=1,filt_nflags
211 ibit= filt_nlistflg(jflag)
212 write(*,*) ibit,' ',creason(ibit)
213 end do
214 write(*,'(1X,"***********************************")')
215 end if
216
217 !
218 !- Set values for altDiffMax
219 !
220 if (nelems_altDiffMax /= MPC_missingValue_INT) then
221 call utl_abort('filt_setup: check namelist section NAMFILT: nelems_altDiffMax should be removed')
222 end if
223 do elem = 1, numElem
224 if (list_altDiffMax(elem) /= MPC_missingValue_INT .and. value_altDiffMax(elem) /= MPC_missingValue_R8) then
225 elemIndex = findElemIndex(list_altDiffMax(elem))
226 if ( elemIndex >= 1 .and. elemIndex <= numElem ) then
227 altDiffMax(elemIndex) = value_altDiffMax(elem)
228 write(*,*) ' filt_setup: altDiffMax value for ', elemList(elemIndex), ' is set to ', altDiffMax(elemIndex)
229 else
230 call utl_abort('filt_setup: Error in value setting for altDiffMax')
231 end if
232 end if
233 end do
234
235 !
236 !- Set the topographic rejection list
237 !
238 if (all(list_topoFilt(:) == '**')) then
239 ! default list
240 filtTopoList(1) = 'SF'
241 filtTopoList(2) = 'UA'
242 filtTopoList(3) = 'AI'
243 filtTopoList(4) = 'SW'
244 filtTopoList(5) = 'PR'
245 filtTopoList(6) = 'AL'
246 filtTopoList(7) = 'TO'
247 filtTopoList(8) = 'CH'
248 else
249 do obsFamilyIndex = 1, nTopoFiltFam
250 if (list_topoFilt(obsFamilyIndex) /= '**') then
251 filtTopoList(obsFamilyIndex) = list_topoFilt(obsFamilyIndex)
252 end if
253 end do
254 end if
255
256 initialized = .true.
257
258 end subroutine filt_setup
259
260 !--------------------------------------------------------------------------
261 ! filt_suprep
262 !--------------------------------------------------------------------------
263 subroutine filt_suprep(obsSpaceData)
264 !
265 !:Purpose: Select the data in the obsSpaceData which are to be assimilated
266 !
267 implicit none
268
269 ! Arguments:
270 type(struct_obs), intent(inout) :: obsSpaceData
271
272 ! Locals:
273 integer :: bodyIndex, headerIndex
274 integer :: ipres, ivco, ierr, loopIndex
275 integer :: idburp, ivnm, iflg, ibad, iknt, iknt_mpiglobal, ilansea
276 logical :: llok, llrej, llbogus
277
278 call utl_tmg_start(22,'----ObsFiltSuprep')
279
280 if(mmpi_myid == 0) write(*,*) 'starting subroutine filt_suprep'
281
282 iknt = 0
283
284 BODY: do bodyIndex = 1, obs_numbody( obsSpaceData )
285 headerIndex = obs_bodyElem_i( obsSpaceData, OBS_HIND, bodyIndex )
286 ivnm = obs_bodyElem_i( obsSpaceData, OBS_VNM , bodyIndex )
287 idburp = obs_headElem_i( obsSpaceData, OBS_ITY , headerIndex )
288 !
289 ! Unwanted data types via types specified in NLIST
290 !
291 llok = .false.
292 do loopIndex = 1, filt_nelems
293 llok = ( ivnm == filt_nlist( loopIndex ) ) .or. llok
294 end do
295 if (.not.llok) then
296 call obs_bodySet_i(obsSpaceData,OBS_ASS,bodyIndex,obs_notAssimilated)
297 cycle BODY
298 end if
299 !
300 ! Allow gz for bogus data only in analysis case
301 !
302 llbogus = ( idburp == 150 .or. idburp == 151 .or. idburp == 152 .or. idburp == 153 )
303 if ( (filterMode == 'analysis' .or. filterMode == 'FSO') .and. llok .and. ivnm == BUFR_NEGZ .and. .not.llbogus ) then
304 if (rejectGZforAnalysis) llok=.false.
305 end if
306 !
307 ! Ground-based GPS (GP) data (codtyp 189)
308 ! LLOK = .TRUE. DY DEFAULT IF ELEMENT IS IN NLIST
309 ! If gps_gb_LASSMET = .FALSE. don't want to assimilate Ps (BUFR_NEPS),
310 ! Ts (BUFR_NETS), or (T-Td)s (BUFR_NESS)
311 !
312 if ( idburp == 189 ) then
313 if (.not.gps_gb_lassmet .and. ( ivnm == BUFR_NEPS .or. &
314 ivnm == BUFR_NETS .or. &
315 ivnm == BUFR_NESS )) then
316 llok = .false.
317 end if
318 end if
319 !
320 ! Exclude T-Td above level RLIMLVHU (mbs)
321 !
322 ivco = obs_bodyElem_i( obsSpaceData, OBS_VCO, bodyIndex )
323 ipres = nint( obs_bodyElem_r( obsSpaceData, OBS_PPP, bodyIndex))
324 if ( ( ivco == 2) .and. ( ivnm == BUFR_NEES ) .and. &
325 ( ipres < nint( filt_rlimlvhu *100.0d0 )) ) then
326 llok=.false.
327 end if
328 !
329 ! Bad data with quality control flags via bit list specified in NLISTFLG
330 !
331 iflg = obs_bodyElem_i( obsSpaceData, OBS_FLG, bodyIndex )
332 llrej = .false.
333 do loopIndex = 1, filt_nflags
334 ibad = 13 - filt_nlistflg( loopIndex )
335 llrej=( btest(iflg,ibad) ) .or. llrej
336 end do
337 !
338 ! Filter TOVS data: check for invalid land/sea/sea-ice flag
339 !
340 if (ivnm == BUFR_NBT1 .or. ivnm == BUFR_NBT2 .or. ivnm == BUFR_NBT3) then
341 ! Here we have to exclude the ssmis and mwhs2 data since the burp file does not contain the ele 8021 land_sea
342 if ( ( tvs_isIdBurpTovs(idburp) ) .and. &
343 ( idburp /= codtyp_get_codtyp('ssmis') ) .and. &
344 ( idburp /= codtyp_get_codtyp('mwhs2') ) ) then
345 ilansea = tvs_ChangedStypValue( obsSpaceData, headerIndex )
346 if (ilansea < 0 .or. ilansea > 2 ) llok = .false.
347 end if
348 end if
349 !
350 ! SAR winds: assimilates wind speed only for SAR winds
351 !
352 if ( ivnm == BUFR_NEFS ) then
353 if ( idburp .ne. 204 ) then
354 llok = .false.
355 end if
356 end if
357 if ( llok .and. .not. llrej ) then
358 call obs_bodySet_i( obsSpaceData, OBS_ASS, bodyIndex, obs_assimilated )
359 iknt = iknt + 1
360 else
361 call obs_bodySet_i( obsSpaceData, OBS_ASS, bodyIndex, obs_notAssimilated )
362 end if
363
364 end do body
365
366 call rpn_comm_allreduce( iknt, iknt_mpiglobal, 1, "MPI_INTEGER", "MPI_SUM", "GRID", ierr )
367 if(mmpi_myid == 0) write(*,*) ' Number of data to be assimilated: ', iknt_mpiglobal
368
369 if(mmpi_myid == 0) write(*,*) 'end of filt_suprep'
370
371 ! abort if there is no data to be assimilated
372 if (iknt_mpiglobal == 0 ) then
373 call utl_abort('SUPREP. NO DATA TO BE ASSIMILATED')
374 end if
375
376 call utl_tmg_stop(22)
377
378 end subroutine filt_suprep
379
380 !--------------------------------------------------------------------------
381 ! filt_topo
382 !--------------------------------------------------------------------------
383 subroutine filt_topo(columnTrlOnTrlLev, obsSpaceData, beSilent)
384 implicit none
385
386 ! Arguments:
387 type(struct_columnData), intent(in) :: columnTrlOnTrlLev
388 type(struct_obs) , intent(inout) :: obsSpaceData
389 logical, intent(in) :: beSilent
390
391 if (all(filtTopoList(:) == ' ')) then
392
393 if (mmpi_myid == 0 .and. .not.beSilent) then
394 write(*,*)
395 write(*,*)' --------------------------------------------------------------'
396 write(*,*)' - filt_topo: NO topographic filtering '
397 write(*,*)' --------------------------------------------------------------'
398 end if
399
400 else
401
402 if (mmpi_myid == 0 .and. .not.beSilent) then
403 write(*,*)
404 write(*,*)' --------------------------------------------------------------'
405 write(*,*)' - filt_topo: topographic filtering of the following obs family'
406 write(*,*)' --------------------------------------------------------------'
407 end if
408
409 if (any(filtTopoList(:) == 'SF')) call filt_topoSurface (columnTrlOnTrlLev,obsSpaceData,beSilent)
410 if (any(filtTopoList(:) == 'UA')) call filt_topoRadiosonde(columnTrlOnTrlLev,obsSpaceData,beSilent)
411 if (any(filtTopoList(:) == 'AI')) call filt_topoAISW (columnTrlOnTrlLev,obsSpaceData,'AI',beSilent)
412 if (any(filtTopoList(:) == 'SW')) call filt_topoAISW (columnTrlOnTrlLev,obsSpaceData,'SW',beSilent)
413 if (any(filtTopoList(:) == 'PR')) call filt_topoProfiler (columnTrlOnTrlLev,obsSpaceData,beSilent)
414 if (any(filtTopoList(:) == 'AL')) call filt_topoAladin (columnTrlOnTrlLev,obsSpaceData,beSilent)
415 if (any(filtTopoList(:) == 'TO')) call filt_topoTovs (columnTrlOnTrlLev,obsSpaceData,beSilent)
416 if (any(filtTopoList(:) == 'CH')) call filt_topoChemistry (columnTrlOnTrlLev,obsSpaceData,beSilent)
417
418 end if
419
420 end subroutine filt_topo
421
422 !--------------------------------------------------------------------------
423 ! filt_topoSurface
424 !--------------------------------------------------------------------------
425 subroutine filt_topoSurface(columnTrlOnTrlLev, obsSpaceData, beSilent)
426 !
427 !:Purpose: Refuse elements which are too far away from the surface.
428 ! Replace the pressure of elements which are slightly below
429 ! the model surface by the pressure of the trial field.
430 !
431 implicit none
432
433 ! Arguments:s
434 type(struct_columnData), intent(in) :: columnTrlOnTrlLev
435 type(struct_obs) , intent(inout) :: obsSpaceData
436 logical , intent(in) :: beSilent
437
438 ! Locals:
439 real(8) :: altitudeDiff
440 integer :: headerIndex, bodyIndex, familyIndex, elemIndex
441 integer :: ivnm,countAssim
442 integer :: countAcc(numElem),countRej(numElem)
443 integer, parameter :: numFamily = 3
444 character(len=2) :: list_family(numFamily)
445 character(len=4) :: varLevel
446
447 !
448 ! reset gps_gb_dzmax for gb gps ztd to value in namelist file
449 !
450 altDiffMax(findElemIndex(BUFR_NEZD)) = gps_gb_dzmax
451
452 if ( .not.beSilent ) then
453 write(*,*) ' '
454 write(*,*) ' filt_topoSurface: '
455 write(*,*) ' '
456 write(*,*) '*****************************************************'
457 write(*,222) 'ELEMENTS ',(elemList(elemIndex),elemIndex=1,numElem)
458 write(*,223) 'REJECTION BOUNDARY(METRE) ',(altDiffMax(elemIndex),elemIndex=1,numElem)
459 write(*,*) '*****************************************************'
460 write(*,*) ' '
461 end if
462
463 ! Loop over the families of interest
464 list_family(1) = 'SF'
465 list_family(2) = 'UA'
466 list_family(3) = 'GP'
467 FAMILY: do familyIndex = 1, numFamily
468
469 ! set counters to zero
470 countRej(:)=0
471 countAcc(:)=0
472
473 ! loop over all header indices of each family
474 call obs_set_current_header_list(obsSpaceData, &
475 list_family(familyIndex))
476 HEADER: do
477 headerIndex = obs_getHeaderIndex(obsSpaceData)
478 if (headerIndex < 0) exit HEADER
479
480 ! loop over all body indices (still in the same family)
481 call obs_set_current_body_list(obsSpaceData, headerIndex)
482 BODY: do
483 bodyIndex = obs_getBodyIndex(obsSpaceData)
484 if (bodyIndex < 0) exit BODY
485
486 ! skip this obs if it is not on height levels
487 if (obs_bodyElem_i(obsSpaceData,OBS_VCO,bodyIndex).ne.1) cycle BODY
488
489 ! skip this obs if already flagged to not be assimilated
490 if (obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == obs_notAssimilated) cycle BODY
491
492 ivnm = obs_bodyElem_i(obsSpaceData,OBS_VNM,bodyIndex)
493 varLevel = vnl_varLevelFromVarnum(ivnm)
494 altitudeDiff = abs( obs_bodyElem_r(obsSpaceData,OBS_PPP,bodyIndex) - &
495 col_getHeight(columnTrlOnTrlLev,col_getNumLev(columnTrlOnTrlLev,varLevel),headerIndex,varLevel) )
496 !
497 ! apply filter to selected elements
498 !
499 elemIndex = findElemIndex(ivnm)
500 if (elemIndex == -1) cycle BODY
501
502 if (altitudeDiff <= altDiffMax(elemIndex)) then
503 ! obs passes the acceptance criteria
504 countAcc(elemIndex) = countAcc(elemIndex)+1
505 else
506 ! obs does not pass the acceptance criteria, reject it
507 call obs_bodySet_i( obsSpaceData,OBS_FLG,bodyIndex, &
508 ibset(obs_bodyElem_i(obsSpaceData,OBS_FLG,bodyIndex),18) )
509 call obs_bodySet_i(obsSpaceData,OBS_ASS,bodyIndex,obs_notAssimilated)
510 countRej(elemIndex) = countRej(elemIndex)+1
511 end if
512 end do BODY
513 end do HEADER
514
515 if ( .not.beSilent ) then
516 write(*,*) ' '
517 write(*,*) '*****************************************************'
518 write(*,*) 'FAMILY = ',list_family(familyIndex)
519 write(*,222) 'ELEMENTS ', (elemList(elemIndex),elemIndex=1,numElem)
520 write(*,222) 'ACCEPTED ',(countAcc(elemIndex),elemIndex=1,numElem)
521 write(*,222) 'REJECTED ',(countRej(elemIndex),elemIndex=1,numElem)
522 write(*,*) '*****************************************************'
523 write(*,*) ' '
524 end if
525222 format(2x,a29,16(2x,i5))
526223 format(2x,a29,16(2x,f5.0))
527
528 end do FAMILY
529
530 countAssim=0
531 do bodyIndex=1,obs_numbody(obsSpaceData)
532 if ( obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == obs_assimilated) countAssim=countAssim+1
533 end do
534 if ( .not.beSilent ) write(*,'(1X," NUMBER OF DATA TO BE ASSIMILATED AFTER ADJUSTMENTS: ",i10)') countAssim
535 if ( .not.beSilent ) write(*,* ) ' '
536
537 end subroutine filt_topoSurface
538
539 !--------------------------------------------------------------------------
540 ! filt_topoRadiosonde
541 !--------------------------------------------------------------------------
542 subroutine filt_topoRadiosonde(columnTrlOnTrlLev, obsSpaceData, beSilent)
543 !
544 !:Purpose: Refuse elements which are too far away from the surface of the model
545 ! Refuse elements which are considered in the free atmosphere of
546 ! the RAOB but fall in the surface boundary layer of the model atmosphere.
547 !
548 implicit none
549
550 ! Arguments:
551 type(struct_columnData), intent(in) :: columnTrlOnTrlLev
552 type(struct_obs), intent(inout) :: obsSpaceData
553 logical, intent(in) :: beSilent
554
555 ! Locals:
556 integer :: headerIndex, bodyIndex, listIndex, elemIndex
557 integer :: ivnm, countAssim
558 integer :: itotacc(numElem), itotrej(numElem), isblrej(numElem)
559 integer :: igzacc(numElem), igzrej(numElem), ibndrej(numElem)
560 real(8) :: zval, obsPressure, altitudeDiff
561 real(8) :: obsSfcAltitude, colSfcAltitude, colPressureBelow, colPressureAbove, zdelp
562 logical :: llok
563 real(8) :: geopotential(1), height(1)
564 integer :: nlev_M
565 real(8) :: lat
566
567 if ( .not.beSilent ) then
568 write(*,*) ' '
569 write(*,*) ' filt_topoRadiosonde: '
570 write(*,*) ' '
571 write(*,*) '************************************************'
572 write(*,222) ' ELEMENTS ',(elemList(elemIndex),elemIndex=1,numElem)
573 write(*,223) ' REJECTION BOUNDARY(METRE) ',(altDiffMax(elemIndex),elemIndex=1,numElem)
574 write(*,223) ' REJECTION SBL (PASCAL) ',(surfaceBufferZone_Pres,elemIndex=1,numElem)
575 write(*,*) '************************************************'
576 write(*,*) ' '
577 end if
578
579 ! set counters to zero
580 itotrej(:)=0
581 itotacc(:)=0
582 isblrej(:)=0
583 igzacc(:)=0
584 igzrej(:)=0
585 ibndrej(:)=0
586
587 nlev_M = col_getNumLev(columnTrlOnTrlLev,'MM')
588
589 ! loop over all header indices of the 'UA' family
590 call obs_set_current_header_list(obsSpaceData, 'UA')
591 HEADER: do
592 headerIndex = obs_getHeaderIndex(obsSpaceData)
593 if (headerIndex < 0) exit HEADER
594
595 ! HEIGHT GZ
596
597 ! loop over all body indices (still in the 'UA' family)
598 call obs_set_current_body_list(obsSpaceData, headerIndex)
599 BODY: do
600 bodyIndex = obs_getBodyIndex(obsSpaceData)
601 if (bodyIndex < 0) exit BODY
602
603 ! skip this obs if it is not on pressure level
604 if( obs_bodyElem_i(obsSpaceData,OBS_VCO,bodyIndex).ne.2 ) cycle BODY
605
606 ! skip this obs if already flagged to not be assimilated
607 if( obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == obs_notAssimilated ) cycle BODY
608
609 ! skip this obs if it is not GZ
610 ivnm=obs_bodyElem_i(obsSpaceData,OBS_VNM,bodyIndex)
611 listIndex = findElemIndex(ivnm)
612 llok = (ivnm == BUFR_NEGZ .and. listIndex.ne.-1)
613 if (.not. llok ) cycle BODY
614
615 ! convert altitude read from column to geopotential
616 height(1) = col_getHeight(columnTrlOnTrlLev,0,headerIndex,'SF')
617 lat = obs_headElem_r(obsSpaceData,OBS_LAT,headerIndex)
618 call phf_height2geopotential(height,lat,geopotential)
619
620 zval = obs_bodyElem_r(obsSpaceData,OBS_VAR,bodyIndex)
621 altitudeDiff = ( zval - geopotential(1) )/ec_rg
622 ! obs is above surface, so it is ok, lets jump to the next obs
623 if(altitudeDiff >= 0.0d0) cycle BODY
624
625 if(altitudeDiff >= -1.0d0*altDiffMax(listIndex)) then
626 ! obs is an acceptably small distance below the surface
627 itotacc(listIndex) = itotacc(listIndex)+1
628 igzacc(listIndex) = igzacc(listIndex)+1
629 else
630 ! too far below surface, reject
631 call obs_bodySet_i(obsSpaceData,OBS_FLG,bodyIndex, &
632 ibset( obs_bodyElem_i(obsSpaceData,OBS_FLG,bodyIndex), 18 ))
633 call obs_bodySet_i(obsSpaceData,OBS_ASS,bodyIndex,obs_notAssimilated)
634 itotrej(listIndex) = itotrej(listIndex)+1
635 igzrej(listIndex) = igzrej(listIndex)+1
636 end if
637 end do BODY
638 !
639 ! REJECT ELEMENTS OF U,V,T-TD,T BELOW THE MODEL SURFACE
640 ! AND THOSE NON SURFACE ELEMENTS PRESENT IN THE SURFACE
641 ! BOUNDARY LAYER OF THE RAOB OR OF THE MODEL.
642 ! AT THIS POINT WE WANT TO KEEP OBSERVATIONS IN THE FREE
643 ! ATMOSPHERE
644 !
645 !---Special case if station elevation is above model elevation
646 ! we want to define colPressureAbove at a level above the station.
647 ! To approximate that value, we will transform the difference
648 ! between the 2 elevations into a difference in pressure using
649 ! the rule of thumb (1Mb =8 metres)
650 !---Even though TT(element=12001) is not assimmilated
651 ! it is treated as if it were for the evaluation step.
652 ! Otherwise we use observations of TT that are too far
653 ! from the model topography in the verification.
654
655 obsSfcAltitude = obs_headElem_r(obsSpaceData,OBS_ALT,headerIndex)
656 colSfcAltitude = col_getHeight(columnTrlOnTrlLev,0,headerIndex,'SF')
657 altitudeDiff = obsSfcAltitude - colSfcAltitude
658
659 ! Set the body list & start at the beginning of the list
660 call obs_set_current_body_list(obsSpaceData, headerIndex)
661 BODY2: do
662 bodyIndex = obs_getBodyIndex(obsSpaceData)
663 if (bodyIndex < 0) exit BODY2
664
665 if( obs_bodyElem_i(obsSpaceData,OBS_VCO,bodyIndex).ne.2 ) cycle BODY2
666
667 ! skip this obs if already flagged to not be assimilated
668 if( obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == obs_notAssimilated ) cycle BODY2
669
670 ivnm = obs_bodyElem_i(obsSpaceData,OBS_VNM,bodyIndex)
671 listIndex = findElemIndex(ivnm)
672 llok = (ivnm.ne.BUFR_NEGZ .and. listIndex.ne.-1)
673 if (.not. llok ) cycle BODY2 ! Proceed with the next bodyIndex
674
675 obsPressure = obs_bodyElem_r(obsSpaceData,OBS_PPP,bodyIndex)
676 colPressureBelow = col_getElem(columnTrlOnTrlLev,1,headerIndex,'P0')
677 colPressureAbove = colPressureBelow - surfaceBufferZone_Pres
678
679 if (useEnkfTopoFilt) then
680 ! Simpler rules used in the EnKF
681 if(obsPressure >= colPressureAbove ) then
682 if(abs(altitudeDiff) >= 50.0D0 .or. obsPressure >= colPressureBelow) then
683 call obs_bodySet_i(obsSpaceData,OBS_FLG,bodyIndex, &
684 ibset(obs_bodyElem_i(obsSpaceData,OBS_FLG,bodyIndex), 18 ))
685 call obs_bodySet_i(obsSpaceData,OBS_ASS,bodyIndex,obs_notAssimilated)
686 itotrej(listIndex) = itotrej(listIndex) + 1
687 ibndrej(listIndex) = ibndrej(listIndex) + 1
688 end if
689 end if
690 else
691 ! Original (and confusing) rules used in Var
692 if (altitudeDiff > 0.0d0) then
693 zdelp = altitudeDiff * 100.d0 / 8.0d0
694 colPressureAbove = colPressureBelow - (zdelp + surfaceBufferZone_Pres)
695 end if
696
697 if(abs(altitudeDiff) <= altDiffMax(listIndex)) then
698 !--Model surface and station altitude are very close
699 ! Accept observation if obsPressure is within the domain
700 ! of the trial field
701 colPressureAbove = col_getPressure(columnTrlOnTrlLev,col_getNumLev(columnTrlOnTrlLev,'MM')-1,headerIndex,'MM')
702 end if
703 if(obsPressure > colPressureBelow ) then
704 call obs_bodySet_i(obsSpaceData,OBS_FLG,bodyIndex, &
705 ibset(obs_bodyElem_i(obsSpaceData,OBS_FLG,bodyIndex), 18 ))
706 call obs_bodySet_i(obsSpaceData,OBS_ASS,bodyIndex,obs_notAssimilated)
707 itotrej(listIndex) = itotrej(listIndex) + 1
708 ibndrej(listIndex) = ibndrej(listIndex) + 1
709 else if(obsPressure <= colPressureBelow .and. obsPressure > colPressureAbove ) then
710 call obs_bodySet_i(obsSpaceData,OBS_FLG,bodyIndex, &
711 ibset( obs_bodyElem_i(obsSpaceData,OBS_FLG,bodyIndex), 18 ))
712 call obs_bodySet_i(obsSpaceData,OBS_ASS,bodyIndex,obs_notAssimilated)
713 itotrej(listIndex) = itotrej(listIndex) + 1
714 isblrej(listIndex) = isblrej(listIndex) + 1
715 end if
716 end if
717
718 end do BODY2
719 end do HEADER
720
721 if ( .not.beSilent ) then
722 write(*,*) ' '
723 write(*,*) '***************************************'
724 write(*,*) 'FAMILY = UA'
725 write(*,222) ' ELEMENTS ', (elemList(elemIndex),elemIndex=1,numElem)
726 write(*,222) ' ACC GZ EXT ',(igzacc(elemIndex),elemIndex=1,numElem)
727 write(*,222) ' ACC TOTAL ',(itotacc(elemIndex),elemIndex=1,numElem)
728 write(*,*) '***************'
729 write(*,222) ' REJ GZ EXT ',(igzrej(elemIndex),elemIndex=1,numElem)
730 write(*,222) ' REJ OUT BND ',(ibndrej(elemIndex),elemIndex=1,numElem)
731 write(*,222) ' REJ SBL ',(isblrej(elemIndex),elemIndex=1,numElem)
732 write(*,222) ' REJ TOTAL ',(itotrej(elemIndex),elemIndex=1,numElem)
733 write(*,*) '***************************************'
734 write(*,*) ' '
735 end if
736222 format(2x,a29,16(2x,i5))
737223 format(2x,a29,16(2x,f6.0))
738
739 countAssim=0
740 do bodyIndex=1,obs_numbody(obsSpaceData)
741 if ( obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == obs_assimilated) countAssim=countAssim+1
742 end do
743 if ( .not.beSilent ) write(*,'(1X," NUMBER OF DATA TO BE ASSIMILATED AFTER ADJUSTMENTS:",i10)') countAssim
744 if ( .not.beSilent ) write(*,*) ' '
745
746 end subroutine filt_topoRadiosonde
747
748 !--------------------------------------------------------------------------
749 ! filt_topoAISW
750 !--------------------------------------------------------------------------
751 subroutine filt_topoAISW(columnTrlOnTrlLev, obsSpaceData, obsFamily, beSilent)
752 !
753 !:Purpose: Refuse elements which are too close to the surface.
754 !
755 implicit none
756
757 ! Arguments:
758 type(struct_columnData), intent(in) :: columnTrlOnTrlLev
759 type(struct_obs), intent(inout) :: obsSpaceData
760 logical, intent(in) :: beSilent
761 character(len=2), intent(in) :: obsFamily
762
763 ! Locals:
764 integer :: headerIndex, bodyIndex, elemIndex, listIndex
765 integer :: ivnm, countRej(numElem), countAssim
766 real(8) :: obsPressure, pressureDiff
767
768 if (obsFamily /= 'AI' .and. obsFamily /= 'SW') then
769 call utl_abort('filt_topoAISW: only AI and SW family are handled by this routine. You ask for '//obsFamily)
770 end if
771
772 if ( .not.beSilent ) then
773 write(*,*) ' '
774 write(*,*) ' filt_topoAISW for obsFamily = ', obsFamily
775 write(*,*) ' '
776 write(*,*) '****************************************************'
777 write(*,222) 'ELEMENTS ', (elemList(elemIndex),elemIndex=1,numElem)
778 write(*,223) 'REJECTION BOUNDARY(HPA) ', (surfaceBufferZone_Pres,elemIndex=1,numElem)
779 write(*,*) '****************************************************'
780 write(*,*) ' '
781 end if
782
783 ! set counters to zero
784 countRej(:)=0
785
786 ! loop over all body indices of each family
787 call obs_set_current_body_list(obsSpaceData, obsFamily)
788 BODY: do
789 bodyIndex = obs_getBodyIndex(obsSpaceData)
790 if (bodyIndex < 0) exit BODY
791
792 ! skip this observation if already flagged to not assimilate
793 if(obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == obs_notAssimilated) cycle BODY
794
795 !
796 ! reject data too close to the model orography, put to
797 ! model orography, data which is below , but close to the surface.
798 !
799 obsPressure = obs_bodyElem_r(obsSpaceData,OBS_PPP,bodyIndex)
800 headerIndex = obs_bodyElem_i(obsSpaceData,OBS_HIND,bodyIndex)
801 pressureDiff = col_getElem(columnTrlOnTrlLev,1,headerIndex,'P0') - obsPressure
802 if ( pressureDiff < surfaceBufferZone_Pres ) then
803 ivnm=obs_bodyElem_i(obsSpaceData,OBS_VNM,bodyIndex)
804 listIndex = findElemIndex(ivnm)
805 if(listIndex == -1) cycle BODY
806 call obs_bodySet_i(obsSpaceData,OBS_ASS,bodyIndex,obs_notAssimilated)
807 countRej(listIndex)=countRej(listIndex)+1
808 call obs_bodySet_i(obsSpaceData,OBS_FLG,bodyIndex, &
809 ibset(obs_bodyElem_i(obsSpaceData,OBS_FLG,bodyIndex),18 ))
810 end if
811 end do BODY
812
813 if ( .not.beSilent ) then
814 write(*,*) ' '
815 write(*,*) '*****************************************************************'
816 write(*,*) ' FAMILY = ',obsFamily
817 write(*,222) 'ELEMENTS ',(elemList(elemIndex),elemIndex=1,numElem)
818 write(*,222) 'REJECTED ',(countRej(elemIndex),elemIndex=1,numElem)
819 write(*,*) '*****************************************************************'
820 write(*,*) ' '
821 end if
822
823 countAssim=0
824 do bodyIndex=1,obs_numbody(obsSpaceData)
825 if ( obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == obs_assimilated) countAssim=countAssim+1
826 end do
827 if ( .not.beSilent ) write(*,'(1X," NUMBER OF DATA TO BE ASSIMILATED AFTER ADJUSTMENTS:",i10)') countAssim
828 if ( .not.beSilent ) write(*,*) ' '
829
830222 format(2x,a29,16(2x,i5))
831223 format(2x,a29,16(2x,f5.0))
832
833end subroutine filt_topoAISW
834
835 !--------------------------------------------------------------------------
836 ! filt_topoProfiler
837 !--------------------------------------------------------------------------
838 subroutine filt_topoProfiler(columnTrlOnTrlLev,obsSpaceData,beSilent)
839 !
840 !:Purpose: Refuse elements which are too far away from the surface of the model
841 ! Refuse elements which are considered in the free atmosphere of
842 ! the RAOB but fall in the surface boundary layer of the model atmosphere.
843 !
844 implicit none
845
846 ! Arguments:
847 type(struct_columnData), intent(in) :: columnTrlOnTrlLev
848 type(struct_obs), intent(inout) :: obsSpaceData
849 logical, intent(in) :: beSilent
850
851 ! Locals:
852 integer :: headerIndex, bodyIndex, listIndex, elemIndex
853 integer :: ivnm, countAssim
854 integer :: itotrej(numElem), isblrej(numElem), ibndrej(numElem)
855 real(8) :: obsAltitude
856 real(8) :: obsSfcAltitude,colSfcAltitude,colAltitudeBelow,colAltitudeAbove
857 logical :: llok, list_is_empty
858
859 if ( .not.beSilent ) then
860 write(*,*) ' '
861 write(*,*) ' filt_topoProfiler: '
862 write(*,*) ' '
863 write(*,*) '************************************************'
864 write(*,222) ' ELEMENTS ',(elemList(elemIndex),elemIndex=1,numElem)
865 write(*,223) ' REJECTION BOUNDARY(METRE) ',(altDiffMax(elemIndex),elemIndex=1,numElem)
866 write(*,223) ' REJECTION SBL (METRE) ',(surfaceBufferZone_Height,elemIndex=1,numElem)
867 write(*,*) '************************************************'
868 write(*,*) ' '
869 end if
870
871 ! set counters to zero
872 itotrej(:)=0
873 isblrej(:)=0
874 ibndrej(:)=0
875
876 ! loop over all header indices of the 'PR' family
877 call obs_set_current_header_list(obsSpaceData, 'PR')
878 HEADER: do
879 headerIndex = obs_getHeaderIndex(obsSpaceData)
880 if (headerIndex < 0) exit HEADER
881
882 ! Set the body list & start at the beginning of the list
883 call obs_set_current_body_list(obsSpaceData, headerIndex,list_is_empty)
884 if (list_is_empty) cycle HEADER ! Proceed to the next HEADER
885
886 !
887 ! REJECT OBS BELOW THE MODEL SURFACE
888 ! AND THOSE NON SURFACE ELEMENTS PRESENT IN THE SURFACE
889 ! BOUNDARY LAYER OF THE RAOB OR OF THE MODEL.
890 ! AT THIS POINT WE WANT TO KEEP OBSERVATIONS IN THE FREE
891 ! ATMOSPHERE
892 !
893 colSfcAltitude = col_getHeight(columnTrlOnTrlLev,0,headerIndex,'SF')
894 obsSfcAltitude = obs_headElem_r(obsSpaceData,OBS_ALT,headerIndex)
895
896 ! loop over all body indices (still in the 'PR' family)
897 BODY: do
898 bodyIndex = obs_getBodyIndex(obsSpaceData)
899 if (bodyIndex < 0) exit BODY
900
901 ! skip this obs if already flagged to not be assimilated
902 if (obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == obs_notAssimilated) cycle BODY
903
904 ivnm = obs_bodyElem_i(obsSpaceData,OBS_VNM,bodyIndex)
905 listIndex = findElemIndex(ivnm)
906 llok = (obs_bodyElem_i(obsSpaceData,OBS_VCO,bodyIndex) == 1 &
907 .and. ivnm /= BUFR_NEGZ .and. listIndex /= -1)
908 if (.not. llok ) cycle BODY ! Proceed to the next bodyIndex
909
910 obsAltitude = obs_bodyElem_r(obsSpaceData,OBS_PPP,bodyIndex)
911 colAltitudeBelow = colSfcAltitude
912 if (obsSfcAltitude > colSfcAltitude) then
913 colAltitudeAbove = obsSfcAltitude + surfaceBufferZone_Height
914 else
915 colAltitudeAbove = colSfcAltitude + surfaceBufferZone_Height
916 end if
917 if(abs(obsSfcAltitude-colSfcAltitude) <= altDiffMax(listIndex)) then
918 !----Model surface and station altitude are very close
919 ! Accept observation if obsAltitude is within the domain
920 ! of the trial field
921 colAltitudeBelow = colSfcAltitude
922 colAltitudeAbove = col_getHeight(columnTrlOnTrlLev,col_getNumLev(columnTrlOnTrlLev,'MM')-1,headerIndex,'MM')
923 end if
924 if(obsAltitude < colAltitudeBelow ) then
925 call obs_bodySet_i(obsSpaceData,OBS_FLG,bodyIndex, &
926 ibset( obs_bodyElem_i(obsSpaceData,OBS_FLG,bodyIndex), 18 ))
927 call obs_bodySet_i(obsSpaceData,OBS_ASS,bodyIndex,obs_notAssimilated)
928 itotrej(listIndex)=itotrej(listIndex)+1
929 ibndrej(listIndex)=ibndrej(listIndex)+1
930 else if(obsAltitude >= colAltitudeBelow .and. obsAltitude < colAltitudeAbove ) then
931 call obs_bodySet_i(obsSpaceData,OBS_FLG,bodyIndex, &
932 ibset( obs_bodyElem_i(obsSpaceData,OBS_FLG,bodyIndex), 18 ))
933 call obs_bodySet_i(obsSpaceData,OBS_ASS,bodyIndex,obs_notAssimilated)
934 itotrej(listIndex)=itotrej(listIndex)+1
935 isblrej(listIndex)=isblrej(listIndex)+1
936 end if
937 end do BODY
938 end do HEADER
939
940 if ( .not.beSilent ) then
941 write(*,*) ' '
942 write(*,*) '***************************************'
943 write(*,*) 'FAMILY = PR'
944 write(*,222) ' ELEMENTS ', (elemList(elemIndex),elemIndex=1,numElem)
945 write(*,*) '************'
946 write(*,222) ' REJ OUT BND ',(ibndrej(elemIndex),elemIndex=1,numElem)
947 write(*,222) ' REJ SBL ',(isblrej(elemIndex),elemIndex=1,numElem)
948 write(*,222) ' REJ TOTAL ',(itotrej(elemIndex),elemIndex=1,numElem)
949 write(*,*) '***************************************'
950 write(*,*) ' '
951 end if
952222 format(2x,a29,16(2x,i5))
953223 format(2x,a29,16(2x,f6.0))
954
955 countAssim=0
956 do bodyIndex=1,obs_numbody(obsSpaceData)
957 if (obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == obs_assimilated) countAssim=countAssim+1
958 end do
959 if ( .not.beSilent ) write(*,'(1X," NUMBER OF DATA TO BE ASSIMILATED AFTER ADJUSTMENTS:",i10)') countAssim
960 if ( .not.beSilent ) write(*,*) ' '
961
962 end subroutine filt_topoProfiler
963
964 !--------------------------------------------------------------------------
965 ! filt_topoAladin
966 !--------------------------------------------------------------------------
967 subroutine filt_topoAladin(columnTrlOnTrlLev, obsSpaceData, beSilent)
968 !
969 !:Purpose: Refuse elements which are considered to be in the free atmosphere
970 ! of the Aladin instrument but which fall in the surface boundary
971 ! layer of the model atmosphere.
972 !
973 implicit none
974
975 ! Arguments:
976 type(struct_columnData), intent(in) :: columnTrlOnTrlLev
977 type(struct_obs), intent(inout) :: obsSpaceData
978 logical, intent(in) :: beSilent
979
980 ! Locals:
981 integer :: headerIndex, bodyIndex, elemIndex
982 integer :: ivnm, countAssim
983 integer :: countAcc(numElem), countRej(numElem)
984 real(8) :: obsAltitude ! altitide of the observation
985 real(8) :: colSfcAltitude ! altitude of the model's lowest layer
986 real(8) :: colAltitudeAbove ! top of the boundary layer
987 logical :: list_is_empty
988
989 if(.not. beSilent )then
990 write(*,*) ' '
991 write(*,*) ' filt_topoAladin: '
992 write(*,*) ' '
993 write(*,*) '************************************************'
994 write(*,222) ' ELEMENTS ',(elemList(elemIndex),elemIndex=1,numElem)
995 write(*,223) ' REJECTION SBL (METRE) ',(surfaceBufferZone_Height,elemIndex=1,numElem)
996 write(*,*) '************************************************'
997 write(*,*) ' '
998 end if
999
1000 ! set counter to zero
1001 countAcc(:)=0
1002 countRej(:)=0
1003
1004 ! loop over all header indices of the 'AL' family
1005 call obs_set_current_header_list(obsSpaceData, 'AL')
1006 HEADER: do
1007 headerIndex = obs_getHeaderIndex(obsSpaceData)
1008 if (headerIndex < 0) exit HEADER
1009
1010 ! Set the body list & start at the beginning of the list
1011 call obs_set_current_body_list(obsSpaceData, headerIndex,list_is_empty)
1012 if (list_is_empty) cycle HEADER ! Proceed to the next HEADER
1013
1014 !
1015 ! REJECT OBS IN THE SURFACE BOUNDARY LAYER OF THE MODEL.
1016 ! AT THIS POINT WE WANT TO KEEP OBSERVATIONS THAT ARE IN THE FREE
1017 ! ATMOSPHERE
1018 !
1019 colSfcAltitude = col_getHeight(columnTrlOnTrlLev,col_getNumLev(columnTrlOnTrlLev,'MM'), &
1020 headerIndex,'MM')
1021 colAltitudeAbove = colSfcAltitude + surfaceBufferZone_Height
1022
1023 ! Loop over all body indices (still in the 'AL' family)
1024 BODY: do
1025 bodyIndex = obs_getBodyIndex(obsSpaceData)
1026 if (bodyIndex < 0) exit BODY
1027
1028 ! Skip this obs if it is not on height levels
1029 if (obs_bodyElem_i(obsSpaceData,OBS_VCO,bodyIndex) /= 1) cycle BODY
1030
1031 ! Skip this obs if already flagged not to be assimilated
1032 if (obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == obs_notAssimilated) cycle BODY
1033
1034 ! Skip this obs if it is not in the list of element types
1035 ivnm = obs_bodyElem_i(obsSpaceData,OBS_VNM,bodyIndex)
1036 elemIndex = findElemIndex(ivnm)
1037 if(elemIndex == -1) cycle BODY
1038
1039
1040 !
1041 ! apply filter to selected elements
1042 !
1043
1044 ! Reject this obs if it is in the boundary layer or below
1045 obsAltitude = obs_bodyElem_r(obsSpaceData,OBS_PPP,bodyIndex)
1046 if(obsAltitude > colAltitudeAbove) then
1047 ! obs passes the acceptance criterion
1048 countAcc(elemIndex) = countAcc(elemIndex)+1
1049
1050 else
1051 ! Flag rejection due to orography
1052 call obs_bodySet_i(obsSpaceData,OBS_FLG,bodyIndex, &
1053 ibset( obs_bodyElem_i(obsSpaceData,OBS_FLG,bodyIndex), 18 ))
1054
1055 ! Do not assimilate the observation
1056 call obs_bodySet_i(obsSpaceData,OBS_ASS,bodyIndex,obs_notAssimilated)
1057 countRej(elemIndex)=countRej(elemIndex)+1
1058 end if
1059 end do BODY
1060 end do HEADER
1061
1062 if(.not. besilent)then
1063 write(*,*) ' '
1064 write(*,*) '***************************************'
1065 write(*,*) 'FAMILY = AL'
1066 write(*,222) ' ELEMENTS ', (elemList(elemIndex),elemIndex=1,numElem)
1067 write(*,222) ' ACCEPTED ', (countAcc(elemIndex),elemIndex=1,numElem)
1068 write(*,222) ' REJECTED ', (countRej(elemIndex),elemIndex=1,numElem)
1069 write(*,*) '***************************************'
1070 write(*,*) ' '
1071 end if
1072222 format(2x,a29,16(2x,i5))
1073223 format(2x,a29,16(2x,f6.0))
1074
1075 countAssim=0
1076 do bodyIndex=1,obs_numbody(obsSpaceData)
1077 if (obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == obs_assimilated) countAssim=countAssim+1
1078 end do
1079
1080 if(.not. besilent)then
1081 write(*,'(1X," NUMBER OF DATA TO BE ASSIMILATED AFTER ADJUSTMENTS:",i10)') countAssim
1082 write(*,*) ' '
1083 end if
1084
1085 end subroutine filt_topoAladin
1086
1087 !--------------------------------------------------------------------------
1088 ! filt_topoTovs
1089 !--------------------------------------------------------------------------
1090 subroutine filt_topoTovs(columnTrlOnTrlLev, obsSpaceData, beSilent)
1091 !
1092 !:Purpose: Refuse data which are too close to the surface.
1093 !
1094 implicit none
1095
1096 ! Arguments:
1097 type(struct_columnData), intent(in) :: columnTrlOnTrlLev
1098 type(struct_obs), intent(inout) :: obsSpaceData
1099 logical, intent(in) :: beSilent
1100
1101 ! Locals:
1102 integer :: headerIndex, bodyIndex
1103 integer :: idatyp, countAssim, countRej
1104 real(8), parameter :: minSfcPressure = 80000.d0
1105
1106 if ( .not.beSilent ) then
1107 write(*,* ) ' '
1108 write(*,* ) ' filt_topoTovs: '
1109 write(*,* ) ' '
1110 write(*,* ) '****************************************************'
1111 write(*,222) 'ELEMENTS ', BUFR_NBT3
1112 write(*,223) 'MINIMUM SFC PRESSURE (PA) ', minSfcPressure
1113 write(*,* ) '****************************************************'
1114 write(*,* ) ' '
1115 end if
1116
1117 ! set counters to zero
1118 countRej=0
1119
1120 ! loop over all header indices of the 'TO' family
1121 call obs_set_current_header_list(obsSpaceData, 'TO')
1122 HEADER: do
1123 headerIndex = obs_getHeaderIndex(obsSpaceData)
1124 if (headerIndex < 0) exit HEADER
1125
1126 idatyp = obs_headElem_i(obsSpaceData,OBS_ITY,headerIndex)
1127 if (idatyp .ne. 185) cycle HEADER ! Proceed to the next headerIndex
1128
1129 ! loop over all body indices (still in the 'TO' family)
1130 call obs_set_current_body_list(obsSpaceData, headerIndex)
1131 BODY: do
1132 bodyIndex = obs_getBodyIndex(obsSpaceData)
1133 if (bodyIndex < 0) exit BODY
1134
1135 if (obs_bodyElem_i(obsSpaceData,OBS_VNM,bodyIndex).ne.BUFR_NBT3) cycle BODY
1136
1137 ! reject obs if the model surface pressure is below the minimum specified value
1138 if (col_getElem(columnTrlOnTrlLev,1,headerIndex,'P0') < minSfcPressure) then
1139 call obs_bodySet_i(obsSpaceData,OBS_ASS,bodyIndex,obs_notAssimilated)
1140 countRej=countRej+1
1141 call obs_bodySet_i(obsSpaceData,OBS_FLG,bodyIndex, &
1142 ibset(obs_bodyElem_i(obsSpaceData,OBS_FLG,bodyIndex),9))
1143 call obs_bodySet_i(obsSpaceData,OBS_FLG,bodyIndex, &
1144 ibset(obs_bodyElem_i(obsSpaceData,OBS_FLG,bodyIndex),18))
1145 end if
1146
1147 end do BODY
1148 end do HEADER
1149
1150 if ( .not.beSilent ) then
1151 write(*,*) ' '
1152 write(*,*) '*****************************************************************'
1153 write(*,*) ' FAMILY = TO'
1154 write(*,222) 'ELEMENTS ', BUFR_NBT3
1155 write(*,222) 'REJECTED ',countRej
1156 write(*,*) '*****************************************************************'
1157 write(*,*) ' '
1158 end if
1159222 format(2x,a29,1(4x,i5))
1160223 format(2x,a29,1(2x,f7.0))
1161
1162 countAssim=0
1163 do bodyIndex=1,obs_numbody(obsSpaceData)
1164 if ( obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == obs_assimilated) countAssim=countAssim+1
1165 end do
1166 if ( .not.beSilent ) write(*,'(1X," NUMBER OF DATA TO BE ASSIMILATED AFTER ADJUSTMENTS:",i10)') countAssim
1167 if ( .not.beSilent ) write(*,* ) ' '
1168
1169 end subroutine filt_topoTovs
1170
1171 !--------------------------------------------------------------------------
1172 ! filt_surfaceWind
1173 !--------------------------------------------------------------------------
1174 subroutine filt_surfaceWind(obsSpaceData, beSilent)
1175 !
1176 !:Purpose: zap sfc wind components at land stations
1177 !
1178 IMPLICIT NONE
1179
1180 ! Arguments:
1181 type(struct_obs), intent(inout) :: obsSpaceData
1182 logical, intent(in) :: beSilent
1183
1184 ! Locals:
1185 INTEGER, parameter :: JPINEL=2,JPIDLND=9
1186 INTEGER :: J,JID,JDATA
1187 LOGICAL :: LLPRINT
1188 INTEGER :: ITYP,IDBURP
1189 INTEGER :: ILISTEL(JPINEL), IDLND(JPIDLND)
1190 INTEGER :: IKOUNTREJ(JPINEL), IKOUNTT
1191 character(len=2), dimension(2) :: list_family
1192 integer :: index_family, index_header, index_body
1193 character(len=12) :: stnid
1194 real(pre_obsReal) :: obsLAT, obsLON, obsPPP
1195
1196 DATA IDLND / 12, 14, 146, 32, 35, 135, 136, 137, 138 /
1197 !
1198 if ( .not. discardlandsfcwind ) return
1199 !
1200 ILISTEL(1)=BUFR_NEUS
1201 ILISTEL(2)=BUFR_NEVS
1202 if ( .not.beSilent ) then
1203 WRITE(*,* ) ' '
1204 WRITE(*,* ) ' filt_surfaceWind:'
1205 WRITE(*,* ) ' '
1206 WRITE(*,* ) '*****************************************************'
1207 WRITE(*,222)'ELEMENTS REJECTED ',( ILISTEL(J),J=1,jpinel)
1208 WRITE(*,222)'LIST OF IDTYP ',( idlnd(J),J=1,jpidlnd)
1209 WRITE(*,* ) '*****************************************************'
1210 WRITE(*,* ) ' '
1211 end if
1212 LLPRINT = .FALSE.
1213 !cc LLPRINT = .TRUE.
1214 !
1215 ! SET COUNTERS TO ZERO
1216 !
1217 DO J=1,JPINEL
1218 IKOUNTREJ(J)=0
1219 END DO
1220
1221 !
1222 ! Loop over the families of interest
1223 !
1224 list_family(1) = 'SF'
1225 list_family(2) = 'UA'
1226 do index_family = 1,2
1227 if ( .not.beSilent ) WRITE(*,'(2x,A9,2x,A2)')'FAMILY = ',list_family(index_family)
1228
1229 !
1230 ! loop over all header indices of each family
1231 !
1232 ! Set the header list
1233 ! (& start at the beginning of the list)
1234 call obs_set_current_header_list(obsSpaceData, &
1235 list_family(index_family))
1236 HEADER: do
1237 index_header = obs_getHeaderIndex(obsSpaceData)
1238 if (index_header < 0) exit HEADER
1239
1240 !
1241 ! loop over all body indices (still in the same family)
1242 !
1243 ! Set the body list
1244 ! (& start at the beginning of the list)
1245 call obs_set_current_body_list(obsSpaceData, index_header)
1246 BODY: do
1247 index_body = obs_getBodyIndex(obsSpaceData)
1248 if (index_body < 0) exit BODY
1249
1250 ! UNCONDITIONALLY REJECT SURFACE WINDS AT SYNOP/TEMP LAND STATIONS
1251 ITYP=obs_bodyElem_i(obsSpaceData,OBS_VNM,INDEX_BODY)
1252 IDBURP = obs_headElem_i(obsSpaceData,OBS_ITY,INDEX_HEADER)
1253 IF ( ITYP == BUFR_NEUS .OR. ITYP == BUFR_NEVS) THEN
1254 DO JID = 1, JPIDLND
1255 IF(IDBURP == IDLND(JID) .AND. &
1256 obs_bodyElem_i(obsSpaceData,OBS_ASS,INDEX_BODY) == obs_assimilated) THEN
1257 call obs_bodySet_i(obsSpaceData,OBS_FLG,INDEX_BODY, &
1258 ibset( obs_bodyElem_i(obsSpaceData,OBS_FLG,INDEX_BODY), 19))
1259 call obs_bodySet_i(obsSpaceData,OBS_ASS,INDEX_BODY,obs_notAssimilated)
1260 DO J = 1, JPINEL
1261 IF(ITYP ==ILISTEL(J)) THEN
1262 IKOUNTREJ(J)=IKOUNTREJ(J)+1
1263 END IF
1264 END DO
1265 IF(LLPRINT .and. .not.beSilent ) THEN
1266 stnid = obs_elem_c(obsSpaceData,'STID',INDEX_HEADER)
1267 obsLAT = obs_headElem_r(obsSpaceData,OBS_LAT,INDEX_HEADER)
1268 obsLON = obs_headElem_r(obsSpaceData,OBS_LON,INDEX_HEADER)
1269 obsPPP = obs_bodyElem_r(obsSpaceData,OBS_PPP,INDEX_BODY)
1270 WRITE(*,225) 'Rej sfc wind lnd',INDEX_HEADER,ITYP,stnid, &
1271 IDBURP, obsLAT, obsLON, obsPPP
1272 END IF
1273 END IF
1274 END DO
1275 END IF ! BUFR_NEUS or BUFR_NEVS
1276 END DO BODY
1277 END DO HEADER
1278 !
1279 if ( .not.beSilent ) then
1280 WRITE(*,* ) ' '
1281 WRITE(*,* ) '*****************************************************'
1282 WRITE(*,222 )'ELEMENTS ', ( ILISTEL(J),J=1,JPINEL)
1283 WRITE(*,222)'REJECTED ',(IKOUNTREJ(J),J=1,JPINEL)
1284 WRITE(*,* ) '*****************************************************'
1285 WRITE(*,* ) ' '
1286 END IF
1287222 FORMAT(2x,a29,10(2x,i5))
1288223 FORMAT(2x,a29,10(2x,f5.0))
1289224 FORMAT(2x,a17,2x,I6,2X,I5,1x,a9,1x,2(2x,f9.2))
1290225 FORMAT(2x,a13,2x,I6,2X,I5,1x,a9,1x,I6,1x,3(2x,f9.2))
1291 !
1292 END DO ! family
1293 !
1294 IKOUNTT=0
1295 DO JDATA=1,obs_numbody(obsSpaceData)
1296 IF ( obs_bodyElem_i(obsSpaceData,OBS_ASS,JDATA) == obs_assimilated) IKOUNTT=IKOUNTT+1
1297 END DO
1298 if ( .not.beSilent ) WRITE(*, &
1299 '(1X," NUMBER OF DATA ASSIMILATED BY MIDAS AFTER ADJUSTMENTS: ",i10)') &
1300 IKOUNTT
1301 if ( .not.beSilent ) WRITE(*,* ) ' '
1302
1303 end subroutine filt_surfaceWind
1304
1305 !--------------------------------------------------------------------------
1306 ! filt_radvel
1307 !--------------------------------------------------------------------------
1308 subroutine filt_radvel(columnTrlOnTrlLev, obsSpaceData, beSilent)
1309 !
1310 !:Purpose: Filter Radvel observations
1311 ! guarantee that altitude values are
1312 ! within bounds Altitude and check the horizontal distance between levels
1313 ! for further processing
1314 implicit none
1315
1316 ! Arguments:
1317 type(struct_columnData), intent(in) :: columnTrlOnTrlLev
1318 type(struct_obs) , intent(inout) :: obsSpaceData
1319 logical , intent(in) :: beSilent
1320
1321 ! Locals:
1322 integer :: bodyIndex, headerIndex, numLevels, bufrCode, obsflag
1323 integer :: fnom, fclos, nulnam, ierr, levelIndex
1324 real(8) :: obsAltitude, radarAltitude, beamElevation
1325 real(8) :: levelAltLow, levelAltHigh
1326 real(8) :: levelBracketLow, levelBracketHigh
1327 real(8) :: levelRangeNear, levelRangeFar
1328 logical, save :: firstCall = .true.
1329
1330 ! Namelist variables:
1331 real(8), save :: maxRangeInterp ! max allowable horizontal distance between levels (in m) for radar winds
1332
1333 namelist /namradvel/ maxRangeInterp
1334
1335 if (.not.beSilent) then
1336 write(*,*)
1337 write(*,*) 'filt_radvel: begin'
1338 end if
1339
1340 ! reading namelist variables
1341 if (firstCall) then
1342 ! default value
1343 maxRangeInterp = -1.0D0
1344
1345 if ( utl_isNamelistPresent('namradvel', './flnml') ) then
1346 nulnam=0
1347 ierr=fnom(nulnam, './flnml', 'FTN+SEQ+R/O', 0)
1348 read(nulnam, nml=namradvel, iostat=ierr)
1349 if ( ierr /= 0 ) call utl_abort('oop_raDvel_nl: Error reading namelist namradvel')
1350 if ( .not. beSilent ) write(*,nml=namradvel)
1351 ierr = fclos(nulnam)
1352 else if ( .not. beSilent ) then
1353 write(*,*)
1354 write(*,*) 'filt_radvel: namradvel is missing in the namelist. The default value will be taken.'
1355 end if
1356 firstCall = .false.
1357 end if
1358 !
1359 ! Loop over all header indices of the 'RA' family (Doppler Velocity)
1360 !
1361 call obs_set_current_header_list(obsSpaceData, 'RA')
1362 HEADER: do
1363 headerIndex = obs_getHeaderIndex(obsSpaceData)
1364 if ( headerIndex < 0 ) exit HEADER
1365 !
1366 numLevels = col_getNumLev(columnTrlOnTrlLev, 'MM')
1367 ! Elevation beam (PPI)
1368 beamElevation = obs_headElem_r(obsSpaceData, OBS_RELE, headerIndex)
1369 ! Altitude radar
1370 radarAltitude = obs_headElem_r(obsSpaceData, OBS_ALT, headerIndex)
1371 !
1372 ! Loop over all body indices of the 'RA' family (Doppler Velocity)
1373 !
1374 call obs_set_current_body_list(obsSpaceData, headerIndex)
1375 BODY: do
1376 bodyIndex = obs_getBodyIndex(obsSpaceData)
1377 if ( bodyIndex < 0 ) exit BODY
1378 ! Check that this observation has the expected bufr element ID
1379 bufrCode = obs_bodyElem_i(obsSpaceData, OBS_VNM, bodyIndex)
1380 if ( bufrCode /= bufr_radvel ) cycle BODY
1381 ! only process observations flagged to be assimilated
1382 if ( obs_bodyElem_i(obsSpaceData, OBS_ASS, bodyIndex) /= obs_assimilated ) cycle BODY
1383
1384 ! Altitude of observation
1385 obsAltitude = obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex)
1386
1387 ! TODO we need to understand why model height at "numLevels" is not always the lowest
1388 ! Observations are rejected if their altitude is below the height 1 which may not be lowest model level...
1389 levelAltLow = col_getHeight(columnTrlOnTrlLev, numLevels, headerIndex,'MM')
1390 if ( obsAltitude < levelAltLow ) then
1391 call obs_bodySet_i(obsSpaceData, OBS_ASS, bodyindex, obs_notAssimilated)
1392 obsFlag = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyindex)
1393 call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyindex, IBSET(obsFlag, 11))
1394 cycle BODY
1395 end if
1396
1397 ! Observations are rejected if observation is above the height of first (highest) model level
1398 levelAltHigh = col_getHeight(columnTrlOnTrlLev, 1, headerIndex,'MM')
1399 if ( obsAltitude > levelAltHigh ) then
1400 call obs_bodySet_i(obsSpaceData, OBS_ASS, bodyindex, obs_notAssimilated)
1401 obsFlag = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyindex)
1402 call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyindex, IBSET(obsFlag, 11))
1403 cycle BODY
1404 end if
1405
1406 ! Levels that bracket the observation from OBS_LYR
1407 ! note to self: like in GEM, level=1 is the highest level
1408 levelIndex = obs_bodyElem_i(obsSpaceData, OBS_LYR, bodyIndex)
1409 levelBracketHigh = col_getHeight(columnTrlOnTrlLev, levelIndex, headerIndex,'MM')
1410 levelBracketLow = col_getHeight(columnTrlOnTrlLev, levelIndex+1, headerIndex,'MM')
1411
1412 ! Observations are rejected if horizontal distance between levels is too large
1413 if ( maxRangeInterp > 0.0 ) then
1414 call rdv_getRangefromH(levelBracketHigh, radarAltitude, beamElevation, levelRangeFar )
1415 call rdv_getRangefromH(levelBracketLow, radarAltitude, beamElevation, levelRangeNear)
1416
1417 if ( abs(levelRangeFar-levelRangeNear) > maxRangeInterp ) then
1418 call obs_bodySet_i(obsSpaceData, OBS_ASS, bodyindex, obs_notAssimilated)
1419 obsFlag = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyindex)
1420 call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyindex, IBSET(obsFlag, 11))
1421 cycle BODY
1422 end if
1423 end if
1424
1425 end do BODY
1426 end do HEADER
1427 if ( .not. beSilent ) write(*,*) 'filt_radvel: end'
1428 end subroutine filt_radvel
1429
1430 ! filt_gpsro
1431 !--------------------------------------------------------------------------
1432 subroutine FILT_GPSRO(columnTrlOnTrlLev, obsSpaceData, beSilent)
1433 !
1434 !:Purpose: Filter GPSRO observations
1435 ! Guarantee that altitude and observation values are
1436 ! within bounds for further processing
1437 !
1438 !:Note: For noncompliant GPSRO observations:
1439 !
1440 ! - Set assimilable flag to 0
1441 ! - Set bit of cma flag 11 ON
1442 !
1443 use gps_mod
1444 implicit none
1445
1446 ! Arguments:
1447 type(struct_columnData), intent(in) :: columnTrlOnTrlLev
1448 type(struct_obs) , intent(inout) :: obsSpaceData
1449 logical , intent(in) :: beSilent
1450
1451 ! Locals:
1452 INTEGER :: INDEX_HEADER, IDATYP, INDEX_BODY
1453 INTEGER :: JL, ISAT, IQLF, iProfile, IFLG, varNum, IDSC
1454 REAL(8) :: ZMT, Rad, Geo, AZM
1455 REAL(8) :: HNH1, HSF, HTP, HMIN, HMAX, ZOBS, ZREF, ZSAT
1456 LOGICAL :: LLEV, LOBS, LNOM, LSAT, LAZM, LALL, LDSC
1457
1458 if (.not.beSilent) then
1459 write(*,*)
1460 write(*,*) 'filt_gpsro: begin'
1461 end if
1462 !
1463 ! Loop over all header indices of the 'RO' family:
1464 !
1465 call obs_set_current_header_list(obsSpaceData,'RO')
1466 gps_numROProfiles=0
1467 HEADER: do
1468 index_header = obs_getHeaderIndex(obsSpaceData)
1469 if (index_header < 0) exit HEADER
1470 !
1471 ! Process only refractivity data (codtyp 169):
1472 !
1473 IDATYP = obs_headElem_i(obsSpaceData,OBS_ITY,INDEX_HEADER)
1474 if ( IDATYP == 169 ) then
1475 gps_numROProfiles=gps_numROProfiles+1
1476 !
1477 ! Basic variables of the profile:
1478 !
1479 AZM = obs_headElem_r(obsSpaceData,OBS_AZA ,INDEX_HEADER)
1480 ISAT = obs_headElem_i(obsSpaceData,OBS_SAT ,INDEX_HEADER)
1481 IQLF = obs_headElem_i(obsSpaceData,OBS_ROQF,INDEX_HEADER)
1482 Rad = obs_headElem_r(obsSpaceData,OBS_TRAD,INDEX_HEADER)
1483 Geo = obs_headElem_r(obsSpaceData,OBS_GEOI,INDEX_HEADER)
1484 LNOM = .NOT.BTEST(IQLF,16-1)
1485 LAZM = .TRUE.
1486 !
1487 ! Check if the satellite is within the accepted set:
1488 !
1489 ZSAT = ABS(gps_WGPS(ISAT,1))+ABS(gps_WGPS(ISAT,2))+ABS(gps_WGPS(ISAT,3))+ABS(gps_WGPS(ISAT,4))
1490 LSAT = ( ZSAT > 0.d0 )
1491 !
1492 ZMT = col_getHeight(columnTrlOnTrlLev,0,index_header,'SF')
1493 !
1494 ! Acceptable height limits:
1495 !
1496 JL = 1
1497 HTP = col_getHeight(columnTrlOnTrlLev,JL,INDEX_HEADER,'TH')
1498 HSF = ZMT+gps_SurfMin
1499 IF (HSF < gps_HsfMin) HSF=gps_HsfMin
1500 IF (HTP > gps_HtpMax) HTP=gps_HtpMax
1501 HMIN=Geo+HSF
1502 HMAX=Geo+HTP
1503 !
1504 ! Loop over all body indices for this index_header:
1505 ! (start at the beginning of the list)
1506 !
1507 call obs_set_current_body_list(obsSpaceData, INDEX_HEADER)
1508 BODY: do
1509 index_body = obs_getBodyIndex(obsSpaceData)
1510 if (index_body < 0) exit BODY
1511 !
1512 ! Altitude and reference order of magnitude value:
1513 !
1514 HNH1= obs_bodyElem_r(obsSpaceData,OBS_PPP,INDEX_BODY)
1515 varNum = obs_bodyElem_i(obsSpaceData,OBS_VNM,INDEX_BODY)
1516 if (varNum == bufr_nebd) then
1517 HNH1=HNH1-Rad
1518 ZREF = 0.025d0*exp(-HNH1/6500.d0)
1519 LAZM = (-0.1d0 < AZM .AND. AZM < 360.1)
1520 else
1521 ZREF = 300.d0*exp(-HNH1/6500.d0)
1522 LAZM = .TRUE.
1523 end if
1524 !
1525 ! Observation:
1526 !
1527 ZOBS= obs_bodyElem_r(obsSpaceData,OBS_VAR,INDEX_BODY)
1528 !
1529 ! Positively verify that the altitude is within bounds:
1530 !
1531 LLEV= (HNH1 > HMIN) .AND. (HNH1 < HMAX)
1532 !
1533 ! Positively verify that the observable is within bounds:
1534 !
1535 LOBS= (ZOBS > (0.3d0*ZREF)) .AND. (ZOBS < (3.d0*ZREF))
1536 !
1537 ! Mark as not assimilable unless all conditions are satisfied:
1538 !
1539 LALL = LLEV .AND. LOBS .AND. LAZM .AND. LNOM .AND. LSAT
1540 if ( .NOT.LALL ) then
1541 call obs_bodySet_i(obsSpaceData,OBS_ASS,INDEX_BODY, obs_notAssimilated)
1542 IFLG = obs_bodyElem_i(obsSpaceData,OBS_FLG,INDEX_BODY)
1543 call obs_bodySet_i(obsSpaceData,OBS_FLG,INDEX_BODY, IBSET(IFLG,11))
1544 end if
1545 ! Do not assimilate bending in mode gps_Level_RO = gps_Level_RO_Ref:
1546 if (varNum == bufr_nebd .and. gps_Level_RO == gps_Level_RO_Ref) then
1547 call obs_bodySet_i(obsSpaceData,OBS_ASS,INDEX_BODY, obs_notAssimilated)
1548 endif
1549 ! Do not assimilate refractivity in mode gps_Level_RO = gps_Level_RO_Bnd:
1550 if (varNum == bufr_nerf .and. gps_Level_RO == gps_Level_RO_Bnd) then
1551 call obs_bodySet_i(obsSpaceData,OBS_ASS,INDEX_BODY, obs_notAssimilated)
1552 endif
1553 end do BODY
1554 end if
1555 end do HEADER
1556 !
1557 ! List to enumerate and cross-link GPSRO headers 0 <= iProfile < gps_numROProfiles):
1558 !
1559 if (gps_numROProfiles > 0) then
1560 if(.not.allocated(gps_vRO_IndexPrf)) allocate(gps_vRO_IndexPrf(gps_numROProfiles, 10))
1561 iProfile=0
1562 !
1563 ! Loop over all header indices of the 'RO' family:
1564 !
1565 call obs_set_current_header_list(obsSpaceData,'RO')
1566 HEADER2: do
1567 index_header = obs_getHeaderIndex(obsSpaceData)
1568 if (index_header < 0) exit HEADER2
1569 !
1570 ! Process only refractivity data (codtyp 169):
1571 !
1572 IDATYP = obs_headElem_i(obsSpaceData,OBS_ITY,INDEX_HEADER)
1573 if ( IDATYP == 169 ) then
1574 iProfile=iProfile+1
1575 gps_vRO_IndexPrf(iProfile, 1) = INDEX_HEADER
1576 ISAT = obs_headElem_i(obsSpaceData,OBS_SAT ,INDEX_HEADER)
1577 IQLF = obs_headElem_i(obsSpaceData,OBS_ROQF,INDEX_HEADER)
1578 LDSC = .not.btest(IQLF,16-3)
1579 if (LDSC) then
1580 IDSC = 0
1581 else
1582 IDSC = 1
1583 end if
1584 varNum = -1
1585 call obs_set_current_body_list(obsSpaceData, INDEX_HEADER)
1586 !
1587 ! Loop over all body indices
1588 ! For storing varNum of each profile for the 'RO' family
1589 !
1590 BODY2: do
1591 index_body = obs_getBodyIndex(obsSpaceData)
1592 if (index_body < 0) exit BODY2
1593 varNum = obs_bodyElem_i(obsSpaceData,OBS_VNM,INDEX_BODY)
1594 if (varNum > 0) exit BODY2
1595 end do BODY2
1596 gps_vRO_IndexPrf(iProfile, 2) = varNum
1597 gps_vRO_IndexPrf(iProfile, 3) = ISAT
1598 gps_vRO_IndexPrf(iProfile, 4) = IDSC
1599 if (.not.beSilent) write(*,*)'RO Prf', gps_numROProfiles, iProfile, varNum, ISAT, IDSC
1600 end if
1601 end do HEADER2
1602 end if
1603
1604 if (.not.beSilent) write(*,*) 'filt_gpsro: end'
1605 end subroutine FILT_GPSRO
1606
1607 !--------------------------------------------------------------------------
1608 ! filt_backScatAnisIce
1609 !--------------------------------------------------------------------------
1610 subroutine filt_backScatAnisIce(obsSpaceData, beSilent)
1611 !
1612 !:Purpose: Filter scatterometer backscatter anisotropy observations
1613 ! where wind speed is too small
1614 !
1615 !:Note: For noncompliant observations:
1616 !
1617 ! - Set assimilable flag to 0
1618 ! - Set bit of cma flag 13 ON
1619 !
1620 implicit none
1621
1622 ! Arguments:
1623 type(struct_obs), intent(inout) :: obsSpaceData
1624 logical, intent(in) :: beSilent
1625
1626 ! Locals:
1627 integer :: bufrCode, headerIndex, bodyIndex
1628 real(8) :: modelWindSpeed
1629
1630 if (.not. obs_famExist(obsSpaceData,'GL')) return
1631
1632 if (.not. beSilent) then
1633 write(*,*)
1634 write(*,*) ' filt_backScatAnisIce: begin'
1635 end if
1636
1637 ! loop over all body indices
1638 call obs_set_current_body_list( obsSpaceData, 'GL' )
1639
1640 BODY: do
1641
1642 bodyIndex = obs_getBodyIndex( obsSpaceData )
1643 if ( bodyIndex < 0 ) exit BODY
1644
1645 bufrCode = obs_bodyElem_i( obsSpaceData, OBS_VNM, bodyIndex )
1646
1647 if ( bufrCode == BUFR_ICES ) then
1648
1649 headerIndex = obs_bodyElem_i( obsSpaceData, OBS_HIND, bodyIndex )
1650 modelWindSpeed = obs_headElem_r( obsSpaceData, OBS_MWS, headerIndex )
1651
1652 if ( modelWindSpeed < 4.0 ) then
1653 call obs_bodySet_i(obsSpaceData, OBS_ASS, bodyIndex, obs_notAssimilated)
1654 call obs_bodySet_i(obsSpaceData, OBS_FLG, bodyIndex, IBSET(obs_bodyElem_i(obsSpaceData,OBS_FLG,bodyIndex),13))
1655 end if
1656
1657 else
1658
1659 cycle BODY
1660
1661 end if
1662
1663 end do BODY
1664
1665 if (.not. beSilent) write(*,*) ' filt_backScatAnisIce: end'
1666
1667 end subroutine filt_backScatAnisIce
1668
1669 !--------------------------------------------------------------------------
1670 ! filt_iceConcentration
1671 !--------------------------------------------------------------------------
1672 subroutine filt_iceConcentration(obsSpaceData, beSilent)
1673 !
1674 !:Purpose: Filter out observations from satellites
1675 ! not specified in the name list
1676 !
1677 !:Note: For noncompliant observations:
1678 !
1679 ! - Set assimilable flag to 0
1680 !
1681 implicit none
1682
1683 ! Arguments:
1684 type(struct_obs), intent(inout) :: obsSpaceData
1685 logical, intent(in) :: beSilent
1686
1687 ! Locals:
1688 character(len=12) :: cstnid
1689 integer :: nulnam, ierr
1690 integer :: headerIndex, bodyIndex, codeType, platformIndex
1691 integer :: fnom, fclos
1692 logical :: inPlatformList
1693 logical, save :: firstCall = .true.
1694 integer, parameter :: maxPlatformIce = 50
1695
1696 ! Namelist variables:
1697 integer, save :: nPlatformIce ! MUST NOT BE INCLUDED IN NAMELIST!
1698 character(len=12), save :: listPlatformIce(maxPlatformIce) ! list of ice obs 'platforms' (station IDs) to assimilate
1699
1700 namelist /namPlatformIce/ nPlatformIce, listPlatformIce
1701
1702 if (.not. obs_famExist(obsSpaceData,'GL')) return
1703
1704 if (firstCall) then
1705 ! set default values for namelist variables
1706 nPlatformIce = MPC_missingValue_INT
1707 listPlatformIce(:) = '1234567890ab'
1708
1709 if (utl_isNamelistPresent('namPlatformIce','./flnml')) then
1710 nulnam = 0
1711 ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
1712 read (nulnam, nml = namPlatformIce, iostat = ierr)
1713 if ( ierr /= 0 ) call utl_abort('filt_iceConcentration: Error reading namelist')
1714 if ( mmpi_myid == 0 ) write(*,nml=namPlatformIce)
1715 ierr = fclos(nulnam)
1716 if (nPlatformIce /= MPC_missingValue_INT) then
1717 call utl_abort('filt_iceConcentration: check namPlatformIce namelist section: nPlatformIce should be removed')
1718 end if
1719 nPlatformIce = 0
1720 do platformIndex = 1, maxPlatformIce
1721 if (listPlatformIce(platformIndex) == '1234567890ab') exit
1722 nPlatformIce = nPlatformIce + 1
1723 end do
1724 else
1725 write(*,*)
1726 write(*,*) 'filt_iceConcentration: namPlatformIce is missing in the namelist. Filtering will be skipped.'
1727 end if
1728 firstCall = .false.
1729 end if
1730
1731 if ( nPlatformIce < 1 ) return
1732
1733 if ( nPlatformIce > maxPlatformIce ) then
1734 call utl_abort('filt_setup: too many elements for listPlatformIce')
1735 end if
1736
1737 if (.not. beSilent) then
1738 write(*,*)
1739 write(*,*) 'filt_iceConcentration: begin'
1740 end if
1741
1742 ! loop over all header indices of the 'GL' family
1743 call obs_set_current_header_list(obsSpaceData, 'GL')
1744
1745 HEADER: do
1746
1747 headerIndex = obs_getHeaderIndex(obsSpaceData)
1748 if (headerIndex < 0) exit HEADER
1749
1750 cstnid = obs_elem_c ( obsSpaceData, 'STID' , headerIndex )
1751 codeType = obs_headElem_i( obsSpaceData, OBS_ITY, headerIndex )
1752
1753 inPlatformList = .false.
1754
1755 PLATFORM: do platformIndex = 1, nPlatformIce
1756
1757 if ( index(cstnid,trim(listPlatformIce(platformIndex))) > 0 .or. &
1758 index(codtyp_get_name(codeType),trim(listPlatformIce(platformIndex))) > 0) then
1759
1760 inPlatformList = .true.
1761 exit PLATFORM
1762
1763 end if
1764
1765 end do PLATFORM
1766
1767 if ( .not. inPlatformList ) then
1768
1769 call obs_set_current_body_list(obsSpaceData, headerIndex)
1770 BODY: do
1771 bodyIndex = obs_getBodyIndex(obsSpaceData)
1772 if (bodyIndex < 0) exit BODY
1773
1774 call obs_bodySet_i(obsSpaceData, OBS_ASS, bodyIndex, obs_notAssimilated)
1775
1776 end do BODY
1777
1778 end if
1779
1780 end do HEADER
1781
1782 if (.not. beSilent) write(*,*) 'filt_iceConcentration: end'
1783
1784 end subroutine filt_iceConcentration
1785
1786 !--------------------------------------------------------------------------
1787 ! filt_topoChemistry
1788 !--------------------------------------------------------------------------
1789 subroutine filt_topoChemistry(columnTrlOnTrlLev, obsSpaceData, beSilent)
1790 !
1791 !:Purpose: Rejects elements which are too far below the model surface
1792 ! or above the model top.
1793 !
1794 !:Comments:
1795 ! Flagging of bit 4 in OBS_FLG done in filt_topoChemistry instead of set_scale_chm
1796 ! since this subroutine is called after chm_setup, allowing use of utl_open_asciifile
1797 !
1798 implicit none
1799
1800 ! Arguments:
1801 type(struct_columnData), intent(in) :: columnTrlOnTrlLev
1802 type(struct_obs), intent(inout) :: obsSpaceData
1803 logical, intent(in) :: beSilent
1804
1805 ! Locals:
1806 integer :: headerIndex, bodyIndex, listIndex, elemIndex, listIndex_stnid
1807 integer :: ivnm, countAssim, jl, icount
1808 real(8) :: obsAltitude, obsPressure, colTopPressure, colSfcPressure
1809 real(8) :: colAltitudeBelow, colAltitudeAbove
1810 logical :: list_is_empty
1811 integer, parameter :: Nmax=100
1812 integer :: Num_stnid_chm,nobslev,Num_chm
1813 character(len=13) :: CstnidList_chm(Nmax)
1814 integer :: countAcc_stnid(Nmax),countRej_stnid(Nmax)
1815 integer :: countRejflg_stnid(Nmax),countRejflg(Nmax)
1816 integer :: countAcc(Nmax),countRej(Nmax),iConstituentList(Nmax)
1817
1818 if (.not.obs_famExist(obsSpaceData,'CH')) return
1819
1820 ! Set counters to zero
1821 countAcc_stnid(:)=0
1822 countRej_stnid(:)=0
1823 countRejflg_stnid(:)=0
1824 Num_stnid_chm=0
1825
1826 countAcc(:)=0
1827 countRej(:)=0
1828 countRejflg(:)=0
1829 Num_chm=0
1830
1831 ! Loop over all header indices of the 'CH' family
1832 call obs_set_current_header_list(obsSpaceData, 'CH')
1833 HEADER: do
1834 headerIndex = obs_getHeaderIndex(obsSpaceData)
1835 if (headerIndex < 0) exit HEADER
1836
1837 ! Set the body list & start at the beginning of the list
1838 call obs_set_current_body_list(obsSpaceData, headerIndex,list_is_empty)
1839
1840 if (list_is_empty) cycle HEADER ! Proceed to next HEADER
1841
1842 ! Set geopotential height and pressure boundaries.
1843
1844 colAltitudeBelow = col_getHeight(columnTrlOnTrlLev,0,headerIndex,'SF')
1845 colAltitudeAbove = col_getHeight(columnTrlOnTrlLev,1,headerIndex,'MM')
1846 colSfcPressure = col_getElem(columnTrlOnTrlLev,1,headerIndex,'P0')
1847 colTopPressure = col_getPressure(columnTrlOnTrlLev,1,headerIndex,'MM')
1848
1849 ! Identify max number of profile points in the profile (exclude BUFR_SCALE_EXPONENT elements)
1850
1851 nobslev = obs_headElem_i(obsSpaceData,OBS_NLV,headerIndex)
1852 bodyIndex =obs_headElem_i(obsSpaceData,OBS_RLN,headerIndex)
1853 do jl=0,obs_headElem_i(obsSpaceData,OBS_NLV,headerIndex)-1
1854 if (obs_bodyElem_i(obsSpaceData,OBS_VNM,bodyIndex+jl) == BUFR_SCALE_EXPONENT) &
1855 nobslev = nobslev-1
1856 end do
1857
1858 ! Identify element index of stnid list for the CH family.
1859
1860 call utl_get_stringId(obs_elem_c(obsSpaceData,'STID',headerIndex),&
1861 nobslev,CstnidList_chm,Num_stnid_chm,Nmax,listIndex_stnid)
1862
1863 ! Loop over all body indices (still in the 'CH' family)
1864 icount=0
1865 BODY: do
1866 bodyIndex = obs_getBodyIndex(obsSpaceData)
1867 if (bodyIndex < 0) exit BODY
1868
1869 ivnm = obs_bodyElem_i(obsSpaceData,OBS_VNM,bodyIndex)
1870 if (ivnm == BUFR_SCALE_EXPONENT) cycle BODY
1871
1872 ! Identify element index of constituent ID list for the CH family.
1873 if (icount == 0) call utl_get_Id(obs_headElem_i(obsSpaceData,OBS_CHM,headerIndex), &
1874 iConstituentList,Num_chm,Nmax,listIndex)
1875 icount=icount+1
1876
1877 ! Check for bit 4 of OBS_FLG, indicating a 'Suspicious element'
1878 if (btest(obs_bodyElem_i(obsSpaceData,OBS_FLG,bodyIndex),4)) &
1879 call obs_bodySet_i(obsSpaceData,OBS_ASS,bodyIndex,obs_notAssimilated)
1880
1881 if (obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == obs_notAssimilated) then
1882
1883 ! Already rejected from input marker/flag.
1884
1885 countRej(listIndex)=countRej(listIndex)+1
1886 countRej_stnid(listIndex_stnid)=countRej_stnid(listIndex_stnid)+1
1887 countRejflg(listIndex)=countRejflg(listIndex)+1
1888 countRejflg_stnid(listIndex_stnid)=countRejflg_stnid(listIndex_stnid)+1
1889
1890 else if (obs_bodyElem_i(obsSpaceData,OBS_VCO,bodyIndex) == 1) then
1891
1892 ! Check as a function of altitude.
1893
1894 obsAltitude = obs_bodyElem_r(obsSpaceData,OBS_PPP,bodyIndex)
1895 !write(*,*) 'rejected zzz ',obs_elem_c(obsSpaceData,'STID',headerIndex),obsAltitude,colSfcPressure,colTopPressure
1896 if( obsAltitude < colAltitudeBelow .or. obsAltitude > colAltitudeAbove ) then
1897 call obs_bodySet_i(obsSpaceData,OBS_FLG,bodyIndex, &
1898 ibset( obs_bodyElem_i(obsSpaceData,OBS_FLG,bodyIndex), 18 ))
1899 call obs_bodySet_i(obsSpaceData,OBS_ASS,bodyIndex,obs_notAssimilated)
1900 countRej(listIndex)=countRej(listIndex)+1
1901 countRej_stnid(listIndex_stnid)=countRej_stnid(listIndex_stnid)+1
1902 else
1903 countAcc(listIndex)=countAcc(listIndex)+1
1904 countAcc_stnid(listIndex_stnid)=countAcc_stnid(listIndex_stnid)+1
1905 end if
1906
1907 else if (obs_bodyElem_i(obsSpaceData,OBS_VCO,bodyIndex) == 2) then
1908
1909 ! Check as a function of pressure.
1910
1911 obsPressure = obs_bodyElem_r(obsSpaceData,OBS_PPP,bodyIndex)
1912
1913 if ( obsPressure > colSfcPressure .or. obsPressure < colTopPressure) then
1914 call obs_bodySet_i(obsSpaceData,OBS_ASS,bodyIndex,obs_notAssimilated)
1915 call obs_bodySet_i(obsSpaceData,OBS_FLG,bodyIndex, &
1916 ibset(obs_bodyElem_i(obsSpaceData,OBS_FLG,bodyIndex),18 ))
1917 countRej(listIndex)=countRej(listIndex)+1
1918 countRej_stnid(listIndex_stnid)=countRej_stnid(listIndex_stnid)+1
1919 else
1920 countAcc(listIndex)=countAcc(listIndex)+1
1921 countAcc_stnid(listIndex_stnid)=countAcc_stnid(listIndex_stnid)+1
1922 end if
1923 else
1924 countAcc(listIndex)=countAcc(listIndex)+1
1925 countAcc_stnid(listIndex_stnid)=countAcc_stnid(listIndex_stnid)+1
1926 end if
1927
1928 end do BODY
1929
1930 end do HEADER
1931
1932 if (Num_stnid_chm > 0 .and. .not.beSilent) then
1933 write(*,*) ' '
1934 write(*,*) '*****************************************************************'
1935 write(*,*) ' filt_topoChemistry: '
1936 write(*,*) ' FAMILY = CH'
1937 write(*,222) 'ELEMENTS for CH stnids',(CstnidList_chm(elemIndex),elemIndex=1,Num_stnid_chm)
1938 write(*,223) 'ACCEPTED for CH stnids',(countAcc_stnid(elemIndex),elemIndex=1,Num_stnid_chm)
1939 write(*,223) 'REJECTED for CH stnids',(countRej_stnid(elemIndex),elemIndex=1,Num_stnid_chm)
1940 write(*,223) 'REJECTED due to marker',(countRejflg_stnid(elemIndex),elemIndex=1,Num_stnid_chm)
1941 write(*,*) ' '
1942 write(*,224) 'ELEMENTS for CH ',(iConstituentList(elemIndex),elemIndex=1,Num_chm)
1943 write(*,224) 'ACCEPTED for CH ',(countAcc(elemIndex),elemIndex=1,Num_chm)
1944 write(*,224) 'REJECTED for CH ',(countRej(elemIndex),elemIndex=1,Num_chm)
1945 write(*,223) 'REJECTED due to marker',(countRejflg(elemIndex),elemIndex=1,Num_stnid_chm)
1946 write(*,*) '*****************************************************************'
1947 write(*,*) ' '
1948
1949 countAssim=0
1950 do bodyIndex=1,obs_numbody(obsSpaceData)
1951 if (obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == obs_assimilated) countAssim=countAssim+1
1952 end do
1953 write(*,'(1X," NUMBER OF DATA TO BE ASSIMILATED AFTER ADJUSTMENTS (after filter_topoChemistry):",i10)') countAssim
1954 write(*,*) ' '
1955 end if
1956222 format(2x,a29,100(2x,a10))
1957223 format(2x,a29,100(2x,i8,2x))
1958224 format(2x,a29,100(2x,i6))
1959
1960 end subroutine filt_topoChemistry
1961
1962 !--------------------------------------------------------------------------
1963 ! filt_bufrCodeAssimilated
1964 !-------------------------------------------------------------------------
1965 function filt_bufrCodeAssimilated(bufrCode) result(assimilated)
1966 !
1967 !:Purpose: To test if a bufr code part of the assimilated observation list
1968 !
1969 implicit none
1970
1971 ! Arguments:
1972 integer, intent(in) :: bufrCode ! The input bufr code
1973 ! Result:
1974 logical :: assimilated ! Assimilated of not
1975
1976 ! Locals:
1977 integer :: elemIndex
1978
1979 if (.not. initialized) call filt_setup('none')
1980
1981 assimilated = .false.
1982
1983 do elemIndex = 1, filt_nelems
1984 if (filt_nlist(elemIndex) == bufrCode) then
1985 assimilated = .true.
1986 return
1987 end if
1988 end do
1989
1990 end function filt_bufrCodeAssimilated
1991
1992 !--------------------------------------------------------------------------
1993 ! filt_getBufrCodeAssimilated
1994 !-------------------------------------------------------------------------
1995 subroutine filt_getBufrCodeAssimilated(bufrCodeList)
1996 !
1997 !:Purpose: To get the assimilated observation list
1998 !
1999 implicit none
2000
2001 ! Arguments:
2002 integer, intent(out) :: bufrCodeList(filt_nelems) ! The list of assimilated bufr codes
2003
2004 if (.not. initialized) call filt_setup('none')
2005
2006 bufrCodeList(:) = filt_nlist(1:filt_nelems)
2007
2008 end subroutine filt_getBufrCodeAssimilated
2009
2010 !--------------------------------------------------------------------------
2011 ! filt_nBufrCodeAssimilated
2012 !-------------------------------------------------------------------------
2013 function filt_nBufrCodeAssimilated() result(nBufrCode)
2014 !
2015 !:Purpose: To get the number of assimilated observations
2016 !
2017 implicit none
2018
2019 ! Result:
2020 integer :: nBufrCode ! The number of assimilated observations
2021
2022 if (.not. initialized) call filt_setup('none')
2023
2024 nBufrCode = filt_nelems
2025
2026 end function filt_nBufrCodeAssimilated
2027
2028end module obsFilter_mod