UFO
ufo_gnssro_bndnbam_mod.F90
Go to the documentation of this file.
1 !
2 ! This software is licensed under the terms of the Apache Licence Version 2.0
3 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
4 !> Fortran module of Gnssro NBAM (NCEP's Bending Angle Method)
5 !> nonlinear operator
6 
8  use, intrinsic:: iso_c_binding
9  use kinds
10  use ufo_vars_mod
11  use ufo_geovals_mod
13  use ufo_basis_mod, only: ufo_basis
14  use obsspace_mod
15  use missing_values_mod
16  use gnssro_mod_conf
20  use fckit_log_module, only : fckit_log
22 
23  implicit none
24  public :: ufo_gnssro_bndnbam
25  private
26 
27  !> Fortran derived type for gnssro trajectory
28  type, extends(ufo_basis) :: ufo_gnssro_bndnbam
29  type(gnssro_conf) :: roconf
30  contains
31  procedure :: setup => ufo_gnssro_bndnbam_setup
32  procedure :: simobs => ufo_gnssro_bndnbam_simobs
33  end type ufo_gnssro_bndnbam
34 
35  contains
36 ! ------------------------------------------------------------------------------
37 subroutine ufo_gnssro_bndnbam_setup(self, f_conf)
38 
39 use fckit_configuration_module, only: fckit_configuration
40 implicit none
41 class(ufo_gnssro_bndnbam), intent(inout) :: self
42 type(fckit_configuration), intent(in) :: f_conf
43 
44 call gnssro_conf_setup(self%roconf,f_conf)
45 
46 end subroutine ufo_gnssro_bndnbam_setup
47 
48 subroutine ufo_gnssro_bndnbam_simobs(self, geovals, hofx, obss)
49  implicit none
50  class(ufo_gnssro_bndnbam), intent(in) :: self
51  type(ufo_geovals), intent(in) :: geovals
52  real(kind_real), intent(inout) :: hofx(:)
53  type(c_ptr), value, intent(in) :: obss
54  character(len=*), parameter :: myname = "ufo_gnssro_bndnbam_simobs"
55  character(max_string) :: err_msg
56  integer :: nrecs, nlocs
57  integer, parameter :: nlevAdd = 13 !num of additional levels on top of exsiting model levels
58  integer, parameter :: ngrd = 80 !num of new veritcal grids for bending angle computation
59  integer :: iobs, k, igrd, irec, icount, kk
60  integer :: nlev, nlev1, nlevExt, nlevCheck
61  type(ufo_geoval), pointer :: t, q, gph, prs, zs
62  real(kind_real), allocatable :: gesT(:,:), gesZ(:,:), gesP(:,:), gesQ(:,:), gesTv(:,:), gesZs(:)
63  real(kind_real), allocatable :: obsLat(:), obsImpP(:),obsLocR(:), obsGeoid(:), obsValue(:)
64  integer(c_size_t), allocatable :: obsRecnum(:)
65  real(kind_real), allocatable :: temperature(:)
66  real(kind_real) :: wf
67  integer :: wi, wi2
68  real(kind_real) :: grids(ngrd)
69  real(kind_real), allocatable :: refIndex(:), refXrad(:), geomz(:)
70  real(kind_real), allocatable :: ref(:), radius(:)
71  real(kind_real) :: sIndx
72  integer :: indx
73  integer :: iflip
74  integer,allocatable :: nlocs_begin(:)
75  integer,allocatable :: nlocs_end(:)
76  real(c_double) :: missing
77  integer, allocatable :: super_refraction_flag(:), super(:), obs_max(:)
78  real(kind_real), allocatable :: toss_max(:)
79  integer :: sr_hgt_idx
80  real(kind_real) :: gradRef, obsImpH
81  integer, allocatable :: LayerIdx(:)
82 
83  write(err_msg,*) myname, ": begin"
84  call fckit_log%info(err_msg)
85 
86  nlocs = obsspace_get_nlocs(obss) ! number of observations
87  nrecs = obsspace_get_nrecs(obss) ! number of records/profiles
88  write(err_msg,*) myname, ': nlocs from gelvals and hofx, nrecs', geovals%nlocs, nlocs, nrecs
89  call fckit_log%info(err_msg)
90  missing = missing_value(missing)
91 
92  allocate(temperature(nlocs))
93  temperature = missing
94  allocate(super_refraction_flag(nlocs))
95  super_refraction_flag = 0
96  allocate(layeridx(nlocs))
97  layeridx = 0
98 
99  if (nlocs > 0) then ! check if ZERO OBS
100 
101 ! check if nobs is consistent in geovals & hofx
102  if (geovals%nlocs /= size(hofx)) then
103  write(err_msg,*) myname, ': nlocs inconsistent!', geovals%nlocs, size(hofx)
104  call abor1_ftn(err_msg)
105  endif
106 
107 ! get variables from geovals
108  call ufo_geovals_get_var(geovals, var_ts, t) ! air temperature
109  call ufo_geovals_get_var(geovals, var_q, q) ! specific humidity
110  call ufo_geovals_get_var(geovals, var_sfc_geomz, zs) ! surface geopotential height/surface altitude
111 
112  if (self%roconf%vertlayer .eq. "mass") then
113  call ufo_geovals_get_var(geovals, var_prs, prs) ! pressure
114  call ufo_geovals_get_var(geovals, var_z, gph) ! geopotential height
115  else if (self%roconf%vertlayer .eq. "full") then
116  call ufo_geovals_get_var(geovals, var_prsi, prs) ! pressure
117  call ufo_geovals_get_var(geovals, var_zi, gph) ! geopotential height
118  else
119  call ufo_geovals_get_var(geovals, var_prsi, prs) ! pressure
120  call ufo_geovals_get_var(geovals, var_zi, gph)
121  write(err_msg,*) myname,': vertlayer has to be mass of full, '//new_line('a')// &
122  ' will use full layer anyway'
123  call fckit_log%info(err_msg)
124  end if
125 
126  nlev = t%nval ! number of model mass levels
127  nlev1 = prs%nval ! number of model pressure/height levels
128 
129  allocate(gesp(nlev1,nlocs))
130  allocate(gesz(nlev1,nlocs))
131  allocate(gest(nlev,nlocs))
132  allocate(gestv(nlev,nlocs))
133  allocate(gesq(nlev,nlocs))
134  allocate(geszs(nlocs))
135 
136 ! copy geovals to local background arrays
137  iflip = 0
138  if (prs%vals(1,1) .lt. prs%vals(prs%nval,1) ) then
139  iflip = 1
140  write(err_msg,'(a)')' ufo_gnssro_bndnbam_simobs:'//new_line('a')// &
141  ' Model vertical height profile is in descending order,'//new_line('a')// &
142  ' but bndNBAM requires it to be ascending order, need flip'
143  call fckit_log%info(err_msg)
144  do k=1, nlev
145  gest(k,:) = t%vals(nlev-k+1,:)
146  gesq(k,:) = q%vals(nlev-k+1,:)
147  gestv(k,:)= gest(k,:)*(1+gesq(k,:)*(rv_over_rd-1))
148  enddo
149  do k=1, nlev1
150  gesp(k,:) = prs%vals(nlev1-k+1,:)
151  gesz(k,:) = gph%vals(nlev1-k+1,:)
152  enddo
153  else ! not flipping
154  do k=1, nlev
155  gest(k,:) = t%vals(k,:)
156  gesq(k,:) = q%vals(k,:)
157  gestv(k,:) = gest(k,:)*(1+gesq(k,:)*(rv_over_rd-1))
158  enddo
159 
160  do k=1, nlev1
161  gesp(k,:) = prs%vals(k,:)
162  gesz(k,:) = gph%vals(k,:)
163  enddo
164  end if
165  geszs(:) = zs%vals(1,:)
166 
167 ! if background t and q are on mass layers,
168 ! while p and z are on interface layers, take the mean of t and q
169 ! -- NBAM manner
170  if ( nlev1 /= nlev ) then
171  do k = nlev, 2, -1
172  gesq(k,:) = half* (gesq(k,:) + gesq(k-1,:))
173  gestv(k,:) = half* (gestv(k,:) + gestv(k-1,:))
174 ! PLEASE KEEP this COMMENT:
175 ! to exactly reproduce nbam, t is converted to tv, tv mean is calcualted,
176 ! then tv mean is converted to t mean
177  gest(k,:) = gestv(k,:)/(1+ gesq(k,:)*(rv_over_rd-1))
178  enddo
179  end if
180 
181 ! set obs space struture
182  allocate(obslat(nlocs))
183  allocate(obsimpp(nlocs))
184  allocate(obslocr(nlocs))
185  allocate(obsgeoid(nlocs))
186  allocate(obsvalue(nlocs))
187  allocate(obsrecnum(nlocs))
188  allocate(nlocs_begin(nrecs))
189  allocate(nlocs_end(nrecs))
190 
191  call obsspace_get_db(obss, "MetaData", "latitude", obslat)
192  call obsspace_get_db(obss, "MetaData", "impact_parameter", obsimpp)
193  call obsspace_get_db(obss, "MetaData", "earth_radius_of_curvature", obslocr)
194  call obsspace_get_db(obss, "MetaData", "geoid_height_above_reference_ellipsoid", obsgeoid)
195  call obsspace_get_db(obss, "ObsValue", "bending_angle", obsvalue)
196  call obsspace_get_recnum(obss, obsrecnum)
197 
198  nlocs_begin=1
199  nlocs_end=1
200  icount = 1
201  do iobs = 1, nlocs-1
202  if (obsrecnum(iobs+1) /= obsrecnum(iobs)) then
203  icount = icount +1 !counting number of records
204  nlocs_end(icount-1)= iobs
205  nlocs_begin(icount) = iobs+1
206  end if
207  end do
208  nlocs_end(nrecs)= nlocs
209  if (icount /= nrecs) then
210  write(err_msg,*) myname, ': record number is not consistent :', icount, nrecs
211  call abor1_ftn(err_msg)
212  end if
213 
214  nlevext = nlev + nlevadd
215  nlevcheck = min(23, nlev) !number of levels to check super refaction
216 
217 ! define new integration grids
218  do igrd = 0, ngrd-1
219  grids(igrd+1) = igrd * ds
220  end do
221 
222 ! bending angle forward model starts
223  allocate(geomz(nlev)) ! geometric height
224  allocate(radius(nlev)) ! tangent point radisu to earth center
225  allocate(ref(nlevext)) ! refractivity
226  allocate(refindex(nlev)) !refactivity index n
227  allocate(refxrad(0:nlevext+1)) !x=nr, model conuterpart impact parameter
228 
229  allocate(super(nlocs))
230  allocate(toss_max(nrecs))
231  allocate(obs_max(nrecs))
232 
233  iobs = 0
234  hofx = missing
235  super = 0
236  obs_max = 0
237  toss_max = 0
238 
239  rec_loop: do irec = 1, nrecs
240 
241  obs_loop: do icount = nlocs_begin(irec), nlocs_end(irec)
242 
243  iobs = icount
244  do k = 1, nlev
245 ! compute guess geometric height from geopotential height
246  call geop2geometric(obslat(iobs), gesz(k,iobs)-geszs(iobs), geomz(k))
247  radius(k) = geomz(k) + geszs(iobs) + obsgeoid(iobs) + obslocr(iobs) ! radius r
248 ! guess refactivity, refactivity index, and impact parameter
249  call compute_refractivity(gest(k,iobs), gesq(k,iobs), gesp(k,iobs), &
250  ref(k), self%roconf%use_compress)
251  refindex(k) = one + (r1em6*ref(k))
252  refxrad(k) = refindex(k) * radius(k)
253  end do
254 
255 ! Data rejection based on model background !
256 ! (1) skip data beyond model levels
257  call get_coordinate_value(obsimpp(iobs),sindx,refxrad(1),nlev,"increasing")
258  if (sindx < one .or. sindx > float(nlev)) cycle obs_loop
259 
260 ! save the obs vertical location index (unit: model layer)
261  layeridx(iobs) = min(max(1, int(sindx)), nlev)
262 
263 ! calculating temperature at obs location to obs space for BackgroundCheck RONBAM
264  indx=sindx
265  wi=min(max(1,indx),nlev)
266  wi2=max(1,min(indx+1,nlev))
267  wf=sindx-float(wi)
268  wf=max(zero,min(wf,one))
269  temperature(iobs)=gestv(wi,iobs)*(one-wf)+gestv(wi2,iobs)*wf
270 
271 ! (2) super-refaction
272 ! (2.1) GSI style super refraction check
273  if(trim(self%roconf%super_ref_qc) == "NBAM") then
274 
275  obsimph = (obsimpp(iobs) - obslocr(iobs)) * r1em3 !impact heigt: a-r_earth
276 
277  if (obsimph <= six) then
278  do k = nlevcheck, 1, -1
279 
280 ! N gradient
281  gradref = 1000.0 * (ref(k+1)-ref(k))/(radius(k+1)-radius(k))
282 ! check for model SR layer
283  if (abs(gradref) >= 0.75*crit_gradrefr .and. obsimpp(iobs) <= refxrad(k+2)) then
284  super_refraction_flag(iobs) = 1
285  cycle obs_loop
286  endif
287  end do
288 
289 ! relax to close-to-SR conditions, and check if obs is inside model SR layer
290 
291  if (self%roconf%sr_steps > 1 &
292  .and. obsvalue(iobs) >= 0.03 ) then
293 
294  do k = nlevcheck, 1, -1
295  gradref = 1000.0 * (ref(k+1)-ref(k))/(radius(k+1)-radius(k))
296  if (abs(gradref) >= half*crit_gradrefr &
297  .and. super(iobs) == 0 &
298  .and. toss_max(irec) <= obsvalue(iobs)) then
299  obs_max(irec) = iobs
300  toss_max(irec)= max(toss_max(irec), obsvalue(iobs))
301  super(iobs) = 1
302  end if
303  end do ! k
304 
305  end if ! end if(self%roconf%sr_steps > 1
306  end if ! obsImpH <= six
307 
308 ! ROPP style super refraction check
309  else if(trim(self%roconf%super_ref_qc) == "ECMWF") then
310 
311  sr_hgt_idx = 1
312  do k = nlev, 2, -1
313  if (refxrad(k) - refxrad(k-1) < 10.0) THEN
314  sr_hgt_idx = k
315  exit
316  end if
317  end do
318 
319  if (obsimpp(iobs) < refxrad(sr_hgt_idx)) then
320  super_refraction_flag(iobs) = 1
321  cycle obs_loop
322  end if
323 
324  else
325  write(err_msg,*) myname, ': super refraction method has to be NBAM or ECMWF!'
326  call abor1_ftn(err_msg)
327  end if
328 
329  if (super_refraction_flag(iobs) .eq. 0) then
331  obslat(iobs), obsgeoid(iobs), obslocr(iobs), obsimpp(iobs), &
332  grids, ngrd, &
333  nlev, nlevext, nlevadd, nlevcheck, &
334  radius(1:nlev),ref(1:nlevext),refindex(1:nlev),refxrad(0:nlevext), &
335  hofx(iobs))
336 
337  end if
338  end do obs_loop
339  end do rec_loop
340 
341  if (trim(self%roconf%super_ref_qc) == "NBAM" .and. self%roconf%sr_steps > 1 ) then
342  rec_loop2: do irec = 1, nrecs
343 
344  if (obs_max(irec) > 0 ) then
345 
346  obs_loop2: do k = nlocs_begin(irec), nlocs_end(irec)
347  obsimph = (obsimpp(k) - obslocr(k)) * r1em3
348  if (obsimph<=six .and. obsimpp(k)<=obsimpp(obs_max(irec)) .and. &
349  hofx(k)/=missing .and. super_refraction_flag(k)==0) then
350  super_refraction_flag(k)=2
351  end if
352  end do obs_loop2
353 
354  end if
355 
356  end do rec_loop2
357  end if
358 
359  deallocate(obslat)
360  deallocate(obsimpp)
361  deallocate(obslocr)
362  deallocate(obsgeoid)
363  deallocate(gesp)
364  deallocate(gesz)
365  deallocate(gest)
366  deallocate(gestv)
367  deallocate(gesq)
368  deallocate(geszs)
369  deallocate(ref)
370  deallocate(refindex)
371  deallocate(refxrad)
372  deallocate(geomz)
373  deallocate(radius)
374  deallocate(obsrecnum)
375  deallocate(nlocs_begin)
376  deallocate(nlocs_end)
377  deallocate(super)
378  deallocate(toss_max)
379  deallocate(obs_max)
380 
381  write(err_msg,*) myname, ": complete"
382  call fckit_log%info(err_msg)
383  end if ! end check if ZERO OBS
384 
385 ! putting temeprature at obs location to obs space for BackgroundCheck RONBAM
386  call obsspace_put_db(obss, "MetaData", "temperature", temperature)
387 ! putting super refraction flag to obs space
388  call obsspace_put_db(obss, "SRflag", "bending_angle", super_refraction_flag)
389 ! saving obs vertical model layer postion for later
390  call obsspace_put_db(obss, "LayerIdx", "bending_angle", layeridx)
391  deallocate(super_refraction_flag)
392  deallocate(temperature)
393  deallocate(layeridx)
394 
395 end subroutine ufo_gnssro_bndnbam_simobs
396 ! ------------------------------------------------------------------------------
397 end module ufo_gnssro_bndnbam_mod
ufo_gnssro_bndnbam_mod::ufo_gnssro_bndnbam_simobs
subroutine ufo_gnssro_bndnbam_simobs(self, geovals, hofx, obss)
Definition: ufo_gnssro_bndnbam_mod.F90:49
gnssro_mod_transform::compute_refractivity
subroutine compute_refractivity(temperature, specH, pressure, refr, use_compress)
Definition: gnssro_mod_transform.F90:64
ufo_vars_mod::var_zi
character(len=maxvarlen), parameter, public var_zi
Definition: ufo_variables_mod.F90:30
ufo_basis_mod
Definition: ufo_basis_mod.F90:6
ufo_gnssro_bndnbam_mod
Fortran module of Gnssro NBAM (NCEP's Bending Angle Method) nonlinear operator.
Definition: ufo_gnssro_bndnbam_mod.F90:7
ufo_gnssro_bndnbam_mod::ufo_gnssro_bndnbam_setup
subroutine ufo_gnssro_bndnbam_setup(self, f_conf)
Definition: ufo_gnssro_bndnbam_mod.F90:38
gnssro_mod_conf::gnssro_conf
Definition: gnssro_mod_conf.F90:14
gnssro_mod_transform::geop2geometric
subroutine geop2geometric(latitude, geopotentialH, geometricZ, dzdh_jac)
Definition: gnssro_mod_transform.F90:41
gnssro_mod_constants::six
real(kind_real), parameter, public six
Definition: gnssro_mod_constants.F90:15
gnssro_mod_constants::ds
real(kind_real), parameter, public ds
Definition: gnssro_mod_constants.F90:16
gnssro_mod_constants::crit_gradrefr
real(kind_real), parameter, public crit_gradrefr
Definition: gnssro_mod_constants.F90:17
ufo_geovals_mod
Definition: ufo_geovals_mod.F90:7
gnssro_mod_constants::r1em6
real(kind_real), parameter, public r1em6
Definition: gnssro_mod_constants.F90:13
ufo_geovals_mod_c
Definition: GeoVaLs.interface.F90:7
ufo_vars_mod::var_prsi
character(len=maxvarlen), parameter, public var_prsi
Definition: ufo_variables_mod.F90:26
gnssro_mod_grids
Definition: gnssro_mod_grids.F90:1
ufo_vars_mod::var_sfc_geomz
character(len=maxvarlen), parameter, public var_sfc_geomz
Definition: ufo_variables_mod.F90:70
gnssro_mod_conf::gnssro_conf_setup
subroutine, public gnssro_conf_setup(roconf, f_conf)
Definition: gnssro_mod_conf.F90:35
ufo_gnssro_bndnbam_util_mod::ufo_gnssro_bndnbam_simobs_single
subroutine, public ufo_gnssro_bndnbam_simobs_single(obsLat, obsGeoid, obsLocR, obsImpP, grids, ngrd, nlev, nlevExt, nlevAdd, nlevCheck, radius, ref, refIndex, refXrad, bendingAngle)
Definition: ufo_gnssro_bndnbam_util_mod.F90:29
ufo_basis_mod::ufo_basis
Definition: ufo_basis_mod.F90:12
ufo_vars_mod
Definition: ufo_variables_mod.F90:8
ufo_vars_mod::var_z
character(len=maxvarlen), parameter, public var_z
Definition: ufo_variables_mod.F90:29
gnssro_mod_grids::get_coordinate_value
subroutine, public get_coordinate_value(fin, fout, x, nx, flag)
Definition: gnssro_mod_grids.F90:13
ufo_gnssro_bndnbam_mod::ufo_gnssro_bndnbam
Fortran derived type for gnssro trajectory.
Definition: ufo_gnssro_bndnbam_mod.F90:28
ufo_geovals_mod::ufo_geovals_get_var
subroutine, public ufo_geovals_get_var(self, varname, geoval)
Definition: ufo_geovals_mod.F90:128
ufo_vars_mod::var_q
character(len=maxvarlen), parameter, public var_q
Definition: ufo_variables_mod.F90:22
ufo_vars_mod::var_ts
character(len=maxvarlen), parameter, public var_ts
Definition: ufo_variables_mod.F90:19
gnssro_mod_constants
Definition: gnssro_mod_constants.F90:2
ufo_geovals_mod::ufo_geovals
type to hold interpolated fields required by the obs operators
Definition: ufo_geovals_mod.F90:47
gnssro_mod_conf
Definition: gnssro_mod_conf.F90:2
ufo_geovals_mod::ufo_geoval
type to hold interpolated field for one variable, one observation
Definition: ufo_geovals_mod.F90:40
gnssro_mod_transform
Definition: gnssro_mod_transform.F90:2
ufo_vars_mod::var_prs
character(len=maxvarlen), parameter, public var_prs
Definition: ufo_variables_mod.F90:25
ufo_geovals_mod_c::ufo_geovals_registry
type(registry_t), public ufo_geovals_registry
Linked list interface - defines registry_t type.
Definition: GeoVaLs.interface.F90:30
ufo_gnssro_bndnbam_util_mod
Fortran module of Gnssro NBAM (NCEP's Bending Angle Method) operator.
Definition: ufo_gnssro_bndnbam_util_mod.F90:7
gnssro_mod_constants::r1em3
real(kind_real), parameter, public r1em3
Definition: gnssro_mod_constants.F90:14