MOM6
MOM_diag_mediator.F90
1 !> The subroutines here provide convenient wrappers to the fms diag_manager
2 !! interfaces with additional diagnostic capabilies.
4 
5 ! This file is part of MOM6. See LICENSE.md for the license.
6 
7 use mom_checksums, only : chksum0, zchksum
9 use mom_coms, only : pe_here
10 use mom_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end
11 use mom_cpu_clock, only : clock_module, clock_routine
12 use mom_error_handler, only : mom_error, fatal, warning, is_root_pe, assert
14 use mom_grid, only : ocean_grid_type
15 use mom_io, only : slasher, vardesc, query_vardesc, mom_read_data
16 use mom_io, only : get_filename_appendix
18 use mom_string_functions, only : lowercase
19 use mom_time_manager, only : time_type
22 use mom_eos, only : eos_type
24 use mom_diag_remap, only : diag_remap_update
25 use mom_diag_remap, only : diag_remap_calc_hmask
26 use mom_diag_remap, only : diag_remap_init, diag_remap_end, diag_remap_do_remap
27 use mom_diag_remap, only : vertically_reintegrate_diag_field, vertically_interpolate_diag_field
28 use mom_diag_remap, only : diag_remap_configure_axes, diag_remap_axes_configured
29 use mom_diag_remap, only : diag_remap_get_axes_info, diag_remap_set_active
30 use mom_diag_remap, only : diag_remap_diag_registration_closed
31 use mom_diag_remap, only : horizontally_average_diag_field
32 
33 use diag_axis_mod, only : get_diag_axis_name
34 use diag_data_mod, only : null_axis_id
35 use diag_manager_mod, only : diag_manager_init, diag_manager_end
36 use diag_manager_mod, only : send_data, diag_axis_init, diag_field_add_attribute
37 ! The following module is needed for PGI since the following line does not compile with PGI 6.5.0
38 ! was: use diag_manager_mod, only : register_diag_field_fms=>register_diag_field
40 use diag_manager_mod, only : register_static_field_fms=>register_static_field
41 use diag_manager_mod, only : get_diag_field_id_fms=>get_diag_field_id
42 use diag_manager_mod, only : diag_field_not_found
43 
44 implicit none ; private
45 
46 #undef __DO_SAFETY_CHECKS__
47 #define IMPLIES(A, B) ((.not. (A)) .or. (B))
48 #define MAX_DSAMP_LEV 2
49 
50 public set_axes_info, post_data, register_diag_field, time_type
51 public set_masks_for_axes
52 public post_data_1d_k
54 public enable_averaging, disable_averaging, query_averaging_enabled
55 public diag_mediator_init, diag_mediator_end, set_diag_mediator_grid
56 public diag_mediator_infrastructure_init
57 public diag_mediator_close_registration, get_diag_time_end
58 public diag_axis_init, ocean_register_diag, register_static_field
59 public register_scalar_field
60 public define_axes_group, diag_masks_set
61 public diag_register_area_ids
62 public register_cell_measure, diag_associate_volume_cell_measure
63 public diag_get_volume_cell_measure_dm_id
64 public diag_set_state_ptrs, diag_update_remap_grids
65 public diag_grid_storage_init, diag_grid_storage_end
66 public diag_copy_diag_to_storage, diag_copy_storage_to_diag
67 public diag_save_grids, diag_restore_grids
68 
69 !> Make a diagnostic available for averaging or output.
70 interface post_data
71  module procedure post_data_3d, post_data_2d, post_data_1d_k, post_data_0d
72 end interface post_data
73 
74 !> Down sample a field
76  module procedure downsample_field_2d, downsample_field_3d
77 end interface downsample_field
78 
79 !> Down sample the mask of a field
80 interface downsample_mask
81  module procedure downsample_mask_2d, downsample_mask_3d
82 end interface downsample_mask
83 
84 !> Down sample a diagnostic field
86  module procedure downsample_diag_field_2d, downsample_diag_field_3d
87 end interface downsample_diag_field
88 
89 !> Contained for down sampled masks
90 type, private :: diag_dsamp
91  real, pointer, dimension(:,:) :: mask2d => null() !< Mask for 2d (x-y) axes
92  real, pointer, dimension(:,:,:) :: mask3d => null() !< Mask for 3d axes
93 end type diag_dsamp
94 
95 !> A group of 1D axes that comprise a 1D/2D/3D mesh
96 type, public :: axes_grp
97  character(len=15) :: id !< The id string for this particular combination of handles.
98  integer :: rank !< Number of dimensions in the list of axes.
99  integer, dimension(:), allocatable :: handles !< Handles to 1D axes.
100  type(diag_ctrl), pointer :: diag_cs => null() !< Circular link back to the main diagnostics control structure
101  !! (Used to avoid passing said structure into every possible call).
102  ! ID's for cell_methods
103  character(len=9) :: x_cell_method = '' !< Default nature of data representation, if axes group
104  !! includes x-direction.
105  character(len=9) :: y_cell_method = '' !< Default nature of data representation, if axes group
106  !! includes y-direction.
107  character(len=9) :: v_cell_method = '' !< Default nature of data representation, if axes group
108  !! includes vertical direction.
109  ! For remapping
110  integer :: nz = 0 !< Vertical dimension of diagnostic
111  integer :: vertical_coordinate_number = 0 !< Index of the corresponding diag_remap_ctrl for this axis group
112  ! For detecting position on the grid
113  logical :: is_h_point = .false. !< If true, indicates that this axes group is for an h-point located field.
114  logical :: is_q_point = .false. !< If true, indicates that this axes group is for a q-point located field.
115  logical :: is_u_point = .false. !< If true, indicates that this axes group is for a u-point located field.
116  logical :: is_v_point = .false. !< If true, indicates that this axes group is for a v-point located field.
117  logical :: is_layer = .false. !< If true, indicates that this axes group is for a layer vertically-located field.
118  logical :: is_interface = .false. !< If true, indicates that this axes group is for an interface
119  !! vertically-located field.
120  logical :: is_native = .true. !< If true, indicates that this axes group is for a native model grid.
121  !! False for any other grid. Used for rank>2.
122  logical :: needs_remapping = .false. !< If true, indicates that this axes group is for a intensive layer-located
123  !! field that must be remapped to these axes. Used for rank>2.
124  logical :: needs_interpolating = .false. !< If true, indicates that this axes group is for a sampled
125  !! interface-located field that must be interpolated to
126  !! these axes. Used for rank>2.
127  integer :: downsample_level = 1 !< If greater than 1, the factor by which this diagnostic/axes/masks be downsampled
128  ! For horizontally averaged diagnositcs (applies to 2d and 3d fields only)
129  type(axes_grp), pointer :: xyave_axes => null() !< The associated 1d axes for horizontall area-averaged diagnostics
130  ! ID's for cell_measures
131  integer :: id_area = -1 !< The diag_manager id for area to be used for cell_measure of variables with this axes_grp.
132  integer :: id_volume = -1 !< The diag_manager id for volume to be used for cell_measure of variables
133  !! with this axes_grp.
134  ! For masking
135  real, pointer, dimension(:,:) :: mask2d => null() !< Mask for 2d (x-y) axes
136  real, pointer, dimension(:,:,:) :: mask3d => null() !< Mask for 3d axes
137  type(diag_dsamp), dimension(2:MAX_DSAMP_LEV) :: dsamp !< Downsample container
138 end type axes_grp
139 
140 !> Contains an array to store a diagnostic target grid
141 type, private :: diag_grids_type
142  real, dimension(:,:,:), allocatable :: h !< Target grid for remapped coordinate
143 end type diag_grids_type
144 
145 !> Stores all the remapping grids and the model's native space thicknesses
146 type, public :: diag_grid_storage
147  integer :: num_diag_coords !< Number of target coordinates
148  real, dimension(:,:,:), allocatable :: h_state !< Layer thicknesses in native space
149  type(diag_grids_type), dimension(:), allocatable :: diag_grids !< Primarily empty, except h field
150 end type diag_grid_storage
151 
152 ! Integers to encode the total cell methods
153 !integer :: PPP=111 ! x:point,y:point,z:point, this kind of diagnostic is not currently present in diag_table.MOM6
154 !integer :: PPS=112 ! x:point,y:point,z:sum , this kind of diagnostic is not currently present in diag_table.MOM6
155 !integer :: PPM=113 ! x:point,y:point,z:mean , this kind of diagnostic is not currently present in diag_table.MOM6
156 integer :: psp=121 !< x:point,y:sum,z:point
157 integer :: pss=122 !< x:point,y:sum,z:point
158 integer :: psm=123 !< x:point,y:sum,z:mean
159 integer :: pmp=131 !< x:point,y:mean,z:point
160 integer :: pmm=133 !< x:point,y:mean,z:mean
161 integer :: spp=211 !< x:sum,y:point,z:point
162 integer :: sps=212 !< x:sum,y:point,z:sum
163 integer :: ssp=221 !< x:sum;y:sum,z:point
164 integer :: mpp=311 !< x:mean,y:point,z:point
165 integer :: mpm=313 !< x:mean,y:point,z:mean
166 integer :: mmp=331 !< x:mean,y:mean,z:point
167 integer :: mms=332 !< x:mean,y:mean,z:sum
168 integer :: sss=222 !< x:sum,y:sum,z:sum
169 integer :: mmm=333 !< x:mean,y:mean,z:mean
170 integer :: msk=-1 !< Use the downsample method of a mask
171 
172 !> This type is used to represent a diagnostic at the diag_mediator level.
173 !!
174 !! There can be both 'primary' and 'seconday' diagnostics. The primaries
175 !! reside in the diag_cs%diags array. They have an id which is an index
176 !! into this array. The secondaries are 'variations' on the primary diagnostic.
177 !! For example the CMOR diagnostics are secondary. The secondary diagnostics
178 !! are kept in a list with the primary diagnostic as the head.
179 type, private :: diag_type
180  logical :: in_use !< True if this entry is being used.
181  integer :: fms_diag_id !< Underlying FMS diag_manager id.
182  integer :: fms_xyave_diag_id = -1 !< For a horizontally area-averaged diagnostic.
183  integer :: downsample_diag_id = -1 !< For a horizontally area-downsampled diagnostic.
184  character(64) :: debug_str = '' !< For FATAL errors and debugging.
185  type(axes_grp), pointer :: axes => null() !< The axis group for this diagnostic
186  type(diag_type), pointer :: next => null() !< Pointer to the next diagnostic
187  real :: conversion_factor = 0. !< A factor to multiply data by before posting to FMS, if non-zero.
188  logical :: v_extensive = .false. !< True for vertically extensive fields (vertically integrated).
189  !! False for intensive (concentrations).
190  integer :: xyz_method = 0 !< A 3 digit integer encoding the diagnostics cell method
191  !! It can be used to determine the downsample algorithm
192 end type diag_type
193 
194 !> Container for down sampling information
196  integer :: isc !< The start i-index of cell centers within the computational domain
197  integer :: iec !< The end i-index of cell centers within the computational domain
198  integer :: jsc !< The start j-index of cell centers within the computational domain
199  integer :: jec !< The end j-index of cell centers within the computational domain
200  integer :: isd !< The start i-index of cell centers within the data domain
201  integer :: ied !< The end i-index of cell centers within the data domain
202  integer :: jsd !< The start j-index of cell centers within the data domain
203  integer :: jed !< The end j-index of cell centers within the data domain
204  integer :: isg !< The start i-index of cell centers within the global domain
205  integer :: ieg !< The end i-index of cell centers within the global domain
206  integer :: jsg !< The start j-index of cell centers within the global domain
207  integer :: jeg !< The end j-index of cell centers within the global domain
208  integer :: isgb !< The start i-index of cell corners within the global domain
209  integer :: iegb !< The end i-index of cell corners within the global domain
210  integer :: jsgb !< The start j-index of cell corners within the global domain
211  integer :: jegb !< The end j-index of cell corners within the global domain
212 
213  !>@{ Axes for each location on a diagnostic grid
214  type(axes_grp) :: axesbl, axestl, axescul, axescvl
215  type(axes_grp) :: axesbi, axesti, axescui, axescvi
216  type(axes_grp) :: axesb1, axest1, axescu1, axescv1
217  type(axes_grp), dimension(:), allocatable :: remap_axestl, remap_axesbl, remap_axescul, remap_axescvl
218  type(axes_grp), dimension(:), allocatable :: remap_axesti, remap_axesbi, remap_axescui, remap_axescvi
219  !!@}
220 
221  real, dimension(:,:), pointer :: mask2dt => null() !< 2D mask array for cell-center points
222  real, dimension(:,:), pointer :: mask2dbu => null() !< 2D mask array for cell-corner points
223  real, dimension(:,:), pointer :: mask2dcu => null() !< 2D mask array for east-face points
224  real, dimension(:,:), pointer :: mask2dcv => null() !< 2D mask array for north-face points
225  !>@{ 3D mask arrays for diagnostics at layers (mask...L) and interfaces (mask...i)
226  real, dimension(:,:,:), pointer :: mask3dtl => null()
227  real, dimension(:,:,:), pointer :: mask3dbl => null()
228  real, dimension(:,:,:), pointer :: mask3dcul => null()
229  real, dimension(:,:,:), pointer :: mask3dcvl => null()
230  real, dimension(:,:,:), pointer :: mask3dti => null()
231  real, dimension(:,:,:), pointer :: mask3dbi => null()
232  real, dimension(:,:,:), pointer :: mask3dcui => null()
233  real, dimension(:,:,:), pointer :: mask3dcvi => null()
234  !!@}
235 end type diagcs_dsamp
236 
237 !> The following data type a list of diagnostic fields an their variants,
238 !! as well as variables that control the handling of model output.
239 type, public :: diag_ctrl
240  integer :: available_diag_doc_unit = -1 !< The unit number of a diagnostic documentation file.
241  !! This file is open if available_diag_doc_unit is > 0.
242  integer :: chksum_iounit = -1 !< The unit number of a diagnostic documentation file.
243  !! This file is open if available_diag_doc_unit is > 0.
244  logical :: diag_as_chksum !< If true, log chksums in a text file instead of posting diagnostics
245 
246 ! The following fields are used for the output of the data.
247  integer :: is !< The start i-index of cell centers within the computational domain
248  integer :: ie !< The end i-index of cell centers within the computational domain
249  integer :: js !< The start j-index of cell centers within the computational domain
250  integer :: je !< The end j-index of cell centers within the computational domain
251 
252  integer :: isd !< The start i-index of cell centers within the data domain
253  integer :: ied !< The end i-index of cell centers within the data domain
254  integer :: jsd !< The start j-index of cell centers within the data domain
255  integer :: jed !< The end j-index of cell centers within the data domain
256  real :: time_int !< The time interval for any fields
257  !! that are offered for averaging [s].
258  type(time_type) :: time_end !< The end time of the valid
259  !! interval for any offered field.
260  logical :: ave_enabled = .false. !< True if averaging is enabled.
261 
262  !>@{ The following are 3D and 2D axis groups defined for output. The names
263  !! indicate the horizontal (B, T, Cu, or Cv) and vertical (L, i, or 1) locations.
264  type(axes_grp) :: axesbl, axestl, axescul, axescvl
265  type(axes_grp) :: axesbi, axesti, axescui, axescvi
266  type(axes_grp) :: axesb1, axest1, axescu1, axescv1
267  !!@}
268  type(axes_grp) :: axeszi !< A 1-D z-space axis at interfaces
269  type(axes_grp) :: axeszl !< A 1-D z-space axis at layer centers
270  type(axes_grp) :: axesnull !< An axis group for scalars
271 
272  real, dimension(:,:), pointer :: mask2dt => null() !< 2D mask array for cell-center points
273  real, dimension(:,:), pointer :: mask2dbu => null() !< 2D mask array for cell-corner points
274  real, dimension(:,:), pointer :: mask2dcu => null() !< 2D mask array for east-face points
275  real, dimension(:,:), pointer :: mask2dcv => null() !< 2D mask array for north-face points
276  !>@{ 3D mask arrays for diagnostics at layers (mask...L) and interfaces (mask...i)
277  real, dimension(:,:,:), pointer :: mask3dtl => null()
278  real, dimension(:,:,:), pointer :: mask3dbl => null()
279  real, dimension(:,:,:), pointer :: mask3dcul => null()
280  real, dimension(:,:,:), pointer :: mask3dcvl => null()
281  real, dimension(:,:,:), pointer :: mask3dti => null()
282  real, dimension(:,:,:), pointer :: mask3dbi => null()
283  real, dimension(:,:,:), pointer :: mask3dcui => null()
284  real, dimension(:,:,:), pointer :: mask3dcvi => null()
285 
286  type(diagcs_dsamp), dimension(2:MAX_DSAMP_LEV) :: dsamp !< Downsample control container
287 
288  !!@}
289 
290 ! Space for diagnostics is dynamically allocated as it is needed.
291 ! The chunk size is how much the array should grow on each new allocation.
292 #define DIAG_ALLOC_CHUNK_SIZE 100
293  type(diag_type), dimension(:), allocatable :: diags !< The list of diagnostics
294  integer :: next_free_diag_id !< The next unused diagnostic ID
295 
296  !> default missing value to be sent to ALL diagnostics registrations
297  real :: missing_value = -1.0e+34
298 
299  !> Number of diagnostic vertical coordinates (remapped)
300  integer :: num_diag_coords
301  !> Control structure for each possible coordinate
302  type(diag_remap_ctrl), dimension(:), allocatable :: diag_remap_cs
303  type(diag_grid_storage) :: diag_grid_temp !< Stores the remapped diagnostic grid
304  logical :: diag_grid_overridden = .false. !< True if the diagnostic grids have been overriden
305 
306  type(axes_grp), dimension(:), allocatable :: &
307  remap_axeszl, & !< The 1-D z-space cell-centered axis for remapping
308  remap_axeszi !< The 1-D z-space interface axis for remapping
309  !!@{
310  type(axes_grp), dimension(:), allocatable :: remap_axestl, remap_axesbl, remap_axescul, remap_axescvl
311  type(axes_grp), dimension(:), allocatable :: remap_axesti, remap_axesbi, remap_axescui, remap_axescvi
312  !!@}
313 
314  ! Pointer to H, G and T&S needed for remapping
315  real, dimension(:,:,:), pointer :: h => null() !< The thicknesses needed for remapping
316  real, dimension(:,:,:), pointer :: t => null() !< The temperatures needed for remapping
317  real, dimension(:,:,:), pointer :: s => null() !< The salinities needed for remapping
318  type(eos_type), pointer :: eqn_of_state => null() !< The equation of state type
319  type(ocean_grid_type), pointer :: g => null() !< The ocean grid type
320  type(verticalgrid_type), pointer :: gv => null() !< The model's vertical ocean grid
321  type(unit_scale_type), pointer :: us => null() !< A dimensional unit scaling type
322 
323  !> The volume cell measure (special diagnostic) manager id
324  integer :: volume_cell_measure_dm_id = -1
325 
326 #if defined(DEBUG) || defined(__DO_SAFETY_CHECKS__)
327  ! Keep a copy of h so that we know whether it has changed. If it has then
328  ! need the target grid for vertical remapping needs to have been updated.
329  real, dimension(:,:,:), allocatable :: h_old
330 #endif
331 
332  !> Number of checksum-only diagnostics
333  integer :: num_chksum_diags
334 
335 end type diag_ctrl
336 
337 ! CPU clocks
338 integer :: id_clock_diag_mediator, id_clock_diag_remap, id_clock_diag_grid_updates
339 
340 contains
341 
342 !> Sets up diagnostics axes
343 subroutine set_axes_info(G, GV, US, param_file, diag_cs, set_vertical)
344  type(ocean_grid_type), intent(inout) :: g !< Ocean grid structure
345  type(verticalgrid_type), intent(in) :: gv !< ocean vertical grid structure
346  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
347  type(param_file_type), intent(in) :: param_file !< Parameter file structure
348  type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
349  logical, optional, intent(in) :: set_vertical !< If true or missing, set up
350  !! vertical axes
351  ! Local variables
352  integer :: id_xq, id_yq, id_zl, id_zi, id_xh, id_yh
353  integer :: id_zl_native, id_zi_native
354  integer :: i, j, k, nz
355  real :: zlev(gv%ke), zinter(gv%ke+1)
356  logical :: set_vert
357 
358  set_vert = .true. ; if (present(set_vertical)) set_vert = set_vertical
359 
360  ! Horizontal axes for the native grids
361  if (g%symmetric) then
362  id_xq = diag_axis_init('xq', g%gridLonB(g%isgB:g%iegB), g%x_axis_units, 'x', &
363  'q point nominal longitude', domain2=g%Domain%mpp_domain)
364  id_yq = diag_axis_init('yq', g%gridLatB(g%jsgB:g%jegB), g%y_axis_units, 'y', &
365  'q point nominal latitude', domain2=g%Domain%mpp_domain)
366  else
367  id_xq = diag_axis_init('xq', g%gridLonB(g%isg:g%ieg), g%x_axis_units, 'x', &
368  'q point nominal longitude', domain2=g%Domain%mpp_domain)
369  id_yq = diag_axis_init('yq', g%gridLatB(g%jsg:g%jeg), g%y_axis_units, 'y', &
370  'q point nominal latitude', domain2=g%Domain%mpp_domain)
371  endif
372  id_xh = diag_axis_init('xh', g%gridLonT(g%isg:g%ieg), g%x_axis_units, 'x', &
373  'h point nominal longitude', domain2=g%Domain%mpp_domain)
374  id_yh = diag_axis_init('yh', g%gridLatT(g%jsg:g%jeg), g%y_axis_units, 'y', &
375  'h point nominal latitude', domain2=g%Domain%mpp_domain)
376 
377  if (set_vert) then
378  nz = gv%ke
379  zinter(1:nz+1) = gv%sInterface(1:nz+1)
380  zlev(1:nz) = gv%sLayer(1:nz)
381  id_zl = diag_axis_init('zl', zlev, trim(gv%zAxisUnits), 'z', &
382  'Layer '//trim(gv%zAxisLongName), &
383  direction=gv%direction)
384  id_zi = diag_axis_init('zi', zinter, trim(gv%zAxisUnits), 'z', &
385  'Interface '//trim(gv%zAxisLongName), &
386  direction=gv%direction)
387  else
388  id_zl = -1 ; id_zi = -1
389  endif
390  id_zl_native = id_zl ; id_zi_native = id_zi
391  ! Vertical axes for the interfaces and layers
392  call define_axes_group(diag_cs, (/ id_zi /), diag_cs%axesZi, &
393  v_cell_method='point', is_interface=.true.)
394  call define_axes_group(diag_cs, (/ id_zl /), diag_cs%axesZL, &
395  v_cell_method='mean', is_layer=.true.)
396 
397  ! Axis groupings for the model layers
398  call define_axes_group(diag_cs, (/ id_xh, id_yh, id_zl /), diag_cs%axesTL, &
399  x_cell_method='mean', y_cell_method='mean', v_cell_method='mean', &
400  is_h_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
401  call define_axes_group(diag_cs, (/ id_xq, id_yq, id_zl /), diag_cs%axesBL, &
402  x_cell_method='point', y_cell_method='point', v_cell_method='mean', &
403  is_q_point=.true., is_layer=.true.)
404  call define_axes_group(diag_cs, (/ id_xq, id_yh, id_zl /), diag_cs%axesCuL, &
405  x_cell_method='point', y_cell_method='mean', v_cell_method='mean', &
406  is_u_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
407  call define_axes_group(diag_cs, (/ id_xh, id_yq, id_zl /), diag_cs%axesCvL, &
408  x_cell_method='mean', y_cell_method='point', v_cell_method='mean', &
409  is_v_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
410 
411  ! Axis groupings for the model interfaces
412  call define_axes_group(diag_cs, (/ id_xh, id_yh, id_zi /), diag_cs%axesTi, &
413  x_cell_method='mean', y_cell_method='mean', v_cell_method='point', &
414  is_h_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
415  call define_axes_group(diag_cs, (/ id_xq, id_yq, id_zi /), diag_cs%axesBi, &
416  x_cell_method='point', y_cell_method='point', v_cell_method='point', &
417  is_q_point=.true., is_interface=.true.)
418  call define_axes_group(diag_cs, (/ id_xq, id_yh, id_zi /), diag_cs%axesCui, &
419  x_cell_method='point', y_cell_method='mean', v_cell_method='point', &
420  is_u_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
421  call define_axes_group(diag_cs, (/ id_xh, id_yq, id_zi /), diag_cs%axesCvi, &
422  x_cell_method='mean', y_cell_method='point', v_cell_method='point', &
423  is_v_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
424 
425  ! Axis groupings for 2-D arrays
426  call define_axes_group(diag_cs, (/ id_xh, id_yh /), diag_cs%axesT1, &
427  x_cell_method='mean', y_cell_method='mean', is_h_point=.true.)
428  call define_axes_group(diag_cs, (/ id_xq, id_yq /), diag_cs%axesB1, &
429  x_cell_method='point', y_cell_method='point', is_q_point=.true.)
430  call define_axes_group(diag_cs, (/ id_xq, id_yh /), diag_cs%axesCu1, &
431  x_cell_method='point', y_cell_method='mean', is_u_point=.true.)
432  call define_axes_group(diag_cs, (/ id_xh, id_yq /), diag_cs%axesCv1, &
433  x_cell_method='mean', y_cell_method='point', is_v_point=.true.)
434 
435  ! Axis group for special null axis from diag manager
436  call define_axes_group(diag_cs, (/ null_axis_id /), diag_cs%axesNull)
437 
438 
439  !Non-native Non-downsampled
440  if (diag_cs%num_diag_coords>0) then
441  allocate(diag_cs%remap_axesZL(diag_cs%num_diag_coords))
442  allocate(diag_cs%remap_axesTL(diag_cs%num_diag_coords))
443  allocate(diag_cs%remap_axesBL(diag_cs%num_diag_coords))
444  allocate(diag_cs%remap_axesCuL(diag_cs%num_diag_coords))
445  allocate(diag_cs%remap_axesCvL(diag_cs%num_diag_coords))
446  allocate(diag_cs%remap_axesZi(diag_cs%num_diag_coords))
447  allocate(diag_cs%remap_axesTi(diag_cs%num_diag_coords))
448  allocate(diag_cs%remap_axesBi(diag_cs%num_diag_coords))
449  allocate(diag_cs%remap_axesCui(diag_cs%num_diag_coords))
450  allocate(diag_cs%remap_axesCvi(diag_cs%num_diag_coords))
451  endif
452 
453  do i=1, diag_cs%num_diag_coords
454  ! For each possible diagnostic coordinate
455  call diag_remap_configure_axes(diag_cs%diag_remap_cs(i), gv, us, param_file)
456 
457  ! This vertical coordinate has been configured so can be used.
458  if (diag_remap_axes_configured(diag_cs%diag_remap_cs(i))) then
459 
460  ! This fetches the 1D-axis id for layers and interfaces and overwrite
461  ! id_zl and id_zi from above. It also returns the number of layers.
462  call diag_remap_get_axes_info(diag_cs%diag_remap_cs(i), nz, id_zl, id_zi)
463 
464  ! Axes for z layers
465  call define_axes_group(diag_cs, (/ id_zl /), diag_cs%remap_axesZL(i), &
466  nz=nz, vertical_coordinate_number=i, &
467  v_cell_method='mean', &
468  is_h_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true.)
469  call define_axes_group(diag_cs, (/ id_xh, id_yh, id_zl /), diag_cs%remap_axesTL(i), &
470  nz=nz, vertical_coordinate_number=i, &
471  x_cell_method='mean', y_cell_method='mean', v_cell_method='mean', &
472  is_h_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
473  xyave_axes=diag_cs%remap_axesZL(i))
474 
475  !! \note Remapping for B points is not yet implemented so needs_remapping is not
476  !! provided for remap_axesBL
477  call define_axes_group(diag_cs, (/ id_xq, id_yq, id_zl /), diag_cs%remap_axesBL(i), &
478  nz=nz, vertical_coordinate_number=i, &
479  x_cell_method='point', y_cell_method='point', v_cell_method='mean', &
480  is_q_point=.true., is_layer=.true., is_native=.false.)
481 
482  call define_axes_group(diag_cs, (/ id_xq, id_yh, id_zl /), diag_cs%remap_axesCuL(i), &
483  nz=nz, vertical_coordinate_number=i, &
484  x_cell_method='point', y_cell_method='mean', v_cell_method='mean', &
485  is_u_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
486  xyave_axes=diag_cs%remap_axesZL(i))
487 
488  call define_axes_group(diag_cs, (/ id_xh, id_yq, id_zl /), diag_cs%remap_axesCvL(i), &
489  nz=nz, vertical_coordinate_number=i, &
490  x_cell_method='mean', y_cell_method='point', v_cell_method='mean', &
491  is_v_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
492  xyave_axes=diag_cs%remap_axesZL(i))
493 
494  ! Axes for z interfaces
495  call define_axes_group(diag_cs, (/ id_zi /), diag_cs%remap_axesZi(i), &
496  nz=nz, vertical_coordinate_number=i, &
497  v_cell_method='point', &
498  is_h_point=.true., is_interface=.true., is_native=.false., needs_interpolating=.true.)
499  call define_axes_group(diag_cs, (/ id_xh, id_yh, id_zi /), diag_cs%remap_axesTi(i), &
500  nz=nz, vertical_coordinate_number=i, &
501  x_cell_method='mean', y_cell_method='mean', v_cell_method='point', &
502  is_h_point=.true., is_interface=.true., is_native=.false., needs_interpolating=.true., &
503  xyave_axes=diag_cs%remap_axesZi(i))
504 
505  !! \note Remapping for B points is not yet implemented so needs_remapping is not provided for remap_axesBi
506  call define_axes_group(diag_cs, (/ id_xq, id_yq, id_zi /), diag_cs%remap_axesBi(i), &
507  nz=nz, vertical_coordinate_number=i, &
508  x_cell_method='point', y_cell_method='point', v_cell_method='point', &
509  is_q_point=.true., is_interface=.true., is_native=.false.)
510 
511  call define_axes_group(diag_cs, (/ id_xq, id_yh, id_zi /), diag_cs%remap_axesCui(i), &
512  nz=nz, vertical_coordinate_number=i, &
513  x_cell_method='point', y_cell_method='mean', v_cell_method='point', &
514  is_u_point=.true., is_interface=.true., is_native=.false., &
515  needs_interpolating=.true., xyave_axes=diag_cs%remap_axesZi(i))
516 
517  call define_axes_group(diag_cs, (/ id_xh, id_yq, id_zi /), diag_cs%remap_axesCvi(i), &
518  nz=nz, vertical_coordinate_number=i, &
519  x_cell_method='mean', y_cell_method='point', v_cell_method='point', &
520  is_v_point=.true., is_interface=.true., is_native=.false., &
521  needs_interpolating=.true., xyave_axes=diag_cs%remap_axesZi(i))
522  endif
523  enddo
524 
525  !Define the downsampled axes
526  call set_axes_info_dsamp(g, gv, param_file, diag_cs, id_zl_native, id_zi_native)
527 
528  call diag_grid_storage_init(diag_cs%diag_grid_temp, g, diag_cs)
529 
530 end subroutine set_axes_info
531 
532 subroutine set_axes_info_dsamp(G, GV, param_file, diag_cs, id_zl_native, id_zi_native)
533  type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
534  type(verticalgrid_type), intent(in) :: GV !< ocean vertical grid structure
535  type(param_file_type), intent(in) :: param_file !< Parameter file structure
536  type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
537  integer, intent(in) :: id_zl_native !< ID of native layers
538  integer, intent(in) :: id_zi_native !< ID of native interfaces
539 
540  ! Local variables
541  integer :: id_xq, id_yq, id_zl, id_zi, id_xh, id_yh
542  integer :: i, j, k, nz, dl
543  real, dimension(:), pointer :: gridLonT_dsamp =>null()
544  real, dimension(:), pointer :: gridLatT_dsamp =>null()
545  real, dimension(:), pointer :: gridLonB_dsamp =>null()
546  real, dimension(:), pointer :: gridLatB_dsamp =>null()
547 
548  id_zl = id_zl_native ; id_zi = id_zi_native
549  !Axes group for native downsampled diagnostics
550  do dl=2,max_dsamp_lev
551  if(dl .ne. 2) call mom_error(fatal, "set_axes_info_dsamp: Downsample level other than 2 is not supported yet!")
552  if (g%symmetric) then
553  allocate(gridlonb_dsamp(diag_cs%dsamp(dl)%isgB:diag_cs%dsamp(dl)%iegB))
554  allocate(gridlatb_dsamp(diag_cs%dsamp(dl)%jsgB:diag_cs%dsamp(dl)%jegB))
555  do i=diag_cs%dsamp(dl)%isgB,diag_cs%dsamp(dl)%iegB; gridlonb_dsamp(i) = g%gridLonB(g%isgB+dl*i); enddo
556  do j=diag_cs%dsamp(dl)%jsgB,diag_cs%dsamp(dl)%jegB; gridlatb_dsamp(j) = g%gridLatB(g%jsgB+dl*j); enddo
557  id_xq = diag_axis_init('xq', gridlonb_dsamp, g%x_axis_units, 'x', &
558  'q point nominal longitude', domain2=g%Domain%mpp_domain_d2)
559  id_yq = diag_axis_init('yq', gridlatb_dsamp, g%y_axis_units, 'y', &
560  'q point nominal latitude', domain2=g%Domain%mpp_domain_d2)
561  deallocate(gridlonb_dsamp,gridlatb_dsamp)
562  else
563  allocate(gridlonb_dsamp(diag_cs%dsamp(dl)%isg:diag_cs%dsamp(dl)%ieg))
564  allocate(gridlatb_dsamp(diag_cs%dsamp(dl)%jsg:diag_cs%dsamp(dl)%jeg))
565  do i=diag_cs%dsamp(dl)%isg,diag_cs%dsamp(dl)%ieg; gridlonb_dsamp(i) = g%gridLonB(g%isg+dl*i-2); enddo
566  do j=diag_cs%dsamp(dl)%jsg,diag_cs%dsamp(dl)%jeg; gridlatb_dsamp(j) = g%gridLatB(g%jsg+dl*j-2); enddo
567  id_xq = diag_axis_init('xq', gridlonb_dsamp, g%x_axis_units, 'x', &
568  'q point nominal longitude', domain2=g%Domain%mpp_domain_d2)
569  id_yq = diag_axis_init('yq', gridlatb_dsamp, g%y_axis_units, 'y', &
570  'q point nominal latitude', domain2=g%Domain%mpp_domain_d2)
571  deallocate(gridlonb_dsamp,gridlatb_dsamp)
572  endif
573 
574  allocate(gridlont_dsamp(diag_cs%dsamp(dl)%isg:diag_cs%dsamp(dl)%ieg))
575  allocate(gridlatt_dsamp(diag_cs%dsamp(dl)%jsg:diag_cs%dsamp(dl)%jeg))
576  do i=diag_cs%dsamp(dl)%isg,diag_cs%dsamp(dl)%ieg; gridlont_dsamp(i) = g%gridLonT(g%isg+dl*i-2); enddo
577  do j=diag_cs%dsamp(dl)%jsg,diag_cs%dsamp(dl)%jeg; gridlatt_dsamp(j) = g%gridLatT(g%jsg+dl*j-2); enddo
578  id_xh = diag_axis_init('xh', gridlont_dsamp, g%x_axis_units, 'x', &
579  'h point nominal longitude', domain2=g%Domain%mpp_domain_d2)
580  id_yh = diag_axis_init('yh', gridlatt_dsamp, g%y_axis_units, 'y', &
581  'h point nominal latitude', domain2=g%Domain%mpp_domain_d2)
582 
583  deallocate(gridlont_dsamp,gridlatt_dsamp)
584 
585  ! Axis groupings for the model layers
586  call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yh, id_zl /), diag_cs%dsamp(dl)%axesTL, dl, &
587  x_cell_method='mean', y_cell_method='mean', v_cell_method='mean', &
588  is_h_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
589  call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yq, id_zl /), diag_cs%dsamp(dl)%axesBL, dl, &
590  x_cell_method='point', y_cell_method='point', v_cell_method='mean', &
591  is_q_point=.true., is_layer=.true.)
592  call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yh, id_zl /), diag_cs%dsamp(dl)%axesCuL, dl, &
593  x_cell_method='point', y_cell_method='mean', v_cell_method='mean', &
594  is_u_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
595  call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yq, id_zl /), diag_cs%dsamp(dl)%axesCvL, dl, &
596  x_cell_method='mean', y_cell_method='point', v_cell_method='mean', &
597  is_v_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
598 
599  ! Axis groupings for the model interfaces
600  call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yh, id_zi /), diag_cs%dsamp(dl)%axesTi, dl, &
601  x_cell_method='mean', y_cell_method='mean', v_cell_method='point', &
602  is_h_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
603  call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yq, id_zi /), diag_cs%dsamp(dl)%axesBi, dl, &
604  x_cell_method='point', y_cell_method='point', v_cell_method='point', &
605  is_q_point=.true., is_interface=.true.)
606  call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yh, id_zi /), diag_cs%dsamp(dl)%axesCui, dl, &
607  x_cell_method='point', y_cell_method='mean', v_cell_method='point', &
608  is_u_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
609  call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yq, id_zi /), diag_cs%dsamp(dl)%axesCvi, dl, &
610  x_cell_method='mean', y_cell_method='point', v_cell_method='point', &
611  is_v_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
612 
613  ! Axis groupings for 2-D arrays
614  call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yh /), diag_cs%dsamp(dl)%axesT1, dl, &
615  x_cell_method='mean', y_cell_method='mean', is_h_point=.true.)
616  call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yq /), diag_cs%dsamp(dl)%axesB1, dl, &
617  x_cell_method='point', y_cell_method='point', is_q_point=.true.)
618  call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yh /), diag_cs%dsamp(dl)%axesCu1, dl, &
619  x_cell_method='point', y_cell_method='mean', is_u_point=.true.)
620  call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yq /), diag_cs%dsamp(dl)%axesCv1, dl, &
621  x_cell_method='mean', y_cell_method='point', is_v_point=.true.)
622 
623  !Non-native axes
624  if (diag_cs%num_diag_coords>0) then
625  allocate(diag_cs%dsamp(dl)%remap_axesTL(diag_cs%num_diag_coords))
626  allocate(diag_cs%dsamp(dl)%remap_axesBL(diag_cs%num_diag_coords))
627  allocate(diag_cs%dsamp(dl)%remap_axesCuL(diag_cs%num_diag_coords))
628  allocate(diag_cs%dsamp(dl)%remap_axesCvL(diag_cs%num_diag_coords))
629  allocate(diag_cs%dsamp(dl)%remap_axesTi(diag_cs%num_diag_coords))
630  allocate(diag_cs%dsamp(dl)%remap_axesBi(diag_cs%num_diag_coords))
631  allocate(diag_cs%dsamp(dl)%remap_axesCui(diag_cs%num_diag_coords))
632  allocate(diag_cs%dsamp(dl)%remap_axesCvi(diag_cs%num_diag_coords))
633  endif
634 
635  do i=1, diag_cs%num_diag_coords
636  ! For each possible diagnostic coordinate
637  !call diag_remap_configure_axes(diag_cs%diag_remap_cs(i), GV, param_file)
638 
639  ! This vertical coordinate has been configured so can be used.
640  if (diag_remap_axes_configured(diag_cs%diag_remap_cs(i))) then
641 
642  ! This fetches the 1D-axis id for layers and interfaces and overwrite
643  ! id_zl and id_zi from above. It also returns the number of layers.
644  call diag_remap_get_axes_info(diag_cs%diag_remap_cs(i), nz, id_zl, id_zi)
645 
646  ! Axes for z layers
647  call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yh, id_zl /), diag_cs%dsamp(dl)%remap_axesTL(i), dl, &
648  nz=nz, vertical_coordinate_number=i, &
649  x_cell_method='mean', y_cell_method='mean', v_cell_method='mean', &
650  is_h_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
651  xyave_axes=diag_cs%remap_axesZL(i))
652 
653  !! \note Remapping for B points is not yet implemented so needs_remapping is not
654  !! provided for remap_axesBL
655  call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yq, id_zl /), diag_cs%dsamp(dl)%remap_axesBL(i), dl, &
656  nz=nz, vertical_coordinate_number=i, &
657  x_cell_method='point', y_cell_method='point', v_cell_method='mean', &
658  is_q_point=.true., is_layer=.true., is_native=.false.)
659 
660  call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yh, id_zl /), diag_cs%dsamp(dl)%remap_axesCuL(i), dl, &
661  nz=nz, vertical_coordinate_number=i, &
662  x_cell_method='point', y_cell_method='mean', v_cell_method='mean', &
663  is_u_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
664  xyave_axes=diag_cs%remap_axesZL(i))
665 
666  call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yq, id_zl /), diag_cs%dsamp(dl)%remap_axesCvL(i), dl, &
667  nz=nz, vertical_coordinate_number=i, &
668  x_cell_method='mean', y_cell_method='point', v_cell_method='mean', &
669  is_v_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
670  xyave_axes=diag_cs%remap_axesZL(i))
671 
672  ! Axes for z interfaces
673  call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yh, id_zi /), diag_cs%dsamp(dl)%remap_axesTi(i), dl, &
674  nz=nz, vertical_coordinate_number=i, &
675  x_cell_method='mean', y_cell_method='mean', v_cell_method='point', &
676  is_h_point=.true., is_interface=.true., is_native=.false., needs_interpolating=.true., &
677  xyave_axes=diag_cs%remap_axesZi(i))
678 
679  !! \note Remapping for B points is not yet implemented so needs_remapping is not provided for remap_axesBi
680  call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yq, id_zi /), diag_cs%dsamp(dl)%remap_axesBi(i), dl, &
681  nz=nz, vertical_coordinate_number=i, &
682  x_cell_method='point', y_cell_method='point', v_cell_method='point', &
683  is_q_point=.true., is_interface=.true., is_native=.false.)
684 
685  call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yh, id_zi /), diag_cs%dsamp(dl)%remap_axesCui(i), dl, &
686  nz=nz, vertical_coordinate_number=i, &
687  x_cell_method='point', y_cell_method='mean', v_cell_method='point', &
688  is_u_point=.true., is_interface=.true., is_native=.false., &
689  needs_interpolating=.true., xyave_axes=diag_cs%remap_axesZi(i))
690 
691  call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yq, id_zi /), diag_cs%dsamp(dl)%remap_axesCvi(i), dl, &
692  nz=nz, vertical_coordinate_number=i, &
693  x_cell_method='mean', y_cell_method='point', v_cell_method='point', &
694  is_v_point=.true., is_interface=.true., is_native=.false., &
695  needs_interpolating=.true., xyave_axes=diag_cs%remap_axesZi(i))
696  endif
697  enddo
698  enddo
699 
700 end subroutine set_axes_info_dsamp
701 
702 
703 !> set_masks_for_axes sets up the 2d and 3d masks for diagnostics using the current grid
704 !! recorded after calling diag_update_remap_grids()
705 subroutine set_masks_for_axes(G, diag_cs)
706  type(ocean_grid_type), target, intent(in) :: g !< The ocean grid type.
707  type(diag_ctrl), pointer :: diag_cs !< A pointer to a type with many variables
708  !! used for diagnostics
709  ! Local variables
710  integer :: c, nk, i, j, k, ii, jj
711  type(axes_grp), pointer :: axes => null(), h_axes => null() ! Current axes, for convenience
712 
713  do c=1, diag_cs%num_diag_coords
714  ! This vertical coordinate has been configured so can be used.
715  if (diag_remap_axes_configured(diag_cs%diag_remap_cs(c))) then
716 
717  ! Level/layer h-points in diagnostic coordinate
718  axes => diag_cs%remap_axesTL(c)
719  nk = axes%nz
720  allocate( axes%mask3d(g%isd:g%ied,g%jsd:g%jed,nk) ) ; axes%mask3d(:,:,:) = 0.
721  call diag_remap_calc_hmask(diag_cs%diag_remap_cs(c), g, axes%mask3d)
722 
723  h_axes => diag_cs%remap_axesTL(c) ! Use the h-point masks to generate the u-, v- and q- masks
724 
725  ! Level/layer u-points in diagnostic coordinate
726  axes => diag_cs%remap_axesCuL(c)
727  call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at u-layers')
728  call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
729  allocate( axes%mask3d(g%IsdB:g%IedB,g%jsd:g%jed,nk) ) ; axes%mask3d(:,:,:) = 0.
730  do k = 1, nk ; do j=g%jsc,g%jec ; do i=g%isc-1,g%iec
731  if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i+1,j,k) > 0.) axes%mask3d(i,j,k) = 1.
732  enddo ; enddo ; enddo
733 
734  ! Level/layer v-points in diagnostic coordinate
735  axes => diag_cs%remap_axesCvL(c)
736  call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at v-layers')
737  call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
738  allocate( axes%mask3d(g%isd:g%ied,g%JsdB:g%JedB,nk) ) ; axes%mask3d(:,:,:) = 0.
739  do k = 1, nk ; do j=g%jsc-1,g%jec ; do i=g%isc,g%iec
740  if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i,j+1,k) > 0.) axes%mask3d(i,j,k) = 1.
741  enddo ; enddo ; enddo
742 
743  ! Level/layer q-points in diagnostic coordinate
744  axes => diag_cs%remap_axesBL(c)
745  call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at q-layers')
746  call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
747  allocate( axes%mask3d(g%IsdB:g%IedB,g%JsdB:g%JedB,nk) ) ; axes%mask3d(:,:,:) = 0.
748  do k = 1, nk ; do j=g%jsc-1,g%jec ; do i=g%isc-1,g%iec
749  if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i+1,j+1,k) + &
750  h_axes%mask3d(i+1,j,k) + h_axes%mask3d(i,j+1,k) > 0.) axes%mask3d(i,j,k) = 1.
751  enddo ; enddo ; enddo
752 
753  ! Interface h-points in diagnostic coordinate (w-point)
754  axes => diag_cs%remap_axesTi(c)
755  call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at h-interfaces')
756  call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
757  allocate( axes%mask3d(g%isd:g%ied,g%jsd:g%jed,nk+1) ) ; axes%mask3d(:,:,:) = 0.
758  do j=g%jsc-1,g%jec+1 ; do i=g%isc-1,g%iec+1
759  if (h_axes%mask3d(i,j,1) > 0.) axes%mask3d(i,j,1) = 1.
760  do k = 2, nk
761  if (h_axes%mask3d(i,j,k-1) + h_axes%mask3d(i,j,k) > 0.) axes%mask3d(i,j,k) = 1.
762  enddo
763  if (h_axes%mask3d(i,j,nk) > 0.) axes%mask3d(i,j,nk+1) = 1.
764  enddo ; enddo
765 
766  h_axes => diag_cs%remap_axesTi(c) ! Use the w-point masks to generate the u-, v- and q- masks
767 
768  ! Interface u-points in diagnostic coordinate
769  axes => diag_cs%remap_axesCui(c)
770  call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at u-interfaces')
771  call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
772  allocate( axes%mask3d(g%IsdB:g%IedB,g%jsd:g%jed,nk+1) ) ; axes%mask3d(:,:,:) = 0.
773  do k = 1, nk+1 ; do j=g%jsc,g%jec ; do i=g%isc-1,g%iec
774  if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i+1,j,k) > 0.) axes%mask3d(i,j,k) = 1.
775  enddo ; enddo ; enddo
776 
777  ! Interface v-points in diagnostic coordinate
778  axes => diag_cs%remap_axesCvi(c)
779  call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at v-interfaces')
780  call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
781  allocate( axes%mask3d(g%isd:g%ied,g%JsdB:g%JedB,nk+1) ) ; axes%mask3d(:,:,:) = 0.
782  do k = 1, nk+1 ; do j=g%jsc-1,g%jec ; do i=g%isc,g%iec
783  if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i,j+1,k) > 0.) axes%mask3d(i,j,k) = 1.
784  enddo ; enddo ; enddo
785 
786  ! Interface q-points in diagnostic coordinate
787  axes => diag_cs%remap_axesBi(c)
788  call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at q-interfaces')
789  call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
790  allocate( axes%mask3d(g%IsdB:g%IedB,g%JsdB:g%JedB,nk+1) ) ; axes%mask3d(:,:,:) = 0.
791  do k = 1, nk ; do j=g%jsc-1,g%jec ; do i=g%isc-1,g%iec
792  if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i+1,j+1,k) + &
793  h_axes%mask3d(i+1,j,k) + h_axes%mask3d(i,j+1,k) > 0.) axes%mask3d(i,j,k) = 1.
794  enddo ; enddo ; enddo
795  endif
796  enddo
797 
798  !Allocate and initialize the downsampled masks for the axes
799  call set_masks_for_axes_dsamp(g, diag_cs)
800 
801 end subroutine set_masks_for_axes
802 
803 subroutine set_masks_for_axes_dsamp(G, diag_cs)
804  type(ocean_grid_type), target, intent(in) :: G !< The ocean grid type.
805  type(diag_ctrl), pointer :: diag_cs !< A pointer to a type with many variables
806  !! used for diagnostics
807  ! Local variables
808  integer :: c, nk, i, j, k, ii, jj
809  integer :: dl
810  type(axes_grp), pointer :: axes => null(), h_axes => null() ! Current axes, for convenience
811 
812  !Each downsampled axis needs both downsampled and non-downsampled mask
813  !The downsampled mask is needed for sending out the diagnostics output via diag_manager
814  !The non-downsampled mask is needed for downsampling the diagnostics field
815  do dl=2,max_dsamp_lev
816  if(dl .ne. 2) call mom_error(fatal, "set_masks_for_axes_dsamp: Downsample level other than 2 is not supported!")
817  do c=1, diag_cs%num_diag_coords
818  ! Level/layer h-points in diagnostic coordinate
819  axes => diag_cs%remap_axesTL(c)
820  call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesTL(c)%dsamp(dl)%mask3d, dl,g%isc, g%jsc, &
821  g%HId2%isc, g%HId2%iec, g%HId2%jsc, g%HId2%jec, g%HId2%isd, g%HId2%ied, g%HId2%jsd, g%HId2%jed)
822  diag_cs%dsamp(dl)%remap_axesTL(c)%mask3d => axes%mask3d !set non-downsampled mask
823  ! Level/layer u-points in diagnostic coordinate
824  axes => diag_cs%remap_axesCuL(c)
825  call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCuL(c)%dsamp(dl)%mask3d, dl,g%IscB,g%JscB, &
826  g%HId2%IscB,g%HId2%IecB,g%HId2%jsc, g%HId2%jec,g%HId2%IsdB,g%HId2%IedB,g%HId2%jsd, g%HId2%jed)
827  diag_cs%dsamp(dl)%remap_axesCul(c)%mask3d => axes%mask3d !set non-downsampled mask
828  ! Level/layer v-points in diagnostic coordinate
829  axes => diag_cs%remap_axesCvL(c)
830  call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCvL(c)%dsamp(dl)%mask3d, dl,g%isc ,g%JscB, &
831  g%HId2%isc ,g%HId2%iec, g%HId2%JscB,g%HId2%JecB,g%HId2%isd ,g%HId2%ied, g%HId2%JsdB,g%HId2%JedB)
832  diag_cs%dsamp(dl)%remap_axesCvL(c)%mask3d => axes%mask3d !set non-downsampled mask
833  ! Level/layer q-points in diagnostic coordinate
834  axes => diag_cs%remap_axesBL(c)
835  call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesBL(c)%dsamp(dl)%mask3d, dl,g%IscB,g%JscB, &
836  g%HId2%IscB,g%HId2%IecB,g%HId2%JscB,g%HId2%JecB,g%HId2%IsdB,g%HId2%IedB,g%HId2%JsdB,g%HId2%JedB)
837  diag_cs%dsamp(dl)%remap_axesBL(c)%mask3d => axes%mask3d !set non-downsampled mask
838  ! Interface h-points in diagnostic coordinate (w-point)
839  axes => diag_cs%remap_axesTi(c)
840  call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesTi(c)%dsamp(dl)%mask3d, dl,g%isc, g%jsc, &
841  g%HId2%isc, g%HId2%iec, g%HId2%jsc, g%HId2%jec, g%HId2%isd, g%HId2%ied, g%HId2%jsd, g%HId2%jed)
842  diag_cs%dsamp(dl)%remap_axesTi(c)%mask3d => axes%mask3d !set non-downsampled mask
843  ! Interface u-points in diagnostic coordinate
844  axes => diag_cs%remap_axesCui(c)
845  call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCui(c)%dsamp(dl)%mask3d, dl,g%IscB,g%JscB, &
846  g%HId2%IscB,g%HId2%IecB,g%HId2%jsc, g%HId2%jec,g%HId2%IsdB,g%HId2%IedB,g%HId2%jsd, g%HId2%jed)
847  diag_cs%dsamp(dl)%remap_axesCui(c)%mask3d => axes%mask3d !set non-downsampled mask
848  ! Interface v-points in diagnostic coordinate
849  axes => diag_cs%remap_axesCvi(c)
850  call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCvi(c)%dsamp(dl)%mask3d, dl,g%isc ,g%JscB, &
851  g%HId2%isc ,g%HId2%iec, g%HId2%JscB,g%HId2%JecB,g%HId2%isd ,g%HId2%ied, g%HId2%JsdB,g%HId2%JedB)
852  diag_cs%dsamp(dl)%remap_axesCvi(c)%mask3d => axes%mask3d !set non-downsampled mask
853  ! Interface q-points in diagnostic coordinate
854  axes => diag_cs%remap_axesBi(c)
855  call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesBi(c)%dsamp(dl)%mask3d, dl,g%IscB,g%JscB, &
856  g%HId2%IscB,g%HId2%IecB,g%HId2%JscB,g%HId2%JecB,g%HId2%IsdB,g%HId2%IedB,g%HId2%JsdB,g%HId2%JedB)
857  diag_cs%dsamp(dl)%remap_axesBi(c)%mask3d => axes%mask3d !set non-downsampled mask
858  enddo
859  enddo
860 end subroutine set_masks_for_axes_dsamp
861 
862 !> Attaches the id of cell areas to axes groups for use with cell_measures
863 subroutine diag_register_area_ids(diag_cs, id_area_t, id_area_q)
864  type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
865  integer, optional, intent(in) :: id_area_t !< Diag_mediator id for area of h-cells
866  integer, optional, intent(in) :: id_area_q !< Diag_mediator id for area of q-cells
867  ! Local variables
868  integer :: fms_id, i
869  if (present(id_area_t)) then
870  fms_id = diag_cs%diags(id_area_t)%fms_diag_id
871  diag_cs%axesT1%id_area = fms_id
872  diag_cs%axesTi%id_area = fms_id
873  diag_cs%axesTL%id_area = fms_id
874  do i=1, diag_cs%num_diag_coords
875  diag_cs%remap_axesTL(i)%id_area = fms_id
876  diag_cs%remap_axesTi(i)%id_area = fms_id
877  enddo
878  endif
879  if (present(id_area_q)) then
880  fms_id = diag_cs%diags(id_area_q)%fms_diag_id
881  diag_cs%axesB1%id_area = fms_id
882  diag_cs%axesBi%id_area = fms_id
883  diag_cs%axesBL%id_area = fms_id
884  do i=1, diag_cs%num_diag_coords
885  diag_cs%remap_axesBL(i)%id_area = fms_id
886  diag_cs%remap_axesBi(i)%id_area = fms_id
887  enddo
888  endif
889 end subroutine diag_register_area_ids
890 
891 !> Sets a handle inside diagnostics mediator to associate 3d cell measures
892 subroutine register_cell_measure(G, diag, Time)
893  type(ocean_grid_type), intent(in) :: g !< Ocean grid structure
894  type(diag_ctrl), target, intent(inout) :: diag !< Regulates diagnostic output
895  type(time_type), intent(in) :: time !< Model time
896  ! Local variables
897  integer :: id
898  id = register_diag_field('ocean_model', 'volcello', diag%axesTL, &
899  time, 'Ocean grid-cell volume', 'm3', &
900  standard_name='ocean_volume', v_extensive=.true., &
901  x_cell_method='sum', y_cell_method='sum')
902  call diag_associate_volume_cell_measure(diag, id)
903 
904 end subroutine register_cell_measure
905 
906 !> Attaches the id of cell volumes to axes groups for use with cell_measures
907 subroutine diag_associate_volume_cell_measure(diag_cs, id_h_volume)
908  type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
909  integer, intent(in) :: id_h_volume !< Diag_manager id for volume of h-cells
910  ! Local variables
911  type(diag_type), pointer :: tmp => null()
912 
913  if (id_h_volume<=0) return ! Do nothing
914  diag_cs%volume_cell_measure_dm_id = id_h_volume ! Record for diag_get_volume_cell_measure_dm_id()
915 
916  ! Set the cell measure for this axes group to the FMS id in this coordinate system
917  diag_cs%diags(id_h_volume)%axes%id_volume = diag_cs%diags(id_h_volume)%fms_diag_id
918 
919  tmp => diag_cs%diags(id_h_volume)%next ! First item in the list, if any
920  do while (associated(tmp))
921  ! Set the cell measure for this axes group to the FMS id in this coordinate system
922  tmp%axes%id_volume = tmp%fms_diag_id
923  tmp => tmp%next ! Move to next axes group for this field
924  enddo
925 
926 end subroutine diag_associate_volume_cell_measure
927 
928 !> Returns diag_manager id for cell measure of h-cells
929 integer function diag_get_volume_cell_measure_dm_id(diag_cs)
930  type(diag_ctrl), intent(in) :: diag_cs !< Diagnostics control structure
931 
932  diag_get_volume_cell_measure_dm_id = diag_cs%volume_cell_measure_dm_id
933 
934 end function diag_get_volume_cell_measure_dm_id
935 
936 !> Defines a group of "axes" from list of handles
937 subroutine define_axes_group(diag_cs, handles, axes, nz, vertical_coordinate_number, &
938  x_cell_method, y_cell_method, v_cell_method, &
939  is_h_point, is_q_point, is_u_point, is_v_point, &
940  is_layer, is_interface, &
941  is_native, needs_remapping, needs_interpolating, &
942  xyave_axes)
943  type(diag_ctrl), target, intent(in) :: diag_cs !< Diagnostics control structure
944  integer, dimension(:), intent(in) :: handles !< A list of 1D axis handles
945  type(axes_grp), intent(out) :: axes !< The group of 1D axes
946  integer, optional, intent(in) :: nz !< Number of layers in this diagnostic grid
947  integer, optional, intent(in) :: vertical_coordinate_number !< Index number for vertical coordinate
948  character(len=*), optional, intent(in) :: x_cell_method !< A x-direction cell method used to construct the
949  !! "cell_methods" attribute in CF convention
950  character(len=*), optional, intent(in) :: y_cell_method !< A y-direction cell method used to construct the
951  !! "cell_methods" attribute in CF convention
952  character(len=*), optional, intent(in) :: v_cell_method !< A vertical direction cell method used to construct
953  !! the "cell_methods" attribute in CF convention
954  logical, optional, intent(in) :: is_h_point !< If true, indicates this axes group for h-point
955  !! located fields
956  logical, optional, intent(in) :: is_q_point !< If true, indicates this axes group for q-point
957  !! located fields
958  logical, optional, intent(in) :: is_u_point !< If true, indicates this axes group for
959  !! u-point located fields
960  logical, optional, intent(in) :: is_v_point !< If true, indicates this axes group for
961  !! v-point located fields
962  logical, optional, intent(in) :: is_layer !< If true, indicates that this axes group is
963  !! for a layer vertically-located field.
964  logical, optional, intent(in) :: is_interface !< If true, indicates that this axes group
965  !! is for an interface vertically-located field.
966  logical, optional, intent(in) :: is_native !< If true, indicates that this axes group is
967  !! for a native model grid. False for any other grid.
968  logical, optional, intent(in) :: needs_remapping !< If true, indicates that this axes group is
969  !! for a intensive layer-located field that must
970  !! be remapped to these axes. Used for rank>2.
971  logical, optional, intent(in) :: needs_interpolating !< If true, indicates that this axes group
972  !! is for a sampled interface-located field that must
973  !! be interpolated to these axes. Used for rank>2.
974  type(axes_grp), optional, target :: xyave_axes !< The corresponding axes group for horizontally
975  !! area-average diagnostics
976  ! Local variables
977  integer :: n
978 
979  n = size(handles)
980  if (n<1 .or. n>3) call mom_error(fatal, "define_axes_group: wrong size for list of handles!")
981  allocate( axes%handles(n) )
982  axes%id = i2s(handles, n) ! Identifying string
983  axes%rank = n
984  axes%handles(:) = handles(:)
985  axes%diag_cs => diag_cs ! A [circular] link back to the diag_cs structure
986  if (present(x_cell_method)) then
987  if (axes%rank<2) call mom_error(fatal, 'define_axes_group: ' // &
988  'Can not set x_cell_method for rank<2.')
989  axes%x_cell_method = trim(x_cell_method)
990  else
991  axes%x_cell_method = ''
992  endif
993  if (present(y_cell_method)) then
994  if (axes%rank<2) call mom_error(fatal, 'define_axes_group: ' // &
995  'Can not set y_cell_method for rank<2.')
996  axes%y_cell_method = trim(y_cell_method)
997  else
998  axes%y_cell_method = ''
999  endif
1000  if (present(v_cell_method)) then
1001  if (axes%rank/=1 .and. axes%rank/=3) call mom_error(fatal, 'define_axes_group: ' // &
1002  'Can not set v_cell_method for rank<>1 or 3.')
1003  axes%v_cell_method = trim(v_cell_method)
1004  else
1005  axes%v_cell_method = ''
1006  endif
1007  if (present(nz)) axes%nz = nz
1008  if (present(vertical_coordinate_number)) axes%vertical_coordinate_number = vertical_coordinate_number
1009  if (present(is_h_point)) axes%is_h_point = is_h_point
1010  if (present(is_q_point)) axes%is_q_point = is_q_point
1011  if (present(is_u_point)) axes%is_u_point = is_u_point
1012  if (present(is_v_point)) axes%is_v_point = is_v_point
1013  if (present(is_layer)) axes%is_layer = is_layer
1014  if (present(is_interface)) axes%is_interface = is_interface
1015  if (present(is_native)) axes%is_native = is_native
1016  if (present(needs_remapping)) axes%needs_remapping = needs_remapping
1017  if (present(needs_interpolating)) axes%needs_interpolating = needs_interpolating
1018  if (present(xyave_axes)) axes%xyave_axes => xyave_axes
1019 
1020  ! Setup masks for this axes group
1021  axes%mask2d => null()
1022  if (axes%rank==2) then
1023  if (axes%is_h_point) axes%mask2d => diag_cs%mask2dT
1024  if (axes%is_u_point) axes%mask2d => diag_cs%mask2dCu
1025  if (axes%is_v_point) axes%mask2d => diag_cs%mask2dCv
1026  if (axes%is_q_point) axes%mask2d => diag_cs%mask2dBu
1027  endif
1028  ! A static 3d mask for non-native coordinates can only be setup when a grid is available
1029  axes%mask3d => null()
1030  if (axes%rank==3 .and. axes%is_native) then
1031  ! Native variables can/should use the native masks copied into diag_cs
1032  if (axes%is_layer) then
1033  if (axes%is_h_point) axes%mask3d => diag_cs%mask3dTL
1034  if (axes%is_u_point) axes%mask3d => diag_cs%mask3dCuL
1035  if (axes%is_v_point) axes%mask3d => diag_cs%mask3dCvL
1036  if (axes%is_q_point) axes%mask3d => diag_cs%mask3dBL
1037  elseif (axes%is_interface) then
1038  if (axes%is_h_point) axes%mask3d => diag_cs%mask3dTi
1039  if (axes%is_u_point) axes%mask3d => diag_cs%mask3dCui
1040  if (axes%is_v_point) axes%mask3d => diag_cs%mask3dCvi
1041  if (axes%is_q_point) axes%mask3d => diag_cs%mask3dBi
1042  endif
1043  endif
1044 
1045 end subroutine define_axes_group
1046 
1047 !> Defines a group of downsampled "axes" from list of handles
1048 subroutine define_axes_group_dsamp(diag_cs, handles, axes, dl, nz, vertical_coordinate_number, &
1049  x_cell_method, y_cell_method, v_cell_method, &
1050  is_h_point, is_q_point, is_u_point, is_v_point, &
1051  is_layer, is_interface, &
1052  is_native, needs_remapping, needs_interpolating, &
1053  xyave_axes)
1054  type(diag_ctrl), target, intent(in) :: diag_cs !< Diagnostics control structure
1055  integer, dimension(:), intent(in) :: handles !< A list of 1D axis handles
1056  type(axes_grp), intent(out) :: axes !< The group of 1D axes
1057  integer, intent(in) :: dl !< Downsample level
1058  integer, optional, intent(in) :: nz !< Number of layers in this diagnostic grid
1059  integer, optional, intent(in) :: vertical_coordinate_number !< Index number for vertical coordinate
1060  character(len=*), optional, intent(in) :: x_cell_method !< A x-direction cell method used to construct the
1061  !! "cell_methods" attribute in CF convention
1062  character(len=*), optional, intent(in) :: y_cell_method !< A y-direction cell method used to construct the
1063  !! "cell_methods" attribute in CF convention
1064  character(len=*), optional, intent(in) :: v_cell_method !< A vertical direction cell method used to construct
1065  !! the "cell_methods" attribute in CF convention
1066  logical, optional, intent(in) :: is_h_point !< If true, indicates this axes group for h-point
1067  !! located fields
1068  logical, optional, intent(in) :: is_q_point !< If true, indicates this axes group for q-point
1069  !! located fields
1070  logical, optional, intent(in) :: is_u_point !< If true, indicates this axes group for
1071  !! u-point located fields
1072  logical, optional, intent(in) :: is_v_point !< If true, indicates this axes group for
1073  !! v-point located fields
1074  logical, optional, intent(in) :: is_layer !< If true, indicates that this axes group is
1075  !! for a layer vertically-located field.
1076  logical, optional, intent(in) :: is_interface !< If true, indicates that this axes group
1077  !! is for an interface vertically-located field.
1078  logical, optional, intent(in) :: is_native !< If true, indicates that this axes group is
1079  !! for a native model grid. False for any other grid.
1080  logical, optional, intent(in) :: needs_remapping !< If true, indicates that this axes group is
1081  !! for a intensive layer-located field that must
1082  !! be remapped to these axes. Used for rank>2.
1083  logical, optional, intent(in) :: needs_interpolating !< If true, indicates that this axes group
1084  !! is for a sampled interface-located field that must
1085  !! be interpolated to these axes. Used for rank>2.
1086  type(axes_grp), optional, target :: xyave_axes !< The corresponding axes group for horizontally
1087  !! area-average diagnostics
1088  ! Local variables
1089  integer :: n
1090 
1091  n = size(handles)
1092  if (n<1 .or. n>3) call mom_error(fatal, "define_axes_group: wrong size for list of handles!")
1093  allocate( axes%handles(n) )
1094  axes%id = i2s(handles, n) ! Identifying string
1095  axes%rank = n
1096  axes%handles(:) = handles(:)
1097  axes%diag_cs => diag_cs ! A [circular] link back to the diag_cs structure
1098  if (present(x_cell_method)) then
1099  if (axes%rank<2) call mom_error(fatal, 'define_axes_group: ' // &
1100  'Can not set x_cell_method for rank<2.')
1101  axes%x_cell_method = trim(x_cell_method)
1102  else
1103  axes%x_cell_method = ''
1104  endif
1105  if (present(y_cell_method)) then
1106  if (axes%rank<2) call mom_error(fatal, 'define_axes_group: ' // &
1107  'Can not set y_cell_method for rank<2.')
1108  axes%y_cell_method = trim(y_cell_method)
1109  else
1110  axes%y_cell_method = ''
1111  endif
1112  if (present(v_cell_method)) then
1113  if (axes%rank/=1 .and. axes%rank/=3) call mom_error(fatal, 'define_axes_group: ' // &
1114  'Can not set v_cell_method for rank<>1 or 3.')
1115  axes%v_cell_method = trim(v_cell_method)
1116  else
1117  axes%v_cell_method = ''
1118  endif
1119  axes%downsample_level = dl
1120  if (present(nz)) axes%nz = nz
1121  if (present(vertical_coordinate_number)) axes%vertical_coordinate_number = vertical_coordinate_number
1122  if (present(is_h_point)) axes%is_h_point = is_h_point
1123  if (present(is_q_point)) axes%is_q_point = is_q_point
1124  if (present(is_u_point)) axes%is_u_point = is_u_point
1125  if (present(is_v_point)) axes%is_v_point = is_v_point
1126  if (present(is_layer)) axes%is_layer = is_layer
1127  if (present(is_interface)) axes%is_interface = is_interface
1128  if (present(is_native)) axes%is_native = is_native
1129  if (present(needs_remapping)) axes%needs_remapping = needs_remapping
1130  if (present(needs_interpolating)) axes%needs_interpolating = needs_interpolating
1131  if (present(xyave_axes)) axes%xyave_axes => xyave_axes
1132 
1133  ! Setup masks for this axes group
1134 
1135  axes%mask2d => null()
1136  if (axes%rank==2) then
1137  if (axes%is_h_point) axes%mask2d => diag_cs%mask2dT
1138  if (axes%is_u_point) axes%mask2d => diag_cs%mask2dCu
1139  if (axes%is_v_point) axes%mask2d => diag_cs%mask2dCv
1140  if (axes%is_q_point) axes%mask2d => diag_cs%mask2dBu
1141  endif
1142  ! A static 3d mask for non-native coordinates can only be setup when a grid is available
1143  axes%mask3d => null()
1144  if (axes%rank==3 .and. axes%is_native) then
1145  ! Native variables can/should use the native masks copied into diag_cs
1146  if (axes%is_layer) then
1147  if (axes%is_h_point) axes%mask3d => diag_cs%mask3dTL
1148  if (axes%is_u_point) axes%mask3d => diag_cs%mask3dCuL
1149  if (axes%is_v_point) axes%mask3d => diag_cs%mask3dCvL
1150  if (axes%is_q_point) axes%mask3d => diag_cs%mask3dBL
1151  elseif (axes%is_interface) then
1152  if (axes%is_h_point) axes%mask3d => diag_cs%mask3dTi
1153  if (axes%is_u_point) axes%mask3d => diag_cs%mask3dCui
1154  if (axes%is_v_point) axes%mask3d => diag_cs%mask3dCvi
1155  if (axes%is_q_point) axes%mask3d => diag_cs%mask3dBi
1156  endif
1157  endif
1158 
1159  axes%dsamp(dl)%mask2d => null()
1160  if (axes%rank==2) then
1161  if (axes%is_h_point) axes%dsamp(dl)%mask2d => diag_cs%dsamp(dl)%mask2dT
1162  if (axes%is_u_point) axes%dsamp(dl)%mask2d => diag_cs%dsamp(dl)%mask2dCu
1163  if (axes%is_v_point) axes%dsamp(dl)%mask2d => diag_cs%dsamp(dl)%mask2dCv
1164  if (axes%is_q_point) axes%dsamp(dl)%mask2d => diag_cs%dsamp(dl)%mask2dBu
1165  endif
1166  ! A static 3d mask for non-native coordinates can only be setup when a grid is available
1167  axes%dsamp(dl)%mask3d => null()
1168  if (axes%rank==3 .and. axes%is_native) then
1169  ! Native variables can/should use the native masks copied into diag_cs
1170  if (axes%is_layer) then
1171  if (axes%is_h_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dTL
1172  if (axes%is_u_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dCuL
1173  if (axes%is_v_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dCvL
1174  if (axes%is_q_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dBL
1175  elseif (axes%is_interface) then
1176  if (axes%is_h_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dTi
1177  if (axes%is_u_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dCui
1178  if (axes%is_v_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dCvi
1179  if (axes%is_q_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dBi
1180  endif
1181  endif
1182 
1183 end subroutine define_axes_group_dsamp
1184 
1185 !> Set up the array extents for doing diagnostics
1186 subroutine set_diag_mediator_grid(G, diag_cs)
1187  type(ocean_grid_type), intent(inout) :: g !< The ocean's grid structure
1188  type(diag_ctrl), intent(inout) :: diag_cs !< Structure used to regulate diagnostic output
1189 
1190  diag_cs%is = g%isc - (g%isd-1) ; diag_cs%ie = g%iec - (g%isd-1)
1191  diag_cs%js = g%jsc - (g%jsd-1) ; diag_cs%je = g%jec - (g%jsd-1)
1192  diag_cs%isd = g%isd ; diag_cs%ied = g%ied
1193  diag_cs%jsd = g%jsd ; diag_cs%jed = g%jed
1194 
1195 end subroutine set_diag_mediator_grid
1196 
1197 !> Make a real scalar diagnostic available for averaging or output
1198 subroutine post_data_0d(diag_field_id, field, diag_cs, is_static)
1199  integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
1200  !! previous call to register_diag_field.
1201  real, intent(in) :: field !< real value being offered for output or averaging
1202  type(diag_ctrl), target, intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1203  logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1204 
1205  ! Local variables
1206  logical :: used, is_stat
1207  type(diag_type), pointer :: diag => null()
1208 
1209  if (id_clock_diag_mediator>0) call cpu_clock_begin(id_clock_diag_mediator)
1210  is_stat = .false. ; if (present(is_static)) is_stat = is_static
1211 
1212  ! Iterate over list of diag 'variants', e.g. CMOR aliases, call send_data
1213  ! for each one.
1214  call assert(diag_field_id < diag_cs%next_free_diag_id, &
1215  'post_data_0d: Unregistered diagnostic id')
1216  diag => diag_cs%diags(diag_field_id)
1217  do while (associated(diag))
1218  if (diag_cs%diag_as_chksum) then
1219  call chksum0(field, diag%debug_str, logunit=diag_cs%chksum_iounit)
1220  else if (is_stat) then
1221  used = send_data(diag%fms_diag_id, field)
1222  elseif (diag_cs%ave_enabled) then
1223  used = send_data(diag%fms_diag_id, field, diag_cs%time_end)
1224  endif
1225  diag => diag%next
1226  enddo
1227 
1228  if (id_clock_diag_mediator>0) call cpu_clock_end(id_clock_diag_mediator)
1229 end subroutine post_data_0d
1230 
1231 !> Make a real 1-d array diagnostic available for averaging or output
1232 subroutine post_data_1d_k(diag_field_id, field, diag_cs, is_static)
1233  integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
1234  !! previous call to register_diag_field.
1235  real, target, intent(in) :: field(:) !< 1-d array being offered for output or averaging
1236  type(diag_ctrl), target, intent(in) :: diag_cs !< Structure used to regulate diagnostic output
1237  logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1238 
1239  ! Local variables
1240  logical :: used ! The return value of send_data is not used for anything.
1241  real, dimension(:), pointer :: locfield => null()
1242  logical :: is_stat
1243  integer :: k, ks, ke
1244  type(diag_type), pointer :: diag => null()
1245 
1246  if (id_clock_diag_mediator>0) call cpu_clock_begin(id_clock_diag_mediator)
1247  is_stat = .false. ; if (present(is_static)) is_stat = is_static
1248 
1249  ! Iterate over list of diag 'variants', e.g. CMOR aliases.
1250  call assert(diag_field_id < diag_cs%next_free_diag_id, &
1251  'post_data_1d_k: Unregistered diagnostic id')
1252  diag => diag_cs%diags(diag_field_id)
1253  do while (associated(diag))
1254 
1255  if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) then
1256  ks = lbound(field,1) ; ke = ubound(field,1)
1257  allocate( locfield( ks:ke ) )
1258 
1259  do k=ks,ke
1260  if (field(k) == diag_cs%missing_value) then
1261  locfield(k) = diag_cs%missing_value
1262  else
1263  locfield(k) = field(k) * diag%conversion_factor
1264  endif
1265  enddo
1266  else
1267  locfield => field
1268  endif
1269 
1270  if (diag_cs%diag_as_chksum) then
1271  call zchksum(locfield, diag%debug_str, logunit=diag_cs%chksum_iounit)
1272  else if (is_stat) then
1273  used = send_data(diag%fms_diag_id, locfield)
1274  elseif (diag_cs%ave_enabled) then
1275  used = send_data(diag%fms_diag_id, locfield, diag_cs%time_end, weight=diag_cs%time_int)
1276  endif
1277  if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) deallocate( locfield )
1278 
1279  diag => diag%next
1280  enddo
1281 
1282  if (id_clock_diag_mediator>0) call cpu_clock_end(id_clock_diag_mediator)
1283 end subroutine post_data_1d_k
1284 
1285 !> Make a real 2-d array diagnostic available for averaging or output
1286 subroutine post_data_2d(diag_field_id, field, diag_cs, is_static, mask)
1287  integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
1288  !! previous call to register_diag_field.
1289  real, intent(in) :: field(:,:) !< 2-d array being offered for output or averaging
1290  type(diag_ctrl), target, intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1291  logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1292  real, optional, intent(in) :: mask(:,:) !< If present, use this real array as the data mask.
1293 
1294  ! Local variables
1295  type(diag_type), pointer :: diag => null()
1296 
1297  if (id_clock_diag_mediator>0) call cpu_clock_begin(id_clock_diag_mediator)
1298 
1299  ! Iterate over list of diag 'variants' (e.g. CMOR aliases) and post each.
1300  call assert(diag_field_id < diag_cs%next_free_diag_id, &
1301  'post_data_2d: Unregistered diagnostic id')
1302  diag => diag_cs%diags(diag_field_id)
1303  do while (associated(diag))
1304  call post_data_2d_low(diag, field, diag_cs, is_static, mask)
1305  diag => diag%next
1306  enddo
1307 
1308  if (id_clock_diag_mediator>0) call cpu_clock_end(id_clock_diag_mediator)
1309 end subroutine post_data_2d
1310 
1311 !> Make a real 2-d array diagnostic available for averaging or output
1312 !! using a diag_type instead of an integer id.
1313 subroutine post_data_2d_low(diag, field, diag_cs, is_static, mask)
1314  type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
1315  real, target, intent(in) :: field(:,:) !< 2-d array being offered for output or averaging
1316  type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1317  logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1318  real, optional,target, intent(in) :: mask(:,:) !< If present, use this real array as the data mask.
1319 
1320  ! Local variables
1321  real, dimension(:,:), pointer :: locfield
1322  real, dimension(:,:), pointer :: locmask
1323  character(len=300) :: mesg
1324  logical :: used, is_stat
1325  integer :: cszi, cszj, dszi, dszj
1326  integer :: isv, iev, jsv, jev, i, j, chksum, isv_o,jsv_o
1327  real, dimension(:,:), allocatable, target :: locfield_dsamp
1328  real, dimension(:,:), allocatable, target :: locmask_dsamp
1329  integer :: dl
1330 
1331  locfield => null()
1332  locmask => null()
1333  is_stat = .false. ; if (present(is_static)) is_stat = is_static
1334 
1335  ! Determine the propery array indices, noting that because of the (:,:)
1336  ! declaration of field, symmetric arrays are using a SW-grid indexing,
1337  ! but non-symmetric arrays are using a NE-grid indexing. Send_data
1338  ! actually only uses the difference between ie and is to determine
1339  ! the output data size and assumes that halos are symmetric.
1340  isv = diag_cs%is ; iev = diag_cs%ie ; jsv = diag_cs%js ; jev = diag_cs%je
1341 
1342  cszi = diag_cs%ie-diag_cs%is +1 ; dszi = diag_cs%ied-diag_cs%isd +1
1343  cszj = diag_cs%je-diag_cs%js +1 ; dszj = diag_cs%jed-diag_cs%jsd +1
1344  if ( size(field,1) == dszi ) then
1345  isv = diag_cs%is ; iev = diag_cs%ie ! Data domain
1346  elseif ( size(field,1) == dszi + 1 ) then
1347  isv = diag_cs%is ; iev = diag_cs%ie+1 ! Symmetric data domain
1348  elseif ( size(field,1) == cszi) then
1349  isv = 1 ; iev = cszi ! Computational domain
1350  elseif ( size(field,1) == cszi + 1 ) then
1351  isv = 1 ; iev = cszi+1 ! Symmetric computational domain
1352  else
1353  write (mesg,*) " peculiar size ",size(field,1)," in i-direction\n"//&
1354  "does not match one of ", cszi, cszi+1, dszi, dszi+1
1355  call mom_error(fatal,"post_data_2d_low: "//trim(diag%debug_str)//trim(mesg))
1356  endif
1357 
1358  if ( size(field,2) == dszj ) then
1359  jsv = diag_cs%js ; jev = diag_cs%je ! Data domain
1360  elseif ( size(field,2) == dszj + 1 ) then
1361  jsv = diag_cs%js ; jev = diag_cs%je+1 ! Symmetric data domain
1362  elseif ( size(field,2) == cszj ) then
1363  jsv = 1 ; jev = cszj ! Computational domain
1364  elseif ( size(field,2) == cszj+1 ) then
1365  jsv = 1 ; jev = cszj+1 ! Symmetric computational domain
1366  else
1367  write (mesg,*) " peculiar size ",size(field,2)," in j-direction\n"//&
1368  "does not match one of ", cszj, cszj+1, dszj, dszj+1
1369  call mom_error(fatal,"post_data_2d_low: "//trim(diag%debug_str)//trim(mesg))
1370  endif
1371 
1372  if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) then
1373  allocate( locfield( lbound(field,1):ubound(field,1), lbound(field,2):ubound(field,2) ) )
1374  do j=jsv,jev ; do i=isv,iev
1375  if (field(i,j) == diag_cs%missing_value) then
1376  locfield(i,j) = diag_cs%missing_value
1377  else
1378  locfield(i,j) = field(i,j) * diag%conversion_factor
1379  endif
1380  enddo ; enddo
1381  locfield(isv:iev,jsv:jev) = field(isv:iev,jsv:jev) * diag%conversion_factor
1382  else
1383  locfield => field
1384  endif
1385 
1386  if (present(mask)) then
1387  locmask => mask
1388  elseif(.NOT. is_stat) then
1389  if(associated(diag%axes%mask2d)) locmask => diag%axes%mask2d
1390  endif
1391 
1392  dl=1
1393  if(.NOT. is_stat) dl = diag%axes%downsample_level !static field downsample i not supported yet
1394  !Downsample the diag field and mask (if present)
1395  if (dl > 1) then
1396  isv_o=isv ; jsv_o=jsv
1397  call downsample_diag_field(locfield, locfield_dsamp, dl, diag_cs, diag,isv,iev,jsv,jev, mask)
1398  if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) deallocate( locfield )
1399  locfield => locfield_dsamp
1400  if (present(mask)) then
1401  call downsample_field_2d(locmask, locmask_dsamp, dl, msk, locmask, diag_cs,diag,isv_o,jsv_o,isv,iev,jsv,jev)
1402  locmask => locmask_dsamp
1403  elseif(associated(diag%axes%dsamp(dl)%mask2d)) then
1404  locmask => diag%axes%dsamp(dl)%mask2d
1405  endif
1406  endif
1407 
1408  if (diag_cs%diag_as_chksum) then
1409  if (diag%axes%is_h_point) then
1410  call hchksum(locfield, diag%debug_str, diag_cs%G%HI, &
1411  logunit=diag_cs%chksum_iounit)
1412  else if (diag%axes%is_u_point) then
1413  call uchksum(locfield, diag%debug_str, diag_cs%G%HI, &
1414  logunit=diag_cs%chksum_iounit)
1415  else if (diag%axes%is_v_point) then
1416  call vchksum(locfield, diag%debug_str, diag_cs%G%HI, &
1417  logunit=diag_cs%chksum_iounit)
1418  else if (diag%axes%is_q_point) then
1419  call bchksum(locfield, diag%debug_str, diag_cs%G%HI, &
1420  logunit=diag_cs%chksum_iounit)
1421  else
1422  call mom_error(fatal, "post_data_2d_low: unknown axis type.")
1423  endif
1424  else
1425  if (is_stat) then
1426  if (present(mask)) then
1427  call assert(size(locfield) == size(locmask), &
1428  'post_data_2d_low is_stat: mask size mismatch: '//diag%debug_str)
1429  used = send_data(diag%fms_diag_id, locfield, &
1430  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, rmask=locmask)
1431  !elseif (associated(diag%axes%mask2d)) then
1432  ! used = send_data(diag%fms_diag_id, locfield, &
1433  ! is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, rmask=diag%axes%mask2d)
1434  else
1435  used = send_data(diag%fms_diag_id, locfield, &
1436  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev)
1437  endif
1438  elseif (diag_cs%ave_enabled) then
1439  if (associated(locmask)) then
1440  call assert(size(locfield) == size(locmask), &
1441  'post_data_2d_low: mask size mismatch: '//diag%debug_str)
1442  used = send_data(diag%fms_diag_id, locfield, diag_cs%time_end, &
1443  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, &
1444  weight=diag_cs%time_int, rmask=locmask)
1445  else
1446  used = send_data(diag%fms_diag_id, locfield, diag_cs%time_end, &
1447  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, &
1448  weight=diag_cs%time_int)
1449  endif
1450  endif
1451  endif
1452  if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.) .and. dl<2) &
1453  deallocate( locfield )
1454 end subroutine post_data_2d_low
1455 
1456 !> Make a real 3-d array diagnostic available for averaging or output.
1457 subroutine post_data_3d(diag_field_id, field, diag_cs, is_static, mask, alt_h)
1459  integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
1460  !! previous call to register_diag_field.
1461  real, intent(in) :: field(:,:,:) !< 3-d array being offered for output or averaging
1462  type(diag_ctrl), target, intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1463  logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1464  real, optional, intent(in) :: mask(:,:,:) !< If present, use this real array as the data mask.
1465  real, dimension(:,:,:), &
1466  target, optional, intent(in) :: alt_h !< An alternate thickness to use for vertically
1467  !! remapping this diagnostic [H ~> m or kg m-2].
1468 
1469  ! Local variables
1470  type(diag_type), pointer :: diag => null()
1471  integer :: nz, i, j, k
1472  real, dimension(:,:,:), allocatable :: remapped_field
1473  logical :: staggered_in_x, staggered_in_y
1474  real, dimension(:,:,:), pointer :: h_diag => null()
1475 
1476  if (present(alt_h)) then
1477  h_diag => alt_h
1478  else
1479  h_diag => diag_cs%h
1480  endif
1481 
1482  if (id_clock_diag_mediator>0) call cpu_clock_begin(id_clock_diag_mediator)
1483 
1484  ! Iterate over list of diag 'variants', e.g. CMOR aliases, different vertical
1485  ! grids, and post each.
1486  call assert(diag_field_id < diag_cs%next_free_diag_id, &
1487  'post_data_3d: Unregistered diagnostic id')
1488  diag => diag_cs%diags(diag_field_id)
1489  do while (associated(diag))
1490  call assert(associated(diag%axes), 'post_data_3d: axes is not associated')
1491 
1492  staggered_in_x = diag%axes%is_u_point .or. diag%axes%is_q_point
1493  staggered_in_y = diag%axes%is_v_point .or. diag%axes%is_q_point
1494 
1495  if (diag%v_extensive .and. .not.diag%axes%is_native) then
1496  ! The field is vertically integrated and needs to be re-gridded
1497  if (present(mask)) then
1498  call mom_error(fatal,"post_data_3d: no mask for regridded field.")
1499  endif
1500 
1501  if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
1502  allocate(remapped_field(size(field,1), size(field,2), diag%axes%nz))
1503  call vertically_reintegrate_diag_field( &
1504  diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), &
1505  diag_cs%G, h_diag, staggered_in_x, staggered_in_y, &
1506  diag%axes%mask3d, diag_cs%missing_value, field, remapped_field)
1507  if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
1508  if (associated(diag%axes%mask3d)) then
1509  ! Since 3d masks do not vary in the vertical, just use as much as is
1510  ! needed.
1511  call post_data_3d_low(diag, remapped_field, diag_cs, is_static, &
1512  mask=diag%axes%mask3d)
1513  else
1514  call post_data_3d_low(diag, remapped_field, diag_cs, is_static)
1515  endif
1516  if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
1517  deallocate(remapped_field)
1518  if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
1519  elseif (diag%axes%needs_remapping) then
1520  ! Remap this field to another vertical coordinate.
1521  if (present(mask)) then
1522  call mom_error(fatal,"post_data_3d: no mask for regridded field.")
1523  endif
1524 
1525  if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
1526  allocate(remapped_field(size(field,1), size(field,2), diag%axes%nz))
1527  call diag_remap_do_remap(diag_cs%diag_remap_cs( &
1528  diag%axes%vertical_coordinate_number), &
1529  diag_cs%G, diag_cs%GV, h_diag, staggered_in_x, staggered_in_y, &
1530  diag%axes%mask3d, diag_cs%missing_value, field, remapped_field)
1531  if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
1532  if (associated(diag%axes%mask3d)) then
1533  ! Since 3d masks do not vary in the vertical, just use as much as is
1534  ! needed.
1535  call post_data_3d_low(diag, remapped_field, diag_cs, is_static, &
1536  mask=diag%axes%mask3d)
1537  else
1538  call post_data_3d_low(diag, remapped_field, diag_cs, is_static)
1539  endif
1540  if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
1541  deallocate(remapped_field)
1542  if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
1543  elseif (diag%axes%needs_interpolating) then
1544  ! Interpolate this field to another vertical coordinate.
1545  if (present(mask)) then
1546  call mom_error(fatal,"post_data_3d: no mask for regridded field.")
1547  endif
1548 
1549  if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
1550  allocate(remapped_field(size(field,1), size(field,2), diag%axes%nz+1))
1551  call vertically_interpolate_diag_field(diag_cs%diag_remap_cs( &
1552  diag%axes%vertical_coordinate_number), &
1553  diag_cs%G, h_diag, staggered_in_x, staggered_in_y, &
1554  diag%axes%mask3d, diag_cs%missing_value, field, remapped_field)
1555  if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
1556  if (associated(diag%axes%mask3d)) then
1557  ! Since 3d masks do not vary in the vertical, just use as much as is
1558  ! needed.
1559  call post_data_3d_low(diag, remapped_field, diag_cs, is_static, &
1560  mask=diag%axes%mask3d)
1561  else
1562  call post_data_3d_low(diag, remapped_field, diag_cs, is_static)
1563  endif
1564  if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
1565  deallocate(remapped_field)
1566  if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
1567  else
1568  call post_data_3d_low(diag, field, diag_cs, is_static, mask)
1569  endif
1570  diag => diag%next
1571  enddo
1572  if (id_clock_diag_mediator>0) call cpu_clock_end(id_clock_diag_mediator)
1573 
1574 end subroutine post_data_3d
1575 
1576 !> Make a real 3-d array diagnostic available for averaging or output
1577 !! using a diag_type instead of an integer id.
1578 subroutine post_data_3d_low(diag, field, diag_cs, is_static, mask)
1579  type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
1580  real, target, intent(in) :: field(:,:,:) !< 3-d array being offered for output or averaging
1581  type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1582  logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1583  real, optional,target, intent(in) :: mask(:,:,:) !< If present, use this real array as the data mask.
1584 
1585  ! Local variables
1586  real, dimension(:,:,:), pointer :: locfield
1587  real, dimension(:,:,:), pointer :: locmask
1588  character(len=300) :: mesg
1589  logical :: used ! The return value of send_data is not used for anything.
1590  logical :: staggered_in_x, staggered_in_y
1591  logical :: is_stat
1592  integer :: cszi, cszj, dszi, dszj
1593  integer :: isv, iev, jsv, jev, ks, ke, i, j, k, isv_c, jsv_c, isv_o,jsv_o
1594  integer :: chksum
1595  real, dimension(:,:,:), allocatable, target :: locfield_dsamp
1596  real, dimension(:,:,:), allocatable, target :: locmask_dsamp
1597  integer :: dl
1598 
1599  locfield => null()
1600  locmask => null()
1601  is_stat = .false. ; if (present(is_static)) is_stat = is_static
1602 
1603  ! Determine the proper array indices, noting that because of the (:,:)
1604  ! declaration of field, symmetric arrays are using a SW-grid indexing,
1605  ! but non-symmetric arrays are using a NE-grid indexing. Send_data
1606  ! actually only uses the difference between ie and is to determine
1607  ! the output data size and assumes that halos are symmetric.
1608  isv = diag_cs%is ; iev = diag_cs%ie ; jsv = diag_cs%js ; jev = diag_cs%je
1609 
1610  cszi = (diag_cs%ie-diag_cs%is) +1 ; dszi = (diag_cs%ied-diag_cs%isd) +1
1611  cszj = (diag_cs%je-diag_cs%js) +1 ; dszj = (diag_cs%jed-diag_cs%jsd) +1
1612  if ( size(field,1) == dszi ) then
1613  isv = diag_cs%is ; iev = diag_cs%ie ! Data domain
1614  elseif ( size(field,1) == dszi + 1 ) then
1615  isv = diag_cs%is ; iev = diag_cs%ie+1 ! Symmetric data domain
1616  elseif ( size(field,1) == cszi) then
1617  isv = 1 ; iev = cszi ! Computational domain
1618  elseif ( size(field,1) == cszi + 1 ) then
1619  isv = 1 ; iev = cszi+1 ! Symmetric computational domain
1620  else
1621  write (mesg,*) " peculiar size ",size(field,1)," in i-direction\n"//&
1622  "does not match one of ", cszi, cszi+1, dszi, dszi+1
1623  call mom_error(fatal,"post_data_3d_low: "//trim(diag%debug_str)//trim(mesg))
1624  endif
1625 
1626  if ( size(field,2) == dszj ) then
1627  jsv = diag_cs%js ; jev = diag_cs%je ! Data domain
1628  elseif ( size(field,2) == dszj + 1 ) then
1629  jsv = diag_cs%js ; jev = diag_cs%je+1 ! Symmetric data domain
1630  elseif ( size(field,2) == cszj ) then
1631  jsv = 1 ; jev = cszj ! Computational domain
1632  elseif ( size(field,2) == cszj+1 ) then
1633  jsv = 1 ; jev = cszj+1 ! Symmetric computational domain
1634  else
1635  write (mesg,*) " peculiar size ",size(field,2)," in j-direction\n"//&
1636  "does not match one of ", cszj, cszj+1, dszj, dszj+1
1637  call mom_error(fatal,"post_data_3d_low: "//trim(diag%debug_str)//trim(mesg))
1638  endif
1639 
1640  ks = lbound(field,3) ; ke = ubound(field,3)
1641  if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) then
1642  allocate( locfield( lbound(field,1):ubound(field,1), lbound(field,2):ubound(field,2), ks:ke ) )
1643  ! locfield(:,:,:) = 0.0 ! Zeroing out this array would be a good idea, but it appears
1644  ! not to be necessary.
1645  isv_c = isv ; jsv_c = jsv
1646  if (diag%fms_xyave_diag_id>0) then
1647  staggered_in_x = diag%axes%is_u_point .or. diag%axes%is_q_point
1648  staggered_in_y = diag%axes%is_v_point .or. diag%axes%is_q_point
1649  ! When averaging a staggered field, edge points are always required.
1650  if (staggered_in_x) isv_c = iev - (diag_cs%ie - diag_cs%is) - 1
1651  if (staggered_in_y) jsv_c = jev - (diag_cs%je - diag_cs%js) - 1
1652  if (isv_c < lbound(locfield,1)) call mom_error(fatal, &
1653  "It is an error to average a staggered diagnostic field that does not "//&
1654  "have i-direction space to represent the symmetric computational domain.")
1655  if (jsv_c < lbound(locfield,2)) call mom_error(fatal, &
1656  "It is an error to average a staggered diagnostic field that does not "//&
1657  "have j-direction space to represent the symmetric computational domain.")
1658  endif
1659 
1660  do k=ks,ke ; do j=jsv,jev ; do i=isv,iev
1661  if (field(i,j,k) == diag_cs%missing_value) then
1662  locfield(i,j,k) = diag_cs%missing_value
1663  else
1664  locfield(i,j,k) = field(i,j,k) * diag%conversion_factor
1665  endif
1666  enddo ; enddo ; enddo
1667  else
1668  locfield => field
1669  endif
1670 
1671  if (present(mask)) then
1672  locmask => mask
1673  elseif(associated(diag%axes%mask3d)) then
1674  locmask => diag%axes%mask3d
1675  endif
1676 
1677  dl=1
1678  if(.NOT. is_stat) dl = diag%axes%downsample_level !static field downsample i not supported yet
1679  !Downsample the diag field and mask (if present)
1680  if (dl > 1) then
1681  isv_o=isv ; jsv_o=jsv
1682  call downsample_diag_field(locfield, locfield_dsamp, dl, diag_cs, diag,isv,iev,jsv,jev, mask)
1683  if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) deallocate( locfield )
1684  locfield => locfield_dsamp
1685  if (present(mask)) then
1686  call downsample_field_3d(locmask, locmask_dsamp, dl, msk, locmask, diag_cs,diag,isv_o,jsv_o,isv,iev,jsv,jev)
1687  locmask => locmask_dsamp
1688  elseif(associated(diag%axes%dsamp(dl)%mask3d)) then
1689  locmask => diag%axes%dsamp(dl)%mask3d
1690  endif
1691  endif
1692 
1693  if (diag%fms_diag_id>0) then
1694  if (diag_cs%diag_as_chksum) then
1695  if (diag%axes%is_h_point) then
1696  call hchksum(locfield, diag%debug_str, diag_cs%G%HI, &
1697  logunit=diag_cs%chksum_iounit)
1698  else if (diag%axes%is_u_point) then
1699  call uchksum(locfield, diag%debug_str, diag_cs%G%HI, &
1700  logunit=diag_cs%chksum_iounit)
1701  else if (diag%axes%is_v_point) then
1702  call vchksum(locfield, diag%debug_str, diag_cs%G%HI, &
1703  logunit=diag_cs%chksum_iounit)
1704  else if (diag%axes%is_q_point) then
1705  call bchksum(locfield, diag%debug_str, diag_cs%G%HI, &
1706  logunit=diag_cs%chksum_iounit)
1707  else
1708  call mom_error(fatal, "post_data_3d_low: unknown axis type.")
1709  endif
1710  else
1711  if (is_stat) then
1712  if (present(mask)) then
1713  call assert(size(locfield) == size(locmask), &
1714  'post_data_3d_low is_stat: mask size mismatch: '//diag%debug_str)
1715  used = send_data(diag%fms_diag_id, locfield, &
1716  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, rmask=locmask)
1717  !elseif (associated(diag%axes%mask2d)) then
1718  ! used = send_data(diag%fms_diag_id, locfield, &
1719  ! is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, rmask=diag%axes%mask2d)
1720  else
1721  used = send_data(diag%fms_diag_id, locfield, &
1722  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev)
1723  endif
1724  elseif (diag_cs%ave_enabled) then
1725  if (associated(locmask)) then
1726  call assert(size(locfield) == size(locmask), &
1727  'post_data_3d_low: mask size mismatch: '//diag%debug_str)
1728  used = send_data(diag%fms_diag_id, locfield, diag_cs%time_end, &
1729  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, &
1730  weight=diag_cs%time_int, rmask=locmask)
1731  else
1732  used = send_data(diag%fms_diag_id, locfield, diag_cs%time_end, &
1733  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, &
1734  weight=diag_cs%time_int)
1735  endif
1736  endif
1737  endif
1738  endif
1739 
1740  if (diag%fms_xyave_diag_id>0) then
1741  call post_xy_average(diag_cs, diag, locfield)
1742  endif
1743 
1744  if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.) .and. dl<2) &
1745  deallocate( locfield )
1746 
1747 end subroutine post_data_3d_low
1748 
1749 !> Post the horizontally area-averaged diagnostic
1750 subroutine post_xy_average(diag_cs, diag, field)
1751  type(diag_type), intent(in) :: diag !< This diagnostic
1752  real, target, intent(in) :: field(:,:,:) !< Diagnostic field
1753  type(diag_ctrl), intent(in) :: diag_cs !< Diagnostics mediator control structure
1754  ! Local variable
1755  real, dimension(size(field,3)) :: averaged_field
1756  logical, dimension(size(field,3)) :: averaged_mask
1757  logical :: staggered_in_x, staggered_in_y, used
1758  integer :: nz, remap_nz, coord
1759 
1760  if (.not. diag_cs%ave_enabled) then
1761  return
1762  endif
1763 
1764  staggered_in_x = diag%axes%is_u_point .or. diag%axes%is_q_point
1765  staggered_in_y = diag%axes%is_v_point .or. diag%axes%is_q_point
1766 
1767  if (diag%axes%is_native) then
1768  call horizontally_average_diag_field(diag_cs%G, diag_cs%h, &
1769  staggered_in_x, staggered_in_y, &
1770  diag%axes%is_layer, diag%v_extensive, &
1771  diag_cs%missing_value, field, &
1772  averaged_field, averaged_mask)
1773  else
1774  nz = size(field, 3)
1775  coord = diag%axes%vertical_coordinate_number
1776  remap_nz = diag_cs%diag_remap_cs(coord)%nz
1777 
1778  call assert(diag_cs%diag_remap_cs(coord)%initialized, &
1779  'post_xy_average: remap_cs not initialized.')
1780 
1781  call assert(implies(diag%axes%is_layer, nz == remap_nz), &
1782  'post_xy_average: layer field dimension mismatch.')
1783  call assert(implies(.not. diag%axes%is_layer, nz == remap_nz+1), &
1784  'post_xy_average: interface field dimension mismatch.')
1785 
1786  call horizontally_average_diag_field(diag_cs%G, diag_cs%diag_remap_cs(coord)%h, &
1787  staggered_in_x, staggered_in_y, &
1788  diag%axes%is_layer, diag%v_extensive, &
1789  diag_cs%missing_value, field, &
1790  averaged_field, averaged_mask)
1791  endif
1792 
1793  if (diag_cs%diag_as_chksum) then
1794  call zchksum(averaged_field, trim(diag%debug_str)//'_xyave', &
1795  logunit=diag_cs%chksum_iounit)
1796  else
1797  used = send_data(diag%fms_xyave_diag_id, averaged_field, diag_cs%time_end, &
1798  weight=diag_cs%time_int, mask=averaged_mask)
1799  endif
1800 end subroutine post_xy_average
1801 
1802 !> This subroutine enables the accumulation of time averages over the specified time interval.
1803 subroutine enable_averaging(time_int_in, time_end_in, diag_cs)
1804  real, intent(in) :: time_int_in !< The time interval [s] over which any
1805  !! values that are offered are valid.
1806  type(time_type), intent(in) :: time_end_in !< The end time of the valid interval
1807  type(diag_ctrl), intent(inout) :: diag_cs !< Structure used to regulate diagnostic output
1808 
1809 ! This subroutine enables the accumulation of time averages over the
1810 ! specified time interval.
1811 
1812 ! if (num_file==0) return
1813  diag_cs%time_int = time_int_in
1814  diag_cs%time_end = time_end_in
1815  diag_cs%ave_enabled = .true.
1816 end subroutine enable_averaging
1817 
1818 !> Call this subroutine to avoid averaging any offered fields.
1819 subroutine disable_averaging(diag_cs)
1820  type(diag_ctrl), intent(inout) :: diag_cs !< Structure used to regulate diagnostic output
1821 
1822  diag_cs%time_int = 0.0
1823  diag_cs%ave_enabled = .false.
1824 
1825 end subroutine disable_averaging
1826 
1827 !> Call this subroutine to determine whether the averaging is
1828 !! currently enabled. .true. is returned if it is.
1829 function query_averaging_enabled(diag_cs, time_int, time_end)
1830  type(diag_ctrl), intent(in) :: diag_cs !< Structure used to regulate diagnostic output
1831  real, optional, intent(out) :: time_int !< Current setting of diag%time_int [s]
1832  type(time_type), optional, intent(out) :: time_end !< Current setting of diag%time_end
1833  logical :: query_averaging_enabled
1834 
1835  if (present(time_int)) time_int = diag_cs%time_int
1836  if (present(time_end)) time_end = diag_cs%time_end
1837  query_averaging_enabled = diag_cs%ave_enabled
1838 end function query_averaging_enabled
1839 
1840 !> This function returns the valid end time for use with diagnostics that are
1841 !! handled outside of the MOM6 diagnostics infrastructure.
1842 function get_diag_time_end(diag_cs)
1843  type(diag_ctrl), intent(in) :: diag_cs !< Structure used to regulate diagnostic output
1844  type(time_type) :: get_diag_time_end
1845  ! This function returns the valid end time for diagnostics that are handled
1846  ! outside of the MOM6 infrastructure, such as via the generic tracer code.
1847 
1848  get_diag_time_end = diag_cs%time_end
1849 end function get_diag_time_end
1850 
1851 !> Returns the "diag_mediator" handle for a group (native, CMOR, z-coord, ...) of diagnostics
1852 !! derived from one field.
1853 integer function register_diag_field(module_name, field_name, axes_in, init_time, &
1854  long_name, units, missing_value, range, mask_variant, standard_name, &
1855  verbose, do_not_log, err_msg, interp_method, tile_count, cmor_field_name, &
1856  cmor_long_name, cmor_units, cmor_standard_name, cell_methods, &
1857  x_cell_method, y_cell_method, v_cell_method, conversion, v_extensive)
1858  character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model"
1859  !! or "ice_shelf_model"
1860  character(len=*), intent(in) :: field_name !< Name of the diagnostic field
1861  type(axes_grp), target, intent(in) :: axes_in !< Container w/ up to 3 integer handles that
1862  !! indicates axes for this field
1863  type(time_type), intent(in) :: init_time !< Time at which a field is first available?
1864  character(len=*), optional, intent(in) :: long_name !< Long name of a field.
1865  character(len=*), optional, intent(in) :: units !< Units of a field.
1866  character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
1867  real, optional, intent(in) :: missing_value !< A value that indicates missing values.
1868  real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
1869  logical, optional, intent(in) :: mask_variant !< If true a logical mask must be provided with
1870  !! post_data calls (not used in MOM?)
1871  logical, optional, intent(in) :: verbose !< If true, FMS is verbose (not used in MOM?)
1872  logical, optional, intent(in) :: do_not_log !< If true, do not log something (not used in MOM?)
1873  character(len=*), optional, intent(out):: err_msg !< String into which an error message might be
1874  !! placed (not used in MOM?)
1875  character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should not
1876  !! be interpolated as a scalar
1877  integer, optional, intent(in) :: tile_count !< no clue (not used in MOM?)
1878  character(len=*), optional, intent(in) :: cmor_field_name !< CMOR name of a field
1879  character(len=*), optional, intent(in) :: cmor_long_name !< CMOR long name of a field
1880  character(len=*), optional, intent(in) :: cmor_units !< CMOR units of a field
1881  character(len=*), optional, intent(in) :: cmor_standard_name !< CMOR standardized name associated with a field
1882  character(len=*), optional, intent(in) :: cell_methods !< String to append as cell_methods attribute. Use '' to
1883  !! have no attribute. If present, this overrides the
1884  !! default constructed from the default for
1885  !! each individual axis direction.
1886  character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction.
1887  !! Use '' have no method.
1888  character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction.
1889  !! Use '' have no method.
1890  character(len=*), optional, intent(in) :: v_cell_method !< Specifies the cell method for the vertical direction.
1891  !! Use '' have no method.
1892  real, optional, intent(in) :: conversion !< A value to multiply data by before writing to file
1893  logical, optional, intent(in) :: v_extensive !< True for vertically extensive fields (vertically
1894  !! integrated). Default/absent for intensive.
1895  ! Local variables
1896  real :: mom_missing_value
1897  type(diag_ctrl), pointer :: diag_cs => null()
1898  type(axes_grp), pointer :: remap_axes => null()
1899  type(axes_grp), pointer :: axes => null()
1900  integer :: dm_id, i, dl
1901  character(len=256) :: new_module_name
1902  logical :: active
1903 
1904  axes => axes_in
1905  mom_missing_value = axes%diag_cs%missing_value
1906  if (present(missing_value)) mom_missing_value = missing_value
1907 
1908  diag_cs => axes%diag_cs
1909  dm_id = -1
1910 
1911  if (axes_in%id == diag_cs%axesTL%id) then
1912  axes => diag_cs%axesTL
1913  elseif (axes_in%id == diag_cs%axesBL%id) then
1914  axes => diag_cs%axesBL
1915  elseif (axes_in%id == diag_cs%axesCuL%id ) then
1916  axes => diag_cs%axesCuL
1917  elseif (axes_in%id == diag_cs%axesCvL%id) then
1918  axes => diag_cs%axesCvL
1919  elseif (axes_in%id == diag_cs%axesTi%id) then
1920  axes => diag_cs%axesTi
1921  elseif (axes_in%id == diag_cs%axesBi%id) then
1922  axes => diag_cs%axesBi
1923  elseif (axes_in%id == diag_cs%axesCui%id ) then
1924  axes => diag_cs%axesCui
1925  elseif (axes_in%id == diag_cs%axesCvi%id) then
1926  axes => diag_cs%axesCvi
1927  endif
1928 
1929  ! Register the native diagnostic
1930  active = register_diag_field_expand_cmor(dm_id, module_name, field_name, axes, &
1931  init_time, long_name=long_name, units=units, missing_value=mom_missing_value, &
1932  range=range, mask_variant=mask_variant, standard_name=standard_name, &
1933  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
1934  interp_method=interp_method, tile_count=tile_count, &
1935  cmor_field_name=cmor_field_name, cmor_long_name=cmor_long_name, &
1936  cmor_units=cmor_units, cmor_standard_name=cmor_standard_name, &
1937  cell_methods=cell_methods, x_cell_method=x_cell_method, &
1938  y_cell_method=y_cell_method, v_cell_method=v_cell_method, &
1939  conversion=conversion, v_extensive=v_extensive)
1940 
1941  ! For each diagnostic coordinate register the diagnostic again under a different module name
1942  do i=1,diag_cs%num_diag_coords
1943  new_module_name = trim(module_name)//'_'//trim(diag_cs%diag_remap_cs(i)%diag_module_suffix)
1944 
1945  ! Register diagnostics remapped to z vertical coordinate
1946  if (axes_in%rank == 3) then
1947  remap_axes => null()
1948  if ((axes_in%id == diag_cs%axesTL%id)) then
1949  remap_axes => diag_cs%remap_axesTL(i)
1950  elseif (axes_in%id == diag_cs%axesBL%id) then
1951  remap_axes => diag_cs%remap_axesBL(i)
1952  elseif (axes_in%id == diag_cs%axesCuL%id ) then
1953  remap_axes => diag_cs%remap_axesCuL(i)
1954  elseif (axes_in%id == diag_cs%axesCvL%id) then
1955  remap_axes => diag_cs%remap_axesCvL(i)
1956  elseif (axes_in%id == diag_cs%axesTi%id) then
1957  remap_axes => diag_cs%remap_axesTi(i)
1958  elseif (axes_in%id == diag_cs%axesBi%id) then
1959  remap_axes => diag_cs%remap_axesBi(i)
1960  elseif (axes_in%id == diag_cs%axesCui%id ) then
1961  remap_axes => diag_cs%remap_axesCui(i)
1962  elseif (axes_in%id == diag_cs%axesCvi%id) then
1963  remap_axes => diag_cs%remap_axesCvi(i)
1964  endif
1965  ! When the MOM_diag_to_Z module has been obsoleted we can assume remap_axes will
1966  ! always exist but in the mean-time we have to do this check:
1967  ! call assert(associated(remap_axes), 'register_diag_field: remap_axes not set')
1968  if (associated(remap_axes)) then
1969  if (remap_axes%needs_remapping .or. remap_axes%needs_interpolating) then
1970  active = register_diag_field_expand_cmor(dm_id, new_module_name, field_name, remap_axes, &
1971  init_time, long_name=long_name, units=units, missing_value=mom_missing_value, &
1972  range=range, mask_variant=mask_variant, standard_name=standard_name, &
1973  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
1974  interp_method=interp_method, tile_count=tile_count, &
1975  cmor_field_name=cmor_field_name, cmor_long_name=cmor_long_name, &
1976  cmor_units=cmor_units, cmor_standard_name=cmor_standard_name, &
1977  cell_methods=cell_methods, x_cell_method=x_cell_method, &
1978  y_cell_method=y_cell_method, v_cell_method=v_cell_method, &
1979  conversion=conversion, v_extensive=v_extensive)
1980  if (active) then
1981  call diag_remap_set_active(diag_cs%diag_remap_cs(i))
1982  endif
1983  endif ! remap_axes%needs_remapping
1984  endif ! associated(remap_axes)
1985  endif ! axes%rank == 3
1986  enddo ! i
1987 
1988  !Register downsampled diagnostics
1989  do dl=2,max_dsamp_lev
1990  ! Do not attempt to checksum the downsampled diagnostics
1991  if (diag_cs%diag_as_chksum) cycle
1992 
1993  new_module_name = trim(module_name)//'_d2'
1994 
1995  if (axes_in%rank == 3 .or. axes_in%rank == 2 ) then
1996  axes => null()
1997  if (axes_in%id == diag_cs%axesTL%id) then
1998  axes => diag_cs%dsamp(dl)%axesTL
1999  elseif (axes_in%id == diag_cs%axesBL%id) then
2000  axes => diag_cs%dsamp(dl)%axesBL
2001  elseif (axes_in%id == diag_cs%axesCuL%id ) then
2002  axes => diag_cs%dsamp(dl)%axesCuL
2003  elseif (axes_in%id == diag_cs%axesCvL%id) then
2004  axes => diag_cs%dsamp(dl)%axesCvL
2005  elseif (axes_in%id == diag_cs%axesTi%id) then
2006  axes => diag_cs%dsamp(dl)%axesTi
2007  elseif (axes_in%id == diag_cs%axesBi%id) then
2008  axes => diag_cs%dsamp(dl)%axesBi
2009  elseif (axes_in%id == diag_cs%axesCui%id ) then
2010  axes => diag_cs%dsamp(dl)%axesCui
2011  elseif (axes_in%id == diag_cs%axesCvi%id) then
2012  axes => diag_cs%dsamp(dl)%axesCvi
2013  elseif (axes_in%id == diag_cs%axesT1%id) then
2014  axes => diag_cs%dsamp(dl)%axesT1
2015  elseif (axes_in%id == diag_cs%axesB1%id) then
2016  axes => diag_cs%dsamp(dl)%axesB1
2017  elseif (axes_in%id == diag_cs%axesCu1%id ) then
2018  axes => diag_cs%dsamp(dl)%axesCu1
2019  elseif (axes_in%id == diag_cs%axesCv1%id) then
2020  axes => diag_cs%dsamp(dl)%axesCv1
2021  else
2022  !Niki: Should we worry about these, e.g., diag_to_Z_CS?
2023  call mom_error(warning,"register_diag_field: Could not find a proper axes for " &
2024  //trim( new_module_name)//"-"//trim(field_name))
2025  endif
2026  endif
2027  ! Register the native diagnostic
2028  if (associated(axes)) then
2029  active = register_diag_field_expand_cmor(dm_id, new_module_name, field_name, axes, &
2030  init_time, long_name=long_name, units=units, missing_value=mom_missing_value, &
2031  range=range, mask_variant=mask_variant, standard_name=standard_name, &
2032  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2033  interp_method=interp_method, tile_count=tile_count, &
2034  cmor_field_name=cmor_field_name, cmor_long_name=cmor_long_name, &
2035  cmor_units=cmor_units, cmor_standard_name=cmor_standard_name, &
2036  cell_methods=cell_methods, x_cell_method=x_cell_method, &
2037  y_cell_method=y_cell_method, v_cell_method=v_cell_method, &
2038  conversion=conversion, v_extensive=v_extensive)
2039  endif
2040 
2041  ! For each diagnostic coordinate register the diagnostic again under a different module name
2042  do i=1,diag_cs%num_diag_coords
2043  new_module_name = trim(module_name)//'_'//trim(diag_cs%diag_remap_cs(i)%diag_module_suffix)//'_d2'
2044 
2045  ! Register diagnostics remapped to z vertical coordinate
2046  if (axes_in%rank == 3) then
2047  remap_axes => null()
2048  if ((axes_in%id == diag_cs%axesTL%id)) then
2049  remap_axes => diag_cs%dsamp(dl)%remap_axesTL(i)
2050  elseif (axes_in%id == diag_cs%axesBL%id) then
2051  remap_axes => diag_cs%dsamp(dl)%remap_axesBL(i)
2052  elseif (axes_in%id == diag_cs%axesCuL%id ) then
2053  remap_axes => diag_cs%dsamp(dl)%remap_axesCuL(i)
2054  elseif (axes_in%id == diag_cs%axesCvL%id) then
2055  remap_axes => diag_cs%dsamp(dl)%remap_axesCvL(i)
2056  elseif (axes_in%id == diag_cs%axesTi%id) then
2057  remap_axes => diag_cs%dsamp(dl)%remap_axesTi(i)
2058  elseif (axes_in%id == diag_cs%axesBi%id) then
2059  remap_axes => diag_cs%dsamp(dl)%remap_axesBi(i)
2060  elseif (axes_in%id == diag_cs%axesCui%id ) then
2061  remap_axes => diag_cs%dsamp(dl)%remap_axesCui(i)
2062  elseif (axes_in%id == diag_cs%axesCvi%id) then
2063  remap_axes => diag_cs%dsamp(dl)%remap_axesCvi(i)
2064  endif
2065 
2066  ! When the MOM_diag_to_Z module has been obsoleted we can assume remap_axes will
2067  ! always exist but in the mean-time we have to do this check:
2068  ! call assert(associated(remap_axes), 'register_diag_field: remap_axes not set')
2069  if (associated(remap_axes)) then
2070  if (remap_axes%needs_remapping .or. remap_axes%needs_interpolating) then
2071  active = register_diag_field_expand_cmor(dm_id, new_module_name, field_name, remap_axes, &
2072  init_time, long_name=long_name, units=units, missing_value=mom_missing_value, &
2073  range=range, mask_variant=mask_variant, standard_name=standard_name, &
2074  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2075  interp_method=interp_method, tile_count=tile_count, &
2076  cmor_field_name=cmor_field_name, cmor_long_name=cmor_long_name, &
2077  cmor_units=cmor_units, cmor_standard_name=cmor_standard_name, &
2078  cell_methods=cell_methods, x_cell_method=x_cell_method, &
2079  y_cell_method=y_cell_method, v_cell_method=v_cell_method, &
2080  conversion=conversion, v_extensive=v_extensive)
2081  if (active) then
2082  call diag_remap_set_active(diag_cs%diag_remap_cs(i))
2083  endif
2084  endif ! remap_axes%needs_remapping
2085  endif ! associated(remap_axes)
2086  endif ! axes%rank == 3
2087  enddo ! i
2088  enddo
2089 
2090  register_diag_field = dm_id
2091 
2092 end function register_diag_field
2093 
2094 !> Returns True if either the native or CMOr version of the diagnostic were registered. Updates 'dm_id'
2095 !! after calling register_diag_field_expand_axes() for both native and CMOR variants of the field.
2096 logical function register_diag_field_expand_cmor(dm_id, module_name, field_name, axes, init_time, &
2097  long_name, units, missing_value, range, mask_variant, standard_name, &
2098  verbose, do_not_log, err_msg, interp_method, tile_count, cmor_field_name, &
2099  cmor_long_name, cmor_units, cmor_standard_name, cell_methods, &
2100  x_cell_method, y_cell_method, v_cell_method, conversion, v_extensive)
2101  integer, intent(inout) :: dm_id !< The diag_mediator ID for this diagnostic group
2102  character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model" or "ice_shelf_model"
2103  character(len=*), intent(in) :: field_name !< Name of the diagnostic field
2104  type(axes_grp), target, intent(in) :: axes !< Container w/ up to 3 integer handles that indicates axes
2105  !! for this field
2106  type(time_type), intent(in) :: init_time !< Time at which a field is first available?
2107  character(len=*), optional, intent(in) :: long_name !< Long name of a field.
2108  character(len=*), optional, intent(in) :: units !< Units of a field.
2109  character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
2110  real, optional, intent(in) :: missing_value !< A value that indicates missing values.
2111  real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
2112  logical, optional, intent(in) :: mask_variant !< If true a logical mask must be provided
2113  !! with post_data calls (not used in MOM?)
2114  logical, optional, intent(in) :: verbose !< If true, FMS is verbose (not used in MOM?)
2115  logical, optional, intent(in) :: do_not_log !< If true, do not log something (not used in MOM?)
2116  character(len=*), optional, intent(out):: err_msg !< String into which an error message might be
2117  !! placed (not used in MOM?)
2118  character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should
2119  !! not be interpolated as a scalar
2120  integer, optional, intent(in) :: tile_count !< no clue (not used in MOM?)
2121  character(len=*), optional, intent(in) :: cmor_field_name !< CMOR name of a field
2122  character(len=*), optional, intent(in) :: cmor_long_name !< CMOR long name of a field
2123  character(len=*), optional, intent(in) :: cmor_units !< CMOR units of a field
2124  character(len=*), optional, intent(in) :: cmor_standard_name !< CMOR standardized name associated with a field
2125  character(len=*), optional, intent(in) :: cell_methods !< String to append as cell_methods attribute.
2126  !! Use '' to have no attribute. If present, this
2127  !! overrides the default constructed from the default
2128  !! for each individual axis direction.
2129  character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction.
2130  !! Use '' have no method.
2131  character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction.
2132  !! Use '' have no method.
2133  character(len=*), optional, intent(in) :: v_cell_method !< Specifies the cell method for the vertical direction.
2134  !! Use '' have no method.
2135  real, optional, intent(in) :: conversion !< A value to multiply data by before writing to file
2136  logical, optional, intent(in) :: v_extensive !< True for vertically extensive fields (vertically
2137  !! integrated). Default/absent for intensive.
2138  ! Local variables
2139  real :: mom_missing_value
2140  type(diag_ctrl), pointer :: diag_cs => null()
2141  type(diag_type), pointer :: this_diag => null()
2142  integer :: fms_id, fms_xyave_id
2143  character(len=256) :: posted_cmor_units, posted_cmor_standard_name, posted_cmor_long_name, cm_string, msg
2144 
2145  mom_missing_value = axes%diag_cs%missing_value
2146  if (present(missing_value)) mom_missing_value = missing_value
2147 
2148  register_diag_field_expand_cmor = .false.
2149  diag_cs => axes%diag_cs
2150 
2151  ! Set up the 'primary' diagnostic, first get an underlying FMS id
2152  fms_id = register_diag_field_expand_axes(module_name, field_name, axes, init_time, &
2153  long_name=long_name, units=units, missing_value=mom_missing_value, &
2154  range=range, mask_variant=mask_variant, standard_name=standard_name, &
2155  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2156  interp_method=interp_method, tile_count=tile_count)
2157  if (.not. diag_cs%diag_as_chksum) &
2158  call attach_cell_methods(fms_id, axes, cm_string, cell_methods, &
2159  x_cell_method, y_cell_method, v_cell_method, &
2160  v_extensive=v_extensive)
2161  if (is_root_pe() .and. diag_cs%available_diag_doc_unit > 0) then
2162  msg = ''
2163  if (present(cmor_field_name)) msg = 'CMOR equivalent is "'//trim(cmor_field_name)//'"'
2164  call log_available_diag(fms_id>0, module_name, field_name, cm_string, &
2165  msg, diag_cs, long_name, units, standard_name)
2166  endif
2167  ! Associated horizontally area-averaged diagnostic
2168  fms_xyave_id = diag_field_not_found
2169  if (associated(axes%xyave_axes)) then
2170  fms_xyave_id = register_diag_field_expand_axes(module_name, trim(field_name)//'_xyave', &
2171  axes%xyave_axes, init_time, &
2172  long_name=long_name, units=units, missing_value=mom_missing_value, &
2173  range=range, mask_variant=mask_variant, standard_name=standard_name, &
2174  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2175  interp_method=interp_method, tile_count=tile_count)
2176  if (.not. diag_cs%diag_as_chksum) &
2177  call attach_cell_methods(fms_xyave_id, axes%xyave_axes, cm_string, &
2178  cell_methods, v_cell_method, v_extensive=v_extensive)
2179  if (is_root_pe() .and. diag_cs%available_diag_doc_unit > 0) then
2180  msg = ''
2181  if (present(cmor_field_name)) msg = 'CMOR equivalent is "'//trim(cmor_field_name)//'_xyave"'
2182  call log_available_diag(fms_xyave_id>0, module_name, trim(field_name)//'_xyave', cm_string, &
2183  msg, diag_cs, long_name, units, standard_name)
2184  endif
2185  endif
2186  this_diag => null()
2187  if (fms_id /= diag_field_not_found .or. fms_xyave_id /= diag_field_not_found) then
2188  call add_diag_to_list(diag_cs, dm_id, fms_id, this_diag, axes, module_name, field_name, msg)
2189  this_diag%fms_xyave_diag_id = fms_xyave_id
2190  !Encode and save the cell methods for this diag
2191  call add_xyz_method(this_diag, axes, x_cell_method, y_cell_method, v_cell_method, v_extensive)
2192  if (present(v_extensive)) this_diag%v_extensive = v_extensive
2193  if (present(conversion)) this_diag%conversion_factor = conversion
2194  register_diag_field_expand_cmor = .true.
2195  endif
2196 
2197  ! For the CMOR variation of the above diagnostic
2198  if (present(cmor_field_name) .and. .not. diag_cs%diag_as_chksum) then
2199  ! Fallback values for strings set to "NULL"
2200  posted_cmor_units = "not provided" !
2201  posted_cmor_standard_name = "not provided" ! Values might be able to be replaced with a CS%missing field?
2202  posted_cmor_long_name = "not provided" !
2203 
2204  ! If attributes are present for MOM variable names, use them first for the register_diag_field
2205  ! call for CMOR verison of the variable
2206  if (present(units)) posted_cmor_units = units
2207  if (present(standard_name)) posted_cmor_standard_name = standard_name
2208  if (present(long_name)) posted_cmor_long_name = long_name
2209 
2210  ! If specified in the call to register_diag_field, override attributes with the CMOR versions
2211  if (present(cmor_units)) posted_cmor_units = cmor_units
2212  if (present(cmor_standard_name)) posted_cmor_standard_name = cmor_standard_name
2213  if (present(cmor_long_name)) posted_cmor_long_name = cmor_long_name
2214 
2215  fms_id = register_diag_field_expand_axes(module_name, cmor_field_name, axes, init_time, &
2216  long_name=trim(posted_cmor_long_name), units=trim(posted_cmor_units), &
2217  missing_value=mom_missing_value, range=range, mask_variant=mask_variant, &
2218  standard_name=trim(posted_cmor_standard_name), verbose=verbose, do_not_log=do_not_log, &
2219  err_msg=err_msg, interp_method=interp_method, tile_count=tile_count)
2220  call attach_cell_methods(fms_id, axes, cm_string, &
2221  cell_methods, x_cell_method, y_cell_method, v_cell_method, &
2222  v_extensive=v_extensive)
2223  if (is_root_pe() .and. diag_cs%available_diag_doc_unit > 0) then
2224  msg = 'native name is "'//trim(field_name)//'"'
2225  call log_available_diag(fms_id>0, module_name, cmor_field_name, cm_string, &
2226  msg, diag_cs, posted_cmor_long_name, posted_cmor_units, &
2227  posted_cmor_standard_name)
2228  endif
2229  ! Associated horizontally area-averaged diagnostic
2230  fms_xyave_id = diag_field_not_found
2231  if (associated(axes%xyave_axes)) then
2232  fms_xyave_id = register_diag_field_expand_axes(module_name, trim(cmor_field_name)//'_xyave', &
2233  axes%xyave_axes, init_time, &
2234  long_name=trim(posted_cmor_long_name), units=trim(posted_cmor_units), &
2235  missing_value=mom_missing_value, range=range, mask_variant=mask_variant, &
2236  standard_name=trim(posted_cmor_standard_name), verbose=verbose, do_not_log=do_not_log, &
2237  err_msg=err_msg, interp_method=interp_method, tile_count=tile_count)
2238  call attach_cell_methods(fms_xyave_id, axes%xyave_axes, cm_string, &
2239  cell_methods, v_cell_method, v_extensive=v_extensive)
2240  if (is_root_pe() .and. diag_cs%available_diag_doc_unit > 0) then
2241  msg = 'native name is "'//trim(field_name)//'_xyave"'
2242  call log_available_diag(fms_xyave_id>0, module_name, trim(cmor_field_name)//'_xyave', &
2243  cm_string, msg, diag_cs, posted_cmor_long_name, posted_cmor_units, &
2244  posted_cmor_standard_name)
2245  endif
2246  endif
2247  this_diag => null()
2248  if (fms_id /= diag_field_not_found .or. fms_xyave_id /= diag_field_not_found) then
2249  call add_diag_to_list(diag_cs, dm_id, fms_id, this_diag, axes, module_name, field_name, msg)
2250  this_diag%fms_xyave_diag_id = fms_xyave_id
2251  !Encode and save the cell methods for this diag
2252  call add_xyz_method(this_diag, axes, x_cell_method, y_cell_method, v_cell_method, v_extensive)
2253  if (present(v_extensive)) this_diag%v_extensive = v_extensive
2254  if (present(conversion)) this_diag%conversion_factor = conversion
2255  register_diag_field_expand_cmor = .true.
2256  endif
2257  endif
2258 
2259 end function register_diag_field_expand_cmor
2260 
2261 !> Returns an FMS id from register_diag_field_fms (the diag_manager routine) after expanding axes
2262 !! (axes-group) into handles and conditionally adding an FMS area_id for cell_measures.
2263 integer function register_diag_field_expand_axes(module_name, field_name, axes, init_time, &
2264  long_name, units, missing_value, range, mask_variant, standard_name, &
2265  verbose, do_not_log, err_msg, interp_method, tile_count)
2266  character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model"
2267  !! or "ice_shelf_model"
2268  character(len=*), intent(in) :: field_name !< Name of the diagnostic field
2269  type(axes_grp), target, intent(in) :: axes !< Container w/ up to 3 integer handles that indicates
2270  !! axes for this field
2271  type(time_type), intent(in) :: init_time !< Time at which a field is first available?
2272  character(len=*), optional, intent(in) :: long_name !< Long name of a field.
2273  character(len=*), optional, intent(in) :: units !< Units of a field.
2274  character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
2275  real, optional, intent(in) :: missing_value !< A value that indicates missing values.
2276  real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
2277  logical, optional, intent(in) :: mask_variant !< If true a logical mask must be provided
2278  !! with post_data calls (not used in MOM?)
2279  logical, optional, intent(in) :: verbose !< If true, FMS is verbose (not used in MOM?)
2280  logical, optional, intent(in) :: do_not_log !< If true, do not log something
2281  !! (not used in MOM?)
2282  character(len=*), optional, intent(out):: err_msg !< String into which an error message might be
2283  !! placed (not used in MOM?)
2284  character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should
2285  !! not be interpolated as a scalar
2286  integer, optional, intent(in) :: tile_count !< no clue (not used in MOM?)
2287  ! Local variables
2288  integer :: fms_id, area_id, volume_id
2289 
2290  ! This gets the cell area associated with the grid location of this variable
2291  area_id = axes%id_area
2292  volume_id = axes%id_volume
2293 
2294  ! Get the FMS diagnostic id
2295  if (axes%diag_cs%diag_as_chksum) then
2296  fms_id = axes%diag_cs%num_chksum_diags + 1
2297  axes%diag_cs%num_chksum_diags = fms_id
2298  else if (present(interp_method) .or. axes%is_h_point) then
2299  ! If interp_method is provided we must use it
2300  if (area_id>0) then
2301  if (volume_id>0) then
2302  fms_id = register_diag_field_fms(module_name, field_name, axes%handles, &
2303  init_time, long_name=long_name, units=units, missing_value=missing_value, &
2304  range=range, mask_variant=mask_variant, standard_name=standard_name, &
2305  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2306  interp_method=interp_method, tile_count=tile_count, area=area_id, volume=volume_id)
2307  else
2308  fms_id = register_diag_field_fms(module_name, field_name, axes%handles, &
2309  init_time, long_name=long_name, units=units, missing_value=missing_value, &
2310  range=range, mask_variant=mask_variant, standard_name=standard_name, &
2311  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2312  interp_method=interp_method, tile_count=tile_count, area=area_id)
2313  endif
2314  else
2315  if (volume_id>0) then
2316  fms_id = register_diag_field_fms(module_name, field_name, axes%handles, &
2317  init_time, long_name=long_name, units=units, missing_value=missing_value, &
2318  range=range, mask_variant=mask_variant, standard_name=standard_name, &
2319  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2320  interp_method=interp_method, tile_count=tile_count, volume=volume_id)
2321  else
2322  fms_id = register_diag_field_fms(module_name, field_name, axes%handles, &
2323  init_time, long_name=long_name, units=units, missing_value=missing_value, &
2324  range=range, mask_variant=mask_variant, standard_name=standard_name, &
2325  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2326  interp_method=interp_method, tile_count=tile_count)
2327  endif
2328  endif
2329  else
2330  ! If interp_method is not provided and the field is not at an h-point then interp_method='none'
2331  if (area_id>0) then
2332  if (volume_id>0) then
2333  fms_id = register_diag_field_fms(module_name, field_name, axes%handles, &
2334  init_time, long_name=long_name, units=units, missing_value=missing_value, &
2335  range=range, mask_variant=mask_variant, standard_name=standard_name, &
2336  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2337  interp_method='none', tile_count=tile_count, area=area_id, volume=volume_id)
2338  else
2339  fms_id = register_diag_field_fms(module_name, field_name, axes%handles, &
2340  init_time, long_name=long_name, units=units, missing_value=missing_value, &
2341  range=range, mask_variant=mask_variant, standard_name=standard_name, &
2342  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2343  interp_method='none', tile_count=tile_count, area=area_id)
2344  endif
2345  else
2346  if (volume_id>0) then
2347  fms_id = register_diag_field_fms(module_name, field_name, axes%handles, &
2348  init_time, long_name=long_name, units=units, missing_value=missing_value, &
2349  range=range, mask_variant=mask_variant, standard_name=standard_name, &
2350  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2351  interp_method='none', tile_count=tile_count, volume=volume_id)
2352  else
2353  fms_id = register_diag_field_fms(module_name, field_name, axes%handles, &
2354  init_time, long_name=long_name, units=units, missing_value=missing_value, &
2355  range=range, mask_variant=mask_variant, standard_name=standard_name, &
2356  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2357  interp_method='none', tile_count=tile_count)
2358  endif
2359  endif
2360  endif
2361 
2362  register_diag_field_expand_axes = fms_id
2363 
2364 end function register_diag_field_expand_axes
2365 
2366 !> Create a diagnostic type and attached to list
2367 subroutine add_diag_to_list(diag_cs, dm_id, fms_id, this_diag, axes, module_name, field_name, msg)
2368  type(diag_ctrl), pointer :: diag_cs !< Diagnostics mediator control structure
2369  integer, intent(inout) :: dm_id !< The diag_mediator ID for this diagnostic group
2370  integer, intent(in) :: fms_id !< The FMS diag_manager ID for this diagnostic
2371  type(diag_type), pointer :: this_diag !< This diagnostic
2372  type(axes_grp), target, intent(in) :: axes !< Container w/ up to 3 integer handles that
2373  !! indicates axes for this field
2374  character(len=*), intent(in) :: module_name !< Name of this module, usually
2375  !! "ocean_model" or "ice_shelf_model"
2376  character(len=*), intent(in) :: field_name !< Name of diagnostic
2377  character(len=*), intent(in) :: msg !< Message for errors
2378 
2379  ! If the diagnostic is needed obtain a diag_mediator ID (if needed)
2380  if (dm_id == -1) dm_id = get_new_diag_id(diag_cs)
2381  ! Create a new diag_type to store links in
2382  call alloc_diag_with_id(dm_id, diag_cs, this_diag)
2383  call assert(associated(this_diag), trim(msg)//': diag_type allocation failed')
2384  ! Record FMS id, masks and conversion factor, in diag_type
2385  this_diag%fms_diag_id = fms_id
2386  this_diag%debug_str = trim(module_name)//"-"//trim(field_name)
2387  this_diag%axes => axes
2388 
2389 end subroutine add_diag_to_list
2390 
2391 !> Adds the encoded "cell_methods" for a diagnostics as a diag% property
2392 !! This allows access to the cell_method for a given diagnostics at the time of sending
2393 subroutine add_xyz_method(diag, axes, x_cell_method, y_cell_method, v_cell_method, v_extensive)
2394  type(diag_type), pointer :: diag !< This diagnostic
2395  type(axes_grp), intent(in) :: axes !< Container w/ up to 3 integer handles that indicates
2396  !! axes for this field
2397  character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction.
2398  !! Use '' have no method.
2399  character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction.
2400  !! Use '' have no method.
2401  character(len=*), optional, intent(in) :: v_cell_method !< Specifies the cell method for the vertical direction.
2402  !! Use '' have no method.
2403  logical, optional, intent(in) :: v_extensive !< True for vertically extensive fields
2404  !! (vertically integrated). Default/absent for intensive.
2405  integer :: xyz_method
2406  character(len=9) :: mstr
2407 
2408  !This is a simple way to encode the cell method information made from 3 strings
2409  !(x_cell_method,y_cell_method,v_cell_method) in a 3 digit integer xyz
2410  !x_cell_method,y_cell_method,v_cell_method can each be 'point' or 'sum' or 'mean'
2411  !We can encode these with setting 1 for 'point', 2 for 'sum, 3 for 'mean' in
2412  !the 100s position for x, 10s position for y, 1s position for z
2413  !E.g., x:sum,y:point,z:mean is 213
2414 
2415  xyz_method = 111
2416 
2417  mstr = diag%axes%v_cell_method
2418  if (present(v_extensive)) then
2419  if (present(v_cell_method)) call mom_error(fatal, "attach_cell_methods: " // &
2420  'Vertical cell method was specified along with the vertically extensive flag.')
2421  if(v_extensive) then
2422  mstr='sum'
2423  else
2424  mstr='mean'
2425  endif
2426  elseif (present(v_cell_method)) then
2427  mstr = v_cell_method
2428  endif
2429  if (trim(mstr)=='sum') then
2430  xyz_method = xyz_method + 1
2431  elseif (trim(mstr)=='mean') then
2432  xyz_method = xyz_method + 2
2433  endif
2434 
2435  mstr = diag%axes%y_cell_method
2436  if (present(y_cell_method)) mstr = y_cell_method
2437  if (trim(mstr)=='sum') then
2438  xyz_method = xyz_method + 10
2439  elseif (trim(mstr)=='mean') then
2440  xyz_method = xyz_method + 20
2441  endif
2442 
2443  mstr = diag%axes%x_cell_method
2444  if (present(x_cell_method)) mstr = x_cell_method
2445  if (trim(mstr)=='sum') then
2446  xyz_method = xyz_method + 100
2447  elseif (trim(mstr)=='mean') then
2448  xyz_method = xyz_method + 200
2449  endif
2450 
2451  diag%xyz_method = xyz_method
2452 end subroutine add_xyz_method
2453 
2454 !> Attaches "cell_methods" attribute to a variable based on defaults for axes_grp or optional arguments.
2455 subroutine attach_cell_methods(id, axes, ostring, cell_methods, &
2456  x_cell_method, y_cell_method, v_cell_method, v_extensive)
2457  integer, intent(in) :: id !< Handle to diagnostic
2458  type(axes_grp), intent(in) :: axes !< Container w/ up to 3 integer handles that indicates
2459  !! axes for this field
2460  character(len=*), intent(out) :: ostring !< The cell_methods strings that would appear in the file
2461  character(len=*), optional, intent(in) :: cell_methods !< String to append as cell_methods attribute.
2462  !! Use '' to have no attribute. If present, this
2463  !! overrides the default constructed from the default
2464  !! for each individual axis direction.
2465  character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction.
2466  !! Use '' have no method.
2467  character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction.
2468  !! Use '' have no method.
2469  character(len=*), optional, intent(in) :: v_cell_method !< Specifies the cell method for the vertical direction.
2470  !! Use '' have no method.
2471  logical, optional, intent(in) :: v_extensive !< True for vertically extensive fields
2472  !! (vertically integrated). Default/absent for intensive.
2473  ! Local variables
2474  character(len=9) :: axis_name
2475  logical :: x_mean, y_mean, x_sum, y_sum
2476 
2477  x_mean = .false.
2478  y_mean = .false.
2479  x_sum = .false.
2480  y_sum = .false.
2481 
2482  ostring = ''
2483  if (present(cell_methods)) then
2484  if (present(x_cell_method) .or. present(y_cell_method) .or. present(v_cell_method) &
2485  .or. present(v_extensive)) then
2486  call mom_error(fatal, "attach_cell_methods: " // &
2487  'Individual direction cell method was specified along with a "cell_methods" string.')
2488  endif
2489  if (len(trim(cell_methods))>0) then
2490  call diag_field_add_attribute(id, 'cell_methods', trim(cell_methods))
2491  ostring = trim(cell_methods)
2492  endif
2493  else
2494  if (present(x_cell_method)) then
2495  if (len(trim(x_cell_method))>0) then
2496  call get_diag_axis_name(axes%handles(1), axis_name)
2497  call diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(x_cell_method))
2498  ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(x_cell_method)
2499  if (trim(x_cell_method)=='mean') x_mean=.true.
2500  if (trim(x_cell_method)=='sum') x_sum=.true.
2501  endif
2502  else
2503  if (len(trim(axes%x_cell_method))>0) then
2504  call get_diag_axis_name(axes%handles(1), axis_name)
2505  call diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(axes%x_cell_method))
2506  ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(axes%x_cell_method)
2507  if (trim(axes%x_cell_method)=='mean') x_mean=.true.
2508  if (trim(axes%x_cell_method)=='sum') x_sum=.true.
2509  endif
2510  endif
2511  if (present(y_cell_method)) then
2512  if (len(trim(y_cell_method))>0) then
2513  call get_diag_axis_name(axes%handles(2), axis_name)
2514  call diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(y_cell_method))
2515  ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(y_cell_method)
2516  if (trim(y_cell_method)=='mean') y_mean=.true.
2517  if (trim(y_cell_method)=='sum') y_sum=.true.
2518  endif
2519  else
2520  if (len(trim(axes%y_cell_method))>0) then
2521  call get_diag_axis_name(axes%handles(2), axis_name)
2522  call diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(axes%y_cell_method))
2523  ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(axes%y_cell_method)
2524  if (trim(axes%y_cell_method)=='mean') y_mean=.true.
2525  if (trim(axes%y_cell_method)=='sum') y_sum=.true.
2526  endif
2527  endif
2528  if (present(v_cell_method)) then
2529  if (present(v_extensive)) call mom_error(fatal, "attach_cell_methods: " // &
2530  'Vertical cell method was specified along with the vertically extensive flag.')
2531  if (len(trim(v_cell_method))>0) then
2532  if (axes%rank==1) then
2533  call get_diag_axis_name(axes%handles(1), axis_name)
2534  elseif (axes%rank==3) then
2535  call get_diag_axis_name(axes%handles(3), axis_name)
2536  endif
2537  call diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(v_cell_method))
2538  ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(v_cell_method)
2539  endif
2540  elseif (present(v_extensive)) then
2541  if(v_extensive) then
2542  if (axes%rank==1) then
2543  call get_diag_axis_name(axes%handles(1), axis_name)
2544  elseif (axes%rank==3) then
2545  call get_diag_axis_name(axes%handles(3), axis_name)
2546  endif
2547  call diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':sum')
2548  ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':sum'
2549  endif
2550  else
2551  if (len(trim(axes%v_cell_method))>0) then
2552  if (axes%rank==1) then
2553  call get_diag_axis_name(axes%handles(1), axis_name)
2554  elseif (axes%rank==3) then
2555  call get_diag_axis_name(axes%handles(3), axis_name)
2556  endif
2557  call diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(axes%v_cell_method))
2558  ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(axes%v_cell_method)
2559  endif
2560  endif
2561  if (x_mean .and. y_mean) then
2562  call diag_field_add_attribute(id, 'cell_methods', 'area:mean')
2563  ostring = trim(adjustl(ostring))//' area:mean'
2564  elseif (x_sum .and. y_sum) then
2565  call diag_field_add_attribute(id, 'cell_methods', 'area:sum')
2566  ostring = trim(adjustl(ostring))//' area:sum'
2567  endif
2568  endif
2569  ostring = adjustl(ostring)
2570 end subroutine attach_cell_methods
2571 
2572 function register_scalar_field(module_name, field_name, init_time, diag_cs, &
2573  long_name, units, missing_value, range, standard_name, &
2574  do_not_log, err_msg, interp_method, cmor_field_name, &
2575  cmor_long_name, cmor_units, cmor_standard_name)
2576  integer :: register_scalar_field !< An integer handle for a diagnostic array.
2577  character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model"
2578  !! or "ice_shelf_model"
2579  character(len=*), intent(in) :: field_name !< Name of the diagnostic field
2580  type(time_type), intent(in) :: init_time !< Time at which a field is first available?
2581  type(diag_ctrl), intent(inout) :: diag_cs !< Structure used to regulate diagnostic output
2582  character(len=*), optional, intent(in) :: long_name !< Long name of a field.
2583  character(len=*), optional, intent(in) :: units !< Units of a field.
2584  character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
2585  real, optional, intent(in) :: missing_value !< A value that indicates missing values.
2586  real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
2587  logical, optional, intent(in) :: do_not_log !< If true, do not log something (not used in MOM?)
2588  character(len=*), optional, intent(out):: err_msg !< String into which an error message might be
2589  !! placed (not used in MOM?)
2590  character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should not
2591  !! be interpolated as a scalar
2592  character(len=*), optional, intent(in) :: cmor_field_name !< CMOR name of a field
2593  character(len=*), optional, intent(in) :: cmor_long_name !< CMOR long name of a field
2594  character(len=*), optional, intent(in) :: cmor_units !< CMOR units of a field
2595  character(len=*), optional, intent(in) :: cmor_standard_name !< CMOR standardized name associated with a field
2596 
2597  ! Local variables
2598  real :: mom_missing_value
2599  integer :: dm_id, fms_id
2600  type(diag_type), pointer :: diag => null(), cmor_diag => null()
2601  character(len=256) :: posted_cmor_units, posted_cmor_standard_name, posted_cmor_long_name
2602 
2603  mom_missing_value = diag_cs%missing_value
2604  if (present(missing_value)) mom_missing_value = missing_value
2605 
2606  dm_id = -1
2607  diag => null()
2608  cmor_diag => null()
2609 
2610  if (diag_cs%diag_as_chksum) then
2611  fms_id = diag_cs%num_chksum_diags + 1
2612  diag_cs%num_chksum_diags = fms_id
2613  else
2614  fms_id = register_diag_field_fms(module_name, field_name, init_time, &
2615  long_name=long_name, units=units, missing_value=mom_missing_value, &
2616  range=range, standard_name=standard_name, do_not_log=do_not_log, &
2617  err_msg=err_msg)
2618  endif
2619 
2620  if (fms_id /= diag_field_not_found) then
2621  dm_id = get_new_diag_id(diag_cs)
2622  call alloc_diag_with_id(dm_id, diag_cs, diag)
2623  call assert(associated(diag), 'register_scalar_field: diag allocation failed')
2624  diag%fms_diag_id = fms_id
2625  diag%debug_str = trim(module_name)//"-"//trim(field_name)
2626  endif
2627 
2628  if (present(cmor_field_name)) then
2629  ! Fallback values for strings set to "not provided"
2630  posted_cmor_units = "not provided"
2631  posted_cmor_standard_name = "not provided"
2632  posted_cmor_long_name = "not provided"
2633 
2634  ! If attributes are present for MOM variable names, use them first for the register_static_field
2635  ! call for CMOR verison of the variable
2636  if (present(units)) posted_cmor_units = units
2637  if (present(standard_name)) posted_cmor_standard_name = standard_name
2638  if (present(long_name)) posted_cmor_long_name = long_name
2639 
2640  ! If specified in the call to register_static_field, override attributes with the CMOR versions
2641  if (present(cmor_units)) posted_cmor_units = cmor_units
2642  if (present(cmor_standard_name)) posted_cmor_standard_name = cmor_standard_name
2643  if (present(cmor_long_name)) posted_cmor_long_name = cmor_long_name
2644 
2645  fms_id = register_diag_field_fms(module_name, cmor_field_name, init_time, &
2646  long_name=trim(posted_cmor_long_name), units=trim(posted_cmor_units), &
2647  missing_value=mom_missing_value, range=range, &
2648  standard_name=trim(posted_cmor_standard_name), do_not_log=do_not_log, err_msg=err_msg)
2649  if (fms_id /= diag_field_not_found) then
2650  if (dm_id == -1) then
2651  dm_id = get_new_diag_id(diag_cs)
2652  endif
2653  call alloc_diag_with_id(dm_id, diag_cs, cmor_diag)
2654  cmor_diag%fms_diag_id = fms_id
2655  cmor_diag%debug_str = trim(module_name)//"-"//trim(cmor_field_name)
2656  endif
2657  endif
2658 
2659  ! Document diagnostics in list of available diagnostics
2660  if (is_root_pe() .and. diag_cs%available_diag_doc_unit > 0) then
2661  call log_available_diag(associated(diag), module_name, field_name, '', '', diag_cs, &
2662  long_name, units, standard_name)
2663  if (present(cmor_field_name)) then
2664  call log_available_diag(associated(cmor_diag), module_name, cmor_field_name, &
2665  '', '', diag_cs, posted_cmor_long_name, posted_cmor_units, &
2666  posted_cmor_standard_name)
2667  endif
2668  endif
2669 
2670  register_scalar_field = dm_id
2671 
2672 end function register_scalar_field
2673 
2674 !> Registers a static diagnostic, returning an integer handle
2675 function register_static_field(module_name, field_name, axes, &
2676  long_name, units, missing_value, range, mask_variant, standard_name, &
2677  do_not_log, interp_method, tile_count, &
2678  cmor_field_name, cmor_long_name, cmor_units, cmor_standard_name, area, &
2679  x_cell_method, y_cell_method, area_cell_method, conversion)
2680  integer :: register_static_field !< An integer handle for a diagnostic array.
2681  character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model"
2682  !! or "ice_shelf_model"
2683  character(len=*), intent(in) :: field_name !< Name of the diagnostic field
2684  type(axes_grp), target, intent(in) :: axes !< Container w/ up to 3 integer handles that
2685  !! indicates axes for this field
2686  character(len=*), optional, intent(in) :: long_name !< Long name of a field.
2687  character(len=*), optional, intent(in) :: units !< Units of a field.
2688  character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
2689  real, optional, intent(in) :: missing_value !< A value that indicates missing values.
2690  real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
2691  logical, optional, intent(in) :: mask_variant !< If true a logical mask must be provided with
2692  !! post_data calls (not used in MOM?)
2693  logical, optional, intent(in) :: do_not_log !< If true, do not log something (not used in MOM?)
2694  character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should not
2695  !! be interpolated as a scalar
2696  integer, optional, intent(in) :: tile_count !< no clue (not used in MOM?)
2697  character(len=*), optional, intent(in) :: cmor_field_name !< CMOR name of a field
2698  character(len=*), optional, intent(in) :: cmor_long_name !< CMOR long name of a field
2699  character(len=*), optional, intent(in) :: cmor_units !< CMOR units of a field
2700  character(len=*), optional, intent(in) :: cmor_standard_name !< CMOR standardized name associated with a field
2701  integer, optional, intent(in) :: area !< fms_id for area_t
2702  character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction.
2703  character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction.
2704  character(len=*), optional, intent(in) :: area_cell_method !< Specifies the cell method for area
2705  real, optional, intent(in) :: conversion !< A value to multiply data by before writing to file
2706 
2707  ! Local variables
2708  real :: mom_missing_value
2709  type(diag_ctrl), pointer :: diag_cs => null()
2710  type(diag_type), pointer :: diag => null(), cmor_diag => null()
2711  integer :: dm_id, fms_id, cmor_id
2712  character(len=256) :: posted_cmor_units, posted_cmor_standard_name, posted_cmor_long_name
2713  character(len=9) :: axis_name
2714 
2715  mom_missing_value = axes%diag_cs%missing_value
2716  if (present(missing_value)) mom_missing_value = missing_value
2717 
2718  diag_cs => axes%diag_cs
2719  dm_id = -1
2720  diag => null()
2721  cmor_diag => null()
2722 
2723  if (diag_cs%diag_as_chksum) then
2724  fms_id = diag_cs%num_chksum_diags + 1
2725  diag_cs%num_chksum_diags = fms_id
2726  else
2727  fms_id = register_static_field_fms(module_name, field_name, axes%handles, &
2728  long_name=long_name, units=units, missing_value=mom_missing_value, &
2729  range=range, mask_variant=mask_variant, standard_name=standard_name, &
2730  do_not_log=do_not_log, &
2731  interp_method=interp_method, tile_count=tile_count, area=area)
2732  endif
2733 
2734  if (fms_id /= diag_field_not_found) then
2735  dm_id = get_new_diag_id(diag_cs)
2736  call alloc_diag_with_id(dm_id, diag_cs, diag)
2737  call assert(associated(diag), 'register_static_field: diag allocation failed')
2738  diag%fms_diag_id = fms_id
2739  diag%debug_str = trim(module_name)//"-"//trim(field_name)
2740  if (present(conversion)) diag%conversion_factor = conversion
2741 
2742  if (diag_cs%diag_as_chksum) then
2743  diag%axes => axes
2744  else
2745  if (present(x_cell_method)) then
2746  call get_diag_axis_name(axes%handles(1), axis_name)
2747  call diag_field_add_attribute(fms_id, 'cell_methods', &
2748  trim(axis_name)//':'//trim(x_cell_method))
2749  endif
2750  if (present(y_cell_method)) then
2751  call get_diag_axis_name(axes%handles(2), axis_name)
2752  call diag_field_add_attribute(fms_id, 'cell_methods', &
2753  trim(axis_name)//':'//trim(y_cell_method))
2754  endif
2755  if (present(area_cell_method)) then
2756  call diag_field_add_attribute(fms_id, 'cell_methods', &
2757  'area:'//trim(area_cell_method))
2758  endif
2759  endif
2760  endif
2761 
2762  if (present(cmor_field_name) .and. .not. diag_cs%diag_as_chksum) then
2763  ! Fallback values for strings set to "not provided"
2764  posted_cmor_units = "not provided"
2765  posted_cmor_standard_name = "not provided"
2766  posted_cmor_long_name = "not provided"
2767 
2768  ! If attributes are present for MOM variable names, use them first for the register_static_field
2769  ! call for CMOR verison of the variable
2770  if (present(units)) posted_cmor_units = units
2771  if (present(standard_name)) posted_cmor_standard_name = standard_name
2772  if (present(long_name)) posted_cmor_long_name = long_name
2773 
2774  ! If specified in the call to register_static_field, override attributes with the CMOR versions
2775  if (present(cmor_units)) posted_cmor_units = cmor_units
2776  if (present(cmor_standard_name)) posted_cmor_standard_name = cmor_standard_name
2777  if (present(cmor_long_name)) posted_cmor_long_name = cmor_long_name
2778 
2779  fms_id = register_static_field_fms(module_name, cmor_field_name, &
2780  axes%handles, long_name=trim(posted_cmor_long_name), units=trim(posted_cmor_units), &
2781  missing_value=mom_missing_value, range=range, mask_variant=mask_variant, &
2782  standard_name=trim(posted_cmor_standard_name), do_not_log=do_not_log, &
2783  interp_method=interp_method, tile_count=tile_count, area=area)
2784  if (fms_id /= diag_field_not_found) then
2785  if (dm_id == -1) then
2786  dm_id = get_new_diag_id(diag_cs)
2787  endif
2788  call alloc_diag_with_id(dm_id, diag_cs, cmor_diag)
2789  cmor_diag%fms_diag_id = fms_id
2790  cmor_diag%debug_str = trim(module_name)//"-"//trim(cmor_field_name)
2791  if (present(conversion)) cmor_diag%conversion_factor = conversion
2792  if (present(x_cell_method)) then
2793  call get_diag_axis_name(axes%handles(1), axis_name)
2794  call diag_field_add_attribute(fms_id, 'cell_methods', trim(axis_name)//':'//trim(x_cell_method))
2795  endif
2796  if (present(y_cell_method)) then
2797  call get_diag_axis_name(axes%handles(2), axis_name)
2798  call diag_field_add_attribute(fms_id, 'cell_methods', trim(axis_name)//':'//trim(y_cell_method))
2799  endif
2800  if (present(area_cell_method)) then
2801  call diag_field_add_attribute(fms_id, 'cell_methods', 'area:'//trim(area_cell_method))
2802  endif
2803  endif
2804  endif
2805 
2806  ! Document diagnostics in list of available diagnostics
2807  if (is_root_pe() .and. diag_cs%available_diag_doc_unit > 0) then
2808  call log_available_diag(associated(diag), module_name, field_name, '', '', diag_cs, &
2809  long_name, units, standard_name)
2810  if (present(cmor_field_name)) then
2811  call log_available_diag(associated(cmor_diag), module_name, cmor_field_name, &
2812  '', '', diag_cs, posted_cmor_long_name, posted_cmor_units, &
2813  posted_cmor_standard_name)
2814  endif
2815  endif
2816 
2817  register_static_field = dm_id
2818 
2819 end function register_static_field
2820 
2821 !> Describe an option setting in the diagnostic files.
2822 subroutine describe_option(opt_name, value, diag_CS)
2823  character(len=*), intent(in) :: opt_name !< The name of the option
2824  character(len=*), intent(in) :: value !< A character string with the setting of the option.
2825  type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
2826 
2827  character(len=240) :: mesg
2828  integer :: len_ind
2829 
2830  len_ind = len_trim(value) ! Add error handling for long values?
2831 
2832  mesg = " ! "//trim(opt_name)//": "//trim(value)
2833  write(diag_cs%available_diag_doc_unit, '(a)') trim(mesg)
2834 end subroutine describe_option
2835 
2836 !> Registers a diagnostic using the information encapsulated in the vardesc
2837 !! type argument and returns an integer handle to this diagostic. That
2838 !! integer handle is negative if the diagnostic is unused.
2839 function ocean_register_diag(var_desc, G, diag_CS, day)
2840  integer :: ocean_register_diag !< An integer handle to this diagnostic.
2841  type(vardesc), intent(in) :: var_desc !< The vardesc type describing the diagnostic
2842  type(ocean_grid_type), intent(in) :: g !< The ocean's grid type
2843  type(diag_ctrl), intent(in), target :: diag_cs !< The diagnotic control structure
2844  type(time_type), intent(in) :: day !< The current model time
2845 
2846  character(len=64) :: var_name ! A variable's name.
2847  character(len=48) :: units ! A variable's units.
2848  character(len=240) :: longname ! A variable's longname.
2849  character(len=8) :: hor_grid, z_grid ! Variable grid info.
2850  type(axes_grp), pointer :: axes => null()
2851 
2852  call query_vardesc(var_desc, units=units, longname=longname, hor_grid=hor_grid, &
2853  z_grid=z_grid, caller="ocean_register_diag")
2854 
2855  ! Use the hor_grid and z_grid components of vardesc to determine the
2856  ! desired axes to register the diagnostic field for.
2857  select case (z_grid)
2858 
2859  case ("L")
2860  select case (hor_grid)
2861  case ("q")
2862  axes => diag_cs%axesBL
2863  case ("h")
2864  axes => diag_cs%axesTL
2865  case ("u")
2866  axes => diag_cs%axesCuL
2867  case ("v")
2868  axes => diag_cs%axesCvL
2869  case ("Bu")
2870  axes => diag_cs%axesBL
2871  case ("T")
2872  axes => diag_cs%axesTL
2873  case ("Cu")
2874  axes => diag_cs%axesCuL
2875  case ("Cv")
2876  axes => diag_cs%axesCvL
2877  case ("z")
2878  axes => diag_cs%axeszL
2879  case default
2880  call mom_error(fatal, "ocean_register_diag: " // &
2881  "unknown hor_grid component "//trim(hor_grid))
2882  end select
2883 
2884  case ("i")
2885  select case (hor_grid)
2886  case ("q")
2887  axes => diag_cs%axesBi
2888  case ("h")
2889  axes => diag_cs%axesTi
2890  case ("u")
2891  axes => diag_cs%axesCui
2892  case ("v")
2893  axes => diag_cs%axesCvi
2894  case ("Bu")
2895  axes => diag_cs%axesBi
2896  case ("T")
2897  axes => diag_cs%axesTi
2898  case ("Cu")
2899  axes => diag_cs%axesCui
2900  case ("Cv")
2901  axes => diag_cs%axesCvi
2902  case ("z")
2903  axes => diag_cs%axeszi
2904  case default
2905  call mom_error(fatal, "ocean_register_diag: " // &
2906  "unknown hor_grid component "//trim(hor_grid))
2907  end select
2908 
2909  case ("1")
2910  select case (hor_grid)
2911  case ("q")
2912  axes => diag_cs%axesB1
2913  case ("h")
2914  axes => diag_cs%axesT1
2915  case ("u")
2916  axes => diag_cs%axesCu1
2917  case ("v")
2918  axes => diag_cs%axesCv1
2919  case ("Bu")
2920  axes => diag_cs%axesB1
2921  case ("T")
2922  axes => diag_cs%axesT1
2923  case ("Cu")
2924  axes => diag_cs%axesCu1
2925  case ("Cv")
2926  axes => diag_cs%axesCv1
2927  case default
2928  call mom_error(fatal, "ocean_register_diag: " // &
2929  "unknown hor_grid component "//trim(hor_grid))
2930  end select
2931 
2932  case default
2933  call mom_error(fatal,&
2934  "ocean_register_diag: unknown z_grid component "//trim(z_grid))
2935  end select
2936 
2937  ocean_register_diag = register_diag_field("ocean_model", trim(var_name), &
2938  axes, day, trim(longname), trim(units), missing_value=-1.0e+34)
2939 
2940 end function ocean_register_diag
2941 
2942 subroutine diag_mediator_infrastructure_init(err_msg)
2943  ! This subroutine initializes the FMS diag_manager.
2944  character(len=*), optional, intent(out) :: err_msg !< An error message
2945 
2946  call diag_manager_init(err_msg=err_msg)
2947 end subroutine diag_mediator_infrastructure_init
2948 
2949 !> diag_mediator_init initializes the MOM diag_mediator and opens the available
2950 !! diagnostics file, if appropriate.
2951 subroutine diag_mediator_init(G, GV, US, nz, param_file, diag_cs, doc_file_dir)
2952  type(ocean_grid_type), target, intent(inout) :: g !< The ocean grid type.
2953  type(verticalgrid_type), target, intent(in) :: gv !< The ocean vertical grid structure
2954  type(unit_scale_type), target, intent(in) :: us !< A dimensional unit scaling type
2955  integer, intent(in) :: nz !< The number of layers in the model's native grid.
2956  type(param_file_type), intent(in) :: param_file !< Parameter file structure
2957  type(diag_ctrl), intent(inout) :: diag_cs !< A pointer to a type with many variables
2958  !! used for diagnostics
2959  character(len=*), optional, intent(in) :: doc_file_dir !< A directory in which to create the
2960  !! file
2961 
2962  ! This subroutine initializes the diag_mediator and the diag_manager.
2963  ! The grid type should have its dimensions set by this point, but it
2964  ! is not necessary that the metrics and axis labels be set up yet.
2965  integer :: ios, i, new_unit
2966  logical :: opened, new_file
2967  character(len=8) :: this_pe
2968  character(len=240) :: doc_file, doc_file_dflt, doc_path
2969  character(len=240), allocatable :: diag_coords(:)
2970 ! This include declares and sets the variable "version".
2971 #include "version_variable.h"
2972  character(len=40) :: mdl = "MOM_diag_mediator" ! This module's name.
2973  character(len=32) :: filename_appendix = '' !fms appendix to filename for ensemble runs
2974 
2975  id_clock_diag_mediator = cpu_clock_id('(Ocean diagnostics framework)', grain=clock_module)
2976  id_clock_diag_remap = cpu_clock_id('(Ocean diagnostics remapping)', grain=clock_routine)
2977  id_clock_diag_grid_updates = cpu_clock_id('(Ocean diagnostics grid updates)', grain=clock_routine)
2978 
2979  ! Allocate and initialize list of all diagnostics (and variants)
2980  allocate(diag_cs%diags(diag_alloc_chunk_size))
2981  diag_cs%next_free_diag_id = 1
2982  do i=1, diag_alloc_chunk_size
2983  call initialize_diag_type(diag_cs%diags(i))
2984  enddo
2985 
2986  ! Read all relevant parameters and write them to the model log.
2987  call log_version(param_file, mdl, version, "")
2988 
2989  call get_param(param_file, mdl, 'NUM_DIAG_COORDS', diag_cs%num_diag_coords, &
2990  'The number of diagnostic vertical coordinates to use. '//&
2991  'For each coordinate, an entry in DIAG_COORDS must be provided.', &
2992  default=1)
2993  if (diag_cs%num_diag_coords>0) then
2994  allocate(diag_coords(diag_cs%num_diag_coords))
2995  if (diag_cs%num_diag_coords==1) then ! The default is to provide just one instance of Z*
2996  call get_param(param_file, mdl, 'DIAG_COORDS', diag_coords, &
2997  'A list of string tuples associating diag_table modules to '//&
2998  'a coordinate definition used for diagnostics. Each string '//&
2999  'is of the form "MODULE_SUFFIX PARAMETER_SUFFIX COORDINATE_NAME".', &
3000  default='z Z ZSTAR')
3001  else ! If using more than 1 diagnostic coordinate, all must be explicitly defined
3002  call get_param(param_file, mdl, 'DIAG_COORDS', diag_coords, &
3003  'A list of string tuples associating diag_table modules to '//&
3004  'a coordinate definition used for diagnostics. Each string '//&
3005  'is of the form "MODULE_SUFFIX,PARAMETER_SUFFIX,COORDINATE_NAME".', &
3006  fail_if_missing=.true.)
3007  endif
3008  allocate(diag_cs%diag_remap_cs(diag_cs%num_diag_coords))
3009  ! Initialize each diagnostic vertical coordinate
3010  do i=1, diag_cs%num_diag_coords
3011  call diag_remap_init(diag_cs%diag_remap_cs(i), diag_coords(i))
3012  enddo
3013  deallocate(diag_coords)
3014  endif
3015 
3016  call get_param(param_file, mdl, 'DIAG_MISVAL', diag_cs%missing_value, &
3017  'Set the default missing value to use for diagnostics.', &
3018  default=1.e20)
3019  call get_param(param_file, mdl, 'DIAG_AS_CHKSUM', diag_cs%diag_as_chksum, &
3020  'Instead of writing diagnostics to the diag manager, write '//&
3021  'a text file containing the checksum (bitcount) of the array.', &
3022  default=.false.)
3023 
3024  if (diag_cs%diag_as_chksum) &
3025  diag_cs%num_chksum_diags = 0
3026 
3027  ! Keep pointers grid, h, T, S needed diagnostic remapping
3028  diag_cs%G => g
3029  diag_cs%GV => gv
3030  diag_cs%US => us
3031  diag_cs%h => null()
3032  diag_cs%T => null()
3033  diag_cs%S => null()
3034  diag_cs%eqn_of_state => null()
3035 
3036 #if defined(DEBUG) || defined(__DO_SAFETY_CHECKS__)
3037  allocate(diag_cs%h_old(g%isd:g%ied,g%jsd:g%jed,nz))
3038  diag_cs%h_old(:,:,:) = 0.0
3039 #endif
3040 
3041  diag_cs%is = g%isc - (g%isd-1) ; diag_cs%ie = g%iec - (g%isd-1)
3042  diag_cs%js = g%jsc - (g%jsd-1) ; diag_cs%je = g%jec - (g%jsd-1)
3043  diag_cs%isd = g%isd ; diag_cs%ied = g%ied
3044  diag_cs%jsd = g%jsd ; diag_cs%jed = g%jed
3045 
3046  !Downsample indices for dl=2 (should be generalized to arbitrary dl, perhaps via a G array)
3047  diag_cs%dsamp(2)%isc = g%HId2%isc - (g%HId2%isd-1) ; diag_cs%dsamp(2)%iec = g%HId2%iec - (g%HId2%isd-1)
3048  diag_cs%dsamp(2)%jsc = g%HId2%jsc - (g%HId2%jsd-1) ; diag_cs%dsamp(2)%jec = g%HId2%jec - (g%HId2%jsd-1)
3049  diag_cs%dsamp(2)%isd = g%HId2%isd ; diag_cs%dsamp(2)%ied = g%HId2%ied
3050  diag_cs%dsamp(2)%jsd = g%HId2%jsd ; diag_cs%dsamp(2)%jed = g%HId2%jed
3051  diag_cs%dsamp(2)%isg = g%HId2%isg ; diag_cs%dsamp(2)%ieg = g%HId2%ieg
3052  diag_cs%dsamp(2)%jsg = g%HId2%jsg ; diag_cs%dsamp(2)%jeg = g%HId2%jeg
3053  diag_cs%dsamp(2)%isgB = g%HId2%isgB ; diag_cs%dsamp(2)%iegB = g%HId2%iegB
3054  diag_cs%dsamp(2)%jsgB = g%HId2%jsgB ; diag_cs%dsamp(2)%jegB = g%HId2%jegB
3055 
3056  ! Initialze available diagnostic log file
3057  if (is_root_pe() .and. (diag_cs%available_diag_doc_unit < 0)) then
3058  write(this_pe,'(i6.6)') pe_here()
3059  doc_file_dflt = "available_diags."//this_pe
3060  call get_param(param_file, mdl, "AVAILABLE_DIAGS_FILE", doc_file, &
3061  "A file into which to write a list of all available "//&
3062  "ocean diagnostics that can be included in a diag_table.", &
3063  default=doc_file_dflt, do_not_log=(diag_cs%available_diag_doc_unit/=-1))
3064  if (len_trim(doc_file) > 0) then
3065  new_file = .true. ; if (diag_cs%available_diag_doc_unit /= -1) new_file = .false.
3066  ! Find an unused unit number.
3067  do new_unit=512,42,-1
3068  inquire( new_unit, opened=opened)
3069  if (.not.opened) exit
3070  enddo
3071  if (opened) call mom_error(fatal, &
3072  "diag_mediator_init failed to find an unused unit number.")
3073 
3074  doc_path = doc_file
3075  if (present(doc_file_dir)) then ; if (len_trim(doc_file_dir) > 0) then
3076  doc_path = trim(slasher(doc_file_dir))//trim(doc_file)
3077  endif ; endif
3078 
3079  diag_cs%available_diag_doc_unit = new_unit
3080 
3081  if (new_file) then
3082  open(diag_cs%available_diag_doc_unit, file=trim(doc_path), access='SEQUENTIAL', form='FORMATTED', &
3083  action='WRITE', status='REPLACE', iostat=ios)
3084  else ! This file is being reopened, and should be appended.
3085  open(diag_cs%available_diag_doc_unit, file=trim(doc_path), access='SEQUENTIAL', form='FORMATTED', &
3086  action='WRITE', status='OLD', position='APPEND', iostat=ios)
3087  endif
3088  inquire(diag_cs%available_diag_doc_unit, opened=opened)
3089  if ((.not.opened) .or. (ios /= 0)) then
3090  call mom_error(fatal, "Failed to open available diags file "//trim(doc_path)//".")
3091  endif
3092  endif
3093  endif
3094 
3095  if (is_root_pe() .and. (diag_cs%chksum_iounit < 0) .and. diag_cs%diag_as_chksum) then
3096  !write(this_pe,'(i6.6)') PE_here()
3097  !doc_file_dflt = "chksum_diag."//this_pe
3098  doc_file_dflt = "chksum_diag"
3099  call get_param(param_file, mdl, "CHKSUM_DIAG_FILE", doc_file, &
3100  "A file into which to write all checksums of the "//&
3101  "diagnostics listed in the diag_table.", &
3102  default=doc_file_dflt, do_not_log=(diag_cs%chksum_iounit/=-1))
3103 
3104  call get_filename_appendix(filename_appendix)
3105  if (len_trim(filename_appendix) > 0) then
3106  doc_file = trim(doc_file) //'.'//trim(filename_appendix)
3107  endif
3108 #ifdef STATSLABEL
3109  doc_file = trim(doc_file)//"."//trim(adjustl(statslabel))
3110 #endif
3111 
3112  if (len_trim(doc_file) > 0) then
3113  new_file = .true. ; if (diag_cs%chksum_iounit /= -1) new_file = .false.
3114  ! Find an unused unit number.
3115  do new_unit=512,42,-1
3116  inquire( new_unit, opened=opened)
3117  if (.not.opened) exit
3118  enddo
3119  if (opened) call mom_error(fatal, &
3120  "diag_mediator_init failed to find an unused unit number.")
3121 
3122  doc_path = doc_file
3123  if (present(doc_file_dir)) then ; if (len_trim(doc_file_dir) > 0) then
3124  doc_path = trim(slasher(doc_file_dir))//trim(doc_file)
3125  endif ; endif
3126 
3127  diag_cs%chksum_iounit = new_unit
3128 
3129  if (new_file) then
3130  open(diag_cs%chksum_iounit, file=trim(doc_path), access='SEQUENTIAL', form='FORMATTED', &
3131  action='WRITE', status='REPLACE', iostat=ios)
3132  else ! This file is being reopened, and should be appended.
3133  open(diag_cs%chksum_iounit, file=trim(doc_path), access='SEQUENTIAL', form='FORMATTED', &
3134  action='WRITE', status='OLD', position='APPEND', iostat=ios)
3135  endif
3136  inquire(diag_cs%chksum_iounit, opened=opened)
3137  if ((.not.opened) .or. (ios /= 0)) then
3138  call mom_error(fatal, "Failed to open checksum diags file "//trim(doc_path)//".")
3139  endif
3140  endif
3141  endif
3142 
3143 end subroutine diag_mediator_init
3144 
3145 !> Set pointers to the default state fields used to remap diagnostics.
3146 subroutine diag_set_state_ptrs(h, T, S, eqn_of_state, diag_cs)
3147  real, dimension(:,:,:), target, intent(in ) :: h !< the model thickness array
3148  real, dimension(:,:,:), target, intent(in ) :: t !< the model temperature array
3149  real, dimension(:,:,:), target, intent(in ) :: s !< the model salinity array
3150  type(eos_type), target, intent(in ) :: eqn_of_state !< Equation of state structure
3151  type(diag_ctrl), intent(inout) :: diag_cs !< diag mediator control structure
3152 
3153  ! Keep pointers to h, T, S needed for the diagnostic remapping
3154  diag_cs%h => h
3155  diag_cs%T => t
3156  diag_cs%S => s
3157  diag_cs%eqn_of_state => eqn_of_state
3158 
3159 end subroutine
3160 
3161 !> Build/update vertical grids for diagnostic remapping.
3162 !! \note The target grids need to be updated whenever sea surface
3163 !! height changes.
3164 subroutine diag_update_remap_grids(diag_cs, alt_h, alt_T, alt_S)
3165  type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
3166  real, target, optional, intent(in ) :: alt_h(:,:,:) !< Used if remapped grids should be something other than
3167  !! the current thicknesses
3168  real, target, optional, intent(in ) :: alt_t(:,:,:) !< Used if remapped grids should be something other than
3169  !! the current temperatures
3170  real, target, optional, intent(in ) :: alt_s(:,:,:) !< Used if remapped grids should be something other than
3171  !! the current salinity
3172  ! Local variables
3173  integer :: i
3174  real, dimension(:,:,:), pointer :: h_diag => null()
3175  real, dimension(:,:,:), pointer :: t_diag => null(), s_diag => null()
3176 
3177  if (present(alt_h)) then
3178  h_diag => alt_h
3179  else
3180  h_diag => diag_cs%h
3181  endif
3182 
3183  if (present(alt_t)) then
3184  t_diag => alt_t
3185  else
3186  t_diag => diag_cs%T
3187  endif
3188 
3189  if (present(alt_s)) then
3190  s_diag => alt_s
3191  else
3192  s_diag => diag_cs%S
3193  endif
3194 
3195  if (id_clock_diag_grid_updates>0) call cpu_clock_begin(id_clock_diag_grid_updates)
3196 
3197  if (diag_cs%diag_grid_overridden) then
3198  call mom_error(fatal, "diag_update_remap_grids was called, but current grids in "// &
3199  "diagnostic structure have been overridden")
3200  endif
3201 
3202  do i=1, diag_cs%num_diag_coords
3203  call diag_remap_update(diag_cs%diag_remap_cs(i), &
3204  diag_cs%G, diag_cs%GV, diag_cs%US, h_diag, t_diag, s_diag, &
3205  diag_cs%eqn_of_state)
3206  enddo
3207 
3208 #if defined(DEBUG) || defined(__DO_SAFETY_CHECKS__)
3209  ! Keep a copy of H - used to check whether grids are up-to-date
3210  ! when doing remapping.
3211  diag_cs%h_old(:,:,:) = diag_cs%h(:,:,:)
3212 #endif
3213 
3214  if (id_clock_diag_grid_updates>0) call cpu_clock_end(id_clock_diag_grid_updates)
3215 
3216 end subroutine diag_update_remap_grids
3217 
3218 !> Sets up the 2d and 3d masks for native diagnostics
3219 subroutine diag_masks_set(G, nz, diag_cs)
3220  type(ocean_grid_type), target, intent(in) :: g !< The ocean grid type.
3221  integer, intent(in) :: nz !< The number of layers in the model's native grid.
3222  type(diag_ctrl), pointer :: diag_cs !< A pointer to a type with many variables
3223  !! used for diagnostics
3224  ! Local variables
3225  integer :: k
3226 
3227  ! 2d masks point to the model masks since they are identical
3228  diag_cs%mask2dT => g%mask2dT
3229  diag_cs%mask2dBu => g%mask2dBu
3230  diag_cs%mask2dCu => g%mask2dCu
3231  diag_cs%mask2dCv => g%mask2dCv
3232 
3233  ! 3d native masks are needed by diag_manager but the native variables
3234  ! can only be masked 2d - for ocean points, all layers exists.
3235  allocate(diag_cs%mask3dTL(g%isd:g%ied,g%jsd:g%jed,1:nz))
3236  allocate(diag_cs%mask3dBL(g%IsdB:g%IedB,g%JsdB:g%JedB,1:nz))
3237  allocate(diag_cs%mask3dCuL(g%IsdB:g%IedB,g%jsd:g%jed,1:nz))
3238  allocate(diag_cs%mask3dCvL(g%isd:g%ied,g%JsdB:g%JedB,1:nz))
3239  do k=1,nz
3240  diag_cs%mask3dTL(:,:,k) = diag_cs%mask2dT(:,:)
3241  diag_cs%mask3dBL(:,:,k) = diag_cs%mask2dBu(:,:)
3242  diag_cs%mask3dCuL(:,:,k) = diag_cs%mask2dCu(:,:)
3243  diag_cs%mask3dCvL(:,:,k) = diag_cs%mask2dCv(:,:)
3244  enddo
3245  allocate(diag_cs%mask3dTi(g%isd:g%ied,g%jsd:g%jed,1:nz+1))
3246  allocate(diag_cs%mask3dBi(g%IsdB:g%IedB,g%JsdB:g%JedB,1:nz+1))
3247  allocate(diag_cs%mask3dCui(g%IsdB:g%IedB,g%jsd:g%jed,1:nz+1))
3248  allocate(diag_cs%mask3dCvi(g%isd:g%ied,g%JsdB:g%JedB,1:nz+1))
3249  do k=1,nz+1
3250  diag_cs%mask3dTi(:,:,k) = diag_cs%mask2dT(:,:)
3251  diag_cs%mask3dBi(:,:,k) = diag_cs%mask2dBu(:,:)
3252  diag_cs%mask3dCui(:,:,k) = diag_cs%mask2dCu(:,:)
3253  diag_cs%mask3dCvi(:,:,k) = diag_cs%mask2dCv(:,:)
3254  enddo
3255 
3256  !Allocate and initialize the downsampled masks
3257  call downsample_diag_masks_set(g, nz, diag_cs)
3258 
3259 end subroutine diag_masks_set
3260 
3261 subroutine diag_mediator_close_registration(diag_CS)
3262  type(diag_ctrl), intent(inout) :: diag_cs !< Structure used to regulate diagnostic output
3263 
3264  integer :: i
3265 
3266  if (diag_cs%available_diag_doc_unit > -1) then
3267  close(diag_cs%available_diag_doc_unit) ; diag_cs%available_diag_doc_unit = -2
3268  endif
3269 
3270  do i=1, diag_cs%num_diag_coords
3271  call diag_remap_diag_registration_closed(diag_cs%diag_remap_cs(i))
3272  enddo
3273 
3274 end subroutine diag_mediator_close_registration
3275 
3276 subroutine diag_mediator_end(time, diag_CS, end_diag_manager)
3277  type(time_type), intent(in) :: time !< The current model time
3278  type(diag_ctrl), intent(inout) :: diag_cs !< Structure used to regulate diagnostic output
3279  logical, optional, intent(in) :: end_diag_manager !< If true, call diag_manager_end()
3280 
3281  ! Local variables
3282  integer :: i
3283 
3284  if (diag_cs%available_diag_doc_unit > -1) then
3285  close(diag_cs%available_diag_doc_unit) ; diag_cs%available_diag_doc_unit = -3
3286  endif
3287  if (diag_cs%chksum_iounit > -1) then
3288  close(diag_cs%chksum_iounit) ; diag_cs%chksum_iounit = -3
3289  endif
3290 
3291  deallocate(diag_cs%diags)
3292 
3293  do i=1, diag_cs%num_diag_coords
3294  call diag_remap_end(diag_cs%diag_remap_cs(i))
3295  enddo
3296 
3297  call diag_grid_storage_end(diag_cs%diag_grid_temp)
3298  deallocate(diag_cs%mask3dTL)
3299  deallocate(diag_cs%mask3dBL)
3300  deallocate(diag_cs%mask3dCuL)
3301  deallocate(diag_cs%mask3dCvL)
3302  deallocate(diag_cs%mask3dTi)
3303  deallocate(diag_cs%mask3dBi)
3304  deallocate(diag_cs%mask3dCui)
3305  deallocate(diag_cs%mask3dCvi)
3306  do i=2,max_dsamp_lev
3307  deallocate(diag_cs%dsamp(i)%mask2dT)
3308  deallocate(diag_cs%dsamp(i)%mask2dBu)
3309  deallocate(diag_cs%dsamp(i)%mask2dCu)
3310  deallocate(diag_cs%dsamp(i)%mask2dCv)
3311  deallocate(diag_cs%dsamp(i)%mask3dTL)
3312  deallocate(diag_cs%dsamp(i)%mask3dBL)
3313  deallocate(diag_cs%dsamp(i)%mask3dCuL)
3314  deallocate(diag_cs%dsamp(i)%mask3dCvL)
3315  deallocate(diag_cs%dsamp(i)%mask3dTi)
3316  deallocate(diag_cs%dsamp(i)%mask3dBi)
3317  deallocate(diag_cs%dsamp(i)%mask3dCui)
3318  deallocate(diag_cs%dsamp(i)%mask3dCvi)
3319  enddo
3320 
3321 #if defined(DEBUG) || defined(__DO_SAFETY_CHECKS__)
3322  deallocate(diag_cs%h_old)
3323 #endif
3324 
3325  if (present(end_diag_manager)) then
3326  if (end_diag_manager) call diag_manager_end(time)
3327  endif
3328 
3329 end subroutine diag_mediator_end
3330 
3331 !> Convert the first n elements (up to 3) of an integer array to an underscore delimited string.
3332 function i2s(a,n_in)
3333  ! "Convert the first n elements of an integer array to a string."
3334  ! Perhaps this belongs elsewhere in the MOM6 code?
3335  integer, dimension(:), intent(in) :: a !< The array of integers to translate
3336  integer, optional , intent(in) :: n_in !< The number of elements to translate, by default all
3337  character(len=15) :: i2s !< The returned string
3338 
3339  character(len=15) :: i2s_temp
3340  integer :: i,n
3341 
3342  n=size(a)
3343  if (present(n_in)) n = n_in
3344 
3345  i2s = ''
3346  do i=1,min(n,3)
3347  write (i2s_temp, '(I4.4)') a(i)
3348  i2s = trim(i2s) //'_'// trim(i2s_temp)
3349  enddo
3350  i2s = adjustl(i2s)
3351 end function i2s
3352 
3353 !> Returns a new diagnostic id, it may be necessary to expand the diagnostics array.
3354 integer function get_new_diag_id(diag_cs)
3355  type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
3356  ! Local variables
3357  type(diag_type), dimension(:), allocatable :: tmp
3358  integer :: i
3359 
3360  if (diag_cs%next_free_diag_id > size(diag_cs%diags)) then
3361  call assert(diag_cs%next_free_diag_id - size(diag_cs%diags) == 1, &
3362  'get_new_diag_id: inconsistent diag id')
3363 
3364  ! Increase the size of diag_cs%diags and copy data over.
3365  ! Do not use move_alloc() because it is not supported by Fortran 90
3366  allocate(tmp(size(diag_cs%diags)))
3367  tmp(:) = diag_cs%diags(:)
3368  deallocate(diag_cs%diags)
3369  allocate(diag_cs%diags(size(tmp) + diag_alloc_chunk_size))
3370  diag_cs%diags(1:size(tmp)) = tmp(:)
3371  deallocate(tmp)
3372 
3373  ! Initialize new part of the diag array.
3374  do i=diag_cs%next_free_diag_id, size(diag_cs%diags)
3375  call initialize_diag_type(diag_cs%diags(i))
3376  enddo
3377  endif
3378 
3379  get_new_diag_id = diag_cs%next_free_diag_id
3380  diag_cs%next_free_diag_id = diag_cs%next_free_diag_id + 1
3381 
3382 end function get_new_diag_id
3383 
3384 !> Initializes a diag_type (used after allocating new memory)
3385 subroutine initialize_diag_type(diag)
3386  type(diag_type), intent(inout) :: diag !< diag_type to be initialized
3387 
3388  diag%in_use = .false.
3389  diag%fms_diag_id = -1
3390  diag%axes => null()
3391  diag%next => null()
3392  diag%conversion_factor = 0.
3393 
3394 end subroutine initialize_diag_type
3395 
3396 !> Make a new diagnostic. Either use memory which is in the array of 'primary'
3397 !! diagnostics, or if that is in use, insert it to the list of secondary diags.
3398 subroutine alloc_diag_with_id(diag_id, diag_cs, diag)
3399  integer, intent(in ) :: diag_id !< id for the diagnostic
3400  type(diag_ctrl), target, intent(inout) :: diag_cs !< structure used to regulate diagnostic output
3401  type(diag_type), pointer :: diag !< structure representing a diagnostic (inout)
3402 
3403  type(diag_type), pointer :: tmp => null()
3404 
3405  if (.not. diag_cs%diags(diag_id)%in_use) then
3406  diag => diag_cs%diags(diag_id)
3407  else
3408  allocate(diag)
3409  tmp => diag_cs%diags(diag_id)%next
3410  diag_cs%diags(diag_id)%next => diag
3411  diag%next => tmp
3412  endif
3413  diag%in_use = .true.
3414 
3415 end subroutine alloc_diag_with_id
3416 
3417 !> Log a diagnostic to the available diagnostics file.
3418 subroutine log_available_diag(used, module_name, field_name, cell_methods_string, comment, &
3419  diag_CS, long_name, units, standard_name)
3420  logical, intent(in) :: used !< Whether this diagnostic was in the diag_table or not
3421  character(len=*), intent(in) :: module_name !< Name of the diagnostic module
3422  character(len=*), intent(in) :: field_name !< Name of this diagnostic field
3423  character(len=*), intent(in) :: cell_methods_string !< The spatial component of the CF cell_methods attribute
3424  character(len=*), intent(in) :: comment !< A comment to append after [Used|Unused]
3425  type(diag_ctrl), intent(in) :: diag_CS !< The diagnotics control structure
3426  character(len=*), optional, intent(in) :: long_name !< CF long name of diagnostic
3427  character(len=*), optional, intent(in) :: units !< Units for diagnostic
3428  character(len=*), optional, intent(in) :: standard_name !< CF standardized name of diagnostic
3429  ! Local variables
3430  character(len=240) :: mesg
3431 
3432  if (used) then
3433  mesg = '"'//trim(module_name)//'", "'//trim(field_name)//'" [Used]'
3434  else
3435  mesg = '"'//trim(module_name)//'", "'//trim(field_name)//'" [Unused]'
3436  endif
3437  if (len(trim((comment)))>0) then
3438  write(diag_cs%available_diag_doc_unit, '(a,x,"(",a,")")') trim(mesg),trim(comment)
3439  else
3440  write(diag_cs%available_diag_doc_unit, '(a)') trim(mesg)
3441  endif
3442  if (present(long_name)) call describe_option("long_name", long_name, diag_cs)
3443  if (present(units)) call describe_option("units", units, diag_cs)
3444  if (present(standard_name)) &
3445  call describe_option("standard_name", standard_name, diag_cs)
3446  if (len(trim((cell_methods_string)))>0) &
3447  call describe_option("cell_methods", trim(cell_methods_string), diag_cs)
3448 
3449 end subroutine log_available_diag
3450 
3451 !> Log the diagnostic chksum to the chksum diag file
3452 subroutine log_chksum_diag(docunit, description, chksum)
3453  integer, intent(in) :: docunit !< Handle of the log file
3454  character(len=*), intent(in) :: description !< Name of the diagnostic module
3455  integer, intent(in) :: chksum !< chksum of the diagnostic
3456 
3457  write(docunit, '(a,x,i9.8)') description, chksum
3458  flush(docunit)
3459 
3460 end subroutine log_chksum_diag
3461 
3462 !> Allocates fields necessary to store diagnostic remapping fields
3463 subroutine diag_grid_storage_init(grid_storage, G, diag)
3464  type(diag_grid_storage), intent(inout) :: grid_storage !< Structure containing a snapshot of the target grids
3465  type(ocean_grid_type), intent(in) :: g !< Horizontal grid
3466  type(diag_ctrl), intent(in) :: diag !< Diagnostic control structure used as the contructor
3467  !! template for this routine
3468 
3469  integer :: m, nz
3470  grid_storage%num_diag_coords = diag%num_diag_coords
3471 
3472  ! Don't do anything else if there are no remapped coordinates
3473  if (grid_storage%num_diag_coords < 1) return
3474 
3475  ! Allocate memory for the native space
3476  allocate(grid_storage%h_state(g%isd:g%ied,g%jsd:g%jed, g%ke))
3477  ! Allocate diagnostic remapping structures
3478  allocate(grid_storage%diag_grids(diag%num_diag_coords))
3479  ! Loop through and allocate memory for the grid on each target coordinate
3480  do m = 1, diag%num_diag_coords
3481  nz = diag%diag_remap_cs(m)%nz
3482  allocate(grid_storage%diag_grids(m)%h(g%isd:g%ied,g%jsd:g%jed, nz))
3483  enddo
3484 
3485 end subroutine diag_grid_storage_init
3486 
3487 !> Copy from the main diagnostic arrays to the grid storage as well as the native thicknesses
3488 subroutine diag_copy_diag_to_storage(grid_storage, h_state, diag)
3489  type(diag_grid_storage), intent(inout) :: grid_storage !< Structure containing a snapshot of the target grids
3490  real, dimension(:,:,:), intent(in) :: h_state !< Current model thicknesses
3491  type(diag_ctrl), intent(in) :: diag !< Diagnostic control structure used as the contructor
3492 
3493  integer :: m
3494 
3495  ! Don't do anything else if there are no remapped coordinates
3496  if (grid_storage%num_diag_coords < 1) return
3497 
3498  grid_storage%h_state(:,:,:) = h_state(:,:,:)
3499  do m = 1,grid_storage%num_diag_coords
3500  if (diag%diag_remap_cs(m)%nz > 0) &
3501  grid_storage%diag_grids(m)%h(:,:,:) = diag%diag_remap_cs(m)%h(:,:,:)
3502  enddo
3503 
3504 end subroutine diag_copy_diag_to_storage
3505 
3506 !> Copy from the stored diagnostic arrays to the main diagnostic grids
3507 subroutine diag_copy_storage_to_diag(diag, grid_storage)
3508  type(diag_ctrl), intent(inout) :: diag !< Diagnostic control structure used as the contructor
3509  type(diag_grid_storage), intent(in) :: grid_storage !< Structure containing a snapshot of the target grids
3510 
3511  integer :: m
3512 
3513  ! Don't do anything else if there are no remapped coordinates
3514  if (grid_storage%num_diag_coords < 1) return
3515 
3516  diag%diag_grid_overridden = .true.
3517  do m = 1,grid_storage%num_diag_coords
3518  if (diag%diag_remap_cs(m)%nz > 0) &
3519  diag%diag_remap_cs(m)%h(:,:,:) = grid_storage%diag_grids(m)%h(:,:,:)
3520  enddo
3521 
3522 end subroutine diag_copy_storage_to_diag
3523 
3524 !> Save the current diagnostic grids in the temporary structure within diag
3525 subroutine diag_save_grids(diag)
3526  type(diag_ctrl), intent(inout) :: diag !< Diagnostic control structure used as the contructor
3527 
3528  integer :: m
3529 
3530  ! Don't do anything else if there are no remapped coordinates
3531  if (diag%num_diag_coords < 1) return
3532 
3533  do m = 1,diag%num_diag_coords
3534  if (diag%diag_remap_cs(m)%nz > 0) &
3535  diag%diag_grid_temp%diag_grids(m)%h(:,:,:) = diag%diag_remap_cs(m)%h(:,:,:)
3536  enddo
3537 
3538 end subroutine diag_save_grids
3539 
3540 !> Restore the diagnostic grids from the temporary structure within diag
3541 subroutine diag_restore_grids(diag)
3542  type(diag_ctrl), intent(inout) :: diag !< Diagnostic control structure used as the contructor
3543 
3544  integer :: m
3545 
3546  ! Don't do anything else if there are no remapped coordinates
3547  if (diag%num_diag_coords < 1) return
3548 
3549  diag%diag_grid_overridden = .false.
3550  do m = 1,diag%num_diag_coords
3551  if (diag%diag_remap_cs(m)%nz > 0) &
3552  diag%diag_remap_cs(m)%h(:,:,:) = diag%diag_grid_temp%diag_grids(m)%h(:,:,:)
3553  enddo
3554 
3555 end subroutine diag_restore_grids
3556 
3557 !> Deallocates the fields in the remapping fields container
3558 subroutine diag_grid_storage_end(grid_storage)
3559  type(diag_grid_storage), intent(inout) :: grid_storage !< Structure containing a snapshot of the target grids
3560  ! Local variables
3561  integer :: m, nz
3562 
3563  ! Don't do anything else if there are no remapped coordinates
3564  if (grid_storage%num_diag_coords < 1) return
3565 
3566  ! Deallocate memory for the native space
3567  deallocate(grid_storage%h_state)
3568  ! Loop through and deallocate memory for the grid on each target coordinate
3569  do m = 1, grid_storage%num_diag_coords
3570  deallocate(grid_storage%diag_grids(m)%h)
3571  enddo
3572  ! Deallocate diagnostic remapping structures
3573  deallocate(grid_storage%diag_grids)
3574 end subroutine diag_grid_storage_end
3575 
3576 !< Allocate and initialize the masks for downsampled diagostics in diag_cs
3577 !! The downsampled masks in the axes would later "point" to these.
3578 subroutine downsample_diag_masks_set(G, nz, diag_cs)
3579  type(ocean_grid_type), target, intent(in) :: G !< The ocean grid type.
3580  integer, intent(in) :: nz !< The number of layers in the model's native grid.
3581  type(diag_ctrl), pointer :: diag_cs !< A pointer to a type with many variables
3582  !! used for diagnostics
3583  ! Local variables
3584  integer :: i,j,k,ii,jj,dl
3585 
3586 !print*,'original c extents ',G%isc,G%iec,G%jsc,G%jec
3587 !print*,'original c extents ',G%iscb,G%iecb,G%jscb,G%jecb
3588 !print*,'coarse c extents ',G%HId2%isc,G%HId2%iec,G%HId2%jsc,G%HId2%jec
3589 !print*,'original d extents ',G%isd,G%ied,G%jsd,G%jed
3590 !print*,'coarse d extents ',G%HId2%isd,G%HId2%ied,G%HId2%jsd,G%HId2%jed
3591 ! original c extents 5 52 5 52
3592 ! original cB-nonsym extents 5 52 5 52
3593 ! original cB-sym extents 4 52 4 52
3594 ! coarse c extents 3 26 3 26
3595 ! original d extents 1 56 1 56
3596 ! original dB-nonsym extents 1 56 1 56
3597 ! original dB-sym extents 0 56 0 56
3598 ! coarse d extents 1 28 1 28
3599 
3600  do dl=2,max_dsamp_lev
3601  ! 2d mask
3602  call downsample_mask(g%mask2dT, diag_cs%dsamp(dl)%mask2dT, dl,g%isc, g%jsc, &
3603  g%HId2%isc, g%HId2%iec, g%HId2%jsc, g%HId2%jec, g%HId2%isd, g%HId2%ied, g%HId2%jsd, g%HId2%jed)
3604  call downsample_mask(g%mask2dBu,diag_cs%dsamp(dl)%mask2dBu, dl,g%IscB,g%JscB, &
3605  g%HId2%IscB,g%HId2%IecB,g%HId2%JscB,g%HId2%JecB,g%HId2%IsdB,g%HId2%IedB,g%HId2%JsdB,g%HId2%JedB)
3606  call downsample_mask(g%mask2dCu,diag_cs%dsamp(dl)%mask2dCu, dl,g%IscB,g%JscB, &
3607  g%HId2%IscB,g%HId2%IecB,g%HId2%jsc, g%HId2%jec,g%HId2%IsdB,g%HId2%IedB,g%HId2%jsd, g%HId2%jed)
3608  call downsample_mask(g%mask2dCv,diag_cs%dsamp(dl)%mask2dCv, dl,g%isc ,g%JscB, &
3609  g%HId2%isc ,g%HId2%iec, g%HId2%JscB,g%HId2%JecB,g%HId2%isd ,g%HId2%ied, g%HId2%JsdB,g%HId2%JedB)
3610  ! 3d native masks are needed by diag_manager but the native variables
3611  ! can only be masked 2d - for ocean points, all layers exists.
3612  allocate(diag_cs%dsamp(dl)%mask3dTL(g%HId2%isd:g%HId2%ied,g%HId2%jsd:g%HId2%jed,1:nz))
3613  allocate(diag_cs%dsamp(dl)%mask3dBL(g%HId2%IsdB:g%HId2%IedB,g%HId2%JsdB:g%HId2%JedB,1:nz))
3614  allocate(diag_cs%dsamp(dl)%mask3dCuL(g%HId2%IsdB:g%HId2%IedB,g%HId2%jsd:g%HId2%jed,1:nz))
3615  allocate(diag_cs%dsamp(dl)%mask3dCvL(g%HId2%isd:g%HId2%ied,g%HId2%JsdB:g%HId2%JedB,1:nz))
3616  do k=1,nz
3617  diag_cs%dsamp(dl)%mask3dTL(:,:,k) = diag_cs%dsamp(dl)%mask2dT(:,:)
3618  diag_cs%dsamp(dl)%mask3dBL(:,:,k) = diag_cs%dsamp(dl)%mask2dBu(:,:)
3619  diag_cs%dsamp(dl)%mask3dCuL(:,:,k) = diag_cs%dsamp(dl)%mask2dCu(:,:)
3620  diag_cs%dsamp(dl)%mask3dCvL(:,:,k) = diag_cs%dsamp(dl)%mask2dCv(:,:)
3621  enddo
3622  allocate(diag_cs%dsamp(dl)%mask3dTi(g%HId2%isd:g%HId2%ied,g%HId2%jsd:g%HId2%jed,1:nz+1))
3623  allocate(diag_cs%dsamp(dl)%mask3dBi(g%HId2%IsdB:g%HId2%IedB,g%HId2%JsdB:g%HId2%JedB,1:nz+1))
3624  allocate(diag_cs%dsamp(dl)%mask3dCui(g%HId2%IsdB:g%HId2%IedB,g%HId2%jsd:g%HId2%jed,1:nz+1))
3625  allocate(diag_cs%dsamp(dl)%mask3dCvi(g%HId2%isd:g%HId2%ied,g%HId2%JsdB:g%HId2%JedB,1:nz+1))
3626  do k=1,nz+1
3627  diag_cs%dsamp(dl)%mask3dTi(:,:,k) = diag_cs%dsamp(dl)%mask2dT(:,:)
3628  diag_cs%dsamp(dl)%mask3dBi(:,:,k) = diag_cs%dsamp(dl)%mask2dBu(:,:)
3629  diag_cs%dsamp(dl)%mask3dCui(:,:,k) = diag_cs%dsamp(dl)%mask2dCu(:,:)
3630  diag_cs%dsamp(dl)%mask3dCvi(:,:,k) = diag_cs%dsamp(dl)%mask2dCv(:,:)
3631  enddo
3632  enddo
3633 end subroutine downsample_diag_masks_set
3634 
3635 !> Get the diagnostics-compute indices (to be passed to send_data) based on the shape of
3636 !! the diag field (the same way they are deduced for non-downsampled fields)
3637 subroutine downsample_diag_indices_get(fo1, fo2, dl, diag_cs, isv, iev, jsv, jev)
3638  integer, intent(in) :: fo1 !< The size of the diag field in x
3639  integer, intent(in) :: fo2 !< The size of the diag field in y
3640  integer, intent(in) :: dl !< Integer downsample level
3641  type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
3642  integer, intent(out) :: isv !< i-start index for diagnostics
3643  integer, intent(out) :: iev !< i-end index for diagnostics
3644  integer, intent(out) :: jsv !< j-start index for diagnostics
3645  integer, intent(out) :: jev !< j-end index for diagnostics
3646  ! Local variables
3647  integer :: dszi,cszi,dszj,cszj,f1,f2
3648  character(len=500) :: mesg
3649  logical, save :: first_check = .true.
3650 
3651  !Check ONCE that the downsampled diag-compute domain is commensurate with the original
3652  !non-downsampled diag-compute domain.
3653  !This is a major limitation of the current implementation of the downsampled diagnostics.
3654  !We assume that the compute domain can be subdivided to dl*dl cells, hence avoiding the need of halo updates.
3655  !We want this check to error out only if there was a downsampled diagnostics requested and about to post that is
3656  !why the check is here and not in the init routines. This check need to be done only once, hence the outer if.
3657  if(first_check) then
3658  if(mod(diag_cs%ie-diag_cs%is+1, dl) .ne. 0 .OR. mod(diag_cs%je-diag_cs%js+1, dl) .ne. 0) then
3659  write (mesg,*) "Non-commensurate downsampled domain is not supported. "//&
3660  "Please choose a layout such that NIGLOBAL/Layout_X and NJGLOBAL/Layout_Y are both divisible by dl=",dl,&
3661  " Current domain extents: ", diag_cs%is,diag_cs%ie, diag_cs%js,diag_cs%je
3662  call mom_error(fatal,"downsample_diag_indices_get: "//trim(mesg))
3663  endif
3664  first_check = .false.
3665  endif
3666 
3667  cszi = diag_cs%dsamp(dl)%iec-diag_cs%dsamp(dl)%isc +1 ; dszi = diag_cs%dsamp(dl)%ied-diag_cs%dsamp(dl)%isd +1
3668  cszj = diag_cs%dsamp(dl)%jec-diag_cs%dsamp(dl)%jsc +1 ; dszj = diag_cs%dsamp(dl)%jed-diag_cs%dsamp(dl)%jsd +1
3669  isv = diag_cs%dsamp(dl)%isc ; iev = diag_cs%dsamp(dl)%iec
3670  jsv = diag_cs%dsamp(dl)%jsc ; jev = diag_cs%dsamp(dl)%jec
3671  f1 = fo1/dl
3672  f2 = fo2/dl
3673  !Correction for the symmetric case
3674  if (diag_cs%G%symmetric) then
3675  f1 = f1 + mod(fo1,dl)
3676  f2 = f2 + mod(fo2,dl)
3677  endif
3678  if ( f1 == dszi ) then
3679  isv = diag_cs%dsamp(dl)%isc ; iev = diag_cs%dsamp(dl)%iec ! field on Data domain, take compute domain indcies
3680  !The rest is not taken with the full MOM6 diag_table
3681  elseif ( f1 == dszi + 1 ) then
3682  isv = diag_cs%dsamp(dl)%isc ; iev = diag_cs%dsamp(dl)%iec+1 ! Symmetric data domain
3683  elseif ( f1 == cszi) then
3684  isv = 1 ; iev = (diag_cs%dsamp(dl)%iec-diag_cs%dsamp(dl)%isc) +1 ! Computational domain
3685  elseif ( f1 == cszi + 1 ) then
3686  isv = 1 ; iev = (diag_cs%dsamp(dl)%iec-diag_cs%dsamp(dl)%isc) +2 ! Symmetric computational domain
3687  else
3688  write (mesg,*) " peculiar size ",f1," in i-direction\n"//&
3689  "does not match one of ", cszi, cszi+1, dszi, dszi+1
3690  call mom_error(fatal,"downsample_diag_indices_get: "//trim(mesg))
3691  endif
3692  if ( f2 == dszj ) then
3693  jsv = diag_cs%dsamp(dl)%jsc ; jev = diag_cs%dsamp(dl)%jec ! Data domain
3694  elseif ( f2 == dszj + 1 ) then
3695  jsv = diag_cs%dsamp(dl)%jsc ; jev = diag_cs%dsamp(dl)%jec+1 ! Symmetric data domain
3696  elseif ( f2 == cszj) then
3697  jsv = 1 ; jev = (diag_cs%dsamp(dl)%jec-diag_cs%dsamp(dl)%jsc) +1 ! Computational domain
3698  elseif ( f2 == cszj + 1 ) then
3699  jsv = 1 ; jev = (diag_cs%dsamp(dl)%jec-diag_cs%dsamp(dl)%jsc) +2 ! Symmetric computational domain
3700  else
3701  write (mesg,*) " peculiar size ",f2," in j-direction\n"//&
3702  "does not match one of ", cszj, cszj+1, dszj, dszj+1
3703  call mom_error(fatal,"downsample_diag_indices_get: "//trim(mesg))
3704  endif
3705 end subroutine downsample_diag_indices_get
3706 
3707 !> This subroutine allocates and computes a downsampled array from an input array
3708 !! It also determines the diagnostics-compurte indices for the downsampled array
3709 !! 3d interface
3710 subroutine downsample_diag_field_3d(locfield, locfield_dsamp, dl, diag_cs, diag, isv, iev, jsv, jev, mask)
3711  real, dimension(:,:,:), pointer :: locfield !< Input array pointer
3712  real, dimension(:,:,:), allocatable, intent(inout) :: locfield_dsamp !< Output (downsampled) array
3713  type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
3714  type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
3715  integer, intent(in) :: dl !< Level of down sampling
3716  integer, intent(inout) :: isv !< i-start index for diagnostics
3717  integer, intent(inout) :: iev !< i-end index for diagnostics
3718  integer, intent(inout) :: jsv !< j-start index for diagnostics
3719  integer, intent(inout) :: jev !< j-end index for diagnostics
3720  real, optional,target, intent(in) :: mask(:,:,:) !< If present, use this real array as the data mask.
3721  ! Locals
3722  real, dimension(:,:,:), pointer :: locmask
3723  integer :: f1,f2,isv_o,jsv_o
3724 
3725  locmask => null()
3726  !Get the correct indices corresponding to input field
3727  !Shape of the input diag field
3728  f1=size(locfield,1)
3729  f2=size(locfield,2)
3730  !Save the extents of the original (fine) domain
3731  isv_o=isv;jsv_o=jsv
3732  !Get the shape of the downsampled field and overwrite isv,iev,jsv,jev with them
3733  call downsample_diag_indices_get(f1,f2, dl, diag_cs,isv,iev,jsv,jev)
3734  !Set the non-downsampled mask, it must be associated and initialized
3735  if (present(mask)) then
3736  locmask => mask
3737  elseif (associated(diag%axes%mask3d)) then
3738  locmask => diag%axes%mask3d
3739  else
3740  call mom_error(fatal, "downsample_diag_field_3d: Cannot downsample without a mask!!! ")
3741  endif
3742 
3743  call downsample_field(locfield, locfield_dsamp, dl, diag%xyz_method, locmask, diag_cs, diag, &
3744  isv_o,jsv_o,isv,iev,jsv,jev)
3745 
3746 end subroutine downsample_diag_field_3d
3747 
3748 !> This subroutine allocates and computes a downsampled array from an input array
3749 !! It also determines the diagnostics-compurte indices for the downsampled array
3750 !! 2d interface
3751 subroutine downsample_diag_field_2d(locfield, locfield_dsamp, dl, diag_cs, diag, isv, iev, jsv, jev, mask)
3752  real, dimension(:,:), pointer :: locfield !< Input array pointer
3753  real, dimension(:,:), allocatable, intent(inout) :: locfield_dsamp !< Output (downsampled) array
3754  type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
3755  type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
3756  integer, intent(in) :: dl !< Level of down sampling
3757  integer, intent(inout) :: isv !< i-start index for diagnostics
3758  integer, intent(inout) :: iev !< i-end index for diagnostics
3759  integer, intent(inout) :: jsv !< j-start index for diagnostics
3760  integer, intent(inout) :: jev !< j-end index for diagnostics
3761  real, optional,target, intent(in) :: mask(:,:) !< If present, use this real array as the data mask.
3762  ! Locals
3763  real, dimension(:,:), pointer :: locmask
3764  integer :: f1,f2,isv_o,jsv_o
3765 
3766  locmask => null()
3767  !Get the correct indices corresponding to input field
3768  !Shape of the input diag field
3769  f1=size(locfield,1)
3770  f2=size(locfield,2)
3771  !Save the extents of the original (fine) domain
3772  isv_o=isv;jsv_o=jsv
3773  !Get the shape of the downsampled field and overwrite isv,iev,jsv,jev with them
3774  call downsample_diag_indices_get(f1,f2, dl, diag_cs,isv,iev,jsv,jev)
3775  !Set the non-downsampled mask, it must be associated and initialized
3776  if (present(mask)) then
3777  locmask => mask
3778  elseif (associated(diag%axes%mask2d)) then
3779  locmask => diag%axes%mask2d
3780  else
3781  call mom_error(fatal, "downsample_diag_field_2d: Cannot downsample without a mask!!! ")
3782  endif
3783 
3784  call downsample_field(locfield, locfield_dsamp, dl, diag%xyz_method, locmask, diag_cs,diag, &
3785  isv_o,jsv_o,isv,iev,jsv,jev)
3786 
3787 end subroutine downsample_diag_field_2d
3788 
3789 !> \section downsampling The down sample algorithm
3790 !!
3791 !! The down sample method could be deduced (before send_data call)
3792 !! from the diag%x_cell_method, diag%y_cell_method and diag%v_cell_method
3793 !!
3794 !! This is the summary of the down sample algoritm for a diagnostic field f:
3795 !! \f[
3796 !! f(Id,Jd) = \sum_{i,j} f(Id+i,Jd+j) * weight(Id+i,Jd+j) / [ \sum_{i,j} weight(Id+i,Jd+j)]
3797 !! \f]
3798 !! Here, i and j run from 0 to dl-1 (dl being the down sample level).
3799 !! Id,Jd are the down sampled (coarse grid) indices run over the coarsened compute grid,
3800 !! if and jf are the original (fine grid) indices.
3801 !!
3802 !! \verbatim
3803 !! Example x_cell y_cell v_cell algorithm_id implemented weight(if,jf)
3804 !! ---------------------------------------------------------------------------------------
3805 !! theta mean mean mean MMM =222 G%areaT(if,jf)*h(if,jf)
3806 !! u point mean mean PMM =022 dyCu(if,jf)*h(if,jf)*delta(if,Id)
3807 !! v mean point mean MPM =202 dxCv(if,jf)*h(if,jf)*delta(jf,Jd)
3808 !! ? point sum mean PSM =012 h(if,jf)*delta(if,Id)
3809 !! volcello sum sum sum SSS =111 1
3810 !! T_dfxy_co sum sum point SSP =110 1
3811 !! umo point sum sum PSS =011 1*delta(if,Id)
3812 !! vmo sum point sum SPS =101 1*delta(jf,Jd)
3813 !! umo_2d point sum point PSP =010 1*delta(if,Id)
3814 !! vmo_2d sum point point SPP =100 1*delta(jf,Jd)
3815 !! ? point mean point PMP =020 dyCu(if,jf)*delta(if,Id)
3816 !! ? mean point point MPP =200 dxCv(if,jf)*delta(jf,Jd)
3817 !! w mean mean point MMP =220 G%areaT(if,jf)
3818 !! h*theta mean mean sum MMS =221 G%areaT(if,jf)
3819 !!
3820 !! delta is the Kronecker delta
3821 !! \endverbatim
3822 
3823 !> This subroutine allocates and computes a down sampled 3d array given an input array
3824 !! The down sample method is based on the "cell_methods" for the diagnostics as explained
3825 !! in the above table
3826 subroutine downsample_field_3d(field_in, field_out, dl, method, mask, diag_cs, diag,isv_o,jsv_o,isv_d,iev_d,jsv_d,jev_d)
3827  real, dimension(:,:,:), pointer :: field_in !< Original field to be down sampled
3828  real, dimension(:,:,:), allocatable :: field_out !< down sampled field
3829  integer, intent(in) :: dl !< Level of down sampling
3830  integer, intent(in) :: method !< Sampling method
3831  real, dimension(:,:,:), pointer :: mask !< Mask for field
3832  type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
3833  type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
3834  integer, intent(in) :: isv_o !< Original i-start index
3835  integer, intent(in) :: jsv_o !< Original j-start index
3836  integer, intent(in) :: isv_d !< i-start index of down sampled data
3837  integer, intent(in) :: iev_d !< i-end index of down sampled data
3838  integer, intent(in) :: jsv_d !< j-start index of down sampled data
3839  integer, intent(in) :: jev_d !< j-end index of down sampled data
3840  ! Locals
3841  character(len=240) :: mesg
3842  integer :: i,j,ii,jj,i0,j0,f1,f2,f_in1,f_in2
3843  integer :: k,ks,ke
3844  real :: ave,total_weight,weight
3845  real :: epsilon = 1.0e-20
3846 
3847  ks=1 ; ke =size(field_in,3)
3848  ! Allocate the down sampled field on the down sampled data domain
3849 ! allocate(field_out(diag_cs%dsamp(dl)%isd:diag_cs%dsamp(dl)%ied,diag_cs%dsamp(dl)%jsd:diag_cs%dsamp(dl)%jed,ks:ke))
3850 ! allocate(field_out(1:size(field_in,1)/dl,1:size(field_in,2)/dl,ks:ke))
3851  f_in1 = size(field_in,1)
3852  f_in2 = size(field_in,2)
3853  f1 = f_in1/dl
3854  f2 = f_in2/dl
3855  !Correction for the symmetric case
3856  if (diag_cs%G%symmetric) then
3857  f1 = f1 + mod(f_in1,dl)
3858  f2 = f2 + mod(f_in2,dl)
3859  endif
3860  allocate(field_out(1:f1,1:f2,ks:ke))
3861 
3862  ! Fill the down sampled field on the down sampled diagnostics (almost always compuate) domain
3863  if(method .eq. mmm) then
3864  do k= ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
3865  i0 = isv_o+dl*(i-isv_d)
3866  j0 = jsv_o+dl*(j-jsv_d)
3867  ave = 0.0
3868  total_weight = 0.0
3869  do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
3870 ! do ii=i0,i0+dl-1 ; do jj=j0,j0+dl-1 !This seems to be faster!!!!
3871  weight = mask(ii,jj,k)*diag_cs%G%areaT(ii,jj)*diag_cs%h(ii,jj,k)
3872  total_weight = total_weight + weight
3873  ave=ave+field_in(ii,jj,k)*weight
3874  enddo; enddo
3875  field_out(i,j,k) = ave/(total_weight+epsilon) !Avoid zero mask at all aggregating cells where ave=0.0
3876  enddo; enddo; enddo
3877  elseif(method .eq. sss) then !e.g., volcello
3878  do k= ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
3879  i0 = isv_o+dl*(i-isv_d)
3880  j0 = jsv_o+dl*(j-jsv_d)
3881  ave = 0.0
3882  total_weight = 0.0
3883  do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
3884 ! do ii=i0,i0+dl-1 ; do jj=j0,j0+dl-1
3885  weight = mask(ii,jj,k)
3886  total_weight = total_weight + weight
3887  ave=ave+field_in(ii,jj,k)*weight
3888  enddo; enddo
3889  field_out(i,j,k) = ave/(total_weight+epsilon) !Avoid zero mask at all aggregating cells where ave=0.0
3890  enddo; enddo; enddo
3891  elseif(method .eq. mmp .or. method .eq. mms) then !e.g., T_advection_xy
3892  do k= ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
3893  i0 = isv_o+dl*(i-isv_d)
3894  j0 = jsv_o+dl*(j-jsv_d)
3895  ave = 0.0
3896  total_weight = 0.0
3897  do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
3898 ! do ii=i0,i0+dl-1 ; do jj=j0,j0+dl-1
3899  weight = mask(ii,jj,k)*diag_cs%G%areaT(ii,jj)
3900  total_weight = total_weight + weight
3901  ave=ave+field_in(ii,jj,k)*weight
3902  enddo; enddo
3903  field_out(i,j,k) = ave/(total_weight+epsilon) !Avoid zero mask at all aggregating cells where ave=0.0
3904  enddo; enddo; enddo
3905  elseif(method .eq. pmm) then
3906  do k= ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
3907  i0 = isv_o+dl*(i-isv_d)
3908  j0 = jsv_o+dl*(j-jsv_d)
3909  ave = 0.0
3910  total_weight = 0.0
3911  ii=i0
3912  do jj=j0,j0+dl-1
3913  weight =mask(ii,jj,k)*diag_cs%G%dyCu(ii,jj)*diag_cs%h(ii,jj,k)
3914  total_weight = total_weight +weight
3915  ave=ave+field_in(ii,jj,k)*weight
3916  enddo
3917  field_out(i,j,k) = ave/(total_weight+epsilon) !Avoid zero mask at all aggregating cells where ave=0.0
3918  enddo; enddo; enddo
3919  elseif(method .eq. psm) then
3920  do k= ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
3921  i0 = isv_o+dl*(i-isv_d)
3922  j0 = jsv_o+dl*(j-jsv_d)
3923  ave = 0.0
3924  total_weight = 0.0
3925  ii=i0
3926  do jj=j0,j0+dl-1
3927  weight =mask(ii,jj,k)*diag_cs%h(ii,jj,k)
3928  total_weight = total_weight +weight
3929  ave=ave+field_in(ii,jj,k)*weight
3930  enddo
3931  field_out(i,j,k) = ave/(total_weight+epsilon) !Avoid zero mask at all aggregating cells where ave=0.0
3932  enddo; enddo; enddo
3933  elseif(method .eq. pss) then !e.g. umo
3934  do k= ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
3935  i0 = isv_o+dl*(i-isv_d)
3936  j0 = jsv_o+dl*(j-jsv_d)
3937  ave = 0.0
3938  total_weight = 0.0
3939  ii=i0
3940  do jj=j0,j0+dl-1
3941  weight =mask(ii,jj,k)
3942  total_weight = total_weight +weight
3943  ave=ave+field_in(ii,jj,k)*weight
3944  enddo
3945  field_out(i,j,k) = ave/(total_weight+epsilon) !Avoid zero mask at all aggregating cells where ave=0.0
3946  enddo; enddo; enddo
3947  elseif(method .eq. sps) then !e.g. vmo
3948  do k= ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
3949  i0 = isv_o+dl*(i-isv_d)
3950  j0 = jsv_o+dl*(j-jsv_d)
3951  ave = 0.0
3952  total_weight = 0.0
3953  jj=j0
3954  do ii=i0,i0+dl-1
3955  weight =mask(ii,jj,k)
3956  total_weight = total_weight +weight
3957  ave=ave+field_in(ii,jj,k)*weight
3958  enddo
3959  field_out(i,j,k) = ave/(total_weight+epsilon) !Avoid zero mask at all aggregating cells where ave=0.0
3960  enddo; enddo; enddo
3961  elseif(method .eq. mpm) then
3962  do k= ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
3963  i0 = isv_o+dl*(i-isv_d)
3964  j0 = jsv_o+dl*(j-jsv_d)
3965  ave = 0.0
3966  total_weight = 0.0
3967  jj=j0
3968  do ii=i0,i0+dl-1
3969  weight = mask(ii,jj,k)*diag_cs%G%dxCv(ii,jj)*diag_cs%h(ii,jj,k)
3970  total_weight = total_weight + weight
3971  ave=ave+field_in(ii,jj,k)*weight
3972  enddo
3973  field_out(i,j,k) = ave/(total_weight+epsilon) !Avoid zero mask at all aggregating cells where ave=0.0
3974  enddo; enddo; enddo
3975  elseif(method .eq. msk) then !The input field is a mask, subsample
3976  field_out(:,:,:) = 0.0
3977  do k= ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
3978  i0 = isv_o+dl*(i-isv_d)
3979  j0 = jsv_o+dl*(j-jsv_d)
3980  ave = 0.0
3981  do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
3982  ave=ave+field_in(ii,jj,k)
3983  enddo; enddo
3984  if(ave > 0.0) field_out(i,j,k)=1.0
3985  enddo; enddo; enddo
3986  else
3987  write (mesg,*) " unknown sampling method: ",method
3988  call mom_error(fatal, "downsample_field_3d: "//trim(mesg)//" "//trim(diag%debug_str))
3989  endif
3990 
3991 end subroutine downsample_field_3d
3992 
3993 !> This subroutine allocates and computes a down sampled 2d array given an input array
3994 !! The down sample method is based on the "cell_methods" for the diagnostics as explained
3995 !! in the above table
3996 subroutine downsample_field_2d(field_in, field_out, dl, method, mask, diag_cs, diag, &
3997  isv_o, jsv_o, isv_d, iev_d, jsv_d, jev_d)
3998  real, dimension(:,:), pointer :: field_in !< Original field to be down sampled
3999  real, dimension(:,:), allocatable :: field_out !< Down sampled field
4000  integer, intent(in) :: dl !< Level of down sampling
4001  integer, intent(in) :: method !< Sampling method
4002  real, dimension(:,:), pointer :: mask !< Mask for field
4003  type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
4004  type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
4005  integer, intent(in) :: isv_o !< Original i-start index
4006  integer, intent(in) :: jsv_o !< Original j-start index
4007  integer, intent(in) :: isv_d !< i-start index of down sampled data
4008  integer, intent(in) :: iev_d !< i-end index of down sampled data
4009  integer, intent(in) :: jsv_d !< j-start index of down sampled data
4010  integer, intent(in) :: jev_d !< j-end index of down sampled data
4011  ! Locals
4012  character(len=240) :: mesg
4013  integer :: i,j,ii,jj,i0,j0,f1,f2,f_in1,f_in2
4014  real :: ave,total_weight,weight
4015  real :: epsilon = 1.0e-20
4016 
4017  ! Allocate the down sampled field on the down sampled data domain
4018 ! allocate(field_out(diag_cs%dsamp(dl)%isd:diag_cs%dsamp(dl)%ied,diag_cs%dsamp(dl)%jsd:diag_cs%dsamp(dl)%jed))
4019 ! allocate(field_out(1:size(field_in,1)/dl,1:size(field_in,2)/dl))
4020  ! Fill the down sampled field on the down sampled diagnostics (almost always compuate) domain
4021  f_in1 = size(field_in,1)
4022  f_in2 = size(field_in,2)
4023  f1 = f_in1/dl
4024  f2 = f_in2/dl
4025  ! Correction for the symmetric case
4026  if (diag_cs%G%symmetric) then
4027  f1 = f1 + mod(f_in1,dl)
4028  f2 = f2 + mod(f_in2,dl)
4029  endif
4030  allocate(field_out(1:f1,1:f2))
4031 
4032  if(method .eq. mmp) then
4033  do j=jsv_d,jev_d ; do i=isv_d,iev_d
4034  i0 = isv_o+dl*(i-isv_d)
4035  j0 = jsv_o+dl*(j-jsv_d)
4036  ave = 0.0
4037  total_weight = 0.0
4038  do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
4039 ! do ii=i0,i0+dl-1 ; do jj=j0,j0+dl-1
4040  weight = mask(ii,jj)*diag_cs%G%areaT(ii,jj)
4041  total_weight = total_weight + weight
4042  ave=ave+field_in(ii,jj)*weight
4043  enddo; enddo
4044  field_out(i,j) = ave/(total_weight+epsilon) !Avoid zero mask at all aggregating cells where ave=0.0
4045  enddo; enddo
4046  elseif(method .eq. ssp) then ! e.g., T_dfxy_cont_tendency_2d
4047  do j=jsv_d,jev_d ; do i=isv_d,iev_d
4048  i0 = isv_o+dl*(i-isv_d)
4049  j0 = jsv_o+dl*(j-jsv_d)
4050  ave = 0.0
4051  total_weight = 0.0
4052  do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
4053 ! do ii=i0,i0+dl-1 ; do jj=j0,j0+dl-1
4054  weight = mask(ii,jj)
4055  total_weight = total_weight + weight
4056  ave=ave+field_in(ii,jj)*weight
4057  enddo; enddo
4058  field_out(i,j) = ave/(total_weight+epsilon) !Avoid zero mask at all aggregating cells where ave=0.0
4059  enddo; enddo
4060  elseif(method .eq. psp) then ! e.g., umo_2d
4061  do j=jsv_d,jev_d ; do i=isv_d,iev_d
4062  i0 = isv_o+dl*(i-isv_d)
4063  j0 = jsv_o+dl*(j-jsv_d)
4064  ave = 0.0
4065  total_weight = 0.0
4066  ii=i0
4067  do jj=j0,j0+dl-1
4068  weight =mask(ii,jj)
4069  total_weight = total_weight +weight
4070  ave=ave+field_in(ii,jj)*weight
4071  enddo
4072  field_out(i,j) = ave/(total_weight+epsilon) !Avoid zero mask at all aggregating cells where ave=0.0
4073  enddo; enddo
4074  elseif(method .eq. spp) then ! e.g., vmo_2d
4075  do j=jsv_d,jev_d ; do i=isv_d,iev_d
4076  i0 = isv_o+dl*(i-isv_d)
4077  j0 = jsv_o+dl*(j-jsv_d)
4078  ave = 0.0
4079  total_weight = 0.0
4080  jj=j0
4081  do ii=i0,i0+dl-1
4082  weight =mask(ii,jj)
4083  total_weight = total_weight +weight
4084  ave=ave+field_in(ii,jj)*weight
4085  enddo
4086  field_out(i,j) = ave/(total_weight+epsilon) !Avoid zero mask at all aggregating cells where ave=0.0
4087  enddo; enddo
4088  elseif(method .eq. pmp) then
4089  do j=jsv_d,jev_d ; do i=isv_d,iev_d
4090  i0 = isv_o+dl*(i-isv_d)
4091  j0 = jsv_o+dl*(j-jsv_d)
4092  ave = 0.0
4093  total_weight = 0.0
4094  ii=i0
4095  do jj=j0,j0+dl-1
4096  weight =mask(ii,jj)*diag_cs%G%dyCu(ii,jj)!*diag_cs%h(ii,jj,1) !Niki?
4097  total_weight = total_weight +weight
4098  ave=ave+field_in(ii,jj)*weight
4099  enddo
4100  field_out(i,j) = ave/(total_weight+epsilon) !Avoid zero mask at all aggregating cells where ave=0.0
4101  enddo; enddo
4102  elseif(method .eq. mpp) then
4103  do j=jsv_d,jev_d ; do i=isv_d,iev_d
4104  i0 = isv_o+dl*(i-isv_d)
4105  j0 = jsv_o+dl*(j-jsv_d)
4106  ave = 0.0
4107  total_weight = 0.0
4108  jj=j0
4109  do ii=i0,i0+dl-1
4110  weight =mask(ii,jj)*diag_cs%G%dxCv(ii,jj)!*diag_cs%h(ii,jj,1) !Niki?
4111  total_weight = total_weight +weight
4112  ave=ave+field_in(ii,jj)*weight
4113  enddo
4114  field_out(i,j) = ave/(total_weight+epsilon) !Avoid zero mask at all aggregating cells where ave=0.0
4115  enddo; enddo
4116  elseif(method .eq. msk) then !The input field is a mask, subsample
4117  field_out(:,:) = 0.0
4118  do j=jsv_d,jev_d ; do i=isv_d,iev_d
4119  i0 = isv_o+dl*(i-isv_d)
4120  j0 = jsv_o+dl*(j-jsv_d)
4121  ave = 0.0
4122  do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
4123  ave=ave+field_in(ii,jj)
4124  enddo; enddo
4125  if(ave > 0.0) field_out(i,j)=1.0
4126  enddo; enddo
4127  else
4128  write (mesg,*) " unknown sampling method: ",method
4129  call mom_error(fatal, "downsample_field_2d: "//trim(mesg)//" "//trim(diag%debug_str))
4130  endif
4131 
4132 end subroutine downsample_field_2d
4133 
4134 !> Allocate and compute the 2d down sampled mask
4135 !! The masks are down sampled based on a minority rule, i.e., a coarse cell is open (1)
4136 !! if at least one of the sub-cells are open, otherwise it's closed (0)
4137 subroutine downsample_mask_2d(field_in, field_out, dl, isc_o, jsc_o, isc_d, iec_d, jsc_d, jec_d, &
4138  isd_d, ied_d, jsd_d, jed_d)
4139  real, dimension(:,:), intent(in) :: field_in !< Original field to be down sampled
4140  real, dimension(:,:), pointer :: field_out !< Down sampled field
4141  integer, intent(in) :: dl !< Level of down sampling
4142  integer, intent(in) :: isc_o !< Original i-start index
4143  integer, intent(in) :: jsc_o !< Original j-start index
4144  integer, intent(in) :: isc_d !< Computational i-start index of down sampled data
4145  integer, intent(in) :: iec_d !< Computational i-end index of down sampled data
4146  integer, intent(in) :: jsc_d !< Computational j-start index of down sampled data
4147  integer, intent(in) :: jec_d !< Computational j-end index of down sampled data
4148  integer, intent(in) :: isd_d !< Computational i-start index of down sampled data
4149  integer, intent(in) :: ied_d !< Computational i-end index of down sampled data
4150  integer, intent(in) :: jsd_d !< Computational j-start index of down sampled data
4151  integer, intent(in) :: jed_d !< Computational j-end index of down sampled data
4152  ! Locals
4153  integer :: i,j,ii,jj,i0,j0
4154  real :: tot_non_zero
4155  ! down sampled mask = 0 unless the mask value of one of the down sampling cells is 1
4156  allocate(field_out(isd_d:ied_d,jsd_d:jed_d))
4157  field_out(:,:) = 0.0
4158  do j=jsc_d,jec_d ; do i=isc_d,iec_d
4159  i0 = isc_o+dl*(i-isc_d)
4160  j0 = jsc_o+dl*(j-jsc_d)
4161  tot_non_zero = 0.0
4162  do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
4163  tot_non_zero = tot_non_zero + field_in(ii,jj)
4164  enddo;enddo
4165  if(tot_non_zero > 0.0) field_out(i,j)=1.0
4166  enddo; enddo
4167 end subroutine downsample_mask_2d
4168 
4169 !> Allocate and compute the 3d down sampled mask
4170 !! The masks are down sampled based on a minority rule, i.e., a coarse cell is open (1)
4171 !! if at least one of the sub-cells are open, otherwise it's closed (0)
4172 subroutine downsample_mask_3d(field_in, field_out, dl, isc_o, jsc_o, isc_d, iec_d, jsc_d, jec_d, &
4173  isd_d, ied_d, jsd_d, jed_d)
4174  real, dimension(:,:,:), intent(in) :: field_in !< Original field to be down sampled
4175  real, dimension(:,:,:), pointer :: field_out !< down sampled field
4176  integer, intent(in) :: dl !< Level of down sampling
4177  integer, intent(in) :: isc_o !< Original i-start index
4178  integer, intent(in) :: jsc_o !< Original j-start index
4179  integer, intent(in) :: isc_d !< Computational i-start index of down sampled data
4180  integer, intent(in) :: iec_d !< Computational i-end index of down sampled data
4181  integer, intent(in) :: jsc_d !< Computational j-start index of down sampled data
4182  integer, intent(in) :: jec_d !< Computational j-end index of down sampled data
4183  integer, intent(in) :: isd_d !< Computational i-start index of down sampled data
4184  integer, intent(in) :: ied_d !< Computational i-end index of down sampled data
4185  integer, intent(in) :: jsd_d !< Computational j-start index of down sampled data
4186  integer, intent(in) :: jed_d !< Computational j-end index of down sampled data
4187  ! Locals
4188  integer :: i,j,ii,jj,i0,j0,k,ks,ke
4189  real :: tot_non_zero
4190  ! down sampled mask = 0 unless the mask value of one of the down sampling cells is 1
4191  ks = lbound(field_in,3) ; ke = ubound(field_in,3)
4192  allocate(field_out(isd_d:ied_d,jsd_d:jed_d,ks:ke))
4193  field_out(:,:,:) = 0.0
4194  do k= ks,ke ; do j=jsc_d,jec_d ; do i=isc_d,iec_d
4195  i0 = isc_o+dl*(i-isc_d)
4196  j0 = jsc_o+dl*(j-jsc_d)
4197  tot_non_zero = 0.0
4198  do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
4199  tot_non_zero = tot_non_zero + field_in(ii,jj,k)
4200  enddo;enddo
4201  if(tot_non_zero > 0.0) field_out(i,j,k)=1.0
4202  enddo; enddo; enddo
4203 end subroutine downsample_mask_3d
4204 
4205 end module mom_diag_mediator
4206 
mom_time_manager
Wraps the FMS time manager functions.
Definition: MOM_time_manager.F90:2
mom_safe_alloc
Convenience functions for safely allocating memory without accidentally reallocating pointer and caus...
Definition: MOM_safe_alloc.F90:3
mom_diag_mediator::diagcs_dsamp
Container for down sampling information.
Definition: MOM_diag_mediator.F90:195
mom_diag_mediator::downsample_field
Down sample a field.
Definition: MOM_diag_mediator.F90:75
mom_verticalgrid
Provides a transparent vertical ocean grid type and supporting routines.
Definition: MOM_verticalGrid.F90:2
mom_checksums::bchksum
Checksums an array (2d or 3d) staggered at corner points.
Definition: MOM_checksums.F90:53
mom_checksums::vchksum
Checksums an array (2d or 3d) staggered at C-grid v points.
Definition: MOM_checksums.F90:38
mom_file_parser::log_version
An overloaded interface to log version information about modules.
Definition: MOM_file_parser.F90:109
mom_diag_mediator
The subroutines here provide convenient wrappers to the fms diag_manager interfaces with additional d...
Definition: MOM_diag_mediator.F90:3
mom_diag_mediator::axes_grp
A group of 1D axes that comprise a 1D/2D/3D mesh.
Definition: MOM_diag_mediator.F90:96
mom_string_functions
Handy functions for manipulating strings.
Definition: MOM_string_functions.F90:2
mom_eos
Provides subroutines for quantities specific to the equation of state.
Definition: MOM_EOS.F90:2
mom_file_parser::param_file_type
A structure that can be parsed to read and document run-time parameters.
Definition: MOM_file_parser.F90:54
mom_checksums::chksum
This is an older interface for 1-, 2-, or 3-D checksums.
Definition: MOM_checksums.F90:63
mom_file_parser::get_param
An overloaded interface to read and log the values of various types of parameters.
Definition: MOM_file_parser.F90:102
mom_io
This module contains I/O framework code.
Definition: MOM_io.F90:2
mom_unit_scaling::unit_scale_type
Describes various unit conversion factors.
Definition: MOM_unit_scaling.F90:14
mom_diag_mediator::diag_grids_type
Contains an array to store a diagnostic target grid.
Definition: MOM_diag_mediator.F90:141
mom_coms
Interfaces to non-domain-oriented communication subroutines, including the MOM6 reproducing sums faci...
Definition: MOM_coms.F90:3
mom_diag_mediator::post_data
Make a diagnostic available for averaging or output.
Definition: MOM_diag_mediator.F90:70
mom_safe_alloc::safe_alloc_alloc
Allocate a 2-d or 3-d allocatable array.
Definition: MOM_safe_alloc.F90:18
mom_eos::eos_type
A control structure for the equation of state.
Definition: MOM_EOS.F90:86
mom_diag_mediator::diag_grid_storage
Stores all the remapping grids and the model's native space thicknesses.
Definition: MOM_diag_mediator.F90:146
mom_diag_mediator::diag_type
This type is used to represent a diagnostic at the diag_mediator level.
Definition: MOM_diag_mediator.F90:179
mom_verticalgrid::verticalgrid_type
Describes the vertical ocean grid, including unit conversion factors.
Definition: MOM_verticalGrid.F90:24
mom_checksums
Routines to calculate checksums of various array and vector types.
Definition: MOM_checksums.F90:2
mom_io::mom_read_data
Read a data field from a file.
Definition: MOM_io.F90:74
mom_diag_mediator::downsample_mask
Down sample the mask of a field.
Definition: MOM_diag_mediator.F90:80
mom_cpu_clock
Wraps the MPP cpu clock functions.
Definition: MOM_cpu_clock.F90:2
mom_file_parser
The MOM6 facility to parse input files for runtime parameters.
Definition: MOM_file_parser.F90:2
mom_diag_manager_wrapper::register_diag_field_fms
A wrapper for register_diag_field_array()
Definition: MOM_diag_manager_wrapper.F90:14
mom_diag_remap
provides runtime remapping of diagnostics to z star, sigma and rho vertical coordinates.
Definition: MOM_diag_remap.F90:56
mom_grid
Provides the ocean grid type.
Definition: MOM_grid.F90:2
mom_unit_scaling
Provides a transparent unit rescaling type to facilitate dimensional consistency testing.
Definition: MOM_unit_scaling.F90:2
mom_io::vardesc
Type for describing a variable, typically a tracer.
Definition: MOM_io.F90:53
mom_checksums::uchksum
Checksums an array (2d or 3d) staggered at C-grid u points.
Definition: MOM_checksums.F90:33
mom_diag_mediator::downsample_diag_field
Down sample a diagnostic field.
Definition: MOM_diag_mediator.F90:85
mom_diag_mediator::diag_dsamp
Contained for down sampled masks.
Definition: MOM_diag_mediator.F90:90
mom_checksums::hchksum
Checksums an array (2d or 3d) staggered at tracer points.
Definition: MOM_checksums.F90:48
mom_diag_remap::diag_remap_ctrl
Represents remapping of diagnostics to a particular vertical coordinate.
Definition: MOM_diag_remap.F90:103
mom_safe_alloc::safe_alloc_ptr
Allocate a pointer to a 1-d, 2-d or 3-d array.
Definition: MOM_safe_alloc.F90:12
mom_diag_manager_wrapper
A simple (very thin) wrapper for register_diag_field to avoid a compiler bug with PGI.
Definition: MOM_diag_manager_wrapper.F90:2
mom_error_handler
Routines for error handling and I/O management.
Definition: MOM_error_handler.F90:2
mom_grid::ocean_grid_type
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:25
mom_diag_mediator::diag_ctrl
The following data type a list of diagnostic fields an their variants, as well as variables that cont...
Definition: MOM_diag_mediator.F90:239