SOCA
soca_getvalues_mod.F90
Go to the documentation of this file.
1 ! (C) Copyright 2020-2021 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 !> Getvalues module
8 
9 use datetime_mod, only: datetime
10 use iso_c_binding
11 use kinds, only: kind_real
12 use ufo_geovals_mod, only: ufo_geovals
13 use ufo_locations_mod, only: ufo_locations
14 use unstructured_interpolation_mod, only: unstrc_interp
15 
16 ! soca modules
18 use soca_geom_mod, only: soca_geom
19 
20 implicit none
21 private
22 
23 !------------------------------------------------------------------------------
24 !> Interpolation between model and observation locations.
25 !!
26 !! Several interpolators need to be created depending on which grid is used
27 !! (h, u, v) and if land masking is used. Since we do not know this information
28 !! until fill_geovals() or fill_geovals_ad() is called, creation of the interp
29 !! is postoned to then
30 type, public :: soca_getvalues
31 
32  !> the interpolators
33  type(unstrc_interp), allocatable :: horiz_interp(:)
34 
35  !> a flag for whether each horiz_interp interpolator has been initialized yet.
36  logical, allocatable :: horiz_interp_init(:)
37 
38 contains
39 
40  !> \name constructors / destructors
41  !! \{
42 
43  !> \copybrief soca_getvalues_create \see soca_getvalues_create
44  procedure :: create => soca_getvalues_create
45 
46  !> \copybrief soca_getvalues_delete \see soca_getvalues_delete
47  procedure :: delete => soca_getvalues_delete
48 
49  !> \}
50 
51  !> \name apply interpolation
52  !! \{
53 
54  !> \copybrief soca_getvalues_getinterp \see soca_getvalues_getinterp
55  procedure :: get_interp => soca_getvalues_getinterp
56 
57  !> \copybrief soca_getvalues_fillgeovals \see soca_getvalues_fillgeovals
58  procedure :: fill_geovals=> soca_getvalues_fillgeovals
59 
60  !> \copybrief soca_getvalues_fillgeovals_ad \see soca_getvalues_fillgeovals_ad
61  procedure :: fill_geovals_ad=> soca_getvalues_fillgeovals_ad
62 
63  !> \}
64 
65 end type
66 
67 
68 !------------------------------------------------------------------------------
69 contains
70 !------------------------------------------------------------------------------
71 
72 
73 !------------------------------------------------------------------------------
74 !> Initialize getvalues
75 !!
76 !! \relates soca_getvalues_mod::soca_getvalues
77 subroutine soca_getvalues_create(self, geom, locs)
78  class(soca_getvalues), intent(inout) :: self
79  type(soca_geom), intent(in) :: geom !< remove this, not used?
80  type(ufo_locations), intent(in) :: locs !< remove this, not used?
81 
82  ! why do things crash if I don't make these allocatable??
83  allocate(self%horiz_interp(6))
84  allocate(self%horiz_interp_init(6))
85  self%horiz_interp_init = .false.
86 
87 end subroutine soca_getvalues_create
88 
89 
90 !------------------------------------------------------------------------------
91 !> Get the index of the interpolator for the given grid/masking.
92 !!
93 !! If the interpolator has not been initialized yet, it will initialize it.
94 !! The index of horiz_interp and horiz_interp_init map to the following
95 !! 1 = h, unmasked 2 = h, masked
96 !! 3 = u, unmasked 4 = u, masked
97 !! 5 = v, unmasked 6 = v, masked
98 !!
99 !! \throws abor1_ftn aborts if illegal choice of \p grid is given
100 !! \relates soca_getvalues_mod::soca_getvalues
101 function soca_getvalues_getinterp(self, geom, grid, masked, locs) result(idx)
102  class(soca_getvalues), intent(inout) :: self
103  type(soca_geom), target, intent(in) :: geom !< source geometry
104  character(len=1), intent(in) :: grid !< "h", "u", or "v"
105  logical, intent(in) :: masked !< if true, use the masked interpolators
106  type(ufo_locations), intent(in) :: locs !< locations to interpolate to
107 
108  integer :: idx
109  integer :: isc, iec, jsc, jec
110  integer :: ngrid_in, ngrid_out
111 
112  real(8), allocatable, dimension(:) :: locs_lons, locs_lats
113  real(kind=kind_real), allocatable :: lats_in(:), lons_in(:)
114 
115  real(kind=kind_real), pointer :: mask(:,:) => null() !< field mask
116  real(kind=kind_real), pointer :: lon(:,:) => null() !< field lon
117  real(kind=kind_real), pointer :: lat(:,:) => null() !< field lat
118 
119  ! interpolation parameters
120  integer :: nn = 3
121  character(len=8) :: wtype = 'barycent'
122 
123  ! get the model lat/lon/interpolator depending on grid type
124  select case(grid)
125  case ('h')
126  idx = 1
127  lon => geom%lon
128  lat => geom%lat
129  mask => geom%mask2d
130  case ('u')
131  idx = 3
132  lon => geom%lonu
133  lat => geom%latu
134  mask => geom%mask2du
135  case ('v')
136  idx = 5
137  lon => geom%lonv
138  lat => geom%latv
139  mask => geom%mask2dv
140  case default
141  call abor1_ftn('error in soca_getvalues_setupinterp. grid: '//grid)
142  end select
143  if (masked) idx = idx + 1
144 
145  ! has interpolation already been initialized? if so return.
146  if (self%horiz_interp_init(idx)) return
147 
148  ! Indices for compute domain (no halo)
149  isc = geom%isc ; iec = geom%iec
150  jsc = geom%jsc ; jec = geom%jec
151 
152  ! get location lat/lons
153  ngrid_out = locs%nlocs()
154  allocate(locs_lons(ngrid_out), locs_lats(ngrid_out))
155  call locs%get_lons(locs_lons)
156  call locs%get_lats(locs_lats)
157 
158  if ( .not. masked ) then
159  ! create interpolation weights for fields that do NOT use the land mask
160  ngrid_in = (iec - isc + 1) * (jec - jsc + 1)
161  allocate(lats_in(ngrid_in), lons_in(ngrid_in))
162  lons_in = reshape(lon(isc:iec,jsc:jec), (/ngrid_in/))
163  lats_in = reshape(lat(isc:iec,jsc:jec), (/ngrid_in/))
164  else
165  ! create interpolation weights for fields that DO use the land mask
166  ngrid_in = count(mask(isc:iec,jsc:jec) > 0)
167  allocate(lats_in(ngrid_in), lons_in(ngrid_in))
168  lons_in = pack(lon(isc:iec,jsc:jec), mask=mask(isc:iec,jsc:jec) > 0)
169  lats_in = pack(lat(isc:iec,jsc:jec), mask=mask(isc:iec,jsc:jec) > 0)
170  end if
171 
172  call self%horiz_interp(idx)%create(geom%f_comm, nn, wtype, &
173  ngrid_in, lats_in, lons_in, &
174  ngrid_out, locs_lats, locs_lons)
175  self%horiz_interp_init(idx) = .true.
176 
177 end function
178 
179 
180 !------------------------------------------------------------------------------
181 !> Destructor
182 !!
183 !! \relates soca_getvalues_mod::soca_getvalues
184 subroutine soca_getvalues_delete(self)
185  class(soca_getvalues), intent(inout) :: self
186 
187  deallocate(self%horiz_interp)
188  deallocate(self%horiz_interp_init)
189 end subroutine soca_getvalues_delete
190 
191 
192 !------------------------------------------------------------------------------
193 !> Forward interpolation from \p geom to \p locs
194 !!
195 !! only locations in \p locs that are valid in the time window between
196 !! \p t1 and \p t2 are populated.
197 !! \relates soca_getvalues_mod::soca_getvalues
198 subroutine soca_getvalues_fillgeovals(self, geom, fld, t1, t2, locs, geovals)
199  class(soca_getvalues), intent(inout) :: self
200  type(soca_geom), intent(in) :: geom !< source grid to interp from
201  class(soca_fields), intent(in) :: fld !< the field to interpolate
202  type(datetime), intent(in) :: t1 !< beginning of time window
203  type(datetime), intent(in) :: t2 !< ending of time window
204  type(ufo_locations), intent(in) :: locs !< locations to interpolate to
205  type(ufo_geovals), intent(inout) :: geovals !< output interpolated values
206 
207  logical(c_bool), allocatable :: time_mask(:)
208  logical :: masked
209  integer :: ivar, nlocs, n
210  integer :: ival, nval, indx
211  integer :: isc, iec, jsc, jec
212  integer :: ns
213  real(kind=kind_real), allocatable :: gom_window(:)
214  real(kind=kind_real), allocatable :: fld3d(:,:,:), fld3d_un(:)
215  type(soca_field), pointer :: fldptr
216  integer :: interp_idx = -1
217 
218  ! Indices for compute domain (no halo)
219  isc = geom%isc ; iec = geom%iec
220  jsc = geom%jsc ; jec = geom%jec
221 
222  ! Get mask for locations in this time window
223  allocate(time_mask(locs%nlocs()))
224  call locs%get_timemask(t1,t2,time_mask)
225 
226  ! Allocate temporary geoval and 3d field for the current time window
227  do ivar = 1, geovals%nvar
228 
229  call fld%get(geovals%variables(ivar), fldptr)
230  if (fldptr%metadata%dummy_atm) cycle ! TODO remove this hack
231  nval = fldptr%nz
232 
233  ! Return if no observations
234  if ( geovals%geovals(ivar)%nlocs == 0 ) return
235 
236  allocate(gom_window(locs%nlocs()))
237  allocate(fld3d(isc:iec,jsc:jec,1:nval))
238 
239  masked = fldptr%metadata%masked
240  fld3d = fldptr%val(isc:iec,jsc:jec,1:nval)
241 
242  ! Apply forward interpolation: Model ---> Obs
243  interp_idx = self%get_interp(geom, fldptr%metadata%grid, masked, locs)
244  do ival = 1, nval
245 
246  if (masked) then
247  ns = count(fldptr%mask(isc:iec,jsc:jec) > 0 )
248  if (.not. allocated(fld3d_un)) allocate(fld3d_un(ns))
249  fld3d_un = pack(fld3d(isc:iec,jsc:jec,ival), mask=fldptr%mask(isc:iec,jsc:jec) > 0)
250  else
251  ns = (iec - isc + 1) * (jec - jsc + 1)
252  if (.not. allocated(fld3d_un)) allocate(fld3d_un(ns))
253  fld3d_un = reshape(fld3d(isc:iec,jsc:jec,ival), (/ns/))
254  end if
255  call self%horiz_interp(interp_idx)%apply(fld3d_un(1:ns), gom_window)
256 
257  ! Fill proper geoval according to time window
258  do indx = 1, locs%nlocs()
259  if (time_mask(indx)) then
260  geovals%geovals(ivar)%vals(ival, indx) = gom_window(indx)
261  end if
262  end do
263  end do
264 
265  ! Deallocate temporary arrays
266  deallocate(fld3d_un)
267  deallocate(fld3d)
268  deallocate(gom_window)
269  end do
270 
271  ! If we reach this point, geovals has been initialized
272  geovals%linit = .true.
273 end subroutine soca_getvalues_fillgeovals
274 
275 
276 !------------------------------------------------------------------------------
277 !> Backward interpolation from \p locs to \p geom
278 !!
279 !! only locations in \p locs that are valid in the time window between
280 !! \p t1 and \p t2 are interpolated to \p incr.
281 !! \relates soca_getvalues_mod::soca_getvalues
282 subroutine soca_getvalues_fillgeovals_ad(self, geom, incr, t1, t2, locs, geovals)
283  class(soca_getvalues), intent(inout) :: self
284  type(soca_geom), intent(in) :: geom !< target geometry
285  class(soca_fields), intent(inout) :: incr !< outout interpolated values
286  type(datetime), intent(in) :: t1 !< beginning of time window
287  type(datetime), intent(in) :: t2 !< ending of time window
288  type(ufo_locations), intent(in) :: locs !< source locations that are interpolated from
289  type(ufo_geovals), intent(in) :: geovals !< input values
290 
291  logical(c_bool), allocatable :: time_mask(:)
292  logical :: masked
293  integer :: ivar, nlocs, n
294  integer :: ival, nval, indx
295  integer :: ni, nj
296  integer :: isc, iec, jsc, jec
297  integer :: ns
298  real(kind=kind_real), allocatable :: gom_window(:,:)
299  real(kind=kind_real), allocatable :: gom_window_ival(:)
300  real(kind=kind_real), allocatable :: incr3d(:,:,:), incr3d_un(:)
301  type(soca_field), pointer :: field
302  logical :: found
303  integer :: interp_idx = -1
304 
305  ! Indices for compute domain (no halo)
306  isc = geom%isc ; iec = geom%iec
307  jsc = geom%jsc ; jec = geom%jec
308 
309  ! Get mask for locations in this time window
310  allocate(time_mask(locs%nlocs()))
311  call locs%get_timemask(t1,t2,time_mask)
312  allocate(gom_window_ival(locs%nlocs()))
313 
314  do ivar = 1, geovals%nvar
315  call incr%get(geovals%variables(ivar), field)
316  nval = field%nz
317 
318  ! Allocate temporary geoval and 3d field for the current time window
319  allocate(gom_window(nval,locs%nlocs()))
320  allocate(incr3d(isc:iec,jsc:jec,1:nval))
321  incr3d = 0.0_kind_real
322  gom_window = 0.0_kind_real
323 
324  ! determine if this variable should use the masked grid
325  masked = field%metadata%masked
326 
327  ! Apply backward interpolation: Obs ---> Model
328  if (masked) then
329  ns = count(field%mask(isc:iec,jsc:jec) > 0)
330  else
331  ni = iec - isc + 1
332  nj = jec - jsc + 1
333  ns = ni * nj
334  end if
335  if (.not.allocated(incr3d_un)) allocate(incr3d_un(ns))
336 
337  interp_idx = self%get_interp(geom, field%metadata%grid, masked, locs)
338  do ival = 1, nval
339  ! Fill proper geoval according to time window
340  do indx = 1, locs%nlocs()
341  if (time_mask(indx)) then
342  gom_window(ival, indx) = geovals%geovals(ivar)%vals(ival, indx)
343  end if
344  end do
345  gom_window_ival = gom_window(ival,1:locs%nlocs())
346 
347  if (masked) then
348  incr3d_un = pack(incr3d(isc:iec,jsc:jec,ival), mask = field%mask(isc:iec,jsc:jec) >0)
349  call self%horiz_interp(interp_idx)%apply_ad(incr3d_un, gom_window_ival)
350  incr3d(isc:iec,jsc:jec,ival) = unpack(incr3d_un, &
351  mask = field%mask(isc:iec,jsc:jec) >0, &
352  field = incr3d(isc:iec,jsc:jec,ival))
353  else
354  incr3d_un = reshape(incr3d(isc:iec,jsc:jec,ival), (/ns/))
355  call self%horiz_interp(interp_idx)%apply_ad(incr3d_un(1:ns), gom_window_ival)
356  incr3d(isc:iec,jsc:jec,ival) = reshape(incr3d_un(1:ns),(/ni,nj/))
357  end if
358  end do
359 
360  field%val(isc:iec, jsc:jec, 1:nval) = field%val(isc:iec, jsc:jec, 1:nval) + &
361  incr3d(isc:iec, jsc:jec, 1:nval)
362 
363  ! Deallocate temporary arrays
364  deallocate(incr3d)
365  deallocate(gom_window)
366 
367  end do
368 
369  deallocate(gom_window_ival)
370 
371 end subroutine soca_getvalues_fillgeovals_ad
372 
373 end module soca_getvalues_mod
Handle fields for the model.
Geometry module.
Getvalues module.
Holds all data and metadata related to a single field variable.
A collection of soca_field types representing a collective state or increment.
Geometry data structure.
Interpolation between model and observation locations.