SOCA
soca_fields_mod.F90
Go to the documentation of this file.
1 ! (C) Copyright 2017-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 
7 !> Handle fields for the model.
8 !!
9 !! soca_fields represents a state or increment, and contains one or more
10 !! soca_field instances for each of the fields. The metadata associated
11 !! with a given field is stored in soca_fields_metadata_mod::soca_fields_metadata
13 
14 ! JEDI modules
15 use datetime_mod, only: datetime, datetime_set, datetime_to_string, &
16  datetime_create, datetime_diff
17 use duration_mod, only: duration, duration_to_string
18 use fckit_configuration_module, only: fckit_configuration
19 use fckit_mpi_module, only: fckit_mpi_min, fckit_mpi_max, fckit_mpi_sum
20 use kinds, only: kind_real
21 use oops_variables_mod, only: oops_variables
22 use tools_const, only: deg2rad
23 
24 ! MOM6 / FMS modules
25 use fms_io_mod, only: fms_io_init, fms_io_exit, register_restart_field, &
26  restart_file_type, restore_state, free_restart_type, save_restart
27 use fms_mod, only: write_data, set_domain
28 use horiz_interp_mod, only : horiz_interp_type
29 use horiz_interp_spherical_mod, only : horiz_interp_spherical, horiz_interp_spherical_del, &
30  horiz_interp_spherical_new
31 use mom_remapping, only : remapping_cs, initialize_remapping, remapping_core_h, &
32  end_remapping
33 use mpp_domains_mod, only : mpp_update_domains
34 
35 ! SOCA modules
37 use soca_geom_mod, only : soca_geom
38 use soca_utils, only: soca_mld
39 
40 implicit none
41 private
42 
43 
44 ! ------------------------------------------------------------------------------
45 ! ------------------------------------------------------------------------------
46 
47 
48 ! ------------------------------------------------------------------------------
49 !> Holds all data and metadata related to a single field variable.
50 !!
51 !! Instances of these types are to be held by soca_fields.
52 !! The members soca_field::mask can remain \c null, in which it is assumed that
53 !! no mask is used.
54 type, public :: soca_field
55 
56  !> The internally used name of the field.
57  character(len=:), allocatable :: name
58 
59  !> The number of vertical levels.
60  integer :: nz
61 
62  !> The actual field data.
63  real(kind=kind_real), allocatable :: val(:,:,:)
64 
65  !> Pointer to the relevant mask in soca_geom_mod::soca_geom
66  !!
67  !! If \c null, it is assumed that no mask is present
68  real(kind=kind_real), pointer :: mask(:,:) => null()!!
69 
70  !> Pointer to the relevant longitudes in soca_geom_mod::soca_geom
71  !!
72  !! \note This should never remain \c null() after initialization of the class.
73  real(kind=kind_real), pointer :: lon(:,:) => null()
74 
75  !> Pointer to the relevant latitudes in soca_geom_mod::soca_geom
76  !!
77  !! \note This should never remain \c null() after initialization of the class.
78  real(kind=kind_real), pointer :: lat(:,:) => null()
79 
80  !> Parameters for the field as determined by the configuration yaml.
81  !!
82  !! see soca_fields_metadata_mod::soca_field_metadata
83  type(soca_field_metadata) :: metadata
84 
85 contains
86 
87  !>\copybrief soca_field_copy \see soca_field_copy
88  procedure :: copy => soca_field_copy
89 
90  !>\copybrief soca_field_delete \see soca_field_delete
91  procedure :: delete => soca_field_delete
92 
93  !>\copybrief soca_field_check_congruent \see soca_field_check_congruent
94  procedure :: check_congruent => soca_field_check_congruent
95 
96  !>\copybrief soca_field_update_halo \see soca_field_update_halo
97  procedure :: update_halo => soca_field_update_halo
98 
99  !>\copybrief soca_field_stencil_interp \see soca_field_stencil_interp
100  procedure :: stencil_interp => soca_field_stencil_interp
101 
102 end type soca_field
103 
104 
105 ! ------------------------------------------------------------------------------
106 !> A collection of soca_field types representing a collective state or increment.
107 !!
108 !! The base class for soca_increment_mod::soca_increment and soca_state_mod::soca_state
109 type, public :: soca_fields
110 
111  !> Pointer to the relevant soca_geom_mod::soca_geom
112  !!
113  !! \note This should never remain \c null() after initialization of the class.
114  type(soca_geom), pointer :: geom => null()
115 
116  !> The soca_field instances that make up the fields
117  type(soca_field), pointer :: fields(:) => null()
118 
119 contains
120  !> \name constructors / destructors
121  !! \{
122 
123  !> \copybrief soca_fields_create \see soca_fields_create
124  procedure :: create => soca_fields_create
125 
126  !> \copybrief soca_fields_copy \see soca_fields_copy
127  procedure :: copy => soca_fields_copy
128 
129  !> \copybrief soca_fields_delete \see soca_fields_delete
130  procedure :: delete => soca_fields_delete
131 
132  !> \}
133 
134  !> \name field getters/checkers
135  !! \{
136 
137  !> \copybrief soca_fields_get \see soca_fields_get
138  procedure :: get => soca_fields_get
139 
140  !> \copybrief soca_fields_has \see soca_fields_has
141  procedure :: has => soca_fields_has
142 
143  !> \copybrief soca_fields_check_congruent \see soca_fields_check_congruent
144  procedure :: check_congruent => soca_fields_check_congruent
145 
146  !> \copybrief soca_fields_check_subset \see soca_fields_check_subset
147  procedure :: check_subset => soca_fields_check_subset
148 
149  !> \}
150 
151  !> \name math operators
152  !! \{
153 
154  !> \copybrief soca_fields_add \see soca_fields_add
155  procedure :: add => soca_fields_add
156 
157  !> \copybrief soca_fields_axpy \see soca_fields_axpy
158  procedure :: axpy => soca_fields_axpy
159 
160  !> \copybrief soca_fields_dotprod \see soca_fields_dotprod
161  procedure :: dot_prod => soca_fields_dotprod
162 
163  !> \copybrief soca_fields_gpnorm \see soca_fields_gpnorm
164  procedure :: gpnorm => soca_fields_gpnorm
165 
166  !> \copybrief soca_fields_mul \see soca_fields_mul
167  procedure :: mul => soca_fields_mul
168 
169  !> \copybrief soca_fields_sub \see soca_fields_sub
170  procedure :: sub => soca_fields_sub
171 
172  !> \copybrief soca_fields_ones \see soca_fields_ones
173  procedure :: ones => soca_fields_ones
174 
175  !> \copybrief soca_fields_zeros \see soca_fields_zeros
176  procedure :: zeros => soca_fields_zeros
177 
178  !> \}
179 
180  !> \name I/O
181  !! \{
182 
183  !> \copybrief soca_fields_read \see soca_fields_read
184  procedure :: read => soca_fields_read
185 
186  !> \copybrief soca_fields_write_file \see soca_fields_write_file
187  procedure :: write_file=> soca_fields_write_file
188 
189  !> \copybrief soca_fields_write_rst \see soca_fields_write_rst
190  procedure :: write_rst => soca_fields_write_rst
191 
192  !> \}
193 
194  !> \name misc
195  !! \{
196 
197  !> \copybrief soca_fields_update_halos \see soca_fields_update_halos
198  procedure :: update_halos => soca_fields_update_halos
199 
200  !> \copybrief soca_fields_colocate \see soca_fields_colocate
201  procedure :: colocate => soca_fields_colocate
202  !> \}
203 
204  !> \name serialization
205  !! \{
206 
207  !> \copybrief soca_fields_serial_size \see soca_fields_serial_size
208  procedure :: serial_size => soca_fields_serial_size
209 
210  !> \copybrief soca_fields_serialize \see soca_fields_serialize
211  procedure :: serialize => soca_fields_serialize
212 
213  !> \copybrief soca_fields_deserialize \see soca_fields_deserialize
214  procedure :: deserialize => soca_fields_deserialize
215 
216  !> \}
217 
218 end type soca_fields
219 
220 
221 ! ------------------------------------------------------------------------------
222 ! ------------------------------------------------------------------------------
223 contains
224 
225 
226 ! ------------------------------------------------------------------------------
227 ! soca_field subroutines
228 ! ------------------------------------------------------------------------------
229 
230 
231 ! ------------------------------------------------------------------------------
232 !> Copy a field from \p rhs to \p self.
233 !!
234 !! If the fields are not congruent, this subroutine will throw an error.
235 !! \p self must be allocated first.
236 !! \relates soca_fields_mod::soca_field
237 subroutine soca_field_copy(self, rhs)
238  class(soca_field), intent(inout) :: self !< The field to copy \b to
239  type(soca_field), intent(in) :: rhs !< The field to copy \b from
240 
241  call self%check_congruent(rhs)
242 
243  ! the only variable that should be different is %val
244  self%val = rhs%val
245 
246  ! NOTE: the pointers (mask, lat, lon) will be different, but should NOT
247  ! be changed to point to rhs pointers. Bad things happen
248 end subroutine soca_field_copy
249 
250 
251 ! ------------------------------------------------------------------------------
252 !> Update the data in the halo region of the field.
253 !!
254 !! \relates soca_fields_mod::soca_field
255 !! \todo have field keep a pointer to its relevant sections of soca_geom?
256 subroutine soca_field_update_halo(self, geom)
257  class(soca_field), intent(inout) :: self
258  type(soca_geom), pointer, intent(in) :: geom !< soca_geom from soca_fields
259 
260  call mpp_update_domains(self%val, geom%Domain%mpp_domain)
261 end subroutine soca_field_update_halo
262 
263 
264 ! ------------------------------------------------------------------------------
265 !> Perform spatial interpolation between two grids.
266 !!
267 !! Interpolation used is inverse distance weidghted, taking into
268 !! consideration the mask.
269 !! \param[in] geom: The geometry to interpolate to
270 !! \param[in] interp2d: interpolation object created by calling
271 !! \c horiz_interp_spherical_new() in FMS
272 !!
273 !! \relates soca_fields_mod::soca_field
274 subroutine soca_field_stencil_interp(self, geom, interp2d)
275  class(soca_field), intent(inout) :: self
276  type(soca_geom), pointer, intent(in) :: geom
277  type(horiz_interp_type), intent(in) :: interp2d
278 
279  integer :: k
280  real(kind=kind_real), allocatable :: val(:,:,:)
281 
282  allocate(val, mold=self%val)
283  val = self%val
284  do k = 1, self%nz
285  call horiz_interp_spherical(interp2d, &
286  & val(geom%isd:geom%ied, geom%jsd:geom%jed,k), &
287  & self%val(geom%isc:geom%iec, geom%jsc:geom%jec,k))
288  end do
289  call self%update_halo(geom)
290 end subroutine soca_field_stencil_interp
291 
292 
293 ! ------------------------------------------------------------------------------
294 !> Make sure the two fields are the same in terms of name, size, shape.
295 !!
296 !! \throws abor1_ftn Halts program if fields are not congruent
297 !! \relates soca_fields_mod::soca_field
298 subroutine soca_field_check_congruent(self, rhs)
299  class(soca_field), intent(in) :: self
300  type(soca_field), intent(in) :: rhs !< other field to check for congruency
301  integer :: i
302 
303  if ( self%nz /= rhs%nz ) call abor1_ftn("soca_field: self%nz /= rhs%nz")
304  if ( self%name /= rhs%name ) call abor1_ftn("soca_field: self%name /= rhs%name")
305  if ( size(shape(self%val)) /= size(shape(rhs%val)) ) &
306  call abor1_ftn("soca_field: shape of self%val /= rhs%val")
307  do i =1, size(shape(self%val))
308  if (size(self%val, dim=i) /= size(rhs%val, dim=i)) &
309  call abor1_ftn("soca_field: shape of self%val /= rhs%val")
310  end do
311 end subroutine soca_field_check_congruent
312 
313 
314 ! ------------------------------------------------------------------------------
315 !> Delete the soca_field object.
316 !!
317 !! \relates soca_fields_mod::soca_field
318 subroutine soca_field_delete(self)
319  class(soca_field), intent(inout) :: self
320 
321  deallocate(self%val)
322 end subroutine
323 
324 
325 ! ------------------------------------------------------------------------------
326 ! soca_fields subroutines
327 ! ------------------------------------------------------------------------------
328 
329 
330 ! ------------------------------------------------------------------------------
331 !> For a given list of field names, initialize the properties of those fields
332 !!
333 !! \param[in] vars: List of variables to initialize. They must be present in the
334 !! configuration file used to create soca_fields_metadata_mod::soca_fields_metadata
335 !!
336 !! \throws abor1_ftn aborts if illegal grid or levels specified
337 !! \relates soca_fields_mod::soca_fields
338 subroutine soca_fields_init_vars(self, vars)
339  class(soca_fields), intent(inout) :: self
340  character(len=:), allocatable, intent(in) :: vars(:)
341 
342  integer :: i, nz
343 
344  allocate(self%fields(size(vars)))
345  do i=1,size(vars)
346  self%fields(i)%name = trim(vars(i))
347 
348  ! get the field metadata parameters that are read in from a config file
349  self%fields(i)%metadata = self%geom%fields_metadata%get(self%fields(i)%name)
350  ! Set grid location and masks
351  select case(self%fields(i)%metadata%grid)
352  case ('h')
353  self%fields(i)%lon => self%geom%lon
354  self%fields(i)%lat => self%geom%lat
355  if (self%fields(i)%metadata%masked) &
356  self%fields(i)%mask => self%geom%mask2d
357  case ('u')
358  self%fields(i)%lon => self%geom%lonu
359  self%fields(i)%lat => self%geom%latu
360  if (self%fields(i)%metadata%masked) &
361  self%fields(i)%mask => self%geom%mask2du
362  case ('v')
363  self%fields(i)%lon => self%geom%lonv
364  self%fields(i)%lat => self%geom%latv
365  if (self%fields(i)%metadata%masked) &
366  self%fields(i)%mask => self%geom%mask2dv
367  case default
368  call abor1_ftn('soca_fields::create(): Illegal grid '// &
369  self%fields(i)%metadata%grid // &
370  ' given for ' // self%fields(i)%name)
371  end select
372 
373  ! determine number of levels
374  if (self%fields(i)%name == self%fields(i)%metadata%getval_name_surface) then
375  ! if this field is a surface getval, override the number of levels with 1
376  nz = 1
377  else
378  select case(self%fields(i)%metadata%levels)
379  case ('full_ocn')
380  nz = self%geom%nzo
381  case ('1') ! TODO, generalize to work with any number?
382  nz = 1
383  case default
384  call abor1_ftn('soca_fields::create(): Illegal levels '//self%fields(i)%metadata%levels// &
385  ' given for ' // self%fields(i)%name)
386  end select
387  endif
388 
389  ! allocate space
390  self%fields(i)%nz = nz
391  allocate(self%fields(i)%val(&
392  self%geom%isd:self%geom%ied, &
393  self%geom%jsd:self%geom%jed, &
394  nz ))
395 
396  end do
397 end subroutine
398 
399 
400 ! ------------------------------------------------------------------------------
401 !> Create a new set of fields, allocate space for them, and initialize to zero
402 !!
403 !! \see soca_fields_init_vars
404 !! \relates soca_fields_mod::soca_fields
405 subroutine soca_fields_create(self, geom, vars)
406  class(soca_fields), intent(inout) :: self
407  type(soca_geom), pointer, intent(inout) :: geom !< geometry to associate with the fields
408  type(oops_variables), intent(inout) :: vars !< list of field names to create
409 
410  character(len=:), allocatable :: vars_str(:)
411  integer :: i
412 
413  ! make sure current object has not already been allocated
414  if (associated(self%fields)) &
415  call abor1_ftn("soca_fields::create(): object already allocated")
416 
417  ! associate geometry
418  self%geom => geom
419 
420  ! initialize the variable parameters
421  allocate(character(len=1024) :: vars_str(vars%nvars()))
422  do i=1,vars%nvars()
423  vars_str(i) = trim(vars%variable(i))
424  end do
425  call soca_fields_init_vars(self, vars_str)
426 
427  ! set everything to zero
428  call self%zeros()
429 end subroutine soca_fields_create
430 
431 
432 ! ------------------------------------------------------------------------------
433 !> delete all the fields
434 !!
435 !! \relates soca_fields_mod::soca_fields
436 subroutine soca_fields_delete(self)
437  class(soca_fields), intent(inout) :: self
438  integer :: i
439 
440  ! clear the fields and nullify pointers
441  nullify(self%geom)
442  do i = 1, size(self%fields)
443  call self%fields(i)%delete()
444  end do
445  deallocate(self%fields)
446  nullify(self%fields)
447 
448 end subroutine
449 
450 
451 ! ------------------------------------------------------------------------------
452 !> Copy the contents of \p rhs to \p self.
453 !!
454 !! \p self will be initialized with the variable names in \p rhs if
455 !! not already initialized.
456 !!
457 !! \see soca_fields_init_vars
458 !! \relates soca_fields_mod::soca_fields
459 subroutine soca_fields_copy(self, rhs)
460  class(soca_fields), intent(inout) :: self
461  class(soca_fields), intent(in) :: rhs !< fields to copy from
462 
463  character(len=:), allocatable :: vars_str(:)
464  integer :: i
465  type(soca_field), pointer :: rhs_fld
466 
467  ! initialize the variables based on the names in rhs
468  if (.not. associated(self%fields)) then
469  self%geom => rhs%geom
470  allocate(character(len=1024) :: vars_str(size(rhs%fields)))
471  do i=1, size(vars_str)
472  vars_str(i) = rhs%fields(i)%name
473  end do
474  call soca_fields_init_vars(self, vars_str)
475  end if
476 
477  ! copy values from rhs to self, only if the variable exists
478  ! in self
479  do i=1,size(self%fields)
480  call rhs%get(self%fields(i)%name, rhs_fld)
481  call self%fields(i)%copy(rhs_fld)
482  end do
483 
484 end subroutine
485 
486 
487 ! ------------------------------------------------------------------------------
488 !> Get a pointer to the soca_field with the given name.
489 !!
490 !! \note use soca_fields::has() if you need to check for optional fields
491 !! \throws abor1_ftn If no field exists with that name, the prorgam aborts
492 !! \relates soca_fields_mod::soca_fields
493 subroutine soca_fields_get(self, name, field)
494  class(soca_fields), intent(in) :: self
495  character(len=*), intent(in) :: name !< name of field to find
496  type(soca_field), pointer, intent(out) :: field !< a pointer to the resulting field
497 
498  integer :: i
499 
500  ! find the field with the given name
501  do i=1,size(self%fields)
502  if (trim(name) == self%fields(i)%name) then
503  field => self%fields(i)
504  return
505  end if
506  end do
507 
508  ! oops, the field was not found
509  call abor1_ftn("soca_fields::get(): cannot find field "//trim(name))
510 end subroutine
511 
512 
513 ! ------------------------------------------------------------------------------
514 !> Returns whether a field with the given name exists
515 !!
516 !! \relates soca_fields_mod::soca_fields
517 function soca_fields_has(self, name) result(res)
518  class(soca_fields), intent(in) :: self
519  character(len=*), intent(in) :: name !< name of field to find
520 
521  logical :: res
522  integer :: i
523 
524  res = .false.
525  do i=1,size(self%fields)
526  if (trim(name) == self%fields(i)%name) then
527  res = .true.
528  return
529  end if
530  end do
531 end function
532 
533 
534 ! ------------------------------------------------------------------------------
535 !> Update the halo region of all fields.
536 !!
537 !! \relates soca_fields_mod::soca_fields
538 subroutine soca_fields_update_halos(self)
539  class(soca_fields), intent(inout) :: self
540  integer :: i
541 
542  do i=1,size(self%fields)
543  call self%fields(i)%update_halo(self%geom)
544  end do
545 end subroutine soca_fields_update_halos
546 
547 
548 ! ------------------------------------------------------------------------------
549 !> Set the value of all fields to one.
550 !!
551 !! \relates soca_fields_mod::soca_fields
552 subroutine soca_fields_ones(self)
553  class(soca_fields), intent(inout) :: self
554  integer :: i
555 
556  do i = 1, size(self%fields)
557  self%fields(i)%val = 1.0_kind_real
558  end do
559 
560 end subroutine soca_fields_ones
561 
562 
563 ! ------------------------------------------------------------------------------
564 !> Reset the value of all fields to zero.
565 !!
566 !! \relates soca_fields_mod::soca_fields
567 subroutine soca_fields_zeros(self)
568  class(soca_fields), intent(inout) :: self
569  integer :: i
570 
571  do i = 1, size(self%fields)
572  self%fields(i)%val = 0.0_kind_real
573  end do
574 
575 end subroutine soca_fields_zeros
576 
577 
578 ! ------------------------------------------------------------------------------
579 !> Add two sets of fields together
580 !!
581 !! \f$ self = self + rhs \f$
582 !!
583 !! \throws abor1_ftn aborts if two fields are not congruent
584 !! \relates soca_fields_mod::soca_fields
585 subroutine soca_fields_add(self, rhs)
586  class(soca_fields), intent(inout) :: self
587  class(soca_fields), intent(in) :: rhs !< other field to add
588  integer :: i
589 
590  ! make sure fields are same shape
591  call self%check_congruent(rhs)
592 
593  ! add
594  do i=1,size(self%fields)
595  self%fields(i)%val = self%fields(i)%val + rhs%fields(i)%val
596  end do
597 end subroutine soca_fields_add
598 
599 
600 ! ------------------------------------------------------------------------------
601 !> subtract two sets of fields
602 !!
603 !! \f$ self = self - rhs \f$
604 !!
605 !! \throws abor1_ftn aborts if two fields are not congruent
606 !! \relates soca_fields_mod::soca_fields
607 subroutine soca_fields_sub(self, rhs)
608  class(soca_fields), intent(inout) :: self
609  class(soca_fields), intent(in) :: rhs !< other field to subtract
610  integer :: i
611 
612  ! make sure fields are same shape
613  call self%check_congruent(rhs)
614 
615  ! subtract
616  do i=1,size(self%fields)
617  self%fields(i)%val = self%fields(i)%val - rhs%fields(i)%val
618  end do
619 end subroutine soca_fields_sub
620 
621 
622 ! ------------------------------------------------------------------------------
623 !> Multiply a set of fields by a constant.
624 !!
625 !! \f$ self = zz * self \f$
626 !! \relates soca_fields_mod::soca_fields
627 subroutine soca_fields_mul(self, zz)
628  class(soca_fields), intent(inout) :: self
629  real(kind=kind_real), intent(in) :: zz !< the constant by which to multipy the field
630  integer :: i
631 
632  do i=1,size(self%fields)
633  self%fields(i)%val = zz * self%fields(i)%val
634  end do
635 end subroutine soca_fields_mul
636 
637 
638 ! ------------------------------------------------------------------------------
639 !> Add two fields (multiplying the rhs first)
640 !!
641 !! \f$self = self + zz * rhs\f$
642 !!
643 !! \throws abor1_ftn aborts if \p is not a subset of \rhs
644 !! \relates soca_fields_mod::soca_fields
645 subroutine soca_fields_axpy(self, zz, rhs)
646  class(soca_fields), intent(inout) :: self
647  real(kind=kind_real), intent(in) :: zz !< constant by which to multiply other rhs
648  class(soca_fields), intent(in) :: rhs !< other field to add
649 
650  type(soca_field), pointer :: f_rhs, f_lhs
651  integer :: i
652 
653  ! make sure fields are correct shape
654  call self%check_subset(rhs)
655 
656  do i=1,size(self%fields)
657  f_lhs => self%fields(i)
658  if (.not. rhs%has(f_lhs%name)) cycle
659  call rhs%get(f_lhs%name, f_rhs)
660  f_lhs%val = f_lhs%val + zz *f_rhs%val
661  end do
662 end subroutine soca_fields_axpy
663 
664 
665 ! ------------------------------------------------------------------------------
666 !> Calculate the global dot product of two sets of fields.
667 !!
668 !! \throws abor1_ftn aborts if two fields are not congruent
669 !! \relates soca_fields_mod::soca_fields
670 subroutine soca_fields_dotprod(self, rhs, zprod)
671  class(soca_fields), intent(in) :: self
672  class(soca_fields), intent(in) :: rhs !< field 2 of dot product
673  real(kind=kind_real), intent(out) :: zprod !< The resulting dot product
674 
675  real(kind=kind_real) :: local_zprod
676  integer :: ii, jj, kk, n
677  type(soca_field), pointer :: field1, field2
678 
679  ! make sure fields are same shape
680  call self%check_congruent(rhs)
681 
682  ! loop over (almost) all fields
683  local_zprod = 0.0_kind_real
684  do n=1,size(self%fields)
685  field1 => self%fields(n)
686  field2 => rhs%fields(n)
687 
688  ! add the given field to the dot product (only using the compute domain)
689  do ii = self%geom%isc, self%geom%iec
690  do jj = self%geom%jsc, self%geom%jec
691  ! masking
692  if (associated(field1%mask)) then
693  if (field1%mask(ii,jj) < 1) cycle
694  endif
695 
696  ! add to dot product
697  do kk=1,field1%nz
698  local_zprod = local_zprod + field1%val(ii,jj,kk) * field2%val(ii,jj,kk)
699  end do
700  end do
701  end do
702  end do
703 
704  ! Get global dot product
705  call self%geom%f_comm%allreduce(local_zprod, zprod, fckit_mpi_sum())
706 end subroutine soca_fields_dotprod
707 
708 
709 ! ------------------------------------------------------------------------------
710 !> read a set of fields from a file
711 !!
712 !! \param[in] f_conf : Configuration with the following parameters
713 !! - "read_from_file" :
714 !! - 0 = Invent the state
715 !! - 1 = read state
716 !! - 2 = (nothing??)
717 !! - 3 = read increment
718 !! - "remap_filename" : (optional) the filename containing "h" to perform the
719 !! vertical remapping of these fields after they are loaded.
720 !! - "date" : (required if read_from_file == 0)
721 !! - "basename" : The common part of the path prepended to the following
722 !! \c *_filename parameters
723 !! - "ocn_filename" : ocean filename
724 !! - "sfc_filename" : (optional) surface field filename
725 !! - "ice_filename" : (optional) ice field filename
726 !! - "wav_filename" : (optoinal) wave field filename
727 !! \param[inout] vdate : If fields are being invented (read_from_file == 0),
728 !! the \p vdate is used as the valid date of the fields. If the fields are
729 !! being read in as a state (read_from_file == 1), \p vdate is set the the
730 !! date from the files
731 !! \relates soca_fields_mod::soca_fields
732 subroutine soca_fields_read(self, f_conf, vdate)
733  class(soca_fields), intent(inout) :: self
734  type(fckit_configuration), intent(in) :: f_conf
735  type(datetime), intent(inout) :: vdate
736 
737  integer, parameter :: max_string_length=800
738  character(len=max_string_length) :: ocn_filename, sfc_filename, ice_filename, wav_filename, filename
739  character(len=:), allocatable :: basename, incr_filename
740  integer :: iread = 0
741  integer :: ii
742  logical :: vert_remap=.false.
743  character(len=max_string_length) :: remap_filename
744  real(kind=kind_real), allocatable :: h_common(:,:,:) !< layer thickness to remap to
745  type(restart_file_type), target :: ocean_restart, sfc_restart, ice_restart, wav_restart
746  type(restart_file_type) :: ocean_remap_restart
747  type(restart_file_type), pointer :: restart
748  integer :: idr
749  integer :: isd, ied, jsd, jed
750  integer :: isc, iec, jsc, jec
751  integer :: i, j, nz, n
752  type(remapping_cs) :: remapCS
753  character(len=:), allocatable :: str
754  real(kind=kind_real), allocatable :: h_common_ij(:), hocn_ij(:), varocn_ij(:), varocn2_ij(:)
755  logical :: read_sfc, read_ice, read_wav
756  type(soca_field), pointer :: field, field2, hocn, mld, layer_depth
757 
758  if ( f_conf%has("read_from_file") ) &
759  call f_conf%get_or_die("read_from_file", iread)
760 
761  call self%get("hocn", hocn)
762 
763  ! Get Indices for data domain and allocate common layer depth array
764  isd = self%geom%isd ; ied = self%geom%ied
765  jsd = self%geom%jsd ; jed = self%geom%jed
766 
767  ! Check if vertical remapping needs to be applied
768  nz = hocn%nz
769  if ( f_conf%has("remap_filename") ) then
770  vert_remap = .true.
771  call f_conf%get_or_die("remap_filename", str)
772  remap_filename = str
773  allocate(h_common(isd:ied,jsd:jed,nz))
774  h_common = 0.0_kind_real
775 
776  ! Read common vertical coordinate from file
777  call fms_io_init()
778  idr = register_restart_field(ocean_remap_restart, remap_filename, 'h', h_common, &
779  domain=self%geom%Domain%mpp_domain)
780  call restore_state(ocean_remap_restart, directory='')
781  call free_restart_type(ocean_remap_restart)
782  call fms_io_exit()
783  end if
784 
785  ! iread = 0: Invent state
786  if (iread==0) then
787  call self%zeros()
788  call f_conf%get_or_die("date", str)
789  call datetime_set(str, vdate)
790  end if
791 
792  ! TODO redo this to be generic
793 
794  ! iread = 1 (state) or 3 (increment): Read restart file
795  if ((iread==1).or.(iread==3)) then
796 
797  ! filename for ocean
798  call f_conf%get_or_die("basename", str)
799  basename = str
800  call f_conf%get_or_die("ocn_filename", str)
801  ocn_filename = trim(basename) // trim(str)
802 
803  ! filename for ocn sfc
804  read_sfc = .false.
805  sfc_filename=""
806  if ( f_conf%has("sfc_filename") ) then
807  call f_conf%get_or_die("basename", str)
808  basename = str
809  call f_conf%get_or_die("sfc_filename", str)
810  sfc_filename = trim(basename)//trim(str)
811  end if
812 
813  ! filename for ice
814  read_ice = .false.
815  ice_filename=""
816  if ( f_conf%has("ice_filename") ) then
817  call f_conf%get_or_die("basename", str)
818  basename = str
819  call f_conf%get_or_die("ice_filename", str)
820  ice_filename = trim(basename)//trim(str)
821  end if
822 
823  ! filename for wav
824  read_wav = .false.
825  wav_filename=""
826  if ( f_conf%has("wav_filename") ) then
827  call f_conf%get_or_die("basename", str)
828  basename = str
829  call f_conf%get_or_die("wav_filename", str)
830  wav_filename = trim(basename)//trim(str)
831  end if
832 
833  call fms_io_init()
834 
835  ! built-in variables
836  do i=1,size(self%fields)
837  if(self%fields(i)%metadata%io_name /= "") then
838  ! which file are we reading from?
839  select case(self%fields(i)%metadata%io_file)
840  case ('ocn')
841  filename = ocn_filename
842  restart => ocean_restart
843  case ('sfc')
844  if (sfc_filename == "") cycle ! we have sfc fields, but no file to read from
845  filename = sfc_filename
846  restart => sfc_restart
847  read_sfc = .true.
848  case ('ice')
849  filename = ice_filename
850  restart => ice_restart
851  read_ice = .true.
852  case ('wav')
853  filename = wav_filename
854  restart => wav_restart
855  read_wav = .true.
856  case default
857  call abor1_ftn('read_file(): illegal io_file: '//self%fields(i)%metadata%io_file)
858  end select
859 
860  ! setup to read
861  if (self%fields(i)%nz == 1) then
862  idr = register_restart_field(restart, filename, self%fields(i)%metadata%io_name, &
863  self%fields(i)%val(:,:,1), domain=self%geom%Domain%mpp_domain)
864  else
865  idr = register_restart_field(restart, filename, self%fields(i)%metadata%io_name, &
866  self%fields(i)%val(:,:,:), domain=self%geom%Domain%mpp_domain)
867  end if
868  end if
869  end do
870 
871  call restore_state(ocean_restart, directory='')
872  call free_restart_type(ocean_restart)
873  if (read_sfc) then
874  call restore_state(sfc_restart, directory='')
875  call free_restart_type(sfc_restart)
876  end if
877  if (read_ice) then
878  call restore_state(ice_restart, directory='')
879  call free_restart_type(ice_restart)
880  end if
881  if (read_wav) then
882  call restore_state(wav_restart, directory='')
883  call free_restart_type(wav_restart)
884  end if
885 
886  call fms_io_exit()
887 
888  ! Indices for compute domain
889  isc = self%geom%isc ; iec = self%geom%iec
890  jsc = self%geom%jsc ; jec = self%geom%jec
891 
892  ! Initialize mid-layer depth from layer thickness
893  if (self%has("layer_depth")) then
894  call self%get("layer_depth", layer_depth)
895  call self%geom%thickness2depth(hocn%val, layer_depth%val)
896  end if
897 
898  ! Compute mixed layer depth TODO: Move somewhere else ...
899  if (self%has("mld") .and. self%has("layer_depth")) then
900  call self%get("tocn", field)
901  call self%get("socn", field2)
902  call self%get("mld", mld)
903  do i = isc, iec
904  do j = jsc, jec
905  mld%val(i,j,1) = soca_mld(&
906  &field2%val(i,j,:),&
907  &field%val(i,j,:),&
908  &layer_depth%val(i,j,:),&
909  &self%geom%lon(i,j),&
910  &self%geom%lat(i,j))
911  end do
912  end do
913  end if
914 
915  ! Remap layers if needed
916  if (vert_remap) then
917  allocate(h_common_ij(nz), hocn_ij(nz), varocn_ij(nz), varocn2_ij(nz))
918  call initialize_remapping(remapcs,'PCM')
919  do i = isc, iec
920  do j = jsc, jec
921  h_common_ij = h_common(i,j,:)
922  hocn_ij = hocn%val(i,j,:)
923 
924  do n=1,size(self%fields)
925  field => self%fields(n)
926  select case(field%name)
927  ! TODO remove hardcoded variable names here
928  ! TODO Add u and v. Remapping u and v will require interpolating h
929  case ('tocn','socn')
930  if (associated(field%mask) .and. field%mask(i,j).eq.1) then
931  varocn_ij = field%val(i,j,:)
932  call remapping_core_h(remapcs, nz, h_common_ij, varocn_ij,&
933  &nz, hocn_ij, varocn2_ij)
934  field%val(i,j,:) = varocn2_ij
935  else
936  field%val(i,j,:) = 0.0_kind_real
937  end if
938  end select
939  end do
940  end do
941  end do
942  hocn%val = h_common
943  deallocate(h_common_ij, hocn_ij, varocn_ij, varocn2_ij)
944  call end_remapping(remapcs)
945  end if
946 
947  ! Update halo
948  do n=1,size(self%fields)
949  field => self%fields(n)
950  call mpp_update_domains(field%val, self%geom%Domain%mpp_domain)
951  end do
952 
953  ! Set vdate if reading state
954  if (iread==1) then
955  call f_conf%get_or_die("date", str)
956  call datetime_set(str, vdate)
957  end if
958 
959  return
960  end if
961 
962 end subroutine soca_fields_read
963 
964 
965 ! ------------------------------------------------------------------------------
966 !> calculate global statistics for each field (min, max, average)
967 !!
968 !! \param[in] nf: The number of fields, should be equal to the size of
969 !! soca_fields::fields
970 !! \param[out] pstat: a 2D array with shape (i,j). For each field index
971 !! i is set as 0 = min, 1 = max, 2 = average, for j number of fields.
972 !! \relates soca_fields_mod::soca_fields
973 subroutine soca_fields_gpnorm(self, nf, pstat)
974  class(soca_fields), intent(in) :: self
975  integer, intent(in) :: nf
976  real(kind=kind_real), intent(out) :: pstat(3, nf)
977 
978  logical :: mask(self%geom%isc:self%geom%iec, self%geom%jsc:self%geom%jec)
979  real(kind=kind_real) :: ocn_count, local_ocn_count, tmp(3)
980  integer :: jj, isc, iec, jsc, jec
981  type(soca_field), pointer :: field
982 
983  ! Indices for compute domain
984  isc = self%geom%isc ; iec = self%geom%iec
985  jsc = self%geom%jsc ; jec = self%geom%jec
986 
987  ! calculate global min, max, mean for each field
988  do jj=1, size(self%fields)
989  call self%get(self%fields(jj)%name, field)
990 
991  ! get the mask and the total number of grid cells
992  if (.not. associated(field%mask)) then
993  mask = .true.
994  else
995  mask = field%mask(isc:iec, jsc:jec) > 0.0
996  end if
997  local_ocn_count = count(mask)
998  call self%geom%f_comm%allreduce(local_ocn_count, ocn_count, fckit_mpi_sum())
999 
1000  ! calculate global min/max/mean
1001  call fldinfo(field%val(isc:iec,jsc:jec,:), mask, tmp)
1002  call self%geom%f_comm%allreduce(tmp(1), pstat(1,jj), fckit_mpi_min())
1003  call self%geom%f_comm%allreduce(tmp(2), pstat(2,jj), fckit_mpi_max())
1004  call self%geom%f_comm%allreduce(tmp(3), pstat(3,jj), fckit_mpi_sum())
1005  pstat(3,jj) = pstat(3,jj)/ocn_count
1006  end do
1007 end subroutine soca_fields_gpnorm
1008 
1009 
1010 ! ------------------------------------------------------------------------------
1011 !> Make sure two sets of fields are the same shape (same variables, same resolution)
1012 !!
1013 !! \throws abor1_ftn aborts if two fields are not congruent.
1014 !!
1015 !! \relates soca_fields_mod::soca_fields
1016 subroutine soca_fields_check_congruent(self, rhs)
1017  class(soca_fields), intent(in) :: self
1018  class(soca_fields), intent(in) :: rhs !< other fields to check for congruency
1019 
1020  integer :: i, j
1021 
1022  ! number of fields should be the same
1023  if (size(self%fields) /= size(rhs%fields)) &
1024  call abor1_ftn("soca_fields: contains different number of fields")
1025 
1026  ! each field should match (name, size, shape)
1027  do i=1,size(self%fields)
1028  if (self%fields(i)%name /= rhs%fields(i)%name) &
1029  call abor1_ftn("soca_fields: field have different names")
1030  do j = 1, size(shape(self%fields(i)%val))
1031  if (size(self%fields(i)%val, dim=j) /= size(rhs%fields(i)%val, dim=j) ) then
1032  call abor1_ftn("soca_fields: field '"//self%fields(i)%name//"' has different dimensions")
1033  end if
1034  end do
1035  end do
1036 end subroutine soca_fields_check_congruent
1037 
1038 
1039 ! ------------------------------------------------------------------------------
1040 !> make sure two sets of fields are the same shape for fields they have in common
1041 !!
1042 !! \throws abor1_ftn aborts if \p self is not a subset of \p rhs
1043 !! \relates soca_fields_mod::soca_fields
1044 subroutine soca_fields_check_subset(self, rhs)
1045  class(soca_fields), intent(in) :: self
1046  class(soca_fields), intent(in) :: rhs !< other field that \p self should be subset of
1047 
1048  type(soca_field), pointer :: fld
1049  integer :: i, j
1050 
1051  ! each field should match (name, size, shape)
1052  do i=1,size(self%fields)
1053  if (.not. rhs%has(self%fields(i)%name)) &
1054  call abor1_ftn("soca_fields: self is not a subset of rhs")
1055  call rhs%get(self%fields(i)%name, fld)
1056  do j = 1, size(shape(fld%val))
1057  if (size(self%fields(i)%val, dim=j) /= size(fld%val, dim=j) ) then
1058  call abor1_ftn("soca_fields: field '"//self%fields(i)%name//"' has different dimensions")
1059  end if
1060  end do
1061  end do
1062 end subroutine soca_fields_check_subset
1063 
1064 
1065 ! ------------------------------------------------------------------------------
1066 !> Save soca fields to file using fms write_data
1067 !!
1068 !! \param[in] filename : The name of the file to save to
1069 !!
1070 !! \relates soca_fields_mod::soca_fields
1071 subroutine soca_fields_write_file(self, filename)
1072  class(soca_fields), intent(in) :: self
1073  character(len=*), intent(in) :: filename
1074 
1075  integer :: ii
1076 
1077  call fms_io_init()
1078  call set_domain( self%geom%Domain%mpp_domain )
1079 
1080  ! write out all fields
1081  do ii = 1, size(self%fields)
1082  call write_data( filename, self%fields(ii)%name, self%fields(ii)%val(:,:,:), self%geom%Domain%mpp_domain)
1083  end do
1084 
1085  ! some other derived fields that should be written out
1086  call write_data( filename, "rossby_radius", self%geom%rossby_radius, self%geom%Domain%mpp_domain)
1087 
1088  call fms_io_exit()
1089 end subroutine soca_fields_write_file
1090 
1091 
1092 ! ------------------------------------------------------------------------------
1093 !> Save soca fields in a restart format
1094 !!
1095 !! TODO this can be generalized even more
1096 !! \relates soca_fields_mod::soca_fields
1097 subroutine soca_fields_write_rst(self, f_conf, vdate)
1098  class(soca_fields), intent(inout) :: self !< Fields
1099  type(fckit_configuration), intent(in) :: f_conf !< Configuration
1100  type(datetime), intent(inout) :: vdate !< DateTime
1101 
1102  integer, parameter :: max_string_length=800
1103  character(len=max_string_length) :: ocn_filename, sfc_filename, ice_filename, wav_filename, filename
1104  type(restart_file_type), target :: ocean_restart, sfc_restart, ice_restart, wav_restart
1105  type(restart_file_type), pointer :: restart
1106  integer :: idr, i
1107  type(soca_field), pointer :: field
1108  logical :: write_sfc, write_ice, write_wav
1109 
1110  write_ice = .false.
1111  write_sfc = .false.
1112  write_wav = .false.
1113  call fms_io_init()
1114 
1115  ! filenames
1116  ocn_filename = soca_genfilename(f_conf,max_string_length,vdate,"ocn")
1117  sfc_filename = soca_genfilename(f_conf,max_string_length,vdate,"sfc")
1118  ice_filename = soca_genfilename(f_conf, max_string_length,vdate,"ice")
1119  wav_filename = soca_genfilename(f_conf, max_string_length,vdate,"wav")
1120 
1121  ! built in variables
1122  do i=1,size(self%fields)
1123  field => self%fields(i)
1124  if (len_trim(field%metadata%io_file) /= 0) then
1125  ! which file are we writing to
1126  select case(field%metadata%io_file)
1127  case ('ocn')
1128  filename = ocn_filename
1129  restart => ocean_restart
1130  case ('sfc')
1131  filename = sfc_filename
1132  restart => sfc_restart
1133  write_sfc = .true.
1134  case ('ice')
1135  filename = ice_filename
1136  restart => ice_restart
1137  write_ice = .true.
1138  case ('wav')
1139  filename = wav_filename
1140  restart => wav_restart
1141  write_wav = .true.
1142  case default
1143  call abor1_ftn('soca_write_restart(): illegal io_file: '//field%metadata%io_file)
1144  end select
1145 
1146  ! write
1147  if (field%nz == 1) then
1148  idr = register_restart_field( restart, filename, field%metadata%io_name, &
1149  field%val(:,:,1), domain=self%geom%Domain%mpp_domain)
1150  else
1151  idr = register_restart_field( restart, filename, field%metadata%io_name, &
1152  field%val(:,:,:), domain=self%geom%Domain%mpp_domain)
1153  end if
1154  end if
1155  end do
1156 
1157  ! write out and cleanup
1158  call save_restart(ocean_restart, directory='')
1159  call free_restart_type(ocean_restart)
1160  if (write_sfc) then
1161  call save_restart(sfc_restart, directory='')
1162  call free_restart_type(sfc_restart)
1163  end if
1164  if (write_ice) then
1165  call save_restart(ice_restart, directory='')
1166  call free_restart_type(ice_restart)
1167  end if
1168  if (write_wav) then
1169  call save_restart(wav_restart, directory='')
1170  call free_restart_type(wav_restart)
1171  end if
1172  call fms_io_exit()
1173 
1174 end subroutine soca_fields_write_rst
1175 
1176 ! ------------------------------------------------------------------------------
1177 !> Colocate by interpolating from one c-grid location to another.
1178 !!
1179 !! \warning only works on the "h" grid currently (not the "u" or "v" grid)
1180 !! \relates soca_fields_mod::soca_fields
1181 subroutine soca_fields_colocate(self, cgridlocout)
1182  class(soca_fields), intent(inout) :: self !< self
1183  character(len=1), intent(in) :: cgridlocout !< colocate to cgridloc (u, v or h)
1184 
1185  integer :: i, k
1186  real(kind=kind_real), allocatable :: val(:,:,:)
1187  real(kind=kind_real), pointer :: lon_out(:,:) => null()
1188  real(kind=kind_real), pointer :: lat_out(:,:) => null()
1189  type(soca_geom), pointer :: g => null()
1190  type(horiz_interp_type) :: interp2d
1191 
1192  ! Associate lon_out and lat_out according to cgridlocout
1193  select case(cgridlocout)
1194  ! TODO: Test colocation to u and v grid
1195  !case ('u')
1196  ! lon_out => self%geom%lonu
1197  ! lat_out => self%geom%latu
1198  !case ('v')
1199  ! lon_out => self%geom%lonv
1200  ! lat_out => self%geom%latv
1201  case ('h')
1202  lon_out => self%geom%lon
1203  lat_out => self%geom%lat
1204  case default
1205  call abor1_ftn('soca_fields::colocate(): unknown c-grid location '// cgridlocout)
1206  end select
1207 
1208  ! Apply interpolation to all fields, when necessary
1209  do i=1,size(self%fields)
1210 
1211  ! Check if already colocated
1212  if (self%fields(i)%metadata%grid == cgridlocout) cycle
1213 
1214  ! Initialize fms spherical idw interpolation
1215  g => self%geom
1216  call horiz_interp_spherical_new(interp2d, &
1217  & real(deg2rad*self%fields(i)%lon(g%isd:g%ied,g%jsd:g%jed), 8), &
1218  & real(deg2rad*self%fields(i)%lat(g%isd:g%ied,g%jsd:g%jed), 8), &
1219  & real(deg2rad*lon_out(g%isc:g%iec,g%jsc:g%jec), 8), &
1220  & real(deg2rad*lat_out(g%isc:g%iec,g%jsc:g%jec), 8))
1221 
1222  ! Make a temporary copy of field
1223  if (allocated(val)) deallocate(val)
1224  allocate(val, mold=self%fields(i)%val)
1225  val = self%fields(i)%val
1226 
1227  ! Interpolate all levels
1228  do k = 1, self%fields(i)%nz
1229  call self%fields(i)%stencil_interp(self%geom, interp2d)
1230  end do
1231 
1232  ! Update c-grid location
1233  self%fields(i)%metadata%grid = cgridlocout
1234  select case(cgridlocout)
1235  ! TODO: Test colocation to u and v grid
1236  !case ('u')
1237  ! self%fields(i)%lon => self%geom%lonu
1238  ! self%fields(i)%lat => self%geom%latu
1239  !case ('v')
1240  ! self%fields(i)%lon => self%geom%lonv
1241  ! self%fields(i)%lat => self%geom%latv
1242  case ('h')
1243  self%fields(i)%lon => self%geom%lon
1244  self%fields(i)%lat => self%geom%lat
1245  end select
1246 
1247  end do
1248  call horiz_interp_spherical_del(interp2d)
1249 
1250 end subroutine soca_fields_colocate
1251 
1252 
1253 ! ------------------------------------------------------------------------------
1254 !> Number of elements to return in the serialized array
1255 !!
1256 !! \see soca_fields_serialize
1257 !! \relates soca_fields_mod::soca_fields
1258 subroutine soca_fields_serial_size(self, geom, vec_size)
1259  class(soca_fields), intent(in) :: self
1260  type(soca_geom), intent(in) :: geom !< todo remove, not needed?
1261  integer, intent(out) :: vec_size !< resulting size of vector
1262 
1263  integer :: i
1264 
1265  ! Loop over fields
1266  vec_size = 0
1267  do i=1,size(self%fields)
1268  vec_size = vec_size + size(self%fields(i)%val)
1269  end do
1270 
1271 end subroutine soca_fields_serial_size
1272 
1273 
1274 ! ------------------------------------------------------------------------------
1275 !> Return the fields as a serialized array
1276 !!
1277 !! \see soca_fields_serial_size
1278 !! \relates soca_fields_mod::soca_fields
1279 subroutine soca_fields_serialize(self, geom, vec_size, vec)
1280  class(soca_fields), intent(in) :: self
1281  type(soca_geom), intent(in) :: geom !< todo remove this, not needed?
1282  integer, intent(in) :: vec_size !< size of vector to return
1283  real(kind=kind_real), intent(out) :: vec(vec_size) !< fields as a serialized vector
1284 
1285  integer :: index, i, nn
1286 
1287  ! Loop over fields, levels and horizontal points
1288  index = 1
1289  do i=1,size(self%fields)
1290  nn = size(self%fields(i)%val)
1291  vec(index:index+nn-1) = reshape(self%fields(i)%val, (/ nn /) )
1292  index = index + nn
1293  end do
1294 
1295 end subroutine soca_fields_serialize
1296 
1297 ! ------------------------------------------------------------------------------
1298 !> Deserialize, creating fields from a single serialized array
1299 !!
1300 !! \see soca_fields_serialize
1301 !! \relates soca_fields_mod::soca_fields
1302 subroutine soca_fields_deserialize(self, geom, vec_size, vec, index)
1303  class(soca_fields), intent(inout) :: self
1304  type(soca_geom), intent(in) :: geom !< todo remove this, not needed?
1305  integer, intent(in) :: vec_size !< size of \p vec
1306  real(kind=kind_real), intent(in) :: vec(vec_size) !< vector to deserialize
1307  integer, intent(inout) :: index !< index in \p vec at which to start deserializing
1308 
1309  integer :: i, nn
1310 
1311  ! Loop over fields, levels and horizontal points
1312  do i=1,size(self%fields)
1313  nn = size(self%fields(i)%val)
1314  self%fields(i)%val = reshape(vec(index+1:index+1+nn), shape(self%fields(i)%val))
1315  index = index + nn
1316  end do
1317 
1318 end subroutine soca_fields_deserialize
1319 
1320 
1321 ! ------------------------------------------------------------------------------
1322 ! Internal module functions/subroutines
1323 ! ------------------------------------------------------------------------------
1324 
1325 ! ------------------------------------------------------------------------------
1326 !> Calculate min/max/mean statistics for a given field, using a mask.
1327 !!
1328 !! \param[in] fld : the field to calculate the statistics on
1329 !! \param[in] mask : statistics are only calculated where \p mask is \c .true.
1330 !! \param[out] info : [0] = min, [1] = max, [2] = average
1331 subroutine fldinfo(fld, mask, info)
1332  real(kind=kind_real), intent(in) :: fld(:,:,:)
1333  logical, intent(in) :: mask(:,:)
1334  real(kind=kind_real), intent(out) :: info(3)
1335 
1336  integer :: z
1337  real(kind=kind_real) :: tmp(3,size(fld, dim=3))
1338 
1339  ! calculate the min/max/sum separately for each masked level
1340  do z = 1, size(tmp, dim=2)
1341  tmp(1,z) = minval(fld(:,:,z), mask=mask)
1342  tmp(2,z) = maxval(fld(:,:,z), mask=mask)
1343  tmp(3,z) = sum( fld(:,:,z), mask=mask) / size(fld, dim=3)
1344  end do
1345 
1346  ! then combine the min/max/sum over all levels
1347  info(1) = minval(tmp(1,:))
1348  info(2) = maxval(tmp(2,:))
1349  info(3) = sum( tmp(3,:))
1350 end subroutine fldinfo
1351 
1352 
1353 ! ------------------------------------------------------------------------------
1354 !> Generate filename (based on oops/qg)
1355 !!
1356 !! The configuration \p f_conf is expected to provide the following
1357 !! - "datadir" : the directory the filenames should be prefixed with
1358 !! - "exp" : experiment name
1359 !! - "type" : one of "fc", "an", "incr", "ens"
1360 !! - "member" : required only if "type == ens"
1361 function soca_genfilename (f_conf,length,vdate,domain_type)
1362  type(fckit_configuration), intent(in) :: f_conf
1363  integer, intent(in) :: length
1364  type(datetime), intent(in) :: vdate
1365  character(len=3), optional, intent(in) :: domain_type
1366 
1367  character(len=length) :: soca_genfilename
1368  character(len=length) :: fdbdir, expver, typ, validitydate, referencedate, sstep, &
1369  & prefix, mmb
1370  type(datetime) :: rdate
1371  type(duration) :: step
1372  integer lenfn
1373  character(len=:), allocatable :: str
1374 
1375  call f_conf%get_or_die("datadir", str)
1376  fdbdir = str
1377  call f_conf%get_or_die("exp", str)
1378  expver = str
1379  call f_conf%get_or_die("type", str)
1380  typ = str
1381 
1382  if (present(domain_type)) then
1383  expver = trim(domain_type)//"."//expver
1384  else
1385  expver = "ocn.ice."//expver
1386  end if
1387  if (typ=="ens") then
1388  call f_conf%get_or_die("member", str)
1389  mmb = str
1390  lenfn = len_trim(fdbdir) + 1 + len_trim(expver) + 1 + len_trim(typ) + 1 + len_trim(mmb)
1391  prefix = trim(fdbdir) // "/" // trim(expver) // "." // trim(typ) // "." // trim(mmb)
1392  else
1393  lenfn = len_trim(fdbdir) + 1 + len_trim(expver) + 1 + len_trim(typ)
1394  prefix = trim(fdbdir) // "/" // trim(expver) // "." // trim(typ)
1395  endif
1396 
1397  if (typ=="fc" .or. typ=="ens") then
1398  call f_conf%get_or_die("date", str)
1399  referencedate = str
1400  call datetime_to_string(vdate, validitydate)
1401  call datetime_create(trim(referencedate), rdate)
1402  call datetime_diff(vdate, rdate, step)
1403  call duration_to_string(step, sstep)
1404  lenfn = lenfn + 1 + len_trim(referencedate) + 1 + len_trim(sstep)
1405  soca_genfilename = trim(prefix) // "." // trim(referencedate) // "." // trim(sstep)
1406  endif
1407 
1408  if (typ=="an" .or. typ=="incr") then
1409  call datetime_to_string(vdate, validitydate)
1410  lenfn = lenfn + 1 + len_trim(validitydate)
1411  soca_genfilename = trim(prefix) // "." // trim(validitydate)
1412  endif
1413 
1414  if (lenfn>length) &
1415  & call abor1_ftn("fields:genfilename: filename too long")
1416 
1417  if ( allocated(str) ) deallocate(str)
1418 
1419 end function soca_genfilename
1420 
1421 
1422 end module soca_fields_mod
Metadata for soca_fields.
Handle fields for the model.
character(len=length) function soca_genfilename(f_conf, length, vdate, domain_type)
Generate filename (based on oops/qg)
subroutine fldinfo(fld, mask, info)
Calculate min/max/mean statistics for a given field, using a mask.
Geometry module.
various utility functions
Definition: soca_utils.F90:7
real(kind=kind_real) function, public soca_mld(sp, pt, p, lon, lat)
calculate mixed layer depth from temp/salinity profile
Definition: soca_utils.F90:64
Holds all of the user configurable meta data associated with a single field.
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.