UFO
ufo_gnssgb_ropp1d_utils_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 ground-based gnss observations following
7 !> the ROPP (2018 Aug) implementation
8 
10 
11 !use iso_c_binding
12 use fckit_log_module, only: fckit_log
13 use kinds, only: kind_real
14 
15 ! ROPP data type and library subroutines
16 use typesizes, only: wp => eightbytereal
17 use datetimetypes, only: dp
18 use ropp_fm_types, only: state1dfm, obs1drefrac
19 use geodesy, only: gravity, r_eff, geometric2geopotential, geopotential2geometric
20 use arrays, only: callocate
21 
22 implicit none
23 public :: init_ropp_1d_statevec
25 public :: init_ropp_1d_obvec
27 public :: ropp_tidy_up_1d
28 public :: ropp_tidy_up_tlad_1d
29 public :: calc_model_z
30 public :: calc_station_phi
31 public :: gnss_ztd_integral
32 private
33 
34 contains
35 
36 ! ------------------------------------------------------------------------------------
37 ! ------------------------------------------------------------------------------------
38 
39 subroutine init_ropp_1d_statevec(step_time,rlon,rlat, temp,shum,pres,phi,lm,phi_sfc,x, iflip)
40 ! Description:
41 ! subroutine to fill a ROPP state vector structure with
42 ! model background fields: Temperature, pressure, specific
43 ! humidity at full-levels, and surface geopotential height.
44 !
45 ! Inputs:
46 ! temp background temperature
47 ! shum background specific humidity
48 ! pres background pressure
49 ! phi geopotential height
50 ! phi_sfc terrain geopotential of background field
51 ! lm number of vertical levels in the background
52 !
53 ! Outputs:
54 ! x Forward model state vector
55 ! ------------------------------------------------------------------------------------
56  implicit none
57 
58  type(state1dfm), intent(out) :: x
59  real(kind=dp), intent(in) :: step_time
60  real(kind=kind_real), intent(in) :: rlat, rlon
61  real(kind=kind_real), intent(in) :: phi_sfc
62  integer, intent(in) :: lm
63  real(kind=kind_real), dimension(lm), intent(in) :: temp,shum,pres,phi
64 
65 ! Local variables
66  character(len=250) :: err_msg
67  real(kind=kind_real) :: rlon_local
68  integer :: n,i,j,k
69  integer, optional, intent(in) :: iflip
70  x%non_ideal = .false.
71  x%direct_ion = .false.
72  x%state_ok = .true.
73 
74 ! ROPP Longitude value is -180.0 to 180.0
75  x%lat = real(rlat,kind=wp)
76  rlon_local = rlon
77  if (rlon_local .gt. 180) rlon_local = rlon_local - 360.
78  x%lon = real(rlon_local,kind=wp)
79  x%time = real(step_time, kind=wp)
80 
81 
82 ! Number of levels in background profile. What about (lm+1) field ?
83  x%n_lev = lm
84 
85 !--------------------------------------------------------------
86 ! allocate arrays for temperature, specific humidity, pressure
87 ! and geopotential height data
88 !--------------------------------------------------------------
89  if (associated(x%temp)) deallocate(x%temp)
90  if (associated(x%shum)) deallocate(x%shum)
91  if (associated(x%pres)) deallocate(x%pres)
92  if (associated(x%geop)) deallocate(x%geop)
93 
94  allocate(x%temp(x%n_lev))
95  allocate(x%shum(x%n_lev))
96  allocate(x%pres(x%n_lev))
97  allocate(x%geop(x%n_lev))
98 !----------------------------------------------------
99 ! ROPP FM requires vertical height profile to be of the ascending order.
100 ! (see ropp_io_ascend ( ROdata )). So we need to flip the data.
101 !----------------------------------------------------
102  write(err_msg,'(4a9,a11)') 'lvl','temp','shum','pres','geop'
103 
104  n = lm
105  if ( present(iflip) .and. iflip .eq. 1) then
106  do k = 1, lm
107  x%temp(n) = real(temp(k),kind=wp)
108  x%shum(n) = real(shum(k),kind=wp)
109  x%pres(n) = real(pres(k),kind=wp)
110  x%geop(n) = real(phi(k),kind=wp)
111  n = n - 1
112  end do
113 else
114  do k = 1, lm
115  x%temp(k) = real(temp(k),kind=wp)
116  x%shum(k) = real(shum(k),kind=wp)
117  x%pres(k) = real(pres(k),kind=wp)
118  x%geop(k) = real(phi(k),kind=wp)
119  end do
120 end if
121 
122 ! sufrace geopotential height value
123  x%geop_sfc = real(phi_sfc,kind=wp)
124  write(err_msg,'("geop_sfc",f15.2)') x%geop_sfc
125 !------------------------------------------------
126 ! covariance matrix, is this used by ROPP FM?
127 !------------------------------------------------
128  x%cov_ok = .true.
129 
130 ! Allocate memory
131 ! For ECMWF example, Covariance matrix for temperature sigma and
132 ! specific humidity sigma, and surface pressure. There the
133 ! size of covariance matrix is 2 * nlevel + 1.
134 
135  n = (2*x%n_lev)+1 ! Number of elements in the state vector
136 
137  if (associated(x%cov%d)) deallocate(x%cov%d)
138  call callocate(x%cov%d, n*(n+1)/2) ! From ROPP utility library
139 
140  do i = 1, x%n_lev
141  x%cov%d(i + i*(i-1)/2) = 1.0_wp
142  end do
143 
144  do i = 1, x%n_lev
145  j = x%n_lev + i
146  x%cov%d(j + j*(j-1)/2) = 1.0_wp
147  enddo
148 
149  x%cov%d(n + n*(n-1)/2) = 1.0_wp
150 
151 !------------------------------
152 ! Rest of the covariance marix
153 !------------------------------
154  if (associated(x%cov%e)) deallocate(x%cov%e)
155  if (associated(x%cov%f)) deallocate(x%cov%f)
156  if (associated(x%cov%s)) deallocate(x%cov%s)
157 
158  x%cov%fact_chol = .false.
159  x%cov%equi_chol = 'N'
160 
161  return
162 end subroutine init_ropp_1d_statevec
163 
164 !------------------------------------------------------------------------------------
165 !------------------------------------------------------------------------------------
166 
167 subroutine init_ropp_1d_statevec_ad(temp_d,shum_d,pres_d,phi_d,lm,x_ad, iflip)
168 
169 ! Description:
170 ! subroutine to fill a ROPP state vector structure with
171 ! model background fields: Temperature, pressure, specific
172 ! humidity at full-levels, and surface geopotential height.
173 !
174 ! Inputs:
175 ! temp background temperature
176 ! shum background specific humidity
177 ! pres background pressure
178 ! phi geopotential height
179 ! phi_sfc terrain geopotential of background field
180 ! lm number of vertical levels in the background
181 !
182 ! Outputs:
183 ! x Forward model state vector
184 !
185 ! ###############################################################
186  type(state1dfm), intent(inout) :: x_ad
187  integer, intent(in) :: lm
188  real(kind=kind_real), dimension(lm), intent(inout) :: temp_d,shum_d,pres_d,phi_d
189 
190 ! Local variables
191  integer :: n,k
192  integer, optional, intent(in) :: iflip
193 !-------------------------------------------------------------------------
194 
195  n = lm
196 
197  if ( present(iflip) .and. iflip .eq. 1) then
198  do k = 1, lm
199 
200 !! x_tl%temp(n,:) = real(temp_d(k),kind=wp)
201  temp_d(k) = temp_d(k) + real(x_ad%temp(n),kind=kind_real)
202  x_ad%temp(n) = 0.0_wp
203 
204 !! x_tl%shum(n,:) = real(shum_d(k),kind=wp)
205  shum_d(k) = shum_d(k) + real(x_ad%shum(n),kind=kind_real)
206  x_ad%shum(n) = 0.0_wp
207 
208 !! x_tl%pres(n,:) = real(pres_d(k),kind=wp)
209  pres_d(k) = pres_d(k) + real(x_ad%pres(n),kind=kind_real)
210  x_ad%pres(n) = 0.0_wp
211 
212 !! x_tl%geop(n,:) = real(phi_d(k),kind=wp)
213  phi_d(k) = phi_d(k) + real(x_ad%geop(n),kind=kind_real)
214  x_ad%geop(n) = 0.0_wp
215 
216  n = n -1
217  end do
218  else
219  do k = 1, lm
220  temp_d(k) = temp_d(k) + real(x_ad%temp(k),kind=kind_real)
221  x_ad%temp(k) = 0.0_wp
222  shum_d(k) = shum_d(k) + real(x_ad%shum(k),kind=kind_real)
223  x_ad%shum(k) = 0.0_wp
224  pres_d(k) = pres_d(k) + real(x_ad%pres(k),kind=kind_real)
225  x_ad%pres(k) = 0.0_wp
226  phi_d(k) = phi_d(k) + real(x_ad%geop(k),kind=kind_real)
227  x_ad%geop(k) = 0.0_wp
228  end do
229 
230  end if
231  return
232 
233 end subroutine init_ropp_1d_statevec_ad
234 
235 !------------------------------------------------------------------------------------
236 !------------------------------------------------------------------------------------
237 
238 subroutine calc_station_phi(rlat, station_height, station_phi)
239 
240 ! Description:
241 ! convert station geometric height to geopotential height
242 ! provide inputs of:
243 ! lat, station_height
244 !
245 ! Inputs:
246 ! rlat latitude
247 ! station_height station geometric height
248 !
249 ! Outputs:
250 ! station_phi: station geopotential height
251 !-----------------------------------------------------------------------------------
252  implicit none
253 
254  real(kind=kind_real), intent(in) :: rlat, station_height
255  real(kind=kind_real), intent(out) :: station_phi
256 
257  ! not used in forward operator at this time -- simulate at model levels only
258  station_phi = geometric2geopotential(real(rlat,kind=wp), real(station_height,kind=wp))
259 
260 end subroutine calc_station_phi
261 
262 !------------------------------------------------------------------------------------
263 !------------------------------------------------------------------------------------
264 
265 subroutine calc_model_z(nlev, rlat, model_phi, model_z)
266 
267 ! Description:
268 ! convert model geopotential height to geometric height
269 ! provide inputs of:
270 ! number_of_model_levels, lat, model_geopotential
271 !
272 ! Inputs:
273 ! nlev number of model levels
274 ! rlat latitude
275 ! model_phi model geopotential height
276 !
277 ! Outputs:
278 ! model_z: model geometric height
279 !-----------------------------------------------------------------------------------
280  implicit none
281 
282  integer, intent(in) :: nlev
283  real(kind=kind_real), intent(in) :: rlat
284  real(kind=kind_real), dimension(nlev), intent(in) :: model_phi
285  real(kind=kind_real), dimension(nlev), intent(out) :: model_z
286 
287  integer ilev
288 
289  ! not used in forward operator at this time -- simulate at model levels only
290  do ilev = 1, nlev
291  model_z(ilev) = geopotential2geometric(real(rlat,kind=wp), real(model_phi(ilev),kind=wp))
292  end do
293 
294 end subroutine calc_model_z
295 
296 !------------------------------------------------------------------------------------
297 !------------------------------------------------------------------------------------
298 
299 subroutine init_ropp_1d_obvec(nlev, ichk, ob_time, rlat, rlon, station_phi, x, y)
300 
301 ! Description:
302 ! subroutine to fill a ROPP observation vector structure
303 ! observation provides the inputs of:
304 ! lat, lon, time
305 !
306 ! forward model will provide the simulation of refractivity
307 !
308 ! Inputs:
309 ! ob_time time of the observation
310 ! rlat latitude
311 ! rlon longitude
312 !
313 ! Outputs:
314 ! y: Partially filled Forward model observation vector
315 !-----------------------------------------------------------------------------------
316  implicit none
317 ! Input model state vector
318  type(state1dfm), intent(in) :: x
319 ! Output observation vector
320  type(obs1drefrac), intent(out) :: y
321 
322  integer, intent(in) :: nlev
323  integer, dimension(nlev), intent(in) :: ichk
324  real(kind=kind_real), intent(in) :: rlat, rlon, station_phi
325  real(kind=dp), intent(in) :: ob_time
326 ! Local variables
327  real(kind=wp) :: r8lat
328  real(kind=kind_real) :: rlon_local
329  character(len=250) :: err_msg
330  integer :: i
331 
332  y%time = real(ob_time,kind=wp)
333  r8lat = real(rlat,kind=wp)
334  y%lat = r8lat
335  rlon_local = rlon
336  if (rlon_local .gt. 180) rlon_local = rlon_local - 360.
337  y%lon = real(rlon_local,kind=wp)
338 
339 !----------------------------------------------------
340 ! allocate refractivity & weights
341 !----------------------------------------------------
342  if (associated(y%refrac)) then
343  deallocate(y%refrac)
344  deallocate(y%weights)
345  deallocate(y%geop)
346  nullify(y%refrac)
347  nullify(y%weights)
348  nullify(y%geop)
349  end if
350 
351  allocate(y%refrac(1:nlev)) ! value computed in fwd model
352  allocate(y%weights(1:nlev)) ! value set in fwd model
353  allocate(y%geop(1:nlev)) ! value set in fwd model
354 
355  do i=1,nlev
356  if (ichk(i) .le. 0) then
357  y%weights(i) = 1.0_wp ! following t_fascod example
358  else
359  y%weights(i) = 0.0_wp
360  end if
361  y%geop(i) = x%geop(i)
362  end do
363 
364  y%refrac(:) = 0.0_wp
365 
366  write(err_msg,'(a9,2a11,2a15)') 'ROPPyvec:','lat', 'lon', 'geop(1)', 'station_phi'
367  call fckit_log%debug(err_msg)
368  write(err_msg,'(9x,2f11.2,2f15.2)') y%lat, y%lon, y%geop(1), station_phi
369  call fckit_log%debug(err_msg)
370 
371 !---------------------------------------------
372 ! covariance matrix, is this used by ROPP FM?
373 !--------------------------------------------
374  y%obs_ok = .true.
375  return
376 
377 end subroutine init_ropp_1d_obvec
378 
379 !-------------------------------------------------------------------------
380 !-------------------------------------------------------------------------
381 subroutine init_ropp_1d_obvec_tlad(nlev, &
382  rlat,rlon,x,y,y_p)
383  implicit none
384 ! Input state vector
385  type(state1dfm), intent(in) :: x
386 ! Output observation vector
387  type(obs1drefrac), intent(out) :: y,y_p
388 
389  integer, intent(in) :: nlev
390  real(kind=kind_real), intent(in) :: rlat,rlon
391  real(kind=wp) :: r8lat
392  real(kind=kind_real) :: rlon_local
393 
394 !-------------------------------------------------------------------------
395  y%time = real(0.0, kind=wp)!)real(ob_time,kind=wp)
396  r8lat = real(rlat,kind=wp)
397  y%lat = real(rlat,kind=wp)
398  rlon_local = rlon
399  if (rlon_local .gt. 180) rlon_local = rlon_local - 360.
400  y%lon = real(rlon_local,kind=wp)
401 
402 ! allocate refractivity
403 !---------------------------------------------------------
404  allocate(y%refrac(1:nlev)) ! value computed in fwd model
405  allocate(y%weights(1:nlev))
406  allocate(y%geop(1:nlev))
407  allocate(y_p%refrac(1:nlev)) ! value computed in fwd model
408  allocate(y_p%weights(1:nlev))
409  allocate(y_p%geop(1:nlev))
410 
411  y%weights(:) = 1.0
412  y_p%weights(:) = 1.0
413  y%refrac(:) = 0.0
414  y_p%refrac(:) = 0.0
415  y%geop(:) = x%geop(:)
416  y_p%geop(:) = x%geop(:)
417 
418  y%obs_ok = .true.
419  return
420 
421 end subroutine init_ropp_1d_obvec_tlad
422 
423 !-------------------------------------------------------------------------
424 !-------------------------------------------------------------------------
425 subroutine gnss_ztd_integral(lm, model_refrac, model_z, ob_terr, model_ztd, l_linear)
426 
427  implicit none
428 
429  integer, intent(in) :: lm
430  real(kind=kind_real), intent(in), dimension(lm) :: model_refrac, model_z
431  real(kind=kind_real), intent(in) :: ob_terr
432  real(kind=kind_real), intent(out) :: model_ztd
433  logical, intent(inout) :: l_linear
434 
435  integer, parameter :: max_string = 800
436  character(max_string) :: err_msg
437  real :: ddzd, c_i
438  integer :: k
439  !-------------------------------------------------------------------------------
440  ! integral of ROPP refractivity values to compute zenith total delay (ztd)
441  !-------------------------------------------------------------------------------
442  l_linear = .false.
443  model_ztd = 0.0
444  ddzd = 0.0
445  do k = 1, lm
446  if (k==1) then
447  if ( model_refrac(k) <= 0 .or. model_refrac(k+1) <= 0 ) then
448  write(err_msg,'(a,2es13.3)') ' unphysical refractivity ', &
449  model_refrac(k), model_refrac(k+1)
450  call fckit_log%info(err_msg)
451  cycle
452  end if
453  ! lower boundary condition -- station below lowest model level
454  if ( ob_terr < model_z(k) ) then
455  c_i = ( log(model_refrac(k+1)) - log(model_refrac(k)) ) / &
456  ( model_z(k) - model_z(k+1) )
457  if ( c_i == 0 ) then
458  write(err_msg,'(a,2es13.3)') ' model refractivity repeating1 ', &
459  model_refrac(k), model_refrac(k+1)
460  call fckit_log%info(err_msg)
461  l_linear = .true.
462  ddzd = model_refrac(k)*(model_z(k+1) - ob_terr) ! use linear estimate
463  cycle
464  end if
465  ddzd = -1.0 * model_refrac(k)/c_i * exp(c_i*model_z(k)) * &
466  ( exp(-c_i*model_z(k+1)) - exp(-c_i*ob_terr) )
467 ! ddzd = model_refrac(k)*(model_z(k+1) - ob_terr) ! linear estimate
468 ! -- new seems wrong but looks better
469 ! ddzd = model_refrac(k)*(model_z(k) - ob_terr) ! linear estimate
470 ! -- old
471  end if
472  else if ( ob_terr >= model_z(k)) then
473  cycle
474  else
475  if ( model_refrac(k) <= 0 .or. model_refrac(k-1) <= 0 ) then
476  write(err_msg,'(a,2es13.3)') ' unphysical refractivity ', &
477  model_refrac(k), model_refrac(k-1)
478  call fckit_log%info(err_msg)
479  cycle
480  end if
481  ! alternate lower boundary condition -- station between levels
482  if ( ob_terr >= model_z(k-1) .and. ob_terr < model_z(k) ) then
483  c_i = ( log(model_refrac(k)) - log(model_refrac(k-1)) ) / &
484  ( model_z(k-1) - model_z(k) )
485  if ( c_i == 0 ) then
486  write(err_msg,'(a,2es13.3)') ' model refractivity repeating2 ', &
487  model_refrac(k), model_refrac(k-1)
488  call fckit_log%info(err_msg)
489  l_linear = .true.
490  ddzd = model_refrac(k) * ( ob_terr - model_z(k-1) ) ! use linear estimate
491  ddzd = ddzd + 0.5*(model_refrac(k)+model_refrac(k-1))*(model_z(k) - ob_terr)
492  cycle
493  end if
494  ddzd = -1.0 * model_refrac(k-1)/c_i * exp(c_i*ob_terr) * &
495  ( exp(-c_i*model_z(k)) - exp(-c_i*ob_terr) )
496 ! ddzd = model_refrac(k) * ( ob_terr - model_z(k-1) ) ! linear
497 ! estimate -- new
498 ! ddzd = model_refrac(k) * ( model_z(k) - ob_terr ) ! linear
499 ! estimate -- old
500  ddzd = ddzd + 0.5*(model_refrac(k)+model_refrac(k-1))*(model_z(k) - ob_terr)
501  else ! normal integral up the column
502  ddzd = 0.5*(model_refrac(k)+model_refrac(k-1))*(model_z(k) - model_z(k-1))
503  end if
504  end if
505  model_ztd = model_ztd + ddzd
506  enddo
507 
508  ! a very bad place to put this suggestions?
509  model_ztd = model_ztd * 1.e-6
510 
511 end subroutine gnss_ztd_integral
512 
513 !-------------------------------------------------------------------------
514 !-------------------------------------------------------------------------
515 subroutine ropp_tidy_up_1d(x,y)
516  implicit none
517  type(state1dfm), intent(inout) :: x
518  type(obs1drefrac), intent(inout) :: y
519 ! x
520  if (associated(x%temp)) deallocate(x%temp)
521  if (associated(x%shum)) deallocate(x%shum)
522  if (associated(x%pres)) deallocate(x%pres)
523  if (associated(x%geop)) deallocate(x%geop)
524 ! y
525  if (associated(y%refrac)) deallocate(y%refrac)
526  if (associated(y%geop)) deallocate(y%geop)
527  if (associated(y%weights)) deallocate(y%weights)
528 
529 end subroutine ropp_tidy_up_1d
530 
531 !-------------------------------------------------------------------------
532 !-------------------------------------------------------------------------
533 subroutine ropp_tidy_up_tlad_1d(x,x_p,y,y_p)
534  implicit none
535  type(state1dfm), intent(inout) :: x,x_p ! x_p can be either x_tl or x_ad
536  type(obs1drefrac), intent(inout) :: y,y_p ! y_p can be either y_tl or y_ad
537 ! x
538  deallocate(x%temp)
539  deallocate(x%shum)
540  deallocate(x%pres)
541  deallocate(x%geop)
542 
543  deallocate(x_p%temp)
544  deallocate(x_p%shum)
545  deallocate(x_p%pres)
546  deallocate(x_p%geop)
547 ! y
548  deallocate(y%refrac)
549  deallocate(y_p%refrac)
550 
551  deallocate(y%weights)
552  deallocate(y_p%weights)
553 
554  deallocate(y%geop)
555  deallocate(y_p%geop)
556  return
557 
558 end subroutine ropp_tidy_up_tlad_1d
559 
560 !-------------------------------------------------------------------------
561 
Fortran module to handle ground-based gnss observations following the ROPP (2018 Aug) implementation.
subroutine, public init_ropp_1d_statevec(step_time, rlon, rlat, temp, shum, pres, phi, lm, phi_sfc, x, iflip)
subroutine, public calc_model_z(nlev, rlat, model_phi, model_z)
subroutine, public init_ropp_1d_obvec(nlev, ichk, ob_time, rlat, rlon, station_phi, x, y)
subroutine, public init_ropp_1d_obvec_tlad(nlev, rlat, rlon, x, y, y_p)
subroutine, public calc_station_phi(rlat, station_height, station_phi)
subroutine, public init_ropp_1d_statevec_ad(temp_d, shum_d, pres_d, phi_d, lm, x_ad, iflip)
subroutine, public gnss_ztd_integral(lm, model_refrac, model_z, ob_terr, model_ztd, l_linear)
subroutine, public ropp_tidy_up_tlad_1d(x, x_p, y, y_p)