FV3 Bundle
ObsSpace.interface.F90
Go to the documentation of this file.
1 ! (C) Copyright 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 radiosonde observations
7 
9 
10 use iso_c_binding
11 use string_f_c_mod
12 use config_mod
13 use datetime_mod
14 use duration_mod
16 use ioda_locs_mod
19 use fckit_log_module, only : fckit_log
20 use fckit_mpi_module
21 use kinds
22 #ifdef HAVE_ODB_API
23 use odb_helper_mod, only: &
25 #endif
26 
27 #ifdef HAVE_ODB
28 use odb, only: &
29  odb_cancel, &
30  odb_close, &
31  odb_open, &
32  odb_select
33 
34 use odbgetput, only: &
35  odb_get, &
36  odb_addview
37 #endif
38 
39 implicit none
40 private
41 
42 public :: ioda_obsdb_registry
43 
44 ! ------------------------------------------------------------------------------
45 integer, parameter :: max_string=800
46 ! ------------------------------------------------------------------------------
47 
48 #define LISTED_TYPE ioda_obsdb
49 
50 !> Linked list interface - defines registry_t type
51 #include "linkedList_i.f"
52 
53 !> Global registry
54 type(registry_t) :: ioda_obsdb_registry
55 
56 ! ------------------------------------------------------------------------------
57 contains
58 ! ------------------------------------------------------------------------------
59 !> Linked list implementation
60 #include "linkedList_c.f"
61 
62 ! ------------------------------------------------------------------------------
63 
64 subroutine ioda_obsdb_setup_c(c_key_self, c_conf) bind(c,name='ioda_obsdb_setup_f90')
65 
66 #ifdef HAVE_ODB_API
67 use odb_helper_mod, only: &
69 #endif
70 
71 use nc_diag_read_mod, only: nc_diag_read_get_dim
74 
76 
77 implicit none
78 
79 integer(c_int), intent(inout) :: c_key_self
80 type(c_ptr), intent(in) :: c_conf !< configuration
81 
82 type(ioda_obsdb), pointer :: self
83 character(len=max_string) :: fin
84 character(len=max_string) :: fout
85 character(len=max_string) :: cfg_fout
86 logical :: fout_exists
87 type(fckit_mpi_comm) :: comm
88 character(len=10) :: cproc
89 integer :: ppos
90 character(len=max_string) :: MyObsType
91 character(len=255) :: record
92 integer :: fvlen
93 integer, allocatable :: dist_indx(:)
94 integer, allocatable :: miss_indx(:)
95 integer :: nobs
96 integer :: nlocs
97 integer :: nvars
98 integer :: iunit
99 type(random_distribution) :: ran_dist
100 integer :: ds, n, nn
101 integer :: input_file_type
102 
103 #ifdef HAVE_ODB
104 integer :: rc, handle, num_pools, num_rows, num_cols
105 interface
106  function setenv (envname, envval, overwrite) bind (c, name = "setenv")
107  use, intrinsic :: iso_c_binding, only: &
108  c_char, &
109  c_int
110  character(kind=c_char) :: envname(*)
111  character(kind=c_char) :: envval(*)
112  integer(kind=c_int), value :: overwrite
113  integer(kind=c_int) :: setenv
114  end function setenv
115 end interface
116 #endif
117 
118 ! Get the obs type
119 myobstype = trim(config_get_string(c_conf,max_string,"ObsType"))
120 
121 if (config_element_exists(c_conf,"ObsData.ObsDataIn")) then
122  fin = config_get_string(c_conf,max_string,"ObsData.ObsDataIn.obsfile")
123  input_file_type = ioda_obsdb_get_ftype(fin)
124 
125  ! For now, just assimilating one variable, so set nvars to 1 by default.
126  !
127  ! For radiance, there are 12 channels of data (channels 7, 8 and 14 were not assimilated
128  ! into the GSI run for April 15, 2018 00Z). The brightness_temperature obs data is actually
129  ! not used at this point. However, the CRTM always creates an hofx with 15 channels, and it
130  ! needs the nobs value to be set as if all 15 channels of the obs data are present. We still
131  ! need to decide how to handle missing channels in obs data, so to get things going for
132  ! now set nvars to 15 for Radiance. Note that when we come to the point where we do want
133  ! to read in the brightness temperature, we will need to address how to handle missing
134  ! channels. Ditto for AOD obs type, where AOD obs (VIIRS) has 11 channels.
135  nvars = 1
136  if (trim(myobstype) .eq. "Radiance") nvars = 15
137  if (trim(myobstype) .eq. "Aod") nvars = 11
138 
139  select case (input_file_type)
140  case (0)
141 
142  call nc_diag_read_init(fin, iunit)
143 
144  ! Get the length of the vectors in the input file
145  fvlen = nc_diag_read_get_dim(iunit, 'nlocs')
146 
147  ! Apply the random distribution, which yields the size and indices that
148  ! are to be selected by this process element out of the file.
149  ran_dist = random_distribution(fvlen)
150  nlocs = ran_dist%nobs_pe()
151  allocate(dist_indx(nlocs))
152  dist_indx = ran_dist%indx
153  nobs = nvars * nlocs
154 
155  ! Read in a variable and check for missing values. Adjust the nobs, nlocs values
156  ! and dist_index accordingly.
157  if ((trim(myobstype) .eq. "Radiosonde") .or. &
158  (trim(myobstype) .eq. "Aircraft")) then
159  call ioda_deselect_missing_values(iunit, "air_temperature", dist_indx, miss_indx)
160 
161  ! Reallocate dist_indx and copy miss_indx in. This should skip over missing values
162  ! from the file.
163  nlocs = size(miss_indx)
164  deallocate(dist_indx)
165  allocate(dist_indx(nlocs))
166  dist_indx = miss_indx
167 
168  nobs = nvars * nlocs
169 
170  deallocate(miss_indx)
171  endif
172 
173  call nc_diag_read_close(fin)
174 
175  case (1)
176 
177 #ifdef HAVE_ODB_API
178 
179  call count_query_results (fin, 'select seqno from "' // trim(fin) // '" where entryno = 1;', nobs)
180  fvlen = nobs
181  nlocs = nobs
182  allocate (dist_indx(nobs))
183  dist_indx(:) = [(/(n, n = 1,nobs)/)]
184 
185 #endif
186 
187  case (2)
188 
189 #ifdef HAVE_ODB
190 
191  rc = setenv("ODB_SRCPATH_OOPS" // c_null_char, trim(fin) // c_null_char, 1_c_int)
192  rc = setenv("ODB_DATAPATH_OOPS" // c_null_char, trim(fin) // c_null_char, 1_c_int)
193  rc = setenv("IOASSIGN" // c_null_char, trim(fin) // '/IOASSIGN' // c_null_char, 1_c_int)
194  handle = odb_open("OOPS", "OLD", num_pools)
195  rc = odb_addview(handle, "query", abort = .false.)
196  rc = odb_select(handle, "query", num_rows, num_cols)
197  allocate (self % odb_data(num_rows,0:num_cols))
198  rc = odb_get(handle, "query", self % odb_data, num_rows)
199  rc = odb_cancel(handle, "query")
200  rc = odb_close(handle)
201  nobs = size(self % odb_data,dim=1)
202  fvlen = nobs
203  nlocs = nobs
204  allocate (dist_indx(nobs))
205  dist_indx(:) = [(/(n, n = 1,nobs)/)]
206 
207 #endif
208 
209  end select
210 
211 else
212  fin = ""
213  fvlen = 0
214  nobs = 0
215  nlocs = 0
216  ! Create a dummy index array
217  allocate(dist_indx(1))
218  dist_indx(1) = -1
219 endif
220 
221 write(record,*) 'ioda_obsdb_setup_c: ', trim(myobstype), ' file in = ',trim(fin)
222 call fckit_log%info(record)
223 
224 ! Check to see if an output file has been requested.
225 if (config_element_exists(c_conf,"ObsData.ObsDataOut")) then
226  cfg_fout = config_get_string(c_conf,max_string,"ObsData.ObsDataOut.obsfile")
227 
228  ! Tag the process rank onto the end of the file name so that multi processes won't
229  ! collide with each other. Place the process rank number right before the file
230  ! extension.
231 
232  ! get the process rank number
233  comm = fckit_mpi_comm()
234  write(cproc,fmt='(i4.4)') comm%rank()
235 
236  ! Find the left-most dot in the file name, and use that to pick off the file name
237  ! and file extension.
238  ppos = scan(trim(cfg_fout), '.', back=.true.)
239  if (ppos > 0) then
240  ! found a file extension
241  fout = cfg_fout(1:ppos-1) // '_' // trim(adjustl(cproc)) // trim(cfg_fout(ppos:))
242  else
243  ! no file extension
244  fout = trim(cfg_fout) // '_' // trim(adjustl(cproc))
245  endif
246 
247  ! Check to see if user is trying to overwrite an existing file. For now always allow
248  ! the overwrite, but issue a warning if we are about to clobber an existing file.
249  inquire(file=trim(fout), exist=fout_exists)
250  if (fout_exists) then
251  write(record,*) 'ioda_obsdb_setup_c: WARNING: Overwriting output file: ', trim(fout)
252  call fckit_log%info(record)
253  endif
254 
255 endif
256 
257 call ioda_obsdb_registry%init()
258 call ioda_obsdb_registry%add(c_key_self)
259 call ioda_obsdb_registry%get(c_key_self, self)
260 
261 call ioda_obsdb_setup(self, fvlen, nobs, dist_indx, nlocs, nvars, fin, fout, myobstype)
262 
263 end subroutine ioda_obsdb_setup_c
264 
265 ! ------------------------------------------------------------------------------
266 
267 subroutine ioda_obsdb_delete_c(c_key_self) bind(c,name='ioda_obsdb_delete_f90')
268 implicit none
269 integer(c_int), intent(inout) :: c_key_self
270 type(ioda_obsdb), pointer :: self
271 
272 call ioda_obsdb_registry%get(c_key_self, self)
273 call ioda_obsdb_delete(self)
274 call ioda_obsdb_registry%remove(c_key_self)
275 
276 end subroutine ioda_obsdb_delete_c
277 
278 ! ------------------------------------------------------------------------------
279 
280 subroutine ioda_obsdb_nobs_c(c_key_self, kobs) bind(c,name='ioda_obsdb_nobs_f90')
281 implicit none
282 integer(c_int), intent(in) :: c_key_self
283 integer(c_int), intent(inout) :: kobs
284 type(ioda_obsdb), pointer :: self
285 
286 call ioda_obsdb_registry%get(c_key_self, self)
287 
288 kobs = self%nobs
289 
290 end subroutine ioda_obsdb_nobs_c
291 
292 ! ------------------------------------------------------------------------------
293 
294 subroutine ioda_obsdb_nlocs_c(c_key_self, klocs) bind(c,name='ioda_obsdb_nlocs_f90')
295 implicit none
296 integer(c_int), intent(in) :: c_key_self
297 integer(c_int), intent(inout) :: klocs
298 type(ioda_obsdb), pointer :: self
299 
300 call ioda_obsdb_registry%get(c_key_self, self)
301 
302 klocs = self%nlocs
303 
304 end subroutine ioda_obsdb_nlocs_c
305 
306 ! ------------------------------------------------------------------------------
307 
308 subroutine ioda_obsdb_getlocations_c(c_key_self, c_t1, c_t2, c_key_locs) bind(c,name='ioda_obsdb_getlocations_f90')
309 implicit none
310 integer(c_int), intent(in) :: c_key_self
311 type(c_ptr), intent(in) :: c_t1, c_t2
312 integer(c_int), intent(inout) :: c_key_locs
313 
314 type(ioda_obsdb), pointer :: self
315 type(datetime) :: t1, t2
316 type(ioda_locs), pointer :: locs
317 
318 call ioda_obsdb_registry%get(c_key_self, self)
319 call c_f_datetime(c_t1, t1)
320 call c_f_datetime(c_t2, t2)
321 
322 call ioda_locs_registry%init()
323 call ioda_locs_registry%add(c_key_locs)
324 call ioda_locs_registry%get(c_key_locs,locs)
325 
326 call ioda_obsdb_getlocs(self, locs, t1, t2)
327 
328 end subroutine ioda_obsdb_getlocations_c
329 
330 ! ------------------------------------------------------------------------------
331 
332 subroutine ioda_obsdb_generate_c(c_key_self, c_conf, c_t1, c_t2) bind(c,name='ioda_obsdb_generate_f90')
333 implicit none
334 integer(c_int), intent(inout) :: c_key_self
335 type(c_ptr), intent(in) :: c_conf !< configuration
336 type(c_ptr), intent(in) :: c_t1, c_t2
337 
338 type(ioda_obsdb), pointer :: self
339 type(datetime) :: t1, t2
340 integer :: fvlen
341 integer :: nobs
342 integer,allocatable :: dist_indx(:)
343 integer :: nlocs
344 integer :: nvars
345 character(len=max_string) :: MyObsType
346 character(len=255) :: record
347 real :: lat, lon1, lon2
348 type(random_distribution) :: ran_dist
349 
350 call ioda_obsdb_registry%get(c_key_self, self)
351 call c_f_datetime(c_t1, t1)
352 call c_f_datetime(c_t2, t2)
353 
354 fvlen = config_get_int(c_conf, "nobs")
355 lat = config_get_real(c_conf, "lat")
356 lon1 = config_get_real(c_conf, "lon1")
357 lon2 = config_get_real(c_conf, "lon2")
358 
359 ! Apply the random distribution, which yeilds nobs and the indices for selecting
360 ! observations out of the file.
361 ran_dist = random_distribution(fvlen)
362 nobs = ran_dist%nobs_pe()
363 allocate(dist_indx(nobs))
364 dist_indx = ran_dist%indx
365 
366 ! For now, set fvlen and nlocs equal to nobs. This may need to change for some obs types.
367 nlocs = nobs
368 
369 ! For now, set nvars to one.
370 nvars = 1
371 
372 ! Record obs type
373 myobstype = trim(config_get_string(c_conf,max_string,"ObsType"))
374 write(record,*) 'ioda_obsdb_generate_c: ', trim(myobstype)
375 call fckit_log%info(record)
376 
377 call ioda_obsdb_generate(self, fvlen, nobs, dist_indx, nlocs, nvars, myobstype, lat, lon1, lon2)
378 
379 deallocate(dist_indx)
380 
381 end subroutine ioda_obsdb_generate_c
382 
383 ! ------------------------------------------------------------------------------
384 
385 subroutine ioda_obsdb_geti_c(c_key_self, c_name_size, c_name, c_vec_size, c_vec) bind(c,name='ioda_obsdb_geti_f90')
386 implicit none
387 integer(c_int), intent(in) :: c_key_self
388 integer(c_int), intent(in) :: c_name_size
389 character(kind=c_char,len=1), intent(in) :: c_name(c_name_size+1)
390 integer(c_int), intent(in) :: c_vec_size
391 integer(c_int), intent(out) :: c_vec(c_vec_size)
392 
393 type(ioda_obsdb), pointer :: self
394 real(kind_real), allocatable :: vdata(:)
395 
396 character(len=c_name_size) :: vname
397 integer :: jj
398 
399 call ioda_obsdb_registry%get(c_key_self, self)
400 
401 ! Copy C character array to Fortran string
402 do jj = 1, c_name_size
403  vname(jj:jj) = c_name(jj)
404 enddo
405 
406 allocate(vdata(c_vec_size))
407 call ioda_obsdb_get_vec(self, vname, vdata)
408 
409 c_vec(:) = nint(vdata(:), c_int)
410 deallocate(vdata)
411 
412 end subroutine ioda_obsdb_geti_c
413 
414 ! ------------------------------------------------------------------------------
415 
416 subroutine ioda_obsdb_getd_c(c_key_self, c_name_size, c_name, c_vec_size, c_vec) bind(c,name='ioda_obsdb_getd_f90')
417 implicit none
418 integer(c_int), intent(in) :: c_key_self
419 integer(c_int), intent(in) :: c_name_size
420 character(kind=c_char,len=1), intent(in) :: c_name(c_name_size+1)
421 integer(c_int), intent(in) :: c_vec_size
422 real(c_double), intent(out) :: c_vec(c_vec_size)
423 
424 type(ioda_obsdb), pointer :: self
425 real(kind_real), allocatable :: vdata(:)
426 
427 character(len=c_name_size) :: vname
428 integer :: i
429 
430 call ioda_obsdb_registry%get(c_key_self, self)
431 
432 ! Copy C character array to Fortran string
433 do i = 1, c_name_size
434  vname(i:i) = c_name(i)
435 enddo
436 
437 ! Quick hack for dealing with inverted observation error values in the netcdf
438 ! file. Need to revisit this in the future and come up with a better solution.
439 allocate(vdata(c_vec_size))
440 call ioda_obsdb_get_vec(self, vname, vdata)
441 
442 if (trim(vname) .eq. "ObsErr") then
443  c_vec = 1.0_kind_real / vdata
444 else
445  c_vec = vdata
446 endif
447 
448 deallocate(vdata)
449 
450 end subroutine ioda_obsdb_getd_c
451 
452 ! ------------------------------------------------------------------------------
453 
454 subroutine ioda_obsdb_puti_c(c_key_self, c_name_size, c_name, c_vec_size, c_vec) bind(c,name='ioda_obsdb_puti_f90')
455 implicit none
456 integer(c_int), intent(in) :: c_key_self
457 integer(c_int), intent(in) :: c_name_size
458 character(kind=c_char,len=1), intent(in) :: c_name(c_name_size+1)
459 integer(c_int), intent(in) :: c_vec_size
460 integer(c_int), intent(in) :: c_vec(c_vec_size)
461 
462 type(ioda_obsdb), pointer :: self
463 real(kind_real), allocatable :: vdata(:)
464 
465 character(len=c_name_size) :: vname
466 integer :: jj
467 
468 call ioda_obsdb_registry%get(c_key_self, self)
469 
470 ! Copy C character array to Fortran string
471 do jj = 1, c_name_size
472  vname(jj:jj) = c_name(jj)
473 enddo
474 
475 allocate(vdata(c_vec_size))
476 vdata(:) = c_vec(:)
477 call ioda_obsdb_put_vec(self, vname, vdata)
478 deallocate(vdata)
479 
480 end subroutine ioda_obsdb_puti_c
481 
482 ! ------------------------------------------------------------------------------
483 
484 subroutine ioda_obsdb_putd_c(c_key_self, c_name_size, c_name, c_vec_size, c_vec) bind(c,name='ioda_obsdb_putd_f90')
485 implicit none
486 integer(c_int), intent(in) :: c_key_self
487 integer(c_int), intent(in) :: c_name_size
488 character(kind=c_char,len=1), intent(in) :: c_name(c_name_size+1)
489 integer(c_int), intent(in) :: c_vec_size
490 real(c_double), intent(in) :: c_vec(c_vec_size)
491 
492 type(ioda_obsdb), pointer :: self
493 
494 character(len=c_name_size) :: vname
495 integer :: i
496 
497 call ioda_obsdb_registry%get(c_key_self, self)
498 
499 ! Copy C character array to Fortran string
500 do i = 1, c_name_size
501  vname(i:i) = c_name(i)
502 enddo
503 
504 call ioda_obsdb_put_vec(self, vname, c_vec)
505 
506 end subroutine ioda_obsdb_putd_c
507 
508 ! ------------------------------------------------------------------------------
509 
510 end module ioda_obsdb_mod_c
subroutine ioda_obsdb_generate_c(c_key_self, c_conf, c_t1, c_t2)
subroutine count_query_results(filename, query, num_results)
subroutine, public ioda_obsdb_setup(self, fvlen, nobs, dist_indx, nlocs, nvars, filename, fileout, obstype)
subroutine ioda_obsdb_nlocs_c(c_key_self, klocs)
subroutine ioda_obsdb_putd_c(c_key_self, c_name_size, c_name, c_vec_size, c_vec)
subroutine ioda_obsdb_geti_c(c_key_self, c_name_size, c_name, c_vec_size, c_vec)
integer, parameter max_string
subroutine ioda_obsdb_nobs_c(c_key_self, kobs)
subroutine ioda_obsdb_delete_c(c_key_self)
subroutine ioda_obsdb_getlocations_c(c_key_self, c_t1, c_t2, c_key_locs)
subroutine ioda_obsdb_getd_c(c_key_self, c_name_size, c_name, c_vec_size, c_vec)
Fortran module handling radiosonde observation space.
Fortran module containing IODA utility programs.
type(registry_t), public ioda_locs_registry
Linked list interface - defines registry_t type.
subroutine, public ioda_obsdb_get_vec(self, vname, vdata)
type(registry_t), public ioda_obsdb_registry
Linked list interface - defines registry_t type.
integer function, public ioda_obsdb_get_ftype(fname)
subroutine, public ioda_obsdb_delete(self)
Fortran module with ODB API utility routines.
subroutine, public ioda_obsdb_getlocs(self, locs, t1, t2)
subroutine, public ioda_obsdb_put_vec(self, vname, vdata)
subroutine nc_diag_read_close(filename, file_ncdr_id, from_pop)
Fortran module handling observation locations.
Fortran module to handle radiosonde observations.
subroutine ioda_obsdb_setup_c(c_key_self, c_conf)
Linked list implementation.
subroutine ioda_obsdb_puti_c(c_key_self, c_name_size, c_name, c_vec_size, c_vec)
subroutine, public ioda_deselect_missing_values(ncid, vname, in_index, out_index)
subroutine, public ioda_obsdb_generate(self, fvlen, nobs, dist_indx, nlocs, nvars, obstype, lat, lon1, lon2)
subroutine nc_diag_read_init(filename, file_ncdr_id, from_push)