UFO
ufo_gnssro_bendmetoffice_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 gnssro bending angle Met Office forward operator
9 
11 
12 use iso_c_binding
13 use kinds
14 use ufo_vars_mod
19 use obsspace_mod
20 use missing_values_mod
23 use fckit_log_module, only : fckit_log
24 
25 implicit none
27 private
28 
29  !> Fortran derived type for gnssro trajectory
31  logical :: vert_interp_ops
32  logical :: pseudo_ops
33  real(kind_real) :: min_temp_grad
34  contains
35  procedure :: setup => ufo_gnssro_bendmetoffice_setup
36  procedure :: simobs => ufo_gnssro_bendmetoffice_simobs
38 
39 contains
40 
41 ! ------------------------------------------------------------------------------
42 ! Get the optional settings for the forward model, and save them in the object
43 ! so that they can be used in the code.
44 ! ------------------------------------------------------------------------------
45 subroutine ufo_gnssro_bendmetoffice_setup(self, vert_interp_ops, pseudo_ops, min_temp_grad)
46 
47 implicit none
48 
49 class(ufo_gnssro_bendmetoffice), intent(inout) :: self
50 logical(c_bool), intent(in) :: vert_interp_ops
51 logical(c_bool), intent(in) :: pseudo_ops
52 real(c_float), intent(in) :: min_temp_grad
53 
54 self % vert_interp_ops = vert_interp_ops
55 self % pseudo_ops = pseudo_ops
56 self % min_temp_grad = min_temp_grad
57 
58 end subroutine ufo_gnssro_bendmetoffice_setup
59 
60 ! ------------------------------------------------------------------------------
61 ! 1-dimensional GNSS-RO forward operator for the Met Office system
62 ! ------------------------------------------------------------------------------
63 subroutine ufo_gnssro_bendmetoffice_simobs(self, geovals, obss, hofx, obs_diags)
64 
65  implicit none
66 
67  ! Arguments to this routine
68  class(ufo_gnssro_bendmetoffice), intent(in) :: self ! The object in which this operator is contained
69  type(ufo_geovals), intent(in) :: geovals ! The model values, interpolated to the observation locations
70  real(kind_real), intent(inout) :: hofx(:) ! The model forecast of the observations
71  type(c_ptr), value, intent(in) :: obss ! The observations, and meta-data for those observations
72  type(ufo_geovals), intent(inout) :: obs_diags ! Observations diagnostics
73 
74  character(len=*), parameter :: myname_ = "ufo_gnssro_bendmetoffice_simobs"
75  integer, parameter :: max_string = 800
76 
77  character(max_string) :: err_msg ! Error message for output
78  character(max_string) :: message ! General message for output
79  integer :: nobs ! Number of observations
80  integer :: ilev ! Loop variable, level number
81  integer :: iobs ! Loop variable, observation number
82  type(ufo_geoval), pointer :: q ! Model background values of specific humidity
83  type(ufo_geoval), pointer :: prs ! Model background values of air pressure
84  type(ufo_geoval), pointer :: theta_heights ! Model heights of levels containing specific humidity
85  type(ufo_geoval), pointer :: rho_heights ! Model heights of levels containing air pressure
86  real(kind_real), allocatable :: obsLat(:) ! Latitude of the observation
87  real(kind_real), allocatable :: obsLon(:) ! Longitude of the observation
88  real(kind_real), allocatable :: impact_param(:) ! Impact parameter of the observation
89  real(kind_real), allocatable :: radius_curv(:) ! Earth's radius of curvature at the observation tangent point
90  real(kind_real), allocatable :: undulation(:) ! Undulation - height of the geoid above the ellipsoid
91  logical :: flip_data ! Whether to reverse the order of the model data
92  logical :: BAErr ! Was there an error in the calculation?
93  integer :: iVar ! Loop variable, obs diagnostics variable number
94  real(kind_real), allocatable :: refractivity(:) ! Refractivity on various model levels
95  real(kind_real), allocatable :: model_heights(:) ! Geopotential heights that refractivity is calculated on
96 
97  write(err_msg,*) "TRACE: ufo_gnssro_bendmetoffice_simobs: begin"
98  call fckit_log%info(err_msg)
99 
100  ! If output to refractivity (and heights of the refractivity levels) is needed,
101  ! then use nval as a way to check whether the array has been initialised (since
102  ! it is called in a loop).
103  DO ivar = 1, obs_diags % nvar
104  IF (obs_diags % variables(ivar) == "refractivity" .OR. &
105  obs_diags % variables(ivar) == "model_heights") THEN
106  write(err_msg,*) "TRACE: ufo_gnssro_bendmetoffice_simobs: initialising obs_diags for " // &
107  obs_diags % variables(ivar)
108  call fckit_log%info(err_msg)
109  obs_diags % geovals(ivar) % nval = 0
110  END IF
111  END DO
112 
113 ! check if nlocs is consistent in geovals & hofx
114  if (geovals%nlocs /= size(hofx)) then
115  write(err_msg,*) myname_, ' error: nlocs inconsistent!'
116  call abor1_ftn(err_msg)
117  endif
118 
119  write(message, *) myname_, ' Running Met Office GNSS-RO forward operator with'
120  call fckit_log%info(message)
121  write(message, *) 'vert_interp_ops =', self % vert_interp_ops, &
122  'pseudo_ops =', self % pseudo_ops
123  call fckit_log%info(message)
124 
125 ! get variables from geovals
126  call ufo_geovals_get_var(geovals, var_q, q) ! specific humidity
127  call ufo_geovals_get_var(geovals, var_prsi, prs) ! pressure
128  call ufo_geovals_get_var(geovals, var_z, theta_heights) ! Geopotential height of the normal model levels
129  call ufo_geovals_get_var(geovals, var_zi, rho_heights) ! Geopotential height of the pressure levels
130 
131  write(message, '(A,10I6)') 'Q: ', q%nval, q%nlocs, shape(q%vals)
132  call fckit_log%info(message)
133  write(message, '(A,10I6)') 'Pressure: ', prs%nval, prs%nlocs, shape(prs%vals)
134  call fckit_log%info(message)
135 
136  nobs = obsspace_get_nlocs(obss)
137 
138  flip_data = .false.
139  if (prs%vals(1,1) .lt. prs%vals(prs%nval,1) ) then
140  write(err_msg,'(a)') ' ufo_gnssro_bendmetoffice_simobs:'//new_line('a')// &
141  ' Model vertical height profile is in descending order,'//new_line('a')// &
142  ' The data will be flipped for processing'
143  call fckit_log%info(err_msg)
144  flip_data = .true.
145  end if
146 
147 ! set obs space struture
148  allocate(obslon(nobs))
149  allocate(obslat(nobs))
150  allocate(impact_param(nobs))
151  allocate(radius_curv(nobs))
152  allocate(undulation(nobs))
153 
154  call obsspace_get_db(obss, "MetaData", "longitude", obslon)
155  call obsspace_get_db(obss, "MetaData", "latitude", obslat)
156  call obsspace_get_db(obss, "MetaData", "impact_parameter", impact_param)
157  call obsspace_get_db(obss, "MetaData", "earth_radius_of_curvature", radius_curv)
158  call obsspace_get_db(obss, "MetaData", "geoid_height_above_reference_ellipsoid", undulation)
159 
160  obs_loop: do iobs = 1, nobs
161 
162  if (flip_data) then
163  call ops_gpsro_forwardmodel(prs % nval, &
164  q % nval, &
165  rho_heights % vals(rho_heights%nval:1:-1, iobs), &
166  theta_heights % vals(theta_heights%nval:1:-1, iobs), &
167  prs % vals(prs%nval:1:-1, iobs), &
168  q % vals(q%nval:1:-1, iobs), &
169  self % pseudo_ops, &
170  self % vert_interp_ops, &
171  self % min_temp_grad, &
172  1, &
173  impact_param(iobs:iobs), &
174  radius_curv(iobs), &
175  obslat(iobs), &
176  undulation(iobs), &
177  hofx(iobs:iobs), &
178  baerr, &
179  refractivity, &
180  model_heights)
181  else
182  call ops_gpsro_forwardmodel(prs % nval, &
183  q % nval, &
184  rho_heights % vals(:,iobs), &
185  theta_heights % vals(:,iobs), &
186  prs % vals(:,iobs), &
187  q % vals(:,iobs), &
188  self % pseudo_ops, &
189  self % vert_interp_ops, &
190  self % min_temp_grad, &
191  1, &
192  impact_param(iobs:iobs), &
193  radius_curv(iobs), &
194  obslat(iobs), &
195  undulation(iobs), &
196  hofx(iobs:iobs), &
197  baerr, &
198  refractivity, &
199  model_heights)
200  end if
201 
202  if (baerr) then
203  write(err_msg,*) "Error with observation processing ", iobs
204  call fckit_log % info(err_msg)
205  end if
206 
207  ! If output to refractivity is needed, then initialise things
208  DO ivar = 1, obs_diags % nvar
209  IF (obs_diags % variables(ivar) == "refractivity") THEN
210  IF (iobs == 1) THEN
211  obs_diags % geovals(ivar) % nval = SIZE(refractivity)
212  ALLOCATE(obs_diags % geovals(ivar) % vals(SIZE(refractivity), obs_diags % nlocs))
213  END IF
214 
215  IF (baerr) THEN
216  obs_diags % geovals(ivar) % vals(:,iobs) = missing_value(obs_diags % geovals(ivar) % vals(1,1))
217  ELSE
218  obs_diags % geovals(ivar) % vals(:,iobs) = refractivity(:)
219  END IF
220  END IF
221 
222  IF (obs_diags % variables(ivar) == "model_heights") THEN
223  IF (iobs == 1) THEN
224  obs_diags % geovals(ivar) % nval = SIZE(model_heights)
225  ALLOCATE(obs_diags % geovals(ivar) % vals(SIZE(model_heights), obs_diags % nlocs))
226  END IF
227 
228  IF (baerr) THEN
229  obs_diags % geovals(ivar) % vals(:,iobs) = missing_value(obs_diags % geovals(ivar) % vals(1,1))
230  ELSE
231  obs_diags % geovals(ivar) % vals(:,iobs) = model_heights(:)
232  END IF
233  END IF
234  END DO
235  end do obs_loop
236 
237  deallocate(obslat)
238  deallocate(obslon)
239  deallocate(impact_param)
240  deallocate(radius_curv)
241  deallocate(undulation)
242 
243  write(err_msg,*) "TRACE: ufo_gnssro_bendmetoffice_simobs: completed"
244  call fckit_log%info(err_msg)
245 
246 end subroutine ufo_gnssro_bendmetoffice_simobs
247 ! ------------------------------------------------------------------------------
248 
249 
250 SUBROUTINE ops_gpsro_forwardmodel(nlevp, &
251  nlevq, &
252  za, &
253  zb, &
254  pressure, &
255  humidity, &
256  GPSRO_pseudo_ops, &
257  GPSRO_vert_interp_ops, &
258  GPSRO_min_temp_grad, &
259  nobs, &
260  zobs, &
261  RO_Rad_Curv, &
262  Latitude, &
263  RO_geoid_und, &
264  ycalc, &
265  BAErr, &
266  refractivity, &
267  model_heights)
268 
269 INTEGER, INTENT(IN) :: nlevp ! no. of p levels in state vec.
270 INTEGER, INTENT(IN) :: nlevq ! no. of theta levels
271 REAL(kind_real), INTENT(IN) :: za(1:nlevp) ! heights of rho levs
272 REAL(kind_real), INTENT(IN) :: zb(1:nlevq) ! heights of theta levs
273 REAL(kind_real), INTENT(IN) :: pressure(1:nlevp) ! Model background pressure
274 REAL(kind_real), INTENT(IN) :: humidity(1:nlevq) ! Model background specific humidity
275 LOGICAL, INTENT(IN) :: GPSRO_pseudo_ops ! Option: Use pseudo-levels in vertical interpolation?
276 LOGICAL, INTENT(IN) :: GPSRO_vert_interp_ops ! Option: Use ln(p) for vertical interpolation? (rather than exner)
277 REAL(kind_real), INTENT(IN) :: GPSRO_min_temp_grad ! The minimum temperature gradient which is used
278 INTEGER, INTENT(IN) :: nobs ! Number of observations in the profile
279 REAL(kind_real), INTENT(IN) :: zobs(1:nobs) ! Impact parameter for the obs
280 REAL(kind_real), INTENT(IN) :: RO_Rad_Curv ! Earth's radius of curvature for these observations
281 REAL(kind_real), INTENT(IN) :: Latitude ! Latitude of this profile
282 REAL(kind_real), INTENT(IN) :: RO_geoid_und ! Undulation - difference between the geoid and the ellipsoid
283 REAL(kind_real), INTENT(INOUT) :: ycalc(1:nobs) ! Model forecast of the observations
284 LOGICAL, INTENT(OUT) :: BAErr ! Was an error encountered during the calculation?
285 REAL(kind_real), INTENT(INOUT), ALLOCATABLE :: refractivity(:) ! Refractivity as calculated
286 REAL(kind_real), INTENT(INOUT), ALLOCATABLE :: model_heights(:) ! Height of the levels for refractivity
287 !
288 ! Things that may need to be output, as they are used by the TL/AD calculation
289 !
290 INTEGER :: nRefLevels ! Number of levels in refractivity calculation
291 REAL(kind_real), ALLOCATABLE :: nr(:) ! Model calculation of impact parameters
292 !
293 ! Local parameters
294 !
295 integer, parameter :: max_string = 800 ! Length of strings
296 character(len=*), parameter :: myname_ = "Ops_GPSRO_ForwardModel"
297 !
298 ! Local variables
299 !
300 INTEGER :: num_pseudo ! Number of levels, including pseudo levels
301 REAL(kind_real) :: x(1:nlevp+nlevq) ! state vector
302 character(max_string) :: err_msg ! Error message to be output
303 
304 ! The model data must be on a staggered grid, with nlevp = nlevq+1
305 IF (nlevp /= nlevq + 1) THEN
306  write(err_msg,*) myname_ // ':' // ' Data must be on a staggered grid nlevp, nlevq = ', nlevp, nlevq
307  call fckit_log % warning(err_msg)
308  write(err_msg,*) myname_ // ':' // ' error: number of levels inconsistent!'
309  call abor1_ftn(err_msg)
310 END IF
311 
312 baerr = .false.
313 
314 CALL ufo_calculate_refractivity (nlevp, &
315  nlevq, &
316  za, &
317  zb, &
318  pressure, &
319  humidity, &
320  gpsro_pseudo_ops, &
321  gpsro_vert_interp_ops, &
322  gpsro_min_temp_grad, &
323  baerr, &
324  nreflevels, &
325  refractivity, &
326  model_heights)
327 
328 ALLOCATE(nr(1:nreflevels))
329 
330 ! no point proceeding further if ...
331 IF (.NOT. baerr) THEN
332  ! 2. Calculate the refractive index * radius on theta model levels (or model impact parameter)
333  CALL ops_gpsrocalc_nr (nreflevels, & ! number of levels for refractivity calculation
334  model_heights, & ! geopotential heights of refractivity levels
335  refractivity, & ! refractivity of model
336  ro_rad_curv, & ! radius of curvature of earth at observation
337  latitude, & ! latitude at observation
338  ro_geoid_und, & ! geoid undulation above WGS-84
339  nr) ! Calculated model impact parameters
340 
341  ! 3. Calculate model bending angle on observation impact parameters
342  CALL ops_gpsrocalc_alpha (nobs, & ! size of ob. vector
343  nreflevels, & ! no. of refractivity levels
344  zobs, & ! obs impact parameters
345  refractivity, & ! refractivity values
346  nr, & ! index * radius product
347  ycalc) ! forward modelled bending angle
348 END IF
349 
350 END SUBROUTINE ops_gpsro_forwardmodel
351 
Fortran module to prepare for Lagrange polynomial interpolation. based on GSI: lagmod....
Definition: lag_interp.F90:4
subroutine, public lag_interp_const(q, x, n)
Definition: lag_interp.F90:24
subroutine, public lag_interp_smthweights(x, xt, aq, bq, w, dw, n)
Definition: lag_interp.F90:174
type(registry_t), public ufo_geovals_registry
Linked list interface - defines registry_t type.
subroutine, public ufo_geovals_get_var(self, varname, geoval)
Fortran module for gnssro bending angle Met Office forward operator.
subroutine ops_gpsro_forwardmodel(nlevp, nlevq, za, zb, pressure, humidity, GPSRO_pseudo_ops, GPSRO_vert_interp_ops, GPSRO_min_temp_grad, nobs, zobs, RO_Rad_Curv, Latitude, RO_geoid_und, ycalc, BAErr, refractivity, model_heights)
subroutine ufo_gnssro_bendmetoffice_simobs(self, geovals, obss, hofx, obs_diags)
subroutine ufo_gnssro_bendmetoffice_setup(self, vert_interp_ops, pseudo_ops, min_temp_grad)
subroutine, public ops_gpsrocalc_nr(nb, zb, refrac, Rad, lat, und, nr)
subroutine, public ops_gpsrocalc_alpha(nobs, nlev, a, refrac, nr, alpha)
Module for containing a general refractivity forward operator and its K-matrix.
subroutine, public ufo_calculate_refractivity(nlevP, nlevq, za, zb, P, q, vert_interp_ops, pseudo_ops, min_temp_grad, refracerr, nRefLevels, refractivity, model_heights, temperature, interp_pressure)
Calculation of the refractivity from the pressure and specific humidity.
character(len=maxvarlen), parameter, public var_prsi
character(len=maxvarlen), parameter, public var_q
character(len=maxvarlen), parameter, public var_zi
character(len=maxvarlen), parameter, public var_z
Fortran module to perform linear interpolation.
Definition: vert_interp.F90:8
type to hold interpolated field for one variable, one observation
type to hold interpolated fields required by the obs operators