FV3 Bundle
ufo_gnssro_bndropp1d_mod.F90
Go to the documentation of this file.
1 ! (C) Copyright 2017-2018 UCAR
2 !
3 ! This software is licensed under the terms of the Apache Licence Version 2.0
4 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5 
6 !> Fortran module to handle gnssro bending angle observations following
7 !> the ROPP (2018 Aug) implementation
8 
10  use iso_c_binding
11  use ufo_vars_mod
12  use ufo_geovals_mod
14  use ufo_basis_mod, only: ufo_basis
15  use vert_interp_mod
17  use obsspace_mod
18 
19  use kinds
20  implicit none
21  public :: ufo_gnssro_bndropp1d
22  private
23 
24  !> Fortran derived type for gnssro trajectory
25  type, extends(ufo_basis) :: ufo_gnssro_bndropp1d
26  contains
27  procedure :: simobs => ufo_gnssro_bndropp1d_simobs
28  end type ufo_gnssro_bndropp1d
29 
30  contains
31 ! ------------------------------------------------------------------------------
32  subroutine ufo_gnssro_bndropp1d_simobs(self, geovals, hofx, obss)
33  use ropp_fm_types, only: state1dfm
34  use ropp_fm_types, only: obs1dbangle
35  use datetimetypes, only: dp
36  implicit none
37  class(ufo_gnssro_BndROPP1D), intent(in):: self
38  type(ufo_geovals), intent(in) :: geovals
39  real(kind_real), intent(inout) :: hofx(:)
40  type(c_ptr), value, intent(in) :: obss
41 
42  type(State1dFM) :: x
43  type(Obs1dBangle) :: y
44 
45  character(len=*), parameter :: myname_="ufo_gnssro_bndropp1d_simobs"
46  real(kind=dp) :: ob_time
47  integer, parameter :: max_string = 800
48 
49  character(max_string) :: err_msg
50  character(len=250) :: record
51  integer :: iobs
52  integer :: nlev, nobs
53  integer :: ierr
54  integer :: nvprof
55  integer, allocatable, dimension(:) :: ichk
56  type(ufo_geoval), pointer :: t, q, prs, z, z_sfc
57  real(kind_real), allocatable :: obsLat(:), obsLon(:), obsImpP(:), obsLocR(:), obsGeoid(:)
58  real(kind_real), allocatable :: obsYYYY(:), obsMM(:), obsDD(:), obsHH(:), obsMN(:), obsSS(:)
59  integer :: obss_nobs
60 
61  write(*,*) "TRACE: ufo_gnssro_bndropp1d_simobs: begin"
62  ! check if nobs is consistent in geovals & hofx
63  if (geovals%nobs /= size(hofx)) then
64  write(err_msg,*) myname_, ' error: nobs inconsistent!'
65  call abor1_ftn(err_msg)
66  endif
67  ! check if prs (pressure at model levels) variable is in geovals and get it
68  call ufo_geovals_get_var(geovals, var_prs, prs,status=ierr)
69  if (ierr/=0) then
70  write(err_msg,*) myname_, trim(var_prs), ' doesnt exist'
71  call abor1_ftn(err_msg)
72  endif
73  ! check if specific humidity variable is in geovals and get it
74  call ufo_geovals_get_var(geovals, var_q, q,status=ierr)
75  if (ierr/=0) then
76  write(err_msg,*) myname_, trim(var_q), ' doesnt exist'
77  call abor1_ftn(err_msg)
78  endif
79  ! check if geopotential height variable is in geovals and get it
80  call ufo_geovals_get_var(geovals, var_z, z,status=ierr)
81  if (ierr/=0) then
82  write(err_msg,*) myname_, trim(var_z), ' doesnt exist'
83  call abor1_ftn(err_msg)
84  endif
85  ! check if surface geopotential height variable is in geovals and get it
86  call ufo_geovals_get_var(geovals, var_sfc_z, z_sfc,status=ierr)
87  if (ierr/=0) then
88  write(err_msg,*) myname_, trim(var_sfc_z), ' doesnt exist'
89  call abor1_ftn(err_msg)
90  endif
91  ! check if t variable is in geovals and get it
92  call ufo_geovals_get_var(geovals, var_t, t,status=ierr)
93  if (ierr/=0) then
94  write(err_msg,*) myname_, trim(var_t), ' doesnt exist'
95  call abor1_ftn(err_msg)
96  endif
97 
98  nlev = q%nval ! number of model levels
99  nobs = geovals%nobs ! number of observations
100 
101  ! read observation vectors
102  obss_nobs = obsspace_get_nobs(obss)
103  !allocate(obsYYYY(obss_nobs))
104  !allocate(obsMM(obss_nobs))
105  !allocate(obsDD(obss_nobs))
106  !allocate(obsHH(obss_nobs))
107  !allocate(obsMN(obss_nobs))
108  !allocate(obsSS(obss_nobs))
109  allocate(obslon(obss_nobs))
110  allocate(obslat(obss_nobs))
111  allocate(obsimpp(obss_nobs))
112  allocate(obslocr(obss_nobs))
113  allocate(obsgeoid(obss_nobs))
114 
115  !call obsspace_get_db(obss, "Metadata", "year", obsYYYY) !Note: variable name not consistent with BUFR table
116  !call obsspace_get_db(obss, "Metadata", "month", obsMM) !Note: variable name not consistent with BUFR table
117  !call obsspace_get_db(obss, "Metadata", "day", obsDD) !Note: variable name not consistent with BUFR table
118  !call obsspace_get_db(obss, "Metadata", "hour", obsHH) !Note: variable name not consistent with BUFR table
119  !call obsspace_get_db(obss, "Metadata", "minute", obsMN) !Note: variable name not consistent with BUFR table
120  !call obsspace_get_db(obss, "Metadata", "second", obsSS) !Note: variable name not consistent with BUFR table
121  call obsspace_get_db(obss, "Metadata", "Longitude", obslon) !Note: variable name not consistent with BUFR table
122  call obsspace_get_db(obss, "Metadata", "Latitude", obslat) !Note: variable name not consistent with BUFR table
123  call obsspace_get_db(obss, "Metadata", "IMPP", obsimpp) !observed impact parameter
124  call obsspace_get_db(obss, "Metadata", "ELRC", obslocr) !local radius of earth. Note: need add to test data
125  call obsspace_get_db(obss, "Metadata", "GEODU", obsgeoid) !Geoid. Note: need add to test data
126 
127  nvprof = 1 ! number of vertical profiles (occultation points)
128  allocate(ichk(nvprof))
129  ichk(:) = 0 ! this will hold QC values for observation from QC flags
130 
131  write(record,*) "DEBUG: ufo_gnssro_bndropp1d_simobs: begin observation loop ", nobs
132  obs_loop: do iobs = 1, nobs
133 
134 ! call init_ob_time(int(obsYYYY(iobs)), &
135 ! int(obsMM(iobs)), &
136 ! int(obsDD(iobs)), &
137 ! int(obsHH(iobs)), &
138 ! int(obsMN(iobs)), &
139 ! int(obsSS(iobs)), &
140 ! ob_time)
141  ob_time = 0.0
142  ! alternatively you can use the analysis time and the observation value of time
143  call init_ropp_1d_statevec(ob_time, &
144  obslon(iobs), &
145  obslat(iobs), &
146  t%vals(:,iobs), &
147  q%vals(:,iobs), &
148  prs%vals(:,iobs), &
149  z%vals(:,iobs), &
150  nlev, &
151  z_sfc%vals(1,iobs), &
152  x)
153  call init_ropp_1d_obvec(nvprof, &
154  obsimpp(iobs), &
155  ichk, ob_time, &
156  obslat(iobs), &
157  obslon(iobs), &
158  obslocr(iobs), &
159  obsgeoid(iobs), &
160  y)
161 
162  call ropp_fm_bangle_1d(x,y)
163 
164  hofx(iobs) = y%bangle(nvprof) ! nvprof is just one point
165 
166  end do obs_loop
167 
168  deallocate(obslat) !Note: to be removed
169  deallocate(obslon)
170  deallocate(obsimpp)
171  deallocate(obslocr)
172  deallocate(obsgeoid)
173  !deallocate(obsYYYY)
174  !deallocate(obsMM)
175  !deallocate(obsDD)
176  !deallocate(obsHH)
177  !deallocate(obsMN)
178  !deallocate(obsSS)
179  write(*,*) "TRACE: ufo_gnssro_bndropp1d_simobs: completed"
180 
181  end subroutine ufo_gnssro_bndropp1d_simobs
182 ! ------------------------------------------------------------------------------
183 
184  subroutine init_ropp_1d_statevec(step_time,rlon,rlat, &
185  temp,shum,pres,phi,lm,phi_sfc,x)
187 ! Description:
188 ! subroutine to fill a ROPP state vector structure with
189 ! model background fields: Temperature, pressure, specific
190 ! humidity at full-levels, and surface geopotential height.
191 !
192 ! Inputs:
193 ! temp background temperature
194 ! shum background specific humidity
195 ! pres background pressure
196 ! phi geopotential height
197 !
198 ! phi_sfc terrain geopotential of background field
199 ! lm number of vertical levels in the background
200 !
201 ! Outputs:
202 ! x Forward model state vector
203 !
204 ! ###############################################################
205 
206 ! For ROPP data type and library subroutine
207 
208  use typesizes, only: wp => eightbytereal
209  use datetimetypes, only: dp
210  use kinds, only: kind_real
211  use ropp_fm_types, only: state1dfm
212  use geodesy, only: gravity, r_eff, geometric2geopotential
213  use arrays, only: callocate
214 
215  implicit none
216 
217 ! Function arguments
218 
219 ! Output state vector
220  type(State1dFM), intent(out) :: x
221  real(kind=dp), intent(in) :: step_time
222  real(kind=kind_real), intent(in) :: rlat, rlon
223  real(kind=kind_real), intent(in) :: phi_sfc
224  integer, intent(in) :: lm
225  real(kind=kind_real), dimension(lm), intent(in) :: temp,shum,pres,phi
226 
227 ! Local variables
228  character(len=250) :: record
229  real(kind=kind_real) :: rlon_local
230  integer n,i,j,k
231 
232 !-------------------------------------------------------------------------
233  x%state_ok = .true.
234  x%new_bangle_op = .true. ! activate ROPP v8 new interpolation scheme
235 
236 ! ROPP Longitude value is -180.0 to 180.0
237 
238  x%lat = real(rlat,kind=wp)
239  rlon_local = rlon
240  if (rlon_local .gt. 180) rlon_local = rlon_local - 360.
241  x%lon = real(rlon_local,kind=wp)
242  x%time = real(step_time,kind=wp)
243 
244 
245 ! Number of levels in background profile. What about (lm+1) field ?
246 
247  x%n_lev = lm
248 
249 !--------------------------------------------------------
250 ! allocate arrays for temperature, specific humidity, pressure
251 ! and geopotential height data
252 !---------------------------------------------------------
253  allocate(x%temp(x%n_lev))
254  allocate(x%shum(x%n_lev))
255  allocate(x%pres(x%n_lev))
256  allocate(x%geop(x%n_lev))
257 !----------------------------------------------------
258 ! ROPP FM requires vertical height profile to be of the ascending order.
259 ! (see ropp_io_ascend ( ROdata )). So we need to flip the data.
260 !----------------------------------------------------
261  n = lm
262  write(record,'(4a9,a11)') 'lvl','temp','shum','pres','geop'
263  do k = 1, lm
264  x%temp(n) = real(temp(k),kind=wp)
265  x%shum(n) = real(shum(k),kind=wp)
266  x%pres(n) = real(pres(k)*100.,kind=wp)
267  x%geop(n) = real(phi(k),kind=wp)
268  write(record,'(5x,i4,f9.2,f9.4,f9.1,f15.1)') &
269  n, x%temp(n), x%shum(n), x%pres(n), x%geop(n)
270  n = n - 1
271  end do
272 
273 ! sufrace geopotential height value
274 
275  x%geop_sfc = real(phi_sfc,kind=wp)
276  write(record,'("geop_sfc",f15.2)') x%geop_sfc
277 
278 !------------------------------------------------
279 ! covariance matrix, is this used by ROPP FM?
280 !------------------------------------------------
281  x%cov_ok = .true.
282 
283 ! Allocate memory
284 ! For ECMWF example, Covariance matrix for temperature sigma and
285 ! specific humidity sigma, and surface pressure. There the
286 ! size of covariance matrix is 2 * nlevel + 1.
287 
288  n = (2*x%n_lev)+1 ! Number of elements in the state vector
289 
290  if (associated(x%cov%d)) deallocate(x%cov%d)
291  call callocate(x%cov%d, n*(n+1)/2) ! From ROPP utility library
292 ! allocate(x%cov%d((n*(n+1))/2)) ! or just use standard Fortran call
293 
294  do i = 1, x%n_lev
295  x%cov%d(i + i*(i-1)/2) = 1.0_wp
296  end do
297 
298  do i = 1, x%n_lev
299  j = x%n_lev + i
300  x%cov%d(j + j*(j-1)/2) = 1.0_wp
301  enddo
302 
303  x%cov%d(n + n*(n-1)/2) = 1.0_wp
304 
305 !-------------------------------------------------------------
306 ! Rest of the covariance marix
307 !--------------------------------------------------------------
308 
309  if (associated(x%cov%e)) deallocate(x%cov%e)
310  if (associated(x%cov%f)) deallocate(x%cov%f)
311  if (associated(x%cov%s)) deallocate(x%cov%s)
312 
313  x%cov%fact_chol = .false.
314  x%cov%equi_chol = 'N'
315 
316  return
317  end subroutine init_ropp_1d_statevec
318 
319  subroutine init_ropp_1d_obvec(nvprof,obs_impact, &
320  ichk,ob_time,rlat,rlon,roc,undulat,y)
322 ! Description:
323 ! subroutine to fill a ROPP observation vector structure
324 ! observation provides the inputs of:
325 ! impact parameter, lat, lon, time, radius of curvature
326 !
327 ! forward model will provide the simulation of bending angle
328 !
329 ! Inputs:
330 ! obs_impact impact parameter
331 ! ob_time time of the observation
332 ! rlat latitude
333 ! rlon longitude
334 ! roc radius of curvature
335 ! undulat undulation correction for radius of curvature
336 !
337 ! Outputs:
338 ! y: Partially filled Forward model observation vector
339 !
340 ! ###############################################################
341 
342 ! For ROPP data type
343 
344  use typesizes, only: wp => eightbytereal
345  use kinds, only: kind_real
346  use datetimetypes, only: dp
347  use ropp_fm_types, only: obs1dbangle
348  use geodesy, only: gravity, r_eff, geopotential2geometric
349 
350  implicit none
351 
352 
353 ! Output state vector
354  type(Obs1dBangle), intent(out) :: y
355 
356  integer, intent(in) :: nvprof
357  integer, dimension(nvprof), intent(in) :: ichk
358  real(kind=kind_real), dimension(nvprof), intent(in) :: obs_impact
359  real(kind=kind_real), intent(in) :: rlat, rlon
360  real(kind=kind_real), intent(in) :: roc, undulat
361  real(kind=dp), intent(in) :: ob_time
362 
363  real(kind=wp) :: r8lat
364  real(kind=kind_real) :: rlon_local
365  character(len=250) :: record
366 
367  integer :: i
368 
369 !-------------------------------------------------------------------------
370 
371  y%time = real(ob_time,kind=wp)
372  r8lat = real(rlat,kind=wp)
373  y%lat = r8lat
374  rlon_local = rlon
375  if (rlon_local .gt. 180) rlon_local = rlon_local - 360.
376  y%lon = real(rlon_local,kind=wp)
377  y%nobs = nvprof
378  y%g_sfc = gravity(r8lat, 0.0_wp) ! 2nd argument is height(m) above the sfc
379  y%r_curve = real(roc,kind=wp) ! ROPP rad of curve (m)
380  y%undulation = real(undulat,kind=wp) ! ROPP undulation corr for rad of curve (m)
381  y%r_earth = r_eff(r8lat)
382 
383 !--------------------------------------------------------
384 ! allocate bending angle, impact parameter & weights
385 !---------------------------------------------------------
386  if (associated(y%bangle)) then
387  deallocate(y%bangle)
388  deallocate(y%impact)
389  deallocate(y%weights)
390  nullify(y%bangle)
391  nullify(y%impact)
392  nullify(y%weights)
393  end if
394 
395  allocate(y%bangle(1:nvprof)) ! value computed in fwd model
396  allocate(y%impact(1:nvprof))
397  allocate(y%weights(1:nvprof)) ! value set in fwd model
398 
399  do i=1,nvprof
400  y%impact(i) = real(obs_impact(i),kind=wp) ! ROPP expects impact parameter in meters
401  if (ichk(i) .le. 0) then
402  y%weights(i) = 1.0_wp ! following t_fascod example
403  else
404  y%weights(i) = 0.0_wp
405  end if
406  end do
407  y%bangle(:) = 0.0_wp ! following t_fascod example
408 
409  write(record,'(a9,2a11,3a15)') 'ROPPyvec:','lat', 'lon', &
410  'g_sfc', 'roc', 'r_earth_eff'
411  write(record,'(9x,2f11.2,f15.6,2f15.2)') y%lat, y%lon, &
412  y%g_sfc, y%r_curve, y%r_earth
413 
414 !------------------------------------------------
415 ! covariance matrix, is this used by ROPP FM?
416 !------------------------------------------------
417  y%obs_ok = .true.
418 
419  return
420  end subroutine init_ropp_1d_obvec
421 
422  subroutine init_ob_time(yyyy, mm, dd, hh, mn, ss, ob_time)
424  use datetimetypes, only: dp
425 
426  integer, intent(in) :: yyyy, mm, dd, hh, mn, ss
427  real(dp), intent(out) :: ob_time
428 
429  integer, dimension(8) :: dt8
430 
431 !---------------------------------------------------------------
432 ! Compute Julian seconds from YYYYMMDDHH information of anal time
433 ! (ropp_utils-6.0/datetime/timesince.f90.
434 !---------------------------------------------------------------
435 ! EXAMPLES
436 ! 1) Current time in seconds since midnight, 1-Jan-2000
437 ! USE DateTime
438 ! INTEGER :: CDT(8)
439 ! REAL(dp) :: JDF
440 ! CALL Date_and_Time_UTC ( Values=CDT )
441 ! --> CDT = 2010,4,9,15,17,14,234
442 ! CALL TimeSince ( CDT, JDF, 1, "JS2000" )
443 ! --> JDF = 324141434.23402011
444 !---------------------------------------------------------------
445 
446  dt8(1) = yyyy
447  dt8(2) = mm
448  dt8(3) = dd
449  dt8(4) = 0 ! time zone offset
450  dt8(5) = hh
451  dt8(6) = mn ! minute
452  dt8(7) = ss ! second
453  dt8(8) = 0 ! millisecond
454  ! in the call to timesince the first argument CDT has dimension(:) which
455  ! causes an issue with the addressing in gfortran
456  ! the time is not used in the operator at this time but this should
457  ! be remedied
458  !call timesince ( dt8, ob_time, 1, "JS2000" )
459  ob_time = 0.
460 
461  end subroutine init_ob_time
462 
463 end module ufo_gnssro_bndropp1d_mod
subroutine, public ufo_geovals_get_var(self, varname, geoval, status)
Fortran module to handle gnssro bending angle observations following the ROPP (2018 Aug) implementati...
subroutine, public lag_interp_smthweights(x, xt, aq, bq, w, dw, n)
Definition: lag_interp.F90:276
integer function, public obsspace_get_nobs(c_dom)
Return the number of observations.
subroutine ufo_gnssro_bndropp1d_simobs(self, geovals, hofx, obss)
Fortran module to prepare for Lagrange polynomial interpolation. based on GSI: lagmod.f90.
Definition: lag_interp.F90:4
Fortran derived type for gnssro trajectory.
subroutine init_ropp_1d_statevec(step_time, rlon, rlat, temp, shum, pres, phi, lm, phi_sfc, x)
character(len=maxvarlen), public var_z
Fortran module to perform linear interpolation.
Definition: vert_interp.F90:8
character(len=maxvarlen), public var_q
character(len=maxvarlen), public var_prs
type(registry_t), public ufo_geovals_registry
Linked list interface - defines registry_t type.
subroutine, public lag_interp_const(q, x, n)
Definition: lag_interp.F90:28
integer, parameter, public kind_real
character(len=maxvarlen), public var_t
Fortran interface to ObsSpace.
Definition: obsspace_mod.F90:9
subroutine init_ropp_1d_obvec(nvprof, obs_impact, ichk, ob_time, rlat, rlon, roc, undulat, y)
character(len=maxvarlen), public var_sfc_z
subroutine init_ob_time(yyyy, mm, dd, hh, mn, ss, ob_time)