FV3 Bundle
ufo_geovals_mod.F90
Go to the documentation of this file.
1 !
2 ! (C) Copyright 2017-2018 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 !
8 
9 use ufo_vars_mod
10 use kinds
12 
13 use fckit_mpi_module, only: fckit_mpi_comm, fckit_mpi_sum
14 
15 implicit none
16 private
17 integer, parameter :: max_string=800
18 
26 public :: ufo_geovals_allocone
27 
28 ! ------------------------------------------------------------------------------
29 
30 !> type to hold interpolated field for one variable, one observation
31 type :: ufo_geoval
32  real(kind_real), allocatable :: vals(:,:) !< values (nval, nobs)
33  integer :: nval !< number of values in profile
34  integer :: nobs !< number of observations
35 end type ufo_geoval
36 
37 !> type to hold interpolated fields required by the obs operators
38 type :: ufo_geovals
39  integer :: nobs !< number of observations
40  integer :: nvar !< number of variables (supposed to be the
41  ! same for same obs operator
42 
43  type(ufo_geoval), allocatable :: geovals(:) !< array of interpolated
44  ! vertical profiles for all obs (nvar)
45 
46  type(ufo_vars) :: variables !< variables list
47 
48  logical :: lalloc !< .true. if type was initialized and allocated
49  ! (only geovals are allocated, not the arrays
50  ! inside of the ufo_geoval type)
51  logical :: linit !< .true. if all the ufo_geoval arrays inside geovals
52  ! were allocated and have data
53 end type ufo_geovals
54 
55 ! ------------------------------------------------------------------------------
56 contains
57 ! ------------------------------------------------------------------------------
58 
59 subroutine ufo_geovals_init(self)
60 implicit none
61 type(ufo_geovals), intent(inout) :: self
62 
63 self%lalloc = .false.
64 self%linit = .false.
65 self%nvar = 0
66 self%nobs = 0
67 
68 end subroutine ufo_geovals_init
69 
70 ! ------------------------------------------------------------------------------
71 
72 subroutine ufo_geovals_setup(self, vars, nobs)
73 implicit none
74 type(ufo_geovals), intent(inout) :: self
75 type(ufo_vars), intent(in) :: vars
76 integer, intent(in) :: nobs
77 
78 integer :: ivar
79 
80 call ufo_geovals_delete(self)
81 self%nobs = nobs
82 self%nvar = vars%nv
83 call ufo_vars_clone(vars, self%variables)
84 allocate(self%geovals(self%nvar))
85 do ivar = 1, self%nvar
86  self%geovals(ivar)%nobs = nobs
87  self%geovals(ivar)%nval = 0
88 enddo
89 self%lalloc = .true.
90 end subroutine ufo_geovals_setup
91 
92 ! ------------------------------------------------------------------------------
93 
94 subroutine ufo_geovals_delete(self)
95 implicit none
96 type(ufo_geovals), intent(inout) :: self
97 
98 integer :: ivar
99 
100 if (self%linit) then
101  do ivar = 1, self%nvar
102  deallocate(self%geovals(ivar)%vals)
103  enddo
104  self%linit = .false.
105 endif
106 if (self%lalloc) then
107  deallocate(self%geovals)
108  self%lalloc = .false.
109  self%nvar = 0
110  self%nobs = 0
111 endif
112 
113 end subroutine ufo_geovals_delete
114 
115 ! ------------------------------------------------------------------------------
116 
117 subroutine ufo_geovals_get_var(self, varname, geoval, status)
118 implicit none
119 type(ufo_geovals), target, intent(in) :: self
120 character(MAXVARLEN), intent(in) :: varname
121 type(ufo_geoval), pointer, intent(inout) :: geoval
122 integer, optional, intent(out) :: status
123 
124 integer :: ivar, status_
125 
126 status_ = 1
127 geoval => null()
128 if (.not. self%lalloc .or. .not. self%linit) then
129  !return
130 endif
131 
132 ivar = ufo_vars_getindex(self%variables, varname)
133 
134 if (ivar < 0) then
135  status_ = 2
136 else
137  geoval => self%geovals(ivar)
138  status_ = 0
139 endif
140 
141 if(present(status)) then
142  status=status_
143 endif
144 end subroutine ufo_geovals_get_var
145 
146 ! ------------------------------------------------------------------------------
147 
148 subroutine ufo_geovals_allocone(self)
149 implicit none
150 type(ufo_geovals), intent(inout) :: self
151 integer :: ivar
152 
153 if (.not. self%lalloc) then
154  call abor1_ftn("ufo_geovals_zero: geovals not allocated")
155 endif
156 
157 do ivar = 1,self%nvar
158  self%geovals(ivar)%nval = 1
159  allocate(self%geovals(ivar)%vals(1,self%nobs))
160 enddo
161 self%linit = .true.
162 
163 end subroutine ufo_geovals_allocone
164 
165 ! ------------------------------------------------------------------------------
166 
167 subroutine ufo_geovals_zero(self)
168 implicit none
169 type(ufo_geovals), intent(inout) :: self
170 integer :: ivar
171 
172 if (.not. self%lalloc) then
173  call abor1_ftn("ufo_geovals_zero: geovals not allocated")
174 endif
175 if (.not. self%linit) then
176  call abor1_ftn("ufo_geovals_zero: geovals not initialized")
177 endif
178 do ivar = 1, self%nvar
179  self%geovals(ivar)%vals(:,:) = 0.0
180 enddo
181 
182 end subroutine ufo_geovals_zero
183 
184 ! ------------------------------------------------------------------------------
185 
186 subroutine ufo_geovals_abs(self)
187 implicit none
188 type(ufo_geovals), intent(inout) :: self
189 integer :: ivar
190 
191 if (.not. self%lalloc) then
192  call abor1_ftn("ufo_geovals_abs: geovals not allocated")
193 endif
194 if (.not. self%linit) then
195  call abor1_ftn("ufo_geovals_abs: geovals not initialized")
196 endif
197 do ivar = 1, self%nvar
198  self%geovals(ivar)%vals = abs(self%geovals(ivar)%vals)
199 enddo
200 
201 end subroutine ufo_geovals_abs
202 
203 ! ------------------------------------------------------------------------------
204 
205 subroutine ufo_geovals_rms(self,vrms)
206 implicit none
207 type(ufo_geovals), intent(in) :: self
208 real(kind_real), intent(inout) :: vrms
209 integer :: jv, jo
210 real(kind_real) :: n
211 
212 if (.not. self%lalloc) then
213  call abor1_ftn("ufo_geovals_rms: geovals not allocated")
214 endif
215 if (.not. self%linit) then
216  call abor1_ftn("ufo_geovals_rms: geovals not initialized")
217 endif
218 vrms=0.0_kind_real
219 n=0.0_kind_real
220 do jv = 1, self%nvar
221  do jo = 1, self%nobs
222  vrms = vrms + sum(self%geovals(jv)%vals(:,jo)**2)
223  n=n+self%geovals(jv)%nval
224  enddo
225 enddo
226 
227 vrms = sqrt(vrms/n)
228 
229 end subroutine ufo_geovals_rms
230 
231 ! ------------------------------------------------------------------------------
232 
233 subroutine ufo_geovals_random(self)
235 implicit none
236 type(ufo_geovals), intent(inout) :: self
237 integer :: ivar
238 
239 if (.not. self%lalloc) then
240  call abor1_ftn("ufo_geovals_random: geovals not allocated")
241 endif
242 if (.not. self%linit) then
243  call abor1_ftn("ufo_geovals_random: geovals not initialized")
244 endif
245 do ivar = 1, self%nvar
246  call random_vector(self%geovals(ivar)%vals)
247 enddo
248 
249 end subroutine ufo_geovals_random
250 
251 ! ------------------------------------------------------------------------------
252 
253 subroutine ufo_geovals_scalmult(self, zz)
254 implicit none
255 type(ufo_geovals), intent(inout) :: self
256 real(kind_real), intent(in) :: zz
257 integer :: jv, jo, jz
258 
259 if (.not. self%lalloc .or. .not. self%linit) then
260  call abor1_ftn("ufo_geovals_scalmult: geovals not allocated")
261 endif
262 
263 do jv=1,self%nvar
264  do jo=1,self%nobs
265  do jz = 1, self%geovals(jv)%nval
266  self%geovals(jv)%vals(jz,jo) = zz * self%geovals(jv)%vals(jz,jo)
267  enddo
268  enddo
269 enddo
270 
271 end subroutine ufo_geovals_scalmult
272 
273 ! ------------------------------------------------------------------------------
274 
275 subroutine ufo_geovals_assign(self, rhs)
276 implicit none
277 type(ufo_geovals), intent(inout) :: self
278 type(ufo_geovals), intent(in) :: rhs
279 integer :: jv, jo, jz
280 integer :: iv
281 character(max_string) :: err_msg
282 
283 if (.not. self%lalloc .or. .not. self%linit) then
284  call abor1_ftn("ufo_geovals_scalmult: geovals not allocated")
285 endif
286 if (.not. rhs%lalloc .or. .not. rhs%linit) then
287  call abor1_ftn("ufo_geovals_scalmult: geovals not allocated")
288 endif
289 
290 if (self%nobs /= rhs%nobs) then
291  call abor1_ftn("ufo_geovals_assign: nobs different between lhs and rhs")
292 endif
293 
294 do jv=1,self%nvar
295  iv = ufo_vars_getindex(rhs%variables, self%variables%fldnames(jv))
296  if (iv < 0) then
297  write(err_msg,*) 'ufo_geovals_assign: var ', trim(self%variables%fldnames(jv)), ' doesnt exist in rhs'
298  call abor1_ftn(trim(err_msg))
299  endif
300  if (self%geovals(jv)%nval /= rhs%geovals(iv)%nval) then
301  write(err_msg,*) 'ufo_geovals_assign: nvals for var ', trim(self%variables%fldnames(jv)), ' are different in lhs and rhs'
302  call abor1_ftn(trim(err_msg))
303  endif
304  do jo=1,self%nobs
305  do jz = 1, self%geovals(jv)%nval
306  self%geovals(jv)%vals(jz,jo) = rhs%geovals(iv)%vals(jz,jo)
307  enddo
308  enddo
309 enddo
310 
311 end subroutine ufo_geovals_assign
312 
313 ! ------------------------------------------------------------------------------
314 !> Sum of two GeoVaLs objects
315 
316 subroutine ufo_geovals_add(self, other)
317 implicit none
318 type(ufo_geovals), intent(inout) :: self
319 type(ufo_geovals), intent(in) :: other
320 integer :: jv, jo, jz
321 integer :: iv
322 character(max_string) :: err_msg
323 
324 if (.not. self%lalloc .or. .not. self%linit) then
325  call abor1_ftn("ufo_geovals_add: geovals not allocated")
326 endif
327 if (.not. other%lalloc .or. .not. other%linit) then
328  call abor1_ftn("ufo_geovals_add: geovals not allocated")
329 endif
330 
331 if (self%nobs /= other%nobs) then
332  call abor1_ftn("ufo_geovals_assign: nobs different between lhs and rhs")
333 endif
334 
335 do jv=1,self%nvar
336  iv = ufo_vars_getindex(other%variables, self%variables%fldnames(jv))
337  if (iv .ne. -1) then !Only add if exists in RHS
338  if (self%geovals(jv)%nval /= other%geovals(iv)%nval) then
339  write(err_msg,*) 'ufo_geovals_assign: nvals for var ', trim(self%variables%fldnames(jv)), ' are different in lhs and rhs'
340  call abor1_ftn(trim(err_msg))
341  endif
342  do jo=1,self%nobs
343  do jz = 1, self%geovals(jv)%nval
344  self%geovals(jv)%vals(jz,jo) = self%geovals(jv)%vals(jz,jo) + other%geovals(iv)%vals(jz,jo)
345  enddo
346  enddo
347  endif
348 enddo
349 
350 end subroutine ufo_geovals_add
351 
352 ! ------------------------------------------------------------------------------
353 !> Difference between two GeoVaLs objects
354 
355 subroutine ufo_geovals_diff(self, other)
356 implicit none
357 type(ufo_geovals), intent(inout) :: self
358 type(ufo_geovals), intent(in) :: other
359 integer :: jv, jo, jz
360 integer :: iv
361 character(max_string) :: err_msg
362 
363 if (.not. self%lalloc .or. .not. self%linit) then
364  call abor1_ftn("ufo_geovals_diff: geovals not allocated")
365 endif
366 if (.not. other%lalloc .or. .not. other%linit) then
367  call abor1_ftn("ufo_geovals_diff: geovals not allocated")
368 endif
369 
370 if (self%nobs /= other%nobs) then
371  call abor1_ftn("ufo_geovals_assign: nobs different between lhs and rhs")
372 endif
373 
374 do jv=1,self%nvar
375  iv = ufo_vars_getindex(other%variables, self%variables%fldnames(jv))
376  if (iv .ne. -1) then !Only subtract if exists in RHS
377  if (self%geovals(jv)%nval /= other%geovals(iv)%nval) then
378  write(err_msg,*) 'ufo_geovals_assign: nvals for var ', trim(self%variables%fldnames(jv)), ' are different in lhs and rhs'
379  call abor1_ftn(trim(err_msg))
380  endif
381  do jo=1,self%nobs
382  do jz = 1, self%geovals(jv)%nval
383  self%geovals(jv)%vals(jz,jo) = self%geovals(jv)%vals(jz,jo) - other%geovals(iv)%vals(jz,jo)
384  enddo
385  enddo
386  endif
387 enddo
388 
389 end subroutine ufo_geovals_diff
390 
391 ! ------------------------------------------------------------------------------
392 !> Copy one GeoVaLs object into another
393 !!
394 
395 subroutine ufo_geovals_copy(self, other)
396 implicit none
397 type(ufo_geovals), intent(in) :: self
398 type(ufo_geovals), intent(inout) :: other
399 integer :: jv
400 
401 if (.not. self%lalloc .or. .not. self%linit) then
402  call abor1_ftn("ufo_geovals_copy: geovals not defined")
403 endif
404 
405 call ufo_geovals_delete(other)
406 
407 call ufo_vars_clone(self%variables,other%variables)
408 
409 other%nobs = self%nobs
410 other%nvar = self%nvar
411 
412 allocate(other%geovals(other%nvar))
413 do jv = 1, other%nvar
414  other%geovals(jv)%nval = self%geovals(jv)%nval
415  other%geovals(jv)%nobs = self%geovals(jv)%nobs
416  allocate(other%geovals(jv)%vals(other%geovals(jv)%nval, other%geovals(jv)%nobs))
417  other%geovals(jv)%vals(:,:) = self%geovals(jv)%vals(:,:)
418 enddo
419 
420 other%lalloc = .true.
421 other%linit = .true.
422 
423 end subroutine ufo_geovals_copy
424 
425 ! ------------------------------------------------------------------------------
426 !> Initialize a GeoVaLs object based on an analytic state
427 !!
428 !! \details **ufo_geovals_analytic_init_c()** takes an existing ufo::GeoVaLs object
429 !! and fills in values based on one of several analytic solutions. This initialization
430 !! is intended to be used with the **TestStateInterpolation()** test; see there for
431 !! further information.
432 !!
433 !! Currently implemented options for analytic_init include:
434 !! * dcmip-test-1-1: 3D deformational flow
435 !! * dcmip-test-1-2: 3D Hadley-like meridional circulation
436 !! * dcmip-test-3-1: Non-orographic gravity waves on a small planet
437 !! * dcmip-test-4-0: Baroclinic instability
438 !!
439 !! \warning Currently only temperature is implemented. For variables other than
440 !! temperature, the input GeoVaLs object is not changed. This effectively
441 !! disables the interpolation test for that variable by setting the normalized
442 !! error to zero.
443 !!
444 !! \warning Currently there is no conversion between temperature and virtual
445 !! temperature
446 !!
447 !! \date May, 2018: Created by M. Miesch (JCSDA)
448 !! \date June, 2018: Added dcmip-test-4.0 (M. Miesch, JCSDA)
449 !!
450 !! \sa test::TestStateInterpolation()
451 !!
452 
453 subroutine ufo_geovals_analytic_init(self, locs, ic)
458 
459 implicit none
460 type(ufo_geovals), intent(inout) :: self
461 type(ioda_locs), intent(in) :: locs
462 character(*), intent(in) :: ic
463 
464 real(kind_real) :: pi = acos(-1.0_kind_real)
465 real(kind_real) :: deg_to_rad,rlat, rlon
466 real(kind_real) :: p0, kz, u0, v0, w0, t0, phis0, ps0, rho0, hum0
467 real(kind_real) :: q1, q2, q3, q4
468 integer :: ivar, iloc, ival
469 
470 if (.not. self%lalloc .or. .not. self%linit) then
471  call abor1_ftn("ufo_geovals_analytic_init: geovals not defined")
472 endif
473 
474 ! The last variable should be the ln pressure coordinate. That's
475 ! where we get the height information for the analytic init
476 if (trim(self%variables%fldnames(self%nvar)) /= trim(var_prsl)) then
477  call abor1_ftn("ufo_geovals_analytic_init: pressure coordinate not defined")
478 endif
479 
480 deg_to_rad = pi/180.0_kind_real
481 
482 do ivar = 1, self%nvar-1
483 
484  do iloc = 1, self%geovals(ivar)%nobs
485 
486  ! convert lat and lon to radians
487  rlat = deg_to_rad * locs%lat(iloc)
488  rlon = deg_to_rad*modulo(locs%lon(iloc)+180.0_kind_real,360.0_kind_real) - pi
489 
490  do ival = 1, self%geovals(ivar)%nval
491 
492  ! obtain height from the existing GeoVaLs object, which should be an
493  ! output of the State::getValues() method
494  ! convert from KPa (ufo standard) to Pa (dcmip standard)
495  p0 = exp(self%geovals(self%nvar)%vals(ival,iloc))*1.0e3_kind_real
496 
497  init_option: select case (trim(ic))
498 
499  case ("dcmip-test-1-1")
500 
501  call test1_advection_deformation(rlon,rlat,p0,kz,0,u0,v0,w0,&
502  t0,phis0,ps0,rho0,hum0,q1,q2,q3,q4)
503 
504  case ("dcmip-test-1-2")
505 
506  call test1_advection_hadley(rlon,rlat,p0,kz,0,u0,v0,w0,&
507  t0,phis0,ps0,rho0,hum0,q1)
508 
509  case ("dcmip-test-3-1")
510 
511  call test3_gravity_wave(rlon,rlat,p0,kz,0,u0,v0,w0,&
512  t0,phis0,ps0,rho0,hum0)
513 
514  case ("dcmip-test-4-0")
515 
516  call test4_baroclinic_wave(0,1.0_kind_real,rlon,rlat,p0,kz,0,u0,v0,w0,&
517  t0,phis0,ps0,rho0,hum0,q1,q2)
518 
519  case default
520 
521  call abor1_ftn("ufo_geovals_analytic_init: invalid analytic_init")
522 
523  end select init_option
524 
525  ! currently only temperture is implemented
526  if (trim(self%variables%fldnames(ivar)) == trim(var_tv)) then
527  ! Warning: we may need a conversion from temperature to
528  ! virtual temperture here
529  self%geovals(ivar)%vals(ival,iloc) = t0
530  endif
531 
532  enddo
533  enddo
534 enddo
535 
536 end subroutine ufo_geovals_analytic_init
537 
538 ! ------------------------------------------------------------------------------
539 !> Normalization of one GeoVaLs object by another
540 !!
541 !! \details This is a normalization operator that first computes the normalization
542 !! factor for each variable based on the rms amplitude of that variable across
543 !! all locations in the reference GeoVaLs object (other). Then each element of
544 !! the input GeoVals object (self) is divided by these normalization factors.
545 !! The operation is done in place. So, after execution, the input GeoVaLs
546 !! object will be nondimensional.
547 !!
548 !! \warning If the reference variable is identially zero across all
549 !! locations, then the result of this operatution is set to zero for that
550 !! variable. This is to used to bypass variables that do not have a reference
551 !! value in the State interpolation test.
552 !!
553 
554 subroutine ufo_geovals_normalize(self, other)
555 implicit none
556 type(ufo_geovals), intent(inout) :: self !> Input GeoVaLs object (LHS)
557 type(ufo_geovals), intent(in) :: other !> Reference GeoVaLs object (RHS)
558 integer :: jv, jo, jz
559 real(kind_real) :: over_nloc, vrms, norm
560 
561 if (.not. self%lalloc .or. .not. self%linit) then
562  call abor1_ftn("ufo_geovals_normalize: geovals not allocated")
563 endif
564 if (.not. other%lalloc .or. .not. other%linit) then
565  call abor1_ftn("ufo_geovals_normalize: geovals not allocated")
566 endif
567 if (self%nvar /= other%nvar) then
568  call abor1_ftn("ufo_geovals_normalize: reference geovals object must have the same variables as the original")
569 endif
570 
571 
572 do jv=1,self%nvar
573 
574  !> Compute normalization factors for the errors based on the rms amplitude of
575  !! each variable across all of the selected locations. Use the "other" GeoVaLs
576  !! object as a reference, since this may be the exact analytic answer
577 
578  over_nloc = 1.0_kind_real / &
579  (real(other%nobs,kind_real)*real(other%geovals(jv)%nval,kind_real))
580 
581  vrms = 0.0_kind_real
582  do jo = 1, other%nobs
583  do jz = 1, other%geovals(jv)%nval
584  vrms = vrms + other%geovals(jv)%vals(jz,jo)**2
585  enddo
586  enddo
587 
588  if (vrms > 0.0_kind_real) then
589  norm = 1.0_kind_real / sqrt(vrms*over_nloc)
590  else
591  norm = 0.0_kind_real
592  endif
593 
594  ! Now loop through the LHS locations to compute the normalized value
595  do jo=1,self%nobs
596  do jz = 1, self%geovals(jv)%nval
597  self%geovals(jv)%vals(jz,jo) = norm*self%geovals(jv)%vals(jz,jo)
598  enddo
599  enddo
600 enddo
601 
602 end subroutine ufo_geovals_normalize
603 
604 ! ------------------------------------------------------------------------------
605 
606 subroutine ufo_geovals_dotprod(self, other, gprod)
607 implicit none
608 real(kind_real), intent(inout) :: gprod
609 type(ufo_geovals), intent(in) :: self, other
610 integer :: ivar, iobs, ival, nval
611 real(kind_real) :: prod
612 
613 type(fckit_mpi_comm) :: f_comm
614 
615 f_comm = fckit_mpi_comm()
616 
617 if (.not. self%lalloc .or. .not. self%linit) then
618  call abor1_ftn("ufo_geovals_dotprod: geovals not allocated")
619 endif
620 
621 if (.not. other%lalloc .or. .not. other%linit) then
622  call abor1_ftn("ufo_geovals_dotprod: geovals not allocated")
623 endif
624 
625 ! just something to put in (dot product of the 1st var and 1st element in the profile
626 prod=0.0
627 do ivar = 1, self%nvar
628  nval = self%geovals(ivar)%nval
629  do ival = 1, nval
630  do iobs = 1, self%nobs
631  prod = prod + self%geovals(ivar)%vals(ival,iobs) * &
632  other%geovals(ivar)%vals(ival,iobs)
633  enddo
634  enddo
635 enddo
636 
637 !Get global dot product
638 call f_comm%allreduce(prod,gprod,fckit_mpi_sum())
639 
640 end subroutine ufo_geovals_dotprod
641 
642 ! ------------------------------------------------------------------------------
643 
644 subroutine ufo_geovals_minmaxavg(self, kobs, pmin, pmax, prms)
645 implicit none
646 integer, intent(inout) :: kobs
647 real(kind_real), intent(inout) :: pmin, pmax, prms
648 type(ufo_geovals), intent(in) :: self
649 
650 kobs = self%nobs
651 pmin=minval(self%geovals(1)%vals)
652 pmax=maxval(self%geovals(1)%vals)
653 prms=0. !sqrt(sum(self%values(:,:)**2)/real(self%nobs*self%nvar,kind_real))
654 
655 end subroutine ufo_geovals_minmaxavg
656 
657 ! ------------------------------------------------------------------------------
658 !> Location where the summed geovals value is maximum
659 !!
660 !! \details This routine computes the rms value over the vertical profile for
661 !! each location and observation then returns the location number and the
662 !! variable number where this rms value is maximum. Intended for use with
663 !! the State interpotation test in which the input GeoVaLs object is a
664 !! nondimensional, positive-definite error measurement.
665 
666 subroutine ufo_geovals_maxloc(self, mxval, iobs, ivar)
667 implicit none
668 real(kind_real), intent(inout) :: mxval
669 integer, intent(inout) :: iobs, ivar
670 
671 type(ufo_geovals), intent(in) :: self
672 real(kind_real) :: vrms
673 integer :: jv, jo, jz
674 
675 if (.not. self%lalloc .or. .not. self%linit) then
676  call abor1_ftn("ufo_geovals_maxloc: geovals not allocated")
677 endif
678 
679 mxval = 0.0_kind_real
680 iobs = 1
681 ivar = 1
682 
683 do jv = 1,self%nvar
684  do jo = 1, self%nobs
685 
686  vrms = 0.0_kind_real
687  do jz = 1, self%geovals(jv)%nval
688  vrms = vrms + self%geovals(jv)%vals(jz,jo)**2
689  enddo
690  vrms = sqrt(vrms/real(self%geovals(jv)%nval,kind_real))
691  if (vrms > mxval) then
692  mxval = vrms
693  iobs = jo
694  ivar = jv
695  endif
696 
697  enddo
698 enddo
699 
700 end subroutine ufo_geovals_maxloc
701 
702 ! ------------------------------------------------------------------------------
703 
704 subroutine ufo_geovals_read_netcdf(self, filename, vars)
705 USE netcdf, ONLY: nf90_float, nf90_double, nf90_int
706 use nc_diag_read_mod, only: nc_diag_read_get_var
707 use nc_diag_read_mod, only: nc_diag_read_get_dim
708 use nc_diag_read_mod, only: nc_diag_read_get_var_dims, nc_diag_read_check_var
709 use nc_diag_read_mod, only: nc_diag_read_get_var_type
711 
712 use ioda_utils_mod
713 
714 implicit none
715 type(ufo_geovals), intent(inout) :: self
716 character(max_string), intent(in) :: filename
717 type(ufo_vars), intent(in) :: vars
718 
719 integer :: iunit, ivar, nobs, nval, fvlen
720 integer :: nvardim, vartype
721 integer, allocatable, dimension(:) :: vardims
722 
723 real(kind_real), allocatable :: fieldr2d(:,:), fieldr1d(:)
724 real, allocatable :: fieldf2d(:,:), fieldf1d(:)
725 integer, allocatable :: fieldi2d(:,:), fieldi1d(:)
726 
727 character(max_string) :: err_msg
728 
729 type(random_distribution) :: distribution
730 integer, allocatable, dimension(:) :: dist_indx
731 
732 ! open netcdf file and read dimensions
733 call nc_diag_read_init(filename, iunit)
734 if (allocated(vardims)) deallocate(vardims)
735 call nc_diag_read_get_var_dims(iunit, vars%fldnames(1), nvardim, vardims)
736 if (nvardim .eq. 1) then
737  fvlen = vardims(1)
738 else
739  fvlen = vardims(2)
740 endif
741 
742 !> round-robin distribute the observations to PEs
743 !> Calculate how many obs. on each PE
744 distribution=random_distribution(fvlen)
745 nobs=distribution%nobs_pe()
746 
747 ! Check for missing values, use virtual_temperature if it exists in the file. This will
748 ! catch Radiosonde and Aircraft obs types which should be the only obs types at this point
749 ! with missing values. This is not a good way to do this in the long run, so this needs
750 ! to be revisited.
751 if (nc_diag_read_check_var(iunit, "virtual_temperature")) then
752  call ioda_deselect_missing_values(iunit, "virtual_temperature", distribution%indx, dist_indx)
753  nobs = size(dist_indx)
754 else
755  allocate(dist_indx(nobs))
756  dist_indx = distribution%indx
757 endif
758 
759 ! allocate geovals structure
760 call ufo_geovals_init(self)
761 call ufo_geovals_setup(self, vars, nobs)
762 
763 do ivar = 1, vars%nv
764  if (.not. nc_diag_read_check_var(iunit, vars%fldnames(ivar))) then
765  write(err_msg,*) 'ufo_geovals_read_netcdf: var ', trim(vars%fldnames(ivar)), ' doesnt exist'
766  call abor1_ftn(trim(err_msg))
767  endif
768  !> get dimensions of variable
769  if (allocated(vardims)) deallocate(vardims)
770  call nc_diag_read_get_var_dims(iunit, vars%fldnames(ivar), nvardim, vardims)
771  !> get variable type
772  vartype = nc_diag_read_get_var_type(iunit, vars%fldnames(ivar))
773  !> read 1d vars (only double precision and integer for now)
774  if (nvardim == 1) then
775  if (vardims(1) /= fvlen) call abor1_ftn('ufo_geovals_read_netcdf: var dim /= fvlen')
776  nval = 1
777 
778  !> allocate geoval for this variable
779  self%geovals(ivar)%nval = nval
780  allocate(self%geovals(ivar)%vals(nval,nobs))
781 
782  if (vartype == nf90_double) then
783  allocate(fieldr1d(vardims(1)))
784  call nc_diag_read_get_var(iunit, vars%fldnames(ivar), fieldr1d)
785  self%geovals(ivar)%vals(1,:) = fieldr1d(dist_indx)
786  deallocate(fieldr1d)
787  elseif (vartype == nf90_float) then
788  allocate(fieldf1d(vardims(1)))
789  call nc_diag_read_get_var(iunit, vars%fldnames(ivar), fieldf1d)
790  self%geovals(ivar)%vals(1,:) = dble(fieldf1d(dist_indx))
791  deallocate(fieldf1d)
792  elseif (vartype == nf90_int) then
793  allocate(fieldi1d(vardims(1)))
794  call nc_diag_read_get_var(iunit, vars%fldnames(ivar), fieldi1d)
795  self%geovals(ivar)%vals(1,:) = fieldi1d(dist_indx)
796  deallocate(fieldi1d)
797  else
798  call abor1_ftn('ufo_geovals_read_netcdf: can only read double, float and int')
799  endif
800  !> read 2d vars (only double precision and integer for now)
801  elseif (nvardim == 2) then
802  if (vardims(2) /= fvlen) call abor1_ftn('ufo_geovals_read_netcdf: var dim /= fvlen')
803  nval = vardims(1)
804 
805  !> allocate geoval for this variable
806  self%geovals(ivar)%nval = nval
807  allocate(self%geovals(ivar)%vals(nval,nobs))
808 
809  if (vartype == nf90_double) then
810  allocate(fieldr2d(vardims(1), vardims(2)))
811  call nc_diag_read_get_var(iunit, vars%fldnames(ivar), fieldr2d)
812  self%geovals(ivar)%vals = fieldr2d(:,dist_indx)
813  deallocate(fieldr2d)
814  elseif (vartype == nf90_float) then
815  allocate(fieldf2d(vardims(1), vardims(2)))
816  call nc_diag_read_get_var(iunit, vars%fldnames(ivar), fieldf2d)
817  self%geovals(ivar)%vals = dble(fieldf2d(:,dist_indx))
818  deallocate(fieldf2d)
819  elseif (vartype == nf90_int) then
820  allocate(fieldi2d(vardims(1), vardims(2)))
821  call nc_diag_read_get_var(iunit, vars%fldnames(ivar), fieldi2d)
822  self%geovals(ivar)%vals = fieldi2d(:,dist_indx)
823  deallocate(fieldi2d)
824  else
825  call abor1_ftn('ufo_geovals_read_netcdf: can only read double, float and int')
826  endif
827  !> only 1d & 2d vars
828  else
829  call abor1_ftn('ufo_geovals_read_netcdf: can only read 1d and 2d fields')
830  endif
831 enddo
832 
833 self%linit = .true.
834 
835 call nc_diag_read_close(filename)
836 
837 end subroutine ufo_geovals_read_netcdf
838 
839 ! ------------------------------------------------------------------------------
840 
841 subroutine ufo_geovals_print(self, iobs)
842 implicit none
843 type(ufo_geovals), intent(in) :: self
844 integer, intent(in) :: iobs
845 
846 type(ufo_geoval), pointer :: geoval
847 character(MAXVARLEN) :: varname
848 integer :: ivar
849 
850 do ivar = 1, self%nvar
851  varname = self%variables%fldnames(ivar)
852  call ufo_geovals_get_var(self, varname, geoval)
853  if (associated(geoval)) then
854  print *, 'geoval test: ', trim(varname), geoval%nval, geoval%vals(:,iobs)
855  else
856  print *, 'geoval test: ', trim(varname), ' doesnt exist'
857  endif
858 enddo
859 
860 end subroutine ufo_geovals_print
861 
862 ! ------------------------------------------------------------------------------
863 
864 end module ufo_geovals_mod
Fortran generic for generating random 1d, 2d and 3d arrays.
subroutine, public ufo_geovals_get_var(self, varname, geoval, status)
subroutine, public ufo_geovals_delete(self)
subroutine, public ufo_geovals_minmaxavg(self, kobs, pmin, pmax, prms)
subroutine, public ufo_geovals_add(self, other)
Sum of two GeoVaLs objects.
subroutine, public ufo_geovals_analytic_init(self, locs, ic)
Initialize a GeoVaLs object based on an analytic state.
Fortran derived type to hold observation locations.
integer function, public ufo_vars_getindex(self, varname)
subroutine, public ufo_geovals_init(self)
subroutine, public ufo_geovals_read_netcdf(self, filename, vars)
subroutine, public ufo_geovals_diff(self, other)
Difference between two GeoVaLs objects.
integer, parameter max_string
subroutine, public ufo_geovals_rms(self, vrms)
subroutine test1_advection_deformation(lon, lat, p, z, zcoords, u, v, w, t, phis, ps, rho, q, q1, q2, q3, q4)
subroutine test4_baroclinic_wave(moist, X, lon, lat, p, z, zcoords, u, v, w, t, phis, ps, rho, q, q1, q2)
subroutine, public ufo_geovals_dotprod(self, other, gprod)
Fortran derived type to represent model variables.
Fortran module containing IODA utility programs.
subroutine, public ufo_geovals_abs(self)
subroutine, public ufo_geovals_random(self)
subroutine, public ufo_geovals_zero(self)
subroutine, public ufo_geovals_allocone(self)
subroutine, public ufo_geovals_normalize(self, other)
Normalization of one GeoVaLs object by another.
character(len=maxvarlen), public var_prsl
subroutine, public ufo_geovals_maxloc(self, mxval, iobs, ivar)
Location where the summed geovals value is maximum.
type to hold interpolated fields required by the obs operators
Fortran module for generating random vectors.
subroutine test1_advection_hadley(lon, lat, p, z, zcoords, u, v, w, t, phis, ps, rho, q, q1)
subroutine nc_diag_read_close(filename, file_ncdr_id, from_pop)
subroutine test3_gravity_wave(lon, lat, p, z, zcoords, u, v, w, t, phis, ps, rho, q)
subroutine, public ufo_geovals_setup(self, vars, nobs)
Fortran module handling observation locations.
integer, parameter, public kind_real
character(len=maxvarlen), public var_tv
subroutine, public ioda_deselect_missing_values(ncid, vname, in_index, out_index)
subroutine, public ufo_geovals_print(self, iobs)
subroutine, public ufo_geovals_assign(self, rhs)
subroutine nc_diag_read_init(filename, file_ncdr_id, from_push)
subroutine, public ufo_geovals_scalmult(self, zz)
subroutine, public ufo_geovals_copy(self, other)
Copy one GeoVaLs object into another.
subroutine, public ufo_vars_clone(self, other)
type to hold interpolated field for one variable, one observation
real(fp), parameter, public pi