UFO
ufo_roobserror_utils_mod.F90
Go to the documentation of this file.
1 !-------------------------------------------------------------------------------
2 ! (C) British Crown Copyright 2020 Met Office
3 !
4 ! This software is licensed under the terms of the Apache Licence Version 2.0
5 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6 !-------------------------------------------------------------------------------
7 
8 !> Fortran module for utility routines used in calculating the observation
9 !> uncertainties, using the Met Office calculations
10 
12 
13 use kinds
14 use fckit_log_module, only: fckit_log
16 use missing_values_mod
17 
18 implicit none
19 
21 
23  INTEGER :: satid = 0 ! Sat ID for each R matrix
24  INTEGER :: origctr = 0 ! Originating centre for each R matrix
25  REAL :: av_temp ! Average temperature of the troposphere (if used)
26  INTEGER :: max_height ! The maximum height used in calculating the average temperature
27  REAL :: latitude ! The latitude of this R matrix (if used)
28  INTEGER :: num_heights ! Number of heights in the R matrix specification (i.e. the size of the matrix)
29  REAL :: clen ! Correlation length-scale for the R matrix
30  REAL :: min_error ! Minimum value of the observation error (in radians)
31  REAL, POINTER :: height(:) ! The height for each specified observation error
32  REAL, POINTER :: frac_err(:) ! The fractional error at this height (obs error / background measurement)
33 end type
34 
35 contains
36 
37 !-------------------------------------------------------------------------------
38 ! Read data needed to set up the Rmatrix.
39 !-------------------------------------------------------------------------------
40 
41 SUBROUTINE ufo_roobserror_getrmatrix(max_mats, & ! The maximum number of matrices
42  filename, & ! The name of the file to be read in
43  Rmatrix, & ! The R matrices read from the files
44  R_num_mats) ! Number of R matrices read in
45 
46 IMPLICIT NONE
47 
48 ! Subroutine arguments:
49 INTEGER, INTENT(IN) :: max_mats ! The maximum number of matrices possible
50 CHARACTER(LEN=*), INTENT(IN) :: filename ! The name of the file to be read in
51 TYPE (rmatrix_type), ALLOCATABLE, INTENT(OUT) :: rmatrix(:) ! The R matrices read from the files
52 INTEGER, INTENT(OUT) :: r_num_mats ! Number of sats/processing R matrices in these files
53 
54 ! Local declarations:
55 CHARACTER(len=*), PARAMETER :: routinename = "ufo_roobserror_getrmatrix"
56 TYPE (rmatrix_type) :: temp_rmat(max_mats) ! Temporary store of the matrices read in
57 INTEGER :: i ! Loop variable when copying matrices
58 INTEGER :: fileunit ! The unit number of the input file
59 CHARACTER(len=200) :: errormessage ! The error message to be output
60 INTEGER :: return_code ! Return code from routines called
61 
62 !-----------------------------------------------
63 ! 0. Open the R-matrix file
64 !-----------------------------------------------
65 
66 r_num_mats = 0
67 
68 fileunit = ufo_utils_iogetfreeunit()
69 OPEN(unit=fileunit, file=filename, action='READ', status='OLD', iostat=return_code)
70 if (return_code /= 0) then
71  WRITE(errormessage, '(3A,I0)') "Error opening ", trim(filename), &
72  ", return code = ", return_code
73  call abor1_ftn(errormessage)
74 end if
75 
76 !-----------------------------------------------
77 ! 1. Read all the namelists that are in the file - read these into a temporary
78 ! array to store the data
79 !-----------------------------------------------
80 
81 DO
82  IF (r_num_mats >= max_mats) THEN
83  WRITE (errormessage, '(A,I0,A,I0)') &
84  "Trying to read too many R-matrices ", &
85  r_num_mats, " out of ", max_mats
86  CALL abor1_ftn(errormessage)
87  END IF
88 
89  CALL ufo_roobserror_read_rmatrix (fileunit, &
90  temp_rmat(r_num_mats + 1), &
91  return_code)
92  IF (return_code < 0) THEN
93  ! End of file reached
94  EXIT
95  ELSE IF (return_code > 0) THEN
96  WRITE(errormessage, '(A,I0)') &
97  "Trouble reading R-matrix namelist, error number ", &
98  return_code
99  CALL abor1_ftn(errormessage)
100  ELSE
101  r_num_mats = r_num_mats + 1
102  END IF
103 END DO
104 
105 !-----------------------------------------------
106 ! 2. Copy the R-matrices from the temporary array to the output variable
107 !-----------------------------------------------
108 
109 ALLOCATE (rmatrix(r_num_mats))
110 DO i = 1, r_num_mats
111  CALL ufo_roobserror_copy_rmatrix (temp_rmat(i), &
112  rmatrix(i))
113 END DO
114 
115 CLOSE(fileunit)
116 
117 !-----------------------------------------------
118 ! 3. Check that all the matrices have the same max_height value
119 !-----------------------------------------------
120 
121 DO i = 2, r_num_mats
122  IF (rmatrix(i) % max_height /= rmatrix(1) % max_height) THEN
123  WRITE (errormessage, '(A,I0,A,I0,A,I0)') &
124  "All the R-matrices should have the same value for max_height Rmatrix(", &
125  i, ") % max_height = ", rmatrix(i) % max_height, &
126  ", RMatrix(1) % max_height = ", rmatrix(1) % max_height
127  CALL abor1_ftn(errormessage)
128  END IF
129 END DO
130 
131 END SUBROUTINE ufo_roobserror_getrmatrix
132 
133 
134 !-------------------------------------------------------------------------------
135 ! (C) Crown copyright Met Office. All rights reserved.
136 ! Refer to COPYRIGHT.txt of this distribution for details.
137 !-------------------------------------------------------------------------------
138 ! Read data needed to set up the Rmatrix.
139 !-------------------------------------------------------------------------------
140 
141 SUBROUTINE ufo_roobserror_read_rmatrix (fileunit, & ! The maximum number of matrices
142  Rmatrix, & ! The R matrices read from the files
143  return_code) ! Number of R matrices read in
144 
145 IMPLICIT NONE
146 
147 ! Subroutine arguments:
148 INTEGER, INTENT(IN) :: fileunit ! The unit number of the open file
149 TYPE (Rmatrix_type), INTENT(OUT) :: Rmatrix ! The R matrix read from the file
150 INTEGER, INTENT(OUT) :: return_code ! Return code from this routine
151 
152 ! Local declarations:
153 CHARACTER(len=*), PARAMETER :: RoutineName = "ufo_roobserror_read_rmatrix"
154 CHARACTER(len=200) :: ErrorMessage
155 REAL :: av_temp ! Average troposphere temperature
156 INTEGER :: max_height ! The maximum height for calculating the average temperature
157 REAL :: latitude ! Latitude
158 REAL :: heights(600) ! Heights of the observation errors
159 REAL :: obs_errors(600) ! Observation errors (on vertical levels)
160 INTEGER :: satid ! Satellite identifier
161 INTEGER :: origc ! Originating centre for the RO data
162 REAL :: clen ! Correlation length-scale for the R-matrix
163 REAL :: min_error ! Minimum observation error
164 
165 namelist / gpsro_ob_error / &
166  av_temp, &
167  heights, &
168  obs_errors, &
169  satid, &
170  origc, &
171  clen, &
172  min_error, &
173  latitude, &
174  max_height
175 
176 ! Initialise values to blank / sensible values
177 
178 satid = missing_value(satid)
179 origc = missing_value(origc)
180 min_error = 0
181 heights = missing_value(heights(1))
182 obs_errors = missing_value(obs_errors(1))
183 clen = 1.0e10
184 av_temp = missing_value(av_temp)
185 latitude = missing_value(latitude)
186 max_height = 0
187 
188 ! Read the namelist, and decode this into the R-matrix structure
189 
190 READ (fileunit, nml = gpsro_ob_error, iostat = return_code)
191 IF (return_code == 0) THEN
192 
193  rmatrix % num_heights = count(heights /= missing_value(heights(1)))
194  IF (count(obs_errors /= missing_value(obs_errors(1))) /= rmatrix % num_heights) THEN
195  WRITE (errormessage, '(A,I0,1X,I0)') "Counts do not match ", &
196  count(obs_errors /= missing_value(obs_errors(1))), rmatrix % num_heights
197  CALL abor1_ftn(errormessage)
198  ELSE
199  ALLOCATE (rmatrix % height(1:rmatrix % num_heights))
200  ALLOCATE (rmatrix % frac_err(1:rmatrix % num_heights))
201  rmatrix % height(:) = pack(heights, heights /= missing_value(heights(1)))
202  rmatrix % frac_err(:) = pack(obs_errors, obs_errors /= missing_value(obs_errors(1)))
203  rmatrix % frac_err(:) = rmatrix % frac_err(:)
204  END IF
205 
206  rmatrix % av_temp = av_temp
207  rmatrix % max_height = max_height
208  rmatrix % latitude = latitude
209  rmatrix % satid = satid
210  rmatrix % origctr = origc
211  rmatrix % clen = clen
212  rmatrix % min_error = min_error
213 END IF
214 
215 END SUBROUTINE ufo_roobserror_read_rmatrix
216 
217 
218 !-------------------------------------------------------------------------------
219 ! (C) Crown copyright Met Office. All rights reserved.
220 ! Refer to COPYRIGHT.txt of this distribution for details.
221 !-------------------------------------------------------------------------------
222 ! Choose the R matrix to apply for this observation. The matrix will be
223 ! be selected to have the same satellite identifier and originating centre as
224 ! requested. The matrices will be interpolated from those with the nearest
225 ! average temperature value.
226 !-------------------------------------------------------------------------------
227 
229  origc, &
230  av_temp, &
231  R_num_sats, &
232  Rmatrix_list, &
233  out_matrix)
234 
235 
236 IMPLICIT NONE
237 
238 ! Subroutine arguments:
239 INTEGER, INTENT(IN) :: satid ! The satellite identifier | These three entries will
240 INTEGER, INTENT(IN) :: origc ! The originating centre | be used to find the
241 REAL, INTENT(IN) :: av_temp ! The average temperature | R matrix from the list
242 INTEGER, INTENT(IN) :: R_num_sats ! The number of matrices in the list
243 TYPE (Rmatrix_type), INTENT(IN) :: RMatrix_list(R_num_sats) ! The list of R matrices to select from
244 TYPE (Rmatrix_type), INTENT(OUT) :: out_matrix ! Output R matrix
245 
246 ! Local declarations:
247 CHARACTER(len=*), PARAMETER :: RoutineName = "ufo_roobserror_interpolate_rmatrix"
248 INTEGER :: i ! Loop variable
249 INTEGER :: lower_match ! The largest av_temp which is smaller than the one being searched for
250 INTEGER :: upper_match ! The smallest av_temp which is larger than the one being searched for
251 REAL :: weight ! Weight to give to the upper matched av_temp
252 CHARACTER(len=200) :: ErrorMessage ! Error message to be output
253 REAL, ALLOCATABLE :: interp_errors(:) ! Observation errors, interpolated to the required av_temp
254 
255 lower_match = 0
256 upper_match = 0
257 DO i = 1, r_num_sats
258  IF (satid == rmatrix_list(i) % satid .AND. &
259  origc == rmatrix_list(i) % origctr) THEN
260  IF (rmatrix_list(i) % av_temp /= missing_value(rmatrix_list(i) % av_temp)) THEN
261  IF (rmatrix_list(i) % av_temp > av_temp) THEN
262  IF (upper_match == 0) THEN
263  upper_match = i
264  ELSE IF (rmatrix_list(i) % av_temp < rmatrix_list(upper_match) % av_temp) THEN
265  upper_match = i
266  END IF
267  ELSE
268  IF (lower_match == 0) THEN
269  lower_match = i
270  ELSE IF (rmatrix_list(i) % av_temp > rmatrix_list(lower_match) % av_temp) THEN
271  lower_match = i
272  END IF
273  END IF
274  END IF
275  END IF
276 END DO
277 
278 IF (upper_match > 0) THEN
279  IF (lower_match > 0) THEN
280  WRITE (errormessage, '(A,3F10.2)') "Interpolating between locations.. ", &
281  rmatrix_list(lower_match) % av_temp, &
282  av_temp, rmatrix_list(upper_match) % av_temp
283  call fckit_log % debug(errormessage)
284  weight = (av_temp - rmatrix_list(lower_match) % av_temp) / &
285  (rmatrix_list(upper_match) % av_temp - rmatrix_list(lower_match) % av_temp)
286 
287  out_matrix % num_heights = rmatrix_list(lower_match) % num_heights
288  ALLOCATE (out_matrix % height(1:out_matrix % num_heights))
289  ALLOCATE (out_matrix % frac_err(1:out_matrix % num_heights))
290  ALLOCATE (interp_errors(1:out_matrix % num_heights))
291  CALL ufo_roobserror_interpolate_heights(rmatrix_list(lower_match) % num_heights, &
292  rmatrix_list(lower_match) % height, &
293  rmatrix_list(upper_match) % num_heights, &
294  rmatrix_list(upper_match) % height, &
295  rmatrix_list(upper_match) % frac_err, &
296  interp_errors)
297  out_matrix % height(:) = rmatrix_list(lower_match) % height(:)
298  out_matrix % frac_err(:) = weight * interp_errors + &
299  (1 - weight) * rmatrix_list(lower_match) % frac_err
300 
301  out_matrix % av_temp = av_temp
302 
303  ! max_height should be the same for every matrix, so do not interpolate
304  out_matrix % max_height = rmatrix_list(lower_match) % max_height
305  out_matrix % satid = satid
306  out_matrix % origctr = origc
307  out_matrix % clen = weight * rmatrix_list(upper_match) % clen + &
308  (1 - weight) * rmatrix_list(lower_match) % clen
309  out_matrix % min_error = weight * rmatrix_list(upper_match) % min_error + &
310  (1 - weight) * rmatrix_list(lower_match) % min_error
311  out_matrix % latitude = missing_value(out_matrix % latitude)
312  ELSE
313  CALL ufo_roobserror_copy_rmatrix(rmatrix_list(upper_match), &
314  out_matrix)
315  END IF
316 ELSE
317  IF (lower_match > 0) THEN
318  CALL ufo_roobserror_copy_rmatrix(rmatrix_list(lower_match), &
319  out_matrix)
320  ELSE
321  WRITE (errormessage, '(A,I0,1X,I0,1X)') "Did not match any matrices ", satid, origc
322  CALL fckit_log % warning(errormessage)
323  CALL ufo_roobserror_copy_rmatrix(rmatrix_list(1), &
324  out_matrix)
325  END IF
326 END IF
327 
329 
330 
331 !-------------------------------------------------------------------------------
332 ! (C) Crown copyright Met Office. All rights reserved.
333 ! Refer to COPYRIGHT.txt of this distribution for details.
334 !-------------------------------------------------------------------------------
335 ! Interpolate the R-matrix values between the source and target heights
336 !-------------------------------------------------------------------------------
337 
338 SUBROUTINE ufo_roobserror_interpolate_heights(target_nheights, &
339  target_heights, &
340  source_nheights, &
341  source_heights, &
342  source_values, &
343  output_values)
344 
345 IMPLICIT NONE
346 
347 ! Subroutine arguments:
348 INTEGER, INTENT(IN) :: target_nheights
349 REAL, INTENT(IN) :: target_heights(1:target_nheights)
350 INTEGER, INTENT(IN) :: source_nheights
351 REAL, INTENT(IN) :: source_heights(1:source_nheights)
352 REAL, INTENT(IN) :: source_values(1:source_nheights)
353 REAL, INTENT(OUT) :: output_values(1:target_nheights)
354 
355 ! Local declarations:
356 CHARACTER(len=*), PARAMETER :: RoutineName = "ufo_roobserror_interpolate_heights"
357 INTEGER :: i
358 INTEGER :: j
359 LOGICAL :: MatchFound
360 REAL :: weight ! Weight to give to the upper level value
361 
362 DO i = 1, target_nheights
363  IF (all(target_heights(i) < source_heights)) THEN
364  ! The target height is less than all in the source, so take the bottom value
365  output_values(i) = source_values(1)
366  ELSE
367  ! Search for a match, where the target height is between two adjacent values
368  matchfound = .false.
369  DO j = 1, source_nheights - 1
370  IF (target_heights(i) >= source_heights(j) .AND. &
371  target_heights(i) < source_heights(j + 1)) THEN
372  matchfound = .true.
373  EXIT
374  END IF
375  END DO
376  IF (matchfound) THEN
377  ! If we have found a match, then interpolate between the heights in
378  ! question
379  weight = (target_heights(i) - source_heights(j)) / &
380  (source_heights(j + 1) - source_heights(j))
381  output_values(i) = weight * source_values(j + 1) + &
382  (1 - weight) * source_values(j)
383  ELSE
384  ! If no match has been found, then the target is greater than all the
385  ! source heights
386  output_values(i) = source_values(source_nheights)
387  END IF
388  END IF
389 END DO
390 
392 
393 
394 !-------------------------------------------------------------------------------
395 ! (C) Crown copyright Met Office. All rights reserved.
396 ! Refer to COPYRIGHT.txt of this distribution for details.
397 !-------------------------------------------------------------------------------
398 ! Read data needed to set up the Rmatrix.
399 !-------------------------------------------------------------------------------
400 
401 SUBROUTINE ufo_roobserror_copy_rmatrix(in_mat, & ! The RMatrix to copy
402  out_mat) ! The target for the copy
403 
404 IMPLICIT NONE
405 
406 ! Subroutine arguments:
407 TYPE (Rmatrix_type), INTENT(IN) :: in_mat ! The R matrix to copy
408 TYPE (Rmatrix_type), INTENT(OUT) :: out_mat ! The target for the copy
409 
410 ! Local declarations:
411 CHARACTER(len=*), PARAMETER :: RoutineName = "ufo_roobserror_copy_rmatrix"
412 
413 out_mat % num_heights = in_mat % num_heights
414 ALLOCATE (out_mat % height(out_mat % num_heights))
415 ALLOCATE (out_mat % frac_err(out_mat % num_heights))
416 out_mat % height(:) = in_mat % height(:)
417 out_mat % frac_err(:) = in_mat % frac_err(:)
418 
419 out_mat % av_temp = in_mat % av_temp
420 out_mat % max_height = in_mat % max_height
421 out_mat % latitude = in_mat % latitude
422 out_mat % satid = in_mat % satid
423 out_mat % origctr = in_mat % origctr
424 out_mat % clen = in_mat % clen
425 out_mat % min_error = in_mat % min_error
426 
427 END SUBROUTINE ufo_roobserror_copy_rmatrix
428 
429 !-------------------------------------------------------------------------------
430 ! Choose the R matrix to apply for this observation. The matrix will be
431 ! be selected to have the same satellite identifier and originating centre as
432 ! requested. The matrix with the nearest latitude to that specified will be
433 ! selected (without interpolation).
434 !-------------------------------------------------------------------------------
435 
437  origc, &
438  latitude, &
439  R_num_sats, &
440  Rmatrix_list, &
441  out_matrix)
442 
443 IMPLICIT NONE
444 
445 ! Subroutine arguments:
446 INTEGER, INTENT(IN) :: satid ! The satellite identifier | These three entries will
447 INTEGER, INTENT(IN) :: origc ! The originating centre | be used to find the
448 REAL(kind_real), INTENT(IN) :: latitude ! The latitude | R matrix from the list
449 INTEGER, INTENT(IN) :: R_num_sats ! The number of matrices in the list
450 TYPE (Rmatrix_type), INTENT(IN) :: RMatrix_list(1:R_num_sats) ! The list of R matrices to select from
451 TYPE (Rmatrix_type), INTENT(OUT) :: out_matrix ! Output R matrix
452 
453 ! Local declarations:
454 CHARACTER(len=*), PARAMETER :: RoutineName = "ufo_roobserror_findnearest_rmatrix"
455 INTEGER :: i ! Loop variable
456 INTEGER :: closest_match ! The R matrix whose latitude is closest to the specification
457 CHARACTER(len=200) :: ErrorMessage ! Error message to be output
458 
459 closest_match = -1
460 DO i = 1, r_num_sats
461  IF (satid == rmatrix_list(i) % satid .AND. &
462  origc == rmatrix_list(i) % origctr) THEN
463  IF (rmatrix_list(i) % latitude /= missing_value(rmatrix_list(i) % latitude)) THEN
464  IF (closest_match < 1) THEN
465  closest_match = i
466  ELSE IF (abs(latitude - rmatrix_list(i) % latitude) < &
467  abs(latitude - rmatrix_list(closest_match) % latitude)) THEN
468  closest_match = i
469  END IF
470  END IF
471  END IF
472 END DO
473 
474 IF (closest_match > 0) THEN
475  CALL ufo_roobserror_copy_rmatrix(rmatrix_list(closest_match), &
476  out_matrix)
477 ELSE
478  WRITE (errormessage, '(A,I0,1X,I0)') "Did not match any matrices ", satid, origc
479  CALL fckit_log % warning(errormessage)
480  CALL ufo_roobserror_copy_rmatrix(rmatrix_list(1), &
481  out_matrix)
482 END IF
483 
485 
486 end module ufo_roobserror_utils_mod
Fortran module for utility routines used in calculating the observation uncertainties,...
subroutine ufo_roobserror_read_rmatrix(fileunit, Rmatrix, return_code)
subroutine ufo_roobserror_copy_rmatrix(in_mat, out_mat)
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)
subroutine ufo_roobserror_interpolate_heights(target_nheights, target_heights, source_nheights, source_heights, source_values, output_values)
Fortran module with various useful routines.
integer function, public ufo_utils_iogetfreeunit()
Find a free file unit.