MOM_diag_mediator.F90

1! This file is part of MOM6, the Modular Ocean Model version 6.
2! See the LICENSE file for licensing information.
3! SPDX-License-Identifier: Apache-2.0
4
5!> The subroutines here provide convenient wrappers to the FMS diag_manager
6!! interfaces with additional diagnostic capabilities.
8
9use mom_array_transform, only : symmetric_sum
10use mom_checksums, only : chksum0, zchksum, hchksum, uchksum, vchksum, bchksum
11use mom_coms, only : pe_here
12use mom_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end
13use mom_cpu_clock, only : clock_module, clock_routine
15use mom_diag_manager_infra, only : mom_diag_manager_init, mom_diag_manager_end
16use mom_diag_manager_infra, only : diag_axis_init=>mom_diag_axis_init, get_mom_diag_axis_name
17use mom_diag_manager_infra, only : send_data_infra, mom_diag_field_add_attribute, east, north
18use mom_diag_manager_infra, only : register_diag_field_infra, register_static_field_infra
19use mom_diag_manager_infra, only : get_mom_diag_field_id, diag_field_not_found
20use mom_diag_manager_infra, only : diag_send_complete_infra
27use mom_eos, only : eos_type
28use mom_error_handler, only : mom_error, fatal, warning, is_root_pe, assert, calltree_showquery
30use mom_file_parser, only : get_param, log_version, param_file_type
31use mom_grid, only : ocean_grid_type
32use mom_interface_heights, only : thickness_to_dz
33use mom_io, only : vardesc, query_vardesc
35use mom_safe_alloc, only : safe_alloc_ptr, safe_alloc_alloc
37use mom_time_manager, only : time_type, get_time
41use mom_domains, only : get_domain_extent, clone_mom_domain
42
43implicit none ; private
44
45#undef __DO_SAFETY_CHECKS__
46#define IMPLIES(A, B) ((.not. (A)) .or. (B))
47
48public set_axes_info, post_data, register_diag_field, time_type
52! post_data_1d_k is a deprecated interface that can be replaced by a call to post_data, but
53! it is being retained for backward compatibility to older versions of the ocean_BGC code.
54public post_data_1d_k
55public safe_alloc_ptr, safe_alloc_alloc
60public diag_axis_init, ocean_register_diag, register_static_field
61public register_scalar_field
72
73!> Make a diagnostic available for averaging or output.
74interface post_data
76end interface post_data
77
78!> Registers a non-array scalar diagnostic, returning an integer handle
79interface register_scalar_field
81end interface register_scalar_field
82
83!> Down sample a field
84interface downsample_field
86end interface downsample_field
87
88!> Down sample the mask of a field
89interface downsample_mask
91end interface downsample_mask
92
93!> Down sample a diagnostic field
94interface downsample_diag_field
96end interface downsample_diag_field
97
98!> Contained for down sampled masks
99type, private :: diag_dsamp
100 real, pointer, dimension(:,:) :: mask2d => null() !< Mask for 2d (x-y) axes [nondim]
101 real, pointer, dimension(:,:,:) :: mask3d => null() !< Mask for 3d axes [nondim]
102end type diag_dsamp
103
104!> A group of 1D axes that comprise a 1D/2D/3D mesh
105type, public :: axes_grp
106 character(len=15) :: id !< The id string for this particular combination of handles.
107 integer :: rank !< Number of dimensions in the list of axes.
108 integer, dimension(:), allocatable :: handles !< Handles to 1D axes.
109 type(diag_ctrl), pointer :: diag_cs => null() !< Circular link back to the main diagnostics control structure
110 !! (Used to avoid passing said structure into every possible call).
111 ! ID's for cell_methods
112 character(len=9) :: x_cell_method = '' !< Default nature of data representation, if axes group
113 !! includes x-direction.
114 character(len=9) :: y_cell_method = '' !< Default nature of data representation, if axes group
115 !! includes y-direction.
116 character(len=9) :: v_cell_method = '' !< Default nature of data representation, if axes group
117 !! includes vertical direction.
118 ! For remapping
119 integer :: nz = 0 !< Vertical dimension of diagnostic
120 integer :: vertical_coordinate_number = 0 !< Index of the corresponding diag_remap_ctrl for this axis group
121 ! For detecting position on the grid
122 logical :: is_h_point = .false. !< If true, indicates that this axes group is for an h-point located field.
123 logical :: is_q_point = .false. !< If true, indicates that this axes group is for a q-point located field.
124 logical :: is_u_point = .false. !< If true, indicates that this axes group is for a u-point located field.
125 logical :: is_v_point = .false. !< If true, indicates that this axes group is for a v-point located field.
126 logical :: is_layer = .false. !< If true, indicates that this axes group is for a layer vertically-located field.
127 logical :: is_interface = .false. !< If true, indicates that this axes group is for an interface
128 !! vertically-located field.
129 logical :: is_native = .true. !< If true, indicates that this axes group is for a native model grid.
130 !! False for any other grid. Used for rank>2.
131 logical :: needs_remapping = .false. !< If true, indicates that this axes group is for a intensive layer-located
132 !! field that must be remapped to these axes. Used for rank>2.
133 logical :: needs_interpolating = .false. !< If true, indicates that this axes group is for a sampled
134 !! interface-located field that must be interpolated to
135 !! these axes. Used for rank>2.
136 integer :: downsample_level_factor = 1 !< If greater than 1, the factor by which this diagnostic will be downsampled
137 integer :: downsample_level_index = 0 !< If greater than 0, the index for the downsample level for this diagnostic
138 !! in the diag_cs%dsamp array.
139 ! For horizontally averaged diagnostics (applies to 2d and 3d fields only)
140 type(axes_grp), pointer :: xyave_axes => null() !< The associated 1d axes for horizontally area-averaged diagnostics
141 ! ID's for cell_measures
142 integer :: id_area = -1 !< The diag_manager id for area to be used for cell_measure of variables with this axes_grp.
143 integer :: id_volume = -1 !< The diag_manager id for volume to be used for cell_measure of variables
144 !! with this axes_grp.
145 ! For masking
146 real, pointer, dimension(:,:) :: mask2d => null() !< Mask for 2d (x-y) axes [nondim]
147 real, pointer, dimension(:,:,:) :: mask3d => null() !< Mask for 3d axes [nondim]
148 type(diag_dsamp), dimension(:), allocatable :: dsamp !< Downsample container
149
150 ! For diagnostics posted piecemeal
151 type(diag_buffer_2d) :: piecemeal_2d !< A dynamically reallocated buffer for 2d piecemeal diagnostics
152 type(diag_buffer_3d) :: piecemeal_3d !< A dynamically reallocated buffer for 3d piecemeal diagnostics
153end type axes_grp
154
155!> Contains an array to store a diagnostic target grid
156type, private :: diag_grids_type
157 real, dimension(:,:,:), allocatable :: h !< Target grid for remapped coordinate [H ~> m or kg m-2] or [Z ~> m]
158end type diag_grids_type
159
160!> Stores all the remapping grids and the model's native space thicknesses
161type, public :: diag_grid_storage
162 integer :: num_diag_coords !< Number of target coordinates
163 real, dimension(:,:,:), allocatable :: h_state !< Layer thicknesses in native
164 !! space [H ~> m or kg m-2]
165 type(diag_grids_type), dimension(:), allocatable :: diag_grids !< Primarily empty, except h field
166end type diag_grid_storage
167
168! Integers to encode the total cell methods
169! Note that vorticity points (the PPP and PPM methods) are not fully dealt with for downsampling.
170integer :: ppp=111 !< x:point,y:point,z:point
171!integer :: PPS=112 ! x:point,y:point,z:sum , this kind of diagnostic is not currently present in diag_table.MOM6
172integer :: ppm=113 !< x:point,y:point,z:mean
173integer :: psp=121 !< x:point,y:sum,z:point
174integer :: pss=122 !< x:point,y:sum,z:point
175integer :: psm=123 !< x:point,y:sum,z:mean
176integer :: pmp=131 !< x:point,y:mean,z:point
177integer :: pmm=133 !< x:point,y:mean,z:mean
178integer :: spp=211 !< x:sum,y:point,z:point
179integer :: sps=212 !< x:sum,y:point,z:sum
180integer :: ssp=221 !< x:sum,y:sum,z:point
181integer :: mpp=311 !< x:mean,y:point,z:point
182integer :: mpm=313 !< x:mean,y:point,z:mean
183integer :: mmp=331 !< x:mean,y:mean,z:point
184integer :: mms=332 !< x:mean,y:mean,z:sum
185integer :: sss=222 !< x:sum,y:sum,z:sum
186integer :: mmm=333 !< x:mean,y:mean,z:mean
187
188!> This type is used to represent a diagnostic at the diag_mediator level.
189!!
190!! There can be both 'primary' and 'secondary' diagnostics. The primaries
191!! reside in the diag_cs%diags array. They have an id which is an index
192!! into this array. The secondaries are 'variations' on the primary diagnostic.
193!! For example the CMOR diagnostics are secondary. The secondary diagnostics
194!! are kept in a list with the primary diagnostic as the head.
195type, private :: diag_type
196 logical :: in_use !< True if this entry is being used.
197 integer :: fms_diag_id !< Underlying FMS diag_manager id.
198 integer :: fms_xyave_diag_id = -1 !< For a horizontally area-averaged diagnostic.
199 integer :: downsample_diag_id = -1 !< For a horizontally area-downsampled diagnostic.
200 character(len=64) :: debug_str = '' !< The diagnostic name and module for FATAL errors and debugging.
201 type(axes_grp), pointer :: axes => null() !< The axis group for this diagnostic
202 type(diag_type), pointer :: next => null() !< Pointer to the next diagnostic
203 real :: conversion_factor = 0. !< If non-zero, a factor to multiply data by before posting to FMS,
204 !! often including factors to undo internal scaling in units of [a A-1 ~> 1]
205 logical :: v_extensive = .false. !< True for vertically extensive fields (vertically integrated).
206 !! False for intensive (concentrations).
207 integer :: xyz_method = 0 !< A 3 digit integer encoding the diagnostics cell method
208 !! It can be used to determine the downsample algorithm
209end type diag_type
210
211!> Container for down sampling information
212type diagcs_dsamp
213 integer :: isc !< The start i-index of cell centers within the computational domain
214 integer :: iec !< The end i-index of cell centers within the computational domain
215 integer :: jsc !< The start j-index of cell centers within the computational domain
216 integer :: jec !< The end j-index of cell centers within the computational domain
217 integer :: isd !< The start i-index of cell centers within the data domain
218 integer :: ied !< The end i-index of cell centers within the data domain
219 integer :: jsd !< The start j-index of cell centers within the data domain
220 integer :: jed !< The end j-index of cell centers within the data domain
221 integer :: isg !< The start i-index of cell centers within the global domain
222 integer :: ieg !< The end i-index of cell centers within the global domain
223 integer :: jsg !< The start j-index of cell centers within the global domain
224 integer :: jeg !< The end j-index of cell centers within the global domain
225 integer :: isgb !< The start i-index of cell corners within the global domain
226 integer :: iegb !< The end i-index of cell corners within the global domain
227 integer :: jsgb !< The start j-index of cell corners within the global domain
228 integer :: jegb !< The end j-index of cell corners within the global domain
229
230 !>@{ Axes for each location on a diagnostic grid
231 type(axes_grp) :: axesbl, axestl, axescul, axescvl
232 type(axes_grp) :: axesbi, axesti, axescui, axescvi
233 type(axes_grp) :: axesb1, axest1, axescu1, axescv1
234 type(axes_grp), dimension(:), allocatable :: remap_axestl, remap_axesbl, remap_axescul, remap_axescvl
235 type(axes_grp), dimension(:), allocatable :: remap_axesti, remap_axesbi, remap_axescui, remap_axescvi
236 !>@}
237
238 real, dimension(:,:), pointer :: mask2dt => null() !< 2D mask array for cell-center points [nondim]
239 real, dimension(:,:), pointer :: mask2dbu => null() !< 2D mask array for cell-corner points [nondim]
240 real, dimension(:,:), pointer :: mask2dcu => null() !< 2D mask array for east-face points [nondim]
241 real, dimension(:,:), pointer :: mask2dcv => null() !< 2D mask array for north-face points [nondim]
242 !>@{ 3D mask arrays for diagnostics at layers (mask...L) and interfaces (mask...i), all [nondim]
243 real, dimension(:,:,:), pointer :: mask3dtl => null()
244 real, dimension(:,:,:), pointer :: mask3dbl => null()
245 real, dimension(:,:,:), pointer :: mask3dcul => null()
246 real, dimension(:,:,:), pointer :: mask3dcvl => null()
247 real, dimension(:,:,:), pointer :: mask3dti => null()
248 real, dimension(:,:,:), pointer :: mask3dbi => null()
249 real, dimension(:,:,:), pointer :: mask3dcui => null()
250 real, dimension(:,:,:), pointer :: mask3dcvi => null()
251 !>@}
252end type diagcs_dsamp
253
254!> The following data type a list of diagnostic fields an their variants,
255!! as well as variables that control the handling of model output.
256type, public :: diag_ctrl
257 integer :: available_diag_doc_unit = -1 !< The unit number of a diagnostic documentation file.
258 !! This file is open if available_diag_doc_unit is > 0.
259 integer :: chksum_iounit = -1 !< The unit number of a diagnostic documentation file.
260 !! This file is open if available_diag_doc_unit is > 0.
261 logical :: diag_as_chksum !< If true, log chksums in a text file instead of posting diagnostics
262 logical :: show_call_tree !< Display the call tree while running. Set by VERBOSITY level.
263 logical :: index_space_axes !< If true, diagnostic horizontal coordinates axes are in index space.
264 logical :: symmetric_downsample_sums !< If true, use rotationally symmetric sums when downsampling diagnostics.
265
266 ! The following fields are used for the output of the data.
267 ! These give the computational-domain sizes, and are relative to a start value
268 ! of 1 in memory for the tracer-point arrays.
269 integer :: is !< The start i-index of cell centers within the computational domain
270 integer :: ie !< The end i-index of cell centers within the computational domain
271 integer :: js !< The start j-index of cell centers within the computational domain
272 integer :: je !< The end j-index of cell centers within the computational domain
273 ! These give the memory-domain sizes, and can start at any value on each PE.
274 integer :: isd !< The start i-index of cell centers within the data domain
275 integer :: ied !< The end i-index of cell centers within the data domain
276 integer :: jsd !< The start j-index of cell centers within the data domain
277 integer :: jed !< The end j-index of cell centers within the data domain
278 real :: time_int !< The time interval for any fields
279 !! that are offered for averaging [s].
280 type(time_type) :: time_end !< The end time of the valid interval for any offered field.
281 logical :: ave_enabled = .false. !< True if averaging is enabled.
282
283 !>@{ The following are 3D and 2D axis groups defined for output. The names
284 !! indicate the horizontal (B, T, Cu, or Cv) and vertical (L, i, or 1) locations.
285 type(axes_grp) :: axesbl, axestl, axescul, axescvl
286 type(axes_grp) :: axesbi, axesti, axescui, axescvi
287 type(axes_grp) :: axesb1, axest1, axescu1, axescv1
288 !>@}
289 type(axes_grp) :: axeszi !< A 1-D z-space axis at interfaces
290 type(axes_grp) :: axeszl !< A 1-D z-space axis at layer centers
291 type(axes_grp) :: axesnull !< An axis group for scalars
292
293 ! Mask arrays for 2D diagnostics
294 real, dimension(:,:), pointer :: mask2dt => null() !< 2D mask array for cell-center points [nondim]
295 real, dimension(:,:), pointer :: mask2dbu => null() !< 2D mask array for cell-corner points [nondim]
296 real, dimension(:,:), pointer :: mask2dcu => null() !< 2D mask array for east-face points [nondim]
297 real, dimension(:,:), pointer :: mask2dcv => null() !< 2D mask array for north-face points [nondim]
298 !>@{ 3D mask arrays for diagnostics at layers (mask...L) and interfaces (mask...i) all [nondim]
299 real, dimension(:,:,:), pointer :: mask3dtl => null()
300 real, dimension(:,:,:), pointer :: mask3dbl => null()
301 real, dimension(:,:,:), pointer :: mask3dcul => null()
302 real, dimension(:,:,:), pointer :: mask3dcvl => null()
303 real, dimension(:,:,:), pointer :: mask3dti => null()
304 real, dimension(:,:,:), pointer :: mask3dbi => null()
305 real, dimension(:,:,:), pointer :: mask3dcui => null()
306 real, dimension(:,:,:), pointer :: mask3dcvi => null()
307
308 integer :: num_diag_dsamp_levels !< The number of downsampled levels requested in the parameters files (default 0)
309 integer, dimension(:), allocatable :: diag_dsamp_levels !< The downsample levels requested by diag registrations
310 type(diagcs_dsamp), dimension(:), allocatable :: dsamp !< An array of downsampling control containers
311 !! for each level of downsampling that is being used,
312 !! with a size determined at runtime via NUM_DIAG_DOWNSAMP_LEV
313 !>@}
314
315! Space for diagnostics is dynamically allocated as it is needed.
316! The chunk size is how much the array should grow on each new allocation.
317#define DIAG_ALLOC_CHUNK_SIZE 100
318 type(diag_type), dimension(:), allocatable :: diags !< The list of diagnostics
319 integer :: next_free_diag_id !< The next unused diagnostic ID
320
321 !> default missing value to be sent to ALL diagnostics registrations [various]
322 real :: missing_value = -1.0e34
323
324 !> Number of diagnostic vertical coordinates (remapped)
325 integer :: num_diag_coords
326 !> Control structure for each possible coordinate
327 type(diag_remap_ctrl), dimension(:), allocatable :: diag_remap_cs
328 type(diag_grid_storage) :: diag_grid_temp !< Stores the remapped diagnostic grid
329 logical :: diag_grid_overridden = .false. !< True if the diagnostic grids have been overriden
330
331 type(axes_grp), dimension(:), allocatable :: &
332 remap_axeszl, & !< The 1-D z-space cell-centered axis for remapping
333 remap_axeszi !< The 1-D z-space interface axis for remapping
334 !>@{ Axes used for remapping
335 type(axes_grp), dimension(:), allocatable :: remap_axestl, remap_axesbl, remap_axescul, remap_axescvl
336 type(axes_grp), dimension(:), allocatable :: remap_axesti, remap_axesbi, remap_axescui, remap_axescvi
337 !>@}
338
339 ! Pointer to H, G and T&S needed for remapping
340 real, dimension(:,:,:), pointer :: h => null() !< The thicknesses needed for remapping [H ~> m or kg m-2]
341 real, dimension(:,:,:), pointer :: t => null() !< The temperatures needed for remapping [C ~> degC]
342 real, dimension(:,:,:), pointer :: s => null() !< The salinities needed for remapping [S ~> ppt]
343 type(eos_type), pointer :: eqn_of_state => null() !< The equation of state type
344 type(thermo_var_ptrs), pointer :: tv => null() !< A structure with thermodynamic variables that are
345 !! used to convert thicknesses to vertical extents
346 type(ocean_grid_type), pointer :: g => null() !< The ocean grid type
347 type(verticalgrid_type), pointer :: gv => null() !< The model's vertical ocean grid
348 type(unit_scale_type), pointer :: us => null() !< A dimensional unit scaling type
349
350 !> The volume cell measure (special diagnostic) manager id
351 integer :: volume_cell_measure_dm_id = -1
352
353#if defined(DEBUG) || defined(__DO_SAFETY_CHECKS__)
354 ! Keep a copy of h so that we know whether it has changed [H ~> m or kg m-2]. If it has then
355 ! need the target grid for vertical remapping needs to have been updated.
356 real, dimension(:,:,:), allocatable :: h_old
357#endif
358
359 !> Number of checksum-only diagnostics
360 integer :: num_chksum_diags
361
362 integer, dimension(:,:), allocatable :: obc_u !< An array that indicates the presence and direction
363 !! of any open boundary conditions at u-points,
364 !! with a value of 0 for no OBC, 1 for an
365 !! Eastern OBC or -1 for a Western OBC
366 integer, dimension(:,:), allocatable :: obc_v !< An array that indicates the presence and direction
367 !! of any open boundary conditions at v-points,
368 !! with a value of 0 for no OBC, 1 for a Northern OBC
369 !! or -1 for a Southern OBC
370 real, dimension(:,:,:), allocatable :: h_begin !< Layer thicknesses at the beginning of the timestep used
371 !! for remapping of extensive variables [H ~> m or kg m-2]
372 real, dimension(:,:,:), allocatable :: dz_begin !< Layer vertical extents at the beginning of the timestep used
373 !! for remapping of extensive variables [Z ~> m]
374
375end type diag_ctrl
376
377!>@{ CPU clocks
378integer :: id_clock_diag_mediator, id_clock_diag_remap, id_clock_diag_grid_updates
379!>@}
380
381contains
382
383!> Sets up diagnostics axes
384subroutine set_axes_info(G, GV, US, param_file, diag_cs, set_vertical)
385 type(ocean_grid_type), intent(inout) :: g !< Ocean grid structure
386 type(verticalgrid_type), intent(in) :: gv !< ocean vertical grid structure
387 type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
388 type(param_file_type), intent(in) :: param_file !< Parameter file structure
389 type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
390 logical, optional, intent(in) :: set_vertical !< If true or missing, set up
391 !! vertical axes
392 ! Local variables
393 integer :: id_xq, id_yq, id_zl, id_zi, id_xh, id_yh, id_null
394 integer :: id_zl_native, id_zi_native
395 integer :: i, j, nz
396 real :: zlev(gv%ke) ! Numerical values for layer vertical coordinates, in unscaled units
397 ! that might be [m], [kg m-3] or [nondim], depending on the coordinate.
398 real :: zinter(gv%ke+1) ! Numerical values for interface vertical coordinates, in unscaled units
399 ! that might be [m], [kg m-3] or [nondim], depending on the coordinate.
400 logical :: set_vert
401 real, allocatable, dimension(:) :: iaxb, iax ! Index-based integer and half-integer i-axis labels [nondim]
402 real, allocatable, dimension(:) :: jaxb, jax ! Index-based integer and half-integer j-axis labels [nondim]
403
404 set_vert = .true. ; if (present(set_vertical)) set_vert = set_vertical
405
406 if (diag_cs%index_space_axes) then
407 allocate(iaxb(g%IsgB:g%IegB))
408 do i=g%IsgB,g%IegB
409 iaxb(i) = real(i)
410 enddo
411 allocate(iax(g%isg:g%ieg))
412 do i=g%isg,g%ieg
413 iax(i) = real(i)-0.5
414 enddo
415 allocate(jaxb(g%JsgB:g%JegB))
416 do j=g%JsgB,g%JegB
417 jaxb(j) = real(j)
418 enddo
419 allocate(jax(g%jsg:g%jeg))
420 do j=g%jsg,g%jeg
421 jax(j) = real(j)-0.5
422 enddo
423 endif
424
425 ! Horizontal axes for the native grids
426 if (diag_cs%index_space_axes) then
427 if (g%symmetric) then
428 id_xq = diag_axis_init('Iq', iaxb(g%IsgB:g%IegB), 'none', 'x', &
429 'Boundary (q) point grid-space longitude', g%Domain, position=east)
430 id_yq = diag_axis_init('Jq', jaxb(g%JsgB:g%JegB), 'none', 'y', &
431 'Boundary (q) point grid-space latitude', g%Domain, position=north)
432 else
433 id_xq = diag_axis_init('Iq', iaxb(g%isg:g%ieg), 'none', 'x', &
434 'Boundary (q) point grid-space longitude', g%Domain, position=east)
435 id_yq = diag_axis_init('Jq', jaxb(g%jsg:g%jeg), 'none', 'y', &
436 'Boundary (q) point grid-space latitude', g%Domain, position=north)
437 endif
438 id_xh = diag_axis_init('ih', iax(g%isg:g%ieg), 'none', 'x', &
439 'Tracer (h) point grid-space longitude', g%Domain)
440 id_yh = diag_axis_init('jh', jax(g%jsg:g%jeg), 'none', 'y', &
441 'Tracer (h) point grid-space latitude', g%Domain)
442 else
443 if (g%symmetric) then
444 id_xq = diag_axis_init('xq', g%gridLonB(g%IsgB:g%IegB), g%x_axis_units, 'x', &
445 'q point nominal longitude', g%Domain, position=east)
446 id_yq = diag_axis_init('yq', g%gridLatB(g%JsgB:g%JegB), g%y_axis_units, 'y', &
447 'q point nominal latitude', g%Domain, position=north)
448 else
449 id_xq = diag_axis_init('xq', g%gridLonB(g%isg:g%ieg), g%x_axis_units, 'x', &
450 'q point nominal longitude', g%Domain, position=east)
451 id_yq = diag_axis_init('yq', g%gridLatB(g%jsg:g%jeg), g%y_axis_units, 'y', &
452 'q point nominal latitude', g%Domain, position=north)
453 endif
454 id_xh = diag_axis_init('xh', g%gridLonT(g%isg:g%ieg), g%x_axis_units, 'x', &
455 'h point nominal longitude', g%Domain)
456 id_yh = diag_axis_init('yh', g%gridLatT(g%jsg:g%jeg), g%y_axis_units, 'y', &
457 'h point nominal latitude', g%Domain)
458 endif
459
460 if (set_vert) then
461 nz = gv%ke
462 zinter(1:nz+1) = gv%sInterface(1:nz+1)
463 zlev(1:nz) = gv%sLayer(1:nz)
464 id_zl = diag_axis_init('zl', zlev, trim(gv%zAxisUnits), 'z', &
465 'Layer '//trim(gv%zAxisLongName), direction=gv%direction)
466 id_zi = diag_axis_init('zi', zinter, trim(gv%zAxisUnits), 'z', &
467 'Interface '//trim(gv%zAxisLongName), direction=gv%direction)
468 else
469 id_zl = -1 ; id_zi = -1
470 endif
471 id_zl_native = id_zl ; id_zi_native = id_zi
472 ! Vertical axes for the interfaces and layers
473 call define_axes_group(diag_cs, (/ id_zi /), diag_cs%axesZi, &
474 v_cell_method='point', is_interface=.true.)
475 call define_axes_group(diag_cs, (/ id_zl /), diag_cs%axesZL, &
476 v_cell_method='mean', is_layer=.true.)
477
478 ! Axis groupings for the model layers
479 call define_axes_group(diag_cs, (/ id_xh, id_yh, id_zl /), diag_cs%axesTL, &
480 x_cell_method='mean', y_cell_method='mean', v_cell_method='mean', &
481 is_h_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
482 call define_axes_group(diag_cs, (/ id_xq, id_yq, id_zl /), diag_cs%axesBL, &
483 x_cell_method='point', y_cell_method='point', v_cell_method='mean', &
484 is_q_point=.true., is_layer=.true.)
485 call define_axes_group(diag_cs, (/ id_xq, id_yh, id_zl /), diag_cs%axesCuL, &
486 x_cell_method='point', y_cell_method='mean', v_cell_method='mean', &
487 is_u_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
488 call define_axes_group(diag_cs, (/ id_xh, id_yq, id_zl /), diag_cs%axesCvL, &
489 x_cell_method='mean', y_cell_method='point', v_cell_method='mean', &
490 is_v_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
491
492 ! Axis groupings for the model interfaces
493 call define_axes_group(diag_cs, (/ id_xh, id_yh, id_zi /), diag_cs%axesTi, &
494 x_cell_method='mean', y_cell_method='mean', v_cell_method='point', &
495 is_h_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
496 call define_axes_group(diag_cs, (/ id_xq, id_yq, id_zi /), diag_cs%axesBi, &
497 x_cell_method='point', y_cell_method='point', v_cell_method='point', &
498 is_q_point=.true., is_interface=.true.)
499 call define_axes_group(diag_cs, (/ id_xq, id_yh, id_zi /), diag_cs%axesCui, &
500 x_cell_method='point', y_cell_method='mean', v_cell_method='point', &
501 is_u_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
502 call define_axes_group(diag_cs, (/ id_xh, id_yq, id_zi /), diag_cs%axesCvi, &
503 x_cell_method='mean', y_cell_method='point', v_cell_method='point', &
504 is_v_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
505
506 ! Axis groupings for 2-D arrays
507 call define_axes_group(diag_cs, (/ id_xh, id_yh /), diag_cs%axesT1, &
508 x_cell_method='mean', y_cell_method='mean', is_h_point=.true.)
509 call define_axes_group(diag_cs, (/ id_xq, id_yq /), diag_cs%axesB1, &
510 x_cell_method='point', y_cell_method='point', is_q_point=.true.)
511 call define_axes_group(diag_cs, (/ id_xq, id_yh /), diag_cs%axesCu1, &
512 x_cell_method='point', y_cell_method='mean', is_u_point=.true.)
513 call define_axes_group(diag_cs, (/ id_xh, id_yq /), diag_cs%axesCv1, &
514 x_cell_method='mean', y_cell_method='point', is_v_point=.true.)
515
516 ! Define array extents for all piecemeal buffers
517 call set_piecemeal_extents(diag_cs)
518
519 ! Axis group for special null axis for scalars from diag manager.
520 id_null = diag_axis_init('scalar_axis', (/0./), 'none', 'N', 'none', null_axis=.true.)
521 call define_axes_group(diag_cs, (/ id_null /), diag_cs%axesNull)
522
523 ! Set axis groups for non-native, non-downsampled grids
524 if (diag_cs%num_diag_coords>0) then
525 allocate(diag_cs%remap_axesZL(diag_cs%num_diag_coords))
526 allocate(diag_cs%remap_axesTL(diag_cs%num_diag_coords))
527 allocate(diag_cs%remap_axesBL(diag_cs%num_diag_coords))
528 allocate(diag_cs%remap_axesCuL(diag_cs%num_diag_coords))
529 allocate(diag_cs%remap_axesCvL(diag_cs%num_diag_coords))
530 allocate(diag_cs%remap_axesZi(diag_cs%num_diag_coords))
531 allocate(diag_cs%remap_axesTi(diag_cs%num_diag_coords))
532 allocate(diag_cs%remap_axesBi(diag_cs%num_diag_coords))
533 allocate(diag_cs%remap_axesCui(diag_cs%num_diag_coords))
534 allocate(diag_cs%remap_axesCvi(diag_cs%num_diag_coords))
535 endif
536
537 do i=1, diag_cs%num_diag_coords
538 ! For each possible diagnostic coordinate
539 call diag_remap_configure_axes(diag_cs%diag_remap_cs(i), g, gv, us, param_file)
540
541 ! Allocate these arrays since the size of the diagnostic array is now known
542 allocate(diag_cs%diag_remap_cs(i)%h(g%isd:g%ied,g%jsd:g%jed, diag_cs%diag_remap_cs(i)%nz))
543 allocate(diag_cs%diag_remap_cs(i)%h_extensive(g%isd:g%ied,g%jsd:g%jed, diag_cs%diag_remap_cs(i)%nz))
544
545 ! This vertical coordinate has been configured so can be used.
546 if (diag_remap_axes_configured(diag_cs%diag_remap_cs(i))) then
547
548 ! This fetches the 1D-axis id for layers and interfaces and overwrite
549 ! id_zl and id_zi from above. It also returns the number of layers.
550 call diag_remap_get_axes_info(diag_cs%diag_remap_cs(i), nz, id_zl, id_zi)
551
552 ! Axes for z layers
553 call define_axes_group(diag_cs, (/ id_zl /), diag_cs%remap_axesZL(i), &
554 nz=nz, vertical_coordinate_number=i, &
555 v_cell_method='mean', &
556 is_h_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true.)
557 call define_axes_group(diag_cs, (/ id_xh, id_yh, id_zl /), diag_cs%remap_axesTL(i), &
558 nz=nz, vertical_coordinate_number=i, &
559 x_cell_method='mean', y_cell_method='mean', v_cell_method='mean', &
560 is_h_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
561 xyave_axes=diag_cs%remap_axesZL(i))
562
563 !! \note Remapping for B points is not yet implemented so needs_remapping is not
564 !! provided for remap_axesBL
565 call define_axes_group(diag_cs, (/ id_xq, id_yq, id_zl /), diag_cs%remap_axesBL(i), &
566 nz=nz, vertical_coordinate_number=i, &
567 x_cell_method='point', y_cell_method='point', v_cell_method='mean', &
568 is_q_point=.true., is_layer=.true., is_native=.false.)
569
570 call define_axes_group(diag_cs, (/ id_xq, id_yh, id_zl /), diag_cs%remap_axesCuL(i), &
571 nz=nz, vertical_coordinate_number=i, &
572 x_cell_method='point', y_cell_method='mean', v_cell_method='mean', &
573 is_u_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
574 xyave_axes=diag_cs%remap_axesZL(i))
575
576 call define_axes_group(diag_cs, (/ id_xh, id_yq, id_zl /), diag_cs%remap_axesCvL(i), &
577 nz=nz, vertical_coordinate_number=i, &
578 x_cell_method='mean', y_cell_method='point', v_cell_method='mean', &
579 is_v_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
580 xyave_axes=diag_cs%remap_axesZL(i))
581
582 ! Axes for z interfaces
583 call define_axes_group(diag_cs, (/ id_zi /), diag_cs%remap_axesZi(i), &
584 nz=nz, vertical_coordinate_number=i, &
585 v_cell_method='point', &
586 is_h_point=.true., is_interface=.true., is_native=.false., needs_interpolating=.true.)
587 call define_axes_group(diag_cs, (/ id_xh, id_yh, id_zi /), diag_cs%remap_axesTi(i), &
588 nz=nz, vertical_coordinate_number=i, &
589 x_cell_method='mean', y_cell_method='mean', v_cell_method='point', &
590 is_h_point=.true., is_interface=.true., is_native=.false., needs_interpolating=.true., &
591 xyave_axes=diag_cs%remap_axesZi(i))
592
593 !! \note Remapping for B points is not yet implemented so needs_remapping is not provided for remap_axesBi
594 call define_axes_group(diag_cs, (/ id_xq, id_yq, id_zi /), diag_cs%remap_axesBi(i), &
595 nz=nz, vertical_coordinate_number=i, &
596 x_cell_method='point', y_cell_method='point', v_cell_method='point', &
597 is_q_point=.true., is_interface=.true., is_native=.false.)
598
599 call define_axes_group(diag_cs, (/ id_xq, id_yh, id_zi /), diag_cs%remap_axesCui(i), &
600 nz=nz, vertical_coordinate_number=i, &
601 x_cell_method='point', y_cell_method='mean', v_cell_method='point', &
602 is_u_point=.true., is_interface=.true., is_native=.false., &
603 needs_interpolating=.true., xyave_axes=diag_cs%remap_axesZi(i))
604
605 call define_axes_group(diag_cs, (/ id_xh, id_yq, id_zi /), diag_cs%remap_axesCvi(i), &
606 nz=nz, vertical_coordinate_number=i, &
607 x_cell_method='mean', y_cell_method='point', v_cell_method='point', &
608 is_v_point=.true., is_interface=.true., is_native=.false., &
609 needs_interpolating=.true., xyave_axes=diag_cs%remap_axesZi(i))
610 endif
611 enddo
612
613 if (diag_cs%index_space_axes) then
614 deallocate(iaxb, iax, jaxb, jax)
615 endif
616 ! Define the downsampled axes
617 call set_axes_info_dsamp(g, gv, param_file, diag_cs, id_zl_native, id_zi_native)
618
619 call diag_grid_storage_init(diag_cs%diag_grid_temp, g, gv, diag_cs)
620
621end subroutine set_axes_info
622
623subroutine set_axes_info_dsamp(G, GV, param_file, diag_cs, id_zl_native, id_zi_native)
624 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
625 type(verticalgrid_type), intent(in) :: GV !< ocean vertical grid structure
626 type(param_file_type), intent(in) :: param_file !< Parameter file structure
627 type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
628 integer, intent(in) :: id_zl_native !< ID of native layers
629 integer, intent(in) :: id_zi_native !< ID of native interfaces
630
631 ! Local variables
632 integer :: id_xq, id_yq, id_zl, id_zi, id_xh, id_yh
633 integer :: i, j, nz, dl, dlfac
634 real, dimension(:), pointer :: gridLonT_dsamp =>null() ! The longitude of downsampled T points for labeling
635 ! the output axes, often in units of [degrees_N] or
636 ! [km] or [m] or [gridpoints].
637 real, dimension(:), pointer :: gridLatT_dsamp =>null() ! The latitude of downsampled T points for labeling
638 ! the output axes, often in units of [degrees_N] or
639 ! [km] or [m] or [gridpoints].
640 real, dimension(:), pointer :: gridLonB_dsamp =>null() ! The longitude of downsampled B points for labeling
641 ! the output axes, often in units of [degrees_N] or
642 ! [km] or [m] or [gridpoints].
643 real, dimension(:), pointer :: gridLatB_dsamp =>null() ! The latitude of downsampled B points for labeling
644 ! the output axes, often in units of [degrees_N] or
645 ! [km] or [m] or [gridpoints].
646
647
648 ! Axes group for native downsampled diagnostics
649 !Loop over the downsampling levels requested in parameters.
650 do dl=1, diag_cs%num_diag_dsamp_levels
651 dlfac = diag_cs%diag_dsamp_levels(dl) ! The actual downsampling factor for this level
652 if (g%symmetric) then
653 allocate(gridlonb_dsamp(diag_cs%dsamp(dl)%isgB:diag_cs%dsamp(dl)%iegB))
654 allocate(gridlatb_dsamp(diag_cs%dsamp(dl)%jsgB:diag_cs%dsamp(dl)%jegB))
655 do i=diag_cs%dsamp(dl)%isgB,diag_cs%dsamp(dl)%iegB ; gridlonb_dsamp(i) = g%gridLonB(g%isgB+dlfac*i) ; enddo
656 do j=diag_cs%dsamp(dl)%jsgB,diag_cs%dsamp(dl)%jegB ; gridlatb_dsamp(j) = g%gridLatB(g%jsgB+dlfac*j) ; enddo
657 id_xq = diag_axis_init('xq', gridlonb_dsamp, g%x_axis_units, 'x', &
658 'q point nominal longitude', g%Domain, coarsen=dl)
659 id_yq = diag_axis_init('yq', gridlatb_dsamp, g%y_axis_units, 'y', &
660 'q point nominal latitude', g%Domain, coarsen=dl)
661 deallocate(gridlonb_dsamp, gridlatb_dsamp)
662 else
663 allocate(gridlonb_dsamp(diag_cs%dsamp(dl)%isg:diag_cs%dsamp(dl)%ieg))
664 allocate(gridlatb_dsamp(diag_cs%dsamp(dl)%jsg:diag_cs%dsamp(dl)%jeg))
665 do i=diag_cs%dsamp(dl)%isg,diag_cs%dsamp(dl)%ieg ; gridlonb_dsamp(i) = g%gridLonB(g%isg+dlfac*i-2) ; enddo
666 do j=diag_cs%dsamp(dl)%jsg,diag_cs%dsamp(dl)%jeg ; gridlatb_dsamp(j) = g%gridLatB(g%jsg+dlfac*j-2) ; enddo
667 id_xq = diag_axis_init('xq', gridlonb_dsamp, g%x_axis_units, 'x', &
668 'q point nominal longitude', g%Domain, coarsen=dl)
669 id_yq = diag_axis_init('yq', gridlatb_dsamp, g%y_axis_units, 'y', &
670 'q point nominal latitude', g%Domain, coarsen=dl)
671 deallocate(gridlonb_dsamp, gridlatb_dsamp)
672 endif
673
674 allocate(gridlont_dsamp(diag_cs%dsamp(dl)%isg:diag_cs%dsamp(dl)%ieg))
675 allocate(gridlatt_dsamp(diag_cs%dsamp(dl)%jsg:diag_cs%dsamp(dl)%jeg))
676 do i=diag_cs%dsamp(dl)%isg,diag_cs%dsamp(dl)%ieg ; gridlont_dsamp(i) = g%gridLonT(g%isg+dlfac*i-2) ; enddo
677 do j=diag_cs%dsamp(dl)%jsg,diag_cs%dsamp(dl)%jeg ; gridlatt_dsamp(j) = g%gridLatT(g%jsg+dlfac*j-2) ; enddo
678 id_xh = diag_axis_init('xh', gridlont_dsamp, g%x_axis_units, 'x', &
679 'h point nominal longitude', g%Domain, coarsen=dl)
680 id_yh = diag_axis_init('yh', gridlatt_dsamp, g%y_axis_units, 'y', &
681 'h point nominal latitude', g%Domain, coarsen=dl)
682
683 deallocate(gridlont_dsamp, gridlatt_dsamp)
684
685 ! Axis groupings for the model layers
686 id_zl = id_zl_native ; id_zi = id_zi_native
687
688 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yh, id_zl /), diag_cs%dsamp(dl)%axesTL, dl, &
689 x_cell_method='mean', y_cell_method='mean', v_cell_method='mean', &
690 is_h_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
691 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yq, id_zl /), diag_cs%dsamp(dl)%axesBL, dl, &
692 x_cell_method='point', y_cell_method='point', v_cell_method='mean', &
693 is_q_point=.true., is_layer=.true.)
694 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yh, id_zl /), diag_cs%dsamp(dl)%axesCuL, dl, &
695 x_cell_method='point', y_cell_method='mean', v_cell_method='mean', &
696 is_u_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
697 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yq, id_zl /), diag_cs%dsamp(dl)%axesCvL, dl, &
698 x_cell_method='mean', y_cell_method='point', v_cell_method='mean', &
699 is_v_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
700
701 ! Axis groupings for the model interfaces
702 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yh, id_zi /), diag_cs%dsamp(dl)%axesTi, dl, &
703 x_cell_method='mean', y_cell_method='mean', v_cell_method='point', &
704 is_h_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
705 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yq, id_zi /), diag_cs%dsamp(dl)%axesBi, dl, &
706 x_cell_method='point', y_cell_method='point', v_cell_method='point', &
707 is_q_point=.true., is_interface=.true.)
708 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yh, id_zi /), diag_cs%dsamp(dl)%axesCui, dl, &
709 x_cell_method='point', y_cell_method='mean', v_cell_method='point', &
710 is_u_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
711 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yq, id_zi /), diag_cs%dsamp(dl)%axesCvi, dl, &
712 x_cell_method='mean', y_cell_method='point', v_cell_method='point', &
713 is_v_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
714
715 ! Axis groupings for 2-D arrays
716 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yh /), diag_cs%dsamp(dl)%axesT1, dl, &
717 x_cell_method='mean', y_cell_method='mean', is_h_point=.true.)
718 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yq /), diag_cs%dsamp(dl)%axesB1, dl, &
719 x_cell_method='point', y_cell_method='point', is_q_point=.true.)
720 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yh /), diag_cs%dsamp(dl)%axesCu1, dl, &
721 x_cell_method='point', y_cell_method='mean', is_u_point=.true.)
722 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yq /), diag_cs%dsamp(dl)%axesCv1, dl, &
723 x_cell_method='mean', y_cell_method='point', is_v_point=.true.)
724
725 ! Axis groupings with a non-native vertical coordinate
726 if (diag_cs%num_diag_coords>0) then
727 allocate(diag_cs%dsamp(dl)%remap_axesTL(diag_cs%num_diag_coords))
728 allocate(diag_cs%dsamp(dl)%remap_axesBL(diag_cs%num_diag_coords))
729 allocate(diag_cs%dsamp(dl)%remap_axesCuL(diag_cs%num_diag_coords))
730 allocate(diag_cs%dsamp(dl)%remap_axesCvL(diag_cs%num_diag_coords))
731 allocate(diag_cs%dsamp(dl)%remap_axesTi(diag_cs%num_diag_coords))
732 allocate(diag_cs%dsamp(dl)%remap_axesBi(diag_cs%num_diag_coords))
733 allocate(diag_cs%dsamp(dl)%remap_axesCui(diag_cs%num_diag_coords))
734 allocate(diag_cs%dsamp(dl)%remap_axesCvi(diag_cs%num_diag_coords))
735 endif
736
737 do i=1, diag_cs%num_diag_coords
738 ! For each possible diagnostic coordinate
739 ! call diag_remap_configure_axes(diag_cs%diag_remap_cs(i), G, GV, param_file)
740
741 ! This vertical coordinate has been configured so can be used.
742 if (diag_remap_axes_configured(diag_cs%diag_remap_cs(i))) then
743
744 ! This fetches the 1D-axis id for layers and interfaces and overwrite
745 ! id_zl and id_zi from above. It also returns the number of layers.
746 call diag_remap_get_axes_info(diag_cs%diag_remap_cs(i), nz, id_zl, id_zi)
747
748 ! Axes for z layers
749 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yh, id_zl /), diag_cs%dsamp(dl)%remap_axesTL(i), dl, &
750 nz=nz, vertical_coordinate_number=i, &
751 x_cell_method='mean', y_cell_method='mean', v_cell_method='mean', &
752 is_h_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
753 xyave_axes=diag_cs%remap_axesZL(i))
754
755 !! \note Remapping for B points is not yet implemented so needs_remapping is not
756 !! provided for remap_axesBL
757 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yq, id_zl /), diag_cs%dsamp(dl)%remap_axesBL(i), dl, &
758 nz=nz, vertical_coordinate_number=i, &
759 x_cell_method='point', y_cell_method='point', v_cell_method='mean', &
760 is_q_point=.true., is_layer=.true., is_native=.false.)
761
762 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yh, id_zl /), diag_cs%dsamp(dl)%remap_axesCuL(i), dl, &
763 nz=nz, vertical_coordinate_number=i, &
764 x_cell_method='point', y_cell_method='mean', v_cell_method='mean', &
765 is_u_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
766 xyave_axes=diag_cs%remap_axesZL(i))
767
768 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yq, id_zl /), diag_cs%dsamp(dl)%remap_axesCvL(i), dl, &
769 nz=nz, vertical_coordinate_number=i, &
770 x_cell_method='mean', y_cell_method='point', v_cell_method='mean', &
771 is_v_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
772 xyave_axes=diag_cs%remap_axesZL(i))
773
774 ! Axes for z interfaces
775 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yh, id_zi /), diag_cs%dsamp(dl)%remap_axesTi(i), dl, &
776 nz=nz, vertical_coordinate_number=i, &
777 x_cell_method='mean', y_cell_method='mean', v_cell_method='point', &
778 is_h_point=.true., is_interface=.true., is_native=.false., needs_interpolating=.true., &
779 xyave_axes=diag_cs%remap_axesZi(i))
780
781 !! \note Remapping for B points is not yet implemented so needs_remapping is not provided for remap_axesBi
782 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yq, id_zi /), diag_cs%dsamp(dl)%remap_axesBi(i), dl, &
783 nz=nz, vertical_coordinate_number=i, &
784 x_cell_method='point', y_cell_method='point', v_cell_method='point', &
785 is_q_point=.true., is_interface=.true., is_native=.false.)
786
787 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yh, id_zi /), diag_cs%dsamp(dl)%remap_axesCui(i), dl, &
788 nz=nz, vertical_coordinate_number=i, &
789 x_cell_method='point', y_cell_method='mean', v_cell_method='point', &
790 is_u_point=.true., is_interface=.true., is_native=.false., &
791 needs_interpolating=.true., xyave_axes=diag_cs%remap_axesZi(i))
792
793 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yq, id_zi /), diag_cs%dsamp(dl)%remap_axesCvi(i), dl, &
794 nz=nz, vertical_coordinate_number=i, &
795 x_cell_method='mean', y_cell_method='point', v_cell_method='point', &
796 is_v_point=.true., is_interface=.true., is_native=.false., &
797 needs_interpolating=.true., xyave_axes=diag_cs%remap_axesZi(i))
798 endif
799 enddo
800 enddo
801
802end subroutine set_axes_info_dsamp
803
804
805!> set_masks_for_axes sets up the 2d and 3d masks for diagnostics using the current grid
806!! recorded after calling diag_update_remap_grids()
807subroutine set_masks_for_axes(G, diag_cs)
808 type(ocean_grid_type), target, intent(in) :: g !< The ocean grid type.
809 type(diag_ctrl), pointer :: diag_cs !< A pointer to a type with many variables
810 !! used for diagnostics
811 ! Local variables
812 integer :: c, nk, i, j, k
813 type(axes_grp), pointer :: axes => null(), h_axes => null() ! Current axes, for convenience
814
815 do c=1, diag_cs%num_diag_coords
816 ! This vertical coordinate has been configured so can be used.
817 if (diag_remap_axes_configured(diag_cs%diag_remap_cs(c))) then
818
819 ! Level/layer h-points in diagnostic coordinate
820 axes => diag_cs%remap_axesTL(c)
821 nk = axes%nz
822 allocate( axes%mask3d(g%isd:g%ied,g%jsd:g%jed,nk), source=0. )
823 call diag_remap_calc_hmask(diag_cs%diag_remap_cs(c), g, axes%mask3d)
824
825 h_axes => diag_cs%remap_axesTL(c) ! Use the h-point masks to generate the u-, v- and q- masks
826
827 ! Level/layer u-points in diagnostic coordinate
828 axes => diag_cs%remap_axesCuL(c)
829 call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at u-layers')
830 call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
831 allocate( axes%mask3d(g%IsdB:g%IedB,g%jsd:g%jed,nk), source=0. )
832 do k = 1, nk ; do j=g%jsc,g%jec ; do i=g%isc-1,g%iec
833 if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i+1,j,k) > 0.) axes%mask3d(i,j,k) = 1.
834 enddo ; enddo ; enddo
835
836 ! Level/layer v-points in diagnostic coordinate
837 axes => diag_cs%remap_axesCvL(c)
838 call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at v-layers')
839 call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
840 allocate( axes%mask3d(g%isd:g%ied,g%JsdB:g%JedB,nk), source=0. )
841 do k = 1, nk ; do j=g%jsc-1,g%jec ; do i=g%isc,g%iec
842 if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i,j+1,k) > 0.) axes%mask3d(i,j,k) = 1.
843 enddo ; enddo ; enddo
844
845 ! Level/layer q-points in diagnostic coordinate
846 axes => diag_cs%remap_axesBL(c)
847 call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at q-layers')
848 call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
849 allocate( axes%mask3d(g%IsdB:g%IedB,g%JsdB:g%JedB,nk), source=0. )
850 do k = 1, nk ; do j=g%jsc-1,g%jec ; do i=g%isc-1,g%iec
851 if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i+1,j+1,k) + &
852 h_axes%mask3d(i+1,j,k) + h_axes%mask3d(i,j+1,k) > 0.) axes%mask3d(i,j,k) = 1.
853 enddo ; enddo ; enddo
854
855 ! Interface h-points in diagnostic coordinate (w-point)
856 axes => diag_cs%remap_axesTi(c)
857 call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at h-interfaces')
858 call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
859 allocate( axes%mask3d(g%isd:g%ied,g%jsd:g%jed,nk+1), source=0. )
860 do j=g%jsc-1,g%jec+1 ; do i=g%isc-1,g%iec+1
861 if (h_axes%mask3d(i,j,1) > 0.) axes%mask3d(i,j,1) = 1.
862 do k = 2, nk
863 if (h_axes%mask3d(i,j,k-1) + h_axes%mask3d(i,j,k) > 0.) axes%mask3d(i,j,k) = 1.
864 enddo
865 if (h_axes%mask3d(i,j,nk) > 0.) axes%mask3d(i,j,nk+1) = 1.
866 enddo ; enddo
867
868 h_axes => diag_cs%remap_axesTi(c) ! Use the w-point masks to generate the u-, v- and q- masks
869
870 ! Interface u-points in diagnostic coordinate
871 axes => diag_cs%remap_axesCui(c)
872 call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at u-interfaces')
873 call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
874 allocate( axes%mask3d(g%IsdB:g%IedB,g%jsd:g%jed,nk+1), source=0. )
875 do k = 1, nk+1 ; do j=g%jsc,g%jec ; do i=g%isc-1,g%iec
876 if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i+1,j,k) > 0.) axes%mask3d(i,j,k) = 1.
877 enddo ; enddo ; enddo
878
879 ! Interface v-points in diagnostic coordinate
880 axes => diag_cs%remap_axesCvi(c)
881 call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at v-interfaces')
882 call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
883 allocate( axes%mask3d(g%isd:g%ied,g%JsdB:g%JedB,nk+1), source=0. )
884 do k = 1, nk+1 ; do j=g%jsc-1,g%jec ; do i=g%isc,g%iec
885 if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i,j+1,k) > 0.) axes%mask3d(i,j,k) = 1.
886 enddo ; enddo ; enddo
887
888 ! Interface q-points in diagnostic coordinate
889 axes => diag_cs%remap_axesBi(c)
890 call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at q-interfaces')
891 call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
892 allocate( axes%mask3d(g%IsdB:g%IedB,g%JsdB:g%JedB,nk+1), source=0. )
893 do k = 1, nk ; do j=g%jsc-1,g%jec ; do i=g%isc-1,g%iec
894 if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i+1,j+1,k) + &
895 h_axes%mask3d(i+1,j,k) + h_axes%mask3d(i,j+1,k) > 0.) axes%mask3d(i,j,k) = 1.
896 enddo ; enddo ; enddo
897 endif
898 enddo
899
900 ! Allocate and initialize the downsampled masks for the axes
901 call set_masks_for_axes_dsamp(g, diag_cs)
902
903end subroutine set_masks_for_axes
904
905subroutine set_masks_for_axes_dsamp(G, diag_cs)
906 type(ocean_grid_type), target, intent(in) :: G !< The ocean grid type.
907 type(diag_ctrl), pointer :: diag_cs !< A pointer to a type with many variables
908 !! used for diagnostics
909 ! Local variables
910 integer :: c, dl, dlfac
911 type(axes_grp), pointer :: axes => null() ! Current axes, for convenience
912
913 ! Each downsampled axis needs both downsampled and non-downsampled masks.
914 ! The downsampled mask is needed for sending out the diagnostics output via diag_manager.
915 ! The non-downsampled mask is needed for downsampling the diagnostics field.
916 do dl=1, diag_cs%num_diag_dsamp_levels
917 dlfac = diag_cs%diag_dsamp_levels(dl) ! The actual downsampling factor for this level
918 do c=1, diag_cs%num_diag_coords
919 ! Level/layer h-points in diagnostic coordinate
920 axes => diag_cs%remap_axesTL(c)
921 call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesTL(c)%dsamp(dl)%mask3d, &
922 dlfac, xyz_method(axes), g%isc, g%jsc, g%isd, g%jsd, &
923 g%HId(dl)%isc, g%HId(dl)%iec, g%HId(dl)%jsc, g%HId(dl)%jec, g%HId(dl)%isd, g%HId(dl)%ied, &
924 g%HId(dl)%jsd, g%HId(dl)%jed)
925 diag_cs%dsamp(dl)%remap_axesTL(c)%mask3d => axes%mask3d ! Set a pointer to the non-downsampled mask
926 ! Level/layer u-points in diagnostic coordinate
927 axes => diag_cs%remap_axesCuL(c)
928 call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCuL(c)%dsamp(dl)%mask3d, &
929 dlfac, xyz_method(axes), g%IscB, g%jsc, g%IsdB, g%jsd, &
930 g%HId(dl)%IscB, g%HId(dl)%IecB, g%HId(dl)%jsc, g%HId(dl)%jec, g%HId(dl)%IsdB, g%HId(dl)%IedB, &
931 g%HId(dl)%jsd, g%HId(dl)%jed)
932 diag_cs%dsamp(dl)%remap_axesCul(c)%mask3d => axes%mask3d ! Set a pointer to the non-downsampled mask
933 ! Level/layer v-points in diagnostic coordinate
934 axes => diag_cs%remap_axesCvL(c)
935 call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCvL(c)%dsamp(dl)%mask3d, &
936 dlfac, xyz_method(axes), g%isc, g%JscB, g%isd, g%JsdB, &
937 g%HId(dl)%isc, g%HId(dl)%iec, g%HId(dl)%JscB, g%HId(dl)%JecB, g%HId(dl)%isd, g%HId(dl)%ied, &
938 g%HId(dl)%JsdB, g%HId(dl)%JedB)
939 diag_cs%dsamp(dl)%remap_axesCvL(c)%mask3d => axes%mask3d ! Set a pointer to the non-downsampled mask
940 ! Level/layer q-points in diagnostic coordinate
941 axes => diag_cs%remap_axesBL(c)
942 call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesBL(c)%dsamp(dl)%mask3d, &
943 dlfac, xyz_method(axes), g%IscB, g%JscB, g%IsdB, g%JsdB, &
944 g%HId(dl)%IscB, g%HId(dl)%IecB, g%HId(dl)%JscB, g%HId(dl)%JecB, g%HId(dl)%IsdB, g%HId(dl)%IedB, &
945 g%HId(dl)%JsdB, g%HId(dl)%JedB)
946 diag_cs%dsamp(dl)%remap_axesBL(c)%mask3d => axes%mask3d ! Set a pointer to the non-downsampled mask
947 ! Interface h-points in diagnostic coordinate (w-point)
948 axes => diag_cs%remap_axesTi(c)
949 call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesTi(c)%dsamp(dl)%mask3d, &
950 dlfac, xyz_method(axes), g%isc, g%jsc, g%isd, g%jsd, &
951 g%HId(dl)%isc, g%HId(dl)%iec, g%HId(dl)%jsc, g%HId(dl)%jec, g%HId(dl)%isd, g%HId(dl)%ied, &
952 g%HId(dl)%jsd, g%HId(dl)%jed)
953 diag_cs%dsamp(dl)%remap_axesTi(c)%mask3d => axes%mask3d ! Set a pointer to the non-downsampled mask
954 ! Interface u-points in diagnostic coordinate
955 axes => diag_cs%remap_axesCui(c)
956 call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCui(c)%dsamp(dl)%mask3d, &
957 dlfac, xyz_method(axes), g%IscB, g%jsc, g%IsdB, g%jsd, &
958 g%HId(dl)%IscB, g%HId(dl)%IecB, g%HId(dl)%jsc, g%HId(dl)%jec, g%HId(dl)%IsdB, g%HId(dl)%IedB, &
959 g%HId(dl)%jsd, g%HId(dl)%jed)
960 diag_cs%dsamp(dl)%remap_axesCui(c)%mask3d => axes%mask3d ! Set a pointer to the non-downsampled mask
961 ! Interface v-points in diagnostic coordinate
962 axes => diag_cs%remap_axesCvi(c)
963 call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCvi(c)%dsamp(dl)%mask3d, &
964 dlfac, xyz_method(axes), g%isc, g%JscB, g%isd, g%JsdB, &
965 g%HId(dl)%isc, g%HId(dl)%iec, g%HId(dl)%JscB, g%HId(dl)%JecB, g%HId(dl)%isd, g%HId(dl)%ied, &
966 g%HId(dl)%JsdB, g%HId(dl)%JedB)
967 diag_cs%dsamp(dl)%remap_axesCvi(c)%mask3d => axes%mask3d ! Set a pointer to the non-downsampled mask
968 ! Interface q-points in diagnostic coordinate
969 axes => diag_cs%remap_axesBi(c)
970 call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesBi(c)%dsamp(dl)%mask3d, &
971 dlfac, xyz_method(axes), g%IscB, g%JscB, g%IsdB, g%JsdB, &
972 g%HId(dl)%IscB, g%HId(dl)%IecB, g%HId(dl)%JscB, g%HId(dl)%JecB, g%HId(dl)%IsdB, g%HId(dl)%IedB, &
973 g%HId(dl)%JsdB, g%HId(dl)%JedB)
974 diag_cs%dsamp(dl)%remap_axesBi(c)%mask3d => axes%mask3d ! Set a pointer to the non-downsampled mask
975 enddo
976 enddo
977end subroutine set_masks_for_axes_dsamp
978
979!> Attaches the id of cell areas to axes groups for use with cell_measures
980subroutine diag_register_area_ids(diag_cs, id_area_t, id_area_q)
981 type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
982 integer, optional, intent(in) :: id_area_t !< Diag_mediator id for area of h-cells
983 integer, optional, intent(in) :: id_area_q !< Diag_mediator id for area of q-cells
984 ! Local variables
985 integer :: fms_id, i
986 if (present(id_area_t)) then
987 fms_id = diag_cs%diags(id_area_t)%fms_diag_id
988 diag_cs%axesT1%id_area = fms_id
989 diag_cs%axesTi%id_area = fms_id
990 diag_cs%axesTL%id_area = fms_id
991 do i=1, diag_cs%num_diag_coords
992 diag_cs%remap_axesTL(i)%id_area = fms_id
993 diag_cs%remap_axesTi(i)%id_area = fms_id
994 enddo
995 endif
996 if (present(id_area_q)) then
997 fms_id = diag_cs%diags(id_area_q)%fms_diag_id
998 diag_cs%axesB1%id_area = fms_id
999 diag_cs%axesBi%id_area = fms_id
1000 diag_cs%axesBL%id_area = fms_id
1001 do i=1, diag_cs%num_diag_coords
1002 diag_cs%remap_axesBL(i)%id_area = fms_id
1003 diag_cs%remap_axesBi(i)%id_area = fms_id
1004 enddo
1005 endif
1006end subroutine diag_register_area_ids
1007
1008!> Sets a handle inside diagnostics mediator to associate 3d cell measures
1009subroutine register_cell_measure(G, diag, Time)
1010 type(ocean_grid_type), intent(in) :: g !< Ocean grid structure
1011 type(diag_ctrl), target, intent(inout) :: diag !< Regulates diagnostic output
1012 type(time_type), intent(in) :: time !< Model time
1013 ! Local variables
1014 integer :: id
1015 id = register_diag_field('ocean_model', 'volcello', diag%axesTL, &
1016 time, 'Ocean grid-cell volume', units='m3', conversion=1.0, &
1017 standard_name='ocean_volume', v_extensive=.true., &
1018 x_cell_method='sum', y_cell_method='sum')
1020
1021end subroutine register_cell_measure
1022
1023!> Attaches the id of cell volumes to axes groups for use with cell_measures
1024subroutine diag_associate_volume_cell_measure(diag_cs, id_h_volume)
1025 type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
1026 integer, intent(in) :: id_h_volume !< Diag_manager id for volume of h-cells
1027 ! Local variables
1028 type(diag_type), pointer :: tmp => null()
1029
1030 if (id_h_volume<=0) return ! Do nothing
1031 diag_cs%volume_cell_measure_dm_id = id_h_volume ! Record for diag_get_volume_cell_measure_dm_id()
1032
1033 ! Set the cell measure for this axes group to the FMS id in this coordinate system
1034 diag_cs%diags(id_h_volume)%axes%id_volume = diag_cs%diags(id_h_volume)%fms_diag_id
1035
1036 tmp => diag_cs%diags(id_h_volume)%next ! First item in the list, if any
1037 do while (associated(tmp))
1038 ! Set the cell measure for this axes group to the FMS id in this coordinate system
1039 tmp%axes%id_volume = tmp%fms_diag_id
1040 tmp => tmp%next ! Move to next axes group for this field
1041 enddo
1042
1044
1045!> Returns diag_manager id for cell measure of h-cells
1046integer function diag_get_volume_cell_measure_dm_id(diag_cs)
1047 type(diag_ctrl), intent(in) :: diag_cs !< Diagnostics control structure
1048
1049 diag_get_volume_cell_measure_dm_id = diag_cs%volume_cell_measure_dm_id
1050
1052
1053!> Define a group of "axes" from a list of handles and associate a mask with it
1054subroutine define_axes_group(diag_cs, handles, axes, nz, vertical_coordinate_number, &
1055 x_cell_method, y_cell_method, v_cell_method, &
1056 is_h_point, is_q_point, is_u_point, is_v_point, &
1057 is_layer, is_interface, &
1058 is_native, needs_remapping, needs_interpolating, &
1059 xyave_axes)
1060 type(diag_ctrl), target, intent(in) :: diag_cs !< Diagnostics control structure
1061 integer, dimension(:), intent(in) :: handles !< A list of 1D axis handles
1062 type(axes_grp), intent(out) :: axes !< The group of 1D axes
1063 integer, optional, intent(in) :: nz !< Number of layers in this diagnostic grid
1064 integer, optional, intent(in) :: vertical_coordinate_number !< Index number for vertical coordinate
1065 character(len=*), optional, intent(in) :: x_cell_method !< A x-direction cell method used to construct the
1066 !! "cell_methods" attribute in CF convention
1067 character(len=*), optional, intent(in) :: y_cell_method !< A y-direction cell method used to construct the
1068 !! "cell_methods" attribute in CF convention
1069 character(len=*), optional, intent(in) :: v_cell_method !< A vertical direction cell method used to construct
1070 !! the "cell_methods" attribute in CF convention
1071 logical, optional, intent(in) :: is_h_point !< If true, indicates this axes group for h-point
1072 !! located fields
1073 logical, optional, intent(in) :: is_q_point !< If true, indicates this axes group for q-point
1074 !! located fields
1075 logical, optional, intent(in) :: is_u_point !< If true, indicates this axes group for
1076 !! u-point located fields
1077 logical, optional, intent(in) :: is_v_point !< If true, indicates this axes group for
1078 !! v-point located fields
1079 logical, optional, intent(in) :: is_layer !< If true, indicates that this axes group is
1080 !! for a layer vertically-located field.
1081 logical, optional, intent(in) :: is_interface !< If true, indicates that this axes group
1082 !! is for an interface vertically-located field.
1083 logical, optional, intent(in) :: is_native !< If true, indicates that this axes group is
1084 !! for a native model grid. False for any other grid.
1085 logical, optional, intent(in) :: needs_remapping !< If true, indicates that this axes group is
1086 !! for a intensive layer-located field that must
1087 !! be remapped to these axes. Used for rank>2.
1088 logical, optional, intent(in) :: needs_interpolating !< If true, indicates that this axes group
1089 !! is for a sampled interface-located field that must
1090 !! be interpolated to these axes. Used for rank>2.
1091 type(axes_grp), optional, target :: xyave_axes !< The corresponding axes group for horizontally
1092 !! area-average diagnostics
1093 ! Local variables
1094 integer :: n
1095
1096 n = size(handles)
1097 if (n<1 .or. n>3) call mom_error(fatal, "define_axes_group: wrong size for list of handles!")
1098 allocate( axes%handles(n) )
1099 axes%id = ints_to_string(handles, max(n,3)) ! Identifying string
1100 axes%rank = n
1101 axes%handles(:) = handles(:)
1102 axes%diag_cs => diag_cs ! A (circular) link back to the diag_cs structure
1103 if (present(x_cell_method)) then
1104 if (axes%rank<2) call mom_error(fatal, 'define_axes_group: ' // &
1105 'Can not set x_cell_method for rank<2.')
1106 axes%x_cell_method = trim(x_cell_method)
1107 else
1108 axes%x_cell_method = ''
1109 endif
1110 if (present(y_cell_method)) then
1111 if (axes%rank<2) call mom_error(fatal, 'define_axes_group: ' // &
1112 'Can not set y_cell_method for rank<2.')
1113 axes%y_cell_method = trim(y_cell_method)
1114 else
1115 axes%y_cell_method = ''
1116 endif
1117 if (present(v_cell_method)) then
1118 if (axes%rank/=1 .and. axes%rank/=3) call mom_error(fatal, 'define_axes_group: ' // &
1119 'Can not set v_cell_method for rank<>1 or 3.')
1120 axes%v_cell_method = trim(v_cell_method)
1121 else
1122 axes%v_cell_method = ''
1123 endif
1124
1125 if (present(nz)) axes%nz = nz
1126 if (present(vertical_coordinate_number)) axes%vertical_coordinate_number = vertical_coordinate_number
1127 if (present(is_h_point)) axes%is_h_point = is_h_point
1128 if (present(is_q_point)) axes%is_q_point = is_q_point
1129 if (present(is_u_point)) axes%is_u_point = is_u_point
1130 if (present(is_v_point)) axes%is_v_point = is_v_point
1131 if (present(is_layer)) axes%is_layer = is_layer
1132 if (present(is_interface)) axes%is_interface = is_interface
1133 if (present(is_native)) axes%is_native = is_native
1134 if (present(needs_remapping)) axes%needs_remapping = needs_remapping
1135 if (present(needs_interpolating)) axes%needs_interpolating = needs_interpolating
1136 if (present(xyave_axes)) axes%xyave_axes => xyave_axes
1137
1138 ! Setup masks for this axes group
1139 axes%mask2d => null()
1140 if (axes%rank==2) then
1141 if (axes%is_h_point) axes%mask2d => diag_cs%mask2dT
1142 if (axes%is_u_point) axes%mask2d => diag_cs%mask2dCu
1143 if (axes%is_v_point) axes%mask2d => diag_cs%mask2dCv
1144 if (axes%is_q_point) axes%mask2d => diag_cs%mask2dBu
1145 endif
1146 ! A static 3d mask for non-native coordinates can only be setup when a grid is available
1147 axes%mask3d => null()
1148 if (axes%rank==3 .and. axes%is_native) then
1149 ! Native variables can/should use the native masks copied into diag_cs
1150 if (axes%is_layer) then
1151 if (axes%is_h_point) axes%mask3d => diag_cs%mask3dTL
1152 if (axes%is_u_point) axes%mask3d => diag_cs%mask3dCuL
1153 if (axes%is_v_point) axes%mask3d => diag_cs%mask3dCvL
1154 if (axes%is_q_point) axes%mask3d => diag_cs%mask3dBL
1155 elseif (axes%is_interface) then
1156 if (axes%is_h_point) axes%mask3d => diag_cs%mask3dTi
1157 if (axes%is_u_point) axes%mask3d => diag_cs%mask3dCui
1158 if (axes%is_v_point) axes%mask3d => diag_cs%mask3dCvi
1159 if (axes%is_q_point) axes%mask3d => diag_cs%mask3dBi
1160 endif
1161 endif
1162
1163
1164end subroutine define_axes_group
1165
1166!> Defines a group of downsampled "axes" from list of handles
1167subroutine define_axes_group_dsamp(diag_cs, handles, axes, dl, nz, vertical_coordinate_number, &
1168 x_cell_method, y_cell_method, v_cell_method, &
1169 is_h_point, is_q_point, is_u_point, is_v_point, &
1170 is_layer, is_interface, &
1171 is_native, needs_remapping, needs_interpolating, &
1172 xyave_axes)
1173 type(diag_ctrl), target, intent(in) :: diag_cs !< Diagnostics control structure
1174 integer, dimension(:), intent(in) :: handles !< A list of 1D axis handles
1175 type(axes_grp), intent(out) :: axes !< The group of 1D axes
1176 integer, intent(in) :: dl !< Downsample level index
1177 integer, optional, intent(in) :: nz !< Number of layers in this diagnostic grid
1178 integer, optional, intent(in) :: vertical_coordinate_number !< Index number for vertical coordinate
1179 character(len=*), optional, intent(in) :: x_cell_method !< A x-direction cell method used to construct the
1180 !! "cell_methods" attribute in CF convention
1181 character(len=*), optional, intent(in) :: y_cell_method !< A y-direction cell method used to construct the
1182 !! "cell_methods" attribute in CF convention
1183 character(len=*), optional, intent(in) :: v_cell_method !< A vertical direction cell method used to construct
1184 !! the "cell_methods" attribute in CF convention
1185 logical, optional, intent(in) :: is_h_point !< If true, indicates this axes group for h-point
1186 !! located fields
1187 logical, optional, intent(in) :: is_q_point !< If true, indicates this axes group for q-point
1188 !! located fields
1189 logical, optional, intent(in) :: is_u_point !< If true, indicates this axes group for
1190 !! u-point located fields
1191 logical, optional, intent(in) :: is_v_point !< If true, indicates this axes group for
1192 !! v-point located fields
1193 logical, optional, intent(in) :: is_layer !< If true, indicates that this axes group is
1194 !! for a layer vertically-located field.
1195 logical, optional, intent(in) :: is_interface !< If true, indicates that this axes group
1196 !! is for an interface vertically-located field.
1197 logical, optional, intent(in) :: is_native !< If true, indicates that this axes group is
1198 !! for a native model grid. False for any other grid.
1199 logical, optional, intent(in) :: needs_remapping !< If true, indicates that this axes group is
1200 !! for a intensive layer-located field that must
1201 !! be remapped to these axes. Used for rank>2.
1202 logical, optional, intent(in) :: needs_interpolating !< If true, indicates that this axes group
1203 !! is for a sampled interface-located field that must
1204 !! be interpolated to these axes. Used for rank>2.
1205 type(axes_grp), optional, target :: xyave_axes !< The corresponding axes group for horizontally
1206 !! area-average diagnostics
1207 ! Local variables
1208 integer :: n
1209
1210 n = size(handles)
1211 if (n<1 .or. n>3) call mom_error(fatal, "define_axes_group: wrong size for list of handles!")
1212 allocate( axes%handles(n) )
1213 axes%id = ints_to_string(handles, max(n,3)) ! Identifying string
1214 axes%rank = n
1215 axes%handles(:) = handles(:)
1216 axes%diag_cs => diag_cs ! A (circular) link back to the diag_cs structure
1217 if (present(x_cell_method)) then
1218 if (axes%rank<2) call mom_error(fatal, 'define_axes_group: ' // &
1219 'Can not set x_cell_method for rank<2.')
1220 axes%x_cell_method = trim(x_cell_method)
1221 else
1222 axes%x_cell_method = ''
1223 endif
1224 if (present(y_cell_method)) then
1225 if (axes%rank<2) call mom_error(fatal, 'define_axes_group: ' // &
1226 'Can not set y_cell_method for rank<2.')
1227 axes%y_cell_method = trim(y_cell_method)
1228 else
1229 axes%y_cell_method = ''
1230 endif
1231 if (present(v_cell_method)) then
1232 if (axes%rank/=1 .and. axes%rank/=3) call mom_error(fatal, 'define_axes_group: ' // &
1233 'Can not set v_cell_method for rank<>1 or 3.')
1234 axes%v_cell_method = trim(v_cell_method)
1235 else
1236 axes%v_cell_method = ''
1237 endif
1238 axes%downsample_level_index = dl
1239 axes%downsample_level_factor = diag_cs%diag_dsamp_levels(dl)
1240 if (present(nz)) axes%nz = nz
1241 if (present(vertical_coordinate_number)) axes%vertical_coordinate_number = vertical_coordinate_number
1242 if (present(is_h_point)) axes%is_h_point = is_h_point
1243 if (present(is_q_point)) axes%is_q_point = is_q_point
1244 if (present(is_u_point)) axes%is_u_point = is_u_point
1245 if (present(is_v_point)) axes%is_v_point = is_v_point
1246 if (present(is_layer)) axes%is_layer = is_layer
1247 if (present(is_interface)) axes%is_interface = is_interface
1248 if (present(is_native)) axes%is_native = is_native
1249 if (present(needs_remapping)) axes%needs_remapping = needs_remapping
1250 if (present(needs_interpolating)) axes%needs_interpolating = needs_interpolating
1251 if (present(xyave_axes)) axes%xyave_axes => xyave_axes
1252
1253 ! Setup masks for this axes group
1254
1255 axes%mask2d => null()
1256 if (axes%rank==2) then
1257 if (axes%is_h_point) axes%mask2d => diag_cs%mask2dT
1258 if (axes%is_u_point) axes%mask2d => diag_cs%mask2dCu
1259 if (axes%is_v_point) axes%mask2d => diag_cs%mask2dCv
1260 if (axes%is_q_point) axes%mask2d => diag_cs%mask2dBu
1261 endif
1262 ! A static 3d mask for non-native coordinates can only be setup when a grid is available
1263 axes%mask3d => null()
1264 if (axes%rank==3 .and. axes%is_native) then
1265 ! Native variables can/should use the native masks copied into diag_cs
1266 if (axes%is_layer) then
1267 if (axes%is_h_point) axes%mask3d => diag_cs%mask3dTL
1268 if (axes%is_u_point) axes%mask3d => diag_cs%mask3dCuL
1269 if (axes%is_v_point) axes%mask3d => diag_cs%mask3dCvL
1270 if (axes%is_q_point) axes%mask3d => diag_cs%mask3dBL
1271 elseif (axes%is_interface) then
1272 if (axes%is_h_point) axes%mask3d => diag_cs%mask3dTi
1273 if (axes%is_u_point) axes%mask3d => diag_cs%mask3dCui
1274 if (axes%is_v_point) axes%mask3d => diag_cs%mask3dCvi
1275 if (axes%is_q_point) axes%mask3d => diag_cs%mask3dBi
1276 endif
1277 endif
1278
1279 if (.Not. allocated(axes%dsamp)) allocate(axes%dsamp(diag_cs%num_diag_dsamp_levels))
1280 axes%dsamp(dl)%mask2d => null()
1281 if (axes%rank==2) then
1282 if (axes%is_h_point) axes%dsamp(dl)%mask2d => diag_cs%dsamp(dl)%mask2dT
1283 if (axes%is_u_point) axes%dsamp(dl)%mask2d => diag_cs%dsamp(dl)%mask2dCu
1284 if (axes%is_v_point) axes%dsamp(dl)%mask2d => diag_cs%dsamp(dl)%mask2dCv
1285 if (axes%is_q_point) axes%dsamp(dl)%mask2d => diag_cs%dsamp(dl)%mask2dBu
1286 endif
1287 ! A static 3d mask for non-native coordinates can only be setup when a grid is available
1288 axes%dsamp(dl)%mask3d => null()
1289 if (axes%rank==3 .and. axes%is_native) then
1290 ! Native variables can/should use the native masks copied into diag_cs
1291 if (axes%is_layer) then
1292 if (axes%is_h_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dTL
1293 if (axes%is_u_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dCuL
1294 if (axes%is_v_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dCvL
1295 if (axes%is_q_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dBL
1296 elseif (axes%is_interface) then
1297 if (axes%is_h_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dTi
1298 if (axes%is_u_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dCui
1299 if (axes%is_v_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dCvi
1300 if (axes%is_q_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dBi
1301 endif
1302 endif
1303
1304end subroutine define_axes_group_dsamp
1305
1306!> Set up the array extents for doing diagnostics
1307subroutine set_diag_mediator_grid(G, diag_cs)
1308 type(ocean_grid_type), intent(inout) :: g !< The ocean's grid structure
1309 type(diag_ctrl), intent(inout) :: diag_cs !< Structure used to regulate diagnostic output
1310
1311 diag_cs%is = g%isc - (g%isd-1) ; diag_cs%ie = g%iec - (g%isd-1)
1312 diag_cs%js = g%jsc - (g%jsd-1) ; diag_cs%je = g%jec - (g%jsd-1)
1313 diag_cs%isd = g%isd ; diag_cs%ied = g%ied
1314 diag_cs%jsd = g%jsd ; diag_cs%jed = g%jed
1315
1316end subroutine set_diag_mediator_grid
1317
1318!> Make a real scalar diagnostic available for averaging or output
1319subroutine post_data_0d(diag_field_id, field, diag_cs, is_static)
1320 integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
1321 !! previous call to register_diag_field.
1322 real, intent(in) :: field !< real value being offered for output or averaging
1323 !! in internally scaled arbitrary units [A ~> a]
1324 type(diag_ctrl), target, intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1325 logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1326
1327 ! Local variables
1328 real :: locfield ! The field being offered in arbitrary unscaled units [a]
1329 logical :: used, is_stat
1330 type(diag_type), pointer :: diag => null()
1331
1332 integer :: time_days
1333 integer :: time_seconds
1334 character(len=300) :: debug_mesg
1335
1336 if (id_clock_diag_mediator>0) call cpu_clock_begin(id_clock_diag_mediator)
1337 is_stat = .false. ; if (present(is_static)) is_stat = is_static
1338
1339 ! Iterate over list of diag 'variants', e.g. CMOR aliases, call send_data
1340 ! for each one.
1341 call assert(diag_field_id < diag_cs%next_free_diag_id, &
1342 'post_data_0d: Unregistered diagnostic id')
1343 diag => diag_cs%diags(diag_field_id)
1344
1345 do while (associated(diag))
1346 locfield = field
1347 if (diag%conversion_factor /= 0.) &
1348 locfield = locfield * diag%conversion_factor
1349
1350 if (diag_cs%diag_as_chksum) then
1351 ! Append timestep to mesg
1352 call get_time(diag_cs%time_end, time_seconds, days=time_days)
1353 write(debug_mesg, '(a, 1x, i0, 1x, i0)') &
1354 trim(diag%debug_str), time_days, time_seconds
1355
1356 call chksum0(locfield, debug_mesg, logunit=diag_cs%chksum_iounit)
1357 elseif (is_stat) then
1358 used = send_data_infra(diag%fms_diag_id, locfield)
1359 elseif (diag_cs%ave_enabled) then
1360 used = send_data_infra(diag%fms_diag_id, locfield, diag_cs%time_end)
1361 endif
1362 diag => diag%next
1363 enddo
1364
1365 if (id_clock_diag_mediator>0) call cpu_clock_end(id_clock_diag_mediator)
1366end subroutine post_data_0d
1367
1368!> Make a real 1-d array diagnostic available for averaging or output
1369subroutine post_data_1d_k(diag_field_id, field, diag_cs, is_static)
1370 integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
1371 !! previous call to register_diag_field.
1372 real, target, intent(in) :: field(:) !< 1-d array being offered for output or averaging
1373 !! in internally scaled arbitrary units [A ~> a]
1374 type(diag_ctrl), target, intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1375 logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1376
1377 ! Local variables
1378 logical :: used ! The return value of send_data is not used for anything.
1379 real, dimension(:), pointer :: locfield => null() ! The field being offered in arbitrary unscaled units [a]
1380 logical :: is_stat
1381 integer :: k, ks, ke
1382 type(diag_type), pointer :: diag => null()
1383
1384 integer :: time_days
1385 integer :: time_seconds
1386 character(len=300) :: debug_mesg
1387
1388 if (id_clock_diag_mediator>0) call cpu_clock_begin(id_clock_diag_mediator)
1389 is_stat = .false. ; if (present(is_static)) is_stat = is_static
1390
1391 ! Iterate over list of diag 'variants', e.g. CMOR aliases.
1392 call assert(diag_field_id < diag_cs%next_free_diag_id, &
1393 'post_data_1d_k: Unregistered diagnostic id')
1394 diag => diag_cs%diags(diag_field_id)
1395 do while (associated(diag))
1396
1397 if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) then
1398 ks = lbound(field,1) ; ke = ubound(field,1)
1399 allocate( locfield( ks:ke ) )
1400
1401 do k=ks,ke
1402 locfield(k) = field(k) * diag%conversion_factor
1403 enddo
1404 else
1405 locfield => field
1406 endif
1407
1408 if (diag_cs%diag_as_chksum) then
1409 ! Append timestep to mesg
1410 call get_time(diag_cs%time_end, time_seconds, days=time_days)
1411 write(debug_mesg, '(a, 1x, i0, 1x, i0)') &
1412 trim(diag%debug_str), time_days, time_seconds
1413
1414 call zchksum(locfield, debug_mesg, logunit=diag_cs%chksum_iounit)
1415 elseif (is_stat) then
1416 used = send_data_infra(diag%fms_diag_id, locfield)
1417 elseif (diag_cs%ave_enabled) then
1418 used = send_data_infra(diag%fms_diag_id, locfield, time=diag_cs%time_end, weight=diag_cs%time_int)
1419 endif
1420 if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) deallocate( locfield )
1421
1422 diag => diag%next
1423 enddo
1424
1425 if (id_clock_diag_mediator>0) call cpu_clock_end(id_clock_diag_mediator)
1426end subroutine post_data_1d_k
1427
1428!> Make a real 2-d array diagnostic available for averaging or output
1429subroutine post_data_2d(diag_field_id, field, diag_cs, is_static, mask)
1430 integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
1431 !! previous call to register_diag_field.
1432 real, intent(in) :: field(:,:) !< 2-d array being offered for output or averaging
1433 !! in internally scaled arbitrary units [A ~> a]
1434 type(diag_ctrl), target, intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1435 logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1436 real, optional, intent(in) :: mask(:,:) !< If present, use this real array as the data mask [nondim]
1437
1438 ! Local variables
1439 type(diag_type), pointer :: diag => null()
1440
1441 if (id_clock_diag_mediator>0) call cpu_clock_begin(id_clock_diag_mediator)
1442
1443 ! Iterate over list of diag 'variants' (e.g. CMOR aliases) and post each.
1444 call assert(diag_field_id < diag_cs%next_free_diag_id, &
1445 'post_data_2d: Unregistered diagnostic id')
1446 diag => diag_cs%diags(diag_field_id)
1447 do while (associated(diag))
1448 call post_data_2d_low(diag, field, diag_cs, is_static, mask)
1449 diag => diag%next
1450 enddo
1451
1452 if (id_clock_diag_mediator>0) call cpu_clock_end(id_clock_diag_mediator)
1453end subroutine post_data_2d
1454
1455!> Make a real 2-d array diagnostic available for averaging or output
1456!! using a diag_type instead of an integer id.
1457subroutine post_data_2d_low(diag, field, diag_cs, is_static, mask)
1458 type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
1459 real, target, intent(in) :: field(:,:) !< 2-d array being offered for output or averaging
1460 !! in internally scaled arbitrary units [A ~> a]
1461 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1462 logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1463 real, optional, target, intent(in) :: mask(:,:) !< If present, use this real array as the data mask [nondim]
1464
1465 ! Local variables
1466 real, dimension(:,:), pointer :: locfield ! The field being offered in arbitrary unscaled units [a]
1467 real, dimension(:,:), pointer :: locmask ! A pointer to the data mask to use [nondim]
1468 logical :: used ! The return value of send_data is not used for anything.
1469 logical :: is_stat, not_static
1470 integer :: cszi, cszj, dszi, dszj
1471 integer :: isv, iev, jsv, jev, i, j, isv_o, jsv_o
1472 real, dimension(:,:), allocatable, target :: locfield_dsamp ! A downsampled version of locfield [a]
1473 real, dimension(:,:), allocatable, target :: locmask_dsamp ! A downsampled version of locmask [nondim]
1474 real, dimension(:,:), pointer :: ones => null() ! An array of ones for testing where masks do not apply [nondim]
1475 real, dimension(:,:), pointer :: mask_in => null() ! A pointer to the input mask [nondim]
1476 integer :: dl, dlfac
1477 integer :: time_days
1478 integer :: time_seconds
1479 character(len=300) :: mesg
1480 character(len=300) :: debug_mesg
1481
1482 locfield => null()
1483 locmask => null()
1484 is_stat = .false. ; if (present(is_static)) is_stat = is_static
1485 not_static = .not. is_stat
1486
1487 ! Determine the proper array indices, noting that because of the (:,:)
1488 ! declaration of field, symmetric arrays are using a SW-grid indexing,
1489 ! but non-symmetric arrays are using a NE-grid indexing. Send_data
1490 ! actually only uses the difference between ie and is to determine
1491 ! the output data size and assumes that halos are symmetric.
1492 isv = diag_cs%is ; iev = diag_cs%ie ; jsv = diag_cs%js ; jev = diag_cs%je
1493
1494 cszi = diag_cs%ie-diag_cs%is +1 ; dszi = diag_cs%ied-diag_cs%isd +1
1495 cszj = diag_cs%je-diag_cs%js +1 ; dszj = diag_cs%jed-diag_cs%jsd +1
1496 if ( size(field,1) == dszi ) then
1497 isv = diag_cs%is ; iev = diag_cs%ie ! Data domain
1498 elseif ( size(field,1) == dszi + 1 ) then
1499 isv = diag_cs%is ; iev = diag_cs%ie+1 ! Symmetric data domain
1500 elseif ( size(field,1) == cszi) then
1501 isv = 1 ; iev = cszi ! Computational domain
1502 elseif ( size(field,1) == cszi + 1 ) then
1503 isv = 1 ; iev = cszi+1 ! Symmetric computational domain
1504 else
1505 write (mesg,*) " peculiar size ",size(field,1)," in i-direction\n"//&
1506 "does not match one of ", cszi, cszi+1, dszi, dszi+1
1507 call mom_error(fatal,"post_data_2d_low: "//trim(diag%debug_str)//trim(mesg))
1508 endif
1509
1510 if ( size(field,2) == dszj ) then
1511 jsv = diag_cs%js ; jev = diag_cs%je ! Data domain
1512 elseif ( size(field,2) == dszj + 1 ) then
1513 jsv = diag_cs%js ; jev = diag_cs%je+1 ! Symmetric data domain
1514 elseif ( size(field,2) == cszj ) then
1515 jsv = 1 ; jev = cszj ! Computational domain
1516 elseif ( size(field,2) == cszj+1 ) then
1517 jsv = 1 ; jev = cszj+1 ! Symmetric computational domain
1518 else
1519 write (mesg,*) " peculiar size ",size(field,2)," in j-direction\n"//&
1520 "does not match one of ", cszj, cszj+1, dszj, dszj+1
1521 call mom_error(fatal,"post_data_2d_low: "//trim(diag%debug_str)//trim(mesg))
1522 endif
1523
1524 if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) then
1525 allocate( locfield( lbound(field,1):ubound(field,1), lbound(field,2):ubound(field,2) ) )
1526 do j=jsv,jev ; do i=isv,iev
1527 locfield(i,j) = field(i,j) * diag%conversion_factor
1528 enddo ; enddo
1529 else
1530 locfield => field
1531 endif
1532
1533 if (present(mask)) then
1534 locmask => mask
1535 elseif (not_static .and. associated(diag%axes)) then
1536 ! If we were to decide to allow masking of static diagnostics, we could do so by changing the line above to
1537 ! elseif (associated(diag%axes) .and. (diag_CS%mask_static_diags .or. not_static)) then
1538 if (associated(diag%axes%mask2d)) locmask => diag%axes%mask2d
1539 endif
1540
1541 dlfac = 1
1542 if (not_static .and. associated(diag%axes)) &
1543 dlfac = diag%axes%downsample_level_factor ! Static field downsampling is not supported yet.
1544 ! Downsample the diag field and mask as appropriate.
1545 if (dlfac > 1) then
1546 dl = diag%axes%downsample_level_index
1547 isv_o = isv ; jsv_o = jsv
1548 call downsample_diag_field(locfield, locfield_dsamp, dl, diag_cs, diag, isv, iev, jsv, jev, mask)
1549 if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) deallocate( locfield )
1550 locfield => locfield_dsamp
1551 if (present(mask)) then
1552 ! Replicate the downsampling of other fields to find unmasked points.
1553 allocate(ones, mold=locmask) ; ones(:,:) = 1.0
1554 mask_in => mask
1555 call downsample_field_2d(ones, locmask_dsamp, dlfac, diag%xyz_method, mask_in, diag_cs, diag, &
1556 isv_o, jsv_o, isv, iev, jsv, jev)
1557 deallocate(ones)
1558 where (abs(locmask_dsamp) > 0.0) locmask_dsamp = 1.0
1559 locmask => locmask_dsamp
1560 elseif (associated(diag%axes%dsamp(dl)%mask2d)) then
1561 locmask => diag%axes%dsamp(dl)%mask2d
1562 endif
1563 endif
1564 if (associated(locmask)) call assert(size(locfield) == size(locmask), &
1565 'post_data_2d_low: mask size mismatch: '//trim(diag%debug_str))
1566
1567 if (diag_cs%diag_as_chksum) then
1568 ! Append timestep to mesg
1569 call get_time(diag_cs%time_end, time_seconds, days=time_days)
1570 write(debug_mesg, '(a, 1x, i0, 1x, i0)') &
1571 trim(diag%debug_str), time_days, time_seconds
1572
1573 if (diag%axes%is_h_point) then
1574 call hchksum(locfield, debug_mesg, diag_cs%G%HI, &
1575 logunit=diag_cs%chksum_iounit)
1576 elseif (diag%axes%is_u_point) then
1577 call uchksum(locfield, debug_mesg, diag_cs%G%HI, &
1578 logunit=diag_cs%chksum_iounit)
1579 elseif (diag%axes%is_v_point) then
1580 call vchksum(locfield, debug_mesg, diag_cs%G%HI, &
1581 logunit=diag_cs%chksum_iounit)
1582 elseif (diag%axes%is_q_point) then
1583 call bchksum(locfield, debug_mesg, diag_cs%G%HI, &
1584 logunit=diag_cs%chksum_iounit)
1585 else
1586 call mom_error(fatal, "post_data_2d_low: unknown axis type.")
1587 endif
1588 else
1589 if (is_stat) then
1590 if (associated(locmask)) then
1591 used = send_data_infra(diag%fms_diag_id, locfield, &
1592 is_in=isv, ie_in=iev, js_in=jsv, je_in=jev, rmask=locmask)
1593 else
1594 used = send_data_infra(diag%fms_diag_id, locfield, &
1595 is_in=isv, ie_in=iev, js_in=jsv, je_in=jev)
1596 endif
1597 elseif (diag_cs%ave_enabled) then
1598 if (associated(locmask)) then
1599 used = send_data_infra(diag%fms_diag_id, locfield, &
1600 is_in=isv, ie_in=iev, js_in=jsv, je_in=jev, &
1601 time=diag_cs%time_end, weight=diag_cs%time_int, rmask=locmask)
1602 else
1603 used = send_data_infra(diag%fms_diag_id, locfield, &
1604 is_in=isv, ie_in=iev, js_in=jsv, je_in=jev, &
1605 time=diag_cs%time_end, weight=diag_cs%time_int)
1606 endif
1607 endif
1608 endif
1609 if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.) .and. dlfac<2) &
1610 deallocate( locfield )
1611end subroutine post_data_2d_low
1612
1613!> Make a real 3-d array diagnostic available for averaging or output.
1614subroutine post_data_3d(diag_field_id, field, diag_cs, is_static, mask, alt_h)
1615
1616 integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
1617 !! previous call to register_diag_field.
1618 real, intent(in) :: field(:,:,:) !< 3-d array being offered for output or averaging
1619 !! in internally scaled arbitrary units [A ~> a]
1620 type(diag_ctrl), target, intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1621 logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1622 real, optional, intent(in) :: mask(:,:,:) !< If present, use this real array as the data mask [nondim]
1623 real, dimension(:,:,:), &
1624 target, optional, intent(in) :: alt_h !< An alternate thickness to use for vertically
1625 !! remapping this diagnostic [H ~> m or kg m-2].
1626
1627 ! Local variables
1628 type(diag_type), pointer :: diag => null()
1629 real, dimension(:,:,:), allocatable :: remapped_field !< The vertically remapped diagnostic [A ~> a]
1630 logical :: staggered_in_x, staggered_in_y, dz_diag_needed, dz_begin_needed
1631 real, dimension(:,:,:), pointer :: h_diag => null() !< A pointer to the thickness to use for vertically
1632 !! remapping this diagnostic [H ~> m or kg m-2].
1633
1634 real, dimension(diag_cs%G%isd:diag_cS%G%ied, diag_cs%G%jsd:diag_cS%G%jed, diag_cs%GV%ke) :: &
1635 dz_diag ! Layer vertical extents for remapping [Z ~> m]
1636
1637 if (id_clock_diag_mediator>0) call cpu_clock_begin(id_clock_diag_mediator)
1638
1639 ! For intensive variables only, we can choose to use a different diagnostic grid to map to
1640 if (present(alt_h)) then
1641 h_diag => alt_h
1642 else
1643 h_diag => diag_cs%h
1644 endif
1645
1646 ! Iterate over list of diag 'variants', e.g. CMOR aliases, different vertical
1647 ! grids, and post each.
1648 call assert(diag_field_id < diag_cs%next_free_diag_id, &
1649 'post_data_3d: Unregistered diagnostic id')
1650
1651 if (diag_cs%show_call_tree) &
1652 call calltree_enter("post_data_3d("//trim(diag_cs%diags(diag_field_id)%debug_str)//")")
1653
1654 ! Find out whether there are any z-based diagnostics
1655 diag => diag_cs%diags(diag_field_id)
1656 dz_diag_needed = .false.
1657 do while (associated(diag))
1658 if (diag%axes%needs_remapping .or. diag%axes%needs_interpolating) then
1659 if (diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%Z_based_coord) &
1660 dz_diag_needed = .true.
1661 endif
1662 diag => diag%next
1663 enddo
1664
1665 ! Determine the diagnostic grid spacing in height units, if it is needed.
1666 if (dz_diag_needed) then
1667 call thickness_to_dz(h_diag, diag_cs%tv, dz_diag, diag_cs%G, diag_cs%GV, diag_cs%US, halo_size=1)
1668 endif
1669
1670 diag => diag_cs%diags(diag_field_id)
1671 do while (associated(diag))
1672 call assert(associated(diag%axes), 'post_data_3d: axes is not associated')
1673
1674 staggered_in_x = diag%axes%is_u_point .or. diag%axes%is_q_point
1675 staggered_in_y = diag%axes%is_v_point .or. diag%axes%is_q_point
1676
1677 if (diag%v_extensive .and. .not.diag%axes%is_native) then
1678 ! The field is vertically integrated and needs to be re-gridded
1679 if (present(mask)) then
1680 call mom_error(fatal,"post_data_3d: no mask for regridded field.")
1681 endif
1682
1683 if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
1684 allocate(remapped_field(size(field,1), size(field,2), diag%axes%nz))
1685 if (diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%Z_based_coord) then
1686 call vertically_reintegrate_diag_field( &
1687 diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), diag_cs%G, &
1688 diag_cs%dz_begin, diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%h_extensive, &
1689 diag_cs%OBC_u, diag_cs%OBC_v, staggered_in_x, staggered_in_y, diag%axes%mask3d, field, remapped_field)
1690 else
1691 call vertically_reintegrate_diag_field( &
1692 diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), diag_cs%G, &
1693 diag_cs%h_begin, diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%h_extensive, &
1694 diag_cs%OBC_u, diag_cs%OBC_v, staggered_in_x, staggered_in_y, diag%axes%mask3d, field, remapped_field)
1695 endif
1696 if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
1697 if (associated(diag%axes%mask3d)) then
1698 ! Since 3d masks do not vary in the vertical, just use as much as is
1699 ! needed.
1700 call post_data_3d_low(diag, remapped_field, diag_cs, is_static, &
1701 mask=diag%axes%mask3d)
1702 else
1703 call post_data_3d_low(diag, remapped_field, diag_cs, is_static)
1704 endif
1705 if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
1706 deallocate(remapped_field)
1707 if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
1708 elseif (diag%axes%needs_remapping) then
1709 ! Remap this field to another vertical coordinate.
1710 if (present(mask)) then
1711 call mom_error(fatal,"post_data_3d: no mask for regridded field.")
1712 endif
1713
1714 if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
1715 allocate(remapped_field(size(field,1), size(field,2), diag%axes%nz))
1716 if (diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%Z_based_coord) then
1717 call diag_remap_do_remap(diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), &
1718 diag_cs%G, diag_cs%GV, diag_cs%US, dz_diag, diag_cs%OBC_u, diag_cs%OBC_v, &
1719 staggered_in_x, staggered_in_y, diag%axes%mask3d, field, remapped_field)
1720 else
1721 call diag_remap_do_remap(diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), &
1722 diag_cs%G, diag_cs%GV, diag_cs%US, h_diag, diag_cs%OBC_u, diag_cs%OBC_v, &
1723 staggered_in_x, staggered_in_y, diag%axes%mask3d, field, remapped_field)
1724 endif
1725 if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
1726 if (associated(diag%axes%mask3d)) then
1727 ! Since 3d masks do not vary in the vertical, just use as much as is
1728 ! needed.
1729 call post_data_3d_low(diag, remapped_field, diag_cs, is_static, &
1730 mask=diag%axes%mask3d)
1731 else
1732 call post_data_3d_low(diag, remapped_field, diag_cs, is_static)
1733 endif
1734 if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
1735 deallocate(remapped_field)
1736 if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
1737 elseif (diag%axes%needs_interpolating) then
1738 ! Interpolate this field to another vertical coordinate.
1739 if (present(mask)) then
1740 call mom_error(fatal,"post_data_3d: no mask for regridded field.")
1741 endif
1742
1743 if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
1744 allocate(remapped_field(size(field,1), size(field,2), diag%axes%nz+1))
1745 if (diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%Z_based_coord) then
1746 call vertically_interpolate_diag_field(diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), &
1747 diag_cs%G, dz_diag, diag_cs%OBC_u, diag_cs%OBC_v, staggered_in_x, staggered_in_y, &
1748 diag%axes%mask3d, field, remapped_field)
1749 else
1750 call vertically_interpolate_diag_field(diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), &
1751 diag_cs%G, h_diag, diag_cs%OBC_u, diag_cs%OBC_v, staggered_in_x, staggered_in_y, &
1752 diag%axes%mask3d, field, remapped_field)
1753 endif
1754 if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
1755 if (associated(diag%axes%mask3d)) then
1756 ! Since 3d masks do not vary in the vertical, just use as much as is needed.
1757 call post_data_3d_low(diag, remapped_field, diag_cs, is_static, &
1758 mask=diag%axes%mask3d)
1759 else
1760 call post_data_3d_low(diag, remapped_field, diag_cs, is_static)
1761 endif
1762 if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
1763 deallocate(remapped_field)
1764 if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
1765 else
1766 call post_data_3d_low(diag, field, diag_cs, is_static, mask)
1767 endif
1768 diag => diag%next
1769 enddo
1770 if (id_clock_diag_mediator>0) call cpu_clock_end(id_clock_diag_mediator)
1771
1772 if (diag_cs%show_call_tree) &
1773 call calltree_leave("post_data_3d("//trim(diag_cs%diags(diag_field_id)%debug_str)//")")
1774
1775end subroutine post_data_3d
1776
1777!> Make a real 3-d array diagnostic available for averaging or output
1778!! using a diag_type instead of an integer id.
1779subroutine post_data_3d_low(diag, field, diag_cs, is_static, mask)
1780 type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
1781 real, target, intent(in) :: field(:,:,:) !< 3-d array being offered for output or averaging
1782 !! in internally scaled arbitrary units [A ~> a]
1783 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1784 logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1785 real, optional,target, intent(in) :: mask(:,:,:) !< If present, use this real array as the data mask [nondim]
1786
1787 ! Local variables
1788 real, dimension(:,:,:), pointer :: locfield ! The field being offered in arbitrary unscaled units [a]
1789 real, dimension(:,:,:), pointer :: locmask ! A pointer to the data mask to use [nondim]
1790 character(len=300) :: mesg
1791 logical :: used ! The return value of send_data is not used for anything.
1792 logical :: staggered_in_x, staggered_in_y
1793 logical :: is_stat, not_static
1794 integer :: cszi, cszj, dszi, dszj
1795 integer :: isv, iev, jsv, jev, ks, ke, i, j, k, isv_c, jsv_c, isv_o, jsv_o
1796 real, dimension(:,:,:), allocatable, target :: locfield_dsamp ! A downsampled version of locfield [a]
1797 real, dimension(:,:,:), allocatable, target :: locmask_dsamp ! A downsampled version of locmask [nondim]
1798 real, dimension(:,:,:), pointer :: ones => null() ! An array of ones for testing where masks do not apply [nondim]
1799 real, dimension(:,:,:), pointer :: mask_in => null() ! A pointer to the input mask [nondim]
1800 integer :: dl, dlfac
1801
1802 integer :: time_days
1803 integer :: time_seconds
1804 character(len=300) :: debug_mesg
1805
1806 locfield => null()
1807 locmask => null()
1808 is_stat = .false. ; if (present(is_static)) is_stat = is_static
1809 not_static = .not. is_stat
1810
1811 ! Determine the proper array indices, noting that because of the (:,:)
1812 ! declaration of field, symmetric arrays are using a SW-grid indexing,
1813 ! but non-symmetric arrays are using a NE-grid indexing. Send_data
1814 ! actually only uses the difference between ie and is to determine
1815 ! the output data size and assumes that halos are symmetric.
1816 !isv = diag_cs%is ; iev = diag_cs%ie ; jsv = diag_cs%js ; jev = diag_cs%je
1817
1818 cszi = (diag_cs%ie-diag_cs%is) +1 ; dszi = (diag_cs%ied-diag_cs%isd) +1
1819 cszj = (diag_cs%je-diag_cs%js) +1 ; dszj = (diag_cs%jed-diag_cs%jsd) +1
1820 if ( size(field,1) == dszi ) then
1821 isv = diag_cs%is ; iev = diag_cs%ie ! Data domain
1822 elseif ( size(field,1) == dszi + 1 ) then
1823 isv = diag_cs%is ; iev = diag_cs%ie+1 ! Symmetric data domain
1824 elseif ( size(field,1) == cszi) then
1825 isv = 1 ; iev = cszi ! Computational domain
1826 elseif ( size(field,1) == cszi + 1 ) then
1827 isv = 1 ; iev = cszi+1 ! Symmetric computational domain
1828 else
1829 write (mesg,*) " peculiar size ",size(field,1)," in i-direction\n"//&
1830 "does not match one of ", cszi, cszi+1, dszi, dszi+1
1831 call mom_error(fatal,"post_data_3d_low: "//trim(diag%debug_str)//trim(mesg))
1832 endif
1833
1834 if ( size(field,2) == dszj ) then
1835 jsv = diag_cs%js ; jev = diag_cs%je ! Data domain
1836 elseif ( size(field,2) == dszj + 1 ) then
1837 jsv = diag_cs%js ; jev = diag_cs%je+1 ! Symmetric data domain
1838 elseif ( size(field,2) == cszj ) then
1839 jsv = 1 ; jev = cszj ! Computational domain
1840 elseif ( size(field,2) == cszj+1 ) then
1841 jsv = 1 ; jev = cszj+1 ! Symmetric computational domain
1842 else
1843 write (mesg,*) " peculiar size ",size(field,2)," in j-direction\n"//&
1844 "does not match one of ", cszj, cszj+1, dszj, dszj+1
1845 call mom_error(fatal,"post_data_3d_low: "//trim(diag%debug_str)//trim(mesg))
1846 endif
1847
1848 ks = lbound(field,3) ; ke = ubound(field,3)
1849 if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) then
1850 allocate( locfield( lbound(field,1):ubound(field,1), lbound(field,2):ubound(field,2), ks:ke ) )
1851 ! locfield(:,:,:) = 0.0 ! Zeroing out this array would be a good idea, but it appears
1852 ! not to be necessary.
1853 isv_c = isv ; jsv_c = jsv
1854 if (diag%fms_xyave_diag_id>0) then
1855 staggered_in_x = diag%axes%is_u_point .or. diag%axes%is_q_point
1856 staggered_in_y = diag%axes%is_v_point .or. diag%axes%is_q_point
1857 ! When averaging a staggered field, edge points are always required.
1858 if (staggered_in_x) isv_c = iev - (diag_cs%ie - diag_cs%is) - 1
1859 if (staggered_in_y) jsv_c = jev - (diag_cs%je - diag_cs%js) - 1
1860 if (isv_c < lbound(locfield,1)) call mom_error(fatal, &
1861 "It is an error to average a staggered diagnostic field that does not "//&
1862 "have i-direction space to represent the symmetric computational domain.")
1863 if (jsv_c < lbound(locfield,2)) call mom_error(fatal, &
1864 "It is an error to average a staggered diagnostic field that does not "//&
1865 "have j-direction space to represent the symmetric computational domain.")
1866 endif
1867
1868 do k=ks,ke ; do j=jsv,jev ; do i=isv,iev
1869 locfield(i,j,k) = field(i,j,k) * diag%conversion_factor
1870 enddo ; enddo ; enddo
1871 else
1872 locfield => field
1873 endif
1874
1875 if (present(mask)) then
1876 locmask => mask
1877 elseif (associated(diag%axes) .and. (not_static)) then
1878 ! If we were to decide to allow masking of static diagnostics, we could do so by changing the line above to
1879 ! elseif (associated(diag%axes) .and. (diag_CS%mask_static_diags .or. not_static)) then
1880 if (associated(diag%axes%mask3d)) locmask => diag%axes%mask3d
1881 endif
1882
1883 dlfac = 1
1884 if (not_static .and. associated(diag%axes)) &
1885 dlfac = diag%axes%downsample_level_factor ! Static field downsampling is not supported yet.
1886 ! Downsample the diag field and mask as appropriate.
1887 if (dlfac > 1) then
1888 dl = diag%axes%downsample_level_index
1889 isv_o = isv ; jsv_o = jsv
1890 !Note that downsample_diag_field_3d takes the downsampling index
1891 call downsample_diag_field(locfield, locfield_dsamp, dl, diag_cs, diag, isv, iev, jsv, jev, mask)
1892 if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) deallocate( locfield )
1893 locfield => locfield_dsamp
1894 if (present(mask)) then
1895 ! Replicate the downsampling of other fields to find unmasked points.
1896 allocate(ones, mold=locmask) ; ones(:,:,:) = 1.0
1897 mask_in => mask
1898 call downsample_field_3d(ones, locmask_dsamp, dlfac, diag%xyz_method, mask_in, diag_cs, diag, &
1899 isv_o, jsv_o, isv, iev, jsv, jev)
1900 deallocate(ones)
1901 where (abs(locmask_dsamp) > 0.0) locmask_dsamp = 1.0
1902 locmask => locmask_dsamp
1903 elseif (associated(diag%axes%dsamp(dl)%mask3d)) then
1904 locmask => diag%axes%dsamp(dl)%mask3d
1905 endif
1906 endif
1907 if (associated(locmask)) call assert(size(locfield) == size(locmask), &
1908 'post_data_3d_low: mask size mismatch: '//trim(diag%debug_str))
1909
1910 if (diag%fms_diag_id>0) then
1911 if (diag_cs%diag_as_chksum) then
1912 ! Append timestep to mesg
1913 call get_time(diag_cs%time_end, time_seconds, days=time_days)
1914 write(debug_mesg, '(a, 1x, i0, 1x, i0)') &
1915 trim(diag%debug_str), time_days, time_seconds
1916
1917 if (diag%axes%is_h_point) then
1918 call hchksum(locfield, debug_mesg, diag_cs%G%HI, &
1919 logunit=diag_cs%chksum_iounit)
1920 elseif (diag%axes%is_u_point) then
1921 call uchksum(locfield, debug_mesg, diag_cs%G%HI, &
1922 logunit=diag_cs%chksum_iounit)
1923 elseif (diag%axes%is_v_point) then
1924 call vchksum(locfield, debug_mesg, diag_cs%G%HI, &
1925 logunit=diag_cs%chksum_iounit)
1926 elseif (diag%axes%is_q_point) then
1927 call bchksum(locfield, debug_mesg, diag_cs%G%HI, &
1928 logunit=diag_cs%chksum_iounit)
1929 else
1930 call mom_error(fatal, "post_data_3d_low: unknown axis type.")
1931 endif
1932 else
1933 if (is_stat) then
1934 if (associated(locmask)) then
1935 used = send_data_infra(diag%fms_diag_id, locfield, &
1936 is_in=isv, ie_in=iev, js_in=jsv, je_in=jev, rmask=locmask)
1937 else
1938 used = send_data_infra(diag%fms_diag_id, locfield, &
1939 is_in=isv, ie_in=iev, js_in=jsv, je_in=jev)
1940 endif
1941 elseif (diag_cs%ave_enabled) then
1942 if (associated(locmask)) then
1943 used = send_data_infra(diag%fms_diag_id, locfield, &
1944 is_in=isv, ie_in=iev, js_in=jsv, je_in=jev, &
1945 time=diag_cs%time_end, weight=diag_cs%time_int, rmask=locmask)
1946 else
1947 used = send_data_infra(diag%fms_diag_id, locfield, &
1948 is_in=isv, ie_in=iev, js_in=jsv, je_in=jev, &
1949 time=diag_cs%time_end, weight=diag_cs%time_int)
1950 endif
1951 endif
1952 endif
1953 endif
1954
1955 if (diag%fms_xyave_diag_id>0 .and. dlfac<2) then
1956 call post_xy_average(diag_cs, diag, locfield)
1957 endif
1958
1959 if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.) .and. dl<2) &
1960 deallocate( locfield )
1961
1962end subroutine post_data_3d_low
1963
1964!> Put data into the buffer for a diagnostic one column at a time
1965subroutine post_data_3d_by_column(diag_field_id, field, diag_cs, i, j)
1966 integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
1967 !! previous call to register_diag_field.
1968 real, dimension(:), intent(in) :: field !< 3-d array being offered for output or averaging
1969 !! in internally scaled arbitrary units [A ~> a]
1970 type(diag_ctrl), target, intent(in) :: diag_cs !< Structure used to regulate diagnostic output
1971 integer, intent(in) :: i !< The i-index to post the data in the buffer
1972 integer, intent(in) :: j !< The j-index to post the data in the buffer
1973
1974 type(diag_type), pointer :: diag => null()
1975 integer :: buffer_slot
1976
1977 diag => diag_cs%diags(diag_field_id)
1978 buffer_slot = diag%axes%piecemeal_3d%check_capacity_by_id(diag_field_id)
1979 diag%axes%piecemeal_3d%buffer(buffer_slot)%field(i,j,:) = field(:)
1980end subroutine post_data_3d_by_column
1981
1982!> Put data into the buffer for a diagnostic one point at a time
1983subroutine post_data_3d_by_point(diag_field_id, field, diag_cs, i, j, k)
1984 integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
1985 !! previous call to register_diag_field.
1986 real, intent(in) :: field !< 3-d array being offered for output or averaging
1987 !! in internally scaled arbitrary units [A ~> a]
1988 type(diag_ctrl), target, intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1989 integer, intent(in) :: i !< The i-index to post the data in the buffer
1990 integer, intent(in) :: j !< The j-index to post the data in the buffer
1991 integer, intent(in) :: k !< The k-index to post the data in the buffer
1992
1993 type(diag_type), pointer :: diag => null()
1994 integer :: buffer_slot
1995
1996 diag => diag_cs%diags(diag_field_id)
1997 buffer_slot = diag%axes%piecemeal_3d%check_capacity_by_id(diag_field_id)
1998 diag%axes%piecemeal_3d%buffer(buffer_slot)%field(i,j,k) = field
1999end subroutine post_data_3d_by_point
2000
2001!> Post the final buffer using the standard post_data interface
2002subroutine post_data_3d_final(diag_field_id, diag_cs)
2003 integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
2004 !! previous call to register_diag_field.
2005 type(diag_ctrl), target, intent(in) :: diag_cs !< Structure used to regulate diagnostic output
2006
2007 type(diag_type), pointer :: diag => null()
2008 integer :: buffer_slot
2009
2010 diag => diag_cs%diags(diag_field_id)
2011 buffer_slot = diag%axes%piecemeal_3d%find_buffer_slot(diag_field_id)
2012 ! Only perform an action if the buffer slot was actually used
2013 if (buffer_slot>0) then
2014 call post_data(diag_field_id, diag%axes%piecemeal_3d%buffer(buffer_slot)%field(:,:,:), diag_cs)
2015 call diag%axes%piecemeal_3d%mark_available(diag_field_id)
2016 endif
2017end subroutine post_data_3d_final
2018
2019!> Calculate and write out diagnostics that are the product of two 3-d arrays at u-points
2020subroutine post_product_u(id, u_a, u_b, G, nz, diag, mask, alt_h)
2021 integer, intent(in) :: id !< The ID for this diagnostic
2022 type(ocean_grid_type), intent(in) :: g !< ocean grid structure
2023 integer, intent(in) :: nz !< The size of the arrays in the vertical
2024 real, dimension(G%IsdB:G%IedB, G%jsd:G%jed, nz), &
2025 intent(in) :: u_a !< The first u-point array in arbitrary units [A]
2026 real, dimension(G%IsdB:G%IedB, G%jsd:G%jed, nz), &
2027 intent(in) :: u_b !< The second u-point array in arbitrary units [B]
2028 type(diag_ctrl), intent(in) :: diag !< regulates diagnostic output
2029 real, optional, intent(in) :: mask(:,:,:) !< If present, use this real array as the data mask [nondim]
2030 real, target, optional, intent(in) :: alt_h(:,:,:) !< An alternate thickness to use for vertically
2031 !! remapping this diagnostic [H ~> m or kg m-2]
2032
2033 ! Local variables
2034 real, dimension(G%IsdB:G%IedB, G%jsd:G%jed, nz) :: u_prod ! The product of u_a and u_b [A B]
2035 integer :: i, j, k
2036
2037 if (id <= 0) return
2038
2039 do k=1,nz ; do j=g%jsc,g%jec ; do i=g%IscB,g%IecB
2040 u_prod(i,j,k) = u_a(i,j,k) * u_b(i,j,k)
2041 enddo ; enddo ; enddo
2042 call post_data(id, u_prod, diag, mask=mask, alt_h=alt_h)
2043
2044end subroutine post_product_u
2045
2046!> Calculate and write out diagnostics that are the vertical sum of the product of two 3-d arrays at u-points
2047subroutine post_product_sum_u(id, u_a, u_b, G, nz, diag)
2048 integer, intent(in) :: id !< The ID for this diagnostic
2049 type(ocean_grid_type), intent(in) :: g !< ocean grid structure
2050 integer, intent(in) :: nz !< The size of the arrays in the vertical
2051 real, dimension(G%IsdB:G%IedB, G%jsd:G%jed, nz), &
2052 intent(in) :: u_a !< The first u-point array in arbitrary units [A]
2053 real, dimension(G%IsdB:G%IedB, G%jsd:G%jed, nz), &
2054 intent(in) :: u_b !< The second u-point array in arbitrary units [B]
2055 type(diag_ctrl), intent(in) :: diag !< regulates diagnostic output
2056
2057 real, dimension(G%IsdB:G%IedB, G%jsd:G%jed) :: u_sum ! The vertical sum of the product of u_a and u_b [A B]
2058 integer :: i, j, k
2059
2060 if (id <= 0) return
2061
2062 u_sum(:,:) = 0.0
2063 do k=1,nz ; do j=g%jsc,g%jec ; do i=g%IscB,g%IecB
2064 u_sum(i,j) = u_sum(i,j) + u_a(i,j,k) * u_b(i,j,k)
2065 enddo ; enddo ; enddo
2066 call post_data(id, u_sum, diag)
2067
2068end subroutine post_product_sum_u
2069
2070!> Calculate and write out diagnostics that are the product of two 3-d arrays at v-points
2071subroutine post_product_v(id, v_a, v_b, G, nz, diag, mask, alt_h)
2072 integer, intent(in) :: id !< The ID for this diagnostic
2073 type(ocean_grid_type), intent(in) :: g !< ocean grid structure
2074 integer, intent(in) :: nz !< The size of the arrays in the vertical
2075 real, dimension(G%isd:G%ied, G%JsdB:G%JedB, nz), &
2076 intent(in) :: v_a !< The first v-point array in arbitrary units [A]
2077 real, dimension(G%isd:G%ied, G%JsdB:G%JedB, nz), &
2078 intent(in) :: v_b !< The second v-point array in arbitrary units [B]
2079 type(diag_ctrl), intent(in) :: diag !< regulates diagnostic output
2080 real, optional, intent(in) :: mask(:,:,:) !< If present, use this real array as the data mask [nondim]
2081 real, target, optional, intent(in) :: alt_h(:,:,:) !< An alternate thickness to use for vertically
2082 !! remapping this diagnostic [H ~> m or kg m-2]
2083
2084 ! Local variables
2085 real, dimension(G%isd:G%ied, G%JsdB:G%JedB, nz) :: v_prod ! The product of v_a and v_b [A B]
2086 integer :: i, j, k
2087
2088 if (id <= 0) return
2089
2090 do k=1,nz ; do j=g%JscB,g%JecB ; do i=g%isc,g%iec
2091 v_prod(i,j,k) = v_a(i,j,k) * v_b(i,j,k)
2092 enddo ; enddo ; enddo
2093 call post_data(id, v_prod, diag, mask=mask, alt_h=alt_h)
2094
2095end subroutine post_product_v
2096
2097!> Calculate and write out diagnostics that are the vertical sum of the product of two 3-d arrays at v-points
2098subroutine post_product_sum_v(id, v_a, v_b, G, nz, diag)
2099 integer, intent(in) :: id !< The ID for this diagnostic
2100 type(ocean_grid_type), intent(in) :: g !< ocean grid structure
2101 integer, intent(in) :: nz !< The size of the arrays in the vertical
2102 real, dimension(G%isd:G%ied, G%JsdB:G%JedB, nz), &
2103 intent(in) :: v_a !< The first v-point array in arbitrary units [A]
2104 real, dimension(G%isd:G%ied, G%JsdB:G%JedB, nz), &
2105 intent(in) :: v_b !< The second v-point array in arbitrary units [B]
2106 type(diag_ctrl), intent(in) :: diag !< regulates diagnostic output
2107
2108 real, dimension(G%isd:G%ied, G%JsdB:G%JedB) :: v_sum ! The vertical sum of the product of v_a and v_b [A B]
2109 integer :: i, j, k
2110
2111 if (id <= 0) return
2112
2113 v_sum(:,:) = 0.0
2114 do k=1,nz ; do j=g%JscB,g%JecB ; do i=g%isc,g%iec
2115 v_sum(i,j) = v_sum(i,j) + v_a(i,j,k) * v_b(i,j,k)
2116 enddo ; enddo ; enddo
2117 call post_data(id, v_sum, diag)
2118
2119end subroutine post_product_sum_v
2120
2121!> Post the horizontally area-averaged diagnostic
2122subroutine post_xy_average(diag_cs, diag, field)
2123 type(diag_type), intent(in) :: diag !< This diagnostic
2124 real, target, intent(in) :: field(:,:,:) !< Diagnostic field in arbitrary units [A ~> a]
2125 type(diag_ctrl), intent(in) :: diag_cs !< Diagnostics mediator control structure
2126 ! Local variable
2127 real, dimension(size(field,3)) :: averaged_field ! The horizontally averaged field [A ~> a]
2128 logical, dimension(size(field,3)) :: averaged_mask
2129 logical :: staggered_in_x, staggered_in_y, used
2130 integer :: nz, remap_nz, coord
2131
2132 integer :: time_days
2133 integer :: time_seconds
2134 character(len=300) :: debug_mesg
2135
2136 if (.not. diag_cs%ave_enabled) then
2137 return
2138 endif
2139
2140 staggered_in_x = diag%axes%is_u_point .or. diag%axes%is_q_point
2141 staggered_in_y = diag%axes%is_v_point .or. diag%axes%is_q_point
2142
2143 if (diag%axes%is_native) then
2144 call horizontally_average_diag_field(diag_cs%G, diag_cs%GV, diag_cs%h, &
2145 staggered_in_x, staggered_in_y, &
2146 diag%axes%is_layer, diag%v_extensive, &
2147 field, averaged_field, averaged_mask)
2148 else
2149 nz = size(field, 3)
2150 coord = diag%axes%vertical_coordinate_number
2151 remap_nz = diag_cs%diag_remap_cs(coord)%nz
2152
2153 call assert(diag_cs%diag_remap_cs(coord)%initialized, &
2154 'post_xy_average: remap_cs not initialized.')
2155
2156 call assert(implies(diag%axes%is_layer, nz == remap_nz), &
2157 'post_xy_average: layer field dimension mismatch.')
2158 call assert(implies(.not. diag%axes%is_layer, nz == remap_nz+1), &
2159 'post_xy_average: interface field dimension mismatch.')
2160
2161 call horizontally_average_diag_field(diag_cs%G, diag_cs%GV, &
2162 diag_cs%diag_remap_cs(coord)%h, &
2163 staggered_in_x, staggered_in_y, &
2164 diag%axes%is_layer, diag%v_extensive, &
2165 field, averaged_field, averaged_mask)
2166 endif
2167
2168 if (diag_cs%diag_as_chksum) then
2169 ! Append timestep to mesg
2170 call get_time(diag_cs%time_end, time_seconds, days=time_days)
2171 write(debug_mesg, '(a, 1x, i0, 1x, i0)') &
2172 trim(diag%debug_str)//'_xyave', time_days, time_seconds
2173
2174 call zchksum(averaged_field, debug_mesg, logunit=diag_cs%chksum_iounit)
2175 else
2176 used = send_data_infra(diag%fms_xyave_diag_id, averaged_field, &
2177 time=diag_cs%time_end, weight=diag_cs%time_int, mask=averaged_mask)
2178 endif
2179end subroutine post_xy_average
2180
2181!> This subroutine enables the accumulation of time averages over the specified time interval.
2182subroutine enable_averaging(time_int_in, time_end_in, diag_cs)
2183 real, intent(in) :: time_int_in !< The time interval [s] over which any
2184 !! values that are offered are valid.
2185 type(time_type), intent(in) :: time_end_in !< The end time of the valid interval
2186 type(diag_ctrl), intent(inout) :: diag_cs !< Structure used to regulate diagnostic output
2187
2188! This subroutine enables the accumulation of time averages over the specified time interval.
2189
2190! if (num_file==0) return
2191 diag_cs%time_int = time_int_in
2192 diag_cs%time_end = time_end_in
2193 diag_cs%ave_enabled = .true.
2194end subroutine enable_averaging
2195
2196!> Enable the accumulation of time averages over the specified time interval in time units.
2197subroutine enable_averages(time_int, time_end, diag_CS, T_to_s)
2198 real, intent(in) :: time_int !< The time interval over which any values
2199 !! that are offered are valid [T ~> s].
2200 type(time_type), intent(in) :: time_end !< The end time of the valid interval.
2201 type(diag_ctrl), intent(inout) :: diag_cs !< A structure that is used to regulate diagnostic output
2202 real, optional, intent(in) :: t_to_s !< A conversion factor for time_int to seconds [s T-1 ~> 1].
2203 ! This subroutine enables the accumulation of time averages over the specified time interval.
2204
2205 if (present(t_to_s)) then
2206 diag_cs%time_int = time_int*t_to_s
2207 elseif (associated(diag_cs%US)) then
2208 diag_cs%time_int = time_int*diag_cs%US%T_to_s
2209 else
2210 diag_cs%time_int = time_int
2211 endif
2212 diag_cs%time_end = time_end
2213 diag_cs%ave_enabled = .true.
2214end subroutine enable_averages
2215
2216!> Call this subroutine to avoid averaging any offered fields.
2217subroutine disable_averaging(diag_cs)
2218 type(diag_ctrl), intent(inout) :: diag_cs !< Structure used to regulate diagnostic output
2219
2220 diag_cs%time_int = 0.0
2221 diag_cs%ave_enabled = .false.
2222end subroutine disable_averaging
2223
2224!> Call this subroutine to determine whether the averaging is
2225!! currently enabled. .true. is returned if it is.
2226function query_averaging_enabled(diag_cs, time_int, time_end)
2227 type(diag_ctrl), intent(in) :: diag_cs !< Structure used to regulate diagnostic output
2228 real, optional, intent(out) :: time_int !< Current setting of diag%time_int [s]
2229 type(time_type), optional, intent(out) :: time_end !< Current setting of diag%time_end
2230 logical :: query_averaging_enabled
2231
2232 if (present(time_int)) time_int = diag_cs%time_int
2233 if (present(time_end)) time_end = diag_cs%time_end
2234 query_averaging_enabled = diag_cs%ave_enabled
2235end function query_averaging_enabled
2236
2237!> This function returns the valid end time for use with diagnostics that are
2238!! handled outside of the MOM6 diagnostics infrastructure.
2239function get_diag_time_end(diag_cs)
2240 type(diag_ctrl), intent(in) :: diag_cs !< Structure used to regulate diagnostic output
2241 type(time_type) :: get_diag_time_end
2242 ! This function returns the valid end time for diagnostics that are handled
2243 ! outside of the MOM6 infrastructure, such as via the generic tracer code.
2244
2245 get_diag_time_end = diag_cs%time_end
2246end function get_diag_time_end
2247
2248!> Returns the "diag_mediator" handle for a group (native, CMOR, z-coord, ...) of diagnostics
2249!! derived from one field.
2250integer function register_diag_field(module_name, field_name, axes_in, init_time, &
2251 long_name, units, missing_value, range, mask_variant, standard_name, &
2252 verbose, do_not_log, err_msg, interp_method, tile_count, cmor_field_name, &
2253 cmor_long_name, cmor_units, cmor_standard_name, cell_methods, &
2254 x_cell_method, y_cell_method, v_cell_method, conversion, v_extensive)
2255 character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model"
2256 !! or "ice_shelf_model"
2257 character(len=*), intent(in) :: field_name !< Name of the diagnostic field
2258 type(axes_grp), target, intent(in) :: axes_in !< Container w/ up to 3 integer handles that
2259 !! indicates axes for this field
2260 type(time_type), intent(in) :: init_time !< Time at which a field is first available?
2261 character(len=*), optional, intent(in) :: long_name !< Long name of a field.
2262 character(len=*), optional, intent(in) :: units !< Units of a field.
2263 character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
2264 real, optional, intent(in) :: missing_value !< A value that indicates missing values in
2265 !! output files, in unscaled arbitrary units [a]
2266 real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
2267 !! in arbitrary units [a]
2268 logical, optional, intent(in) :: mask_variant !< If true a logical mask must be provided with
2269 !! post_data calls (not used in MOM?)
2270 logical, optional, intent(in) :: verbose !< If true, FMS is verbose (not used in MOM?)
2271 logical, optional, intent(in) :: do_not_log !< If true, do not log something (not used in MOM?)
2272 character(len=*), optional, intent(out):: err_msg !< String into which an error message might be
2273 !! placed (not used in MOM?)
2274 character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should not
2275 !! be interpolated as a scalar
2276 integer, optional, intent(in) :: tile_count !< no clue (not used in MOM?)
2277 character(len=*), optional, intent(in) :: cmor_field_name !< CMOR name of a field
2278 character(len=*), optional, intent(in) :: cmor_long_name !< CMOR long name of a field
2279 character(len=*), optional, intent(in) :: cmor_units !< CMOR units of a field
2280 character(len=*), optional, intent(in) :: cmor_standard_name !< CMOR standardized name associated with a field
2281 character(len=*), optional, intent(in) :: cell_methods !< String to append as cell_methods attribute. Use '' to
2282 !! have no attribute. If present, this overrides the
2283 !! default constructed from the default for
2284 !! each individual axis direction.
2285 character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction.
2286 !! Use '' have no method.
2287 character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction.
2288 !! Use '' have no method.
2289 character(len=*), optional, intent(in) :: v_cell_method !< Specifies the cell method for the vertical direction.
2290 !! Use '' have no method.
2291 real, optional, intent(in) :: conversion !< A value to multiply data by before writing to files,
2292 !! often including factors to undo internal scaling and
2293 !! in units of [a A-1 ~> 1]
2294 logical, optional, intent(in) :: v_extensive !< True for vertically extensive fields (vertically
2295 !! integrated). Default/absent for intensive.
2296 ! Local variables
2297 real :: mom_missing_value ! A value used to indicate missing values in output files, in arbitrary units [a]
2298 type(diag_ctrl), pointer :: diag_cs => null() ! A structure that is used to regulate diagnostic output
2299 type(axes_grp), pointer :: remap_axes
2300 type(axes_grp), pointer :: axes
2301 type(axes_grp), pointer :: axes_d2
2302 integer :: dm_id, i, dl
2303 character(len=256) :: msg, cm_string
2304 character(len=256) :: new_module_name
2305 character(len=480) :: module_list, var_list
2306 character(len=24) :: dimensions
2307 integer :: num_modnm, num_varnm
2308 logical :: active
2309 character(len=2) :: dl_str
2310
2311 diag_cs => axes_in%diag_cs
2312
2313 ! Check if the axes match a standard grid axis.
2314 ! If not, allocate the new axis and copy the contents.
2315 if (axes_in%id == diag_cs%axesTL%id) then
2316 axes => diag_cs%axesTL
2317 elseif (axes_in%id == diag_cs%axesBL%id) then
2318 axes => diag_cs%axesBL
2319 elseif (axes_in%id == diag_cs%axesCuL%id) then
2320 axes => diag_cs%axesCuL
2321 elseif (axes_in%id == diag_cs%axesCvL%id) then
2322 axes => diag_cs%axesCvL
2323 elseif (axes_in%id == diag_cs%axesTi%id) then
2324 axes => diag_cs%axesTi
2325 elseif (axes_in%id == diag_cs%axesBi%id) then
2326 axes => diag_cs%axesBi
2327 elseif (axes_in%id == diag_cs%axesCui%id) then
2328 axes => diag_cs%axesCui
2329 elseif (axes_in%id == diag_cs%axesCvi%id) then
2330 axes => diag_cs%axesCvi
2331 elseif (axes_in%id == diag_cs%axesT1%id) then
2332 axes => diag_cs%axesT1
2333 elseif (axes_in%id == diag_cs%axesB1%id) then
2334 axes => diag_cs%axesB1
2335 elseif (axes_in%id == diag_cs%axesCu1%id) then
2336 axes => diag_cs%axesCu1
2337 elseif (axes_in%id == diag_cs%axesCv1%id) then
2338 axes => diag_cs%axesCv1
2339 else
2340 allocate(axes)
2341 axes = axes_in
2342 endif
2343
2344 mom_missing_value = axes%diag_cs%missing_value
2345 if (present(missing_value)) mom_missing_value = missing_value
2346
2347 diag_cs => axes%diag_cs
2348 dm_id = -1
2349
2350 module_list = "{"//trim(module_name)
2351 num_modnm = 1
2352
2353 ! Register the native diagnostic
2354 active = register_diag_field_expand_cmor(dm_id, module_name, field_name, axes, &
2355 init_time, long_name=long_name, units=units, missing_value=mom_missing_value, &
2356 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2357 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2358 interp_method=interp_method, tile_count=tile_count, &
2359 cmor_field_name=cmor_field_name, cmor_long_name=cmor_long_name, &
2360 cmor_units=cmor_units, cmor_standard_name=cmor_standard_name, &
2361 cell_methods=cell_methods, x_cell_method=x_cell_method, &
2362 y_cell_method=y_cell_method, v_cell_method=v_cell_method, &
2363 conversion=conversion, v_extensive=v_extensive)
2364 if (associated(axes%xyave_axes)) then
2365 num_varnm = 2 ; var_list = "{"//trim(field_name)//","//trim(field_name)//"_xyave"
2366 else
2367 num_varnm = 1 ; var_list = "{"//trim(field_name)
2368 endif
2369 if (present(cmor_field_name)) then
2370 if (associated(axes%xyave_axes)) then
2371 num_varnm = num_varnm + 2
2372 var_list = trim(var_list)//","//trim(cmor_field_name)//","//trim(cmor_field_name)//"_xyave"
2373 else
2374 num_varnm = num_varnm + 1
2375 var_list = trim(var_list)//","//trim(cmor_field_name)
2376 endif
2377 endif
2378 var_list = trim(var_list)//"}"
2379
2380 ! For each diagnostic coordinate register the diagnostic again under a different module name
2381 do i=1,diag_cs%num_diag_coords
2382 new_module_name = trim(module_name)//'_'//trim(diag_cs%diag_remap_cs(i)%diag_module_suffix)
2383
2384 ! Register diagnostics remapped to z vertical coordinate
2385 if (axes_in%rank == 3) then
2386 remap_axes => null()
2387 if ((axes_in%id == diag_cs%axesTL%id)) then
2388 remap_axes => diag_cs%remap_axesTL(i)
2389 elseif (axes_in%id == diag_cs%axesBL%id) then
2390 remap_axes => diag_cs%remap_axesBL(i)
2391 elseif (axes_in%id == diag_cs%axesCuL%id ) then
2392 remap_axes => diag_cs%remap_axesCuL(i)
2393 elseif (axes_in%id == diag_cs%axesCvL%id) then
2394 remap_axes => diag_cs%remap_axesCvL(i)
2395 elseif (axes_in%id == diag_cs%axesTi%id) then
2396 remap_axes => diag_cs%remap_axesTi(i)
2397 elseif (axes_in%id == diag_cs%axesBi%id) then
2398 remap_axes => diag_cs%remap_axesBi(i)
2399 elseif (axes_in%id == diag_cs%axesCui%id ) then
2400 remap_axes => diag_cs%remap_axesCui(i)
2401 elseif (axes_in%id == diag_cs%axesCvi%id) then
2402 remap_axes => diag_cs%remap_axesCvi(i)
2403 endif
2404 ! When the MOM_diag_to_Z module has been obsoleted we can assume remap_axes will
2405 ! always exist but in the mean-time we have to do this check:
2406 ! call assert(associated(remap_axes), 'register_diag_field: remap_axes not set')
2407 if (associated(remap_axes)) then
2408 if (remap_axes%needs_remapping .or. remap_axes%needs_interpolating) then
2409 active = register_diag_field_expand_cmor(dm_id, new_module_name, field_name, remap_axes, &
2410 init_time, long_name=long_name, units=units, missing_value=mom_missing_value, &
2411 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2412 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2413 interp_method=interp_method, tile_count=tile_count, &
2414 cmor_field_name=cmor_field_name, cmor_long_name=cmor_long_name, &
2415 cmor_units=cmor_units, cmor_standard_name=cmor_standard_name, &
2416 cell_methods=cell_methods, x_cell_method=x_cell_method, &
2417 y_cell_method=y_cell_method, v_cell_method=v_cell_method, &
2418 conversion=conversion, v_extensive=v_extensive)
2419 if (active) then
2420 call diag_remap_set_active(diag_cs%diag_remap_cs(i))
2421 endif
2422 module_list = trim(module_list)//","//trim(new_module_name)
2423 num_modnm = num_modnm + 1
2424 endif ! remap_axes%needs_remapping
2425 endif ! associated(remap_axes)
2426 endif ! axes%rank == 3
2427 enddo ! i
2428
2429 ! Register downsampled diagnostics
2430 do dl=1, diag_cs%num_diag_dsamp_levels
2431 ! Do not attempt to checksum the downsampled diagnostics
2432 if (diag_cs%diag_as_chksum) cycle
2433
2434 write(dl_str, '(i0)') diag_cs%diag_dsamp_levels(dl)
2435 new_module_name = trim(module_name)//'_d'//trim(dl_str)
2436
2437 axes_d2 => null()
2438 if (axes_in%rank == 3 .or. axes_in%rank == 2 ) then
2439 if (axes_in%id == diag_cs%axesTL%id) then
2440 axes_d2 => diag_cs%dsamp(dl)%axesTL
2441 elseif (axes_in%id == diag_cs%axesBL%id) then
2442 axes_d2 => diag_cs%dsamp(dl)%axesBL
2443 elseif (axes_in%id == diag_cs%axesCuL%id ) then
2444 axes_d2 => diag_cs%dsamp(dl)%axesCuL
2445 elseif (axes_in%id == diag_cs%axesCvL%id) then
2446 axes_d2 => diag_cs%dsamp(dl)%axesCvL
2447 elseif (axes_in%id == diag_cs%axesTi%id) then
2448 axes_d2 => diag_cs%dsamp(dl)%axesTi
2449 elseif (axes_in%id == diag_cs%axesBi%id) then
2450 axes_d2 => diag_cs%dsamp(dl)%axesBi
2451 elseif (axes_in%id == diag_cs%axesCui%id ) then
2452 axes_d2 => diag_cs%dsamp(dl)%axesCui
2453 elseif (axes_in%id == diag_cs%axesCvi%id) then
2454 axes_d2 => diag_cs%dsamp(dl)%axesCvi
2455 elseif (axes_in%id == diag_cs%axesT1%id) then
2456 axes_d2 => diag_cs%dsamp(dl)%axesT1
2457 elseif (axes_in%id == diag_cs%axesB1%id) then
2458 axes_d2 => diag_cs%dsamp(dl)%axesB1
2459 elseif (axes_in%id == diag_cs%axesCu1%id ) then
2460 axes_d2 => diag_cs%dsamp(dl)%axesCu1
2461 elseif (axes_in%id == diag_cs%axesCv1%id) then
2462 axes_d2 => diag_cs%dsamp(dl)%axesCv1
2463 else
2464 !Niki: Should we worry about these, e.g., diag_to_Z_CS?
2465 call mom_error(warning,"register_diag_field: Could not find a proper axes for " &
2466 //trim(new_module_name)//"-"//trim(field_name))
2467 endif
2468 endif
2469
2470 ! Register the native diagnostic
2471 if (associated(axes_d2)) then
2472 active = register_diag_field_expand_cmor(dm_id, new_module_name, field_name, axes_d2, &
2473 init_time, long_name=long_name, units=units, missing_value=mom_missing_value, &
2474 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2475 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2476 interp_method=interp_method, tile_count=tile_count, &
2477 cmor_field_name=cmor_field_name, cmor_long_name=cmor_long_name, &
2478 cmor_units=cmor_units, cmor_standard_name=cmor_standard_name, &
2479 cell_methods=cell_methods, x_cell_method=x_cell_method, &
2480 y_cell_method=y_cell_method, v_cell_method=v_cell_method, &
2481 conversion=conversion, v_extensive=v_extensive)
2482 module_list = trim(module_list)//","//trim(new_module_name)
2483 num_modnm = num_modnm + 1
2484 endif
2485
2486 ! For each diagnostic coordinate register the diagnostic again under a different module name
2487 do i=1,diag_cs%num_diag_coords
2488 new_module_name = trim(module_name)//'_'//trim(diag_cs%diag_remap_cs(i)%diag_module_suffix)//'_d'//trim(dl_str)
2489
2490 ! Register diagnostics remapped to z vertical coordinate
2491 if (axes_in%rank == 3) then
2492 remap_axes => null()
2493 if ((axes_in%id == diag_cs%axesTL%id)) then
2494 remap_axes => diag_cs%dsamp(dl)%remap_axesTL(i)
2495 elseif (axes_in%id == diag_cs%axesBL%id) then
2496 remap_axes => diag_cs%dsamp(dl)%remap_axesBL(i)
2497 elseif (axes_in%id == diag_cs%axesCuL%id ) then
2498 remap_axes => diag_cs%dsamp(dl)%remap_axesCuL(i)
2499 elseif (axes_in%id == diag_cs%axesCvL%id) then
2500 remap_axes => diag_cs%dsamp(dl)%remap_axesCvL(i)
2501 elseif (axes_in%id == diag_cs%axesTi%id) then
2502 remap_axes => diag_cs%dsamp(dl)%remap_axesTi(i)
2503 elseif (axes_in%id == diag_cs%axesBi%id) then
2504 remap_axes => diag_cs%dsamp(dl)%remap_axesBi(i)
2505 elseif (axes_in%id == diag_cs%axesCui%id ) then
2506 remap_axes => diag_cs%dsamp(dl)%remap_axesCui(i)
2507 elseif (axes_in%id == diag_cs%axesCvi%id) then
2508 remap_axes => diag_cs%dsamp(dl)%remap_axesCvi(i)
2509 endif
2510
2511 ! When the MOM_diag_to_Z module has been obsoleted we can assume remap_axes will
2512 ! always exist but in the mean-time we have to do this check:
2513 ! call assert(associated(remap_axes), 'register_diag_field: remap_axes not set')
2514 if (associated(remap_axes)) then
2515 if (remap_axes%needs_remapping .or. remap_axes%needs_interpolating) then
2516 active = register_diag_field_expand_cmor(dm_id, new_module_name, field_name, remap_axes, &
2517 init_time, long_name=long_name, units=units, missing_value=mom_missing_value, &
2518 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2519 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2520 interp_method=interp_method, tile_count=tile_count, &
2521 cmor_field_name=cmor_field_name, cmor_long_name=cmor_long_name, &
2522 cmor_units=cmor_units, cmor_standard_name=cmor_standard_name, &
2523 cell_methods=cell_methods, x_cell_method=x_cell_method, &
2524 y_cell_method=y_cell_method, v_cell_method=v_cell_method, &
2525 conversion=conversion, v_extensive=v_extensive)
2526 if (active) then
2527 call diag_remap_set_active(diag_cs%diag_remap_cs(i))
2528 endif
2529 module_list = trim(module_list)//","//trim(new_module_name)
2530 num_modnm = num_modnm + 1
2531 endif ! remap_axes%needs_remapping
2532 endif ! associated(remap_axes)
2533 endif ! axes%rank == 3
2534 enddo ! i
2535 enddo ! dl
2536
2537 dimensions = ""
2538 if (axes_in%is_h_point) dimensions = trim(dimensions)//" xh, yh,"
2539 if (axes_in%is_q_point) dimensions = trim(dimensions)//" xq, yq,"
2540 if (axes_in%is_u_point) dimensions = trim(dimensions)//" xq, yh,"
2541 if (axes_in%is_v_point) dimensions = trim(dimensions)//" xh, yq,"
2542 if (axes_in%is_layer) dimensions = trim(dimensions)//" zl,"
2543 if (axes_in%is_interface) dimensions = trim(dimensions)//" zi,"
2544 if (len_trim(dimensions) > 0) dimensions = trim_trailing_commas(dimensions)
2545
2546 if (is_root_pe() .and. (diag_cs%available_diag_doc_unit > 0)) then
2547 msg = ''
2548 if (present(cmor_field_name)) msg = 'CMOR equivalent is "'//trim(cmor_field_name)//'"'
2549 call attach_cell_methods(-1, axes, cm_string, cell_methods, &
2550 x_cell_method, y_cell_method, v_cell_method, &
2551 v_extensive=v_extensive)
2552 module_list = trim(module_list)//"}"
2553 if (num_modnm <= 1) module_list = module_name
2554 if (num_varnm <= 1) var_list = ''
2555
2556 call log_available_diag(dm_id>0, module_list, field_name, cm_string, msg, diag_cs, &
2557 long_name, units, standard_name, variants=var_list, dimensions=dimensions)
2558 endif
2559
2560 register_diag_field = dm_id
2561
2562end function register_diag_field
2563
2564!> Returns True if either the native or CMOR version of the diagnostic were registered. Updates 'dm_id'
2565!! after calling register_diag_field_expand_axes() for both native and CMOR variants of the field.
2566logical function register_diag_field_expand_cmor(dm_id, module_name, field_name, axes, init_time, &
2567 long_name, units, missing_value, range, mask_variant, standard_name, &
2568 verbose, do_not_log, err_msg, interp_method, tile_count, cmor_field_name, &
2569 cmor_long_name, cmor_units, cmor_standard_name, cell_methods, &
2570 x_cell_method, y_cell_method, v_cell_method, conversion, v_extensive)
2571 integer, intent(inout) :: dm_id !< The diag_mediator ID for this diagnostic group
2572 character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model" or "ice_shelf_model"
2573 character(len=*), intent(in) :: field_name !< Name of the diagnostic field
2574 type(axes_grp), intent(in) :: axes !< Container with up to 3 integer handles that indicates axes
2575 !! for this field
2576 type(time_type), intent(in) :: init_time !< Time at which a field is first available?
2577 character(len=*), optional, intent(in) :: long_name !< Long name of a field.
2578 character(len=*), optional, intent(in) :: units !< Units of a field.
2579 character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
2580 real, optional, intent(in) :: missing_value !< A value that indicates missing values in
2581 !! output files, in unscaled arbitrary units [a]
2582 real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
2583 !! in arbitrary units [a]
2584 logical, optional, intent(in) :: mask_variant !< If true a logical mask must be provided
2585 !! with post_data calls (not used in MOM?)
2586 logical, optional, intent(in) :: verbose !< If true, FMS is verbose (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
2591 !! not be interpolated as a scalar
2592 integer, optional, intent(in) :: tile_count !< no clue (not used in MOM?)
2593 character(len=*), optional, intent(in) :: cmor_field_name !< CMOR name of a field
2594 character(len=*), optional, intent(in) :: cmor_long_name !< CMOR long name of a field
2595 character(len=*), optional, intent(in) :: cmor_units !< CMOR units of a field
2596 character(len=*), optional, intent(in) :: cmor_standard_name !< CMOR standardized name associated with a field
2597 character(len=*), optional, intent(in) :: cell_methods !< String to append as cell_methods attribute.
2598 !! Use '' to have no attribute. If present, this
2599 !! overrides the default constructed from the default
2600 !! for each individual axis direction.
2601 character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction.
2602 !! Use '' have no method.
2603 character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction.
2604 !! Use '' have no method.
2605 character(len=*), optional, intent(in) :: v_cell_method !< Specifies the cell method for the vertical direction.
2606 !! Use '' have no method.
2607 real, optional, intent(in) :: conversion !< A value to multiply data by before writing to files,
2608 !! often including factors to undo internal scaling and
2609 !! in units of [a A-1 ~> 1]
2610 logical, optional, intent(in) :: v_extensive !< True for vertically extensive fields (vertically
2611 !! integrated). Default/absent for intensive.
2612 ! Local variables
2613 real :: mom_missing_value ! A value used to indicate missing values in output files, in arbitrary units [a]
2614 type(diag_ctrl), pointer :: diag_cs => null()
2615 type(diag_type), pointer :: this_diag => null()
2616 integer :: fms_id, fms_xyave_id
2617 character(len=256) :: posted_cmor_units, posted_cmor_standard_name, posted_cmor_long_name, cm_string
2618
2619 mom_missing_value = axes%diag_cs%missing_value
2620 if (present(missing_value)) mom_missing_value = missing_value
2621
2623 diag_cs => axes%diag_cs
2624
2625 ! Set up the 'primary' diagnostic, first get an underlying FMS id
2626 fms_id = register_diag_field_expand_axes(module_name, field_name, axes, init_time, &
2627 long_name=long_name, units=units, missing_value=mom_missing_value, &
2628 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2629 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2630 interp_method=interp_method, tile_count=tile_count)
2631 if (.not. diag_cs%diag_as_chksum) &
2632 call attach_cell_methods(fms_id, axes, cm_string, cell_methods, &
2633 x_cell_method, y_cell_method, v_cell_method, &
2634 v_extensive=v_extensive)
2635 ! Associated horizontally area-averaged diagnostic
2636 fms_xyave_id = diag_field_not_found
2637 if (associated(axes%xyave_axes)) then
2638 fms_xyave_id = register_diag_field_expand_axes(module_name, trim(field_name)//'_xyave', &
2639 axes%xyave_axes, init_time, &
2640 long_name=long_name, units=units, missing_value=mom_missing_value, &
2641 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2642 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2643 interp_method=interp_method, tile_count=tile_count)
2644 if (.not. diag_cs%diag_as_chksum) &
2645 call attach_cell_methods(fms_xyave_id, axes%xyave_axes, cm_string, &
2646 cell_methods, v_cell_method, v_extensive=v_extensive)
2647 endif
2648 this_diag => null()
2649 if (fms_id /= diag_field_not_found .or. fms_xyave_id /= diag_field_not_found) then
2650 call add_diag_to_list(diag_cs, dm_id, fms_id, this_diag, axes, module_name, field_name)
2651 this_diag%fms_xyave_diag_id = fms_xyave_id
2652 ! Encode and save the cell methods for this diagnostic
2653 this_diag%xyz_method = xyz_method(axes, x_cell_method, y_cell_method, v_cell_method, v_extensive)
2654 if (present(v_extensive)) this_diag%v_extensive = v_extensive
2655 if (present(conversion)) this_diag%conversion_factor = conversion
2657 endif
2658
2659 ! For the CMOR variation of the above diagnostic
2660 if (present(cmor_field_name) .and. .not. diag_cs%diag_as_chksum) then
2661 ! Fallback values for strings set to "NULL"
2662 posted_cmor_units = "not provided" !
2663 posted_cmor_standard_name = "not provided" ! Values might be able to be replaced with a CS%missing field?
2664 posted_cmor_long_name = "not provided" !
2665
2666 ! If attributes are present for MOM variable names, use them first for the register_diag_field
2667 ! call for CMOR version of the variable
2668 if (present(units)) posted_cmor_units = units
2669 if (present(standard_name)) posted_cmor_standard_name = standard_name
2670 if (present(long_name)) posted_cmor_long_name = long_name
2671
2672 ! If specified in the call to register_diag_field, override attributes with the CMOR versions
2673 if (present(cmor_units)) posted_cmor_units = cmor_units
2674 if (present(cmor_standard_name)) posted_cmor_standard_name = cmor_standard_name
2675 if (present(cmor_long_name)) posted_cmor_long_name = cmor_long_name
2676
2677 fms_id = register_diag_field_expand_axes(module_name, cmor_field_name, axes, init_time, &
2678 long_name=trim(posted_cmor_long_name), units=trim(posted_cmor_units), &
2679 missing_value=mom_missing_value, range=range, mask_variant=mask_variant, &
2680 standard_name=trim(posted_cmor_standard_name), verbose=verbose, do_not_log=do_not_log, &
2681 err_msg=err_msg, interp_method=interp_method, tile_count=tile_count)
2682 call attach_cell_methods(fms_id, axes, cm_string, &
2683 cell_methods, x_cell_method, y_cell_method, v_cell_method, &
2684 v_extensive=v_extensive)
2685 ! Associated horizontally area-averaged diagnostic
2686 fms_xyave_id = diag_field_not_found
2687 if (associated(axes%xyave_axes)) then
2688 fms_xyave_id = register_diag_field_expand_axes(module_name, trim(cmor_field_name)//'_xyave', &
2689 axes%xyave_axes, init_time, &
2690 long_name=trim(posted_cmor_long_name), units=trim(posted_cmor_units), &
2691 missing_value=mom_missing_value, range=range, mask_variant=mask_variant, &
2692 standard_name=trim(posted_cmor_standard_name), verbose=verbose, do_not_log=do_not_log, &
2693 err_msg=err_msg, interp_method=interp_method, tile_count=tile_count)
2694 call attach_cell_methods(fms_xyave_id, axes%xyave_axes, cm_string, &
2695 cell_methods, v_cell_method, v_extensive=v_extensive)
2696 endif
2697 this_diag => null()
2698 if (fms_id /= diag_field_not_found .or. fms_xyave_id /= diag_field_not_found) then
2699 call add_diag_to_list(diag_cs, dm_id, fms_id, this_diag, axes, module_name, field_name)
2700 this_diag%fms_xyave_diag_id = fms_xyave_id
2701 ! Encode and save the cell methods for this diagnostic
2702 this_diag%xyz_method = xyz_method(axes, x_cell_method, y_cell_method, v_cell_method, v_extensive)
2703 if (present(v_extensive)) this_diag%v_extensive = v_extensive
2704 if (present(conversion)) this_diag%conversion_factor = conversion
2706 endif
2707 endif
2708
2710
2711!> Returns an FMS id from register_diag_field_fms (the diag_manager routine) after expanding axes
2712!! (axes-group) into handles and conditionally adding an FMS area_id for cell_measures.
2713integer function register_diag_field_expand_axes(module_name, field_name, axes, init_time, &
2714 long_name, units, missing_value, range, mask_variant, standard_name, &
2715 verbose, do_not_log, err_msg, interp_method, tile_count)
2716 character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model"
2717 !! or "ice_shelf_model"
2718 character(len=*), intent(in) :: field_name !< Name of the diagnostic field
2719 type(axes_grp), target, intent(in) :: axes !< Container with up to 3 integer handles that indicates
2720 !! axes for this field
2721 type(time_type), intent(in) :: init_time !< Time at which a field is first available?
2722 character(len=*), optional, intent(in) :: long_name !< Long name of a field.
2723 character(len=*), optional, intent(in) :: units !< Units of a field.
2724 character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
2725 real, optional, intent(in) :: missing_value !< A value that indicates missing values in
2726 !! output files, in unscaled arbitrary units [a]
2727 real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
2728 !! in arbitrary units [a]
2729 logical, optional, intent(in) :: mask_variant !< If true a logical mask must be provided
2730 !! with post_data calls (not used in MOM?)
2731 logical, optional, intent(in) :: verbose !< If true, FMS is verbose (not used in MOM?)
2732 logical, optional, intent(in) :: do_not_log !< If true, do not log something
2733 !! (not used in MOM?)
2734 character(len=*), optional, intent(out):: err_msg !< String into which an error message might be
2735 !! placed (not used in MOM?)
2736 character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should
2737 !! not be interpolated as a scalar
2738 integer, optional, intent(in) :: tile_count !< no clue (not used in MOM?)
2739 ! Local variables
2740 integer :: fms_id, area_id, volume_id
2741
2742 ! This gets the cell area associated with the grid location of this variable
2743 area_id = axes%id_area
2744 volume_id = axes%id_volume
2745
2746 ! Get the FMS diagnostic id
2747 if (axes%diag_cs%diag_as_chksum) then
2748 fms_id = axes%diag_cs%num_chksum_diags + 1
2749 axes%diag_cs%num_chksum_diags = fms_id
2750 elseif (present(interp_method) .or. axes%is_h_point) then
2751 ! If interp_method is provided we must use it
2752 if (area_id>0) then
2753 if (volume_id>0) then
2754 fms_id = register_diag_field_infra(module_name, field_name, axes%handles, &
2755 init_time, long_name=long_name, units=units, missing_value=missing_value, &
2756 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2757 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2758 interp_method=interp_method, tile_count=tile_count, area=area_id, volume=volume_id)
2759 else
2760 fms_id = register_diag_field_infra(module_name, field_name, axes%handles, &
2761 init_time, long_name=long_name, units=units, missing_value=missing_value, &
2762 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2763 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2764 interp_method=interp_method, tile_count=tile_count, area=area_id)
2765 endif
2766 else
2767 if (volume_id>0) then
2768 fms_id = register_diag_field_infra(module_name, field_name, axes%handles, &
2769 init_time, long_name=long_name, units=units, missing_value=missing_value, &
2770 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2771 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2772 interp_method=interp_method, tile_count=tile_count, volume=volume_id)
2773 else
2774 fms_id = register_diag_field_infra(module_name, field_name, axes%handles, &
2775 init_time, long_name=long_name, units=units, missing_value=missing_value, &
2776 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2777 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2778 interp_method=interp_method, tile_count=tile_count)
2779 endif
2780 endif
2781 else
2782 ! If interp_method is not provided and the field is not at an h-point then interp_method='none'
2783 if (area_id>0) then
2784 if (volume_id>0) then
2785 fms_id = register_diag_field_infra(module_name, field_name, axes%handles, &
2786 init_time, long_name=long_name, units=units, missing_value=missing_value, &
2787 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2788 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2789 interp_method='none', tile_count=tile_count, area=area_id, volume=volume_id)
2790 else
2791 fms_id = register_diag_field_infra(module_name, field_name, axes%handles, &
2792 init_time, long_name=long_name, units=units, missing_value=missing_value, &
2793 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2794 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2795 interp_method='none', tile_count=tile_count, area=area_id)
2796 endif
2797 else
2798 if (volume_id>0) then
2799 fms_id = register_diag_field_infra(module_name, field_name, axes%handles, &
2800 init_time, long_name=long_name, units=units, missing_value=missing_value, &
2801 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2802 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2803 interp_method='none', tile_count=tile_count, volume=volume_id)
2804 else
2805 fms_id = register_diag_field_infra(module_name, field_name, axes%handles, &
2806 init_time, long_name=long_name, units=units, missing_value=missing_value, &
2807 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2808 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2809 interp_method='none', tile_count=tile_count)
2810 endif
2811 endif
2812 endif
2813
2815
2817
2818!> Create a diagnostic type and attached to list
2819subroutine add_diag_to_list(diag_cs, dm_id, fms_id, this_diag, axes, module_name, field_name)
2820 type(diag_ctrl), pointer :: diag_cs !< Diagnostics mediator control structure
2821 integer, intent(inout) :: dm_id !< The diag_mediator ID for this diagnostic group
2822 integer, intent(in) :: fms_id !< The FMS diag_manager ID for this diagnostic
2823 type(diag_type), pointer :: this_diag !< This diagnostic
2824 type(axes_grp), target, intent(in) :: axes !< Container with up to 3 integer handles that
2825 !! indicates axes for this field
2826 character(len=*), intent(in) :: module_name !< Name of this module, usually
2827 !! "ocean_model" or "ice_shelf_model"
2828 character(len=*), intent(in) :: field_name !< Name of diagnostic
2829
2830 ! If the diagnostic is needed obtain a diag_mediator ID (if needed)
2831 if (dm_id == -1) dm_id = get_new_diag_id(diag_cs)
2832 ! Create a new diag_type to store links in
2833 call alloc_diag_with_id(dm_id, diag_cs, this_diag)
2834 call assert(associated(this_diag), 'add_diag_to_list: allocation failed for '//trim(field_name))
2835 ! Record FMS id, masks and conversion factor, in diag_type
2836 this_diag%fms_diag_id = fms_id
2837 this_diag%debug_str = trim(module_name)//"-"//trim(field_name)
2838 this_diag%axes => axes
2839
2840end subroutine add_diag_to_list
2841
2842!> Returns an integer encoding the "cell_methods" for diagnostic, allowing simpler
2843!! access to the cell_method for a given diagnostic at the time of sending
2844integer function xyz_method(axes, x_cell_method, y_cell_method, v_cell_method, v_extensive)
2845 type(axes_grp), intent(in) :: axes !< Container w/ up to 3 integer handles that indicates
2846 !! axes for this field
2847 character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction.
2848 !! Use '' have no method.
2849 character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction.
2850 !! Use '' have no method.
2851 character(len=*), optional, intent(in) :: v_cell_method !< Specifies the cell method for the vertical direction.
2852 !! Use '' have no method.
2853 logical, optional, intent(in) :: v_extensive !< True for vertically extensive fields
2854 !! (vertically integrated). Default/absent for intensive.
2855
2856 character(len=9) :: mstr
2857
2858 ! This is a simple way to encode the cell method information made from 3 strings
2859 ! (x_cell_method,y_cell_method,v_cell_method) in a 3 digit integer xyz
2860 ! x_cell_method,y_cell_method,v_cell_method can each be 'point' or 'sum' or 'mean'
2861 ! We can encode these with setting 1 for 'point', 2 for 'sum, 3 for 'mean' in
2862 ! the 100s position for x, 10s position for y, 1s position for z
2863 ! E.g., x:sum,y:point,z:mean is 213
2864
2865 xyz_method = 111
2866
2867 mstr = axes%v_cell_method
2868 if (present(v_extensive)) then
2869 if (present(v_cell_method)) call mom_error(fatal, "xyz_method: " // &
2870 'Vertical cell method was specified along with the vertically extensive flag.')
2871 if (v_extensive) then
2872 mstr='sum'
2873 else
2874 mstr='mean'
2875 endif
2876 elseif (present(v_cell_method)) then
2877 mstr = v_cell_method
2878 endif
2879 if (trim(mstr)=='sum') then
2881 elseif (trim(mstr)=='mean') then
2883 endif
2884
2885 mstr = axes%y_cell_method
2886 if (present(y_cell_method)) mstr = y_cell_method
2887 if (trim(mstr)=='sum') then
2888 xyz_method = xyz_method + 10
2889 elseif (trim(mstr)=='mean') then
2890 xyz_method = xyz_method + 20
2891 endif
2892
2893 mstr = axes%x_cell_method
2894 if (present(x_cell_method)) mstr = x_cell_method
2895 if (trim(mstr)=='sum') then
2896 xyz_method = xyz_method + 100
2897 elseif (trim(mstr)=='mean') then
2898 xyz_method = xyz_method + 200
2899 endif
2900
2901end function xyz_method
2902
2903!> Attaches "cell_methods" attribute to a variable based on defaults for axes_grp or optional arguments.
2904subroutine attach_cell_methods(id, axes, ostring, cell_methods, &
2905 x_cell_method, y_cell_method, v_cell_method, v_extensive)
2906 integer, intent(in) :: id !< Handle to diagnostic
2907 type(axes_grp), intent(in) :: axes !< Container with up to 3 integer handles that indicates
2908 !! axes for this field
2909 character(len=*), intent(out) :: ostring !< The cell_methods strings that would appear in the file
2910 character(len=*), optional, intent(in) :: cell_methods !< String to append as cell_methods attribute.
2911 !! Use '' to have no attribute. If present, this
2912 !! overrides the default constructed from the default
2913 !! for each individual axis direction.
2914 character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction.
2915 !! Use '' have no method.
2916 character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction.
2917 !! Use '' have no method.
2918 character(len=*), optional, intent(in) :: v_cell_method !< Specifies the cell method for the vertical direction.
2919 !! Use '' have no method.
2920 logical, optional, intent(in) :: v_extensive !< True for vertically extensive fields
2921 !! (vertically integrated). Default/absent for intensive.
2922 ! Local variables
2923 character(len=9) :: axis_name
2924 logical :: x_mean, y_mean, x_sum, y_sum
2925
2926 x_mean = .false.
2927 y_mean = .false.
2928 x_sum = .false.
2929 y_sum = .false.
2930
2931 ostring = ''
2932 if (present(cell_methods)) then
2933 if (present(x_cell_method) .or. present(y_cell_method) .or. present(v_cell_method) &
2934 .or. present(v_extensive)) then
2935 call mom_error(fatal, "attach_cell_methods: " // &
2936 'Individual direction cell method was specified along with a "cell_methods" string.')
2937 endif
2938 if (len(trim(cell_methods))>0) then
2939 call mom_diag_field_add_attribute(id, 'cell_methods', trim(cell_methods))
2940 ostring = trim(cell_methods)
2941 endif
2942 else
2943 if (present(x_cell_method)) then
2944 if (len(trim(x_cell_method))>0) then
2945 call get_mom_diag_axis_name(axes%handles(1), axis_name)
2946 call mom_diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(x_cell_method))
2947 ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(x_cell_method)
2948 if (trim(x_cell_method)=='mean') x_mean=.true.
2949 if (trim(x_cell_method)=='sum') x_sum=.true.
2950 endif
2951 else
2952 if (len(trim(axes%x_cell_method))>0) then
2953 call get_mom_diag_axis_name(axes%handles(1), axis_name)
2954 call mom_diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(axes%x_cell_method))
2955 ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(axes%x_cell_method)
2956 if (trim(axes%x_cell_method)=='mean') x_mean=.true.
2957 if (trim(axes%x_cell_method)=='sum') x_sum=.true.
2958 endif
2959 endif
2960 if (present(y_cell_method)) then
2961 if (len(trim(y_cell_method))>0) then
2962 call get_mom_diag_axis_name(axes%handles(2), axis_name)
2963 call mom_diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(y_cell_method))
2964 ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(y_cell_method)
2965 if (trim(y_cell_method)=='mean') y_mean=.true.
2966 if (trim(y_cell_method)=='sum') y_sum=.true.
2967 endif
2968 else
2969 if (len(trim(axes%y_cell_method))>0) then
2970 call get_mom_diag_axis_name(axes%handles(2), axis_name)
2971 call mom_diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(axes%y_cell_method))
2972 ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(axes%y_cell_method)
2973 if (trim(axes%y_cell_method)=='mean') y_mean=.true.
2974 if (trim(axes%y_cell_method)=='sum') y_sum=.true.
2975 endif
2976 endif
2977 if (present(v_cell_method)) then
2978 if (present(v_extensive)) call mom_error(fatal, "attach_cell_methods: " // &
2979 'Vertical cell method was specified along with the vertically extensive flag.')
2980 if (len(trim(v_cell_method))>0) then
2981 if (axes%rank==1) then
2982 call get_mom_diag_axis_name(axes%handles(1), axis_name)
2983 elseif (axes%rank==3) then
2984 call get_mom_diag_axis_name(axes%handles(3), axis_name)
2985 endif
2986 call mom_diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(v_cell_method))
2987 ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(v_cell_method)
2988 endif
2989 elseif (present(v_extensive)) then
2990 if (v_extensive) then
2991 if (axes%rank==1) then
2992 call get_mom_diag_axis_name(axes%handles(1), axis_name)
2993 elseif (axes%rank==3) then
2994 call get_mom_diag_axis_name(axes%handles(3), axis_name)
2995 endif
2996 call mom_diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':sum')
2997 ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':sum'
2998 endif
2999 else
3000 if (len(trim(axes%v_cell_method))>0) then
3001 if (axes%rank==1) then
3002 call get_mom_diag_axis_name(axes%handles(1), axis_name)
3003 elseif (axes%rank==3) then
3004 call get_mom_diag_axis_name(axes%handles(3), axis_name)
3005 endif
3006 call mom_diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(axes%v_cell_method))
3007 ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(axes%v_cell_method)
3008 endif
3009 endif
3010 if (x_mean .and. y_mean) then
3011 call mom_diag_field_add_attribute(id, 'cell_methods', 'area:mean')
3012 ostring = trim(adjustl(ostring))//' area:mean'
3013 elseif (x_sum .and. y_sum) then
3014 call mom_diag_field_add_attribute(id, 'cell_methods', 'area:sum')
3015 ostring = trim(adjustl(ostring))//' area:sum'
3016 endif
3017 endif
3018 ostring = adjustl(ostring)
3019end subroutine attach_cell_methods
3020
3021
3022!> Registers a non-array scalar diagnostic, returning an integer handle
3023function register_scalar_field_axes(module_name, field_name, axes, init_time, &
3024 long_name, units, missing_value, range, standard_name, &
3025 do_not_log, err_msg, interp_method, cmor_field_name, &
3026 cmor_long_name, cmor_units, cmor_standard_name, conversion) result (register_scalar_field)
3027 integer :: register_scalar_field !< An integer handle for a diagnostic array.
3028 character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model"
3029 !! or "ice_shelf_model"
3030 character(len=*), intent(in) :: field_name !< Name of the diagnostic field
3031 type(axes_grp), target, intent(in) :: axes !< Container with up to 3 integer handles that
3032 !! indicates axes for this field
3033 type(time_type), intent(in) :: init_time !< Time at which a field is first available?
3034 character(len=*), optional, intent(in) :: long_name !< Long name of a field.
3035 character(len=*), optional, intent(in) :: units !< Units of a field.
3036 character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
3037 real, optional, intent(in) :: missing_value !< A value that indicates missing values in
3038 !! output files, in unscaled arbitrary units [a]
3039 real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
3040 !! in arbitrary units [a]
3041 logical, optional, intent(in) :: do_not_log !< If true, do not log something (not used in MOM?)
3042 character(len=*), optional, intent(out):: err_msg !< String into which an error message might be
3043 !! placed (not used in MOM?)
3044 character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should not
3045 !! be interpolated as a scalar
3046 character(len=*), optional, intent(in) :: cmor_field_name !< CMOR name of a field
3047 character(len=*), optional, intent(in) :: cmor_long_name !< CMOR long name of a field
3048 character(len=*), optional, intent(in) :: cmor_units !< CMOR units of a field
3049 character(len=*), optional, intent(in) :: cmor_standard_name !< CMOR standardized name associated with a field
3050 real, optional, intent(in) :: conversion !< A value to multiply data by before writing to files,
3051 !! often including factors to undo internal scaling and
3052 !! in units of [a A-1 ~> 1]
3053
3054 register_scalar_field = register_scalar_field_cs(module_name, field_name, init_time, axes%diag_cs, &
3055 long_name, units, missing_value, range, standard_name, &
3056 do_not_log, err_msg, interp_method, cmor_field_name, &
3057 cmor_long_name, cmor_units, cmor_standard_name, conversion)
3058
3059end function register_scalar_field_axes
3060
3061
3062!> Registers a scalar diagnostic, returning an integer handle
3063function register_scalar_field_cs(module_name, field_name, init_time, diag_cs, &
3064 long_name, units, missing_value, range, standard_name, &
3065 do_not_log, err_msg, interp_method, cmor_field_name, &
3066 cmor_long_name, cmor_units, cmor_standard_name, conversion) result (register_scalar_field)
3067 integer :: register_scalar_field !< An integer handle for a diagnostic array.
3068 character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model"
3069 !! or "ice_shelf_model"
3070 character(len=*), intent(in) :: field_name !< Name of the diagnostic field
3071 type(time_type), intent(in) :: init_time !< Time at which a field is first available?
3072 type(diag_ctrl), intent(inout) :: diag_cs !< Structure used to regulate diagnostic output
3073 character(len=*), optional, intent(in) :: long_name !< Long name of a field.
3074 character(len=*), optional, intent(in) :: units !< Units of a field.
3075 character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
3076 real, optional, intent(in) :: missing_value !< A value that indicates missing values in
3077 !! output files, in unscaled arbitrary units [a]
3078 real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
3079 !! in arbitrary units [a]
3080 logical, optional, intent(in) :: do_not_log !< If true, do not log something (not used in MOM?)
3081 character(len=*), optional, intent(out):: err_msg !< String into which an error message might be
3082 !! placed (not used in MOM?)
3083 character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should not
3084 !! be interpolated as a scalar
3085 character(len=*), optional, intent(in) :: cmor_field_name !< CMOR name of a field
3086 character(len=*), optional, intent(in) :: cmor_long_name !< CMOR long name of a field
3087 character(len=*), optional, intent(in) :: cmor_units !< CMOR units of a field
3088 character(len=*), optional, intent(in) :: cmor_standard_name !< CMOR standardized name associated with a field
3089 real, optional, intent(in) :: conversion !< A value to multiply data by before writing to files,
3090 !! often including factors to undo internal scaling and
3091 !! in units of [a A-1 ~> 1]
3092
3093 ! Local variables
3094 real :: mom_missing_value ! A value used to indicate missing values in output files, in arbitrary units [a]
3095 integer :: dm_id, fms_id
3096 type(diag_type), pointer :: diag => null(), cmor_diag => null()
3097 character(len=256) :: posted_cmor_units, posted_cmor_standard_name, posted_cmor_long_name
3098 character(len=16) :: dimensions
3099
3100 mom_missing_value = diag_cs%missing_value
3101 if (present(missing_value)) mom_missing_value = missing_value
3102
3103 dm_id = -1
3104 diag => null()
3105 cmor_diag => null()
3106
3107 if (diag_cs%diag_as_chksum) then
3108 fms_id = diag_cs%num_chksum_diags + 1
3109 diag_cs%num_chksum_diags = fms_id
3110 else
3111 fms_id = register_diag_field_infra(module_name, field_name, init_time, &
3112 long_name=long_name, units=units, missing_value=mom_missing_value, &
3113 range=range, standard_name=standard_name, do_not_log=do_not_log, &
3114 err_msg=err_msg)
3115 endif
3116
3117 if (fms_id /= diag_field_not_found) then
3118 dm_id = get_new_diag_id(diag_cs)
3119 call alloc_diag_with_id(dm_id, diag_cs, diag)
3120 call assert(associated(diag), 'register_scalar_field: diag allocation failed')
3121 diag%fms_diag_id = fms_id
3122 diag%debug_str = trim(module_name)//"-"//trim(field_name)
3123 if (present(conversion)) diag%conversion_factor = conversion
3124 endif
3125
3126 if (present(cmor_field_name)) then
3127 ! Fallback values for strings set to "not provided"
3128 posted_cmor_units = "not provided"
3129 posted_cmor_standard_name = "not provided"
3130 posted_cmor_long_name = "not provided"
3131
3132 ! If attributes are present for MOM variable names, use them as defaults for the
3133 ! register_diag_field_infra call for CMOR version of the variable
3134 if (present(units)) posted_cmor_units = units
3135 if (present(standard_name)) posted_cmor_standard_name = standard_name
3136 if (present(long_name)) posted_cmor_long_name = long_name
3137
3138 ! If specified in the call to register_scalar_field, override attributes with the CMOR versions
3139 if (present(cmor_units)) posted_cmor_units = cmor_units
3140 if (present(cmor_standard_name)) posted_cmor_standard_name = cmor_standard_name
3141 if (present(cmor_long_name)) posted_cmor_long_name = cmor_long_name
3142
3143 fms_id = register_diag_field_infra(module_name, cmor_field_name, init_time, &
3144 long_name=trim(posted_cmor_long_name), units=trim(posted_cmor_units), &
3145 missing_value=mom_missing_value, range=range, &
3146 standard_name=trim(posted_cmor_standard_name), do_not_log=do_not_log, err_msg=err_msg)
3147 if (fms_id /= diag_field_not_found) then
3148 if (dm_id == -1) then
3149 dm_id = get_new_diag_id(diag_cs)
3150 endif
3151 call alloc_diag_with_id(dm_id, diag_cs, cmor_diag)
3152 cmor_diag%fms_diag_id = fms_id
3153 cmor_diag%debug_str = trim(module_name)//"-"//trim(cmor_field_name)
3154 if (present(conversion)) cmor_diag%conversion_factor = conversion
3155 endif
3156 endif
3157
3158 dimensions = "scalar"
3159
3160 ! Document diagnostics in list of available diagnostics
3161 if (is_root_pe() .and. diag_cs%available_diag_doc_unit > 0) then
3162 if (present(cmor_field_name)) then
3163 call log_available_diag(associated(diag), module_name, field_name, '', '', diag_cs, &
3164 long_name, units, standard_name, &
3165 variants="{"//trim(field_name)//","//trim(cmor_field_name)//"}", &
3166 dimensions=dimensions)
3167 else
3168 call log_available_diag(associated(diag), module_name, field_name, '', '', diag_cs, &
3169 long_name, units, standard_name, dimensions=dimensions)
3170 endif
3171 endif
3172
3173 register_scalar_field = dm_id
3174
3175end function register_scalar_field_cs
3176
3177!> Registers a static diagnostic, returning an integer handle
3178function register_static_field(module_name, field_name, axes, &
3179 long_name, units, missing_value, range, mask_variant, standard_name, &
3180 do_not_log, interp_method, tile_count, &
3181 cmor_field_name, cmor_long_name, cmor_units, cmor_standard_name, area, &
3182 x_cell_method, y_cell_method, area_cell_method, conversion)
3183 integer :: register_static_field !< An integer handle for a diagnostic array.
3184 character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model"
3185 !! or "ice_shelf_model"
3186 character(len=*), intent(in) :: field_name !< Name of the diagnostic field
3187 type(axes_grp), target, intent(in) :: axes !< Container with up to 3 integer handles that
3188 !! indicates axes for this field
3189 character(len=*), optional, intent(in) :: long_name !< Long name of a field.
3190 character(len=*), optional, intent(in) :: units !< Units of a field.
3191 character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
3192 real, optional, intent(in) :: missing_value !< A value that indicates missing values in
3193 !! output files, in unscaled arbitrary units [a]
3194 real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
3195 !! in arbitrary units [a]
3196 logical, optional, intent(in) :: mask_variant !< If true a logical mask must be provided with
3197 !! post_data calls (not used in MOM?)
3198 logical, optional, intent(in) :: do_not_log !< If true, do not log something (not used in MOM?)
3199 character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should not
3200 !! be interpolated as a scalar
3201 integer, optional, intent(in) :: tile_count !< no clue (not used in MOM?)
3202 character(len=*), optional, intent(in) :: cmor_field_name !< CMOR name of a field
3203 character(len=*), optional, intent(in) :: cmor_long_name !< CMOR long name of a field
3204 character(len=*), optional, intent(in) :: cmor_units !< CMOR units of a field
3205 character(len=*), optional, intent(in) :: cmor_standard_name !< CMOR standardized name associated with a field
3206 integer, optional, intent(in) :: area !< fms_id for area_t
3207 character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction.
3208 character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction.
3209 character(len=*), optional, intent(in) :: area_cell_method !< Specifies the cell method for area
3210 real, optional, intent(in) :: conversion !< A value to multiply data by before writing to files,
3211 !! often including factors to undo internal scaling and
3212 !! in units of [a A-1 ~> 1]
3213
3214 ! Local variables
3215 real :: mom_missing_value ! A value used to indicate missing values in output files, in arbitrary units [a]
3216 type(diag_ctrl), pointer :: diag_cs => null() !< A structure that is used to regulate diagnostic output
3217 type(diag_type), pointer :: diag => null(), cmor_diag => null()
3218 integer :: dm_id, fms_id
3219 character(len=256) :: posted_cmor_units, posted_cmor_standard_name, posted_cmor_long_name
3220 character(len=9) :: axis_name
3221 character(len=24) :: dimensions
3222
3223 mom_missing_value = axes%diag_cs%missing_value
3224 if (present(missing_value)) mom_missing_value = missing_value
3225
3226 diag_cs => axes%diag_cs
3227 dm_id = -1
3228 diag => null()
3229 cmor_diag => null()
3230
3231 if (diag_cs%diag_as_chksum) then
3232 fms_id = diag_cs%num_chksum_diags + 1
3233 diag_cs%num_chksum_diags = fms_id
3234 else
3235 fms_id = register_static_field_infra(module_name, field_name, axes%handles, &
3236 long_name=long_name, units=units, missing_value=mom_missing_value, &
3237 range=range, mask_variant=mask_variant, standard_name=standard_name, &
3238 do_not_log=do_not_log, &
3239 interp_method=interp_method, tile_count=tile_count, area=area)
3240 endif
3241
3242 if (fms_id /= diag_field_not_found) then
3243 dm_id = get_new_diag_id(diag_cs)
3244 call alloc_diag_with_id(dm_id, diag_cs, diag)
3245 call assert(associated(diag), 'register_static_field: diag allocation failed')
3246 diag%fms_diag_id = fms_id
3247 diag%debug_str = trim(module_name)//"-"//trim(field_name)
3248 if (present(conversion)) diag%conversion_factor = conversion
3249
3250 if (diag_cs%diag_as_chksum) then
3251 diag%axes => axes
3252 else
3253 if (present(x_cell_method)) then
3254 call get_mom_diag_axis_name(axes%handles(1), axis_name)
3255 call mom_diag_field_add_attribute(fms_id, 'cell_methods', &
3256 trim(axis_name)//':'//trim(x_cell_method))
3257 endif
3258 if (present(y_cell_method)) then
3259 call get_mom_diag_axis_name(axes%handles(2), axis_name)
3260 call mom_diag_field_add_attribute(fms_id, 'cell_methods', &
3261 trim(axis_name)//':'//trim(y_cell_method))
3262 endif
3263 if (present(area_cell_method)) then
3264 call mom_diag_field_add_attribute(fms_id, 'cell_methods', &
3265 'area:'//trim(area_cell_method))
3266 endif
3267 endif
3268 endif
3269
3270 if (present(cmor_field_name) .and. .not. diag_cs%diag_as_chksum) then
3271 ! Fallback values for strings set to "not provided"
3272 posted_cmor_units = "not provided"
3273 posted_cmor_standard_name = "not provided"
3274 posted_cmor_long_name = "not provided"
3275
3276 ! If attributes are present for MOM variable names, use them first for the register_static_field
3277 ! call for CMOR version of the variable
3278 if (present(units)) posted_cmor_units = units
3279 if (present(standard_name)) posted_cmor_standard_name = standard_name
3280 if (present(long_name)) posted_cmor_long_name = long_name
3281
3282 ! If specified in the call to register_static_field, override attributes with the CMOR versions
3283 if (present(cmor_units)) posted_cmor_units = cmor_units
3284 if (present(cmor_standard_name)) posted_cmor_standard_name = cmor_standard_name
3285 if (present(cmor_long_name)) posted_cmor_long_name = cmor_long_name
3286
3287 fms_id = register_static_field_infra(module_name, cmor_field_name, axes%handles, &
3288 long_name=trim(posted_cmor_long_name), units=trim(posted_cmor_units), &
3289 missing_value=mom_missing_value, range=range, mask_variant=mask_variant, &
3290 standard_name=trim(posted_cmor_standard_name), do_not_log=do_not_log, &
3291 interp_method=interp_method, tile_count=tile_count, area=area)
3292 if (fms_id /= diag_field_not_found) then
3293 if (dm_id == -1) then
3294 dm_id = get_new_diag_id(diag_cs)
3295 endif
3296 call alloc_diag_with_id(dm_id, diag_cs, cmor_diag)
3297 cmor_diag%fms_diag_id = fms_id
3298 cmor_diag%debug_str = trim(module_name)//"-"//trim(cmor_field_name)
3299 if (present(conversion)) cmor_diag%conversion_factor = conversion
3300 if (present(x_cell_method)) then
3301 call get_mom_diag_axis_name(axes%handles(1), axis_name)
3302 call mom_diag_field_add_attribute(fms_id, 'cell_methods', trim(axis_name)//':'//trim(x_cell_method))
3303 endif
3304 if (present(y_cell_method)) then
3305 call get_mom_diag_axis_name(axes%handles(2), axis_name)
3306 call mom_diag_field_add_attribute(fms_id, 'cell_methods', trim(axis_name)//':'//trim(y_cell_method))
3307 endif
3308 if (present(area_cell_method)) then
3309 call mom_diag_field_add_attribute(fms_id, 'cell_methods', 'area:'//trim(area_cell_method))
3310 endif
3311 endif
3312 endif
3313
3314 dimensions = ""
3315 if (axes%is_h_point) dimensions = trim(dimensions)//" xh, yh,"
3316 if (axes%is_q_point) dimensions = trim(dimensions)//" xq, yq,"
3317 if (axes%is_u_point) dimensions = trim(dimensions)//" xq, yh,"
3318 if (axes%is_v_point) dimensions = trim(dimensions)//" xh, yq,"
3319 if (axes%is_layer) dimensions = trim(dimensions)//" zl,"
3320 if (axes%is_interface) dimensions = trim(dimensions)//" zi,"
3321 if (len_trim(dimensions) > 0) dimensions = trim_trailing_commas(dimensions)
3322
3323 ! Document diagnostics in list of available diagnostics
3324 if (is_root_pe() .and. diag_cs%available_diag_doc_unit > 0) then
3325 if (present(cmor_field_name)) then
3326 call log_available_diag(associated(diag), module_name, field_name, '', '', diag_cs, &
3327 long_name, units, standard_name, &
3328 variants="{"//trim(field_name)//","//trim(cmor_field_name)//"}", &
3329 dimensions=dimensions)
3330 else
3331 call log_available_diag(associated(diag), module_name, field_name, '', '', diag_cs, &
3332 long_name, units, standard_name, dimensions=dimensions)
3333 endif
3334 endif
3335
3336 register_static_field = dm_id
3337
3338end function register_static_field
3339
3340!> Describe an option setting in the diagnostic files.
3341subroutine describe_option(opt_name, value, diag_CS)
3342 character(len=*), intent(in) :: opt_name !< The name of the option
3343 character(len=*), intent(in) :: value !< A character string with the setting of the option.
3344 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
3345
3346 character(len=480) :: mesg
3347 integer :: len_ind
3348
3349 len_ind = len_trim(value) ! Add error handling for long values?
3350
3351 mesg = " ! "//trim(opt_name)//": "//trim(value)
3352 write(diag_cs%available_diag_doc_unit, '(a)') trim(mesg)
3353end subroutine describe_option
3354
3355!> Registers a diagnostic using the information encapsulated in the vardesc
3356!! type argument and returns an integer handle to this diagnostic. That
3357!! integer handle is negative if the diagnostic is unused.
3358function ocean_register_diag(var_desc, G, diag_CS, day)
3359 integer :: ocean_register_diag !< An integer handle to this diagnostic.
3360 type(vardesc), intent(in) :: var_desc !< The vardesc type describing the diagnostic
3361 type(ocean_grid_type), intent(in) :: g !< The ocean's grid type
3362 type(diag_ctrl), intent(in), target :: diag_cs !< The diagnostic control structure
3363 type(time_type), intent(in) :: day !< The current model time
3364
3365 character(len=64) :: var_name ! A variable's name.
3366 character(len=48) :: units ! A variable's units.
3367 character(len=240) :: longname ! A variable's longname.
3368 character(len=8) :: hor_grid, z_grid ! Variable grid info.
3369 real :: conversion ! A multiplicative factor for unit conversions for output,
3370 ! as might be needed to convert from intensive to extensive
3371 ! or for dimensional consistency testing [various] or [a A-1 ~> 1]
3372 type(axes_grp), pointer :: axes => null()
3373
3374 call query_vardesc(var_desc, units=units, longname=longname, hor_grid=hor_grid, &
3375 z_grid=z_grid, conversion=conversion, caller="ocean_register_diag")
3376
3377 ! Use the hor_grid and z_grid components of vardesc to determine the
3378 ! desired axes to register the diagnostic field for.
3379 select case (z_grid)
3380
3381 case ("L")
3382 select case (hor_grid)
3383 case ("q") ; axes => diag_cs%axesBL
3384 case ("h") ; axes => diag_cs%axesTL
3385 case ("u") ; axes => diag_cs%axesCuL
3386 case ("v") ; axes => diag_cs%axesCvL
3387 case ("Bu") ; axes => diag_cs%axesBL
3388 case ("T") ; axes => diag_cs%axesTL
3389 case ("Cu") ; axes => diag_cs%axesCuL
3390 case ("Cv") ; axes => diag_cs%axesCvL
3391 case ("z") ; axes => diag_cs%axeszL
3392 case default ; call mom_error(fatal, "ocean_register_diag: " // &
3393 "unknown hor_grid component "//trim(hor_grid))
3394 end select
3395
3396 case ("i")
3397 select case (hor_grid)
3398 case ("q") ; axes => diag_cs%axesBi
3399 case ("h") ; axes => diag_cs%axesTi
3400 case ("u") ; axes => diag_cs%axesCui
3401 case ("v") ; axes => diag_cs%axesCvi
3402 case ("Bu") ; axes => diag_cs%axesBi
3403 case ("T") ; axes => diag_cs%axesTi
3404 case ("Cu") ; axes => diag_cs%axesCui
3405 case ("Cv") ; axes => diag_cs%axesCvi
3406 case ("z") ; axes => diag_cs%axeszi
3407 case default ; call mom_error(fatal, "ocean_register_diag: " // &
3408 "unknown hor_grid component "//trim(hor_grid))
3409 end select
3410
3411 case ("1")
3412 select case (hor_grid)
3413 case ("q") ; axes => diag_cs%axesB1
3414 case ("h") ; axes => diag_cs%axesT1
3415 case ("u") ; axes => diag_cs%axesCu1
3416 case ("v") ; axes => diag_cs%axesCv1
3417 case ("Bu") ; axes => diag_cs%axesB1
3418 case ("T") ; axes => diag_cs%axesT1
3419 case ("Cu") ; axes => diag_cs%axesCu1
3420 case ("Cv") ; axes => diag_cs%axesCv1
3421 case default ; call mom_error(fatal, "ocean_register_diag: " // &
3422 "unknown hor_grid component "//trim(hor_grid))
3423 end select
3424
3425 case default
3426 call mom_error(fatal,&
3427 "ocean_register_diag: unknown z_grid component "//trim(z_grid))
3428 end select
3429
3430 ocean_register_diag = register_diag_field("ocean_model", trim(var_name), axes, day, &
3431 trim(longname), units=trim(units), conversion=conversion, missing_value=-1.0e+34)
3432
3433end function ocean_register_diag
3434
3435subroutine diag_mediator_infrastructure_init(err_msg)
3436 ! This subroutine initializes the FMS diag_manager.
3437 character(len=*), optional, intent(out) :: err_msg !< An error message
3438
3439 call mom_diag_manager_init(err_msg=err_msg)
3441
3442
3443!> diag_mediator_init initializes the MOM diag_mediator and opens the available
3444!! diagnostics file, if appropriate.
3445subroutine diag_mediator_init(G, GV, US, nz, param_file, diag_cs, doc_file_dir)
3446 type(ocean_grid_type), target, intent(inout) :: g !< The ocean grid type.
3447 type(verticalgrid_type), target, intent(in) :: gv !< The ocean vertical grid structure
3448 type(unit_scale_type), target, intent(in) :: us !< A dimensional unit scaling type
3449 integer, intent(in) :: nz !< The number of layers in the model's native grid.
3450 type(param_file_type), intent(in) :: param_file !< Parameter file structure
3451 type(diag_ctrl), intent(inout) :: diag_cs !< A pointer to a type with many variables
3452 !! used for diagnostics
3453 character(len=*), optional, intent(in) :: doc_file_dir !< A directory in which to create the
3454 !! file
3455
3456 ! This subroutine initializes the diag_mediator and the diag_manager.
3457 ! The grid type should have its dimensions set by this point, but it
3458 ! is not necessary that the metrics and axis labels be set up yet.
3459
3460 ! Local variables
3461 integer :: ios, i, new_unit, dl, dlfac
3462 logical :: opened, new_file
3463 integer :: remap_answer_date ! The vintage of the order of arithmetic and expressions to use
3464 ! for remapping. Values below 20190101 recover the remapping
3465 ! answers from 2018, while higher values use more robust
3466 ! forms of the same remapping expressions.
3467 integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags.
3468 logical :: om4_remap_via_sub_cells ! Use the OM4-era ramap_via_sub_cells for diagnostics
3469 logical :: dz_diag_needed ! Logical set True if we need to store dz_begin for reintegrating
3470 character(len=8) :: this_pe
3471 character(len=240) :: doc_file, doc_file_dflt, doc_path
3472 character(len=240), allocatable :: diag_coords(:)
3473 ! This include declares and sets the variable "version".
3474# include "version_variable.h"
3475 character(len=40) :: mdl = "MOM_diag_mediator" ! This module's name.
3476 character(len=32) :: filename_appendix = '' ! FMS appendix to filename for ensemble runs
3477 character(len=16) :: dsamp_domain_name
3478
3479 id_clock_diag_mediator = cpu_clock_id('(Ocean diagnostics framework)', grain=clock_module)
3480 id_clock_diag_remap = cpu_clock_id('(Ocean diagnostics remapping)', grain=clock_routine)
3481 id_clock_diag_grid_updates = cpu_clock_id('(Ocean diagnostics grid updates)', grain=clock_routine)
3482
3483 ! Allocate and initialize list of all diagnostics (and variants)
3484 allocate(diag_cs%diags(diag_alloc_chunk_size))
3485 diag_cs%next_free_diag_id = 1
3486 do i=1, diag_alloc_chunk_size
3487 call initialize_diag_type(diag_cs%diags(i))
3488 enddo
3489
3490 diag_cs%show_call_tree = calltree_showquery()
3491
3492 ! Read all relevant parameters and write them to the model log.
3493 call log_version(param_file, mdl, version, "")
3494
3495 call get_param(param_file, mdl, 'NUM_DIAG_COORDS', diag_cs%num_diag_coords, &
3496 'The number of diagnostic vertical coordinates to use. '//&
3497 'For each coordinate, an entry in DIAG_COORDS must be provided.', &
3498 default=1)
3499 call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, &
3500 "This sets the default value for the various _ANSWER_DATE parameters.", &
3501 default=99991231)
3502 call get_param(param_file, mdl, "REMAPPING_USE_OM4_SUBCELLS", om4_remap_via_sub_cells, &
3503 do_not_log=.true., default=.true.)
3504 call get_param(param_file, mdl, "DIAG_REMAPPING_USE_OM4_SUBCELLS", om4_remap_via_sub_cells, &
3505 "If true, use the OM4 remapping-via-subcells algorithm for diagnostics. "//&
3506 "See REMAPPING_USE_OM4_SUBCELLS for details. "//&
3507 "We recommend setting this option to false.", default=om4_remap_via_sub_cells)
3508 call get_param(param_file, mdl, "REMAPPING_ANSWER_DATE", remap_answer_date, &
3509 "The vintage of the expressions and order of arithmetic to use for remapping. "//&
3510 "Values below 20190101 result in the use of older, less accurate expressions "//&
3511 "that were in use at the end of 2018. Higher values result in the use of more "//&
3512 "robust and accurate forms of mathematically equivalent expressions.", &
3513 default=default_answer_date, do_not_log=.not.gv%Boussinesq)
3514 if (.not.gv%Boussinesq) remap_answer_date = max(remap_answer_date, 20230701)
3515 call get_param(param_file, mdl, 'USE_INDEX_DIAGNOSTIC_AXES', diag_cs%index_space_axes, &
3516 'If true, use a grid index coordinate convention for diagnostic axes. ', &
3517 default=.false.)
3518 call get_param(param_file, mdl, 'SYMMETRIC_DOWNSAMPLE_SUMS', diag_cs%symmetric_downsample_sums, &
3519 'If true, use rotationally symmetric sums when downsampling diagnostics.', &
3520 default=.false.)
3521
3522
3523 dz_diag_needed = .false.
3524 if (diag_cs%num_diag_coords>0) then
3525 allocate(diag_coords(diag_cs%num_diag_coords))
3526 if (diag_cs%num_diag_coords==1) then ! The default is to provide just one instance of Z*
3527 call get_param(param_file, mdl, 'DIAG_COORDS', diag_coords, &
3528 'A list of string tuples associating diag_table modules to '//&
3529 'a coordinate definition used for diagnostics. Each string '//&
3530 'is of the form "MODULE_SUFFIX PARAMETER_SUFFIX COORDINATE_NAME".', &
3531 default='z Z ZSTAR')
3532 else ! If using more than 1 diagnostic coordinate, all must be explicitly defined
3533 call get_param(param_file, mdl, 'DIAG_COORDS', diag_coords, &
3534 'A list of string tuples associating diag_table modules to '//&
3535 'a coordinate definition used for diagnostics. Each string '//&
3536 'is of the form "MODULE_SUFFIX,PARAMETER_SUFFIX,COORDINATE_NAME".', &
3537 fail_if_missing=.true.)
3538 endif
3539 allocate(diag_cs%diag_remap_cs(diag_cs%num_diag_coords))
3540 ! Initialize each diagnostic vertical coordinate
3541 do i=1, diag_cs%num_diag_coords
3542 call diag_remap_init(diag_cs%diag_remap_cs(i), diag_coords(i), om4_remap_via_sub_cells, remap_answer_date, gv)
3543 if (diag_cs%diag_remap_cs(i)%Z_based_coord) dz_diag_needed = .true.
3544 enddo
3545 deallocate(diag_coords)
3546 endif
3547
3548 call get_param(param_file, mdl, 'DIAG_MISVAL', diag_cs%missing_value, &
3549 'Set the default missing value to use for diagnostics.', &
3550 units="various", default=1.e20)
3551 call get_param(param_file, mdl, 'DIAG_AS_CHKSUM', diag_cs%diag_as_chksum, &
3552 'Instead of writing diagnostics to the diag manager, write '//&
3553 'a text file containing the checksum (bitcount) of the array.', &
3554 default=.false.)
3555
3556 if (diag_cs%diag_as_chksum) &
3557 diag_cs%num_chksum_diags = 0
3558
3559 ! Keep pointers to the grid, h, T, S needed for diagnostic remapping
3560 diag_cs%G => g
3561 diag_cs%GV => gv
3562 diag_cs%US => us
3563 diag_cs%h => null()
3564 diag_cs%T => null()
3565 diag_cs%S => null()
3566 diag_cs%eqn_of_state => null()
3567 diag_cs%tv => null()
3568
3569 allocate(diag_cs%h_begin(g%isd:g%ied,g%jsd:g%jed,nz))
3570 if (dz_diag_needed) allocate(diag_cs%dz_begin(g%isd:g%ied,g%jsd:g%jed,nz))
3571#if defined(DEBUG) || defined(__DO_SAFETY_CHECKS__)
3572 allocate(diag_cs%h_old(g%isd:g%ied,g%jsd:g%jed,nz), source=0.0)
3573#endif
3574 allocate(diag_cs%OBC_u(g%IsdB:g%IedB,g%jsd:g%jed), source=0)
3575 allocate(diag_cs%OBC_v(g%isd:g%ied,g%JsdB:g%JedB), source=0)
3576
3577 diag_cs%is = g%isc - (g%isd-1) ; diag_cs%ie = g%iec - (g%isd-1)
3578 diag_cs%js = g%jsc - (g%jsd-1) ; diag_cs%je = g%jec - (g%jsd-1)
3579 diag_cs%isd = g%isd ; diag_cs%ied = g%ied
3580 diag_cs%jsd = g%jsd ; diag_cs%jed = g%jed
3581
3582 ! In this code design
3583 ! diag_cs%num_diag_dsamp_levels is the number of downsampling levels requested in the parameters
3584 ! diag_cs%diag_dsamp_levels(dl) is the actual downsampling factor for each level,
3585 ! which is used to as the division factor to define the axes for that level.
3586 ! Note that the downsampling axes and domains are created at initialization based on what is
3587 ! requested in the parameter files (default is none) regardless of whether
3588 ! any downsampled diagnostics are present in the diag_table.
3589 ! Are downsampled diagnostics requested?
3590 call get_param(param_file, mdl, 'NUM_DIAG_DOWNSAMP_LEV', diag_cs%num_diag_dsamp_levels, &
3591 'The number of diagnostic downsample levels to use. '//&
3592 'For each level, an entry in DIAG_DOWNSAMP_LEV must be provided.', &
3593 default=0)
3594 if (diag_cs%num_diag_dsamp_levels > 0) then
3595 allocate(diag_cs%diag_dsamp_levels(diag_cs%num_diag_dsamp_levels))
3596 call get_param(param_file, mdl, 'DIAG_DOWNSAMP_LEVS', diag_cs%diag_dsamp_levels, &
3597 'A comma separated list of diagnostic downsample levels to be used. ', &
3598 fail_if_missing=.true.)
3599
3600 allocate(diag_cs%dsamp(diag_cs%num_diag_dsamp_levels))
3601 ! Initialize the global grid extents for all requested levels of diagnostics coarsening.
3602 allocate(g%HId(diag_cs%num_diag_dsamp_levels))
3603 !Allocate downsampling domains
3604 allocate(g%Domain%mpp_domain_d(diag_cs%num_diag_dsamp_levels))
3605 !Create and populated the downsampling domains and grids
3606 do dl=1, diag_cs%num_diag_dsamp_levels
3607 dlfac = diag_cs%diag_dsamp_levels(dl)
3608 !Create the auxiliary mpp_domain for this level of downsampled diagnostics
3609 !Downsample diagnostics calculations do not need halos.
3610 write(dsamp_domain_name, '(a,i0)') trim("MOM_domain_d"),dlfac
3611 call clone_mom_domain(g%Domain, g%Domain%mpp_domain_d(dl), coarsen=dlfac, & !halo_size=0, &
3612 domain_name=dsamp_domain_name)
3613
3614 !Set the grid extents for this level of downsampling.
3615 call get_domain_extent(g%Domain, g%HId(dl)%isc, g%HId(dl)%iec, g%HId(dl)%jsc, g%HId(dl)%jec, &
3616 g%HId(dl)%isd, g%HId(dl)%ied, g%HId(dl)%jsd, g%HId(dl)%jed, &
3617 g%HId(dl)%isg, g%HId(dl)%ieg, g%HId(dl)%jsg, g%HId(dl)%jeg, &
3618 coarsen=dl)
3619
3620 ! Set array sizes for fields that are discretized at tracer cell boundaries.
3621 g%HId(dl)%IscB = g%HId(dl)%isc ; g%HId(dl)%JscB = g%HId(dl)%jsc
3622 g%HId(dl)%IsdB = g%HId(dl)%isd ; g%HId(dl)%JsdB = g%HId(dl)%jsd
3623 g%HId(dl)%IsgB = g%HId(dl)%isg ; g%HId(dl)%JsgB = g%HId(dl)%jsg
3624 if (g%symmetric) then
3625 g%HId(dl)%IscB = g%HId(dl)%isc-1 ; g%HId(dl)%JscB = g%HId(dl)%jsc-1
3626 g%HId(dl)%IsdB = g%HId(dl)%isd-1 ; g%HId(dl)%JsdB = g%HId(dl)%jsd-1
3627 g%HId(dl)%IsgB = g%HId(dl)%isg-1 ; g%HId(dl)%JsgB = g%HId(dl)%jsg-1
3628 endif
3629 g%HId(dl)%IecB = g%HId(dl)%iec ; g%HId(dl)%JecB = g%HId(dl)%jec
3630 g%HId(dl)%IedB = g%HId(dl)%ied ; g%HId(dl)%JedB = g%HId(dl)%jed
3631 g%HId(dl)%IegB = g%HId(dl)%ieg ; g%HId(dl)%JegB = g%HId(dl)%jeg
3632
3633 !Downsample indices for diagnostics that are on a coarser grid than the model grid.
3634 diag_cs%dsamp(dl)%isc = g%HId(dl)%isc - (g%HId(dl)%isd-1)
3635 diag_cs%dsamp(dl)%iec = g%HId(dl)%iec - (g%HId(dl)%isd-1)
3636 diag_cs%dsamp(dl)%jsc = g%HId(dl)%jsc - (g%HId(dl)%jsd-1)
3637 diag_cs%dsamp(dl)%jec = g%HId(dl)%jec - (g%HId(dl)%jsd-1)
3638 diag_cs%dsamp(dl)%isd = g%HId(dl)%isd ; diag_cs%dsamp(dl)%ied = g%HId(dl)%ied
3639 diag_cs%dsamp(dl)%jsd = g%HId(dl)%jsd ; diag_cs%dsamp(dl)%jed = g%HId(dl)%jed
3640 diag_cs%dsamp(dl)%isg = g%HId(dl)%isg ; diag_cs%dsamp(dl)%ieg = g%HId(dl)%ieg
3641 diag_cs%dsamp(dl)%jsg = g%HId(dl)%jsg ; diag_cs%dsamp(dl)%jeg = g%HId(dl)%jeg
3642 diag_cs%dsamp(dl)%isgB = g%HId(dl)%isgB ; diag_cs%dsamp(dl)%iegB = g%HId(dl)%iegB
3643 diag_cs%dsamp(dl)%jsgB = g%HId(dl)%jsgB ; diag_cs%dsamp(dl)%jegB = g%HId(dl)%jegB
3644 enddo
3645 endif
3646 ! Initialze available diagnostic log file
3647 if (is_root_pe() .and. (diag_cs%available_diag_doc_unit < 0)) then
3648 write(this_pe,'(i6.6)') pe_here()
3649 doc_file_dflt = "available_diags."//this_pe
3650 call get_param(param_file, mdl, "AVAILABLE_DIAGS_FILE", doc_file, &
3651 "A file into which to write a list of all available "//&
3652 "ocean diagnostics that can be included in a diag_table.", &
3653 default=doc_file_dflt, do_not_log=(diag_cs%available_diag_doc_unit/=-1))
3654 if (len_trim(doc_file) > 0) then
3655 new_file = .true. ; if (diag_cs%available_diag_doc_unit /= -1) new_file = .false.
3656 ! Find an unused unit number.
3657 do new_unit=512,42,-1
3658 inquire( new_unit, opened=opened)
3659 if (.not.opened) exit
3660 enddo
3661 if (opened) call mom_error(fatal, &
3662 "diag_mediator_init failed to find an unused unit number.")
3663
3664 doc_path = doc_file
3665 if (present(doc_file_dir)) then ; if (len_trim(doc_file_dir) > 0) then
3666 doc_path = trim(slasher(doc_file_dir))//trim(doc_file)
3667 endif ; endif
3668
3669 diag_cs%available_diag_doc_unit = new_unit
3670
3671 if (new_file) then
3672 open(diag_cs%available_diag_doc_unit, file=trim(doc_path), access='SEQUENTIAL', form='FORMATTED', &
3673 action='WRITE', status='REPLACE', iostat=ios)
3674 else ! This file is being reopened, and should be appended.
3675 open(diag_cs%available_diag_doc_unit, file=trim(doc_path), access='SEQUENTIAL', form='FORMATTED', &
3676 action='WRITE', status='OLD', position='APPEND', iostat=ios)
3677 endif
3678 inquire(diag_cs%available_diag_doc_unit, opened=opened)
3679 if ((.not.opened) .or. (ios /= 0)) then
3680 call mom_error(fatal, "Failed to open available diags file "//trim(doc_path)//".")
3681 endif
3682 endif
3683 endif
3684
3685 if (is_root_pe() .and. (diag_cs%chksum_iounit < 0) .and. diag_cs%diag_as_chksum) then
3686 ! write(this_pe,'(i6.6)') PE_here()
3687 ! doc_file_dflt = "chksum_diag."//this_pe
3688 doc_file_dflt = "chksum_diag"
3689 call get_param(param_file, mdl, "CHKSUM_DIAG_FILE", doc_file, &
3690 "A file into which to write all checksums of the "//&
3691 "diagnostics listed in the diag_table.", &
3692 default=doc_file_dflt, do_not_log=(diag_cs%chksum_iounit/=-1))
3693
3694 call get_filename_appendix(filename_appendix)
3695 if (len_trim(filename_appendix) > 0) then
3696 doc_file = trim(doc_file) //'.'//trim(filename_appendix)
3697 endif
3698#ifdef STATSLABEL
3699 doc_file = trim(doc_file)//"."//trim(adjustl(statslabel))
3700#endif
3701
3702 if (len_trim(doc_file) > 0) then
3703 new_file = .true. ; if (diag_cs%chksum_iounit /= -1) new_file = .false.
3704 ! Find an unused unit number.
3705 do new_unit=512,42,-1
3706 inquire( new_unit, opened=opened)
3707 if (.not.opened) exit
3708 enddo
3709 if (opened) call mom_error(fatal, &
3710 "diag_mediator_init failed to find an unused unit number.")
3711
3712 doc_path = doc_file
3713 if (present(doc_file_dir)) then ; if (len_trim(doc_file_dir) > 0) then
3714 doc_path = trim(slasher(doc_file_dir))//trim(doc_file)
3715 endif ; endif
3716
3717 diag_cs%chksum_iounit = new_unit
3718
3719 if (new_file) then
3720 open(diag_cs%chksum_iounit, file=trim(doc_path), access='SEQUENTIAL', form='FORMATTED', &
3721 action='WRITE', status='REPLACE', iostat=ios)
3722 else ! This file is being reopened, and should be appended.
3723 open(diag_cs%chksum_iounit, file=trim(doc_path), access='SEQUENTIAL', form='FORMATTED', &
3724 action='WRITE', status='OLD', position='APPEND', iostat=ios)
3725 endif
3726 inquire(diag_cs%chksum_iounit, opened=opened)
3727 if ((.not.opened) .or. (ios /= 0)) then
3728 call mom_error(fatal, "Failed to open checksum diags file "//trim(doc_path)//".")
3729 endif
3730 endif
3731 endif
3732
3733end subroutine diag_mediator_init
3734
3735!> diag_mediator_set_OBC_info stores limited information about the locations and orientations
3736!! of open boundary condition segments at velocity points..
3737subroutine diag_mediator_set_obc_info(G, OBC_seg_u, OBC_seg_v, diag_cs)
3738 type(ocean_grid_type), intent(inout) :: g !< The ocean grid type.
3739 integer, dimension(G%IsdB:G%IedB,G%jsd:G%jed), &
3740 intent(in) :: obc_seg_u !< An array that indicates the presence and direction
3741 !! of any open boundary conditions at u-points,
3742 !! with a value of 0 for no OBC, a positive value for an
3743 !! Eastern OBC or a negative value for a Western OBC
3744 integer, dimension(G%isd:G%ied,G%JsdB:G%JedB), &
3745 intent(in) :: obc_seg_v !< An array that indicates the presence and direction
3746 !! of any open boundary conditions at v-points,
3747 !! with a value of 0 for no OBC, a positive value for a
3748 !! Northern OBC or a negative value for a Southern OBC
3749 type(diag_ctrl), intent(inout) :: diag_cs !< A defined type used to regulate diagnostics
3750
3751 integer :: i, j
3752
3753 do j=g%jsd,g%jed ; do i=g%IsdB,g%IedB
3754 diag_cs%OBC_u(i,j) = 0
3755 if (obc_seg_u(i,j) > 0) diag_cs%OBC_u(i,j) = 1
3756 if (obc_seg_u(i,j) < 0) diag_cs%OBC_u(i,j) = -1
3757 enddo ; enddo
3758
3759 do j=g%JsdB,g%JedB ; do i=g%isd,g%ied
3760 diag_cs%OBC_v(i,j) = 0.0
3761 if (obc_seg_v(i,j) > 0) diag_cs%OBC_v(i,j) = 1
3762 if (obc_seg_v(i,j) < 0) diag_cs%OBC_v(i,j) = -1
3763 enddo ; enddo
3764
3765end subroutine diag_mediator_set_obc_info
3766
3767
3768!> Set pointers to the default state fields used to remap diagnostics.
3769subroutine diag_set_state_ptrs(h, tv, diag_cs)
3770 real, dimension(:,:,:), target, intent(in ) :: h !< the model thickness array [H ~> m or kg m-2]
3771 type(thermo_var_ptrs), target, intent(in ) :: tv !< A structure with thermodynamic variables that are
3772 !! used to convert thicknesses to vertical extents
3773 type(diag_ctrl), intent(inout) :: diag_cs !< diag mediator control structure
3774
3775 ! Keep pointers to h, T, S needed for the diagnostic remapping
3776 diag_cs%h => h
3777 diag_cs%T => tv%T
3778 diag_cs%S => tv%S
3779 diag_cs%eqn_of_state => tv%eqn_of_state
3780 diag_cs%tv => tv
3781
3782end subroutine
3783
3784!> Build/update vertical grids for diagnostic remapping.
3785!! \note The target grids need to be updated whenever sea surface
3786!! height changes.
3787subroutine diag_update_remap_grids(diag_cs, alt_h, alt_T, alt_S, update_intensive, update_extensive )
3788 type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
3789 real, target, optional, intent(in ) :: alt_h(:,:,:) !< Used if remapped grids should be something other than
3790 !! the current thicknesses [H ~> m or kg m-2]
3791 real, target, optional, intent(in ) :: alt_t(:,:,:) !< Used if remapped grids should be something other than
3792 !! the current temperatures [C ~> degC]
3793 real, target, optional, intent(in ) :: alt_s(:,:,:) !< Used if remapped grids should be something other than
3794 !! the current salinity [S ~> ppt]
3795 logical, optional, intent(in ) :: update_intensive !< If true (default), update the grids used for
3796 !! intensive diagnostics
3797 logical, optional, intent(in ) :: update_extensive !< If true (not default), update the grids used for
3798 !! intensive diagnostics
3799 ! Local variables
3800 integer :: m
3801 real, dimension(:,:,:), pointer :: h_diag => null() ! The layer thicknesses for diagnostics [H ~> m or kg m-2]
3802 real, dimension(:,:,:), pointer :: t_diag => null() ! The layer temperatures for diagnostics [C ~> degC]
3803 real, dimension(:,:,:), pointer :: s_diag => null() ! The layer salinities for diagnostics [S ~> ppt]
3804 real, dimension(diag_cs%G%isd:diag_cS%G%ied, diag_cs%G%jsd:diag_cS%G%jed, diag_cs%GV%ke) :: &
3805 dz_diag ! Layer vertical extents for remapping [Z ~> m]
3806 logical :: update_intensive_local, update_extensive_local, dz_diag_needed
3807
3808 if (diag_cs%show_call_tree) call calltree_enter("diag_update_remap_grids()")
3809
3810 ! Set values based on optional input arguments
3811 if (present(alt_h)) then
3812 h_diag => alt_h
3813 else
3814 h_diag => diag_cs%h
3815 endif
3816
3817 if (present(alt_t)) then
3818 t_diag => alt_t
3819 else
3820 t_diag => diag_cs%T
3821 endif
3822
3823 if (present(alt_s)) then
3824 s_diag => alt_s
3825 else
3826 s_diag => diag_cs%S
3827 endif
3828
3829 ! Defaults here are based on wanting to update intensive quantities frequently as soon as the model state changes.
3830 ! Conversely, for extensive quantities, in an effort to close budgets and to be consistent with the total time
3831 ! tendency, we construct the diagnostic grid at the beginning of the baroclinic timestep and remap all extensive
3832 ! quantities to the same grid
3833 update_intensive_local = .true.
3834 if (present(update_intensive)) update_intensive_local = update_intensive
3835 update_extensive_local = .false.
3836 if (present(update_extensive)) update_extensive_local = update_extensive
3837
3838 if (id_clock_diag_grid_updates>0) call cpu_clock_begin(id_clock_diag_grid_updates)
3839
3840 if (diag_cs%diag_grid_overridden) then
3841 call mom_error(fatal, "diag_update_remap_grids was called, but current grids in "// &
3842 "diagnostic structure have been overridden")
3843 endif
3844
3845 ! Determine the diagnostic grid spacing in height units, if it is needed.
3846 dz_diag_needed = .false.
3847 if (update_intensive_local .or. update_extensive_local) then
3848 do m=1, diag_cs%num_diag_coords
3849 if (diag_cs%diag_remap_cs(m)%Z_based_coord) dz_diag_needed = .true.
3850 enddo
3851 endif
3852 if (dz_diag_needed) then
3853 call thickness_to_dz(h_diag, diag_cs%tv, dz_diag, diag_cs%G, diag_cs%GV, diag_cs%US, halo_size=1)
3854 endif
3855
3856 if (update_intensive_local) then
3857 do m=1, diag_cs%num_diag_coords
3858 if (diag_cs%diag_remap_cs(m)%Z_based_coord) then
3859 call diag_remap_update(diag_cs%diag_remap_cs(m), diag_cs%G, diag_cs%GV, diag_cs%US, dz_diag, t_diag, s_diag, &
3860 diag_cs%eqn_of_state, diag_cs%diag_remap_cs(m)%h)
3861 else
3862 call diag_remap_update(diag_cs%diag_remap_cs(m), diag_cs%G, diag_cs%GV, diag_cs%US, h_diag, t_diag, s_diag, &
3863 diag_cs%eqn_of_state, diag_cs%diag_remap_cs(m)%h)
3864 endif
3865 enddo
3866 endif
3867 if (update_extensive_local) then
3868 diag_cs%h_begin(:,:,:) = diag_cs%h(:,:,:)
3869 if (dz_diag_needed) diag_cs%dz_begin(:,:,:) = dz_diag(:,:,:)
3870 do m=1, diag_cs%num_diag_coords
3871 if (diag_cs%diag_remap_cs(m)%Z_based_coord) then
3872 call diag_remap_update(diag_cs%diag_remap_cs(m), diag_cs%G, diag_cs%GV, diag_cs%US, dz_diag, t_diag, s_diag, &
3873 diag_cs%eqn_of_state, diag_cs%diag_remap_cs(m)%h_extensive)
3874 else
3875 call diag_remap_update(diag_cs%diag_remap_cs(m), diag_cs%G, diag_cs%GV, diag_cs%US, h_diag, t_diag, s_diag, &
3876 diag_cs%eqn_of_state, diag_cs%diag_remap_cs(m)%h_extensive)
3877 endif
3878 enddo
3879 endif
3880
3881#if defined(DEBUG) || defined(__DO_SAFETY_CHECKS__)
3882 ! Keep a copy of H - used to check whether grids are up-to-date
3883 ! when doing remapping.
3884 diag_cs%h_old(:,:,:) = diag_cs%h(:,:,:)
3885#endif
3886
3887 if (id_clock_diag_grid_updates>0) call cpu_clock_end(id_clock_diag_grid_updates)
3888
3889 if (diag_cs%show_call_tree) call calltree_leave("diag_update_remap_grids()")
3890
3891end subroutine diag_update_remap_grids
3892
3893!> Sets up the 2d and 3d masks for native diagnostics
3894subroutine diag_masks_set(G, nz, diag_cs)
3895 type(ocean_grid_type), target, intent(in) :: g !< The ocean grid type.
3896 integer, intent(in) :: nz !< The number of layers in the model's native grid.
3897 type(diag_ctrl), pointer :: diag_cs !< A pointer to a type with many variables
3898 !! used for diagnostics
3899 ! Local variables
3900 integer :: k
3901
3902 ! 2d masks point to the model masks since they are identical
3903 diag_cs%mask2dT => g%mask2dT
3904 diag_cs%mask2dBu => g%mask2dBu
3905 diag_cs%mask2dCu => g%mask2dCu
3906 diag_cs%mask2dCv => g%mask2dCv
3907
3908 ! 3d native masks are needed by diag_manager but the native variables
3909 ! can only be masked 2d - for ocean points, all layers exists.
3910 allocate(diag_cs%mask3dTL(g%isd:g%ied,g%jsd:g%jed,1:nz))
3911 allocate(diag_cs%mask3dBL(g%IsdB:g%IedB,g%JsdB:g%JedB,1:nz))
3912 allocate(diag_cs%mask3dCuL(g%IsdB:g%IedB,g%jsd:g%jed,1:nz))
3913 allocate(diag_cs%mask3dCvL(g%isd:g%ied,g%JsdB:g%JedB,1:nz))
3914 do k=1,nz
3915 diag_cs%mask3dTL(:,:,k) = diag_cs%mask2dT(:,:)
3916 diag_cs%mask3dBL(:,:,k) = diag_cs%mask2dBu(:,:)
3917 diag_cs%mask3dCuL(:,:,k) = diag_cs%mask2dCu(:,:)
3918 diag_cs%mask3dCvL(:,:,k) = diag_cs%mask2dCv(:,:)
3919 enddo
3920 allocate(diag_cs%mask3dTi(g%isd:g%ied,g%jsd:g%jed,1:nz+1))
3921 allocate(diag_cs%mask3dBi(g%IsdB:g%IedB,g%JsdB:g%JedB,1:nz+1))
3922 allocate(diag_cs%mask3dCui(g%IsdB:g%IedB,g%jsd:g%jed,1:nz+1))
3923 allocate(diag_cs%mask3dCvi(g%isd:g%ied,g%JsdB:g%JedB,1:nz+1))
3924 do k=1,nz+1
3925 diag_cs%mask3dTi(:,:,k) = diag_cs%mask2dT(:,:)
3926 diag_cs%mask3dBi(:,:,k) = diag_cs%mask2dBu(:,:)
3927 diag_cs%mask3dCui(:,:,k) = diag_cs%mask2dCu(:,:)
3928 diag_cs%mask3dCvi(:,:,k) = diag_cs%mask2dCv(:,:)
3929 enddo
3930
3931 ! Allocate and initialize the downsampled masks
3932 call downsample_diag_masks_set(g, nz, diag_cs)
3933
3934end subroutine diag_masks_set
3935
3936!> Set the extents and fill values for the piecemeal buffers for all axes
3937subroutine set_piecemeal_extents(diag_cs)
3938 type(diag_ctrl), intent(inout) :: diag_cs !< A pointer to a type with many variables
3939 !! used for diagnostics
3940
3941 ! Piecemeal buffers for 2d axes
3942 call diag_cs%axesT1%piecemeal_2d%set_extents_from_array(diag_cs%mask2dT, diag_cs%missing_value)
3943 call diag_cs%axesB1%piecemeal_2d%set_extents_from_array(diag_cs%mask2dBu, diag_cs%missing_value)
3944 call diag_cs%axesCu1%piecemeal_2d%set_extents_from_array(diag_cs%mask2dCu, diag_cs%missing_value)
3945 call diag_cs%axesCv1%piecemeal_2d%set_extents_from_array(diag_cs%mask2dCv, diag_cs%missing_value)
3946
3947 ! Piecemeal buffers for 3d axes
3948 call diag_cs%axesTL%piecemeal_3d%set_extents_from_array(diag_cs%mask3dTL, diag_cs%missing_value)
3949 call diag_cs%axesBL%piecemeal_3d%set_extents_from_array(diag_cs%mask3dBL, diag_cs%missing_value)
3950 call diag_cs%axesCuL%piecemeal_3d%set_extents_from_array(diag_cs%mask3dCuL, diag_cs%missing_value)
3951 call diag_cs%axesCvL%piecemeal_3d%set_extents_from_array(diag_cs%mask3dCvL, diag_cs%missing_value)
3952 call diag_cs%axesTi%piecemeal_3d%set_extents_from_array(diag_cs%mask3dTi, diag_cs%missing_value)
3953 call diag_cs%axesBi%piecemeal_3d%set_extents_from_array(diag_cs%mask3dBi, diag_cs%missing_value)
3954 call diag_cs%axesCui%piecemeal_3d%set_extents_from_array(diag_cs%mask3dCui, diag_cs%missing_value)
3955 call diag_cs%axesCvi%piecemeal_3d%set_extents_from_array(diag_cs%mask3dCvi, diag_cs%missing_value)
3956
3957end subroutine set_piecemeal_extents
3958
3959subroutine diag_mediator_close_registration(diag_CS)
3960 type(diag_ctrl), intent(inout) :: diag_cs !< Structure used to regulate diagnostic output
3961
3962 integer :: i
3963
3964 if (diag_cs%available_diag_doc_unit > -1) then
3965 close(diag_cs%available_diag_doc_unit) ; diag_cs%available_diag_doc_unit = -2
3966 endif
3967
3968 do i=1, diag_cs%num_diag_coords
3969 call diag_remap_diag_registration_closed(diag_cs%diag_remap_cs(i))
3970 enddo
3971
3973
3974subroutine axes_grp_end(axes)
3975 type(axes_grp), intent(inout) :: axes !< Axes group to be destroyed
3976
3977 deallocate(axes%handles)
3978 if (associated(axes%mask2d)) deallocate(axes%mask2d)
3979 if (associated(axes%mask3d)) deallocate(axes%mask3d)
3980end subroutine axes_grp_end
3981
3982subroutine diag_mediator_end(time, diag_CS, end_diag_manager)
3983 type(time_type), intent(in) :: time !< The current model time
3984 type(diag_ctrl), intent(inout) :: diag_cs !< Structure used to regulate diagnostic output
3985 logical, optional, intent(in) :: end_diag_manager !< If true, call diag_manager_end()
3986
3987 ! Local variables
3988 type(diag_type), pointer :: diag, next_diag
3989 integer :: i, dl
3990
3991 if (diag_cs%available_diag_doc_unit > -1) then
3992 close(diag_cs%available_diag_doc_unit) ; diag_cs%available_diag_doc_unit = -3
3993 endif
3994 if (diag_cs%chksum_iounit > -1) then
3995 close(diag_cs%chksum_iounit) ; diag_cs%chksum_iounit = -3
3996 endif
3997
3998 do i=1, diag_cs%next_free_diag_id - 1
3999 if (associated(diag_cs%diags(i)%next)) then
4000 next_diag => diag_cs%diags(i)%next
4001 do while (associated(next_diag))
4002 diag => next_diag
4003 next_diag => diag%next
4004 deallocate(diag)
4005 enddo
4006 endif
4007 enddo
4008
4009 deallocate(diag_cs%diags)
4010
4011 do i=1, diag_cs%num_diag_coords
4012 call diag_remap_end(diag_cs%diag_remap_cs(i))
4013 enddo
4014
4015 call diag_grid_storage_end(diag_cs%diag_grid_temp)
4016 if (associated(diag_cs%mask3dTL)) deallocate(diag_cs%mask3dTL)
4017 if (associated(diag_cs%mask3dBL)) deallocate(diag_cs%mask3dBL)
4018 if (associated(diag_cs%mask3dCuL)) deallocate(diag_cs%mask3dCuL)
4019 if (associated(diag_cs%mask3dCvL)) deallocate(diag_cs%mask3dCvL)
4020 if (associated(diag_cs%mask3dTi)) deallocate(diag_cs%mask3dTi)
4021 if (associated(diag_cs%mask3dBi)) deallocate(diag_cs%mask3dBi)
4022 if (associated(diag_cs%mask3dCui)) deallocate(diag_cs%mask3dCui)
4023 if (associated(diag_cs%mask3dCvi)) deallocate(diag_cs%mask3dCvi)
4024 do dl=1, diag_cs%num_diag_dsamp_levels
4025 if (associated(diag_cs%dsamp(dl)%mask2dT)) deallocate(diag_cs%dsamp(dl)%mask2dT)
4026 if (associated(diag_cs%dsamp(dl)%mask2dBu)) deallocate(diag_cs%dsamp(dl)%mask2dBu)
4027 if (associated(diag_cs%dsamp(dl)%mask2dCu)) deallocate(diag_cs%dsamp(dl)%mask2dCu)
4028 if (associated(diag_cs%dsamp(dl)%mask2dCv)) deallocate(diag_cs%dsamp(dl)%mask2dCv)
4029 if (associated(diag_cs%dsamp(dl)%mask3dTL)) deallocate(diag_cs%dsamp(dl)%mask3dTL)
4030 if (associated(diag_cs%dsamp(dl)%mask3dBL)) deallocate(diag_cs%dsamp(dl)%mask3dBL)
4031 if (associated(diag_cs%dsamp(dl)%mask3dCuL)) deallocate(diag_cs%dsamp(dl)%mask3dCuL)
4032 if (associated(diag_cs%dsamp(dl)%mask3dCvL)) deallocate(diag_cs%dsamp(dl)%mask3dCvL)
4033 if (associated(diag_cs%dsamp(dl)%mask3dTi)) deallocate(diag_cs%dsamp(dl)%mask3dTi)
4034 if (associated(diag_cs%dsamp(dl)%mask3dBi)) deallocate(diag_cs%dsamp(dl)%mask3dBi)
4035 if (associated(diag_cs%dsamp(dl)%mask3dCui)) deallocate(diag_cs%dsamp(dl)%mask3dCui)
4036 if (associated(diag_cs%dsamp(dl)%mask3dCvi)) deallocate(diag_cs%dsamp(dl)%mask3dCvi)
4037
4038 do i=1,diag_cs%num_diag_coords
4039 if (associated(diag_cs%dsamp(dl)%remap_axesTL(i)%dsamp(dl)%mask3d)) &
4040 deallocate(diag_cs%dsamp(dl)%remap_axesTL(i)%dsamp(dl)%mask3d)
4041 if (associated(diag_cs%dsamp(dl)%remap_axesCuL(i)%dsamp(dl)%mask3d)) &
4042 deallocate(diag_cs%dsamp(dl)%remap_axesCuL(i)%dsamp(dl)%mask3d)
4043 if (associated(diag_cs%dsamp(dl)%remap_axesCvL(i)%dsamp(dl)%mask3d)) &
4044 deallocate(diag_cs%dsamp(dl)%remap_axesCvL(i)%dsamp(dl)%mask3d)
4045 if (associated(diag_cs%dsamp(dl)%remap_axesBL(i)%dsamp(dl)%mask3d)) &
4046 deallocate(diag_cs%dsamp(dl)%remap_axesBL(i)%dsamp(dl)%mask3d)
4047 if (associated(diag_cs%dsamp(dl)%remap_axesTi(i)%dsamp(dl)%mask3d)) &
4048 deallocate(diag_cs%dsamp(dl)%remap_axesTi(i)%dsamp(dl)%mask3d)
4049 if (associated(diag_cs%dsamp(dl)%remap_axesCui(i)%dsamp(dl)%mask3d)) &
4050 deallocate(diag_cs%dsamp(dl)%remap_axesCui(i)%dsamp(dl)%mask3d)
4051 if (associated(diag_cs%dsamp(dl)%remap_axesCvi(i)%dsamp(dl)%mask3d)) &
4052 deallocate(diag_cs%dsamp(dl)%remap_axesCvi(i)%dsamp(dl)%mask3d)
4053 if (associated(diag_cs%dsamp(dl)%remap_axesBi(i)%dsamp(dl)%mask3d)) &
4054 deallocate(diag_cs%dsamp(dl)%remap_axesBi(i)%dsamp(dl)%mask3d)
4055 enddo
4056 enddo
4057
4058 ! axes_grp masks may point to diag_cs masks, so do these after mask dealloc
4059 do i=1, diag_cs%num_diag_coords
4060 call axes_grp_end(diag_cs%remap_axesZL(i))
4061 call axes_grp_end(diag_cs%remap_axesZi(i))
4062 call axes_grp_end(diag_cs%remap_axesTL(i))
4063 call axes_grp_end(diag_cs%remap_axesTi(i))
4064 call axes_grp_end(diag_cs%remap_axesBL(i))
4065 call axes_grp_end(diag_cs%remap_axesBi(i))
4066 call axes_grp_end(diag_cs%remap_axesCuL(i))
4067 call axes_grp_end(diag_cs%remap_axesCui(i))
4068 call axes_grp_end(diag_cs%remap_axesCvL(i))
4069 call axes_grp_end(diag_cs%remap_axesCvi(i))
4070 enddo
4071
4072 if (diag_cs%num_diag_coords > 0) then
4073 deallocate(diag_cs%remap_axesZL)
4074 deallocate(diag_cs%remap_axesZi)
4075 deallocate(diag_cs%remap_axesTL)
4076 deallocate(diag_cs%remap_axesTi)
4077 deallocate(diag_cs%remap_axesBL)
4078 deallocate(diag_cs%remap_axesBi)
4079 deallocate(diag_cs%remap_axesCuL)
4080 deallocate(diag_cs%remap_axesCui)
4081 deallocate(diag_cs%remap_axesCvL)
4082 deallocate(diag_cs%remap_axesCvi)
4083 endif
4084
4085 do dl=1, diag_cs%num_diag_dsamp_levels
4086 if (allocated(diag_cs%dsamp(dl)%remap_axesTL)) &
4087 deallocate(diag_cs%dsamp(dl)%remap_axesTL)
4088 if (allocated(diag_cs%dsamp(dl)%remap_axesTi)) &
4089 deallocate(diag_cs%dsamp(dl)%remap_axesTi)
4090 if (allocated(diag_cs%dsamp(dl)%remap_axesBL)) &
4091 deallocate(diag_cs%dsamp(dl)%remap_axesBL)
4092 if (allocated(diag_cs%dsamp(dl)%remap_axesBi)) &
4093 deallocate(diag_cs%dsamp(dl)%remap_axesBi)
4094 if (allocated(diag_cs%dsamp(dl)%remap_axesCuL)) &
4095 deallocate(diag_cs%dsamp(dl)%remap_axesCuL)
4096 if (allocated(diag_cs%dsamp(dl)%remap_axesCui)) &
4097 deallocate(diag_cs%dsamp(dl)%remap_axesCui)
4098 if (allocated(diag_cs%dsamp(dl)%remap_axesCvL)) &
4099 deallocate(diag_cs%dsamp(dl)%remap_axesCvL)
4100 if (allocated(diag_cs%dsamp(dl)%remap_axesCvi)) &
4101 deallocate(diag_cs%dsamp(dl)%remap_axesCvi)
4102 enddo
4103
4104
4105#if defined(DEBUG) || defined(__DO_SAFETY_CHECKS__)
4106 deallocate(diag_cs%h_old)
4107#endif
4108
4109 if (present(end_diag_manager)) then
4110 if (end_diag_manager) call mom_diag_manager_end(time)
4111 endif
4112
4113end subroutine diag_mediator_end
4114
4115!> Returns a new diagnostic id, it may be necessary to expand the diagnostics array.
4116integer function get_new_diag_id(diag_cs)
4117 type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
4118 ! Local variables
4119 type(diag_type), dimension(:), allocatable :: tmp
4120 integer :: i
4121
4122 if (diag_cs%next_free_diag_id > size(diag_cs%diags)) then
4123 call assert(diag_cs%next_free_diag_id - size(diag_cs%diags) == 1, &
4124 'get_new_diag_id: inconsistent diag id')
4125
4126 ! Increase the size of diag_cs%diags and copy data over.
4127 ! Do not use move_alloc() because it is not supported by Fortran 90
4128 allocate(tmp(size(diag_cs%diags)))
4129 tmp(:) = diag_cs%diags(:)
4130 deallocate(diag_cs%diags)
4131 allocate(diag_cs%diags(size(tmp) + diag_alloc_chunk_size))
4132 diag_cs%diags(1:size(tmp)) = tmp(:)
4133 deallocate(tmp)
4134
4135 ! Initialize new part of the diag array.
4136 do i=diag_cs%next_free_diag_id, size(diag_cs%diags)
4137 call initialize_diag_type(diag_cs%diags(i))
4138 enddo
4139 endif
4140
4141 get_new_diag_id = diag_cs%next_free_diag_id
4142 diag_cs%next_free_diag_id = diag_cs%next_free_diag_id + 1
4143
4144end function get_new_diag_id
4145
4146!> Initializes a diag_type (used after allocating new memory)
4147subroutine initialize_diag_type(diag)
4148 type(diag_type), intent(inout) :: diag !< diag_type to be initialized
4149
4150 diag%in_use = .false.
4151 diag%fms_diag_id = -1
4152 diag%axes => null()
4153 diag%next => null()
4154 diag%conversion_factor = 0.
4155
4156end subroutine initialize_diag_type
4157
4158!> Make a new diagnostic. Either use memory which is in the array of 'primary'
4159!! diagnostics, or if that is in use, insert it to the list of secondary diagnostics.
4160subroutine alloc_diag_with_id(diag_id, diag_cs, diag)
4161 integer, intent(in ) :: diag_id !< id for the diagnostic
4162 type(diag_ctrl), target, intent(inout) :: diag_cs !< structure used to regulate diagnostic output
4163 type(diag_type), pointer :: diag !< structure representing a diagnostic (inout)
4164
4165 type(diag_type), pointer :: tmp => null()
4166
4167 if (.not. diag_cs%diags(diag_id)%in_use) then
4168 diag => diag_cs%diags(diag_id)
4169 else
4170 allocate(diag)
4171 tmp => diag_cs%diags(diag_id)%next
4172 diag_cs%diags(diag_id)%next => diag
4173 diag%next => tmp
4174 endif
4175 diag%in_use = .true.
4176
4177end subroutine alloc_diag_with_id
4178
4179!> Log a diagnostic to the available diagnostics file.
4180subroutine log_available_diag(used, module_name, field_name, cell_methods_string, comment, &
4181 diag_CS, long_name, units, standard_name, variants, dimensions)
4182 logical, intent(in) :: used !< Whether this diagnostic was in the diag_table or not
4183 character(len=*), intent(in) :: module_name !< Name of the diagnostic module
4184 character(len=*), intent(in) :: field_name !< Name of this diagnostic field
4185 character(len=*), intent(in) :: cell_methods_string !< The spatial component of the CF cell_methods attribute
4186 character(len=*), intent(in) :: comment !< A comment to append after [Used|Unused]
4187 type(diag_ctrl), intent(in) :: diag_CS !< The diagnostics control structure
4188 character(len=*), optional, intent(in) :: dimensions !< Descriptor of the horizontal and vertical dimensions
4189 character(len=*), optional, intent(in) :: long_name !< CF long name of diagnostic
4190 character(len=*), optional, intent(in) :: units !< Units for diagnostic
4191 character(len=*), optional, intent(in) :: standard_name !< CF standardized name of diagnostic
4192 character(len=*), optional, intent(in) :: variants !< Alternate modules and variable names for
4193 !! this diagnostic and derived diagnostics
4194 ! Local variables
4195 character(len=240) :: mesg
4196
4197 if (used) then
4198 mesg = '"'//trim(field_name)//'" [Used]'
4199 else
4200 mesg = '"'//trim(field_name)//'" [Unused]'
4201 endif
4202 if (len(trim((comment)))>0) then
4203 write(diag_cs%available_diag_doc_unit, '(a,1x,"(",a,")")') trim(mesg),trim(comment)
4204 else
4205 write(diag_cs%available_diag_doc_unit, '(a)') trim(mesg)
4206 endif
4207 call describe_option("modules", module_name, diag_cs)
4208 if (present(dimensions)) then
4209 if (len(trim(dimensions)) > 0) then
4210 call describe_option("dimensions", dimensions, diag_cs)
4211 endif
4212 endif
4213 if (present(long_name)) call describe_option("long_name", long_name, diag_cs)
4214 if (present(units)) call describe_option("units", units, diag_cs)
4215 if (present(standard_name)) &
4216 call describe_option("standard_name", standard_name, diag_cs)
4217 if (len(trim((cell_methods_string)))>0) &
4218 call describe_option("cell_methods", trim(cell_methods_string), diag_cs)
4219 if (present(variants)) then ; if (len(trim(variants)) > 0) then
4220 call describe_option("variants", variants, diag_cs)
4221 endif ; endif
4222end subroutine log_available_diag
4223
4224!> Log the diagnostic chksum to the chksum diag file
4225subroutine log_chksum_diag(docunit, description, chksum)
4226 integer, intent(in) :: docunit !< Handle of the log file
4227 character(len=*), intent(in) :: description !< Name of the diagnostic module
4228 integer, intent(in) :: chksum !< chksum of the diagnostic
4229
4230 write(docunit, '(a,1x,i9.8)') description, chksum
4231 flush(docunit)
4232
4233end subroutine log_chksum_diag
4234
4235!> Allocates fields necessary to store diagnostic remapping fields
4236subroutine diag_grid_storage_init(grid_storage, G, GV, diag)
4237 type(diag_grid_storage), intent(inout) :: grid_storage !< Structure containing a snapshot of the target grids
4238 type(ocean_grid_type), intent(in) :: g !< Horizontal grid
4239 type(verticalgrid_type), intent(in) :: gv !< ocean vertical grid structure
4240 type(diag_ctrl), intent(in) :: diag !< Diagnostic control structure used as the constructor
4241 !! template for this routine
4242
4243 integer :: m, nz
4244 grid_storage%num_diag_coords = diag%num_diag_coords
4245
4246 ! Don't do anything else if there are no remapped coordinates
4247 if (grid_storage%num_diag_coords < 1) return
4248
4249 ! Allocate memory for the native space
4250 allocate( grid_storage%h_state(g%isd:g%ied, g%jsd:g%jed, gv%ke))
4251 ! Allocate diagnostic remapping structures
4252 allocate(grid_storage%diag_grids(diag%num_diag_coords))
4253 ! Loop through and allocate memory for the grid on each target coordinate
4254 do m = 1, diag%num_diag_coords
4255 nz = diag%diag_remap_cs(m)%nz
4256 allocate(grid_storage%diag_grids(m)%h(g%isd:g%ied,g%jsd:g%jed, nz))
4257 enddo
4258
4259end subroutine diag_grid_storage_init
4260
4261!> Copy from the main diagnostic arrays to the grid storage as well as the native thicknesses
4262subroutine diag_copy_diag_to_storage(grid_storage, h_state, diag)
4263 type(diag_grid_storage), intent(inout) :: grid_storage !< Structure containing a snapshot of the target grids
4264 real, dimension(:,:,:), intent(in) :: h_state !< Current model thicknesses [H ~> m or kg m-2]
4265 type(diag_ctrl), intent(in) :: diag !< Diagnostic control structure used as the constructor
4266
4267 integer :: m
4268
4269 ! Don't do anything else if there are no remapped coordinates
4270 if (grid_storage%num_diag_coords < 1) return
4271
4272 grid_storage%h_state(:,:,:) = h_state(:,:,:)
4273 do m = 1,grid_storage%num_diag_coords
4274 if (diag%diag_remap_cs(m)%nz > 0) &
4275 grid_storage%diag_grids(m)%h(:,:,:) = diag%diag_remap_cs(m)%h(:,:,:)
4276 enddo
4277
4278end subroutine diag_copy_diag_to_storage
4279
4280!> Copy from the stored diagnostic arrays to the main diagnostic grids
4281subroutine diag_copy_storage_to_diag(diag, grid_storage)
4282 type(diag_ctrl), intent(inout) :: diag !< Diagnostic control structure used as the constructor
4283 type(diag_grid_storage), intent(in) :: grid_storage !< Structure containing a snapshot of the target grids
4284
4285 integer :: m
4286
4287 ! Don't do anything else if there are no remapped coordinates
4288 if (grid_storage%num_diag_coords < 1) return
4289
4290 diag%diag_grid_overridden = .true.
4291 do m = 1,grid_storage%num_diag_coords
4292 if (diag%diag_remap_cs(m)%nz > 0) &
4293 diag%diag_remap_cs(m)%h(:,:,:) = grid_storage%diag_grids(m)%h(:,:,:)
4294 enddo
4295
4296end subroutine diag_copy_storage_to_diag
4297
4298!> Save the current diagnostic grids in the temporary structure within diag
4299subroutine diag_save_grids(diag)
4300 type(diag_ctrl), intent(inout) :: diag !< Diagnostic control structure used as the constructor
4301
4302 integer :: m
4303
4304 ! Don't do anything else if there are no remapped coordinates
4305 if (diag%num_diag_coords < 1) return
4306
4307 do m = 1,diag%num_diag_coords
4308 if (diag%diag_remap_cs(m)%nz > 0) &
4309 diag%diag_grid_temp%diag_grids(m)%h(:,:,:) = diag%diag_remap_cs(m)%h(:,:,:)
4310 enddo
4311
4312end subroutine diag_save_grids
4313
4314!> Restore the diagnostic grids from the temporary structure within diag
4315subroutine diag_restore_grids(diag)
4316 type(diag_ctrl), intent(inout) :: diag !< Diagnostic control structure used as the constructor
4317
4318 integer :: m
4319
4320 ! Don't do anything else if there are no remapped coordinates
4321 if (diag%num_diag_coords < 1) return
4322
4323 diag%diag_grid_overridden = .false.
4324 do m = 1,diag%num_diag_coords
4325 if (diag%diag_remap_cs(m)%nz > 0) &
4326 diag%diag_remap_cs(m)%h(:,:,:) = diag%diag_grid_temp%diag_grids(m)%h(:,:,:)
4327 enddo
4328
4329end subroutine diag_restore_grids
4330
4331!> Deallocates the fields in the remapping fields container
4332subroutine diag_grid_storage_end(grid_storage)
4333 type(diag_grid_storage), intent(inout) :: grid_storage !< Structure containing a snapshot of the target grids
4334 ! Local variables
4335 integer :: m
4336
4337 ! Don't do anything else if there are no remapped coordinates
4338 if (grid_storage%num_diag_coords < 1) return
4339
4340 ! Deallocate memory for the native space
4341 deallocate(grid_storage%h_state)
4342 ! Loop through and deallocate memory for the grid on each target coordinate
4343 do m = 1, grid_storage%num_diag_coords
4344 deallocate(grid_storage%diag_grids(m)%h)
4345 enddo
4346 ! Deallocate diagnostic remapping structures
4347 deallocate(grid_storage%diag_grids)
4348end subroutine diag_grid_storage_end
4349
4350!< Allocate and initialize the masks for downsampled diagnostics in diag_cs
4351!! The downsampled masks in the axes would later "point" to these.
4352subroutine downsample_diag_masks_set(G, nz, diag_cs)
4353 type(ocean_grid_type), target, intent(in) :: G !< The ocean grid type.
4354 integer, intent(in) :: nz !< The number of layers in the model's native grid.
4355 type(diag_ctrl), pointer :: diag_cs !< A pointer to a type with many variables
4356 !! used for diagnostics
4357 ! Local variables
4358 integer :: k, dl, dlfac
4359
4360!print*,'original c extents ',G%isc,G%iec,G%jsc,G%jec
4361!print*,'original c extents ',G%iscb,G%iecb,G%jscb,G%jecb
4362!print*,'coarse c extents ',G%HId2%isc,G%HId2%iec,G%HId2%jsc,G%HId2%jec
4363!print*,'original d extents ',G%isd,G%ied,G%jsd,G%jed
4364!print*,'coarse d extents ',G%HId2%isd,G%HId2%ied,G%HId2%jsd,G%HId2%jed
4365! original c extents 5 52 5 52
4366! original cB-nonsym extents 5 52 5 52
4367! original cB-sym extents 4 52 4 52
4368! coarse c extents 3 26 3 26
4369! original d extents 1 56 1 56
4370! original dB-nonsym extents 1 56 1 56
4371! original dB-sym extents 0 56 0 56
4372! coarse d extents 1 28 1 28
4373
4374 do dl=1, diag_cs%num_diag_dsamp_levels
4375 dlfac = diag_cs%diag_dsamp_levels(dl) !Actual downsampling factor for this level
4376 ! 2d mask
4377 call downsample_mask(g%mask2dT, diag_cs%dsamp(dl)%mask2dT, dlfac, mmp, g%isc, g%jsc, g%isd, g%jsd, &
4378 g%HId(dl)%isc, g%HId(dl)%iec, g%HId(dl)%jsc, g%HId(dl)%jec, g%HId(dl)%isd, g%HId(dl)%ied, &
4379 g%HId(dl)%jsd, g%HId(dl)%jed)
4380 call downsample_mask(g%mask2dBu, diag_cs%dsamp(dl)%mask2dBu, dlfac, ppp,g%IscB, g%JscB, g%IsdB, g%JsdB, &
4381 g%HId(dl)%IscB,g%HId(dl)%IecB, g%HId(dl)%JscB,g%HId(dl)%JecB,g%HId(dl)%IsdB,g%HId(dl)%IedB, &
4382 g%HId(dl)%JsdB,g%HId(dl)%JedB)
4383 call downsample_mask(g%mask2dCu, diag_cs%dsamp(dl)%mask2dCu, dlfac, pmp, g%IscB, g%jsc, g%IsdB, g%jsd, &
4384 g%HId(dl)%IscB,g%HId(dl)%IecB, g%HId(dl)%jsc, g%HId(dl)%jec,g%HId(dl)%IsdB,g%HId(dl)%IedB, &
4385 g%HId(dl)%jsd, g%HId(dl)%jed)
4386 call downsample_mask(g%mask2dCv, diag_cs%dsamp(dl)%mask2dCv, dlfac, mpp, g %isc ,g%JscB, g%isd, g%JsdB, &
4387 g%HId(dl)%isc ,g%HId(dl)%iec, g%HId(dl)%JscB,g%HId(dl)%JecB,g%HId(dl)%isd ,g%HId(dl)%ied, &
4388 g%HId(dl)%JsdB,g%HId(dl)%JedB)
4389 ! 3d native masks are needed by diag_manager but the native variables
4390 ! can only be masked 2d - for ocean points, all layers exists.
4391 allocate(diag_cs%dsamp(dl)%mask3dTL(g%HId(dl)%isd:g%HId(dl)%ied,g%HId(dl)%jsd:g%HId(dl)%jed,1:nz))
4392 allocate(diag_cs%dsamp(dl)%mask3dBL(g%HId(dl)%IsdB:g%HId(dl)%IedB,g%HId(dl)%JsdB:g%HId(dl)%JedB,1:nz))
4393 allocate(diag_cs%dsamp(dl)%mask3dCuL(g%HId(dl)%IsdB:g%HId(dl)%IedB,g%HId(dl)%jsd:g%HId(dl)%jed,1:nz))
4394 allocate(diag_cs%dsamp(dl)%mask3dCvL(g%HId(dl)%isd:g%HId(dl)%ied,g%HId(dl)%JsdB:g%HId(dl)%JedB,1:nz))
4395 do k=1,nz
4396 diag_cs%dsamp(dl)%mask3dTL(:,:,k) = diag_cs%dsamp(dl)%mask2dT(:,:)
4397 diag_cs%dsamp(dl)%mask3dBL(:,:,k) = diag_cs%dsamp(dl)%mask2dBu(:,:)
4398 diag_cs%dsamp(dl)%mask3dCuL(:,:,k) = diag_cs%dsamp(dl)%mask2dCu(:,:)
4399 diag_cs%dsamp(dl)%mask3dCvL(:,:,k) = diag_cs%dsamp(dl)%mask2dCv(:,:)
4400 enddo
4401 allocate(diag_cs%dsamp(dl)%mask3dTi(g%HId(dl)%isd:g%HId(dl)%ied,g%HId(dl)%jsd:g%HId(dl)%jed,1:nz+1))
4402 allocate(diag_cs%dsamp(dl)%mask3dBi(g%HId(dl)%IsdB:g%HId(dl)%IedB,g%HId(dl)%JsdB:g%HId(dl)%JedB,1:nz+1))
4403 allocate(diag_cs%dsamp(dl)%mask3dCui(g%HId(dl)%IsdB:g%HId(dl)%IedB,g%HId(dl)%jsd:g%HId(dl)%jed,1:nz+1))
4404 allocate(diag_cs%dsamp(dl)%mask3dCvi(g%HId(dl)%isd:g%HId(dl)%ied,g%HId(dl)%JsdB:g%HId(dl)%JedB,1:nz+1))
4405 do k=1,nz+1
4406 diag_cs%dsamp(dl)%mask3dTi(:,:,k) = diag_cs%dsamp(dl)%mask2dT(:,:)
4407 diag_cs%dsamp(dl)%mask3dBi(:,:,k) = diag_cs%dsamp(dl)%mask2dBu(:,:)
4408 diag_cs%dsamp(dl)%mask3dCui(:,:,k) = diag_cs%dsamp(dl)%mask2dCu(:,:)
4409 diag_cs%dsamp(dl)%mask3dCvi(:,:,k) = diag_cs%dsamp(dl)%mask2dCv(:,:)
4410 enddo
4411 enddo
4412end subroutine downsample_diag_masks_set
4413
4414!> Get the diagnostics-compute indices (to be passed to send_data) based on the shape of
4415!! the diagnostic field (the same way they are deduced for non-downsampled fields)
4416subroutine downsample_diag_indices_get(fo1, fo2, dl, diag_cs, isv, iev, jsv, jev)
4417 integer, intent(in) :: fo1 !< The size of the original diag field in x on data domain including halos
4418 integer, intent(in) :: fo2 !< The size of the original diag field in y on data domain including halos
4419 integer, intent(in) :: dl !< Index of downsample level
4420 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
4421 integer, intent(out) :: isv !< i-start index for diagnostics
4422 integer, intent(out) :: iev !< i-end index for diagnostics
4423 integer, intent(out) :: jsv !< j-start index for diagnostics
4424 integer, intent(out) :: jev !< j-end index for diagnostics
4425 ! Local variables
4426 integer :: dszi, cszi, dszj, cszj, f1, f2, dlfac
4427 character(len=500) :: mesg
4428 logical, save :: first_check = .true.
4429
4430 ! The current implementation of the downsampled diagnostics assumes that the tracer-point
4431 ! computational domain on each processor can be evenly divided by dL in each direction, which
4432 ! avoids the need for halo updates or checks that the halo regions are up-to-date. The following
4433 ! check that this assumption is true is only relevant if there are in fact downsampled diagnostics,
4434 ! which is why it occurs during the first call to this routine instead of during initialization.
4435 dlfac = diag_cs%diag_dsamp_levels(dl) !Actual downsampling factor for this level
4436 if (first_check) then
4437 if (mod(diag_cs%ie-diag_cs%is+1, dlfac) /= 0 .OR. &
4438 mod(diag_cs%je-diag_cs%js+1, dlfac) /= 0) then
4439 write (mesg,*) "Non-commensurate downsampled domain is not supported. "//&
4440 "Please choose a layout such that NIGLOBAL/Layout_X and NJGLOBAL/Layout_Y are both divisible by dL=", &
4441 dlfac,&
4442 " Current domain extents: ", diag_cs%is,diag_cs%ie, diag_cs%js,diag_cs%je
4443 call mom_error(fatal,"downsample_diag_indices_get: "//trim(mesg))
4444 endif
4445 first_check = .false.
4446 endif
4447
4448 cszi = diag_cs%dsamp(dl)%iec-diag_cs%dsamp(dl)%isc +1 ; dszi = diag_cs%dsamp(dl)%ied-diag_cs%dsamp(dl)%isd +1
4449 cszj = diag_cs%dsamp(dl)%jec-diag_cs%dsamp(dl)%jsc +1 ; dszj = diag_cs%dsamp(dl)%jed-diag_cs%dsamp(dl)%jsd +1
4450 !isv = diag_cs%dsamp(dl)%isc ; iev = diag_cs%dsamp(dl)%iec
4451 !jsv = diag_cs%dsamp(dl)%jsc ; jev = diag_cs%dsamp(dl)%jec
4452
4453 f1 = fo1/dlfac
4454 f2 = fo2/dlfac
4455 ! Correction for the symmetric case
4456 if (diag_cs%G%symmetric) then
4457 f1 = f1 + mod(fo1,dlfac)
4458 f2 = f2 + mod(fo2,dlfac)
4459 endif
4460
4461 ! Find the range of indices in the downsampled computational domain.
4462 if ( f1 == dszi ) then
4463 isv = diag_cs%dsamp(dl)%isc ; iev = diag_cs%dsamp(dl)%iec ! Field on Data domain, take compute domain indices
4464 elseif ( f1 == dszi + 1 ) then
4465 isv = diag_cs%dsamp(dl)%isc ; iev = diag_cs%dsamp(dl)%iec+1 ! Symmetric data domain
4466 elseif ( f1 == cszi) then
4467 isv = 1 ; iev = (diag_cs%dsamp(dl)%iec-diag_cs%dsamp(dl)%isc) +1 ! Computational domain
4468 elseif ( f1 == cszi + 1 ) then
4469 isv = 1 ; iev = (diag_cs%dsamp(dl)%iec-diag_cs%dsamp(dl)%isc) +2 ! Symmetric computational domain
4470 else
4471 write (mesg,*) " dl =",dl,",dL =",dlfac,",fo1 =",fo1," f1 =",f1," peculiar size for diag field in i-direction\n"//&
4472 "does not match one of ", cszi, cszi+1, dszi, dszi+1
4473 call mom_error(fatal,"downsample_diag_indices_get: "//trim(mesg))
4474 endif
4475 if ( f2 == dszj ) then
4476 jsv = diag_cs%dsamp(dl)%jsc ; jev = diag_cs%dsamp(dl)%jec ! Data domain
4477 elseif ( f2 == dszj + 1 ) then
4478 jsv = diag_cs%dsamp(dl)%jsc ; jev = diag_cs%dsamp(dl)%jec+1 ! Symmetric data domain
4479 elseif ( f2 == cszj) then
4480 jsv = 1 ; jev = (diag_cs%dsamp(dl)%jec-diag_cs%dsamp(dl)%jsc) +1 ! Computational domain
4481 elseif ( f2 == cszj + 1 ) then
4482 jsv = 1 ; jev = (diag_cs%dsamp(dl)%jec-diag_cs%dsamp(dl)%jsc) +2 ! Symmetric computational domain
4483 else
4484 write (mesg,*) " dl =",dl,",dL =",dlfac,",fo2 =",fo2," f2 =",f2," peculiar size for diag field in j-direction\n"//&
4485 "does not match one of ", cszj, cszj+1, dszj, dszj+1
4486 call mom_error(fatal,"downsample_diag_indices_get: "//trim(mesg))
4487 endif
4488end subroutine downsample_diag_indices_get
4489
4490!> This subroutine allocates and computes a downsampled array from an input array.
4491!! It also determines the diagnostic computational grid indices for the downsampled array.
4492!! 3d interface
4493subroutine downsample_diag_field_3d(locfield, locfield_dsamp, dl, diag_cs, diag, isv, iev, jsv, jev, mask)
4494 real, dimension(:,:,:), pointer :: locfield !< Input array pointer in arbitrary units [A ~> a]
4495 real, dimension(:,:,:), allocatable, intent(inout) :: locfield_dsamp !< Output (downsampled) array [A ~> a]
4496 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
4497 type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
4498 integer, intent(in) :: dl !< Index of Level of down sampling
4499 integer, intent(inout) :: isv !< i-start index for diagnostics
4500 integer, intent(inout) :: iev !< i-end index for diagnostics
4501 integer, intent(inout) :: jsv !< j-start index for diagnostics
4502 integer, intent(inout) :: jev !< j-end index for diagnostics
4503 real, optional,target, intent(in) :: mask(:,:,:) !< If present, use this real array as the data mask [nondim]
4504 ! Local variables
4505 real, dimension(:,:,:), pointer :: locmask ! A pointer to the mask [nondim]
4506 integer :: f1, f2, isv_o, jsv_o
4507
4508 locmask => null()
4509 ! Get the correct indices corresponding to input field based on its shape.
4510 f1 = size(locfield, 1)
4511 f2 = size(locfield, 2)
4512 ! Save the extents of the original (fine) domain
4513 isv_o = isv ; jsv_o = jsv
4514 ! Get the shape of the downsampled field and overwrite isv, iev, jsv and jev with them
4515 call downsample_diag_indices_get(f1, f2, dl, diag_cs, isv, iev, jsv, jev)
4516 ! Set the pointer to the non-downsampled mask, which must be associated and initialized
4517 if (present(mask)) then
4518 locmask => mask
4519 elseif (associated(diag%axes%mask3d)) then
4520 locmask => diag%axes%mask3d
4521 else
4522 call mom_error(fatal, "downsample_diag_field_3d: Cannot downsample without a mask!!! ")
4523 endif
4524 call downsample_field(locfield, locfield_dsamp, diag_cs%diag_dsamp_levels(dl), diag%xyz_method, &
4525 locmask, diag_cs, diag, isv_o, jsv_o, isv, iev, jsv, jev)
4526
4527end subroutine downsample_diag_field_3d
4528
4529!> This subroutine allocates and computes a downsampled array from an input array.
4530!! It also determines the diagnostic computational grid indices for the downsampled array.
4531!! 2d interface
4532subroutine downsample_diag_field_2d(locfield, locfield_dsamp, dl, diag_cs, diag, isv, iev, jsv, jev, mask)
4533 real, dimension(:,:), pointer :: locfield !< Input array pointer in arbitrary units [A ~> a]
4534 real, dimension(:,:), allocatable, intent(inout) :: locfield_dsamp !< Output (downsampled) array [A ~> a]
4535 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
4536 type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
4537 integer, intent(in) :: dl !< Index of Level of down sampling
4538 integer, intent(inout) :: isv !< i-start index for diagnostics
4539 integer, intent(inout) :: iev !< i-end index for diagnostics
4540 integer, intent(inout) :: jsv !< j-start index for diagnostics
4541 integer, intent(inout) :: jev !< j-end index for diagnostics
4542 real, optional,target, intent(in) :: mask(:,:) !< If present, use this real array as the data mask [nondim].
4543 ! Local variables
4544 real, dimension(:,:), pointer :: locmask ! A pointer to the mask [nondim]
4545 integer :: f1, f2, isv_o, jsv_o
4546
4547 locmask => null()
4548 ! Get the correct indices corresponding to input field based on its shape.
4549 f1 = size(locfield,1)
4550 f2 = size(locfield,2)
4551 ! Save the extents of the original (fine) domain
4552 isv_o = isv ; jsv_o = jsv
4553 ! Get the shape of the downsampled field and overwrite isv, iev, jsv and jev with them
4554 call downsample_diag_indices_get(f1, f2, dl, diag_cs, isv, iev, jsv, jev)
4555 ! Set the non-downsampled mask, it must be associated and initialized
4556 if (present(mask)) then
4557 locmask => mask
4558 elseif (associated(diag%axes%mask2d)) then
4559 locmask => diag%axes%mask2d
4560 else
4561 call mom_error(fatal, "downsample_diag_field_2d: Cannot downsample without a mask!!! ")
4562 endif
4563
4564 call downsample_field(locfield, locfield_dsamp, diag_cs%diag_dsamp_levels(dl), diag%xyz_method, &
4565 locmask, diag_cs, diag, isv_o, jsv_o, isv, iev, jsv, jev)
4566
4567end subroutine downsample_diag_field_2d
4568
4569!> \section downsampling The down sample algorithm
4570!!
4571!! The down sample method could be deduced (before send_data call)
4572!! from the diag%x_cell_method, diag%y_cell_method and diag%v_cell_method
4573!!
4574!! This is the summary of the down sample algorithm for a diagnostic field f:
4575!! \f[
4576!! f(Id,Jd) = \sum_{i,j} f(Id+i,Jd+j) * weight(Id+i,Jd+j) / [ \sum_{i,j} weight(Id+i,Jd+j)]
4577!! \f]
4578!! Here, i and j run from 0 to dl-1 (dl being the down sample level).
4579!! Id,Jd are the down sampled (coarse grid) indices run over the coarsened compute grid,
4580!! if and jf are the original (fine grid) indices.
4581!!
4582!! \verbatim
4583!! Example x_cell y_cell v_cell algorithm_id implemented weight(if,jf)
4584!! ---------------------------------------------------------------------------------------
4585!! theta mean mean mean MMM =222 G%areaT(if,jf)*h(if,jf)
4586!! u point mean mean PMM =022 dyCu(if,jf)*h(if,jf)*delta(if,Id)
4587!! v mean point mean MPM =202 dxCv(if,jf)*h(if,jf)*delta(jf,Jd)
4588!! ? point sum mean PSM =012 h(if,jf)*delta(if,Id)
4589!! volcello sum sum sum SSS =111 1
4590!! T_dfxy_co sum sum point SSP =110 1
4591!! umo point sum sum PSS =011 1*delta(if,Id)
4592!! vmo sum point sum SPS =101 1*delta(jf,Jd)
4593!! umo_2d point sum point PSP =010 1*delta(if,Id)
4594!! vmo_2d sum point point SPP =100 1*delta(jf,Jd)
4595!! ? point mean point PMP =020 dyCu(if,jf)*delta(if,Id)
4596!! ? mean point point MPP =200 dxCv(if,jf)*delta(jf,Jd)
4597!! w mean mean point MMP =220 G%areaT(if,jf)
4598!! h*theta mean mean sum MMS =221 G%areaT(if,jf)
4599!!
4600!! delta is the Kronecker delta
4601!! \endverbatim
4602
4603!> This subroutine allocates and computes a down sampled 3d array given an input array
4604!! The down sample method is based on the "cell_methods" for the diagnostics as explained
4605!! in the above table
4606subroutine downsample_field_3d(field_in, field_out, dL, method, mask, diag_cs, diag, &
4607 isv_o, jsv_o, isv_d, iev_d, jsv_d, jev_d)
4608 real, dimension(:,:,:), pointer :: field_in !< Original field to be downsampled in arbitrary units [A ~> a]
4609 real, dimension(:,:,:), allocatable :: field_out !< Downsampled field in the same arbitrary units [A ~> a]
4610 integer, intent(in) :: dL !< Level of down sampling
4611 integer, intent(in) :: method !< Sampling method
4612 real, dimension(:,:,:), pointer :: mask !< Mask for input field [nondim]
4613 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
4614 type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
4615 integer, intent(in) :: isv_o !< Original i-start index
4616 integer, intent(in) :: jsv_o !< Original j-start index
4617 integer, intent(in) :: isv_d !< i-start index of down sampled data
4618 integer, intent(in) :: iev_d !< i-end index of down sampled data
4619 integer, intent(in) :: jsv_d !< j-start index of down sampled data
4620 integer, intent(in) :: jev_d !< j-end index of down sampled data
4621
4622 ! Local variables
4623 character(len=240) :: mesg
4624 integer :: i, j, k, i_dn, j_dn, ks, ke, i0, j0, f1, f2, f_in1, f_in2
4625 integer :: ii, jj ! The index locations on the full grid that contribute to the averages.
4626 integer :: i0_off, j0_off ! The starting point offsets between full array and reduced array
4627 ! indices when i or j is 0.
4628 real :: wt(dL,dL) ! The nondimensional, area-, volume- or mass-based weight for an input
4629 ! value [nondim], [L2 ~> m2], [H L ~> m2 or kg m-1] or [H L2 ~> m3 or kg]
4630 real :: wtd_field(dL,dL) ! The weighted field to sum, in [A ~> a], [A L2 ~> a m2],
4631 ! [A H L ~> a m2 or a kg m-1] or [A H L2 ~> a m3 or a kg]
4632 real :: wt_1d(dL) ! The nondimensional, area-, volume- or mass-based weight for an input
4633 ! value [nondim], [L2 ~> m2], [H L ~> m2 or kg m-1] or [H L2 ~> m3 or kg]
4634 real :: wtd_field_1d(dL) ! The weighted field to sum, in [A ~> a], [A L2 ~> a m2],
4635 ! [A H L ~> a m2 or a kg m-1] or [A H L2 ~> a m3 or a kg]
4636 real :: ave ! The running sum of the average, in [A ~> a], [A L2 ~> a m2],
4637 ! [A H L ~> a m2 or a kg m-1] or [A H L2 ~> a m3 or a kg]
4638 real :: weight ! The nondimensional, area-, volume- or mass-based weight for an input
4639 ! value [nondim], [L2 ~> m2], [H L ~> m2 or kg m-1] or [H L2 ~> m3 or kg]
4640 real :: total_weight ! The sum of weights contributing to a point [nondim], [L2 ~> m2],
4641 ! [H L ~> m2 or kg m-1] or [H L2 ~> m3 or kg]
4642 real :: h_face ! The thickness at a velocity face [H ~> m or kg m-2]
4643 real :: eps_vol ! A negligibly small volume or mass [H L2 ~> m3 or kg]
4644 real :: eps_area ! A negligibly small area [L2 ~> m2]
4645 real :: eps_face ! A negligibly small face area [H L ~> m2 or kg m-1]
4646 logical :: naive ! If true, use naive rotatially variant sums to reproduct previous answers.
4647
4648 ks = 1 ; ke = size(field_in,3)
4649
4650 ! It would be better to use a max with eps_vol instead of adding it into the denominator.
4651 eps_face = 1.0e-20 * diag_cs%G%US%m_to_L * diag_cs%GV%m_to_H
4652 eps_area = 1.0e-20 * diag_cs%G%US%m_to_L**2
4653 eps_vol = 1.0e-20 * diag_cs%G%US%m_to_L**2 * diag_cs%GV%m_to_H
4654
4655 naive = .not.diag_cs%symmetric_downsample_sums
4656
4657 ! Allocate the down sampled field on the down sampled data domain
4658! 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))
4659! allocate(field_out(1:size(field_in,1)/dl,1:size(field_in,2)/dl,ks:ke))
4660 f_in1 = size(field_in, 1)
4661 f_in2 = size(field_in, 2)
4662 f1 = f_in1 / dl
4663 f2 = f_in2 / dl
4664 ! Correction for the symmetric case
4665 if (diag_cs%G%symmetric) then
4666 f1 = f1 + mod(f_in1, dl)
4667 f2 = f2 + mod(f_in2, dl)
4668 endif
4669 allocate(field_out(1:f1,1:f2,ks:ke), source=0.0)
4670
4671 ! These are the starting point offsets between full array and reduced array indices when i or j is 0.
4672 i0_off = (isv_o-1) - dl*isv_d
4673 j0_off = (jsv_o-1) - dl*jsv_d
4674
4675 ! Fill the down sampled field on the down sampled diagnostics (almost always compute) domain
4676 if (method == mmm) then
4677 do k=ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
4678 do j_dn=1,dl ; do i_dn=1,dl
4679 ! ii and jj are the index locations on the full grid that contribute to the averages.
4680 jj = j_dn + (dl*j + j0_off) ; ii = i_dn + (dl*i + i0_off)
4681 wt(i_dn,j_dn) = mask(ii,jj,k) * diag_cs%G%areaT(ii,jj) * diag_cs%h(ii,jj,k)
4682 wtd_field(i_dn,j_dn) = field_in(ii,jj,k) * wt(i_dn,j_dn)
4683 enddo ; enddo
4684 field_out(i,j,k) = square_sum(wtd_field(1:dl,1:dl), dl, naive) / &
4685 (square_sum(wt(1:dl,1:dl), dl, naive) + eps_vol) ! Eps_vol avoids division by 0.
4686 enddo ; enddo ; enddo
4687 elseif (method == sss) then ! e.g., volcello
4688 do k=ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
4689 do j_dn=1,dl ; do i_dn=1,dl
4690 jj = j_dn + (dl*j + j0_off) ; ii = i_dn + (dl*i + i0_off)
4691 wtd_field(i_dn,j_dn) = field_in(ii,jj,k) * mask(ii,jj,k)
4692 enddo ; enddo
4693 field_out(i,j,k) = square_sum(wtd_field(1:dl,1:dl), dl, naive) ! This is a masked sum.
4694 enddo ; enddo ; enddo
4695 elseif (method == mmp .or. method == mms) then ! e.g., T_advection_xy
4696 do k=ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
4697 do j_dn=1,dl ; do i_dn=1,dl
4698 ! ii and jj are the index locations on the full grid that contribute to the averages.
4699 jj = j_dn + (dl*j + j0_off) ; ii = i_dn + (dl*i + i0_off)
4700 wt(i_dn,j_dn) = mask(ii,jj,k) * diag_cs%G%areaT(ii,jj)
4701 wtd_field(i_dn,j_dn) = field_in(ii,jj,k) * wt(i_dn,j_dn)
4702 enddo ; enddo
4703 field_out(i,j,k) = square_sum(wtd_field(1:dl,1:dl), dl, naive) / &
4704 (square_sum(wt(1:dl,1:dl), dl, naive) + eps_area) ! Eps_area avoids division by 0.
4705 enddo ; enddo ; enddo
4706 elseif (method == pmm) then
4707 do k=ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
4708 ii = dl*i + i0_off + (dl-1)
4709 do j_dn=1,dl
4710 jj = j_dn + (dl*j + j0_off)
4711 if (diag_cs%OBC_u(ii,jj) == 0) then ! This is not an OBC face.
4712 h_face = 0.5*(diag_cs%h(ii,jj,k) + diag_cs%h(ii+1,jj,k))
4713 elseif (diag_cs%OBC_u(ii,jj) < 0) then ! This is a western OBC face.
4714 h_face = diag_cs%h(ii+1,jj,k)
4715 else ! (diag_cs%OBC_u(II,jj) > 0) ! This is an eastern OBC face.
4716 h_face = diag_cs%h(ii,jj,k)
4717 endif
4718 wt_1d(j_dn) = mask(ii,jj,k) * diag_cs%G%dyCu(ii,jj) * h_face
4719 wtd_field_1d(j_dn) = field_in(ii,jj,k) * wt_1d(j_dn)
4720 enddo
4721 field_out(i,j,k) = sum_1d(wtd_field_1d(1:dl), dl) / &
4722 (sum_1d(wt_1d(1:dl), dl) + eps_face) ! Eps_face avoids division by 0.
4723 enddo ; enddo ; enddo
4724 elseif (method == pss) then ! e.g. umo
4725 do k=ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
4726 ii = dl*i + i0_off + (dl-1)
4727 do j_dn=1,dl
4728 jj = j_dn + (dl*j + j0_off)
4729 wtd_field_1d(j_dn) = field_in(ii,jj,k) * mask(ii,jj,k)
4730 enddo
4731 field_out(i,j,k) = sum_1d(wtd_field_1d(1:dl), dl) ! This is a masked sum.
4732 enddo ; enddo ; enddo
4733 elseif (method == sps) then ! e.g. vmo
4734 do k=ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
4735 jj = dl*j + j0_off + (dl-1)
4736 do i_dn=1,dl
4737 ii = i_dn + (dl*i + i0_off)
4738 wtd_field_1d(i_dn) = field_in(ii,jj,k) * mask(ii,jj,k)
4739 enddo
4740 field_out(i,j,k) = sum_1d(wtd_field_1d(1:dl), dl) ! This is a masked sum.
4741 enddo ; enddo ; enddo
4742 elseif (method == mpm) then
4743 do k=ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
4744 jj = dl*j + j0_off + (dl-1)
4745 do i_dn=1,dl
4746 ii = i_dn + (dl*i + i0_off)
4747 if (diag_cs%OBC_v(ii,jj) == 0) then ! This is not an OBC face.
4748 h_face = 0.5*(diag_cs%h(ii,jj,k) + diag_cs%h(ii,jj+1,k))
4749 elseif (diag_cs%OBC_v(ii,jj) < 0) then ! This is a southern OBC face.
4750 h_face = diag_cs%h(ii,jj+1,k)
4751 else ! (diag_cs%OBC_v(ii,JJ) > 0) ! This is a northern OBC face.
4752 h_face = diag_cs%h(ii,jj,k)
4753 endif
4754 wt_1d(i_dn) = mask(ii,jj,k) * diag_cs%G%dxCv(ii,jj) * h_face
4755 wtd_field_1d(i_dn) = field_in(ii,jj,k) * wt_1d(i_dn)
4756 enddo
4757 field_out(i,j,k) = sum_1d(wtd_field_1d(1:dl), dl) / &
4758 (sum_1d(wt_1d(1:dl), dl) + eps_face) ! Eps_face avoids division by 0.
4759 enddo ; enddo ; enddo
4760 else
4761 write (mesg,*) " unknown sampling method: ",method
4762 call mom_error(fatal, "downsample_field_3d: "//trim(mesg)//" "//trim(diag%debug_str))
4763 endif
4764
4765end subroutine downsample_field_3d
4766
4767!> This subroutine allocates and computes a down sampled 2d array given an input array
4768!! The down sample method is based on the "cell_methods" for the diagnostics as explained
4769!! in the above table
4770subroutine downsample_field_2d(field_in, field_out, dl, method, mask, diag_cs, diag, &
4771 isv_o, jsv_o, isv_d, iev_d, jsv_d, jev_d)
4772 real, dimension(:,:), pointer :: field_in !< Original field to be downsampled in arbitrary units [A ~> a]
4773 real, dimension(:,:), allocatable :: field_out !< Downsampled field in the same arbitrary units [A ~> a]
4774 integer, intent(in) :: dl !< Level of down sampling
4775 integer, intent(in) :: method !< Sampling method
4776 real, dimension(:,:), pointer :: mask !< Mask for input field [nondim]
4777 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
4778 type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
4779 integer, intent(in) :: isv_o !< Original i-start index
4780 integer, intent(in) :: jsv_o !< Original j-start index
4781 integer, intent(in) :: isv_d !< i-start index of down sampled data
4782 integer, intent(in) :: iev_d !< i-end index of down sampled data
4783 integer, intent(in) :: jsv_d !< j-start index of down sampled data
4784 integer, intent(in) :: jev_d !< j-end index of down sampled data
4785
4786 ! Local variables
4787 character(len=240) :: mesg
4788 integer :: i, j, i_dn, j_dn, i0, j0, f1, f2, f_in1, f_in2
4789 integer :: ii, jj ! The index locations on the full grid that contribute to the averages.
4790 integer :: i0_off, j0_off ! The starting point offsets between full array and reduced array
4791 ! indices when i or j is 0.
4792 real :: wt(dL,dL) ! The nondimensional, area-, volume- or mass-based weight for an input
4793 ! value [nondim], [L2 ~> m2], [H L ~> m2 or kg m-1] or [H L2 ~> m3 or kg]
4794 real :: wtd_field(dL,dL) ! The weighted field to sum, in [A ~> a], [A L2 ~> a m2],
4795 ! [A H L ~> a m2 or a kg m-1] or [A H L2 ~> a m3 or a kg]
4796 real :: wt_1d(dL) ! The nondimensional, area-, volume- or mass-based weight for an input
4797 ! value [nondim], [L2 ~> m2], [H L ~> m2 or kg m-1] or [H L2 ~> m3 or kg]
4798 real :: wtd_field_1d(dL) ! The weighted field to sum, in [A ~> a], [A L2 ~> a m2],
4799 ! [A H L ~> a m2 or a kg m-1] or [A H L2 ~> a m3 or a kg]
4800 real :: ave ! The running sum of the average, in [A ~> a] or [A L2 ~> a m2]
4801 real :: weight ! The nondimensional or area-weighted weight for an input value [nondim] or [L2 ~> m2]
4802 real :: total_weight ! The sum of weights contributing to a point [nondim] or [L2 ~> m2]
4803 real :: eps_area ! A negligibly small area [L2 ~> m2]
4804 real :: eps_len ! A negligibly small horizontal length [L ~> m]
4805 logical :: naive ! If true, use naive rotatially variant sums to reproduct previous answers.
4806
4807 eps_len = 1.0e-20 * diag_cs%G%US%m_to_L
4808 eps_area = 1.0e-20 * diag_cs%G%US%m_to_L**2
4809
4810 naive = .not.diag_cs%symmetric_downsample_sums
4811
4812 ! Allocate the down sampled field on the down sampled data domain
4813! allocate(field_out(diag_cs%dsamp(dl)%isd:diag_cs%dsamp(dl)%ied,diag_cs%dsamp(dl)%jsd:diag_cs%dsamp(dl)%jed))
4814! allocate(field_out(1:size(field_in,1)/dl,1:size(field_in,2)/dl))
4815 ! Fill the down sampled field on the down sampled diagnostics (almost always compute) domain
4816 f_in1 = size(field_in,1)
4817 f_in2 = size(field_in,2)
4818 f1 = f_in1 / dl
4819 f2 = f_in2 / dl
4820 ! Correction for the symmetric case
4821 if (diag_cs%G%symmetric) then
4822 f1 = f1 + mod(f_in1,dl)
4823 f2 = f2 + mod(f_in2,dl)
4824 endif
4825 allocate(field_out(1:f1,1:f2))
4826
4827 ! These are the starting point offsets between full array and reduced array indices when i or j is 0.
4828 i0_off = (isv_o-1) - dl*isv_d
4829 j0_off = (jsv_o-1) - dl*jsv_d
4830
4831 if (method == mmp) then
4832 do j=jsv_d,jev_d ; do i=isv_d,iev_d
4833 do j_dn=1,dl ; do i_dn=1,dl
4834 ! ii and jj are the index locations on the full grid that contribute to the averages.
4835 jj = j_dn + (dl*j + j0_off) ; ii = i_dn + (dl*i + i0_off)
4836 wt(i_dn,j_dn) = mask(ii,jj) * diag_cs%G%areaT(ii,jj)
4837 wtd_field(i_dn,j_dn) = field_in(ii,jj) * wt(i_dn,j_dn)
4838 enddo ; enddo
4839 field_out(i,j) = square_sum(wtd_field(1:dl,1:dl), dl, naive) / &
4840 (square_sum(wt(1:dl,1:dl), dl, naive) + eps_area) ! Eps_area avoids division by 0.
4841 enddo ; enddo
4842 elseif (method == ssp) then ! e.g., T_dfxy_cont_tendency_2d
4843 do j=jsv_d,jev_d ; do i=isv_d,iev_d
4844 do j_dn=1,dl ; do i_dn=1,dl
4845 jj = j_dn + (dl*j + j0_off) ; ii = i_dn + (dl*i + i0_off)
4846 wtd_field(i_dn,j_dn) = field_in(ii,jj) * mask(ii,jj)
4847 enddo ; enddo
4848 field_out(i,j) = square_sum(wtd_field(1:dl,1:dl), dl, naive) ! This is a masked sum.
4849 enddo ; enddo
4850 elseif (method == psp) then ! e.g., umo_2d
4851 do j=jsv_d,jev_d ; do i=isv_d,iev_d
4852 ii = dl*i + i0_off + (dl-1)
4853 do j_dn=1,dl
4854 jj = j_dn + (dl*j + j0_off)
4855 wtd_field_1d(j_dn) = field_in(ii,jj) * mask(ii,jj)
4856 enddo
4857 field_out(i,j) = sum_1d(wtd_field_1d(1:dl), dl) ! This is a masked sum.
4858 enddo ; enddo
4859 elseif (method == spp) then ! e.g., vmo_2d
4860 do j=jsv_d,jev_d ; do i=isv_d,iev_d
4861 jj = dl*j + j0_off + (dl-1)
4862 do i_dn=1,dl
4863 ii = i_dn + (dl*i + i0_off)
4864 wtd_field_1d(i_dn) = field_in(ii,jj) * mask(ii,jj)
4865 enddo
4866 field_out(i,j) = sum_1d(wtd_field_1d(1:dl), dl) ! This is a masked sum.
4867 enddo ; enddo
4868 elseif (method == pmp) then
4869 do j=jsv_d,jev_d ; do i=isv_d,iev_d
4870 ii = dl*i + i0_off + (dl-1)
4871 do j_dn=1,dl
4872 jj = j_dn + (dl*j + j0_off)
4873 ! Should this weight include the total thickness interpolated to velocity points?
4874 wt_1d(j_dn) = mask(ii,jj) * diag_cs%G%dyCu(ii,jj)
4875 wtd_field_1d(j_dn) = field_in(ii,jj) * wt_1d(j_dn)
4876 enddo
4877 field_out(i,j) = sum_1d(wtd_field_1d(1:dl), dl) / &
4878 (sum_1d(wt_1d(1:dl), dl) + eps_len) ! Eps_len avoids division by 0.
4879 enddo ; enddo
4880 elseif (method == mpp) then
4881 do j=jsv_d,jev_d ; do i=isv_d,iev_d
4882 jj = dl*j + j0_off + (dl-1)
4883 do i_dn=1,dl
4884 ii = i_dn + (dl*i + i0_off)
4885 ! Should this weight include the total thickness interpolated to velocity points?
4886 wt_1d(i_dn) = mask(ii,jj) * diag_cs%G%dxCv(ii,jj)
4887 wtd_field_1d(i_dn) = field_in(ii,jj) * wt_1d(i_dn)
4888 enddo
4889 field_out(i,j) = sum_1d(wtd_field_1d(1:dl), dl) / &
4890 (sum_1d(wt_1d(1:dl), dl) + eps_len) ! Eps_len avoids division by 0.
4891 enddo ; enddo
4892 else
4893 write (mesg,*) " unknown sampling method: ",method
4894 call mom_error(fatal, "downsample_field_2d: "//trim(mesg)//" "//trim(diag%debug_str))
4895 endif
4896
4897end subroutine downsample_field_2d
4898
4899!> Do a rotationally symmetric sum of the elements of a 1-d array.
4900function sum_1d(field, sz) result(sum)
4901 integer, intent(in) :: sz !< The size of the array to sum
4902 real, intent(in) :: field(sz) !< The field to sum in arbitrary units [A ~> a]
4903 real :: sum !< The rotationally symmetric sum of the entries in field [A ~> a]
4904
4905 ! Local variables
4906 integer :: i, sz_2
4907
4908 if (sz == 2) then ! The order of arithmetic does not matter.
4909 sum = field(1) + field(2)
4910 elseif (sz == 3) then ! Use simpler code that has the same order of sums as the general case.
4911 sum = field(2) + (field(1) + field(3))
4912 else
4913 ! This is a copy of the general code from symmetric_sum_1d in MOM_array_transform
4914 sz_2 = sz / 2 ! Note that for an odd number sz_2 is rounded down.
4915 sum = 0.0
4916 if (2*sz_2 < sz) sum = field(sz_2+1)
4917 ! Add pairs of values, working from the inside out.
4918 do i=sz_2,1,-1
4919 sum = sum + (field(i) + field(sz+1-i))
4920 enddo
4921 endif
4922end function sum_1d
4923
4924!> Return the sum of the elements of a square 2-d array, perhaps using a rotationally symmetric sum.
4925!! This could eventually wrap symmetric_sum, but for now it also can reproduce the previous answers.
4926function square_sum(field, sz, naive_sum) result(sum)
4927 integer, intent(in) :: sz !< The size of the array along each axis
4928 real, intent(in) :: field(sz, sz) !< The field to sum in arbitrary units [A ~> a]
4929 logical, optional, intent(in) :: naive_sum !< If true, sum the elements in the order they appear in memory.
4930 real :: sum !< The sum of the entries in field [A ~> a]
4931
4932 ! Local variables
4933 integer :: i, j
4934 logical :: simple_sum
4935
4936 simple_sum = .true. ; if (present(naive_sum)) simple_sum = naive_sum
4937
4938 if (sz == 1) then
4939 sum = field(1,1)
4940 elseif (simple_sum) then
4941 ! This non-rotationally symmetric sum is here to reproduce previous results.
4942 sum = 0.0
4943 do j=1,sz ; do i=1,sz ; sum = sum + field(i,j) ; enddo ; enddo
4944 elseif (sz == 2) then
4945 ! This copy of code from symmetric_sum may facilitate inlining in a common case.
4946 sum = (field(1,1) + field(2,2)) + (field(2,1) + field(1,2))
4947 elseif (sz == 3) then
4948 ! This copy of code from symmetric_sum may facilitate inlining in a common case.
4949 sum = (field(2,2) + ((field(1,2) + field(3,2)) + (field(2,1) + field(2,3)))) + &
4950 ((field(1,1) + field(3,3)) + (field(3,1) + field(1,3)))
4951 else
4952 sum = symmetric_sum(field(1:sz,1:sz))
4953 endif
4954
4955end function square_sum
4956
4957
4958!> Allocate and compute the 2d down sampled mask.
4959!! The masks are down sampled based on a minority rule, i.e., a coarse cell is open (1)
4960!! if at least one of the sub-cells are open, otherwise it's closed (0), using the same points
4961!! that would appear in the downsampled sum, average or down-selection.
4962subroutine downsample_mask_2d(mask_in, mask_dsamp, dL, method, isc_o, jsc_o, isd_o, jsd_o, &
4963 isc_d, iec_d, jsc_d, jec_d, isd_d, ied_d, jsd_d, jed_d)
4964 integer, intent(in) :: isd_o !< Original data domain i-start index
4965 integer, intent(in) :: jsd_o !< Original data domain j-start index
4966 real, dimension(isd_o:,jsd_o:), intent(in) :: mask_in !< Original mask to be down sampled [nondim]
4967 real, dimension(:,:), pointer :: mask_dsamp !< Down-sampled mask [nondim]
4968 integer, intent(in) :: method !< Sampling method
4969 integer, intent(in) :: dL !< Level of down sampling
4970 integer, intent(in) :: isc_o !< Original i-start index
4971 integer, intent(in) :: jsc_o !< Original j-start index
4972 integer, intent(in) :: isc_d !< Computational i-start index of down sampled data
4973 integer, intent(in) :: iec_d !< Computational i-end index of down sampled data
4974 integer, intent(in) :: jsc_d !< Computational j-start index of down sampled data
4975 integer, intent(in) :: jec_d !< Computational j-end index of down sampled data
4976 integer, intent(in) :: isd_d !< Data domain i-start index of down sampled data
4977 integer, intent(in) :: ied_d !< Data domain i-end index of down sampled data
4978 integer, intent(in) :: jsd_d !< Data domain j-start index of down sampled data
4979 integer, intent(in) :: jed_d !< Data domain j-end index of down sampled data
4980
4981 ! Local variables
4982 real :: tot_non_zero ! The sum of mask values in the down-scaled cell or face [nondim]
4983 character(len=8) :: method_str
4984 integer :: i, j, i_dn, j_dn
4985 integer :: ii, jj ! The index locations on the full grid that contribute to the averages.
4986 integer :: i0_off, j0_off ! The starting point offsets between full array and reduced array
4987 ! indices when i or j is 0.
4988
4989 ! down sampled mask = 0 unless the mask value of one of the down sampling cells is 1
4990 allocate(mask_dsamp(isd_d:ied_d, jsd_d:jed_d), source=0.0)
4991
4992 i0_off = ((isc_o-1) - dl*isc_d)
4993 j0_off = ((jsc_o-1) - dl*jsc_d)
4994 if ((method == mmm) .or. (method == mmp) .or. (method == mms) .or. (method == sss)) then
4995 ! This applies at tracer points.
4996 do j=jsc_d,jec_d ; do i=isc_d,iec_d
4997 tot_non_zero = 0.0
4998 do j_dn=1,dl ; do i_dn=1,dl
4999 ii = i_dn + (dl*i + i0_off)
5000 jj = j_dn + (dl*j + j0_off)
5001 tot_non_zero = tot_non_zero + abs(mask_in(ii,jj))
5002 enddo ; enddo
5003 if (tot_non_zero > 0.0) mask_dsamp(i,j) = 1.0
5004 enddo ; enddo
5005 elseif ((method == pmm) .or. (method == psp) .or. (method == pmp) .or. (method == pss)) then
5006 ! This applies at u-velocity points.
5007 do j=jsc_d,jec_d ; do i=isc_d,iec_d
5008 tot_non_zero = 0.0
5009 ii = (dl*i + i0_off) + (dl-1)
5010 do j_dn=1,dl
5011 jj = j_dn + (dl*j + j0_off)
5012 tot_non_zero = tot_non_zero + abs(mask_in(ii,jj))
5013 enddo
5014 if (tot_non_zero > 0.0) mask_dsamp(i,j) = 1.0
5015 enddo ; enddo
5016 elseif ((method == mpm) .or. (method == spp) .or. (method == mpp) .or. (method == sps)) then
5017 ! This applies at v-velocity points.
5018 do j=jsc_d,jec_d ; do i=isc_d,iec_d
5019 tot_non_zero = 0.0
5020 jj = (dl*j + j0_off) + (dl-1)
5021 do i_dn=1,dl
5022 ii = i_dn + (dl*i + i0_off)
5023 tot_non_zero = tot_non_zero + abs(mask_in(ii,jj))
5024 enddo
5025 if (tot_non_zero > 0.0) mask_dsamp(i,j) = 1.0
5026 enddo ; enddo
5027 elseif ((method == ppp) .or. (method == ppm)) then
5028 ! This applies at corner (vorticity) points.
5029 do j=jsc_d,jec_d ; do i=isc_d,iec_d
5030 ii = (dl*i + i0_off) + (dl-1)
5031 jj = (dl*j + j0_off) + (dl-1)
5032 if (abs(mask_in(ii,jj)) > 0.0) mask_dsamp(i,j) = 1.0
5033 enddo ; enddo
5034 else
5035 write(method_str, '(I0)') method
5036 call mom_error(fatal, "downsample_mask_2d: unknown sampling method "//trim(method_str))
5037 endif
5038
5039end subroutine downsample_mask_2d
5040
5041
5042!> Allocate and compute the 3d down sampled mask.
5043!! The masks are down sampled based on a minority rule, i.e., a coarse cell is open (1)
5044!! if at least one of the sub-cells are open, otherwise it's closed (0), using the same points
5045!! that would appear in the downsampled sum, average or down-selection.
5046subroutine downsample_mask_3d(mask_in, mask_dsamp, dL, method, isc_o, jsc_o, isd_o, jsd_o, &
5047 isc_d, iec_d, jsc_d, jec_d, isd_d, ied_d, jsd_d, jed_d)
5048 integer, intent(in) :: isd_o !< Original data domain i-start index
5049 integer, intent(in) :: jsd_o !< Original data domain j-start index
5050 real, dimension(isd_o:,jsd_o:,:), intent(in) :: mask_in !< Original mask to be down sampled [nondim]
5051 real, dimension(:,:,:), pointer :: mask_dsamp !< Down-sampled mask [nondim]
5052 integer, intent(in) :: dL !< Level of down sampling
5053 integer, intent(in) :: method !< Sampling method
5054 integer, intent(in) :: isc_o !< Original i-start index
5055 integer, intent(in) :: jsc_o !< Original j-start index
5056 integer, intent(in) :: isc_d !< Computational i-start index of down sampled data
5057 integer, intent(in) :: iec_d !< Computational i-end index of down sampled data
5058 integer, intent(in) :: jsc_d !< Computational j-start index of down sampled data
5059 integer, intent(in) :: jec_d !< Computational j-end index of down sampled data
5060 integer, intent(in) :: isd_d !< Computational i-start index of down sampled data
5061 integer, intent(in) :: ied_d !< Computational i-end index of down sampled data
5062 integer, intent(in) :: jsd_d !< Computational j-start index of down sampled data
5063 integer, intent(in) :: jed_d !< Computational j-end index of down sampled data
5064
5065 ! Local variables
5066 real :: tot_non_zero ! The sum of mask values in the down-scaled cell or face [nondim]
5067 character(len=8) :: method_str
5068 integer :: i, j, i_dn, j_dn, k, ks, ke
5069 integer :: ii, jj ! The index locations on the full grid that contribute to the averages.
5070 integer :: i0_off, j0_off ! The starting point offsets between full array and reduced array
5071 ! indices when i or j is 0.
5072
5073 ! down sampled mask = 0 unless the mask value of one of the down sampling cells is 1
5074 ks = lbound(mask_in, 3) ; ke = ubound(mask_in, 3)
5075 allocate(mask_dsamp(isd_d:ied_d, jsd_d:jed_d, ks:ke), source=0.0)
5076
5077 i0_off = ((isc_o-1) - dl*isc_d)
5078 j0_off = ((jsc_o-1) - dl*jsc_d)
5079 if ((method == mmm) .or. (method == mmp) .or. (method == mms) .or. (method == sss)) then
5080 ! This applies at tracer points.
5081 do k=ks,ke ; do j=jsc_d,jec_d ; do i=isc_d,iec_d
5082 tot_non_zero = 0.0
5083 do j_dn=1,dl ; do i_dn=1,dl
5084 ii = i_dn + (dl*i + i0_off)
5085 jj = j_dn + (dl*j + j0_off)
5086 tot_non_zero = tot_non_zero + abs(mask_in(ii,jj,k))
5087 enddo ; enddo
5088 if (tot_non_zero > 0.0) mask_dsamp(i,j,k) = 1.0
5089 enddo ; enddo ; enddo
5090 elseif ((method == pmm) .or. (method == psp) .or. (method == pmp) .or. (method == pss)) then
5091 ! This applies at u-velocity points.
5092 do k=ks,ke ; do j=jsc_d,jec_d ; do i=isc_d,iec_d
5093 tot_non_zero = 0.0
5094 ii = (dl*i + i0_off) + (dl-1)
5095 do j_dn=1,dl
5096 jj = j_dn + (dl*j + j0_off)
5097 tot_non_zero = tot_non_zero + abs(mask_in(ii,jj,k))
5098 enddo
5099 if (tot_non_zero > 0.0) mask_dsamp(i,j,k) = 1.0
5100 enddo ; enddo ; enddo
5101 elseif ((method == mpm) .or. (method == spp) .or. (method == mpp) .or. (method == sps)) then
5102 ! This applies at v-velocity points.
5103 do k=ks,ke ; do j=jsc_d,jec_d ; do i=isc_d,iec_d
5104 tot_non_zero = 0.0
5105 jj = (dl*j + j0_off) + (dl-1)
5106 do i_dn=1,dl
5107 ii = i_dn + (dl*i + i0_off)
5108 tot_non_zero = tot_non_zero + abs(mask_in(ii,jj,k))
5109 enddo
5110 if (tot_non_zero > 0.0) mask_dsamp(i,j,k) = 1.0
5111 enddo ; enddo ; enddo
5112 elseif ((method == ppp) .or. (method == ppm)) then
5113 ! This applies at corner (vorticity) points.
5114 do k=ks,ke ; do j=jsc_d,jec_d ; do i=isc_d,iec_d
5115 ii = (dl*i + i0_off) + (dl-1)
5116 jj = (dl*j + j0_off) + (dl-1)
5117 if (abs(mask_in(ii,jj,k)) > 0.0) mask_dsamp(i,j,k) = 1.0
5118 enddo ; enddo ; enddo
5119 else
5120 write(method_str, '(I0)') method
5121 call mom_error(fatal, "downsample_mask_3d: unknown sampling method "//trim(method_str))
5122 endif
5123
5124end subroutine downsample_mask_3d
5125
5126
5127!> Fakes a register of a diagnostic to find out if an obsolete
5128!! parameter appears in the diag_table.
5129logical function found_in_diagtable(diag, varName)
5130 type(diag_ctrl), intent(in) :: diag !< A structure used to control diagnostics.
5131 character(len=*), intent(in) :: varname !< The obsolete diagnostic name
5132 ! Local
5133 integer :: handle ! Integer handle returned from diag_manager
5134
5135 ! We use register_static_field_fms() instead of register_static_field() so
5136 ! that the diagnostic does not appear in the available diagnostics list.
5137 handle = register_static_field_infra('ocean_model', varname, diag%axesT1%handles)
5138
5139 found_in_diagtable = (handle>0)
5140
5141end function found_in_diagtable
5142
5143!> Finishes the diag manager reduction methods as needed for the time_step
5144subroutine mom_diag_send_complete()
5145 call diag_send_complete_infra()
5146end subroutine mom_diag_send_complete
5147
5148end module mom_diag_mediator