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