MPAS-JEDI
mpasjedi_lineargetvalues_mod.F90
Go to the documentation of this file.
1 ! (C) Copyright 2020 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 
7 
8 use iso_c_binding
9 
10 ! fckit
11 use fckit_mpi_module, only: fckit_mpi_sum
12 use fckit_configuration_module, only: fckit_configuration
13 use fckit_log_module, only: fckit_log
14 
15 ! oops
16 use datetime_mod, only: datetime, datetime_to_string
17 use kinds, only: kind_real
18 
19 ! saber
20 use interpolatorbump_mod, only: bump_interpolator
21 
22 ! ufo
23 use ufo_locations_mod
24 use ufo_geovals_mod, only: ufo_geovals
25 use ufo_vars_mod
26 
27 !MPAS-Model
28 use mpas_constants
29 use mpas_derived_types
30 use mpas_field_routines
31 use mpas_kind_types, only: strkind
32 use mpas_pool_routines
33 
34 !mpas-jedi
36 use mpas_geom_mod
39 use mpas4da_mod
41 
42 ! --------------------------------------------------------------------------------------------------
43 
44 implicit none
45 private
48 
50  private
51  real(kind=kind_real), allocatable :: obs_field(:,:), mod_field(:,:)
52  logical(c_bool), allocatable :: time_mask(:)
53  contains
54  procedure, public :: create
55  procedure, public :: delete
56  procedure, public :: fill_geovals_tl
57  procedure, public :: fill_geovals_ad
59 
60 integer, parameter :: max_string=8000
61 character(max_string) :: message
62 
63 #define LISTED_TYPE mpasjedi_lineargetvalues
64 
65 !> Linked list interface - defines registry_t type
66 #include <oops/util/linkedList_i.f>
67 
68 !> Global registry
69 type(registry_t) :: mpas_lineargetvalues_registry
70 
71 ! --------------------------------------------------------------------------------------------------
72 
73 contains
74 
75 ! --------------------------------------------------------------------------------------------------
76 !> Linked list implementation
77 #include <oops/util/linkedList_c.f>
78 
79 ! --------------------------------------------------------------------------------------------------
80 
81 !> \brief LinearGetValues class 'create' logic
82 !!
83 !! \details **create** This subroutine constructs an mpasjedi_lineargetvalues object
84 !! class instance.
85 subroutine create(self, geom, locs, f_conf)
86  implicit none
87  class(mpasjedi_lineargetvalues), intent(inout) :: self !< lineargetvalues self
88  type(mpas_geom), intent(in) :: geom !< geometry (mpas mesh)
89  type(ufo_locations), intent(in) :: locs !< ufo geovals (obs) locations
90  type(fckit_configuration), intent(in) :: f_conf !< configuration
91 
92  integer :: nlocs, maxlevels
93 
94  call getvalues_base_create(self, geom, locs, f_conf)
95 
96  ! Get grid dimensions
97  ! -------------------
98  nlocs = locs % nlocs() ! # of location for entire window
99  maxlevels = geom%nVertLevelsP1
100 
101  if (allocated(self%time_mask) .or. allocated(self%obs_field) .or. allocated(self%mod_field)) then
102  ! If we're in here this subroutine has not been called as it is intended and someone should know.
103  call abor1_ftn('--> mpasjedi_lineargetvalues::create subroutine called when member arrays already allocated.')
104  end if
105 
106  ! These working arrays, used in fill_geovals_tl and fill_geovals_ad, are not repeatedly allocated
107  ! and deallocated during the inner minimization loop by instead allocating them here.
108  ! They are deallocated in the delete subroutine.
109  allocate(self%time_mask(nlocs))
110  allocate(self%obs_field(nlocs,maxlevels))
111  if (.not. self%use_bump_interp) then
112  allocate(self%mod_field(geom%nCellsSolve, maxlevels))
113  end if
114 
115 end subroutine create
116 
117 
118 ! --------------------------------------------------------------------------------------------------
119 
120 !> \brief LinearGetValues class 'delete' logic
121 !!
122 !! \details **delete** This subroutine deletes (frees memory) for an mpas_jedilineargetvalues object
123 !! class instance.
124 subroutine delete(self)
125  implicit none
126  class(mpasjedi_lineargetvalues), intent(inout) :: self !< lineargetvalues self
127 
128  if (allocated(self%time_mask)) deallocate(self%time_mask)
129  if (allocated(self%obs_field)) deallocate(self%obs_field)
130  if (allocated(self%mod_field)) deallocate(self%mod_field)
131  call getvalues_base_delete(self)
132 
133 end subroutine delete
134 
135 ! --------------------------------------------------------------------------------------------------
136 
137 subroutine fill_geovals_tl(self, geom, inc, t1, t2, locs, gom)
138  implicit none
139  class(mpasjedi_lineargetvalues), intent(inout) :: self !< lineargetvalues self
140  type(mpas_geom), intent(in) :: geom !< geometry (mpas mesh)
141  class(mpas_fields), intent(in) :: inc !< increment containing geovars
142  type(datetime), intent(in) :: t1 !< time window begin
143  type(datetime), intent(in) :: t2 !< time window end
144  type(ufo_locations), intent(in) :: locs !< observation locations
145  type(ufo_geovals), intent(inout) :: gom !< geovals
146 
147  character(len=*), parameter :: myname = 'fill_geovals_tl'
148 
149  integer :: jvar, jlev, ilev, jloc, nDims
150  integer :: nCells, nlevels, nlocs, nlocsg
151 
152  type (mpas_pool_data_type), pointer :: gdata
153  type (mpas_pool_iterator_type) :: poolItr
154  character (len=MAXVARLEN) :: geovar
155 
156  logical :: allocateGeo
157 
158  ! Get grid dimensions and checks
159  ! ------------------------------
160  ncells = geom % nCellsSolve
161  nlocs = locs % nlocs() ! # of location for entire window
162 
163  ! If no observations can early exit
164  ! ---------------------------------
165  call geom%f_comm%allreduce(nlocs,nlocsg,fckit_mpi_sum())
166  if (nlocsg == 0) then
167  return
168  endif
169 
170  ! Get mask for locations in this time window
171  ! ------------------------------------------
172  call locs%get_timemask(t1, t2, self%time_mask)
173 
174  ! TL of interpolate fields to obs locations using pre-calculated weights
175  ! ----------------------------------------------------------------------
176  call mpas_pool_begin_iteration(inc%subFields)
177  do while ( mpas_pool_get_next_member(inc%subFields, poolitr) )
178  if (poolitr % memberType == mpas_pool_field) then
179 
180  geovar = trim(poolitr % memberName)
181  ndims = poolitr % nDims
182  call inc%get(geovar, gdata)
183 
184  jvar = ufo_vars_getindex(gom%variables, geovar)
185  if ( jvar < 1 ) cycle
186 
187  self%obs_field = mpas_jedi_zero_kr
188 
189  nlevels = gom%geovals(jvar)%nval
190 
191  if (poolitr % dataType == mpas_pool_real) then
192  if (ndims == 1) then
193  if (self%use_bump_interp) then
194  call self%bumpinterp%apply(gdata%r1%array(1:ncells), &
195  self%obs_field(:,1))
196  else
197  call self%unsinterp%apply(gdata%r1%array(1:ncells), &
198  self%obs_field(:,1))
199  endif
200  else if (ndims == 2) then
201  if (self%use_bump_interp) then
202  call self%bumpinterp%apply(gdata%r2%array(1:nlevels,1:ncells), &
203  self%obs_field(:,1:nlevels), &
204  trans_in=.true.)
205  else
206  ! Transpose to get the slices we pass to 'apply' contiguous in memory
207  self%mod_field(:,1:nlevels) = transpose(gdata%r2%array(1:nlevels,1:ncells))
208  do jlev = 1, nlevels
209  call self%unsinterp%apply(self%mod_field(:,jlev), &
210  self%obs_field(:,jlev))
211  enddo
212  endif
213  end if
214  else
215  call abor1_ftn('--> fill_geovals_tl: not a real field')
216  end if
217 
218  do jlev = 1, nlevels
219  ilev = nlevels - jlev + 1
220  do jloc = 1, nlocs
221  !BJJ-tmp vertical flip, top-to-bottom for CRTM geoval
222  if (self%time_mask(jloc)) gom%geovals(jvar)%vals(ilev,jloc) = self%obs_field(jloc,jlev)
223  end do
224  end do
225  endif
226  end do
227 
228 end subroutine fill_geovals_tl
229 
230 subroutine fill_geovals_ad(self, geom, inc, t1, t2, locs, gom)
231  implicit none
232  class(mpasjedi_lineargetvalues), intent(inout) :: self !< lineargetvalues self
233  type(mpas_geom), intent(in) :: geom !< geometry (mpas mesh)
234  class(mpas_fields), intent(inout) :: inc !< increment containing geovars
235  type(datetime), intent(in) :: t1 !< time window begin
236  type(datetime), intent(in) :: t2 !< time window end
237  type(ufo_locations), intent(in) :: locs !< observation locations
238  type(ufo_geovals), intent(in) :: gom !< geovals
239 
240  character(len=*), parameter :: myname = 'fill_geovals_ad'
241 
242  integer :: jvar, jlev, ilev, jloc, nDims
243  integer :: nCells, nlevels, nlocs, nlocsg
244 
245  type (mpas_pool_data_type), pointer :: gdata
246  type (mpas_pool_iterator_type) :: poolItr
247  character (len=MAXVARLEN) :: geovar
248 
249  ! Get grid dimensions and checks
250  ! ------------------------------
251  ncells = geom % nCellsSolve
252  nlocs = locs % nlocs() ! # of location for entire window
253  write(message,*) 'fill_geovals_ad: nlocs : ',nlocs
254  call fckit_log%debug(message)
255 
256  ! Get mask for locations in this time window
257  ! ------------------------------------------
258  call locs%get_timemask(t1, t2, self%time_mask)
259 
260  ! If no observations can early exit
261  ! ---------------------------------
262  call geom%f_comm%allreduce(nlocs,nlocsg,fckit_mpi_sum())
263  if (nlocsg == 0) then
264  return
265  endif
266 
267  ! zero out adjoint geovar fields
268  call inc%zeros()
269 
270  ! Adjoint of interpolate fields to obs locations using pre-calculated weights
271  ! ---------------------------------------------------------------------------
272  call mpas_pool_begin_iteration(inc%subFields)
273  do while ( mpas_pool_get_next_member(inc%subFields, poolitr) )
274  if (poolitr % memberType == mpas_pool_field) then
275 
276  geovar = trim(poolitr % memberName)
277  ndims = poolitr % nDims
278  call inc%get(geovar, gdata)
279 
280  jvar = ufo_vars_getindex(gom%variables, geovar)
281  if ( jvar < 1 ) cycle
282 
283  self%obs_field = mpas_jedi_zero_kr
284 
285  write(message,*) 'fill_geovals_ad: nDims, geovar =', ndims , geovar
286  call fckit_log%debug(message)
287  nlevels = gom%geovals(jvar)%nval
288  do jlev = 1, nlevels
289  !ORG- obs_field(:,jlev) = gom%geovals(jvar)%vals(jlev,:)
290  !BJJ-tmp vertical flip, top-to-bottom for CRTM geoval
291  ilev = nlevels - jlev + 1
292  do jloc = 1, nlocs
293  if (self%time_mask(jloc)) then
294  self%obs_field(jloc,jlev) = gom%geovals(jvar)%vals(ilev, jloc)
295  endif
296  end do
297  end do
298  if (poolitr % dataType == mpas_pool_real) then
299  if (self%use_bump_interp) then
300  if (ndims == 1) then
301  call self%bumpinterp%apply_ad(self%obs_field(:,1), &
302  gdata%r1%array(1:ncells))
303  else if (ndims == 2) then
304  call self%bumpinterp%apply_ad(self%obs_field(:,1:nlevels), &
305  gdata%r2%array(1:nlevels,1:ncells), &
306  trans_in=.true.)
307  end if
308  else ! Unstructured interpolation
309  do jlev = 1, nlevels
310  call self%unsinterp%apply_ad(self%mod_field(:,jlev), &
311  self%obs_field(:,jlev))
312  end do
313  if (ndims == 1) then
314  gdata%r1%array(1:ncells) = self%mod_field(:,1)
315  else if (ndims == 2) then
316  gdata%r2%array(1:nlevels,1:ncells) = transpose(self%mod_field(:,1:nlevels))
317  end if
318  endif ! self%use_bump_interp
319  else
320  call abor1_ftn('--> fill_geovals_ad: not a real field')
321  end if
322 
323  endif ! poolItr % memberType == MPAS_POOL_FIELD
324  end do
325 
326 end subroutine fill_geovals_ad
327 
real(kind=kind_real), parameter mpas_jedi_zero_kr
subroutine create(self, geom, locs, f_conf)
GetValues class 'create' logic.
subroutine, public getvalues_base_create(self, geom, locs, f_conf)
Linked list implementation.
subroutine, public getvalues_base_delete(self)
GetValues base class 'delete' logic.
subroutine delete(self)
GetValues class 'delete' logic.
subroutine fill_geovals_ad(self, geom, inc, t1, t2, locs, gom)
type(registry_t), public mpas_lineargetvalues_registry
Linked list interface - defines registry_t type.
subroutine fill_geovals_tl(self, geom, inc, t1, t2, locs, gom)
Fortran derived type to hold MPAS field.
Fortran derived type to hold geometry definition.