SOCA
soca_increment_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 !> Increment fields
8 
9 use atlas_module, only: atlas_fieldset, atlas_field, atlas_real
10 use fckit_configuration_module, only: fckit_configuration
11 use kinds, only: kind_real
12 use oops_variables_mod, only: oops_variables
13 use random_mod, only: normal_distribution
14 
15 ! soca modules
19 use soca_geom_mod, only : soca_geom
20 
21 
22 implicit none
23 private
24 
25 !-------------------------------------------------------------------------------
26 !> Increment fields.
27 !!
28 !! Any procedures that are shared with soca_state are implemented
29 !! in the soca_fields base class
30 type, public, extends(soca_fields) :: soca_increment
31 
32 contains
33  !> \name get/set for a single point
34  !! \{
35 
36  !> \copybrief soca_increment_getpoint \see soca_increment_getpoint
37  procedure :: getpoint => soca_increment_getpoint
38 
39  !> \copybrief soca_increment_setpoint \see soca_increment_setpoint
40  procedure :: setpoint => soca_increment_setpoint
41 
42  !> \}
43 
44 
45  !> \name atlas I/O
46  !! \{
47 
48  !> \copybrief soca_increment_set_atlas \see soca_increment_set_atlas
49  procedure :: set_atlas => soca_increment_set_atlas
50 
51  !> \copybrief soca_increment_to_atlas \see soca_increment_to_atlas
52  procedure :: to_atlas => soca_increment_to_atlas
53 
54  !> \copybrief soca_increment_from_atlas \see soca_increment_from_atlas
55  procedure :: from_atlas => soca_increment_from_atlas
56 
57  !> \}
58 
59 
60  !> \name math operators
61  !! \{
62 
63  !> \copybrief soca_increment_dirac \see soca_increment_dirac
64  procedure :: dirac => soca_increment_dirac
65 
66  !> \copybrief soca_increment_random \see soca_increment_random
67  procedure :: random => soca_increment_random
68 
69  !> \copybrief soca_increment_schur \see soca_increment_schur
70  procedure :: schur => soca_increment_schur
71 
72  !> \}
73 
74 
75  !> \copybrief soca_increment_change_resol \see soca_increment_change_resol
76  procedure :: convert => soca_increment_change_resol
77 
78 end type
79 
80 
81 ! ------------------------------------------------------------------------------
82 contains
83 ! ------------------------------------------------------------------------------
84 
85 
86 ! ------------------------------------------------------------------------------
87 !> initialize fields with random normal distribution
88 !!
89 !! \note "hocn" field, if present, is NOT randomized, because doing so
90 !! causes problems
91 !! \relates soca_increment_mod::soca_increment
92 subroutine soca_increment_random(self)
93  class(soca_increment), intent(inout) :: self
94 
95  integer, parameter :: rseed = 1 ! constant for reproducability of tests
96  ! NOTE: random seeds are not quite working the way expected,
97  ! it is only set the first time normal_distribution() is called with a seed
98  integer :: jz, i
99 
100  type(soca_field), pointer :: field
101 
102  ! set random values
103  do i = 1, size(self%fields)
104  field => self%fields(i)
105  ! TODO remove this once increment / state are fully separated
106  ! NOTE: can't randomize "hocn", testIncrementInterpAD fails
107  if (field%name == 'hocn') cycle
108  call normal_distribution(field%val, 0.0_kind_real, 1.0_kind_real, rseed)
109  end do
110 
111  ! mask out land, set to zero
112  do i=1,size(self%fields)
113  field => self%fields(i)
114  if (.not. associated(field%mask) ) cycle
115  do jz=1,field%nz
116  field%val(:,:,jz) = field%val(:,:,jz) * field%mask(:,:)
117  end do
118  end do
119 
120  ! update domains
121  call self%update_halos()
122 end subroutine soca_increment_random
123 
124 
125 ! ------------------------------------------------------------------------------
126 !> perform a shur product between two sets of fields
127 !!
128 !! \relates soca_increment_mod::soca_increment
129 subroutine soca_increment_schur(self,rhs)
130  class(soca_increment), intent(inout) :: self
131  class(soca_increment), intent(in) :: rhs !< other incrment in schur product
132  integer :: i
133 
134  ! make sure fields are same shape
135  call self%check_congruent(rhs)
136 
137  ! schur product
138  do i=1,size(self%fields)
139  self%fields(i)%val = self%fields(i)%val * rhs%fields(i)%val
140  end do
141 end subroutine soca_increment_schur
142 
143 
144 ! ------------------------------------------------------------------------------
145 !> Get the values at a specific grid point
146 !!
147 !! \todo clean this up so that the variable names are not hardcoded
148 !! \relates soca_increment_mod::soca_increment
149 subroutine soca_increment_getpoint(self, geoiter, values)
150  class(soca_increment), intent( in) :: self
151  type(soca_geom_iter), intent( in) :: geoiter !< iterator pointing to desired gridpoint
152  !> return values for every field in a vertical column
153  real(kind=kind_real), intent(inout) :: values(:)
154 
155  integer :: ff, ii, nz
156  type(soca_field), pointer :: field
157 
158  ! get values
159  ! TODO generalize field names
160  ii = 0
161  do ff = 1, size(self%fields)
162  field => self%fields(ff)
163  select case(field%name)
164  case("tocn", "socn", "ssh", "uocn", "vocn", "hocn", "cicen", "hicen", "hsnon", "chl", "biop")
165  nz = field%nz
166  values(ii+1:ii+nz) = field%val(geoiter%iind, geoiter%jind,:)
167  ii = ii + nz
168  end select
169  end do
170 end subroutine soca_increment_getpoint
171 
172 
173 ! ------------------------------------------------------------------------------
174 !> Set the values at a specific grid point
175 !!
176 !! \todo need to remove the hardcoded variable names
177 !! \relates soca_increment_mod::soca_increment
178 subroutine soca_increment_setpoint(self, geoiter, values)
179  class(soca_increment), intent(inout) :: self
180  type(soca_geom_iter), intent( in) :: geoiter !< iterator pointing to desired gridpoint
181  !> values to set. Values are for for every field in a vertical column
182  real(kind=kind_real), intent( in) :: values(:)
183 
184  integer :: ff, ii, nz
185  type(soca_field), pointer :: field
186 
187  ! Set values
188  ! TODO generalize field names
189  ii = 0
190  do ff = 1, size(self%fields)
191  field => self%fields(ff)
192  select case(field%name)
193  case("tocn", "socn", "ssh", "uocn", "vocn", "hocn", "cicen", "hicen", "hsnon", "chl", "biop")
194  nz = field%nz
195  field%val(geoiter%iind, geoiter%jind,:) = values(ii+1:ii+nz)
196  ii = ii + nz
197  end select
198  end do
199 end subroutine soca_increment_setpoint
200 
201 
202 ! ------------------------------------------------------------------------------
203 !> Apply a dirac increment
204 !!
205 !! \raises abor1_ftn aborts if there is an error in the input configuration
206 !! \todo generalize by removing the hardcoded int=>field_name
207 !! \relates soca_increment_mod::soca_increment
208 subroutine soca_increment_dirac(self, f_conf)
209  class(soca_increment), intent(inout) :: self
210  type(fckit_configuration), value, intent(in):: f_conf !< Configuration
211 
212  integer :: isc, iec, jsc, jec
213  integer :: ndir,n, jz
214  integer,allocatable :: ixdir(:),iydir(:),izdir(:),ifdir(:)
215 
216  type(soca_field), pointer :: field
217 
218  ! Get Diracs size
219  ndir = f_conf%get_size("ixdir")
220  if (( f_conf%get_size("iydir") /= ndir ) .or. &
221  ( f_conf%get_size("izdir") /= ndir ) .or. &
222  ( f_conf%get_size("ifdir") /= ndir )) &
223  call abor1_ftn('soca_fields_dirac: inconsistent sizes for ixdir, iydir, izdir, and ifdir')
224 
225  ! Allocation
226  allocate(ixdir(ndir))
227  allocate(iydir(ndir))
228  allocate(izdir(ndir))
229  allocate(ifdir(ndir))
230 
231  ! Get Diracs positions
232  call f_conf%get_or_die("ixdir", ixdir)
233  call f_conf%get_or_die("iydir", iydir)
234  call f_conf%get_or_die("izdir", izdir)
235  call f_conf%get_or_die("ifdir", ifdir)
236 
237  ! get PE domain bounds
238  isc = self%geom%isc ; iec = self%geom%iec
239  jsc = self%geom%jsc ; jec = self%geom%jec
240 
241  ! Setup Diracs
242  call self%zeros()
243  do n=1,ndir
244  ! skip this index if not in the bounds of this PE
245  if (ixdir(n) > iec .or. ixdir(n) < isc) cycle
246  if (iydir(n) > jec .or. iydir(n) < jsc) cycle
247 
248  field => null()
249  select case(ifdir(n))
250  case (1)
251  call self%get("tocn", field)
252  case (2)
253  call self%get("socn", field)
254  case (3)
255  call self%get("ssh", field)
256  case (4)
257  call self%get("cicen", field)
258  case (5)
259  call self%get("hicen", field)
260  case (6)
261  call self%get("chl", field)
262  case (7)
263  call self%get("biop", field)
264  case default
265  ! TODO print error that out of range
266  end select
267  if (associated(field)) then
268  jz = 1
269  if (field%nz > 1) jz = izdir(n)
270  field%val(ixdir(n),iydir(n),izdir(n)) = 1.0
271  end if
272  end do
273 end subroutine soca_increment_dirac
274 
275 
276 ! ------------------------------------------------------------------------------
277 !> Setup atlas fields
278 !!
279 !! \see soca_increment_to_atlas
280 !! \see soca_increment_from_atlas
281 !! \relates soca_increment_mod::soca_increment
282 subroutine soca_increment_set_atlas(self, geom, vars, afieldset)
283  class(soca_increment), intent(in) :: self
284  type(soca_geom), intent(in) :: geom
285  type(oops_variables), intent(in) :: vars
286  type(atlas_fieldset), intent(inout) :: afieldset
287 
288  integer :: jvar, i, jz, nz
289  logical :: var_found
290  character(len=1024) :: fieldname
291  type(soca_field), pointer :: field
292  type(atlas_field) :: afield
293 
294  do jvar = 1,vars%nvars()
295  var_found = .false.
296  do i=1,size(self%fields)
297  field => self%fields(i)
298  if (trim(vars%variable(jvar))==trim(field%name)) then
299  if (.not.afieldset%has_field(vars%variable(jvar))) then
300  ! Variable dimension
301  nz = field%nz
302  if (nz==1) nz = 0
303 
304  ! Create field
305  afield = geom%afunctionspace%create_field(name=vars%variable(jvar),kind=atlas_real(kind_real),levels=nz)
306 
307  ! Add field
308  call afieldset%add(afield)
309 
310  ! Release pointer
311  call afield%final()
312  end if
313  ! Set flag
314  var_found = .true.
315  exit
316  end if
317  end do
318  if (.not.var_found) call abor1_ftn('variable '//trim(vars%variable(jvar))//' not found in increment')
319  end do
320 
321 end subroutine soca_increment_set_atlas
322 
323 
324 ! ------------------------------------------------------------------------------
325 !> Convert the increment to an atlas fieldset
326 !!
327 !! \relates soca_increment_mod::soca_increment
328 subroutine soca_increment_to_atlas(self, geom, vars, afieldset)
329  class(soca_increment), intent(in) :: self
330  type(soca_geom), intent(in) :: geom
331  type(oops_variables), intent(in) :: vars
332  type(atlas_fieldset), intent(inout) :: afieldset
333 
334  integer :: jvar, i, jz, nz
335  real(kind=kind_real), pointer :: real_ptr_1(:), real_ptr_2(:,:)
336  logical :: var_found
337  character(len=1024) :: fieldname
338  type(soca_field), pointer :: field
339  type(atlas_field) :: afield
340 
341  do jvar = 1,vars%nvars()
342  var_found = .false.
343  do i=1,size(self%fields)
344  field => self%fields(i)
345  if (trim(vars%variable(jvar))==trim(field%name)) then
346  ! Variable dimension
347  nz = field%nz
348  if (nz==1) nz = 0
349 
350  if (afieldset%has_field(vars%variable(jvar))) then
351  ! Get field
352  afield = afieldset%field(vars%variable(jvar))
353  else
354  ! Create field
355  afield = geom%afunctionspace%create_field(name=vars%variable(jvar),kind=atlas_real(kind_real),levels=nz)
356 
357  ! Add field
358  call afieldset%add(afield)
359  end if
360 
361  ! Copy data
362  if (nz==0) then
363  call afield%data(real_ptr_1)
364  real_ptr_1 = reshape(field%val(geom%isc:geom%iec,geom%jsc:geom%jec,1), &
365  & (/(geom%iec-geom%isc+1)*(geom%jec-geom%jsc+1)/))
366  else
367  call afield%data(real_ptr_2)
368  do jz=1,nz
369  real_ptr_2(jz,:) = reshape(field%val(geom%isc:geom%iec,geom%jsc:geom%jec,jz), &
370  & (/(geom%iec-geom%isc+1)*(geom%jec-geom%jsc+1)/))
371  end do
372  end if
373 
374  ! Release pointer
375  call afield%final()
376 
377  ! Set flag
378  var_found = .true.
379  exit
380  end if
381  end do
382  if (.not.var_found) call abor1_ftn('variable '//trim(vars%variable(jvar))//' not found in increment')
383 end do
384 
385 end subroutine soca_increment_to_atlas
386 
387 
388 ! ------------------------------------------------------------------------------
389 !> Set the our increment values from an atlas fieldset
390 !!
391 !! \relates soca_increment_mod::soca_increment
392 subroutine soca_increment_from_atlas(self, geom, vars, afieldset)
393  class(soca_increment), intent(inout) :: self
394  type(soca_geom), intent(in) :: geom
395  type(oops_variables), intent(in) :: vars
396  type(atlas_fieldset), intent(in) :: afieldset
397 
398  integer :: jvar, i, jz, nz
399  real(kind=kind_real), pointer :: real_ptr_1(:), real_ptr_2(:,:)
400  logical :: var_found
401  character(len=1024) :: fieldname
402  type(soca_field), pointer :: field
403  type(atlas_field) :: afield
404 
405  ! Initialization
406  call self%zeros()
407 
408  do jvar = 1,vars%nvars()
409  var_found = .false.
410  do i=1,size(self%fields)
411  field => self%fields(i)
412  if (trim(vars%variable(jvar))==trim(field%name)) then
413  ! Variable dimension
414  nz = field%nz
415  if (nz==1) nz = 0
416 
417  ! Get field
418  afield = afieldset%field(vars%variable(jvar))
419 
420  ! Copy data
421  if (nz==0) then
422  call afield%data(real_ptr_1)
423  field%val(geom%isc:geom%iec,geom%jsc:geom%jec,1) = reshape(real_ptr_1, &
424  & (/geom%iec-geom%isc+1,geom%jec-geom%jsc+1/))
425  else
426  call afield%data(real_ptr_2)
427  do jz=1,nz
428  field%val(geom%isc:geom%iec,geom%jsc:geom%jec,jz) = reshape(real_ptr_2(jz,:), &
429  & (/geom%iec-geom%isc+1,geom%jec-geom%jsc+1/))
430  end do
431  end if
432 
433  ! Release pointer
434  call afield%final()
435 
436  ! Set flag
437  var_found = .true.
438  exit
439  end if
440  end do
441  if (.not.var_found) call abor1_ftn('variable '//trim(vars%variable(jvar))//' not found in increment')
442  end do
443 
444 end subroutine soca_increment_from_atlas
445 
446 
447 ! ------------------------------------------------------------------------------
448 !> Change resolution
449 !!
450 !! \relates soca_increment_mod::soca_increment
451 subroutine soca_increment_change_resol(self, rhs)
452  class(soca_increment), intent(inout) :: self ! target
453  class(soca_increment), intent(in) :: rhs ! source
454 
455  integer :: n
456  type(soca_convertstate_type) :: convert_state
457  type(soca_field), pointer :: field1, field2, hocn1, hocn2
458 
459  call rhs%get("hocn", hocn1)
460  call self%get("hocn", hocn2)
461 
462  call convert_state%setup(rhs%geom, self%geom, hocn1, hocn2)
463  do n = 1, size(rhs%fields)
464  if (trim(rhs%fields(n)%name)=="hocn") cycle ! skip layer thickness
465  field1 => rhs%fields(n)
466  call self%get(trim(field1%name),field2)
467  call convert_state%change_resol2d(field1, field2, rhs%geom, self%geom)
468  end do !n
469  call convert_state%clean()
470 end subroutine soca_increment_change_resol
471 
472 ! ------------------------------------------------------------------------------
473 
474 end module soca_increment_mod
Handle fields for the model.
Geometry iterator.
Geometry module.
Increment fields.
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.