OOPS
qg_obsdb_mod.F90
Go to the documentation of this file.
1 ! (C) Copyright 2009-2016 ECMWF.
2 ! (C) Copyright 2017-2019 UCAR.
3 !
4 ! This software is licensed under the terms of the Apache Licence Version 2.0
5 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6 ! In applying this licence, ECMWF does not waive the privileges and immunities
7 ! granted to it by virtue of its status as an intergovernmental organisation nor
8 ! does it submit to any jurisdiction.
9 
11 
12 use atlas_module
13 use datetime_mod
14 use duration_mod
15 use fckit_configuration_module, only: fckit_configuration
16 use fckit_log_module, only: fckit_log
17 use iso_c_binding
18 use kinds
19 use netcdf
21 use qg_locs_mod
22 use qg_obsvec_mod
24 use qg_tools_mod
25 use random_mod
26 use string_f_c_mod
27 
28 implicit none
29 
30 private
31 public :: qg_obsdb
32 public :: qg_obsdb_registry
34 ! ------------------------------------------------------------------------------
35 integer,parameter :: rseed = 1 !< Random seed (for reproducibility)
36 
38  character(len=50) :: colname !< Column name
39  type(column_data),pointer :: next => null() !< Next column
40  integer :: nlev !< Number of levels
41  real(kind_real),allocatable :: values(:,:) !< Values
42 end type column_data
43 
45  character(len=50) :: grpname !< Group name
46  type(group_data),pointer :: next => null() !< Next group
47  integer :: nobs !< Number of observations
48  type(datetime),allocatable :: times(:) !< Time-slots
49  type(column_data),pointer :: colhead => null() !< Head column
50 end type group_data
51 
53  integer :: ngrp !< Number of groups
54  character(len=1024) :: filein !< Input filename
55  character(len=1024) :: fileout !< Output filename
56  type(group_data),pointer :: grphead => null() !< Head group
57 end type qg_obsdb
58 
59 #define LISTED_TYPE qg_obsdb
60 
61 !> Linked list interface - defines registry_t type
62 #include "oops/util/linkedList_i.f"
63 
64 !> Global registry
65 type(registry_t) :: qg_obsdb_registry
66 ! ------------------------------------------------------------------------------
67 contains
68 ! ------------------------------------------------------------------------------
69 ! Public
70 ! ------------------------------------------------------------------------------
71 !> Linked list implementation
72 #include "oops/util/linkedList_c.f"
73 ! ------------------------------------------------------------------------------
74 !> Setup observation data
75 subroutine qg_obsdb_setup(self,f_conf,winbgn,winend)
76 use string_utils
77 
78 implicit none
79 
80 ! Passed variables
81 type(qg_obsdb),intent(inout) :: self !< Observation data
82 type(fckit_configuration),intent(in) :: f_conf !< FCKIT configuration
83 type(datetime),intent(in) :: winbgn !< Start of window
84 type(datetime),intent(in) :: winend !< End of window
85 
86 ! Local variables
87 character(len=1024) :: fin,fout
88 character(len=:),allocatable :: str
89 
90 ! Input file
91 if (f_conf%has("obsdatain")) then
92  call f_conf%get_or_die("obsdatain.obsfile",str)
93  fin = str
94 else
95  fin = ''
96 endif
97 call fckit_log%info('qg_obsdb_setup: file in = '//trim(fin))
98 
99 ! Output file
100 if (f_conf%has("obsdataout")) then
101  call f_conf%get_or_die("obsdataout.obsfile",str)
102  call swap_name_member(f_conf, str)
103 
104  fout = str
105  call fckit_log%info('qg_obsdb_setup: file out = '//trim(fout))
106 else
107  fout = ''
108 endif
109 
110 ! Set attributes
111 self%ngrp = 0
112 self%filein = fin
113 self%fileout = fout
114 
115 ! Read observation data
116 if (self%filein/='') call qg_obsdb_read(self,winbgn,winend)
117 
118 end subroutine qg_obsdb_setup
119 ! ------------------------------------------------------------------------------
120 !> Delete observation data
121 subroutine qg_obsdb_delete(self)
122 
123 implicit none
124 
125 ! Passed variables
126 type(qg_obsdb),intent(inout) :: self !< Observation data
127 
128 ! Local variables
129 type(group_data),pointer :: jgrp
130 type(column_data),pointer :: jcol
131 integer :: jobs
132 
133 ! Write observation data
134 if (self%fileout/='') call qg_obsdb_write(self)
135 
136 ! Release memory
137 do while (associated(self%grphead))
138  jgrp => self%grphead
139  self%grphead => jgrp%next
140  do jobs=1,jgrp%nobs
141  call datetime_delete(jgrp%times(jobs))
142  enddo
143  deallocate(jgrp%times)
144  do while (associated(jgrp%colhead))
145  jcol => jgrp%colhead
146  jgrp%colhead => jcol%next
147  deallocate(jcol%values)
148  deallocate(jcol)
149  enddo
150  deallocate(jgrp)
151 enddo
152 
153 end subroutine qg_obsdb_delete
154 ! ------------------------------------------------------------------------------
155 !> Get observation data
156 subroutine qg_obsdb_get(self,grp,col,ovec,local,idx)
157 
158 implicit none
159 
160 ! Passed variables
161 type(qg_obsdb),intent(in) :: self !< Observation data
162 character(len=*),intent(in) :: grp !< Group
163 character(len=*),intent(in) :: col !< Column
164 type(qg_obsvec),intent(inout) :: ovec !< Observation vector
165 logical,intent(in) :: local !< whether local subsetting is to be done
166 integer,intent(in),optional :: idx(:) !< subset of indices to get
167 
168 ! Local variables
169 type(group_data),pointer :: jgrp
170 type(column_data),pointer :: jcol
171 integer :: jobs,jlev
172 
173 ! Print Group and column
174 call fckit_log%debug('qg_obsdb_get: grp = '//trim(grp))
175 call fckit_log%debug('qg_obsdb_get: col = '//trim(col))
176 
177 ! Find observation group
178 call qg_obsdb_find_group(self,grp,jgrp)
179 if (.not.associated(jgrp)) then
180  jgrp => self%grphead
181  do while (associated(jgrp))
182  call fckit_log%info('qg_obsdb_get: group '//trim(jgrp%grpname)//' exists')
183  jgrp => jgrp%next
184  enddo
185  call fckit_log%error('qg_obsdb_get: cannot find '//trim(grp))
186  call abor1_ftn('qg_obsdb_get: obs group not found')
187 endif
188 
189 ! Find observation column
190 call qg_obsdb_find_column(jgrp,col,jcol)
191 if (.not.associated(jcol)) call abor1_ftn('qg_obsdb_get: obs column not found')
192 
193 ! Get observation data
194 if (allocated(ovec%values)) deallocate(ovec%values)
195 ovec%nlev = jcol%nlev
196 if (local) then
197  ! if local subset, but desired subset is zero length, return empty vector.
198  if ( .not. present(idx)) then
199  ovec%nobs = 0
200  else
201  ovec%nobs = size(idx)
202  endif
203 
204  ! get only a subset of the obs
205  allocate(ovec%values(ovec%nlev,ovec%nobs))
206  do jobs=1,ovec%nobs
207  do jlev=1,jcol%nlev
208  ovec%values(jlev,jobs) = jcol%values(jlev,idx(jobs)+1)
209  enddo
210  enddo
211 else
212  ! get all the obs
213  ovec%nobs = jgrp%nobs
214  allocate(ovec%values(ovec%nlev,ovec%nobs))
215  do jobs=1,jgrp%nobs
216  do jlev=1,jcol%nlev
217  ovec%values(jlev,jobs) = jcol%values(jlev,jobs)
218  enddo
219  enddo
220 end if
221 
222 end subroutine qg_obsdb_get
223 ! ------------------------------------------------------------------------------
224 !> Put observations data
225 subroutine qg_obsdb_put(self,grp,col,ovec)
226 
227 implicit none
228 
229 ! Passed variables
230 type(qg_obsdb),intent(inout) :: self !< Observation data
231 character(len=*),intent(in) :: grp !< Group
232 character(len=*),intent(in) :: col !< Column
233 type(qg_obsvec),intent(in) :: ovec !< Observation vector
234 
235 ! Local variables
236 type(group_data),pointer :: jgrp
237 type(column_data),pointer :: jcol
238 integer :: jobs,jlev
239 
240 ! Find observation group
241 call qg_obsdb_find_group(self,grp,jgrp)
242 if (.not.associated(jgrp)) then
243  jgrp => self%grphead
244  do while (associated(jgrp))
245  call fckit_log%info('qg_obsdb_put: group '//trim(jgrp%grpname)//' exists')
246  jgrp => jgrp%next
247  enddo
248  call fckit_log%error('qg_obsdb_put: cannot find '//trim(grp))
249  call abor1_ftn('qg_obsdb_put: obs group not found')
250 endif
251 
252 ! Find observation column (and add it if not there)
253 call qg_obsdb_find_column(jgrp,col,jcol)
254 if (.not.associated(jcol)) then
255  if (.not.associated(jgrp%colhead)) call abor1_ftn('qg_obsdb_put: no locations')
256  jcol => jgrp%colhead
257  do while (associated(jcol%next))
258  jcol => jcol%next
259  enddo
260  allocate(jcol%next)
261  jcol => jcol%next
262  jcol%colname = col
263  jcol%nlev = ovec%nlev
264  allocate(jcol%values(jcol%nlev,jgrp%nobs))
265 endif
266 
267 ! Put observation data
268 if (ovec%nobs/=jgrp%nobs) call abor1_ftn('qg_obsdb_put: error obs number')
269 if (ovec%nlev/=jcol%nlev) call abor1_ftn('qg_obsdb_put: error col number')
270 do jobs=1,jgrp%nobs
271  do jlev=1,jcol%nlev
272  jcol%values(jlev,jobs) = ovec%values(jlev,jobs)
273  enddo
274 enddo
275 
276 end subroutine qg_obsdb_put
277 ! ------------------------------------------------------------------------------
278 !> Test observation data existence
279 subroutine qg_obsdb_has(self,grp,col,has)
280 
281 implicit none
282 
283 ! Passed variables
284 type(qg_obsdb),intent(in) :: self !< Observation data
285 character(len=*),intent(in) :: grp !< Group
286 character(len=*),intent(in) :: col !< Column
287 integer,intent(out) :: has !< Test flag
288 
289 ! Passed variables
290 type(group_data),pointer :: jgrp
291 type(column_data),pointer :: jcol
292 
293 ! Initialization
294 has = 0
295 
296 ! Find observation group
297 call qg_obsdb_find_group(self,grp,jgrp)
298 if (associated(jgrp)) then
299  ! Find observation column
300  call qg_obsdb_find_column(jgrp,col,jcol)
301  if (associated(jcol)) has = 1
302 endif
303 
304 end subroutine qg_obsdb_has
305 ! ------------------------------------------------------------------------------
306 !> Get locations from observation data
307 subroutine qg_obsdb_locations(self,grp,t1,t2,fields,c_times)
308 
309 implicit none
310 
311 ! Passed variables
312 type(qg_obsdb),intent(in) :: self !< Observation data
313 character(len=*),intent(in) :: grp !< Group
314 type(datetime),intent(in) :: t1 !< Time 1
315 type(datetime),intent(in) :: t2 !< Time 2
316 type(atlas_fieldset), intent(inout) :: fields !< Locations FieldSet
317 type(c_ptr), intent(in), value :: c_times !< pointer to times array in C++
318 
319 ! Local variables
320 integer :: nlocs, jo
321 integer,allocatable :: indx(:)
322 character(len=8),parameter :: col = 'Location'
323 type(group_data),pointer :: jgrp
324 type(column_data),pointer :: jcol
325 type(atlas_field) :: field_z, field_lonlat
326 real(kind_real), pointer :: z(:), lonlat(:,:)
327 
328 ! Count observations
329 call qg_obsdb_count_time(self,grp,t1,t2,nlocs)
330 allocate(indx(nlocs))
331 call qg_obsdb_count_indx(self,grp,t1,t2,indx)
332 
333 ! Find observation group
334 call qg_obsdb_find_group(self,grp,jgrp)
335 if (.not.associated(jgrp)) call abor1_ftn('qg_obsdb_locations: obs group not found')
336 
337 ! Find observation column
338 call qg_obsdb_find_column(jgrp,col,jcol)
339 if (.not.associated(jcol)) call abor1_ftn('qg_obsdb_locations: obs column not found')
340 
341 ! Set number of observations
342 
343 field_lonlat = atlas_field(name="lonlat", kind=atlas_real(kind_real), shape=[2,nlocs])
344 field_z = atlas_field(name="altitude", kind=atlas_real(kind_real), shape=[nlocs])
345 
346 call field_lonlat%data(lonlat)
347 call field_z%data(z)
348 
349 ! Copy coordinates
350 do jo = 1, nlocs
351  lonlat(1,jo) = jcol%values(1,indx(jo))
352  lonlat(2,jo) = jcol%values(2,indx(jo))
353  z(jo) = jcol%values(3,indx(jo))
354  call f_c_push_to_datetime_vector(c_times, jgrp%times(indx(jo)))
355 enddo
356 
357 call fields%add(field_lonlat)
358 call fields%add(field_z)
359 
360 ! release pointers
361 call field_lonlat%final()
362 call field_z%final()
363 
364 deallocate(indx)
365 
366 end subroutine qg_obsdb_locations
367 ! ------------------------------------------------------------------------------
368 !> Generate observation data
369 subroutine qg_obsdb_generate(self,grp,f_conf,bgn,step,ktimes,kobs)
370 
371 implicit none
372 
373 ! Passed variables
374 type(qg_obsdb),intent(inout) :: self !< Observation data
375 character(len=*),intent(in) :: grp !< Group
376 type(fckit_configuration),intent(in) :: f_conf !< FCKIT configuration
377 type(datetime),intent(in) :: bgn !< Start time
378 type(duration),intent(in) :: step !< Time-step
379 integer,intent(in) :: ktimes !< Number of time-slots
380 integer,intent(inout) :: kobs !< Number of observations
381 
382 ! Local variables
383 integer :: nlev,nlocs
384 real(kind_real) :: err
385 type(datetime),allocatable :: times(:)
386 type(qg_obsvec) :: obsloc,obserr
387 
388 ! Get number of observations
389 call f_conf%get_or_die("obs_density",nlocs)
390 kobs = nlocs*ktimes
391 
392 ! Allocation
393 allocate(times(kobs))
394 
395 ! Generate locations
396 call qg_obsdb_generate_locations(nlocs,ktimes,bgn,step,times,obsloc)
397 
398 ! Create observations data
399 call qg_obsdb_create(self,trim(grp),times,obsloc)
400 
401 ! Create observation error
402 call f_conf%get_or_die("obs_error",err)
403 call f_conf%get_or_die("nval",nlev)
404 call qg_obsvec_setup(obserr,nlev,kobs)
405 obserr%values(:,:) = err
406 call qg_obsdb_put(self,trim(grp),'ObsError',obserr)
407 
408 ! Release memory
409 deallocate(times)
410 deallocate(obsloc%values)
411 deallocate(obserr%values)
412 
413 end subroutine qg_obsdb_generate
414 ! ------------------------------------------------------------------------------
415 !> Get observation data size
416 subroutine qg_obsdb_nobs(self,grp,kobs)
417 
418 implicit none
419 
420 ! Passed variables
421 type(qg_obsdb),intent(in) :: self !< Observation data
422 character(len=*),intent(in) :: grp !< Group
423 integer,intent(inout) :: kobs !< Number of observations
424 
425 ! Local variables
426 type(group_data),pointer :: jgrp
427 
428 ! Find group
429 call qg_obsdb_find_group(self,grp,jgrp)
430 
431 ! Get observation data size
432 if (associated(jgrp)) then
433  kobs = jgrp%nobs
434 else
435  kobs = 0
436 endif
437 
438 end subroutine qg_obsdb_nobs
439 ! ------------------------------------------------------------------------------
440 ! Private
441 ! ------------------------------------------------------------------------------
442 !> Read observation data
443 subroutine qg_obsdb_read(self,winbgn,winend)
444 
445 implicit none
446 
447 ! Passed variables
448 type(qg_obsdb),intent(inout) :: self !< Observation data
449 type(datetime),intent(in) :: winbgn !< Start of window
450 type(datetime),intent(in) :: winend !< End of window
451 
452 ! Local variables
453 integer :: igrp,icol,iobs,ncol,nobsfile,jobs
454 integer :: ncid,grpname_id,ngrp_id,nobs_id,ncol_id,times_id,nlev_id,colname_id,values_id
455 type(group_data),pointer :: jgrp
456 type(column_data),pointer :: jcol
457 character(len=6) :: igrpchar
458 character(len=50) :: stime
459 character(len=1024) :: record
460 logical, allocatable :: inwindow(:)
461 type(datetime) :: tobs
462 type(datetime), allocatable :: alltimes(:)
463 real(kind_real),allocatable :: readbuf(:,:)
464 
465 ! Open NetCDF file
466 call ncerr(nf90_open(trim(self%filein),nf90_nowrite,ncid))
467 
468 ! Get dimensions ids
469 call ncerr(nf90_inq_dimid(ncid,'ngrp',ngrp_id))
470 
471 ! Get dimensions
472 call ncerr(nf90_inquire_dimension(ncid,ngrp_id,len=self%ngrp))
473 
474 ! Get variables ids
475 call ncerr(nf90_inq_varid(ncid,'grpname',grpname_id))
476 
477 do igrp=1,self%ngrp
478  ! Allocation
479  if (igrp==1) then
480  allocate(self%grphead)
481  jgrp => self%grphead
482  else
483  allocate(jgrp%next)
484  jgrp => jgrp%next
485  endif
486  write(igrpchar,'(i6.6)') igrp
487 
488  ! Get variables
489  call ncerr(nf90_get_var(ncid,grpname_id,jgrp%grpname,(/1,igrp/),(/50,1/)))
490 
491  ! Get dimensions ids
492  call ncerr(nf90_inq_dimid(ncid,'nobs_'//igrpchar,nobs_id))
493  call ncerr(nf90_inq_dimid(ncid,'ncol_'//igrpchar,ncol_id))
494 
495  ! Get dimensions
496  call ncerr(nf90_inquire_dimension(ncid,nobs_id,len=nobsfile))
497  call ncerr(nf90_inquire_dimension(ncid,ncol_id,len=ncol))
498  write(record,*) 'qg_obsdb_read: reading ',nobsfile,' ',jgrp%grpname,' observations'
499  call fckit_log%info(record)
500 
501  ! Get variables ids
502  call ncerr(nf90_inq_varid(ncid,'times_'//igrpchar,times_id))
503  call ncerr(nf90_inq_varid(ncid,'nlev_'//igrpchar,nlev_id))
504  call ncerr(nf90_inq_varid(ncid,'colname_'//igrpchar,colname_id))
505  call ncerr(nf90_inq_varid(ncid,'values_'//igrpchar,values_id))
506 
507  ! Allocation
508  allocate(inwindow(nobsfile))
509  allocate(alltimes(nobsfile))
510 
511  ! Read in times
512  call ncerr(nf90_get_var(ncid,grpname_id,jgrp%grpname,(/1,igrp/),(/50,1/)))
513  jgrp%nobs = 0
514  do iobs=1,nobsfile
515  call ncerr(nf90_get_var(ncid,times_id,stime,(/1,iobs/),(/50,1/)))
516  call datetime_create(stime,tobs)
517  if (tobs > winbgn .and. tobs <= winend) then
518  inwindow(iobs) = .true.
519  alltimes(iobs) = tobs
520  jgrp%nobs = jgrp%nobs + 1
521  else
522  inwindow(iobs) = .false.
523  endif
524  end do
525  write(record,*) 'qg_obsdb_read: keeping ',jgrp%nobs,' ',jgrp%grpname,' observations'
526  call fckit_log%info(record)
527 
528  allocate(jgrp%times(jgrp%nobs))
529  jobs=0
530  do iobs=1,nobsfile
531  if (inwindow(iobs)) then
532  jobs = jobs + 1
533  jgrp%times(jobs) = alltimes(iobs)
534  endif
535  end do
536  deallocate(alltimes)
537  write(record,*) 'qg_obsdb_read: ',jgrp%grpname,' after times ',jgrp%nobs
538  call fckit_log%info(record)
539 
540  ! Loop over columns
541  do icol=1,ncol
542  ! Allocation
543  if (icol==1) then
544  allocate(jgrp%colhead)
545  jcol => jgrp%colhead
546  else
547  allocate(jcol%next)
548  jcol => jcol%next
549  endif
550 
551  ! Get variables
552  call ncerr(nf90_get_var(ncid,nlev_id,jcol%nlev,(/icol/)))
553  call ncerr(nf90_get_var(ncid,colname_id,jcol%colname,(/1,icol/),(/50,1/)))
554  write(record,*) 'qg_obsdb_read: ',jgrp%grpname,' after get var ',jgrp%nobs
555  call fckit_log%info(record)
556 
557  ! Allocation
558  allocate(readbuf(jcol%nlev,nobsfile))
559  allocate(jcol%values(jcol%nlev,jgrp%nobs))
560 
561  ! Get values
562  call ncerr(nf90_get_var(ncid,values_id,readbuf(1:jcol%nlev,:),(/1,icol,1/),(/jcol%nlev,1,nobsfile/)))
563  write(record,*) 'qg_obsdb_read: ',jgrp%grpname,' after get values ',jgrp%nobs
564  call fckit_log%info(record)
565 
566  jobs = 0
567  do iobs=1,nobsfile
568  if (inwindow(iobs)) then
569  jobs = jobs + 1
570  jcol%values(:,jobs) = readbuf(:,iobs)
571  endif
572  enddo
573  deallocate(readbuf)
574  write(record,*) 'qg_obsdb_read: ',jgrp%grpname,' after readbuf ',jgrp%nobs, jobs
575  call fckit_log%info(record)
576  enddo
577  deallocate(inwindow)
578  write(record,*) 'qg_obsdb_read: ',jgrp%grpname,' done ',jgrp%nobs
579  call fckit_log%info(record)
580 enddo
581 
582  write(record,*) 'qg_obsdb_read: closing file'
583  call fckit_log%info(record)
584 ! Close NetCDF file
585 call ncerr(nf90_close(ncid))
586 
587 end subroutine qg_obsdb_read
588 ! ------------------------------------------------------------------------------
589 !> Write observation data
590 subroutine qg_obsdb_write(self)
591 
592 implicit none
593 
594 ! Passed variables
595 type(qg_obsdb),intent(in) :: self !< Observation data
596 
597 ! Local variables
598 integer :: igrp,icol,iobs,ncol,nlevmax
599 integer :: ncid,nstrmax_id,grpname_id,ngrp_id,nobs_id,ncol_id,nlevmax_id,times_id,nlev_id,colname_id,values_id
600 type(group_data),pointer :: jgrp
601 type(column_data),pointer :: jcol
602 character(len=6) :: igrpchar
603 character(len=50) :: stime
604 
605 ! Create NetCDF file
606 call ncerr(nf90_create(trim(self%fileout),or(nf90_clobber,nf90_64bit_offset),ncid))
607 
608 ! Define dimensions
609 call ncerr(nf90_def_dim(ncid,'nstrmax',50,nstrmax_id))
610 call ncerr(nf90_def_dim(ncid,'ngrp',self%ngrp,ngrp_id))
611 
612 ! Define variable
613 call ncerr(nf90_def_var(ncid,'grpname',nf90_char,(/nstrmax_id,ngrp_id/),grpname_id))
614 
615 ! End definitions
616 call ncerr(nf90_enddef(ncid))
617 
618 ! Loop over groups
619 igrp = 0
620 jgrp => self%grphead
621 do while (associated(jgrp))
622  igrp = igrp+1
623  if (jgrp%nobs > 0) then
624  write(igrpchar,'(i6.6)') igrp
625  ! Enter definitions mode
626  call ncerr(nf90_redef(ncid))
627 
628  ! Compute dimensions
629  ncol = 0
630  nlevmax = 0
631  jcol => jgrp%colhead
632  do while (associated(jcol))
633  ncol = ncol+1
634  nlevmax = max(jcol%nlev,nlevmax)
635  jcol => jcol%next
636  enddo
637 
638  ! Define dimensions
639  call ncerr(nf90_def_dim(ncid,'nobs_'//igrpchar,jgrp%nobs,nobs_id))
640  call ncerr(nf90_def_dim(ncid,'ncol_'//igrpchar,ncol,ncol_id))
641  call ncerr(nf90_def_dim(ncid,'nlevmax_'//igrpchar,nlevmax,nlevmax_id))
642 
643  ! Define variable
644  call ncerr(nf90_def_var(ncid,'times_'//igrpchar,nf90_char,(/nstrmax_id,nobs_id/),times_id))
645  call ncerr(nf90_def_var(ncid,'nlev_'//igrpchar,nf90_int,(/ncol_id/),nlev_id))
646  call ncerr(nf90_def_var(ncid,'colname_'//igrpchar,nf90_char,(/nstrmax_id,ncol_id/),colname_id))
647  call ncerr(nf90_def_var(ncid,'values_'//igrpchar,nf90_double,(/nlevmax_id,ncol_id,nobs_id/),values_id))
648 
649  ! End definitions
650  call ncerr(nf90_enddef(ncid))
651 
652  ! Put variables
653  call ncerr(nf90_put_var(ncid,grpname_id,jgrp%grpname,(/1,igrp/),(/50,1/)))
654  do iobs=1,jgrp%nobs
655  call datetime_to_string(jgrp%times(iobs),stime)
656  call ncerr(nf90_put_var(ncid,times_id,stime,(/1,iobs/),(/50,1/)))
657  end do
658 
659  ! Loop over columns
660  icol = 0
661  jcol => jgrp%colhead
662  do while (associated(jcol))
663  icol = icol+1
664 
665  ! Put variables
666  call ncerr(nf90_put_var(ncid,nlev_id,jcol%nlev,(/icol/)))
667  call ncerr(nf90_put_var(ncid,colname_id,jcol%colname,(/1,icol/),(/50,1/)))
668  call ncerr(nf90_put_var(ncid,values_id,jcol%values(1:jcol%nlev,:),(/1,icol,1/),(/jcol%nlev,1,jgrp%nobs/)))
669 
670  ! Update
671  jcol => jcol%next
672  enddo
673  endif
674  ! Update
675  jgrp=>jgrp%next
676 end do
677 
678 ! Close NetCDF file
679 call ncerr(nf90_close(ncid))
680 
681 end subroutine qg_obsdb_write
682 ! ------------------------------------------------------------------------------
683 !> Find observation data group
684 subroutine qg_obsdb_find_group(self,grp,find)
685 
686 implicit none
687 
688 ! Passed variables
689 type(qg_obsdb),intent(in) :: self !< Observation data
690 character(len=*),intent(in) :: grp !< Group
691 type(group_data),pointer,intent(inout) :: find !< Result
692 
693 ! Initialization
694 find => self%grphead
695 
696 ! Loop
697 do while (associated(find))
698  if (find%grpname==grp) exit
699  find => find%next
700 enddo
701 
702 end subroutine qg_obsdb_find_group
703 ! ------------------------------------------------------------------------------
704 !> Find observation data column
705 subroutine qg_obsdb_find_column(grp,col,find)
706 
707 implicit none
708 
709 ! Passed variables
710 type(group_data),intent(in) :: grp !< Observation data
711 character(len=*),intent(in) :: col !< Column
712 type(column_data),pointer,intent(inout) :: find !< Result
713 
714 ! Initialization
715 find=>grp%colhead
716 
717 ! Loop
718 do while (associated(find))
719  if (find%colname==col) exit
720  find => find%next
721 enddo
722 
723 end subroutine qg_obsdb_find_column
724 ! ------------------------------------------------------------------------------
725 !> Generate random locations
726 subroutine qg_obsdb_generate_locations(nlocs,ntimes,bgn,step,times,obsloc)
727 
728 implicit none
729 
730 ! Passed variables
731 integer,intent(in) :: nlocs !< Number of locations
732 integer,intent(in) :: ntimes !< Number of time-slots
733 type(datetime),intent(in) :: bgn !< Start time
734 type(duration),intent(in) :: step !< Time-step
735 type(datetime),intent(inout) :: times(nlocs*ntimes) !< Time-slots
736 type(qg_obsvec),intent(inout) :: obsloc !< Observation locations
737 
738 ! Local variables
739 integer :: jobs,iobs,jstep
740 real(kind_real) :: x(nlocs),y(nlocs),z(nlocs),lon(nlocs),lat(nlocs)
741 type(datetime) :: now
742 
743 ! Generate random locations
744 call uniform_distribution(x,0.0_kind_real,domain_zonal,rseed)
745 call uniform_distribution(y,0.0_kind_real,domain_meridional,rseed)
746 call uniform_distribution(z,0.0_kind_real,domain_depth,rseed)
747 
748 ! Convert to lon/lat
749 do jobs=1,nlocs
750  call xy_to_lonlat(x(jobs),y(jobs),lon(jobs),lat(jobs))
751 enddo
752 
753 ! Setup observation vector
754 call qg_obsvec_setup(obsloc,3,nlocs*ntimes)
755 
756 ! Set observation locations
757 now = bgn
758 iobs=0
759 do jstep=1,ntimes
760  do jobs=1,nlocs
761  iobs = iobs+1
762  times(iobs) = now
763  obsloc%values(:,iobs) = (/lon(jobs),lat(jobs),z(jobs)/)
764  enddo
765  call datetime_update(now,step)
766 enddo
767 
768 ! Release memory
769 call datetime_delete(now)
770 
771 end subroutine qg_obsdb_generate_locations
772 ! ------------------------------------------------------------------------------
773 !> Create observation data
774 subroutine qg_obsdb_create(self,grp,times,locs)
775 
776 implicit none
777 
778 ! Passed varaibles
779 type(qg_obsdb),intent(inout) :: self !< Observation data
780 character(len=*),intent(in) :: grp !< Group
781 type(datetime),intent(in) :: times(:) !< Time-slots
782 type(qg_obsvec),intent(in) :: locs !< Locations
783 
784 ! Local variables
785 type(group_data),pointer :: igrp
786 integer :: jobs,jlev
787 
788 ! Find observation group
789 call qg_obsdb_find_group(self,grp,igrp)
790 if (associated(igrp)) call abor1_ftn('qg_obsdb_create: obs group already exists')
791 if (associated(self%grphead)) then
792  igrp => self%grphead
793  do while (associated(igrp%next))
794  igrp => igrp%next
795  enddo
796  allocate(igrp%next)
797  igrp => igrp%next
798 else
799  allocate(self%grphead)
800  igrp => self%grphead
801 endif
802 
803 ! Create observation data
804 igrp%grpname = grp
805 igrp%nobs = size(times)
806 allocate(igrp%times(igrp%nobs))
807 igrp%times(:) = times(:)
808 allocate(igrp%colhead)
809 igrp%colhead%colname = 'Location'
810 igrp%colhead%nlev = 3
811 allocate(igrp%colhead%values(3,igrp%nobs))
812 if (locs%nlev/=3) call abor1_ftn('qg_obsdb_create: error locations not 3D')
813 if (locs%nobs/=igrp%nobs) call abor1_ftn('qg_obsdb_create: error locations number')
814 do jobs=1,igrp%nobs
815  do jlev=1,3
816  igrp%colhead%values(jlev,jobs) = locs%values(jlev,jobs)
817  enddo
818 enddo
819 self%ngrp = self%ngrp+1
820 
821 end subroutine qg_obsdb_create
822 ! ------------------------------------------------------------------------------
823 !> Get observation data size between times
824 subroutine qg_obsdb_count_time(self,grp,t1,t2,kobs)
825 
826 implicit none
827 
828 ! Passed variables
829 type(qg_obsdb),intent(in) :: self !< Observation data
830 character(len=*),intent(in) :: grp !< Group
831 type(datetime),intent(in) :: t1 !< Time 1
832 type(datetime),intent(in) :: t2 !< Time 2
833 integer,intent(inout) :: kobs !< Number of observations
834 
835 ! Local variables
836 type(group_data),pointer :: jgrp
837 integer :: jobs
838 
839 ! Find observation group
840 call qg_obsdb_find_group(self,grp,jgrp)
841 if (.not.associated(jgrp)) call abor1_ftn('qg_obsdb_count_time: obs group not found')
842 
843 ! Time selection
844 kobs = 0
845 do jobs=1,jgrp%nobs
846  if ((t1<jgrp%times(jobs)).and.(jgrp%times(jobs)<=t2)) kobs = kobs+1
847 enddo
848 
849 end subroutine qg_obsdb_count_time
850 ! ------------------------------------------------------------------------------
851 !> Get observation data index
852 subroutine qg_obsdb_count_indx(self,grp,t1,t2,indx)
853 
854 implicit none
855 
856 ! Passed variables
857 type(qg_obsdb),intent(in) :: self !< Observation data
858 character(len=*),intent(in) :: grp !< Group
859 type(datetime),intent(in) :: t1 !< Time 1
860 type(datetime),intent(in) :: t2 !< Time 2
861 integer,intent(inout) :: indx(:) !< Observation index
862 
863 ! Local variables
864 type(group_data),pointer :: jgrp
865 integer :: jobs,io
866 
867 ! Find observation group
868 call qg_obsdb_find_group(self,grp,jgrp)
869 if (.not.associated(jgrp)) call abor1_ftn('qg_obsdb_count_indx: obs group not found')
870 
871 ! Time selection
872 io = 0
873 do jobs=1,jgrp%nobs
874  if ((t1<jgrp%times(jobs)).and.(jgrp%times(jobs)<=t2)) then
875  io = io+1
876  indx(io)=jobs
877  endif
878 enddo
879 
880 end subroutine qg_obsdb_count_indx
881 ! ------------------------------------------------------------------------------
882 end module qg_obsdb_mod
qg_obsdb_mod::qg_obsdb_generate
subroutine, public qg_obsdb_generate(self, grp, f_conf, bgn, step, ktimes, kobs)
Generate observation data.
Definition: qg_obsdb_mod.F90:370
qg_obsdb_mod::column_data
Definition: qg_obsdb_mod.F90:37
qg_projection_mod
Definition: qg_projection_mod.F90:9
qg_obsdb_mod::qg_obsdb_generate_locations
subroutine qg_obsdb_generate_locations(nlocs, ntimes, bgn, step, times, obsloc)
Generate random locations.
Definition: qg_obsdb_mod.F90:727
qg_constants_mod::domain_zonal
real(kind_real), parameter, public domain_zonal
Model domain in zonal direction (m)
Definition: qg_constants_mod.F90:32
qg_obsdb_mod::qg_obsdb_registry
type(registry_t), public qg_obsdb_registry
Linked list interface - defines registry_t type.
Definition: qg_obsdb_mod.F90:65
qg_obsdb_mod::qg_obsdb_find_group
subroutine qg_obsdb_find_group(self, grp, find)
Find observation data group.
Definition: qg_obsdb_mod.F90:685
qg_obsdb_mod::qg_obsdb
Definition: qg_obsdb_mod.F90:52
qg_obsdb_mod::qg_obsdb_read
subroutine qg_obsdb_read(self, winbgn, winend)
Read observation data.
Definition: qg_obsdb_mod.F90:444
qg_fields_mod::rseed
integer, parameter rseed
Random seed (for reproducibility)
Definition: qg_fields_mod.F90:49
qg_obsvec_mod
Definition: qg_obsvec_mod.F90:10
qg_obsdb_mod
Definition: qg_obsdb_mod.F90:10
qg_locs_mod
Definition: qg_locs_mod.F90:9
qg_obsdb_mod::qg_obsdb_create
subroutine qg_obsdb_create(self, grp, times, locs)
Create observation data.
Definition: qg_obsdb_mod.F90:775
qg_obsdb_mod::qg_obsdb_delete
subroutine, public qg_obsdb_delete(self)
Delete observation data.
Definition: qg_obsdb_mod.F90:122
qg_constants_mod
Definition: qg_constants_mod.F90:9
qg_obsdb_mod::qg_obsdb_count_time
subroutine qg_obsdb_count_time(self, grp, t1, t2, kobs)
Get observation data size between times.
Definition: qg_obsdb_mod.F90:825
qg_obsdb_mod::qg_obsdb_setup
subroutine, public qg_obsdb_setup(self, f_conf, winbgn, winend)
Linked list implementation.
Definition: qg_obsdb_mod.F90:76
qg_obsdb_mod::qg_obsdb_locations
subroutine, public qg_obsdb_locations(self, grp, t1, t2, fields, c_times)
Get locations from observation data.
Definition: qg_obsdb_mod.F90:308
qg_projection_mod::xy_to_lonlat
subroutine, public xy_to_lonlat(x, y, lon, lat, mapfac)
Convert x/y to lon/lat.
Definition: qg_projection_mod.F90:23
qg_obsdb_mod::qg_obsdb_nobs
subroutine, public qg_obsdb_nobs(self, grp, kobs)
Get observation data size.
Definition: qg_obsdb_mod.F90:417
qg_obsdb_mod::group_data
Definition: qg_obsdb_mod.F90:44
qg_obsdb_mod::qg_obsdb_get
subroutine, public qg_obsdb_get(self, grp, col, ovec, local, idx)
Get observation data.
Definition: qg_obsdb_mod.F90:157
qg_obsdb_mod::qg_obsdb_count_indx
subroutine qg_obsdb_count_indx(self, grp, t1, t2, indx)
Get observation data index.
Definition: qg_obsdb_mod.F90:853
qg_obsvec_mod::qg_obsvec_setup
subroutine, public qg_obsvec_setup(self, nlev, nobs)
Linked list implementation.
Definition: qg_obsvec_mod.F90:58
qg_constants_mod::domain_depth
real(kind_real), parameter, public domain_depth
Model depth (m)
Definition: qg_constants_mod.F90:34
qg_obsdb_mod::qg_obsdb_has
subroutine, public qg_obsdb_has(self, grp, col, has)
Test observation data existence.
Definition: qg_obsdb_mod.F90:280
oops_variables_mod::has
logical function has(this, var)
Definition: variables_mod.F90:140
qg_tools_mod
Definition: qg_tools_mod.F90:9
qg_obsdb_mod::qg_obsdb_write
subroutine qg_obsdb_write(self)
Write observation data.
Definition: qg_obsdb_mod.F90:591
qg_tools_mod::ncerr
subroutine, public ncerr(info)
Check NetCDF status.
Definition: qg_tools_mod.F90:99
qg_obsdb_mod::qg_obsdb_find_column
subroutine qg_obsdb_find_column(grp, col, find)
Find observation data column.
Definition: qg_obsdb_mod.F90:706
qg_obsvec_mod::qg_obsvec
Definition: qg_obsvec_mod.F90:35
qg_constants_mod::domain_meridional
real(kind_real), parameter, public domain_meridional
Model domain in meridional direction (m)
Definition: qg_constants_mod.F90:33
qg_obsdb_mod::qg_obsdb_put
subroutine, public qg_obsdb_put(self, grp, col, ovec)
Put observations data.
Definition: qg_obsdb_mod.F90:226