1 program da_tune_obs_hollingsworth2
3 !-----------------------------------------------------------
5 ! Purpose: Program for tuning observation errors
6 ! (Hollingsworth method)
7 ! Ref: Tellus (1986) 38, pp.111-161 (Part I & II)
8 !-----------------------------------------------------------
10 use da_control
, only
: filename_len
, earth_radius
, pi
14 character*5, parameter :: missing_c
= '*****'
15 integer, parameter :: ounit
= 10
16 integer, parameter :: num_bins_p
= 10
17 integer, parameter :: obs_qc_pointer
= 0
18 integer, parameter :: max_stations
= 2526
19 integer, parameter :: max_times
= 200
20 integer, parameter :: inunit
= 35
21 integer, parameter :: min_obs
= 30
22 integer, parameter :: num_bins
= 100
23 integer, parameter :: missing_i
= -88
24 real, parameter :: bottom_pressure
= 1000.0
25 real, parameter :: bin_width_p
= 100.0
26 real, parameter :: missing_r
= -888888.0
27 real, parameter :: max_distance
= 5000 ! km
30 integer :: qcflag(1:max_times
)
31 real :: ominusb(1:max_times
)
32 real :: sigmao(1:max_times
)
33 real :: pressure(1:max_times
)
37 character*5 :: id(1:max_stations
)
40 integer :: num_stations
41 integer :: num_obs(1:max_stations
)
44 real :: lat(1:max_stations
)
45 real :: lon(1:max_stations
)
46 real :: mean_ominusb(1:max_stations
)
47 real :: dis(1:max_stations
,1:max_stations
)
48 real :: cov(1:num_bins
)
49 type (sub_type
) :: data(1:max_stations
)
52 character(len
=filename_len
) :: filename
53 ! character*5 :: station_chosen
54 character*5 :: station_id
55 integer :: times
, n
, b
59 integer :: percent_reject
61 real :: lati
, long
, iv
, error
62 ! type (obs_type) :: obs(1:num_bins_p)
63 type (obs_type
),allocatable
:: obs(:)
66 real :: bin_start_p(1:num_bins_p
) ! Number of pressure bins
67 real :: obs_err_used(1:num_bins_p
) ! Obs error currently used
70 ! for temperature it is 1.0
71 ! data obs_err_used/ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0/
72 data obs_err_used
/ 1.1, 1.1, 1.1, 1.4, 1.8, 2.3, 2.8, 3.3, 3.3, 2.7/
73 ! 1000 900 800 700 600 500 400 300 200 100
74 print*,' num_bins_p = ',num_bins_p
75 allocate ( obs( 1: num_bins_p
) )
77 bin_start_p(b
) = bottom_pressure
- real(b
-1) * bin_width_p
79 ! Initialize obs structure:
80 call da_init_obs( obs(b
) )
83 !-----------------------------------------------------------------------
84 ! [1.0] Read in O-B data and fill structure:
85 !-----------------------------------------------------------------------
88 ! station_chosen = "MOWV3"
89 ! filename = "da_tune_obs_hollingsworth2_poamvu_"//station_chosen(1:5)//"_omb.out"
90 ! station_chosen = "91652"
91 ! filename = "da_tune_obs_hollingsworth2_soundt_"//station_chosen(1:5)//"_omb.out"
92 filename
= "hollingsworth2.out"
93 open(ounit
, file
= filename
, status
= "unknown" )
98 do ! Loop over obs in file:
100 read(inunit
,'(a5,2f9.3,3f17.7,i8)')station_id
, lati
, long
, &
103 ! [1.1] Exit when come to end of file markers:
104 ! if ( station_id(1:5) == '*****' .or. station_id == '*end*' ) exit
105 if ( station_id(1:5) == '*****' .or
. station_id
== '*end*' ) then
106 print*,' Hit station_id as ',station_id
112 ! if ( station_id(1:5) == station_chosen ) then
114 p_ob
= 0.01 * p_ob
! Convert Pa to hPa
116 bin
= int( (bottom_pressure
- p_ob
)/bin_width_p
) + 1
117 if (bin
<= 0) bin
= 1
118 if (bin
> num_bins_p
) bin
= num_bins_p
120 call da_store_ominusb( station_id
, times
, qc
, lati
, long
, &
121 p_ob
, iv
, error
, obs(bin
) )
126 ! [1.5] A station_id = '*end*' indicates the complete end of the file:
127 if ( station_id
== '*end*' ) then
134 write(0,'(A,i8)')' Number of analysis times read in = ', num_times
137 !-----------------------------------------------------------------------
138 ! [2.0]: Calculate O-B statistics over all stations:
139 !-----------------------------------------------------------------------
141 write(0,'(A)')' P(hPA) Stations Obs-Used Obs-Reject Mean(O-B) STDV(O-B)'
142 write(ounit
,'(A)')' P(hPA) Stations Obs-Used Obs-Reject Mean(O-B) STDV(O-B)'
143 ! write(0,'(a,a)')' Station chosen = ', station_chosen
145 call da_obs_stats( num_times
, obs(b
) )
148 if ( obs(b
) % num_stations
> 0 ) then
149 percent_reject
= nint( 100.0 * real( obs(b
) % num_reject
) / &
150 real( obs(b
) % num_keep
+ obs(b
) % num_reject
) )
153 write(ounit
,'(i6,i8,2x,2i8,i3,2f13.6)')int(bin_start_p(b
)), &
154 obs(b
) % num_stations
, obs(b
) % num_keep
, &
155 obs(b
) % num_reject
, percent_reject
, &
156 obs(b
) % mean_omb
, obs(b
) % stdv_omb
160 !-----------------------------------------------------------------------
161 ! [3.0]: Calculate matrix of distances between points:
162 !-----------------------------------------------------------------------
165 n
= obs(b
) % num_stations
166 call da_get_distance( n
, obs(b
) % lat(1:n
), obs(b
) % lon(1:n
), obs(b
) % dis(1:n
,1:n
) )
167 write(0,'(4i8,2f13.6)')int(bin_start_p(b
)), obs(b
) % num_stations
, &
168 obs(b
) % num_keep
, obs(b
) % num_reject
, &
169 obs(b
) % mean_omb
, obs(b
) % stdv_omb
172 !-----------------------------------------------------------------------
173 ! [4.0]: Calculate O-B covariances:
174 !-----------------------------------------------------------------------
177 ! call da_bin_covariance( obs(b) % num_stations, num_times, max_distance, obs(b) )
178 call da_bin_covariance( obs(b
) % num_stations
, num_times
, max_distance
, obs(b
), obs_err_used(b
),b
)
183 subroutine da_init_obs( obs
)
187 type (obs_type
), intent(out
) :: obs
193 obs
% num_stations
= 0
195 obs
% id(:) = missing_c
196 obs
% lat(:) = missing_r
197 obs
% lon(:) = missing_r
198 do n
= 1, max_stations
199 obs
% data(n
) % qcflag(:) = missing_i
200 obs
% data(n
) % ominusb(:) = missing_r
201 obs
% data(n
) % sigmao(:) = missing_r
202 obs
% data(n
) % pressure(:) = missing_r
205 end subroutine da_init_obs
207 subroutine da_store_ominusb( station_id
, times
, qc
, lati
, long
, p_ob
, &
212 character*5, intent(in
) :: station_id
213 integer, intent(in
) :: times
214 integer, intent(in
) :: qc
215 real, intent(in
) :: lati
216 real, intent(in
) :: long
217 real, intent(in
) :: p_ob
218 real, intent(in
) :: iv
219 real, intent(in
) :: error
220 type(obs_type
), intent(inout
) :: obs
222 integer :: this_station
226 pi_over_180
= pi
/ 180.0
228 ! [1.0] Count total number of obs rejected by QC:
230 if ( qc
< obs_qc_pointer
) then
231 obs
% num_reject
= obs
% num_reject
+ 1
233 obs
% num_keep
= obs
% num_keep
+ 1
236 ! [2.0] Check if this station seen before:
238 if ( ANY(obs
% id(:) == station_id
) ) then
239 do n
= 1, obs
% num_stations
240 if ( obs
% id(n
) == station_id
) then
245 obs
% num_stations
= obs
% num_stations
+ 1
246 this_station
= obs
% num_stations
247 obs
% id(this_station
) = station_id
249 if ( long
>= 0.0 ) then
250 obs
% lon(this_station
) = pi_over_180
* long
252 obs
% lon(this_station
) = pi_over_180
* ( 180.0 - long
)
254 obs
% lat(this_station
) = pi_over_180
* lati
258 ! [3.0] Check if getting too many stations or times for array sizes:
260 if ( obs
% num_stations
> max_stations
) then
261 write(0,'(A)')' Need to increase max_stations. Stopping'
265 ! [4.0] Read in qc, O-B, and sigma_o:
267 obs
% data(this_station
) % qcflag(times
) = qc
268 obs
% data(this_station
) % ominusb(times
) = iv
269 obs
% data(this_station
) % sigmao(times
) = error
270 obs
% data(this_station
) % pressure(times
) = p_ob
272 end subroutine da_store_ominusb
274 subroutine da_obs_stats( num_times
, obs
)
276 integer, intent(in
) :: num_times
277 type (obs_type
), intent(inout
) :: obs
279 integer, parameter :: num_bins
= 101
281 integer :: n
, nobs
, nobs_station
, times
, b
, count
282 integer :: bin_count(1:num_bins
), maxcount
283 real :: sumobs1
, sumobs2
, sum_station
284 real :: min_bin
, max_bin
, bin_width
, omb
, x
, z
285 real :: bin_start(1:num_bins
)
286 real :: dommean(1:num_times
)
295 do n
= 1, obs
% num_stations
298 obs
% mean_ominusb(n
) = 0.0
299 do times
= 1, num_times
300 if ( obs
% data(n
) % qcflag(times
) >= obs_qc_pointer
) then
302 sumobs1
= sumobs1
+ obs
% data(n
) % ominusb(times
)
303 sumobs2
= sumobs2
+ obs
% data(n
) % ominusb(times
) **2
305 nobs_station
= nobs_station
+ 1
306 sum_station
= sum_station
+ obs
% data(n
) % ominusb(times
)
309 obs
% num_obs(n
) = nobs_station
310 if ( nobs_station
> 0 ) then
311 obs
% mean_ominusb(n
) = sum_station
/ real(nobs_station
)
316 ! Calculate basic statistics:
318 obs
% mean_omb
= sumobs1
/ real(nobs
)
319 obs
% stdv_omb
= sumobs2
/ real(nobs
) ! Actually mean square here.
320 obs
% stdv_omb
= sqrt( obs
% stdv_omb
- obs
% mean_omb
**2 )
323 ! Get data distribution:
324 max_bin
= 5.0 * obs
% stdv_omb
327 bin_width
= 2.0 * max_bin
/ real(num_bins
)
329 write (0,*) "Cannot work with a single observation"
335 bin_start(b
) = min_bin
+ real(b
-1) * bin_width
338 do n
= 1, obs
% num_stations
339 do times
= 1, num_times
340 omb
= obs
% data(n
) % ominusb(times
)
342 if ( obs
% data(n
) % qcflag(times
) >= obs_qc_pointer
) then
344 b
= int( (omb
-min_bin
) / bin_width
) + 1
346 if (b
>= 1 .and
. b
<= num_bins
) bin_count(b
) = bin_count(b
) + 1
351 maxcount
= maxval(bin_count(:))
352 ! write(0,'(a,i8)')' Max count = ', maxcount
353 ! write(0,'(a)')' Bin x=O-B z=(x-xm)/sd Count exp(-0.5*z*z)'
355 x
= bin_start(b
) + 0.5 * bin_width
356 z
= ( x
- obs
% mean_omb
) / obs
% stdv_omb
357 ! write(0,'(i4,4f12.5)')b, x, z, bin_count(b)/real(maxcount), exp(-0.5*z*z)
361 ! Get time series of mean error:
363 ! write(0,'(a)')' Time NumObs Domain Mean'
364 do times
= 1, num_times
366 do n
= 1, obs
% num_stations
367 if ( obs
% data(n
) % qcflag(times
) >= obs_qc_pointer
) then
368 dommean(times
) = dommean(times
) + obs
% data(n
) % ominusb(times
)
372 if ( count
> 0 ) dommean(times
) = dommean(times
) / real(count
)
373 ! write(0,'(i4,i8,f12.5)')times, count, dommean(times)
377 ! Remove station mean from O-B values (removes instrumental/model bias):
379 do n
= 1, obs
% num_stations
380 do times
= 1, num_times
381 if ( obs
% data(n
) % qcflag(times
) >= obs_qc_pointer
) then
382 obs
% data(n
) % ominusb(times
) = obs
% data(n
) % ominusb(times
) - &
383 obs
% mean_ominusb(n
)
389 end subroutine da_obs_stats
391 subroutine da_get_distance( num_stations
, lat
, lon
, dis
)
395 integer, intent(in
) :: num_stations
396 real, intent(in
) :: lat(1:num_stations
)
397 real, intent(in
) :: lon(1:num_stations
)
398 real, intent(out
) :: dis(1:num_stations
,1:num_stations
)
401 real :: pi_over_2
, colat1
, colat2
, londiff
406 dis(1:num_stations
,1:num_stations
) = 0.0
407 do n1
= 1, num_stations
408 colat1
= pi_over_2
- lat(n1
)
410 do n2
= n1
, num_stations
412 colat2
= pi_over_2
- lat(n2
)
413 londiff
= abs(lon(n2
) - lon(n1
))
415 dist
= acos( cos(colat1
) * cos(colat2
) + &
416 sin(colat1
) * sin(colat2
) * cos(londiff
) ) !in radians
417 dis(n1
,n2
) = earth_radius
* dist
! in km
423 end subroutine da_get_distance
425 subroutine da_bin_covariance( num_stations
, num_times
, max_distance
, obs
, obs_err_used
, lvl
)
429 integer, intent(in
) :: num_stations
430 integer, intent(in
) :: num_times
431 real, intent(in
) :: max_distance
432 type (obs_type
), intent(inout
) :: obs
433 real, intent(in
) :: obs_err_used
434 integer, intent(in
) :: lvl
436 integer :: b
, n1
, n2
, times
, sum_b
437 real :: bin_width
, bin_dis
438 real :: sum_dis
, sum_covar
, sum2covar
439 real :: covar
, gaussian
440 real :: x
, y
, sum_x
, sum_x2
, sum_xy
, sum_y
441 real :: gradient
, lengthscale
, intercept
442 real :: bk_err_var
, bk_err_stdv
, ob_err_stdv
443 real :: avg_dis(1:num_bins
)
444 real :: coverr(1:num_bins
)
445 integer :: sum_obs(1:num_bins
)
446 logical :: bin_pass(1:num_bins
)
453 bin_width
= max_distance
/ real(num_bins
)
455 ! Calculate covariance and sum for all good obs:
458 bin_dis
= real(b
-1) * bin_width
463 do n1
= 1, num_stations
464 do n2
= n1
, num_stations
465 if ( obs
% dis(n1
,n2
) >= bin_dis
.and
. &
466 obs
% dis(n1
,n2
) < bin_dis
+ bin_width
.and
. &
467 obs
% dis(n1
,n2
) /= 0.0 ) then
469 do times
= 1, num_times
470 if ( obs
% data(n2
) % qcflag(times
) >= obs_qc_pointer
.and
. &
471 obs
% data(n1
) % qcflag(times
) >= obs_qc_pointer
) then
473 covar
= obs
% data(n2
) % ominusb(times
) * &
474 obs
% data(n1
) % ominusb(times
)
476 sum_obs(b
) = sum_obs(b
) + 1
477 sum_dis
= sum_dis
+ obs
% dis(n1
,n2
)
478 sum_covar
= covar
+ sum_covar
479 sum2covar
= covar
**2 + sum2covar
488 ! write(6,'(2i8,2f15.5)')b, sum_obs(b), sum_covar, obs % cov(b)
490 ! Calculate average separation and covariance of obs:
491 if ( sum_obs(b
) > 0 ) then
492 avg_dis(b
) = sum_dis
/ real(sum_obs(b
))
493 obs
% cov(b
) = sum_covar
/ real(sum_obs(b
))
494 sum2covar
= sum2covar
/ real(sum_obs(b
))
497 ! Calculate 95% error bar for obs % cov estimate = 1.96 s/sqrt(n-1):
498 ! Needs min_obs observations in bin to be considered in Gaussian calc.
499 bin_pass(b
) = .false
.
501 if ( sum_obs(b
) > min_obs
) then
502 coverr(b
) = 1.96 * sqrt( sum2covar
- obs
% cov(b
)**2 ) / &
503 sqrt( real(sum_obs(b
) - 1) )
505 if ( coverr(b
) > 0.0 .and
. coverr(b
) < obs
% cov(b
) ) then
512 ! Fit good data to a Gaussian to get lengthscale:
513 ! B = B0 exp (-r**2 / 8s**2) => ln B = ln B0 - r**2 / 8s**2
514 ! y = gradient * x + intercept => gradient = -1/8s**2, x = r**2, intercept = lnB0
520 if ( any(bin_pass
) ) then
527 if ( bin_pass(b
) ) then
528 x
= avg_dis(b
) * avg_dis(b
)
529 y
= log( obs
% cov(b
) )
532 sum_x2
= sum_x2
+ x
* x
533 sum_xy
= sum_xy
+ x
* y
538 if ( sum_b
> 1 ) then
539 gradient
= ( real(sum_b
) * sum_xy
- sum_x
* sum_y
) / &
540 ( real(sum_b
) * sum_x2
- sum_x
* sum_x
)
541 intercept
= ( sum_x2
* sum_y
- sum_x
* sum_xy
) / &
542 ( real(sum_b
) * sum_x2
- sum_x
* sum_x
)
543 if ( gradient
< 0.0 ) then
544 lengthscale
= sqrt( -1.0 / ( 8.0 * gradient
) )
545 bk_err_var
= exp(intercept
)
550 ! Print out covariance data:
555 write(0,'(a)')' Bin NumObs Separation(km) Covariance CovErr GaussFit'
556 if ( gradient
< 0.0 ) then
557 gaussian
= bk_err_var
*exp(-0.125*(avg_dis(b
)/lengthscale
)**2)
560 if (obs
% cov(b
) /= 0.0 ) then
561 write(0,'(i5,i7,3e14.6,l1,e13.6)')b
, sum_obs(b
), avg_dis(b
), &
563 coverr(b
), bin_pass(b
), gaussian
564 if (obs
% cov(b
) /= 0.0 .and
. bin_pass(b
) ) &
565 write(70+lvl
,'(2i7,4e14.6)')b
, sum_obs(b
), avg_dis(b
), obs
% cov(b
),&
571 if ( sum_b
> 1 ) then
572 ob_err_stdv
= sqrt( obs
% stdv_omb
**2 - bk_err_var
)
573 bk_err_stdv
= sqrt( bk_err_var
)
574 write(0,'(a)')' The following are derived from Gaussian fit (warning):'
575 write(0,'(3(a,e14.6),2(a,i3),a)')' Scale =', lengthscale
, &
576 ' km, ob_err_stdv = ', ob_err_stdv
, &
577 ' , bk_err_stdv = ', bk_err_stdv
, &
578 ' using', sum_b
, ' /', num_bins
, ' bins.'
579 write(30,'(4f15.3)') ob_err_stdv
, bk_err_stdv
, obs_err_used
, lengthscale
582 end subroutine da_bin_covariance
584 end program da_tune_obs_hollingsworth2