1 subroutine da_qc_gpsref(it, grid, ob, iv)
3 !-----------------------------------------------------------------------
4 ! Purpose: quality control for gpsro refractivity
5 ! this subroutine is only called when anal_type_verify=false
7 ! 2020-06: QC code previously in da_get_innov_vector_gpsref
8 !-----------------------------------------------------------------------
12 integer, intent(in) :: it ! External iteration
13 type(domain), intent(in) :: grid ! first guess state.
14 type(y_type), intent(inout) :: ob ! Observation structure.
15 type(iv_type), intent(inout) :: iv ! O-B structure.
17 integer :: n ! Loop counter
18 integer :: i, j, k ! Index dimension
19 real, allocatable :: model_ref(:,:) ! model gpsref at ob loc
20 real, allocatable :: zdiff(:) ! dz for dndz qc
21 real, allocatable :: model_t(:,:) ! model value t at ob location
22 real, allocatable :: int_t(:,:,:) ! xb%t for interpolating t for gsi qc
27 real, allocatable :: dndz_obs(:),dndz_mod(:)
28 real, allocatable :: dndz2_obs(:),dndz2_mod(:)
31 real :: pcnt1, pcnt2, pcnt3
34 real :: height_below(5000)
35 character(len=40) :: name_qc(5000)
38 real :: g_height, g_lat, cutoff, stddev
41 integer :: nrej_maxiv, nrej_dndz, nrej_dndz2, nrej_cutoff
42 integer :: nrej_pcnt_below, nrej_pcnt_above, nrej_pcnt_middle
43 integer, parameter :: nhh = 13
44 real, dimension(nhh) :: hh = (/ 500.0, 1000.0, 1500.0, 2000.0, 3000.0, &
45 5000.0, 6000.0, 7000.0, 8000.0, 10000.0, &
46 12000.0, 15000.0, 18000.0 /)
47 integer, dimension(nhh+1) :: nrej_allqc, nrej_height, ngood, ntotal
50 character(len=30) :: filename
52 if (trace_use_dull) call da_trace_entry("da_qc_gpsref")
54 ! if (iv%info(gpsref)%nlocal < 1) then
55 ! if (trace_use_dull) call da_trace_exit("da_qc_gpsref")
59 write(unit=message(1),fmt='(A)') ' calling da_qc_gpsref'
60 call da_message(message(1:1))
74 if ( iv%info(gpsref)%n2 >= iv%info(gpsref)%n1 ) then
75 allocate (model_ref(iv%info(gpsref)%max_lev,iv%info(gpsref)%n1:iv%info(gpsref)%n2))
78 do n=iv%info(gpsref)%n1,iv%info(gpsref)%n2
80 do k = 1, iv%info(gpsref)%levels(n)
81 if ( iv%gpsref(n)%ref(k)%qc /= missing_data ) then
82 model_ref(k,n) = ob%gpsref(n)%ref(k) - iv%gpsref(n)%ref(k)%inv
88 !==========================================
89 ! Quality check 1: Gross error check
90 !==========================================
91 ! Quality check : Gross error(departure from the background) check
92 if ( check_max_iv ) then
93 do n = iv%info(gpsref)%n1, iv%info(gpsref)%n2
94 do k = 1, iv%info(gpsref)%levels(n)
95 max_iv_thresh = iv%gpsref(n)%ref(k)%error * max_error_ref
96 if ( iv%gpsref(n)%ref(k)%qc >= obs_qc_pointer ) then
97 if ( abs(iv%gpsref(n)%ref(k)%inv) > max_iv_thresh ) then
98 iv%gpsref(n)%ref(k)%qc = fails_error_max
99 if ( iv%info(gpsref)%proc_domain(1,n) ) nrej_maxiv = nrej_maxiv + 1
106 !==========================================
107 ! Quality check 2.1: dNdz
108 ! Quality check 2.2: d2N_dz2
109 !==========================================
110 if ( gpsref_qc_dndz_opt==1 .or. gpsref_qc_dndz2_opt==1 ) then
112 ! refer to Poli et al. (2009) ------------------------------------------------
113 ! flag if fit in each of these two qc steps for both of obs. and model
114 ! qc_dndz: dN/dz < -50 km^-1
115 ! qc_dndz2: abs(d^2N/dz^2) > 100 km^-2
116 ! Shu-Ya Chen (2009-07-29)
117 do n=iv%info(gpsref)%n1,iv%info(gpsref)%n2
118 if (iv%info(gpsref)%levels(n) <= 2) cycle
119 top_level = 0 ! initialize
120 do k=1,iv%info(gpsref)%levels(n)
121 if (model_ref(k,n) > 0.0) top_level=k
123 if ( top_level <= 2 ) cycle
125 ! zdiff values are used for both gpsref_qc_dndz_opt and gpsref_qc_dndz2_opt
126 allocate (zdiff(1:top_level))
127 zdiff(1) = iv%gpsref(n)%h(2) - iv%gpsref(n)%h(1)
128 zdiff(top_level) = iv%gpsref(n)%h(top_level) - iv%gpsref(n)%h(top_level-1)
129 do k = 2, top_level-1
130 zdiff(k) = iv%gpsref(n)%h(k+1) - iv%gpsref(n)%h(k-1)
132 zdiff(:) = zdiff(:) * 0.001 ! meter to km
134 ! dndz values are used for both gpsref_qc_dndz_opt and gpsref_qc_dndz2_opt calculation
135 allocate(dndz_obs(top_level))
136 allocate(dndz_mod(top_level))
138 ! check for bottom boundary (Forward Difference)
139 dndz_obs(1) = (ob%gpsref(n)%ref(2)-ob%gpsref(n)%ref(1)) / zdiff(1)
140 dndz_mod(1) = (model_ref(2,n)-model_ref(1,n)) / zdiff(1)
141 ! check for upper boundary (Backward Difference)
142 dndz_obs(top_level) = &
143 (ob%gpsref(n)%ref(top_level)-ob%gpsref(n)%ref(top_level-1)) / zdiff(top_level)
144 dndz_mod(top_level) = &
145 (model_ref(top_level,n)-model_ref(top_level-1,n))/ zdiff(top_level)
146 ! check for middle levels (Central Difference)
147 do k = 2, top_level-1
148 dndz_obs(k) = (ob%gpsref(n)%ref(k+1)-ob%gpsref(n)%ref(k-1))/zdiff(k)
149 dndz_mod(k) = (model_ref(k+1,n)-model_ref(k-1,n))/zdiff(k)
152 if ( gpsref_qc_dndz_opt==1 ) then
153 ! setting qc flags according to dndz values
155 if (iv%gpsref(n)%ref(k)%qc /= missing_data) then
156 ! default gpsref_qc_dndz_thresh = -50.0
157 if ( (dndz_obs(k) < gpsref_qc_dndz_thresh) .or. &
158 (dndz_mod(k) < gpsref_qc_dndz_thresh) ) then
159 iv%gpsref(n)%ref(k)%qc = qcflag_dndz
160 if ( iv%info(gpsref)%proc_domain(1,n) ) nrej_dndz = nrej_dndz + 1
164 end if ! gpsref_qc_dndz_opt
166 if ( gpsref_qc_dndz2_opt==1 ) then
168 allocate(dndz2_obs(top_level))
169 allocate(dndz2_mod(top_level))
171 ! check for bottom boundary
172 dndz2_obs(1) = (dndz_obs(2)-dndz_obs(1)) / zdiff(1)
173 dndz2_mod(1) = (dndz_mod(2)-dndz_mod(1)) / zdiff(1)
174 ! check for upper boundary
175 dndz2_obs(top_level) = (dndz_obs(top_level)-dndz_obs(top_level-1)) / zdiff(top_level)
176 dndz2_mod(top_level) = (dndz_mod(top_level)-dndz_mod(top_level-1)) / zdiff(top_level)
177 ! check for middle levels
178 do k = 2, top_level-1
179 dndz2_obs(k) = (dndz_obs(k+1)-dndz_obs(k-1)) / zdiff(k)
180 dndz2_mod(k) = (dndz_mod(k+1)-dndz_mod(k-1)) / zdiff(k)
183 ! setting qc flags according to dndz2 values
185 if (iv%gpsref(n)%ref(k)%qc /= missing_data) then
186 ! default gpsref_qc_dndz_thresh = -100.0
187 if ( (abs(dndz2_obs(k)) > gpsref_qc_dndz2_thresh) .or. &
188 (abs(dndz2_mod(k)) > gpsref_qc_dndz2_thresh) ) then
189 iv%gpsref(n)%ref(k)%qc = qcflag_dndz2
190 if ( iv%info(gpsref)%proc_domain(1,n) ) nrej_dndz2 = nrej_dndz2 + 1
194 deallocate(dndz2_obs,dndz2_mod)
195 end if ! gpsref_qc_dndz2_opt
197 deallocate(dndz_obs,dndz_mod)
199 end do ! end of do iv%info(gpsref)%n1~n2
200 end if ! gpsref_qc_dndz_opt or gpsref_qc_dndz2_opt
202 ! End of Poli's check. (2009) -------------------------------------------------
205 !==========================================
206 ! Quality check 3: GSI's regional QC
207 !==========================================
208 if ( gpsref_qc_gsi_opt == 1 ) then
210 ! t_iwabuchi 20111216 GSI's regional QC
211 ! START GSI-QC REGIONAL TI-GSI -------------------------------------------------
212 ! ------------------------------------------------------------------------------
213 ! GSI-QC Implementation, Ted Iwabuchi
214 ! First release: 2011-03-04
215 ! Review 3.3.1 : 2011-12-16
218 ! 2011-05-04 : bug in formula fixed
219 ! 2011-05-11 : add abs, and validated
220 ! 2011-06-06 : reviewd, added comments
221 ! 2011-12-16 : implemented for 3.3.1
224 ! Modified version of Cucurull, 2010 for NCEP GSI-Global DA
225 ! NCEP-GSI regional DA setupref.f90
227 ! O-B cutoff depending on assigned std dev for each height and latitude
229 ! > 30 all observation is rejected
230 ! 11 - 30 km 0.25 + 0.5 cos (lambda) [c1] 3
231 ! 9 - 11 km (11-h)/2 x c2 + (h-9)/2 x c1 3
232 ! 6 - 9 km 0.5 (if T<= 240) [c2] 3
233 ! 0.01 x T^2 - 0.455 T + 52.075 (if T>240)
234 ! 4 - 6 km (6-h)/2 x c3 + (h-4)/2 x c2 3
235 ! 0 - 4 km 1+2.5 cos (lambda) [c3] 1
238 ! ------------------------------------------------------------------------------
241 ! GSI code: comGSI_v3Beta/src/main/setupref.f90
245 ! See TI-GSI blocks, and don't forget to declare type of variables
246 ! used in this block:
248 ! 1 real :: g_height, g_lat, cutoff, stddev ! QC cutoff GSI
249 ! 2 real, allocatable :: model_t(:,:) ! Model value t at ob location
250 ! 3 integer, parameter :: qc_cutoff = -36
253 ! t_iwabuchi 20111216 (hmn) allocate model_t for GSI regional QC
254 allocate (model_t(iv%info(gpsref)%max_lev,iv%info(gpsref)%n1:iv%info(gpsref)%n2))
257 allocate (int_t(ims:ime, jms:jme, kms:kme))
259 call da_interp_lin_3d (int_t, iv%info(gpsref), model_t)
262 ! process refracticity data n from n1 to n2
264 do n=iv%info(gpsref)%n1,iv%info(gpsref)%n2
265 do k=1,iv%info(gpsref)%levels(n)
267 g_height = iv%gpsref(n)% h(k)
268 g_lat = cos ( radian * iv%info(gpsref)%lat(k,n) )
270 if ( g_height >= 0.0 .and. g_height < 4000.0) then
272 stddev = 1.0 + 2.5 * g_lat
274 else if (g_height >= 4000.0 .and. g_height < 6000.0) then
276 if (model_t(k,n) <= 240.0) then
279 stddev = 0.001 * model_t(k,n) * model_t(k,n) - 0.455 * model_t(k,n) + 52.075
281 stddev = (6000.0 - g_height)/2000.0 * (1.0 + 2.5 * g_lat) &
282 + (g_height - 4000.0)/2000.0 * stddev
284 else if (g_height >= 6000.0 .and. g_height < 9000.0) then
286 if (model_t(k,n) <= 240.0) then
289 ! 0.01 x T^2 - 0.455 T + 52.075 (if T>240)
290 stddev = 0.001 * model_t(k,n) * model_t(k,n) - 0.455 * model_t(k,n) + 52.075
293 else if (g_height >= 9000.0 .and. g_height < 11000.0) then
295 if (model_t(k,n) <= 240.0) then
298 stddev = 0.001 * model_t(k,n) * model_t(k,n) - 0.455 * model_t(k,n) + 52.075
300 stddev = (11000.0 - g_height)/2000.0 * stddev &
301 + (g_height - 9000)/2000.0 * (0.25 + 0.5 * g_lat)
304 else if (g_height >= 11000.0 .and. g_height < 30000.0) then
306 stddev = 0.25 + 0.5 * g_lat
308 else if (g_height >= 30000.0 ) then
313 if (iv%gpsref(n)%ref(k)%qc /= missing_data) then
314 ! Check innovation, stddev, and cutoff
315 if (abs(iv%gpsref(n)%ref(k)%inv) >= stddev * cutoff) then
316 iv%gpsref(n)%ref(k)%qc = qcflag_cutoff
317 if ( iv%info(gpsref)%proc_domain(1,n) ) nrej_cutoff = nrej_cutoff + 1
326 end if ! gpsref_qc_gsi_opt
328 ! End GSI-QC REGIONAL TI-GSI ------------------------------------------------
332 !==========================================
333 ! Quality check 4: Error percentage check and Low levels quality control
334 !==========================================
335 if ( gpsref_qc_pcnt_opt == 1 ) then
337 ! Quality check : Error percentage check.
339 ! assign namelist settings to local variables
340 h_1 = gpsref_qc_pcnt_h1 ! default 7000.0
341 h_2 = gpsref_qc_pcnt_h2 ! default 25000.0
342 pcnt1 = gpsref_qc_pcnt_below ! default 0.05 ! test 0.02
343 pcnt2 = gpsref_qc_pcnt_above ! default 0.04 ! test 0.01
344 pcnt3 = gpsref_qc_pcnt_middle ! default 0.10 ! test 0.03
346 do n=iv%info(gpsref)%n1,iv%info(gpsref)%n2
348 do k=1, iv%info(gpsref)%levels(n)
350 ! incremetal refractivity or the relative error:
351 ! abs[(O-B)/{(O+B)/2}] (Lidia Cucurull 2005)
353 percnt = 2.0 * abs(iv%gpsref(n)%ref(k)%inv / &
354 (ob%gpsref(n)%ref(k) + model_ref(k,n)))
356 if (iv%gpsref(n)%ref(k)%qc /= missing_data) then
358 if (iv%gpsref(n)%h(k) < h_1) then
359 if (percnt > pcnt1) iv%gpsref(n)%ref(k)%qc = qcflag_pcnt_below
360 else if (iv%gpsref(n)%h(k) > h_2) then
361 if (percnt > pcnt3) iv%gpsref(n)%ref(k)%qc = qcflag_pcnt_above
363 if (percnt > pcnt2) iv%gpsref(n)%ref(k)%qc = qcflag_pcnt_middle
369 ! Quality check : Low levels quality control
371 ! Search for the GPS RO's name with the 'qcflag_pcnt_below':
373 if ( maxval(iv%info(gpsref)%levels(:)) > 1 ) then ! gpsref in profiles
378 do n=iv%info(gpsref)%n1,iv%info(gpsref)%n2
380 iv%info(gpsref)%levels(n) = iv%info(gpsref)%levels(n)
381 do k=1, iv%info(gpsref)%levels(n)
382 if (iv%gpsref(n)%ref(k)%qc == qcflag_pcnt_below) then
383 name_qc(nn) = iv%info(gpsref)%name(n)
384 height_below(nn) = max(iv%gpsref(n)%h(k),height_below(nn))
387 if (height_below(nn) == 0.0) nn = nn - 1
390 ! Set the flag qcflag_pcnt_below to the levels below percnt < pcnt1::
392 do n=iv%info(gpsref)%n1,iv%info(gpsref)%n2
394 if (iv%info(gpsref)%name(n) == name_qc(na)) then
395 do k=1, iv%info(gpsref)%levels(n)
396 if (iv%gpsref(n)%h(k) < height_below(na) .and. &
397 iv%gpsref(n)%ref(k)%qc /= missing_data) then
398 iv%gpsref(n)%ref(k)%qc = qcflag_pcnt_below
406 else ! gpsref not in profiles
407 do na = iv%info(gpsref)%n1, iv%info(gpsref)%n2
408 if ( iv%gpsref(na)%ref(1)%qc == qcflag_pcnt_below) then
409 do nb = iv%info(gpsref)%n1, iv%info(gpsref)%n2
410 if ( iv%info(gpsref)%id(nb) == iv%info(gpsref)%id(na) .and. &
411 iv%info(gpsref)%name(nb) == iv%info(gpsref)%name(na) .and. &
412 iv%gpsref(nb)%ref(1)%qc /= missing_data .and. &
413 iv%gpsref(nb)%h(1) <= iv%gpsref(na)%h(1) ) then
414 iv%gpsref(nb)%ref(1)%qc = qcflag_pcnt_below
419 end if ! end of if gpsref profiles
421 do n = iv%info(gpsref)%n1, iv%info(gpsref)%n2
422 if ( .not. iv%info(gpsref)%proc_domain(1,n) ) cycle
423 do k = 1, iv%info(gpsref)%levels(n)
424 if (iv%gpsref(n)%ref(k)%qc == qcflag_pcnt_middle) &
425 nrej_pcnt_middle = nrej_pcnt_middle + 1
426 if (iv%gpsref(n)%ref(k)%qc == qcflag_pcnt_below) &
427 nrej_pcnt_below = nrej_pcnt_below + 1
428 if (iv%gpsref(n)%ref(k)%qc == qcflag_pcnt_above) &
429 nrej_pcnt_above = nrej_pcnt_above + 1
433 end if ! gpsref_qc_pcnt_opt
434 ! ------------------------------------------------------------------------------
436 deallocate (model_ref)
439 ! hcl-todo: add other qc counts and prints
441 nlocal_loop: do n = iv%info(gpsref)%n1, iv%info(gpsref)%n2
442 if ( .not. iv%info(gpsref)%proc_domain(1,n) ) cycle nlocal_loop
443 do k = 1, iv%info(gpsref)%levels(n)
444 ! find the index of height bins for grouping statistics
446 if ( iv%gpsref(n)%h(k) <= hh(1) ) then
448 else if ( iv%gpsref(n)%h(k) > hh(nhh) ) then
451 nhh_loop: do khh = 2, nhh
452 if ( iv%gpsref(n)%h(k) > hh(khh-1) .and. &
453 iv%gpsref(n)%h(k) <= hh(khh) ) then
459 if ( ihh < 1 ) cycle nlocal_loop ! no valid h index found
460 if ( iv%gpsref(n)%ref(k)%qc /= missing_data ) then
461 ntotal(ihh) = ntotal(ihh) + 1
462 if ( iv%gpsref(n)%ref(k)%qc < obs_qc_pointer .and. &
463 iv%gpsref(n)%ref(k)%qc /= qcflag_height ) then
464 nrej_allqc(ihh) = nrej_allqc(ihh) + 1
466 if ( iv%gpsref(n)%ref(k)%qc == qcflag_height ) then
467 nrej_height(ihh) = nrej_height(ihh) + 1
469 if ( iv%gpsref(n)%ref(k)%qc >= obs_qc_pointer ) then
470 ngood(ihh) = ngood(ihh) + 1
478 call da_proc_sum_int (nrej_maxiv)
479 call da_proc_sum_int (nrej_dndz)
480 call da_proc_sum_int (nrej_dndz2)
481 call da_proc_sum_int (nrej_pcnt_below)
482 call da_proc_sum_int (nrej_pcnt_above)
483 call da_proc_sum_int (nrej_pcnt_middle)
484 call da_proc_sum_int (nrej_cutoff)
485 call da_proc_sum_ints (nrej_allqc(:))
486 call da_proc_sum_ints (nrej_height(:))
487 call da_proc_sum_ints (ntotal(:))
488 call da_proc_sum_ints (ngood(:))
491 if (num_fgat_time > 1) then
492 write(filename,'(a,i2.2,a,i2.2)') 'qcstat_gpsref_', it, '_t', iv%time
494 write(filename,'(a,i2.2)') 'qcstat_gpsref_', it
496 call da_get_unit(iunit)
497 open(iunit,file=trim(filename),form='formatted',iostat=ios)
499 write(unit=message(1),fmt='(a,a)') 'error opening the output file ', filename
500 call da_error(__FILE__,__LINE__,message(1:1))
503 write(iunit, fmt='(/a/)') &
504 'Quality Control Statistics for GPSRO Refractivity' ! exclude missing_data
505 write(iunit,'(20x,a,13i7,a,i5,a)') ' TOTAL', nint(hh(:)), ' >', nint(hh(nhh)), ' meter'
506 write(iunit,'(a20,20i7)') ' ntotal = ', sum(ntotal), ntotal(:)
507 write(iunit,'(a20,20i7)') ' ngood = ', sum(ngood), ngood(:)
508 write(iunit,'(a20,20i7)') ' nrej_height = ', sum(nrej_height), nrej_height(:)
509 write(iunit,'(a20,20i7)') ' nrej_allqc = ', sum(nrej_allqc), nrej_allqc(:)
511 write(iunit,'(a20,i7)') ' nrej_maxiv = ', nrej_maxiv
512 write(iunit,'(a20,i7)') ' nrej_dndz = ', nrej_dndz
513 write(iunit,'(a20,i7)') ' nrej_dndz2 = ', nrej_dndz2
514 write(iunit,'(a20,i7)') ' nrej_pcnt_below = ', nrej_pcnt_below
515 write(iunit,'(a20,i7)') ' nrej_pcnt_middle = ', nrej_pcnt_middle
516 write(iunit,'(a20,i7)') ' nrej_pcnt_above = ', nrej_pcnt_above
517 write(iunit,'(a20,i7)') ' nrej_cutoff = ', nrej_cutoff
520 call da_free_unit(iunit)
523 if (trace_use_dull) call da_trace_exit("da_qc_gpsref")
525 end subroutine da_qc_gpsref