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