UFO
gnssro_mod_obserror.F90
Go to the documentation of this file.
1 !==========================================================================
3 !==========================================================================
4 
5 use kinds
8 use fckit_log_module, only: fckit_log
9 
10 contains
11 subroutine bending_angle_obserr_ecmwf(obsImpH, obsValue, nobs, obsErr, QCflags, missing)
12 implicit none
13 integer, intent(in) :: nobs
14 real(kind_real), dimension(nobs),intent(in) :: obsImpH, obsValue
15 integer(c_int), dimension(nobs),intent(in) :: QCflags(:)
16 real(kind_real), dimension(nobs),intent(out) :: obsErr
17 real(kind_real) :: H_km, missing
18 integer :: i
19 
20 obserr = missing
21 
22 do i = 1, nobs
23 
24 if (qcflags(i) .eq. 0) then
25 
26  h_km = obsimph(i)/1000.0_kind_real
27 
28  if ( h_km <= 10.0 ) then
29  obserr(i) = (h_km*1.25 + (10-h_km)*20)/10.0
30  obserr(i) = obserr(i)/100.0*obsvalue(i)
31  else if ( h_km > 10.0 .and. h_km <= 32.0 ) then
32  obserr(i) = 1.25/100.0*obsvalue(i)
33  else
34  obserr(i) = 3.0*1e-6
35  end if
36 end if
37 
38 end do
39 
40 end subroutine bending_angle_obserr_ecmwf
41 !----------------------------------------
42 
43 subroutine bending_angle_obserr_nrl(obsLat, obsImpH, obsValue, nobs, obsErr, QCflags, missing)
44 implicit none
45 integer, intent(in) :: nobs
46 real(kind_real), dimension(nobs),intent(in) :: obsImpH, obsValue, obsLat
47 integer(c_int), dimension(nobs),intent(in) :: QCflags(:)
48 real(kind_real), dimension(nobs),intent(out) :: obsErr
49 real(kind_real):: H_m, missing
50 real(kind_real):: lat_in_rad,trop_proxy,damping_factor,errfac
51 real(kind_real):: max_sfc_error, min_ba_error
52 
53 integer :: i
54 
55 obserr = missing
56 max_sfc_error = 20.0 ! %
57 min_ba_error = 1.25 ! %
58 
59 do i = 1, nobs
60 
61 if (qcflags(i) .eq. 0) then
62 
63  h_m = obsimph(i)
64  lat_in_rad = deg2rad * obslat(i)
65  trop_proxy = 8666.66 + 3333.33*cos(2.0*lat_in_rad)
66  damping_factor = 0.66 + cos(lat_in_rad)/3.0
67  errfac = max_sfc_error*damping_factor*(trop_proxy - h_m)/trop_proxy
68  errfac = max(min_ba_error, errfac)
69  obserr(i) = max(obsvalue(i)*errfac/100.0, 3.0*1e-6) ! noise floor at top of RO profile
70 
71 end if
72 
73 end do
74 
75 end subroutine bending_angle_obserr_nrl
76 !--------------------------------------
77 
78 subroutine bending_angle_obserr_nbam(obsLat, obsImpH, obsSaid, nobs, obsErr, QCflags, missing)
79 implicit none
80 integer, intent(in) :: nobs
81 real(kind_real), dimension(nobs),intent(in) :: obsImpH, obsLat
82 integer(c_int), dimension(nobs),intent(in) :: obsSaid, QCflags(:)
83 real(kind_real), dimension(nobs),intent(out) :: obsErr
84 real(kind_real) :: H_km, missing
85 
86 integer :: i
87 
88 obserr = missing
89 
90 do i = 1, nobs
91 
92 if (qcflags(i) .eq. 0) then
93 
94  h_km = obsimph(i)/1000.0_kind_real
95  if( (obssaid(i)==41).or.(obssaid(i)==722).or.(obssaid(i)==723).or. &
96  (obssaid(i)==4).or.(obssaid(i)==42).or.(obssaid(i)==3).or. &
97  (obssaid(i)==5).or.(obssaid(i)==821.or.(obssaid(i)==421)).or. &
98  (obssaid(i)==440).or.(obssaid(i)==43)) then
99  if( abs(obslat(i))>= 40.00 ) then
100  if(h_km>12.0) then
101  obserr(i)=0.19032 +0.287535 *h_km-0.00260813*h_km**2
102  else
103  obserr(i)=-3.20978 +1.26964 *h_km-0.0622538 *h_km**2
104  endif
105  else
106  if(h_km>18.) then
107  obserr(i)=-1.87788 +0.354718 *h_km-0.00313189 *h_km**2
108  else
109  obserr(i)=-2.41024 +0.806594 *h_km-0.027257 *h_km**2
110  endif
111  endif
112 
113  else !!!! CDAAC processing
114  if( abs(obslat(i))>= 40.00 ) then
115  if ( h_km>12.00 ) then
116  obserr(i)=-0.685627 +0.377174 *h_km-0.00421934 *h_km**2
117  else
118  obserr(i)=-3.27737 +1.20003 *h_km-0.0558024 *h_km**2
119  endif
120  else
121  if( h_km>18.00 ) then
122  obserr(i)=-2.73867 +0.447663 *h_km-0.00475603 *h_km**2
123  else
124  obserr(i)=-3.45303 +0.908216 *h_km-0.0293331 *h_km**2
125  endif
126  endif
127  obserr(i) = 0.001 /abs(exp(obserr(i)))
128 
129  endif
130 
131 end if
132 
133 end do
134 
135 end subroutine bending_angle_obserr_nbam
136 !---------------------------------------
137 
138 subroutine refractivity_obserr_nbam(obsLat, obsZ, nobs, obsErr, QCflags,missing)
139 implicit none
140 integer, intent(in) :: nobs
141 real(kind_real), dimension(nobs),intent(in) :: obsLat, obsZ
142 real(kind_real), dimension(nobs),intent(out) :: obsErr
143 integer(c_int), dimension(nobs),intent(in) :: QCflags(:)
144 real(kind_real) :: H_km, missing
145 
146 integer :: i
147 
148 obserr = missing
149 
150 do i = 1, nobs
151 
152  if (qcflags(i) .eq. 0) then
153  h_km = obsz(i)/1000.0_kind_real
154  if( abs(obslat(i))>= 20.0 ) then
155  obserr(i)=-1.321+0.341*h_km-0.005*h_km**2
156  else
157  if(h_km > 10.0) then
158  obserr(i)=2.013-0.060*h_km+0.0045*h_km**2
159  else
160  obserr(i)=-1.18+0.058*h_km+0.025*h_km**2
161  endif
162  endif
163  obserr(i) = 1.0_kind_real/abs(exp(obserr(i)))
164  end if
165 end do
166 
167 end subroutine refractivity_obserr_nbam
168 
169 
170 subroutine gnssro_obserr_avtemp(nobs, n_horiz, rmatrix_filename, obsSatid, obsOrigC, nlevs, &
171  air_temperature, geopotential_height, obsZ, obsValue, obsErr, &
172  QCflags, missing)
173 
174 implicit none
175 
176 ! Subroutine arguments
177 integer, intent(in) :: nobs ! Number of observations
178 integer, intent(in) :: n_horiz ! Number of geovals per observation
179 character(len=*), intent(in) :: rmatrix_filename ! Name of the R-matrix file
180 integer, intent(in) :: obsSatid(:) ! Satellite identifier
181 integer, intent(in) :: obsOrigC(:) ! Originating centre number
182 integer, intent(in) :: nlevs ! Number of model levels
183 real, intent(in) :: air_temperature(:,:) ! Temperature of the model background
184 real, intent(in) :: geopotential_height(:,:) ! Geopotential height of the model levels
185 real(kind_real), intent(in) :: obsZ(:) ! Height of the observation
186 real(kind_real), intent(in) :: obsValue(:) ! The observed value
187 real(kind_real), intent(out) :: obsErr(:) ! The calculated observation error (uncertainty)
188 integer(c_int), intent(in) :: QCflags(:) ! Quality control flags for the observations
189 real(kind_real), intent(in) :: missing ! Missing value indicator
190 
191 ! Local parameters
192 integer, parameter :: Rmax_num = 1000 ! Max number of R matrices to be read in
193 
194 ! Local variables
195 type(rmatrix_type), allocatable :: Rmatrix_list(:) ! List of all the R matrices to use
196 type(rmatrix_type) :: Rmatrix ! The chosen R matrix
197 real(kind_real) :: frac_err ! Fractional observation error
198 real :: av_temp
199 integer :: npoints
200 integer :: ilev
201 integer :: R_num_sats ! Actual number of R-matrices read in
202 character(len=200) :: Message ! Message to be output
203 integer :: iob ! Loop variable, observation number
204 integer :: igeoval ! Loop variable, geoval number
205 integer :: iheight ! Loop variable, height in profile
206 
207 ! Read in R matrix data
208 CALL ufo_roobserror_getrmatrix(rmax_num, & ! Max number of R matrices to read in
209  rmatrix_filename, & ! The name of the file to be read in
210  rmatrix_list, & ! List of all R matrices to use
211  r_num_sats) ! Number of R matrices read in
212 !--------------------------------------------------------
213 ! Choose the R-matrix values. We use different code if we are passed
214 ! a matrix which depends on latitude or average temperature
215 !--------------------------------------------------------
216 
217 do iob = 1, nobs
218  if (qcflags(iob) .eq. 0) then
219 
220  IF (rmatrix_list(1) % av_temp > 0) THEN
221  !--------------------------------------------------------
222  ! Choose R matrix depending on satid, origctr and the average
223  ! background temperature between the surface and 20km
224  !--------------------------------------------------------
225 
226  ! Calculate the average troposphere temperature for this profile
227 
228  av_temp = 0
229  npoints = 0
230  igeoval = (iob-1) * n_horiz + (n_horiz + 1) / 2
231  DO ilev = 1, nlevs
232  IF (geopotential_height(igeoval, ilev) < rmatrix_list(1) % max_height) THEN
233  av_temp = av_temp + air_temperature(igeoval, ilev)
234  npoints = npoints + 1
235  END IF
236  END DO
237 
238  IF (npoints > 0) THEN
239  av_temp = av_temp / npoints
240  ELSE
241  av_temp = missing
242  END IF
243 
244  ! Find the observation error matrix which best matches the average
245  ! temperature we found
246 
247  CALL ufo_roobserror_interpolate_rmatrix(obssatid(iob), &
248  obsorigc(iob), &
249  av_temp, &
250  r_num_sats, &
251  rmatrix_list, &
252  rmatrix)
253 
254  ELSE
255  WRITE (message, '(2A)') "RMatrices must have positive average ", &
256  "temperature"
257  CALL abor1_ftn(message)
258  END IF
259 
260  do iheight = 1, rmatrix % num_heights - 1
261  if (obsz(iob) < rmatrix % height(iheight + 1)) then
262  exit
263  end if
264  end do
265 
266  ! Fractional error
267  frac_err = rmatrix % frac_err(iheight) + &
268  (rmatrix % frac_err(iheight + 1) - rmatrix % frac_err(iheight)) * &
269  (obsz(iob) - rmatrix % height(iheight)) / &
270  (rmatrix % height(iheight + 1) - rmatrix % height(iheight))
271 
272  WRITE(message,'(A,I8,2F16.4,2E26.8)') 'Result', iob, obsz(iob), frac_err, &
273  obserr(iob), max(frac_err * obsvalue(iob), rmatrix % min_error)
274  CALL fckit_log % debug(message)
275 
276  ! Standard deviation
277  obserr(iob) = max(frac_err * obsvalue(iob), rmatrix % min_error)
278 
279  else
280  obserr(iob) = missing
281  end if
282 end do
283 
284 end subroutine gnssro_obserr_avtemp
285 
286 
287 subroutine gnssro_obserr_latitude(nobs, rmatrix_filename, obsSatid, obsOrigC, obsLat, obsZ, obsValue, obsErr, QCflags, missing)
288 
289 implicit none
290 
291 ! Subroutine arguments
292 integer, intent(in) :: nobs ! Number of observations
293 character(len=*), intent(in) :: rmatrix_filename ! Name of the R-matrix file
294 integer, intent(in) :: obsSatid(:) ! Satellite identifier
295 integer, intent(in) :: obsOrigC(:) ! Originating centre number
296 real(kind_real), intent(in) :: obsLat(:) ! Latitude of the observation
297 real(kind_real), intent(in) :: obsZ(:) ! Height of the observation
298 real(kind_real), intent(in) :: obsValue(:) ! The observed value
299 real(kind_real), intent(out) :: obsErr(:) ! The calculated observation error (uncertainty)
300 integer(c_int), intent(in) :: QCflags(:) ! Quality control flags for the observations
301 real(kind_real), intent(in) :: missing ! Missing value indicator
302 
303 ! Local parameters
304 integer, parameter :: Rmax_num = 1000 ! Max number of R matrices to be read in
305 
306 ! Local variables
307 type(rmatrix_type), allocatable :: Rmatrix_list(:) ! List of all the R matrices to use
308 type(rmatrix_type) :: Rmatrix ! The chosen R matrix
309 real(kind_real) :: frac_err ! Fractional observation error
310 integer :: R_num_sats ! Actual number of R-matrices read in
311 character(len=200) :: Message ! Message to be output
312 integer :: iob ! Loop variable, observation number
313 integer :: iheight ! Loop variable, height in profile
314 
315 ! Read in R matrix data
316 CALL ufo_roobserror_getrmatrix(rmax_num, & ! Max number of R matrices to read in
317  rmatrix_filename, & ! The name of the file to be read in
318  rmatrix_list, & ! List of all R matrices to use
319  r_num_sats) ! Number of R matrices read in
320 
321 !--------------------------------------------------------
322 ! Choose R matrix depending on satid, origctr and latitude
323 ! No interpolation between matrices to match old code
324 !--------------------------------------------------------
325 
326 do iob = 1, nobs
327  if (qcflags(iob) .eq. 0) then
328  IF (rmatrix_list(1) % latitude > 0) THEN
329  CALL ufo_roobserror_findnearest_rmatrix(obssatid(iob), &
330  obsorigc(iob), &
331  obslat(iob), &
332  r_num_sats, &
333  rmatrix_list, &
334  rmatrix)
335  ELSE
336  WRITE (message, '(2A)') "RMatrices must have positive average ", &
337  "temperature or latitude"
338  CALL abor1_ftn(message)
339  END IF
340 
341  do iheight = 1, rmatrix % num_heights
342  if (obsz(iob) < rmatrix % height(iheight)) then
343  exit
344  end if
345  end do
346 
347  ! Calculate fractional error
348  if (iheight == 1) then
349  frac_err = rmatrix % frac_err(iheight)
350  else if (iheight > rmatrix % num_heights) then
351  frac_err = rmatrix % frac_err(rmatrix % num_heights)
352  else
353  frac_err = rmatrix % frac_err(iheight - 1) + &
354  (rmatrix % frac_err(iheight) - rmatrix % frac_err(iheight - 1)) * &
355  (obsz(iob) - rmatrix % height(iheight - 1)) / &
356  (rmatrix % height(iheight) - rmatrix % height(iheight - 1))
357  end if
358 
359  WRITE(message,'(A,I8,2F16.4,2E21.8,F12.4)') 'Result', iob, obsz(iob), &
360  frac_err, obserr(iob), max(frac_err * obsvalue(iob), rmatrix % min_error), &
361  obslat(iob)
362  CALL fckit_log % debug(message)
363 
364  ! Standard deviation
365  obserr(iob) = max(frac_err * obsvalue(iob), rmatrix % min_error)
366 
367  else
368  WRITE(message,'(A,I8,2F16.4)') 'Missing', iob, obsz(iob), obslat(iob)
369  CALL fckit_log % debug(message)
370  obserr(iob) = missing
371  end if
372 end do
373 
374 end subroutine gnssro_obserr_latitude
375 
376 end module gnssro_mod_obserror
377 
subroutine bending_angle_obserr_nbam(obsLat, obsImpH, obsSaid, nobs, obsErr, QCflags, missing)
subroutine bending_angle_obserr_ecmwf(obsImpH, obsValue, nobs, obsErr, QCflags, missing)
subroutine gnssro_obserr_latitude(nobs, rmatrix_filename, obsSatid, obsOrigC, obsLat, obsZ, obsValue, obsErr, QCflags, missing)
subroutine refractivity_obserr_nbam(obsLat, obsZ, nobs, obsErr, QCflags, missing)
subroutine gnssro_obserr_avtemp(nobs, n_horiz, rmatrix_filename, obsSatid, obsOrigC, nlevs, air_temperature, geopotential_height, obsZ, obsValue, obsErr, QCflags, missing)
subroutine bending_angle_obserr_nrl(obsLat, obsImpH, obsValue, nobs, obsErr, QCflags, missing)
Fortran module for utility routines used in calculating the observation uncertainties,...
subroutine, public ufo_roobserror_getrmatrix(max_mats, filename, Rmatrix, R_num_mats)
subroutine ufo_roobserror_findnearest_rmatrix(satid, origc, latitude, R_num_sats, Rmatrix_list, out_matrix)
subroutine ufo_roobserror_interpolate_rmatrix(satid, origc, av_temp, R_num_sats, Rmatrix_list, out_matrix)