← back to index

src/framework/MOM_diag_mediator.F90

portedportable, not yet portedexecuted, not portableexecutable, not hit by this run

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_manager_infra, only : diag_send_complete_infra
20use MOM_diag_remap, only : diag_remap_ctrl, diag_remap_update, diag_remap_calc_hmask
21use MOM_diag_remap, only : diag_remap_init, diag_remap_end, diag_remap_do_remap
22use MOM_diag_remap, only : vertically_reintegrate_diag_field, vertically_interpolate_diag_field
23use MOM_diag_remap, only : horizontally_average_diag_field, diag_remap_get_axes_info
24use MOM_diag_remap, only : diag_remap_configure_axes, diag_remap_axes_configured
25use MOM_diag_remap, only : diag_remap_diag_registration_closed, diag_remap_set_active
26use MOM_EOS, only : EOS_type
27use MOM_error_handler, only : MOM_error, FATAL, WARNING, is_root_pe, assert, callTree_showQuery
28use MOM_error_handler, only : callTree_enter, callTree_leave, callTree_waypoint
29use MOM_file_parser, only : get_param, log_version, param_file_type
30use MOM_grid, only : ocean_grid_type
31use MOM_interface_heights, only : thickness_to_dz
32use MOM_io, only : vardesc, query_vardesc
33use MOM_io, only : get_filename_appendix
34use MOM_safe_alloc, only : safe_alloc_ptr, safe_alloc_alloc
35use MOM_string_functions, only : lowercase, slasher, ints_to_string, trim_trailing_commas
36use MOM_time_manager, only : time_type, get_time
37use MOM_unit_scaling, only : unit_scale_type
38use MOM_variables, only : thermo_var_ptrs
39use MOM_verticalGrid, only : verticalGrid_type
40
41implicit none ; private
42
43#undef __DO_SAFETY_CHECKS__
44#define IMPLIES(A, B) ((.not. (A)) .or. (B))
45#define MAX_DSAMP_LEV 2
46
47public set_axes_info, post_data, register_diag_field, time_type
48public post_data_3d_by_column, post_data_3d_final
49public post_product_u, post_product_sum_u, post_product_v, post_product_sum_v
50public set_masks_for_axes, MOM_diag_send_complete
51! post_data_1d_k is a deprecated interface that can be replaced by a call to post_data, but
52! it is being retained for backward compatibility to older versions of the ocean_BGC code.
53public post_data_1d_k
54public safe_alloc_ptr, safe_alloc_alloc
55public enable_averaging, enable_averages, disable_averaging, query_averaging_enabled
56public diag_mediator_init, diag_mediator_end, set_diag_mediator_grid
57public diag_mediator_infrastructure_init
58public diag_mediator_close_registration, get_diag_time_end
59public diag_axis_init, ocean_register_diag, register_static_field
60public register_scalar_field
61public define_axes_group, diag_masks_set
62public set_piecemeal_extents
63public diag_register_area_ids
64public register_cell_measure, diag_associate_volume_cell_measure
65public diag_get_volume_cell_measure_dm_id
66public diag_set_state_ptrs, diag_update_remap_grids
67public diag_grid_storage_init, diag_grid_storage_end
68public diag_copy_diag_to_storage, diag_copy_storage_to_diag
69public diag_save_grids, diag_restore_grids
70public found_in_diagtable
71
72!> Make a diagnostic available for averaging or output.
73interface post_data
74 module procedure post_data_3d, post_data_2d, post_data_1d_k, post_data_0d
75end interface post_data
76
77!> Registers a non-array scalar diagnostic, returning an integer handle
78interface register_scalar_field
79 module procedure register_scalar_field_CS, register_scalar_field_axes
80end interface register_scalar_field
81
82!> Down sample a field
83interface downsample_field
84 module procedure downsample_field_2d, downsample_field_3d
85end interface downsample_field
86
87!> Down sample the mask of a field
88interface downsample_mask
89 module procedure downsample_mask_2d, downsample_mask_3d
90end interface downsample_mask
91
92!> Down sample a diagnostic field
93interface downsample_diag_field
94 module procedure downsample_diag_field_2d, downsample_diag_field_3d
95end interface downsample_diag_field
96
97!> Contained for down sampled masks
98type, private :: diag_dsamp
99 real, pointer, dimension(:,:) :: mask2d => null() !< Mask for 2d (x-y) axes [nondim]
100 real, pointer, dimension(:,:,:) :: mask3d => null() !< Mask for 3d axes [nondim]
101end type diag_dsamp
102
103!> A group of 1D axes that comprise a 1D/2D/3D mesh
104type, public :: axes_grp
105 character(len=15) :: id !< The id string for this particular combination of handles.
106 integer :: rank !< Number of dimensions in the list of axes.
107 integer, dimension(:), allocatable :: handles !< Handles to 1D axes.
108 type(diag_ctrl), pointer :: diag_cs => null() !< Circular link back to the main diagnostics control structure
109 !! (Used to avoid passing said structure into every possible call).
110 ! ID's for cell_methods
111 character(len=9) :: x_cell_method = '' !< Default nature of data representation, if axes group
112 !! includes x-direction.
113 character(len=9) :: y_cell_method = '' !< Default nature of data representation, if axes group
114 !! includes y-direction.
115 character(len=9) :: v_cell_method = '' !< Default nature of data representation, if axes group
116 !! includes vertical direction.
117 ! For remapping
118 integer :: nz = 0 !< Vertical dimension of diagnostic
119 integer :: vertical_coordinate_number = 0 !< Index of the corresponding diag_remap_ctrl for this axis group
120 ! For detecting position on the grid
121 logical :: is_h_point = .false. !< If true, indicates that this axes group is for an h-point located field.
122 logical :: is_q_point = .false. !< If true, indicates that this axes group is for a q-point located field.
123 logical :: is_u_point = .false. !< If true, indicates that this axes group is for a u-point located field.
124 logical :: is_v_point = .false. !< If true, indicates that this axes group is for a v-point located field.
125 logical :: is_layer = .false. !< If true, indicates that this axes group is for a layer vertically-located field.
126 logical :: is_interface = .false. !< If true, indicates that this axes group is for an interface
127 !! vertically-located field.
128 logical :: is_native = .true. !< If true, indicates that this axes group is for a native model grid.
129 !! False for any other grid. Used for rank>2.
130 logical :: needs_remapping = .false. !< If true, indicates that this axes group is for a intensive layer-located
131 !! field that must be remapped to these axes. Used for rank>2.
132 logical :: needs_interpolating = .false. !< If true, indicates that this axes group is for a sampled
133 !! interface-located field that must be interpolated to
134 !! these axes. Used for rank>2.
135 integer :: downsample_level = 1 !< If greater than 1, the factor by which this diagnostic/axes/masks be downsampled
136 ! For horizontally averaged diagnostics (applies to 2d and 3d fields only)
137 type(axes_grp), pointer :: xyave_axes => null() !< The associated 1d axes for horizontally area-averaged diagnostics
138 ! ID's for cell_measures
139 integer :: id_area = -1 !< The diag_manager id for area to be used for cell_measure of variables with this axes_grp.
140 integer :: id_volume = -1 !< The diag_manager id for volume to be used for cell_measure of variables
141 !! with this axes_grp.
142 ! For masking
143 real, pointer, dimension(:,:) :: mask2d => null() !< Mask for 2d (x-y) axes [nondim]
144 real, pointer, dimension(:,:,:) :: mask3d => null() !< Mask for 3d axes [nondim]
145 type(diag_dsamp), dimension(2:MAX_DSAMP_LEV) :: dsamp !< Downsample container
146
147 ! For diagnostics posted piecemeal
148 type(diag_buffer_2d) :: piecemeal_2d !< A dynamically reallocated buffer for 2d piecemeal diagnostics
149 type(diag_buffer_3d) :: piecemeal_3d !< A dynamically reallocated buffer for 3d piecemeal diagnostics
150end type axes_grp
151
152!> Contains an array to store a diagnostic target grid
153type, private :: diag_grids_type
154 real, dimension(:,:,:), allocatable :: h !< Target grid for remapped coordinate [H ~> m or kg m-2] or [Z ~> m]
155end type diag_grids_type
156
157!> Stores all the remapping grids and the model's native space thicknesses
158type, public :: diag_grid_storage
159 integer :: num_diag_coords !< Number of target coordinates
160 real, dimension(:,:,:), allocatable :: h_state !< Layer thicknesses in native
161 !! space [H ~> m or kg m-2]
162 type(diag_grids_type), dimension(:), allocatable :: diag_grids !< Primarily empty, except h field
163end type diag_grid_storage
164
165! Integers to encode the total cell methods
166!integer :: PPP=111 ! x:point,y:point,z:point, this kind of diagnostic is not currently present in diag_table.MOM6
167!integer :: PPS=112 ! x:point,y:point,z:sum , this kind of diagnostic is not currently present in diag_table.MOM6
168!integer :: PPM=113 ! x:point,y:point,z:mean , this kind of diagnostic is not currently present in diag_table.MOM6
169integer :: PSP=121 !< x:point,y:sum,z:point
170integer :: PSS=122 !< x:point,y:sum,z:point
171integer :: PSM=123 !< x:point,y:sum,z:mean
172integer :: PMP=131 !< x:point,y:mean,z:point
173integer :: PMM=133 !< x:point,y:mean,z:mean
174integer :: SPP=211 !< x:sum,y:point,z:point
175integer :: SPS=212 !< x:sum,y:point,z:sum
176integer :: SSP=221 !< x:sum,y:sum,z:point
177integer :: MPP=311 !< x:mean,y:point,z:point
178integer :: MPM=313 !< x:mean,y:point,z:mean
179integer :: MMP=331 !< x:mean,y:mean,z:point
180integer :: MMS=332 !< x:mean,y:mean,z:sum
181integer :: SSS=222 !< x:sum,y:sum,z:sum
182integer :: MMM=333 !< x:mean,y:mean,z:mean
183integer :: MSK=-1 !< Use the downsample method of a mask
184
185!> This type is used to represent a diagnostic at the diag_mediator level.
186!!
187!! There can be both 'primary' and 'secondary' diagnostics. The primaries
188!! reside in the diag_cs%diags array. They have an id which is an index
189!! into this array. The secondaries are 'variations' on the primary diagnostic.
190!! For example the CMOR diagnostics are secondary. The secondary diagnostics
191!! are kept in a list with the primary diagnostic as the head.
192type, private :: diag_type
193 logical :: in_use !< True if this entry is being used.
194 integer :: fms_diag_id !< Underlying FMS diag_manager id.
195 integer :: fms_xyave_diag_id = -1 !< For a horizontally area-averaged diagnostic.
196 integer :: downsample_diag_id = -1 !< For a horizontally area-downsampled diagnostic.
197 character(len=64) :: debug_str = '' !< The diagnostic name and module for FATAL errors and debugging.
198 type(axes_grp), pointer :: axes => null() !< The axis group for this diagnostic
199 type(diag_type), pointer :: next => null() !< Pointer to the next diagnostic
200 real :: conversion_factor = 0. !< If non-zero, a factor to multiply data by before posting to FMS,
201 !! often including factors to undo internal scaling in units of [a A-1 ~> 1]
202 logical :: v_extensive = .false. !< True for vertically extensive fields (vertically integrated).
203 !! False for intensive (concentrations).
204 integer :: xyz_method = 0 !< A 3 digit integer encoding the diagnostics cell method
205 !! It can be used to determine the downsample algorithm
206end type diag_type
207
208!> Container for down sampling information
209type diagcs_dsamp
210 integer :: isc !< The start i-index of cell centers within the computational domain
211 integer :: iec !< The end i-index of cell centers within the computational domain
212 integer :: jsc !< The start j-index of cell centers within the computational domain
213 integer :: jec !< The end j-index of cell centers within the computational domain
214 integer :: isd !< The start i-index of cell centers within the data domain
215 integer :: ied !< The end i-index of cell centers within the data domain
216 integer :: jsd !< The start j-index of cell centers within the data domain
217 integer :: jed !< The end j-index of cell centers within the data domain
218 integer :: isg !< The start i-index of cell centers within the global domain
219 integer :: ieg !< The end i-index of cell centers within the global domain
220 integer :: jsg !< The start j-index of cell centers within the global domain
221 integer :: jeg !< The end j-index of cell centers within the global domain
222 integer :: isgB !< The start i-index of cell corners within the global domain
223 integer :: iegB !< The end i-index of cell corners within the global domain
224 integer :: jsgB !< The start j-index of cell corners within the global domain
225 integer :: jegB !< The end j-index of cell corners within the global domain
226
227 !>@{ Axes for each location on a diagnostic grid
228 type(axes_grp) :: axesBL, axesTL, axesCuL, axesCvL
229 type(axes_grp) :: axesBi, axesTi, axesCui, axesCvi
230 type(axes_grp) :: axesB1, axesT1, axesCu1, axesCv1
231 type(axes_grp), dimension(:), allocatable :: remap_axesTL, remap_axesBL, remap_axesCuL, remap_axesCvL
232 type(axes_grp), dimension(:), allocatable :: remap_axesTi, remap_axesBi, remap_axesCui, remap_axesCvi
233 !>@}
234
235 real, dimension(:,:), pointer :: mask2dT => null() !< 2D mask array for cell-center points [nondim]
236 real, dimension(:,:), pointer :: mask2dBu => null() !< 2D mask array for cell-corner points [nondim]
237 real, dimension(:,:), pointer :: mask2dCu => null() !< 2D mask array for east-face points [nondim]
238 real, dimension(:,:), pointer :: mask2dCv => null() !< 2D mask array for north-face points [nondim]
239 !>@{ 3D mask arrays for diagnostics at layers (mask...L) and interfaces (mask...i), all [nondim]
240 real, dimension(:,:,:), pointer :: mask3dTL => null()
241 real, dimension(:,:,:), pointer :: mask3dBL => null()
242 real, dimension(:,:,:), pointer :: mask3dCuL => null()
243 real, dimension(:,:,:), pointer :: mask3dCvL => null()
244 real, dimension(:,:,:), pointer :: mask3dTi => null()
245 real, dimension(:,:,:), pointer :: mask3dBi => null()
246 real, dimension(:,:,:), pointer :: mask3dCui => null()
247 real, dimension(:,:,:), pointer :: mask3dCvi => null()
248 !>@}
249end type diagcs_dsamp
250
251!> The following data type a list of diagnostic fields an their variants,
252!! as well as variables that control the handling of model output.
253type, public :: diag_ctrl
254 integer :: available_diag_doc_unit = -1 !< The unit number of a diagnostic documentation file.
255 !! This file is open if available_diag_doc_unit is > 0.
256 integer :: chksum_iounit = -1 !< The unit number of a diagnostic documentation file.
257 !! This file is open if available_diag_doc_unit is > 0.
258 logical :: diag_as_chksum !< If true, log chksums in a text file instead of posting diagnostics
259 logical :: show_call_tree !< Display the call tree while running. Set by VERBOSITY level.
260 logical :: index_space_axes !< If true, diagnostic horizontal coordinates axes are in index space.
261
262 ! The following fields are used for the output of the data.
263 ! These give the computational-domain sizes, and are relative to a start value
264 ! of 1 in memory for the tracer-point arrays.
265 integer :: is !< The start i-index of cell centers within the computational domain
266 integer :: ie !< The end i-index of cell centers within the computational domain
267 integer :: js !< The start j-index of cell centers within the computational domain
268 integer :: je !< The end j-index of cell centers within the computational domain
269 ! These give the memory-domain sizes, and can start at any value on each PE.
270 integer :: isd !< The start i-index of cell centers within the data domain
271 integer :: ied !< The end i-index of cell centers within the data domain
272 integer :: jsd !< The start j-index of cell centers within the data domain
273 integer :: jed !< The end j-index of cell centers within the data domain
274 real :: time_int !< The time interval for any fields
275 !! that are offered for averaging [s].
276 type(time_type) :: time_end !< The end time of the valid interval for any offered field.
277 logical :: ave_enabled = .false. !< True if averaging is enabled.
278
279 !>@{ The following are 3D and 2D axis groups defined for output. The names
280 !! indicate the horizontal (B, T, Cu, or Cv) and vertical (L, i, or 1) locations.
281 type(axes_grp) :: axesBL, axesTL, axesCuL, axesCvL
282 type(axes_grp) :: axesBi, axesTi, axesCui, axesCvi
283 type(axes_grp) :: axesB1, axesT1, axesCu1, axesCv1
284 !>@}
285 type(axes_grp) :: axesZi !< A 1-D z-space axis at interfaces
286 type(axes_grp) :: axesZL !< A 1-D z-space axis at layer centers
287 type(axes_grp) :: axesNull !< An axis group for scalars
288
289 ! Mask arrays for 2D diagnostics
290 real, dimension(:,:), pointer :: mask2dT => null() !< 2D mask array for cell-center points [nondim]
291 real, dimension(:,:), pointer :: mask2dBu => null() !< 2D mask array for cell-corner points [nondim]
292 real, dimension(:,:), pointer :: mask2dCu => null() !< 2D mask array for east-face points [nondim]
293 real, dimension(:,:), pointer :: mask2dCv => null() !< 2D mask array for north-face points [nondim]
294 !>@{ 3D mask arrays for diagnostics at layers (mask...L) and interfaces (mask...i) all [nondim]
295 real, dimension(:,:,:), pointer :: mask3dTL => null()
296 real, dimension(:,:,:), pointer :: mask3dBL => null()
297 real, dimension(:,:,:), pointer :: mask3dCuL => null()
298 real, dimension(:,:,:), pointer :: mask3dCvL => null()
299 real, dimension(:,:,:), pointer :: mask3dTi => null()
300 real, dimension(:,:,:), pointer :: mask3dBi => null()
301 real, dimension(:,:,:), pointer :: mask3dCui => null()
302 real, dimension(:,:,:), pointer :: mask3dCvi => null()
303
304 type(diagcs_dsamp), dimension(2:MAX_DSAMP_LEV) :: dsamp !< Downsample control container
305
306 !>@}
307
308! Space for diagnostics is dynamically allocated as it is needed.
309! The chunk size is how much the array should grow on each new allocation.
310#define DIAG_ALLOC_CHUNK_SIZE 100
311 type(diag_type), dimension(:), allocatable :: diags !< The list of diagnostics
312 integer :: next_free_diag_id !< The next unused diagnostic ID
313
314 !> default missing value to be sent to ALL diagnostics registrations [various]
315 real :: missing_value = -1.0e34
316
317 !> Number of diagnostic vertical coordinates (remapped)
318 integer :: num_diag_coords
319 !> Control structure for each possible coordinate
320 type(diag_remap_ctrl), dimension(:), allocatable :: diag_remap_cs
321 type(diag_grid_storage) :: diag_grid_temp !< Stores the remapped diagnostic grid
322 logical :: diag_grid_overridden = .false. !< True if the diagnostic grids have been overriden
323
324 type(axes_grp), dimension(:), allocatable :: &
325 remap_axesZL, & !< The 1-D z-space cell-centered axis for remapping
326 remap_axesZi !< The 1-D z-space interface axis for remapping
327 !>@{ Axes used for remapping
328 type(axes_grp), dimension(:), allocatable :: remap_axesTL, remap_axesBL, remap_axesCuL, remap_axesCvL
329 type(axes_grp), dimension(:), allocatable :: remap_axesTi, remap_axesBi, remap_axesCui, remap_axesCvi
330 !>@}
331
332 ! Pointer to H, G and T&S needed for remapping
333 real, dimension(:,:,:), pointer :: h => null() !< The thicknesses needed for remapping [H ~> m or kg m-2]
334 real, dimension(:,:,:), pointer :: T => null() !< The temperatures needed for remapping [C ~> degC]
335 real, dimension(:,:,:), pointer :: S => null() !< The salinities needed for remapping [S ~> ppt]
336 type(EOS_type), pointer :: eqn_of_state => null() !< The equation of state type
337 type(thermo_var_ptrs), pointer :: tv => null() !< A structure with thermodynamic variables that are
338 !! used to convert thicknesses to vertical extents
339 type(ocean_grid_type), pointer :: G => null() !< The ocean grid type
340 type(verticalGrid_type), pointer :: GV => null() !< The model's vertical ocean grid
341 type(unit_scale_type), pointer :: US => null() !< A dimensional unit scaling type
342
343 !> The volume cell measure (special diagnostic) manager id
344 integer :: volume_cell_measure_dm_id = -1
345
346#if defined(DEBUG) || defined(__DO_SAFETY_CHECKS__)
347 ! Keep a copy of h so that we know whether it has changed [H ~> m or kg m-2]. If it has then
348 ! need the target grid for vertical remapping needs to have been updated.
349 real, dimension(:,:,:), allocatable :: h_old
350#endif
351
352 !> Number of checksum-only diagnostics
353 integer :: num_chksum_diags
354
355 real, dimension(:,:,:), allocatable :: h_begin !< Layer thicknesses at the beginning of the timestep used
356 !! for remapping of extensive variables [H ~> m or kg m-2]
357 real, dimension(:,:,:), allocatable :: dz_begin !< Layer vertical extents at the beginning of the timestep used
358 !! for remapping of extensive variables [Z ~> m]
359
360end type diag_ctrl
361
362!>@{ CPU clocks
363integer :: id_clock_diag_mediator, id_clock_diag_remap, id_clock_diag_grid_updates
364!>@}
365
366contains
367
368!> Sets up diagnostics axes
3691subroutine set_axes_info(G, GV, US, param_file, diag_cs, set_vertical)
370 type(ocean_grid_type), intent(inout) :: G !< Ocean grid structure
371 type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure
372 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
373 type(param_file_type), intent(in) :: param_file !< Parameter file structure
374 type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
375 logical, optional, intent(in) :: set_vertical !< If true or missing, set up
376 !! vertical axes
377 ! Local variables
378 integer :: id_xq, id_yq, id_zl, id_zi, id_xh, id_yh, id_null
379 integer :: id_zl_native, id_zi_native
380 integer :: i, j, nz
3812 real :: zlev(GV%ke) ! Numerical values for layer vertical coordinates, in unscaled units
382 ! that might be [m], [kg m-3] or [nondim], depending on the coordinate.
3832 real :: zinter(GV%ke+1) ! Numerical values for interface vertical coordinates, in unscaled units
384 ! that might be [m], [kg m-3] or [nondim], depending on the coordinate.
385 logical :: set_vert
3861 real, allocatable, dimension(:) :: IaxB, iax ! Index-based integer and half-integer i-axis labels [nondim]
3871 real, allocatable, dimension(:) :: JaxB, jax ! Index-based integer and half-integer j-axis labels [nondim]
388
389
3901 set_vert = .true. ; if (present(set_vertical)) set_vert = set_vertical
391
392
3931 if (diag_cs%index_space_axes) then
3940 allocate(IaxB(G%IsgB:G%IegB))
3950 do i=G%IsgB, G%IegB
3960 Iaxb(i)=real(i)
397 enddo
3980 allocate(iax(G%isg:G%ieg))
3990 do i=G%isg, G%ieg
4000 iax(i)=real(i)-0.5
401 enddo
4020 allocate(JaxB(G%JsgB:G%JegB))
4030 do j=G%JsgB, G%JegB
4040 JaxB(j)=real(j)
405 enddo
4060 allocate(jax(G%jsg:G%jeg))
4070 do j=G%jsg, G%jeg
4080 jax(j)=real(j)-0.5
409 enddo
410 endif
411
412 ! Horizontal axes for the native grids
4131 if (G%symmetric) then
4141 if (diag_cs%index_space_axes) then
415 id_xq = diag_axis_init('iq', IaxB(G%isgB:G%iegB), 'none', 'x', &
4160 'q point grid-space longitude', G%Domain, position=EAST)
417 id_yq = diag_axis_init('jq', JaxB(G%jsgB:G%jegB), 'none', 'y', &
4180 'q point grid space latitude', G%Domain, position=NORTH)
419 else
420 id_xq = diag_axis_init('xq', G%gridLonB(G%isgB:G%iegB), G%x_axis_units, 'x', &
4211 'q point nominal longitude', G%Domain, position=EAST)
422 id_yq = diag_axis_init('yq', G%gridLatB(G%jsgB:G%jegB), G%y_axis_units, 'y', &
4231 'q point nominal latitude', G%Domain, position=NORTH)
424 endif
425 else
4260 if (diag_cs%index_space_axes) then
427 id_xq = diag_axis_init('Iq', IaxB(G%isg:G%ieg), 'none', 'x', &
4280 'q point grid-space longitude', G%Domain, position=EAST)
429 id_yq = diag_axis_init('Jq', JaxB(G%jsg:G%jeg), 'none', 'y', &
4300 'q point grid space latitude', G%Domain, position=NORTH)
431 else
432 id_xq = diag_axis_init('xq', G%gridLonB(G%isg:G%ieg), G%x_axis_units, 'x', &
4330 'q point nominal longitude', G%Domain, position=EAST)
434 id_yq = diag_axis_init('yq', G%gridLatB(G%jsg:G%jeg), G%y_axis_units, 'y', &
4350 'q point nominal latitude', G%Domain, position=NORTH)
436 endif
437 endif
438
4391 if (diag_cs%index_space_axes) then
440 id_xh = diag_axis_init('ih', iax(G%isg:G%ieg), 'none', 'x', &
4410 'h point grid-space longitude', G%Domain)
442 id_yh = diag_axis_init('jh', jax(G%jsg:G%jeg), 'none', 'y', &
4430 'h point grid space latitude', G%Domain)
444 else
445 id_xh = diag_axis_init('xh', G%gridLonT(G%isg:G%ieg), G%x_axis_units, 'x', &
4461 'h point nominal longitude', G%Domain)
447 id_yh = diag_axis_init('yh', G%gridLatT(G%jsg:G%jeg), G%y_axis_units, 'y', &
4481 'h point nominal latitude', G%Domain)
449 endif
450
4511 if (set_vert) then
4521 nz = GV%ke
45377 zinter(1:nz+1) = GV%sInterface(1:nz+1)
45476 zlev(1:nz) = GV%sLayer(1:nz)
455 id_zl = diag_axis_init('zl', zlev, trim(GV%zAxisUnits), 'z', &
4561 'Layer '//trim(GV%zAxisLongName), direction=GV%direction)
457 id_zi = diag_axis_init('zi', zinter, trim(GV%zAxisUnits), 'z', &
4581 'Interface '//trim(GV%zAxisLongName), direction=GV%direction)
459 else
4600 id_zl = -1 ; id_zi = -1
461 endif
4621 id_zl_native = id_zl ; id_zi_native = id_zi
463 ! Vertical axes for the interfaces and layers
464 call define_axes_group(diag_cs, (/ id_zi /), diag_cs%axesZi, &
4652 v_cell_method='point', is_interface=.true.)
466 call define_axes_group(diag_cs, (/ id_zL /), diag_cs%axesZL, &
4672 v_cell_method='mean', is_layer=.true.)
468
469 ! Axis groupings for the model layers
470 call define_axes_group(diag_cs, (/ id_xh, id_yh, id_zL /), diag_cs%axesTL, &
471 x_cell_method='mean', y_cell_method='mean', v_cell_method='mean', &
4724 is_h_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
473 call define_axes_group(diag_cs, (/ id_xq, id_yq, id_zL /), diag_cs%axesBL, &
474 x_cell_method='point', y_cell_method='point', v_cell_method='mean', &
4754 is_q_point=.true., is_layer=.true.)
476 call define_axes_group(diag_cs, (/ id_xq, id_yh, id_zL /), diag_cs%axesCuL, &
477 x_cell_method='point', y_cell_method='mean', v_cell_method='mean', &
4784 is_u_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
479 call define_axes_group(diag_cs, (/ id_xh, id_yq, id_zL /), diag_cs%axesCvL, &
480 x_cell_method='mean', y_cell_method='point', v_cell_method='mean', &
4814 is_v_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
482
483 ! Axis groupings for the model interfaces
484 call define_axes_group(diag_cs, (/ id_xh, id_yh, id_zi /), diag_cs%axesTi, &
485 x_cell_method='mean', y_cell_method='mean', v_cell_method='point', &
4864 is_h_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
487 call define_axes_group(diag_cs, (/ id_xq, id_yq, id_zi /), diag_cs%axesBi, &
488 x_cell_method='point', y_cell_method='point', v_cell_method='point', &
4894 is_q_point=.true., is_interface=.true.)
490 call define_axes_group(diag_cs, (/ id_xq, id_yh, id_zi /), diag_cs%axesCui, &
491 x_cell_method='point', y_cell_method='mean', v_cell_method='point', &
4924 is_u_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
493 call define_axes_group(diag_cs, (/ id_xh, id_yq, id_zi /), diag_cs%axesCvi, &
494 x_cell_method='mean', y_cell_method='point', v_cell_method='point', &
4954 is_v_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
496
497 ! Axis groupings for 2-D arrays
498 call define_axes_group(diag_cs, (/ id_xh, id_yh /), diag_cs%axesT1, &
4993 x_cell_method='mean', y_cell_method='mean', is_h_point=.true.)
500 call define_axes_group(diag_cs, (/ id_xq, id_yq /), diag_cs%axesB1, &
5013 x_cell_method='point', y_cell_method='point', is_q_point=.true.)
502 call define_axes_group(diag_cs, (/ id_xq, id_yh /), diag_cs%axesCu1, &
5033 x_cell_method='point', y_cell_method='mean', is_u_point=.true.)
504 call define_axes_group(diag_cs, (/ id_xh, id_yq /), diag_cs%axesCv1, &
5053 x_cell_method='mean', y_cell_method='point', is_v_point=.true.)
506
507 ! Define array extents for all piecemeal buffers
5081 call set_piecemeal_extents(diag_cs)
509
510 ! Axis group for special null axis for scalars from diag manager.
5111 id_null = diag_axis_init('scalar_axis', (/0./), 'none', 'N', 'none', null_axis=.true.)
5122 call define_axes_group(diag_cs, (/ id_null /), diag_cs%axesNull)
513
514 ! Set axis groups for non-native, non-downsampled grids
5151 if (diag_cs%num_diag_coords>0) then
5163 allocate(diag_cs%remap_axesZL(diag_cs%num_diag_coords))
5173 allocate(diag_cs%remap_axesTL(diag_cs%num_diag_coords))
5183 allocate(diag_cs%remap_axesBL(diag_cs%num_diag_coords))
5193 allocate(diag_cs%remap_axesCuL(diag_cs%num_diag_coords))
5203 allocate(diag_cs%remap_axesCvL(diag_cs%num_diag_coords))
5213 allocate(diag_cs%remap_axesZi(diag_cs%num_diag_coords))
5223 allocate(diag_cs%remap_axesTi(diag_cs%num_diag_coords))
5233 allocate(diag_cs%remap_axesBi(diag_cs%num_diag_coords))
5243 allocate(diag_cs%remap_axesCui(diag_cs%num_diag_coords))
5253 allocate(diag_cs%remap_axesCvi(diag_cs%num_diag_coords))
526 endif
527
5282 do i=1, diag_cs%num_diag_coords
529 ! For each possible diagnostic coordinate
5301 call diag_remap_configure_axes(diag_cs%diag_remap_cs(i), G, GV, US, param_file)
531
532 ! Allocate these arrays since the size of the diagnostic array is now known
5331 allocate(diag_cs%diag_remap_cs(i)%h(G%isd:G%ied,G%jsd:G%jed, diag_cs%diag_remap_cs(i)%nz))
5341 allocate(diag_cs%diag_remap_cs(i)%h_extensive(G%isd:G%ied,G%jsd:G%jed, diag_cs%diag_remap_cs(i)%nz))
535
536 ! This vertical coordinate has been configured so can be used.
5372 if (diag_remap_axes_configured(diag_cs%diag_remap_cs(i))) then
538
539 ! This fetches the 1D-axis id for layers and interfaces and overwrite
540 ! id_zl and id_zi from above. It also returns the number of layers.
5411 call diag_remap_get_axes_info(diag_cs%diag_remap_cs(i), nz, id_zL, id_zi)
542
543 ! Axes for z layers
544 call define_axes_group(diag_cs, (/ id_zL /), diag_cs%remap_axesZL(i), &
545 nz=nz, vertical_coordinate_number=i, &
546 v_cell_method='mean', &
5472 is_h_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true.)
548 call define_axes_group(diag_cs, (/ id_xh, id_yh, id_zL /), diag_cs%remap_axesTL(i), &
549 nz=nz, vertical_coordinate_number=i, &
550 x_cell_method='mean', y_cell_method='mean', v_cell_method='mean', &
551 is_h_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
5524 xyave_axes=diag_cs%remap_axesZL(i))
553
554 !! \note Remapping for B points is not yet implemented so needs_remapping is not
555 !! provided for remap_axesBL
556 call define_axes_group(diag_cs, (/ id_xq, id_yq, id_zL /), diag_cs%remap_axesBL(i), &
557 nz=nz, vertical_coordinate_number=i, &
558 x_cell_method='point', y_cell_method='point', v_cell_method='mean', &
5594 is_q_point=.true., is_layer=.true., is_native=.false.)
560
561 call define_axes_group(diag_cs, (/ id_xq, id_yh, id_zL /), diag_cs%remap_axesCuL(i), &
562 nz=nz, vertical_coordinate_number=i, &
563 x_cell_method='point', y_cell_method='mean', v_cell_method='mean', &
564 is_u_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
5654 xyave_axes=diag_cs%remap_axesZL(i))
566
567 call define_axes_group(diag_cs, (/ id_xh, id_yq, id_zL /), diag_cs%remap_axesCvL(i), &
568 nz=nz, vertical_coordinate_number=i, &
569 x_cell_method='mean', y_cell_method='point', v_cell_method='mean', &
570 is_v_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
5714 xyave_axes=diag_cs%remap_axesZL(i))
572
573 ! Axes for z interfaces
574 call define_axes_group(diag_cs, (/ id_zi /), diag_cs%remap_axesZi(i), &
575 nz=nz, vertical_coordinate_number=i, &
576 v_cell_method='point', &
5772 is_h_point=.true., is_interface=.true., is_native=.false., needs_interpolating=.true.)
578 call define_axes_group(diag_cs, (/ id_xh, id_yh, id_zi /), diag_cs%remap_axesTi(i), &
579 nz=nz, vertical_coordinate_number=i, &
580 x_cell_method='mean', y_cell_method='mean', v_cell_method='point', &
581 is_h_point=.true., is_interface=.true., is_native=.false., needs_interpolating=.true., &
5824 xyave_axes=diag_cs%remap_axesZi(i))
583
584 !! \note Remapping for B points is not yet implemented so needs_remapping is not provided for remap_axesBi
585 call define_axes_group(diag_cs, (/ id_xq, id_yq, id_zi /), diag_cs%remap_axesBi(i), &
586 nz=nz, vertical_coordinate_number=i, &
587 x_cell_method='point', y_cell_method='point', v_cell_method='point', &
5884 is_q_point=.true., is_interface=.true., is_native=.false.)
589
590 call define_axes_group(diag_cs, (/ id_xq, id_yh, id_zi /), diag_cs%remap_axesCui(i), &
591 nz=nz, vertical_coordinate_number=i, &
592 x_cell_method='point', y_cell_method='mean', v_cell_method='point', &
593 is_u_point=.true., is_interface=.true., is_native=.false., &
5944 needs_interpolating=.true., xyave_axes=diag_cs%remap_axesZi(i))
595
596 call define_axes_group(diag_cs, (/ id_xh, id_yq, id_zi /), diag_cs%remap_axesCvi(i), &
597 nz=nz, vertical_coordinate_number=i, &
598 x_cell_method='mean', y_cell_method='point', v_cell_method='point', &
599 is_v_point=.true., is_interface=.true., is_native=.false., &
6004 needs_interpolating=.true., xyave_axes=diag_cs%remap_axesZi(i))
601 endif
602 enddo
603
6041 if (diag_cs%index_space_axes) then
6050 deallocate(IaxB, iax, JaxB, jax)
606 endif
607 ! Define the downsampled axes
6081 call set_axes_info_dsamp(G, GV, param_file, diag_cs, id_zl_native, id_zi_native)
609
6101 call diag_grid_storage_init(diag_CS%diag_grid_temp, G, GV, diag_CS)
611
6121end subroutine set_axes_info
613
6141subroutine set_axes_info_dsamp(G, GV, param_file, diag_cs, id_zl_native, id_zi_native)
615 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
616 type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure
617 type(param_file_type), intent(in) :: param_file !< Parameter file structure
618 type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
619 integer, intent(in) :: id_zl_native !< ID of native layers
620 integer, intent(in) :: id_zi_native !< ID of native interfaces
621
622 ! Local variables
623 integer :: id_xq, id_yq, id_zl, id_zi, id_xh, id_yh
624 integer :: i, j, nz, dl
625 real, dimension(:), pointer :: gridLonT_dsamp =>NULL() ! The longitude of downsampled T points for labeling
626 ! the output axes, often in units of [degrees_N] or
627 ! [km] or [m] or [gridpoints].
628 real, dimension(:), pointer :: gridLatT_dsamp =>NULL() ! The latitude of downsampled T points for labeling
629 ! the output axes, often in units of [degrees_N] or
630 ! [km] or [m] or [gridpoints].
631 real, dimension(:), pointer :: gridLonB_dsamp =>NULL() ! The longitude of downsampled B points for labeling
632 ! the output axes, often in units of [degrees_N] or
633 ! [km] or [m] or [gridpoints].
634 real, dimension(:), pointer :: gridLatB_dsamp =>NULL() ! The latitude of downsampled B points for labeling
635 ! the output axes, often in units of [degrees_N] or
636 ! [km] or [m] or [gridpoints].
637
638
6391 id_zl = id_zl_native ; id_zi = id_zi_native
640 ! Axes group for native downsampled diagnostics
6412 do dl=2,MAX_DSAMP_LEV
6421 if (dl /= 2) call MOM_error(FATAL, "set_axes_info_dsamp: Downsample level other than 2 is not supported yet!")
6431 if (G%symmetric) then
6441 allocate(gridLonB_dsamp(diag_cs%dsamp(dl)%isgB:diag_cs%dsamp(dl)%iegB))
6451 allocate(gridLatB_dsamp(diag_cs%dsamp(dl)%jsgB:diag_cs%dsamp(dl)%jegB))
64662 do i=diag_cs%dsamp(dl)%isgB,diag_cs%dsamp(dl)%iegB ; gridLonB_dsamp(i) = G%gridLonB(G%isgB+dl*i) ; enddo
64732 do j=diag_cs%dsamp(dl)%jsgB,diag_cs%dsamp(dl)%jegB ; gridLatB_dsamp(j) = G%gridLatB(G%jsgB+dl*j) ; enddo
648 id_xq = diag_axis_init('xq', gridLonB_dsamp, G%x_axis_units, 'x', &
6491 'q point nominal longitude', G%Domain, coarsen=2)
650 id_yq = diag_axis_init('yq', gridLatB_dsamp, G%y_axis_units, 'y', &
6511 'q point nominal latitude', G%Domain, coarsen=2)
6521 deallocate(gridLonB_dsamp, gridLatB_dsamp)
653 else
6540 allocate(gridLonB_dsamp(diag_cs%dsamp(dl)%isg:diag_cs%dsamp(dl)%ieg))
6550 allocate(gridLatB_dsamp(diag_cs%dsamp(dl)%jsg:diag_cs%dsamp(dl)%jeg))
6560 do i=diag_cs%dsamp(dl)%isg,diag_cs%dsamp(dl)%ieg ; gridLonB_dsamp(i) = G%gridLonB(G%isg+dl*i-2) ; enddo
6570 do j=diag_cs%dsamp(dl)%jsg,diag_cs%dsamp(dl)%jeg ; gridLatB_dsamp(j) = G%gridLatB(G%jsg+dl*j-2) ; enddo
658 id_xq = diag_axis_init('xq', gridLonB_dsamp, G%x_axis_units, 'x', &
6590 'q point nominal longitude', G%Domain, coarsen=2)
660 id_yq = diag_axis_init('yq', gridLatB_dsamp, G%y_axis_units, 'y', &
6610 'q point nominal latitude', G%Domain, coarsen=2)
6620 deallocate(gridLonB_dsamp, gridLatB_dsamp)
663 endif
664
6651 allocate(gridLonT_dsamp(diag_cs%dsamp(dl)%isg:diag_cs%dsamp(dl)%ieg))
6661 allocate(gridLatT_dsamp(diag_cs%dsamp(dl)%jsg:diag_cs%dsamp(dl)%jeg))
66761 do i=diag_cs%dsamp(dl)%isg,diag_cs%dsamp(dl)%ieg ; gridLonT_dsamp(i) = G%gridLonT(G%isg+dl*i-2) ; enddo
66831 do j=diag_cs%dsamp(dl)%jsg,diag_cs%dsamp(dl)%jeg ; gridLatT_dsamp(j) = G%gridLatT(G%jsg+dl*j-2) ; enddo
669 id_xh = diag_axis_init('xh', gridLonT_dsamp, G%x_axis_units, 'x', &
6701 'h point nominal longitude', G%Domain, coarsen=2)
671 id_yh = diag_axis_init('yh', gridLatT_dsamp, G%y_axis_units, 'y', &
6721 'h point nominal latitude', G%Domain, coarsen=2)
673
6741 deallocate(gridLonT_dsamp, gridLatT_dsamp)
675
676 ! Axis groupings for the model layers
677 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yh, id_zL /), diag_cs%dsamp(dl)%axesTL, dl, &
678 x_cell_method='mean', y_cell_method='mean', v_cell_method='mean', &
6794 is_h_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
680 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yq, id_zL /), diag_cs%dsamp(dl)%axesBL, dl, &
681 x_cell_method='point', y_cell_method='point', v_cell_method='mean', &
6824 is_q_point=.true., is_layer=.true.)
683 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yh, id_zL /), diag_cs%dsamp(dl)%axesCuL, dl, &
684 x_cell_method='point', y_cell_method='mean', v_cell_method='mean', &
6854 is_u_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
686 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yq, id_zL /), diag_cs%dsamp(dl)%axesCvL, dl, &
687 x_cell_method='mean', y_cell_method='point', v_cell_method='mean', &
6884 is_v_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
689
690 ! Axis groupings for the model interfaces
691 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yh, id_zi /), diag_cs%dsamp(dl)%axesTi, dl, &
692 x_cell_method='mean', y_cell_method='mean', v_cell_method='point', &
6934 is_h_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
694 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yq, id_zi /), diag_cs%dsamp(dl)%axesBi, dl, &
695 x_cell_method='point', y_cell_method='point', v_cell_method='point', &
6964 is_q_point=.true., is_interface=.true.)
697 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yh, id_zi /), diag_cs%dsamp(dl)%axesCui, dl, &
698 x_cell_method='point', y_cell_method='mean', v_cell_method='point', &
6994 is_u_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
700 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yq, id_zi /), diag_cs%dsamp(dl)%axesCvi, dl, &
701 x_cell_method='mean', y_cell_method='point', v_cell_method='point', &
7024 is_v_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
703
704 ! Axis groupings for 2-D arrays
705 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yh /), diag_cs%dsamp(dl)%axesT1, dl, &
7063 x_cell_method='mean', y_cell_method='mean', is_h_point=.true.)
707 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yq /), diag_cs%dsamp(dl)%axesB1, dl, &
7083 x_cell_method='point', y_cell_method='point', is_q_point=.true.)
709 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yh /), diag_cs%dsamp(dl)%axesCu1, dl, &
7103 x_cell_method='point', y_cell_method='mean', is_u_point=.true.)
711 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yq /), diag_cs%dsamp(dl)%axesCv1, dl, &
7123 x_cell_method='mean', y_cell_method='point', is_v_point=.true.)
713
714 ! Axis groupings with a non-native vertical coordinate
7151 if (diag_cs%num_diag_coords>0) then
7163 allocate(diag_cs%dsamp(dl)%remap_axesTL(diag_cs%num_diag_coords))
7173 allocate(diag_cs%dsamp(dl)%remap_axesBL(diag_cs%num_diag_coords))
7183 allocate(diag_cs%dsamp(dl)%remap_axesCuL(diag_cs%num_diag_coords))
7193 allocate(diag_cs%dsamp(dl)%remap_axesCvL(diag_cs%num_diag_coords))
7203 allocate(diag_cs%dsamp(dl)%remap_axesTi(diag_cs%num_diag_coords))
7213 allocate(diag_cs%dsamp(dl)%remap_axesBi(diag_cs%num_diag_coords))
7223 allocate(diag_cs%dsamp(dl)%remap_axesCui(diag_cs%num_diag_coords))
7233 allocate(diag_cs%dsamp(dl)%remap_axesCvi(diag_cs%num_diag_coords))
724 endif
725
7263 do i=1, diag_cs%num_diag_coords
727 ! For each possible diagnostic coordinate
728 ! call diag_remap_configure_axes(diag_cs%diag_remap_cs(i), G, GV, param_file)
729
730 ! This vertical coordinate has been configured so can be used.
7312 if (diag_remap_axes_configured(diag_cs%diag_remap_cs(i))) then
732
733 ! This fetches the 1D-axis id for layers and interfaces and overwrite
734 ! id_zl and id_zi from above. It also returns the number of layers.
7351 call diag_remap_get_axes_info(diag_cs%diag_remap_cs(i), nz, id_zL, id_zi)
736
737 ! Axes for z layers
738 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yh, id_zL /), diag_cs%dsamp(dl)%remap_axesTL(i), dl, &
739 nz=nz, vertical_coordinate_number=i, &
740 x_cell_method='mean', y_cell_method='mean', v_cell_method='mean', &
741 is_h_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
7424 xyave_axes=diag_cs%remap_axesZL(i))
743
744 !! \note Remapping for B points is not yet implemented so needs_remapping is not
745 !! provided for remap_axesBL
746 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yq, id_zL /), diag_cs%dsamp(dl)%remap_axesBL(i), dl, &
747 nz=nz, vertical_coordinate_number=i, &
748 x_cell_method='point', y_cell_method='point', v_cell_method='mean', &
7494 is_q_point=.true., is_layer=.true., is_native=.false.)
750
751 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yh, id_zL /), diag_cs%dsamp(dl)%remap_axesCuL(i), dl, &
752 nz=nz, vertical_coordinate_number=i, &
753 x_cell_method='point', y_cell_method='mean', v_cell_method='mean', &
754 is_u_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
7554 xyave_axes=diag_cs%remap_axesZL(i))
756
757 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yq, id_zL /), diag_cs%dsamp(dl)%remap_axesCvL(i), dl, &
758 nz=nz, vertical_coordinate_number=i, &
759 x_cell_method='mean', y_cell_method='point', v_cell_method='mean', &
760 is_v_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
7614 xyave_axes=diag_cs%remap_axesZL(i))
762
763 ! Axes for z interfaces
764 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yh, id_zi /), diag_cs%dsamp(dl)%remap_axesTi(i), dl, &
765 nz=nz, vertical_coordinate_number=i, &
766 x_cell_method='mean', y_cell_method='mean', v_cell_method='point', &
767 is_h_point=.true., is_interface=.true., is_native=.false., needs_interpolating=.true., &
7684 xyave_axes=diag_cs%remap_axesZi(i))
769
770 !! \note Remapping for B points is not yet implemented so needs_remapping is not provided for remap_axesBi
771 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yq, id_zi /), diag_cs%dsamp(dl)%remap_axesBi(i), dl, &
772 nz=nz, vertical_coordinate_number=i, &
773 x_cell_method='point', y_cell_method='point', v_cell_method='point', &
7744 is_q_point=.true., is_interface=.true., is_native=.false.)
775
776 call define_axes_group_dsamp(diag_cs, (/ id_xq, id_yh, id_zi /), diag_cs%dsamp(dl)%remap_axesCui(i), dl, &
777 nz=nz, vertical_coordinate_number=i, &
778 x_cell_method='point', y_cell_method='mean', v_cell_method='point', &
779 is_u_point=.true., is_interface=.true., is_native=.false., &
7804 needs_interpolating=.true., xyave_axes=diag_cs%remap_axesZi(i))
781
782 call define_axes_group_dsamp(diag_cs, (/ id_xh, id_yq, id_zi /), diag_cs%dsamp(dl)%remap_axesCvi(i), dl, &
783 nz=nz, vertical_coordinate_number=i, &
784 x_cell_method='mean', y_cell_method='point', v_cell_method='point', &
785 is_v_point=.true., is_interface=.true., is_native=.false., &
7864 needs_interpolating=.true., xyave_axes=diag_cs%remap_axesZi(i))
787 endif
788 enddo
789 enddo
790
7911end subroutine set_axes_info_dsamp
792
793
794!> set_masks_for_axes sets up the 2d and 3d masks for diagnostics using the current grid
795!! recorded after calling diag_update_remap_grids()
7961subroutine set_masks_for_axes(G, diag_cs)
797 type(ocean_grid_type), target, intent(in) :: G !< The ocean grid type.
798 type(diag_ctrl), pointer :: diag_cs !< A pointer to a type with many variables
799 !! used for diagnostics
800 ! Local variables
801 integer :: c, nk, i, j, k
802 type(axes_grp), pointer :: axes => NULL(), h_axes => NULL() ! Current axes, for convenience
803
8042 do c=1, diag_cs%num_diag_coords
805 ! This vertical coordinate has been configured so can be used.
8062 if (diag_remap_axes_configured(diag_cs%diag_remap_cs(c))) then
807
808 ! Level/layer h-points in diagnostic coordinate
8091 axes => diag_cs%remap_axesTL(c)
8101 nk = axes%nz
811289510 allocate( axes%mask3d(G%isd:G%ied,G%jsd:G%jed,nk), source=0. )
8121 call diag_remap_calc_hmask(diag_cs%diag_remap_cs(c), G, axes%mask3d)
813
8141 h_axes => diag_cs%remap_axesTL(c) ! Use the h-point masks to generate the u-, v- and q- masks
815
816 ! Level/layer u-points in diagnostic coordinate
8171 axes => diag_cs%remap_axesCuL(c)
8181 call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at u-layers')
8191 call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
820291754 allocate( axes%mask3d(G%IsdB:G%IedB,G%jsd:G%jed,nk), source=0. )
821241594 do k = 1, nk ; do j=G%jsc,G%jec ; do I=G%isc-1,G%iec
822241560 if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i+1,j,k) > 0.) axes%mask3d(I,j,k) = 1.
823 enddo ; enddo ; enddo
824
825 ! Level/layer v-points in diagnostic coordinate
8261 axes => diag_cs%remap_axesCvL(c)
8271 call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at v-layers')
8281 call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
829293767 allocate( axes%mask3d(G%isd:G%ied,G%JsdB:G%JedB,nk), source=0. )
830243607 do k = 1, nk ; do J=G%jsc-1,G%jec ; do i=G%isc,G%iec
831243573 if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i,j+1,k) > 0.) axes%mask3d(i,J,k) = 1.
832 enddo ; enddo ; enddo
833
834 ! Level/layer q-points in diagnostic coordinate
8351 axes => diag_cs%remap_axesBL(c)
8361 call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at q-layers')
8371 call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
838296044 allocate( axes%mask3d(G%IsdB:G%IedB,G%JsdB:G%JedB,nk), source=0. )
839245620 do k = 1, nk ; do J=G%jsc-1,G%jec ; do I=G%isc-1,G%iec
840243573 if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i+1,j+1,k) + &
841136919 h_axes%mask3d(i+1,j,k) + h_axes%mask3d(i,j+1,k) > 0.) axes%mask3d(I,J,k) = 1.
842 enddo ; enddo ; enddo
843
844 ! Interface h-points in diagnostic coordinate (w-point)
8451 axes => diag_cs%remap_axesTi(c)
8461 call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at h-interfaces')
8471 call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
848298283 allocate( axes%mask3d(G%isd:G%ied,G%jsd:G%jed,nk+1), source=0. )
8497627 do J=G%jsc-1,G%jec+1 ; do i=G%isc-1,G%iec+1
8507564 if (h_axes%mask3d(i,j,1) > 0.) axes%mask3d(i,J,1) = 1.
851249612 do K = 2, nk
852249612 if (h_axes%mask3d(i,j,k-1) + h_axes%mask3d(i,j,k) > 0.) axes%mask3d(i,J,k) = 1.
853 enddo
8547626 if (h_axes%mask3d(i,j,nk) > 0.) axes%mask3d(i,J,nk+1) = 1.
855 enddo ; enddo
856
8571 h_axes => diag_cs%remap_axesTi(c) ! Use the w-point masks to generate the u-, v- and q- masks
858
859 ! Interface u-points in diagnostic coordinate
8601 axes => diag_cs%remap_axesCui(c)
8611 call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at u-interfaces')
8621 call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
863300595 allocate( axes%mask3d(G%IsdB:G%IedB,G%jsd:G%jed,nk+1), source=0. )
864248915 do k = 1, nk+1 ; do j=G%jsc,G%jec ; do I=G%isc-1,G%iec
865248880 if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i+1,j,k) > 0.) axes%mask3d(I,j,k) = 1.
866 enddo ; enddo ; enddo
867
868 ! Interface v-points in diagnostic coordinate
8691 axes => diag_cs%remap_axesCvi(c)
8701 call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at v-interfaces')
8711 call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
872302669 allocate( axes%mask3d(G%isd:G%ied,G%JsdB:G%JedB,nk+1), source=0. )
873250989 do k = 1, nk+1 ; do J=G%jsc-1,G%jec ; do i=G%isc,G%iec
874250954 if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i,j+1,k) > 0.) axes%mask3d(i,J,k) = 1.
875 enddo ; enddo ; enddo
876
877 ! Interface q-points in diagnostic coordinate
8781 axes => diag_cs%remap_axesBi(c)
8791 call assert(axes%nz == nk, 'set_masks_for_axes: vertical size mismatch at q-interfaces')
8801 call assert(.not. associated(axes%mask3d), 'set_masks_for_axes: already associated')
881305015 allocate( axes%mask3d(G%IsdB:G%IedB,G%JsdB:G%JedB,nk+1), source=0. )
882245620 do k = 1, nk ; do J=G%jsc-1,G%jec ; do I=G%isc-1,G%iec
883243573 if (h_axes%mask3d(i,j,k) + h_axes%mask3d(i+1,j+1,k) + &
884141984 h_axes%mask3d(i+1,j,k) + h_axes%mask3d(i,j+1,k) > 0.) axes%mask3d(I,J,k) = 1.
885 enddo ; enddo ; enddo
886 endif
887 enddo
888
889 ! Allocate and initialize the downsampled masks for the axes
8901 call set_masks_for_axes_dsamp(G, diag_cs)
891
8921end subroutine set_masks_for_axes
893
8941subroutine set_masks_for_axes_dsamp(G, diag_cs)
895 type(ocean_grid_type), target, intent(in) :: G !< The ocean grid type.
896 type(diag_ctrl), pointer :: diag_cs !< A pointer to a type with many variables
897 !! used for diagnostics
898 ! Local variables
899 integer :: c, dl
900 type(axes_grp), pointer :: axes => NULL() ! Current axes, for convenience
901
902 ! Each downsampled axis needs both downsampled and non-downsampled masks.
903 ! The downsampled mask is needed for sending out the diagnostics output via diag_manager.
904 ! The non-downsampled mask is needed for downsampling the diagnostics field.
9052 do dl=2,MAX_DSAMP_LEV
9061 if (dl /= 2) call MOM_error(FATAL, "set_masks_for_axes_dsamp: Downsample level other than 2 is not supported!")
9073 do c=1, diag_cs%num_diag_coords
908 ! Level/layer h-points in diagnostic coordinate
9091 axes => diag_cs%remap_axesTL(c)
910 call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesTL(c)%dsamp(dl)%mask3d, &
911 dl, G%isc, G%jsc, G%isd, G%jsd, &
9121 G%HId2%isc, G%HId2%iec, G%HId2%jsc, G%HId2%jec, G%HId2%isd, G%HId2%ied, G%HId2%jsd, G%HId2%jed)
9131 diag_cs%dsamp(dl)%remap_axesTL(c)%mask3d => axes%mask3d ! Set a pointer to the non-downsampled mask
914 ! Level/layer u-points in diagnostic coordinate
9151 axes => diag_cs%remap_axesCuL(c)
916 call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCuL(c)%dsamp(dl)%mask3d, &
917 dl, G%IscB, G%jsc, G%IsdB, G%jsd, &
9181 G%HId2%IscB, G%HId2%IecB, G%HId2%jsc, G%HId2%jec, G%HId2%IsdB, G%HId2%IedB, G%HId2%jsd, G%HId2%jed)
9191 diag_cs%dsamp(dl)%remap_axesCul(c)%mask3d => axes%mask3d ! Set a pointer to the non-downsampled mask
920 ! Level/layer v-points in diagnostic coordinate
9211 axes => diag_cs%remap_axesCvL(c)
922 call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCvL(c)%dsamp(dl)%mask3d, &
923 dl, G%isc, G%JscB, G%isd, G%JsdB, &
9241 G%HId2%isc, G%HId2%iec, G%HId2%JscB, G%HId2%JecB, G%HId2%isd, G%HId2%ied, G%HId2%JsdB, G%HId2%JedB)
9251 diag_cs%dsamp(dl)%remap_axesCvL(c)%mask3d => axes%mask3d ! Set a pointer to the non-downsampled mask
926 ! Level/layer q-points in diagnostic coordinate
9271 axes => diag_cs%remap_axesBL(c)
928 call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesBL(c)%dsamp(dl)%mask3d, &
929 dl, G%IscB, G%JscB, G%IsdB, G%JsdB, &
9301 G%HId2%IscB, G%HId2%IecB, G%HId2%JscB, G%HId2%JecB, G%HId2%IsdB, G%HId2%IedB, G%HId2%JsdB, G%HId2%JedB)
9311 diag_cs%dsamp(dl)%remap_axesBL(c)%mask3d => axes%mask3d ! Set a pointer to the non-downsampled mask
932 ! Interface h-points in diagnostic coordinate (w-point)
9331 axes => diag_cs%remap_axesTi(c)
934 call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesTi(c)%dsamp(dl)%mask3d, &
935 dl, G%isc, G%jsc, G%isd, G%jsd, &
9361 G%HId2%isc, G%HId2%iec, G%HId2%jsc, G%HId2%jec, G%HId2%isd, G%HId2%ied, G%HId2%jsd, G%HId2%jed)
9371 diag_cs%dsamp(dl)%remap_axesTi(c)%mask3d => axes%mask3d ! Set a pointer to the non-downsampled mask
938 ! Interface u-points in diagnostic coordinate
9391 axes => diag_cs%remap_axesCui(c)
940 call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCui(c)%dsamp(dl)%mask3d, &
941 dl, G%IscB, G%jsc, G%IsdB, G%jsd, &
9421 G%HId2%IscB, G%HId2%IecB, G%HId2%jsc, G%HId2%jec, G%HId2%IsdB, G%HId2%IedB, G%HId2%jsd, G%HId2%jed)
9431 diag_cs%dsamp(dl)%remap_axesCui(c)%mask3d => axes%mask3d ! Set a pointer to the non-downsampled mask
944 ! Interface v-points in diagnostic coordinate
9451 axes => diag_cs%remap_axesCvi(c)
946 call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCvi(c)%dsamp(dl)%mask3d, &
947 dl, G%isc, G%JscB, G%isd, G%JsdB, &
9481 G%HId2%isc, G%HId2%iec, G%HId2%JscB, G%HId2%JecB, G%HId2%isd, G%HId2%ied, G%HId2%JsdB, G%HId2%JedB)
9491 diag_cs%dsamp(dl)%remap_axesCvi(c)%mask3d => axes%mask3d ! Set a pointer to the non-downsampled mask
950 ! Interface q-points in diagnostic coordinate
9511 axes => diag_cs%remap_axesBi(c)
952 call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesBi(c)%dsamp(dl)%mask3d, &
953 dl, G%IscB, G%JscB, G%IsdB, G%JsdB, &
9541 G%HId2%IscB, G%HId2%IecB, G%HId2%JscB, G%HId2%JecB, G%HId2%IsdB, G%HId2%IedB, G%HId2%JsdB, G%HId2%JedB)
9552 diag_cs%dsamp(dl)%remap_axesBi(c)%mask3d => axes%mask3d ! Set a pointer to the non-downsampled mask
956 enddo
957 enddo
9581end subroutine set_masks_for_axes_dsamp
959
960!> Attaches the id of cell areas to axes groups for use with cell_measures
9611subroutine diag_register_area_ids(diag_cs, id_area_t, id_area_q)
962 type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
963 integer, optional, intent(in) :: id_area_t !< Diag_mediator id for area of h-cells
964 integer, optional, intent(in) :: id_area_q !< Diag_mediator id for area of q-cells
965 ! Local variables
966 integer :: fms_id, i
9671 if (present(id_area_t)) then
9681 fms_id = diag_cs%diags(id_area_t)%fms_diag_id
9691 diag_cs%axesT1%id_area = fms_id
9701 diag_cs%axesTi%id_area = fms_id
9711 diag_cs%axesTL%id_area = fms_id
9722 do i=1, diag_cs%num_diag_coords
9731 diag_cs%remap_axesTL(i)%id_area = fms_id
9742 diag_cs%remap_axesTi(i)%id_area = fms_id
975 enddo
976 endif
9771 if (present(id_area_q)) then
9780 fms_id = diag_cs%diags(id_area_q)%fms_diag_id
9790 diag_cs%axesB1%id_area = fms_id
9800 diag_cs%axesBi%id_area = fms_id
9810 diag_cs%axesBL%id_area = fms_id
9820 do i=1, diag_cs%num_diag_coords
9830 diag_cs%remap_axesBL(i)%id_area = fms_id
9840 diag_cs%remap_axesBi(i)%id_area = fms_id
985 enddo
986 endif
9871end subroutine diag_register_area_ids
988
989!> Sets a handle inside diagnostics mediator to associate 3d cell measures
9901subroutine register_cell_measure(G, diag, Time)
991 type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
992 type(diag_ctrl), target, intent(inout) :: diag !< Regulates diagnostic output
993 type(time_type), intent(in) :: Time !< Model time
994 ! Local variables
995 integer :: id
996 id = register_diag_field('ocean_model', 'volcello', diag%axesTL, &
997 Time, 'Ocean grid-cell volume', units='m3', conversion=1.0, &
998 standard_name='ocean_volume', v_extensive=.true., &
9991 x_cell_method='sum', y_cell_method='sum')
10001 call diag_associate_volume_cell_measure(diag, id)
1001
10021end subroutine register_cell_measure
1003
1004!> Attaches the id of cell volumes to axes groups for use with cell_measures
10051subroutine diag_associate_volume_cell_measure(diag_cs, id_h_volume)
1006 type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
1007 integer, intent(in) :: id_h_volume !< Diag_manager id for volume of h-cells
1008 ! Local variables
1009 type(diag_type), pointer :: tmp => NULL()
1010
10111 if (id_h_volume<=0) return ! Do nothing
10120 diag_cs%volume_cell_measure_dm_id = id_h_volume ! Record for diag_get_volume_cell_measure_dm_id()
1013
1014 ! Set the cell measure for this axes group to the FMS id in this coordinate system
10150 diag_cs%diags(id_h_volume)%axes%id_volume = diag_cs%diags(id_h_volume)%fms_diag_id
1016
10170 tmp => diag_cs%diags(id_h_volume)%next ! First item in the list, if any
10180 do while (associated(tmp))
1019 ! Set the cell measure for this axes group to the FMS id in this coordinate system
10200 tmp%axes%id_volume = tmp%fms_diag_id
10210 tmp => tmp%next ! Move to next axes group for this field
1022 enddo
1023
1024end subroutine diag_associate_volume_cell_measure
1025
1026!> Returns diag_manager id for cell measure of h-cells
10271integer function diag_get_volume_cell_measure_dm_id(diag_cs)
1028 type(diag_ctrl), intent(in) :: diag_cs !< Diagnostics control structure
1029
10301 diag_get_volume_cell_measure_dm_id = diag_cs%volume_cell_measure_dm_id
1031
10321end function diag_get_volume_cell_measure_dm_id
1033
1034!> Define a group of "axes" from a list of handles and associate a mask with it
103550subroutine define_axes_group(diag_cs, handles, axes, nz, vertical_coordinate_number, &
1036 x_cell_method, y_cell_method, v_cell_method, &
1037 is_h_point, is_q_point, is_u_point, is_v_point, &
1038 is_layer, is_interface, &
1039 is_native, needs_remapping, needs_interpolating, &
1040 xyave_axes)
1041 type(diag_ctrl), target, intent(in) :: diag_cs !< Diagnostics control structure
1042 integer, dimension(:), intent(in) :: handles !< A list of 1D axis handles
1043 type(axes_grp), intent(out) :: axes !< The group of 1D axes
1044 integer, optional, intent(in) :: nz !< Number of layers in this diagnostic grid
1045 integer, optional, intent(in) :: vertical_coordinate_number !< Index number for vertical coordinate
1046 character(len=*), optional, intent(in) :: x_cell_method !< A x-direction cell method used to construct the
1047 !! "cell_methods" attribute in CF convention
1048 character(len=*), optional, intent(in) :: y_cell_method !< A y-direction cell method used to construct the
1049 !! "cell_methods" attribute in CF convention
1050 character(len=*), optional, intent(in) :: v_cell_method !< A vertical direction cell method used to construct
1051 !! the "cell_methods" attribute in CF convention
1052 logical, optional, intent(in) :: is_h_point !< If true, indicates this axes group for h-point
1053 !! located fields
1054 logical, optional, intent(in) :: is_q_point !< If true, indicates this axes group for q-point
1055 !! located fields
1056 logical, optional, intent(in) :: is_u_point !< If true, indicates this axes group for
1057 !! u-point located fields
1058 logical, optional, intent(in) :: is_v_point !< If true, indicates this axes group for
1059 !! v-point located fields
1060 logical, optional, intent(in) :: is_layer !< If true, indicates that this axes group is
1061 !! for a layer vertically-located field.
1062 logical, optional, intent(in) :: is_interface !< If true, indicates that this axes group
1063 !! is for an interface vertically-located field.
1064 logical, optional, intent(in) :: is_native !< If true, indicates that this axes group is
1065 !! for a native model grid. False for any other grid.
1066 logical, optional, intent(in) :: needs_remapping !< If true, indicates that this axes group is
1067 !! for a intensive layer-located field that must
1068 !! be remapped to these axes. Used for rank>2.
1069 logical, optional, intent(in) :: needs_interpolating !< If true, indicates that this axes group
1070 !! is for a sampled interface-located field that must
1071 !! be interpolated to these axes. Used for rank>2.
1072 type(axes_grp), optional, target :: xyave_axes !< The corresponding axes group for horizontally
1073 !! area-average diagnostics
1074 ! Local variables
1075 integer :: n
1076
107725 n = size(handles)
107825 if (n<1 .or. n>3) call MOM_error(FATAL, "define_axes_group: wrong size for list of handles!")
107925 allocate( axes%handles(n) )
108025 axes%id = ints_to_string(handles, max(n,3)) ! Identifying string
108125 axes%rank = n
108286 axes%handles(:) = handles(:)
108325 axes%diag_cs => diag_cs ! A (circular) link back to the diag_cs structure
108425 if (present(x_cell_method)) then
108520 if (axes%rank<2) call MOM_error(FATAL, 'define_axes_group: ' // &
10860 'Can not set x_cell_method for rank<2.')
108720 axes%x_cell_method = trim(x_cell_method)
1088 else
10895 axes%x_cell_method = ''
1090 endif
109125 if (present(y_cell_method)) then
109220 if (axes%rank<2) call MOM_error(FATAL, 'define_axes_group: ' // &
10930 'Can not set y_cell_method for rank<2.')
109420 axes%y_cell_method = trim(y_cell_method)
1095 else
10965 axes%y_cell_method = ''
1097 endif
109825 if (present(v_cell_method)) then
109920 if (axes%rank/=1 .and. axes%rank/=3) call MOM_error(FATAL, 'define_axes_group: ' // &
11000 'Can not set v_cell_method for rank<>1 or 3.')
110120 axes%v_cell_method = trim(v_cell_method)
1102 else
11035 axes%v_cell_method = ''
1104 endif
1105
110625 if (present(nz)) axes%nz = nz
110725 if (present(vertical_coordinate_number)) axes%vertical_coordinate_number = vertical_coordinate_number
110825 if (present(is_h_point)) axes%is_h_point = is_h_point
110925 if (present(is_q_point)) axes%is_q_point = is_q_point
111025 if (present(is_u_point)) axes%is_u_point = is_u_point
111125 if (present(is_v_point)) axes%is_v_point = is_v_point
111225 if (present(is_layer)) axes%is_layer = is_layer
111325 if (present(is_interface)) axes%is_interface = is_interface
111425 if (present(is_native)) axes%is_native = is_native
111525 if (present(needs_remapping)) axes%needs_remapping = needs_remapping
111625 if (present(needs_interpolating)) axes%needs_interpolating = needs_interpolating
111725 if (present(xyave_axes)) axes%xyave_axes => xyave_axes
1118
1119 ! Setup masks for this axes group
112025 axes%mask2d => null()
112125 if (axes%rank==2) then
11224 if (axes%is_h_point) axes%mask2d => diag_cs%mask2dT
11234 if (axes%is_u_point) axes%mask2d => diag_cs%mask2dCu
11244 if (axes%is_v_point) axes%mask2d => diag_cs%mask2dCv
11254 if (axes%is_q_point) axes%mask2d => diag_cs%mask2dBu
1126 endif
1127 ! A static 3d mask for non-native coordinates can only be setup when a grid is available
112825 axes%mask3d => null()
112925 if (axes%rank==3 .and. axes%is_native) then
1130 ! Native variables can/should use the native masks copied into diag_cs
11318 if (axes%is_layer) then
11324 if (axes%is_h_point) axes%mask3d => diag_cs%mask3dTL
11334 if (axes%is_u_point) axes%mask3d => diag_cs%mask3dCuL
11344 if (axes%is_v_point) axes%mask3d => diag_cs%mask3dCvL
11354 if (axes%is_q_point) axes%mask3d => diag_cs%mask3dBL
11364 elseif (axes%is_interface) then
11374 if (axes%is_h_point) axes%mask3d => diag_cs%mask3dTi
11384 if (axes%is_u_point) axes%mask3d => diag_cs%mask3dCui
11394 if (axes%is_v_point) axes%mask3d => diag_cs%mask3dCvi
11404 if (axes%is_q_point) axes%mask3d => diag_cs%mask3dBi
1141 endif
1142 endif
1143
1144
114525end subroutine define_axes_group
1146
1147!> Defines a group of downsampled "axes" from list of handles
114840subroutine define_axes_group_dsamp(diag_cs, handles, axes, dl, nz, vertical_coordinate_number, &
1149 x_cell_method, y_cell_method, v_cell_method, &
1150 is_h_point, is_q_point, is_u_point, is_v_point, &
1151 is_layer, is_interface, &
1152 is_native, needs_remapping, needs_interpolating, &
1153 xyave_axes)
1154 type(diag_ctrl), target, intent(in) :: diag_cs !< Diagnostics control structure
1155 integer, dimension(:), intent(in) :: handles !< A list of 1D axis handles
1156 type(axes_grp), intent(out) :: axes !< The group of 1D axes
1157 integer, intent(in) :: dl !< Downsample level
1158 integer, optional, intent(in) :: nz !< Number of layers in this diagnostic grid
1159 integer, optional, intent(in) :: vertical_coordinate_number !< Index number for vertical coordinate
1160 character(len=*), optional, intent(in) :: x_cell_method !< A x-direction cell method used to construct the
1161 !! "cell_methods" attribute in CF convention
1162 character(len=*), optional, intent(in) :: y_cell_method !< A y-direction cell method used to construct the
1163 !! "cell_methods" attribute in CF convention
1164 character(len=*), optional, intent(in) :: v_cell_method !< A vertical direction cell method used to construct
1165 !! the "cell_methods" attribute in CF convention
1166 logical, optional, intent(in) :: is_h_point !< If true, indicates this axes group for h-point
1167 !! located fields
1168 logical, optional, intent(in) :: is_q_point !< If true, indicates this axes group for q-point
1169 !! located fields
1170 logical, optional, intent(in) :: is_u_point !< If true, indicates this axes group for
1171 !! u-point located fields
1172 logical, optional, intent(in) :: is_v_point !< If true, indicates this axes group for
1173 !! v-point located fields
1174 logical, optional, intent(in) :: is_layer !< If true, indicates that this axes group is
1175 !! for a layer vertically-located field.
1176 logical, optional, intent(in) :: is_interface !< If true, indicates that this axes group
1177 !! is for an interface vertically-located field.
1178 logical, optional, intent(in) :: is_native !< If true, indicates that this axes group is
1179 !! for a native model grid. False for any other grid.
1180 logical, optional, intent(in) :: needs_remapping !< If true, indicates that this axes group is
1181 !! for a intensive layer-located field that must
1182 !! be remapped to these axes. Used for rank>2.
1183 logical, optional, intent(in) :: needs_interpolating !< If true, indicates that this axes group
1184 !! is for a sampled interface-located field that must
1185 !! be interpolated to these axes. Used for rank>2.
1186 type(axes_grp), optional, target :: xyave_axes !< The corresponding axes group for horizontally
1187 !! area-average diagnostics
1188 ! Local variables
1189 integer :: n
1190
119120 n = size(handles)
119220 if (n<1 .or. n>3) call MOM_error(FATAL, "define_axes_group: wrong size for list of handles!")
119320 allocate( axes%handles(n) )
119420 axes%id = ints_to_string(handles, max(n,3)) ! Identifying string
119520 axes%rank = n
119676 axes%handles(:) = handles(:)
119720 axes%diag_cs => diag_cs ! A (circular) link back to the diag_cs structure
119820 if (present(x_cell_method)) then
119920 if (axes%rank<2) call MOM_error(FATAL, 'define_axes_group: ' // &
12000 'Can not set x_cell_method for rank<2.')
120120 axes%x_cell_method = trim(x_cell_method)
1202 else
12030 axes%x_cell_method = ''
1204 endif
120520 if (present(y_cell_method)) then
120620 if (axes%rank<2) call MOM_error(FATAL, 'define_axes_group: ' // &
12070 'Can not set y_cell_method for rank<2.')
120820 axes%y_cell_method = trim(y_cell_method)
1209 else
12100 axes%y_cell_method = ''
1211 endif
121220 if (present(v_cell_method)) then
121316 if (axes%rank/=1 .and. axes%rank/=3) call MOM_error(FATAL, 'define_axes_group: ' // &
12140 'Can not set v_cell_method for rank<>1 or 3.')
121516 axes%v_cell_method = trim(v_cell_method)
1216 else
12174 axes%v_cell_method = ''
1218 endif
121920 axes%downsample_level = dl
122020 if (present(nz)) axes%nz = nz
122120 if (present(vertical_coordinate_number)) axes%vertical_coordinate_number = vertical_coordinate_number
122220 if (present(is_h_point)) axes%is_h_point = is_h_point
122320 if (present(is_q_point)) axes%is_q_point = is_q_point
122420 if (present(is_u_point)) axes%is_u_point = is_u_point
122520 if (present(is_v_point)) axes%is_v_point = is_v_point
122620 if (present(is_layer)) axes%is_layer = is_layer
122720 if (present(is_interface)) axes%is_interface = is_interface
122820 if (present(is_native)) axes%is_native = is_native
122920 if (present(needs_remapping)) axes%needs_remapping = needs_remapping
123020 if (present(needs_interpolating)) axes%needs_interpolating = needs_interpolating
123120 if (present(xyave_axes)) axes%xyave_axes => xyave_axes
1232
1233 ! Setup masks for this axes group
1234
123520 axes%mask2d => null()
123620 if (axes%rank==2) then
12374 if (axes%is_h_point) axes%mask2d => diag_cs%mask2dT
12384 if (axes%is_u_point) axes%mask2d => diag_cs%mask2dCu
12394 if (axes%is_v_point) axes%mask2d => diag_cs%mask2dCv
12404 if (axes%is_q_point) axes%mask2d => diag_cs%mask2dBu
1241 endif
1242 ! A static 3d mask for non-native coordinates can only be setup when a grid is available
124320 axes%mask3d => null()
124420 if (axes%rank==3 .and. axes%is_native) then
1245 ! Native variables can/should use the native masks copied into diag_cs
12468 if (axes%is_layer) then
12474 if (axes%is_h_point) axes%mask3d => diag_cs%mask3dTL
12484 if (axes%is_u_point) axes%mask3d => diag_cs%mask3dCuL
12494 if (axes%is_v_point) axes%mask3d => diag_cs%mask3dCvL
12504 if (axes%is_q_point) axes%mask3d => diag_cs%mask3dBL
12514 elseif (axes%is_interface) then
12524 if (axes%is_h_point) axes%mask3d => diag_cs%mask3dTi
12534 if (axes%is_u_point) axes%mask3d => diag_cs%mask3dCui
12544 if (axes%is_v_point) axes%mask3d => diag_cs%mask3dCvi
12554 if (axes%is_q_point) axes%mask3d => diag_cs%mask3dBi
1256 endif
1257 endif
1258
125920 axes%dsamp(dl)%mask2d => null()
126020 if (axes%rank==2) then
12614 if (axes%is_h_point) axes%dsamp(dl)%mask2d => diag_cs%dsamp(dl)%mask2dT
12624 if (axes%is_u_point) axes%dsamp(dl)%mask2d => diag_cs%dsamp(dl)%mask2dCu
12634 if (axes%is_v_point) axes%dsamp(dl)%mask2d => diag_cs%dsamp(dl)%mask2dCv
12644 if (axes%is_q_point) axes%dsamp(dl)%mask2d => diag_cs%dsamp(dl)%mask2dBu
1265 endif
1266 ! A static 3d mask for non-native coordinates can only be setup when a grid is available
126720 axes%dsamp(dl)%mask3d => null()
126820 if (axes%rank==3 .and. axes%is_native) then
1269 ! Native variables can/should use the native masks copied into diag_cs
12708 if (axes%is_layer) then
12714 if (axes%is_h_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dTL
12724 if (axes%is_u_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dCuL
12734 if (axes%is_v_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dCvL
12744 if (axes%is_q_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dBL
12754 elseif (axes%is_interface) then
12764 if (axes%is_h_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dTi
12774 if (axes%is_u_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dCui
12784 if (axes%is_v_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dCvi
12794 if (axes%is_q_point) axes%dsamp(dl)%mask3d => diag_cs%dsamp(dl)%mask3dBi
1280 endif
1281 endif
1282
128320end subroutine define_axes_group_dsamp
1284
1285!> Set up the array extents for doing diagnostics
12860subroutine set_diag_mediator_grid(G, diag_cs)
1287 type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure
1288 type(diag_ctrl), intent(inout) :: diag_CS !< Structure used to regulate diagnostic output
1289
12900 diag_cs%is = G%isc - (G%isd-1) ; diag_cs%ie = G%iec - (G%isd-1)
12910 diag_cs%js = G%jsc - (G%jsd-1) ; diag_cs%je = G%jec - (G%jsd-1)
12920 diag_cs%isd = G%isd ; diag_cs%ied = G%ied
12930 diag_cs%jsd = G%jsd ; diag_cs%jed = G%jed
1294
12950end subroutine set_diag_mediator_grid
1296
1297!> Make a real scalar diagnostic available for averaging or output
12980subroutine post_data_0d(diag_field_id, field, diag_cs, is_static)
1299 integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
1300 !! previous call to register_diag_field.
1301 real, intent(in) :: field !< real value being offered for output or averaging
1302 !! in internally scaled arbitrary units [A ~> a]
1303 type(diag_ctrl), target, intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1304 logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1305
1306 ! Local variables
1307 real :: locfield ! The field being offered in arbitrary unscaled units [a]
1308 logical :: used, is_stat
1309 type(diag_type), pointer :: diag => null()
1310
1311 integer :: time_days
1312 integer :: time_seconds
1313 character(len=300) :: debug_mesg
1314
13150 if (id_clock_diag_mediator>0) call cpu_clock_begin(id_clock_diag_mediator)
13160 is_stat = .false. ; if (present(is_static)) is_stat = is_static
1317
1318 ! Iterate over list of diag 'variants', e.g. CMOR aliases, call send_data
1319 ! for each one.
1320 call assert(diag_field_id < diag_cs%next_free_diag_id, &
13210 'post_data_0d: Unregistered diagnostic id')
13220 diag => diag_cs%diags(diag_field_id)
1323
13240 do while (associated(diag))
13250 locfield = field
13260 if (diag%conversion_factor /= 0.) &
13270 locfield = locfield * diag%conversion_factor
1328
13290 if (diag_cs%diag_as_chksum) then
1330 ! Append timestep to mesg
13310 call get_time(diag_cs%time_end, time_seconds, days=time_days)
1332 write(debug_mesg, '(a, 1x, i0, 1x, i0)') &
13330 trim(diag%debug_str), time_days, time_seconds
1334
13350 call chksum0(locfield, debug_mesg, logunit=diag_cs%chksum_iounit)
13360 elseif (is_stat) then
13370 used = send_data_infra(diag%fms_diag_id, locfield)
13380 elseif (diag_cs%ave_enabled) then
13390 used = send_data_infra(diag%fms_diag_id, locfield, diag_cs%time_end)
1340 endif
13410 diag => diag%next
1342 enddo
1343
13440 if (id_clock_diag_mediator>0) call cpu_clock_end(id_clock_diag_mediator)
13450end subroutine post_data_0d
1346
1347!> Make a real 1-d array diagnostic available for averaging or output
13480subroutine post_data_1d_k(diag_field_id, field, diag_cs, is_static)
1349 integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
1350 !! previous call to register_diag_field.
1351 real, target, intent(in) :: field(:) !< 1-d array being offered for output or averaging
1352 !! in internally scaled arbitrary units [A ~> a]
1353 type(diag_ctrl), target, intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1354 logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1355
1356 ! Local variables
1357 logical :: used ! The return value of send_data is not used for anything.
1358 real, dimension(:), pointer :: locfield => NULL() ! The field being offered in arbitrary unscaled units [a]
1359 logical :: is_stat
1360 integer :: k, ks, ke
1361 type(diag_type), pointer :: diag => null()
1362
1363 integer :: time_days
1364 integer :: time_seconds
1365 character(len=300) :: debug_mesg
1366
13670 if (id_clock_diag_mediator>0) call cpu_clock_begin(id_clock_diag_mediator)
13680 is_stat = .false. ; if (present(is_static)) is_stat = is_static
1369
1370 ! Iterate over list of diag 'variants', e.g. CMOR aliases.
1371 call assert(diag_field_id < diag_cs%next_free_diag_id, &
13720 'post_data_1d_k: Unregistered diagnostic id')
13730 diag => diag_cs%diags(diag_field_id)
13740 do while (associated(diag))
1375
13760 if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) then
13770 ks = lbound(field,1) ; ke = ubound(field,1)
13780 allocate( locfield( ks:ke ) )
1379
13800 do k=ks,ke
13810 locfield(k) = field(k) * diag%conversion_factor
1382 enddo
1383 else
13840 locfield => field
1385 endif
1386
13870 if (diag_cs%diag_as_chksum) then
1388 ! Append timestep to mesg
13890 call get_time(diag_cs%time_end, time_seconds, days=time_days)
1390 write(debug_mesg, '(a, 1x, i0, 1x, i0)') &
13910 trim(diag%debug_str), time_days, time_seconds
1392
13930 call zchksum(locfield, debug_mesg, logunit=diag_cs%chksum_iounit)
13940 elseif (is_stat) then
13950 used = send_data_infra(diag%fms_diag_id, locfield)
13960 elseif (diag_cs%ave_enabled) then
13970 used = send_data_infra(diag%fms_diag_id, locfield, time=diag_cs%time_end, weight=diag_cs%time_int)
1398 endif
13990 if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) deallocate( locfield )
1400
14010 diag => diag%next
1402 enddo
1403
14040 if (id_clock_diag_mediator>0) call cpu_clock_end(id_clock_diag_mediator)
14050end subroutine post_data_1d_k
1406
1407!> Make a real 2-d array diagnostic available for averaging or output
1408122subroutine post_data_2d(diag_field_id, field, diag_cs, is_static, mask)
1409 integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
1410 !! previous call to register_diag_field.
1411 real, intent(in) :: field(:,:) !< 2-d array being offered for output or averaging
1412 !! in internally scaled arbitrary units [A ~> a]
1413 type(diag_ctrl), target, intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1414 logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1415 real, optional, intent(in) :: mask(:,:) !< If present, use this real array as the data mask [nondim]
1416
1417 ! Local variables
1418 type(diag_type), pointer :: diag => null()
1419
1420122 if (id_clock_diag_mediator>0) call cpu_clock_begin(id_clock_diag_mediator)
1421
1422 ! Iterate over list of diag 'variants' (e.g. CMOR aliases) and post each.
1423 call assert(diag_field_id < diag_cs%next_free_diag_id, &
1424122 'post_data_2d: Unregistered diagnostic id')
1425122 diag => diag_cs%diags(diag_field_id)
1426244 do while (associated(diag))
1427122 call post_data_2d_low(diag, field, diag_cs, is_static, mask)
1428122 diag => diag%next
1429 enddo
1430
1431122 if (id_clock_diag_mediator>0) call cpu_clock_end(id_clock_diag_mediator)
1432122end subroutine post_data_2d
1433
1434!> Make a real 2-d array diagnostic available for averaging or output
1435!! using a diag_type instead of an integer id.
1436122subroutine post_data_2d_low(diag, field, diag_cs, is_static, mask)
1437 type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
1438 real, target, intent(in) :: field(:,:) !< 2-d array being offered for output or averaging
1439 !! in internally scaled arbitrary units [A ~> a]
1440 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1441 logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1442 real, optional, target, intent(in) :: mask(:,:) !< If present, use this real array as the data mask [nondim]
1443
1444 ! Local variables
1445122 real, dimension(:,:), pointer :: locfield ! The field being offered in arbitrary unscaled units [a]
1446122 real, dimension(:,:), pointer :: locmask ! A pointer to the data mask to use [nondim]
1447 logical :: used ! The return value of send_data is not used for anything.
1448 logical :: is_stat, not_static
1449 integer :: cszi, cszj, dszi, dszj
1450 integer :: isv, iev, jsv, jev, i, j, isv_o, jsv_o
1451122 real, dimension(:,:), allocatable, target :: locfield_dsamp ! A downsampled version of locfield [a]
1452122 real, dimension(:,:), allocatable, target :: locmask_dsamp ! A downsampled version of locmask [nondim]
1453 integer :: dl
1454 integer :: time_days
1455 integer :: time_seconds
1456 character(len=300) :: mesg
1457 character(len=300) :: debug_mesg
1458
1459122 locfield => NULL()
1460122 locmask => NULL()
146126 is_stat = .false. ; if (present(is_static)) is_stat = is_static
1462122 not_static = .not. is_stat
1463
1464 ! Determine the proper array indices, noting that because of the (:,:)
1465 ! declaration of field, symmetric arrays are using a SW-grid indexing,
1466 ! but non-symmetric arrays are using a NE-grid indexing. Send_data
1467 ! actually only uses the difference between ie and is to determine
1468 ! the output data size and assumes that halos are symmetric.
1469122 isv = diag_cs%is ; iev = diag_cs%ie ; jsv = diag_cs%js ; jev = diag_cs%je
1470
1471122 cszi = diag_cs%ie-diag_cs%is +1 ; dszi = diag_cs%ied-diag_cs%isd +1
1472122 cszj = diag_cs%je-diag_cs%js +1 ; dszj = diag_cs%jed-diag_cs%jsd +1
1473122 if ( size(field,1) == dszi ) then
147487 isv = diag_cs%is ; iev = diag_cs%ie ! Data domain
147535 elseif ( size(field,1) == dszi + 1 ) then
147635 isv = diag_cs%is ; iev = diag_cs%ie+1 ! Symmetric data domain
14770 elseif ( size(field,1) == cszi) then
14780 isv = 1 ; iev = cszi ! Computational domain
14790 elseif ( size(field,1) == cszi + 1 ) then
14800 isv = 1 ; iev = cszi+1 ! Symmetric computational domain
1481 else
14820 write (mesg,*) " peculiar size ",size(field,1)," in i-direction\n"//&
14830 "does not match one of ", cszi, cszi+1, dszi, dszi+1
14840 call MOM_error(FATAL,"post_data_2d_low: "//trim(diag%debug_str)//trim(mesg))
1485 endif
1486
1487122 if ( size(field,2) == dszj ) then
148887 jsv = diag_cs%js ; jev = diag_cs%je ! Data domain
148935 elseif ( size(field,2) == dszj + 1 ) then
149035 jsv = diag_cs%js ; jev = diag_cs%je+1 ! Symmetric data domain
14910 elseif ( size(field,2) == cszj ) then
14920 jsv = 1 ; jev = cszj ! Computational domain
14930 elseif ( size(field,2) == cszj+1 ) then
14940 jsv = 1 ; jev = cszj+1 ! Symmetric computational domain
1495 else
14960 write (mesg,*) " peculiar size ",size(field,2)," in j-direction\n"//&
14970 "does not match one of ", cszj, cszj+1, dszj, dszj+1
14980 call MOM_error(FATAL,"post_data_2d_low: "//trim(diag%debug_str)//trim(mesg))
1499 endif
1500
1501122 if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) then
15021 allocate( locfield( lbound(field,1):ubound(field,1), lbound(field,2):ubound(field,2) ) )
15037261 do j=jsv,jev ; do i=isv,iev
15047260 locfield(i,j) = field(i,j) * diag%conversion_factor
1505 enddo ; enddo
1506 else
1507121 locfield => field
1508 endif
1509
1510122 if (present(mask)) then
15111 locmask => mask
1512121 elseif (not_static .and. associated(diag%axes)) then
1513 ! If we were to decide to allow masking of static diagnostics, we could do so by changing the line above to
1514 ! elseif (associated(diag%axes) .and. (diag_CS%mask_static_diags .or. not_static)) then
151596 if (associated(diag%axes%mask2d)) locmask => diag%axes%mask2d
1516 endif
1517
1518122 dl = 1
1519122 if (not_static .and. associated(diag%axes)) &
152096 dl = diag%axes%downsample_level ! Static field downsampling is not supported yet.
1521 ! Downsample the diag field and mask as appropriate.
1522122 if (dl > 1) then
15230 isv_o = isv ; jsv_o = jsv
15240 call downsample_diag_field(locfield, locfield_dsamp, dl, diag_cs, diag, isv, iev, jsv, jev, mask)
15250 if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) deallocate( locfield )
15260 locfield => locfield_dsamp
15270 if (present(mask)) then
1528 call downsample_field_2d(locmask, locmask_dsamp, dl, MSK, locmask, diag_cs, diag, &
15290 isv_o, jsv_o, isv, iev, jsv, jev)
15300 locmask => locmask_dsamp
15310 elseif (associated(diag%axes%dsamp(dl)%mask2d)) then
15320 locmask => diag%axes%dsamp(dl)%mask2d
1533 endif
1534 endif
1535122 if (associated(locmask)) call assert(size(locfield) == size(locmask), &
1536485 'post_data_2d_low: mask size mismatch: '//trim(diag%debug_str))
1537
1538122 if (diag_cs%diag_as_chksum) then
1539 ! Append timestep to mesg
15400 call get_time(diag_cs%time_end, time_seconds, days=time_days)
1541 write(debug_mesg, '(a, 1x, i0, 1x, i0)') &
15420 trim(diag%debug_str), time_days, time_seconds
1543
15440 if (diag%axes%is_h_point) then
1545 call hchksum(locfield, debug_mesg, diag_cs%G%HI, &
15460 logunit=diag_cs%chksum_iounit)
15470 elseif (diag%axes%is_u_point) then
1548 call uchksum(locfield, debug_mesg, diag_cs%G%HI, &
15490 logunit=diag_cs%chksum_iounit)
15500 elseif (diag%axes%is_v_point) then
1551 call vchksum(locfield, debug_mesg, diag_cs%G%HI, &
15520 logunit=diag_cs%chksum_iounit)
15530 elseif (diag%axes%is_q_point) then
1554 call Bchksum(locfield, debug_mesg, diag_cs%G%HI, &
15550 logunit=diag_cs%chksum_iounit)
1556 else
15570 call MOM_error(FATAL, "post_data_2d_low: unknown axis type.")
1558 endif
1559 else
1560122 if (is_stat) then
156126 if (associated(locmask)) then
1562 used = send_data_infra(diag%fms_diag_id, locfield, &
15631 is_in=isv, ie_in=iev, js_in=jsv, je_in=jev, rmask=locmask)
1564 else
1565 used = send_data_infra(diag%fms_diag_id, locfield, &
156625 is_in=isv, ie_in=iev, js_in=jsv, je_in=jev)
1567 endif
156896 elseif (diag_cs%ave_enabled) then
156996 if (associated(locmask)) then
1570 used = send_data_infra(diag%fms_diag_id, locfield, &
1571 is_in=isv, ie_in=iev, js_in=jsv, je_in=jev, &
157296 time=diag_cs%time_end, weight=diag_cs%time_int, rmask=locmask)
1573 else
1574 used = send_data_infra(diag%fms_diag_id, locfield, &
1575 is_in=isv, ie_in=iev, js_in=jsv, je_in=jev, &
15760 time=diag_cs%time_end, weight=diag_cs%time_int)
1577 endif
1578 endif
1579 endif
1580122 if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.) .and. dl<2) &
15811 deallocate( locfield )
1582244end subroutine post_data_2d_low
1583
1584!> Make a real 3-d array diagnostic available for averaging or output.
158572subroutine post_data_3d(diag_field_id, field, diag_cs, is_static, mask, alt_h)
1586
1587 integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
1588 !! previous call to register_diag_field.
1589 real, intent(in) :: field(:,:,:) !< 3-d array being offered for output or averaging
1590 !! in internally scaled arbitrary units [A ~> a]
1591 type(diag_ctrl), target, intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1592 logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1593 real, optional, intent(in) :: mask(:,:,:) !< If present, use this real array as the data mask [nondim]
1594 real, dimension(:,:,:), &
1595 target, optional, intent(in) :: alt_h !< An alternate thickness to use for vertically
1596 !! remapping this diagnostic [H ~> m or kg m-2].
1597
1598 ! Local variables
1599 type(diag_type), pointer :: diag => null()
160072 real, dimension(:,:,:), allocatable :: remapped_field !< The vertically remapped diagnostic [A ~> a]
1601 logical :: staggered_in_x, staggered_in_y, dz_diag_needed, dz_begin_needed
1602 real, dimension(:,:,:), pointer :: h_diag => NULL() !< A pointer to the thickness to use for vertically
1603 !! remapping this diagnostic [H ~> m or kg m-2].
1604
1605 real, dimension(diag_cs%G%isd:diag_cS%G%ied, diag_cs%G%jsd:diag_cS%G%jed, diag_cs%GV%ke) :: &
1606144 dz_diag ! Layer vertical extents for remapping [Z ~> m]
1607
160872 if (id_clock_diag_mediator>0) call cpu_clock_begin(id_clock_diag_mediator)
1609
1610 ! For intensive variables only, we can choose to use a different diagnostic grid to map to
161172 if (present(alt_h)) then
16120 h_diag => alt_h
1613 else
161472 h_diag => diag_cs%h
1615 endif
1616
1617 ! Iterate over list of diag 'variants', e.g. CMOR aliases, different vertical
1618 ! grids, and post each.
1619 call assert(diag_field_id < diag_cs%next_free_diag_id, &
162072 'post_data_3d: Unregistered diagnostic id')
1621
162272 if (diag_cs%show_call_tree) &
16230 call callTree_enter("post_data_3d("//trim(diag_cs%diags(diag_field_id)%debug_str)//")")
1624
1625 ! Find out whether there are any z-based diagnostics
162672 diag => diag_cs%diags(diag_field_id)
162772 dz_diag_needed = .false.
1628144 do while (associated(diag))
162972 if (diag%axes%needs_remapping .or. diag%axes%needs_interpolating) then
163072 if (diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%Z_based_coord) &
16310 dz_diag_needed = .true.
1632 endif
163372 diag => diag%next
1634 enddo
1635
1636 ! Determine the diagnostic grid spacing in height units, if it is needed.
163772 if (dz_diag_needed) then
16380 call thickness_to_dz(h_diag, diag_cs%tv, dz_diag, diag_cs%G, diag_cs%GV, diag_cs%US, halo_size=1)
1639 endif
1640
164172 diag => diag_cs%diags(diag_field_id)
1642144 do while (associated(diag))
164372 call assert(associated(diag%axes), 'post_data_3d: axes is not associated')
1644
164572 staggered_in_x = diag%axes%is_u_point .or. diag%axes%is_q_point
164672 staggered_in_y = diag%axes%is_v_point .or. diag%axes%is_q_point
1647
164872 if (diag%v_extensive .and. .not.diag%axes%is_native) then
1649 ! The field is vertically integrated and needs to be re-gridded
16500 if (present(mask)) then
16510 call MOM_error(FATAL,"post_data_3d: no mask for regridded field.")
1652 endif
1653
16540 if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
16550 allocate(remapped_field(size(field,1), size(field,2), diag%axes%nz))
16560 if (diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%Z_based_coord) then
1657 call vertically_reintegrate_diag_field( &
1658 diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), diag_cs%G, &
1659 diag_cs%dz_begin, diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%h_extensive, &
16600 staggered_in_x, staggered_in_y, diag%axes%mask3d, field, remapped_field)
1661 else
1662 call vertically_reintegrate_diag_field( &
1663 diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), diag_cs%G, &
1664 diag_cs%h_begin, diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%h_extensive, &
16650 staggered_in_x, staggered_in_y, diag%axes%mask3d, field, remapped_field)
1666 endif
16670 if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
16680 if (associated(diag%axes%mask3d)) then
1669 ! Since 3d masks do not vary in the vertical, just use as much as is
1670 ! needed.
1671 call post_data_3d_low(diag, remapped_field, diag_cs, is_static, &
16720 mask=diag%axes%mask3d)
1673 else
16740 call post_data_3d_low(diag, remapped_field, diag_cs, is_static)
1675 endif
16760 if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
16770 deallocate(remapped_field)
16780 if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
167972 elseif (diag%axes%needs_remapping) then
1680 ! Remap this field to another vertical coordinate.
168160 if (present(mask)) then
16820 call MOM_error(FATAL,"post_data_3d: no mask for regridded field.")
1683 endif
1684
168560 if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
168660 allocate(remapped_field(size(field,1), size(field,2), diag%axes%nz))
168760 if (diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%Z_based_coord) then
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, dz_diag, staggered_in_x, staggered_in_y, &
16900 diag%axes%mask3d, field, remapped_field)
1691 else
1692 call diag_remap_do_remap(diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), &
1693 diag_cs%G, diag_cs%GV, diag_cs%US, h_diag, staggered_in_x, staggered_in_y, &
169460 diag%axes%mask3d, field, remapped_field)
1695 endif
169660 if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
169760 if (associated(diag%axes%mask3d)) then
1698 ! Since 3d masks do not vary in the vertical, just use as much as is
1699 ! needed.
1700 call post_data_3d_low(diag, remapped_field, diag_cs, is_static, &
170160 mask=diag%axes%mask3d)
1702 else
17030 call post_data_3d_low(diag, remapped_field, diag_cs, is_static)
1704 endif
170560 if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
170660 deallocate(remapped_field)
170760 if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
170812 elseif (diag%axes%needs_interpolating) then
1709 ! Interpolate this field to another vertical coordinate.
171012 if (present(mask)) then
17110 call MOM_error(FATAL,"post_data_3d: no mask for regridded field.")
1712 endif
1713
171412 if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
171512 allocate(remapped_field(size(field,1), size(field,2), diag%axes%nz+1))
171612 if (diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%Z_based_coord) then
1717 call vertically_interpolate_diag_field(diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), &
1718 diag_cs%G, dz_diag, staggered_in_x, staggered_in_y, &
17190 diag%axes%mask3d, field, remapped_field)
1720 else
1721 call vertically_interpolate_diag_field(diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), &
1722 diag_cs%G, h_diag, staggered_in_x, staggered_in_y, &
172312 diag%axes%mask3d, field, remapped_field)
1724 endif
172512 if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
172612 if (associated(diag%axes%mask3d)) then
1727 ! Since 3d masks do not vary in the vertical, just use as much as is needed.
1728 call post_data_3d_low(diag, remapped_field, diag_cs, is_static, &
172912 mask=diag%axes%mask3d)
1730 else
17310 call post_data_3d_low(diag, remapped_field, diag_cs, is_static)
1732 endif
173312 if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
173412 deallocate(remapped_field)
173512 if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
1736 else
17370 call post_data_3d_low(diag, field, diag_cs, is_static, mask)
1738 endif
173972 diag => diag%next
1740 enddo
174172 if (id_clock_diag_mediator>0) call cpu_clock_end(id_clock_diag_mediator)
1742
174372 if (diag_cs%show_call_tree) &
17440 call callTree_leave("post_data_3d("//trim(diag_cs%diags(diag_field_id)%debug_str)//")")
1745
174672end subroutine post_data_3d
1747
1748!> Make a real 3-d array diagnostic available for averaging or output
1749!! using a diag_type instead of an integer id.
175072subroutine post_data_3d_low(diag, field, diag_cs, is_static, mask)
1751 type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
1752 real, target, intent(in) :: field(:,:,:) !< 3-d array being offered for output or averaging
1753 !! in internally scaled arbitrary units [A ~> a]
1754 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1755 logical, optional, intent(in) :: is_static !< If true, this is a static field that is always offered.
1756 real, optional,target, intent(in) :: mask(:,:,:) !< If present, use this real array as the data mask [nondim]
1757
1758 ! Local variables
175972 real, dimension(:,:,:), pointer :: locfield ! The field being offered in arbitrary unscaled units [a]
176072 real, dimension(:,:,:), pointer :: locmask ! A pointer to the data mask to use [nondim]
1761 character(len=300) :: mesg
1762 logical :: used ! The return value of send_data is not used for anything.
1763 logical :: staggered_in_x, staggered_in_y
1764 logical :: is_stat, not_static
1765 integer :: cszi, cszj, dszi, dszj
1766 integer :: isv, iev, jsv, jev, ks, ke, i, j, k, isv_c, jsv_c, isv_o, jsv_o
176772 real, dimension(:,:,:), allocatable, target :: locfield_dsamp ! A downsampled version of locfield [a]
176872 real, dimension(:,:,:), allocatable, target :: locmask_dsamp ! A downsampled version of locmask [nondim]
1769 integer :: dl
1770
1771 integer :: time_days
1772 integer :: time_seconds
1773 character(len=300) :: debug_mesg
1774
177572 locfield => NULL()
177672 locmask => NULL()
17770 is_stat = .false. ; if (present(is_static)) is_stat = is_static
177872 not_static = .not. is_stat
1779
1780 ! Determine the proper array indices, noting that because of the (:,:)
1781 ! declaration of field, symmetric arrays are using a SW-grid indexing,
1782 ! but non-symmetric arrays are using a NE-grid indexing. Send_data
1783 ! actually only uses the difference between ie and is to determine
1784 ! the output data size and assumes that halos are symmetric.
178572 isv = diag_cs%is ; iev = diag_cs%ie ; jsv = diag_cs%js ; jev = diag_cs%je
1786
178772 cszi = (diag_cs%ie-diag_cs%is) +1 ; dszi = (diag_cs%ied-diag_cs%isd) +1
178872 cszj = (diag_cs%je-diag_cs%js) +1 ; dszj = (diag_cs%jed-diag_cs%jsd) +1
178972 if ( size(field,1) == dszi ) then
179048 isv = diag_cs%is ; iev = diag_cs%ie ! Data domain
179124 elseif ( size(field,1) == dszi + 1 ) then
179224 isv = diag_cs%is ; iev = diag_cs%ie+1 ! Symmetric data domain
17930 elseif ( size(field,1) == cszi) then
17940 isv = 1 ; iev = cszi ! Computational domain
17950 elseif ( size(field,1) == cszi + 1 ) then
17960 isv = 1 ; iev = cszi+1 ! Symmetric computational domain
1797 else
17980 write (mesg,*) " peculiar size ",size(field,1)," in i-direction\n"//&
17990 "does not match one of ", cszi, cszi+1, dszi, dszi+1
18000 call MOM_error(FATAL,"post_data_3d_low: "//trim(diag%debug_str)//trim(mesg))
1801 endif
1802
180372 if ( size(field,2) == dszj ) then
180460 jsv = diag_cs%js ; jev = diag_cs%je ! Data domain
180512 elseif ( size(field,2) == dszj + 1 ) then
180612 jsv = diag_cs%js ; jev = diag_cs%je+1 ! Symmetric data domain
18070 elseif ( size(field,2) == cszj ) then
18080 jsv = 1 ; jev = cszj ! Computational domain
18090 elseif ( size(field,2) == cszj+1 ) then
18100 jsv = 1 ; jev = cszj+1 ! Symmetric computational domain
1811 else
18120 write (mesg,*) " peculiar size ",size(field,2)," in j-direction\n"//&
18130 "does not match one of ", cszj, cszj+1, dszj, dszj+1
18140 call MOM_error(FATAL,"post_data_3d_low: "//trim(diag%debug_str)//trim(mesg))
1815 endif
1816
181772 ks = lbound(field,3) ; ke = ubound(field,3)
181872 if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) then
18190 allocate( locfield( lbound(field,1):ubound(field,1), lbound(field,2):ubound(field,2), ks:ke ) )
1820 ! locfield(:,:,:) = 0.0 ! Zeroing out this array would be a good idea, but it appears
1821 ! not to be necessary.
18220 isv_c = isv ; jsv_c = jsv
18230 if (diag%fms_xyave_diag_id>0) then
18240 staggered_in_x = diag%axes%is_u_point .or. diag%axes%is_q_point
18250 staggered_in_y = diag%axes%is_v_point .or. diag%axes%is_q_point
1826 ! When averaging a staggered field, edge points are always required.
18270 if (staggered_in_x) isv_c = iev - (diag_cs%ie - diag_cs%is) - 1
18280 if (staggered_in_y) jsv_c = jev - (diag_cs%je - diag_cs%js) - 1
18290 if (isv_c < lbound(locfield,1)) call MOM_error(FATAL, &
1830 "It is an error to average a staggered diagnostic field that does not "//&
18310 "have i-direction space to represent the symmetric computational domain.")
18320 if (jsv_c < lbound(locfield,2)) call MOM_error(FATAL, &
1833 "It is an error to average a staggered diagnostic field that does not "//&
18340 "have j-direction space to represent the symmetric computational domain.")
1835 endif
1836
18370 do k=ks,ke ; do j=jsv,jev ; do i=isv,iev
18380 locfield(i,j,k) = field(i,j,k) * diag%conversion_factor
1839 enddo ; enddo ; enddo
1840 else
184172 locfield => field
1842 endif
1843
184472 if (present(mask)) then
184572 locmask => mask
18460 elseif (associated(diag%axes) .and. (not_static)) then
1847 ! If we were to decide to allow masking of static diagnostics, we could do so by changing the line above to
1848 ! elseif (associated(diag%axes) .and. (diag_CS%mask_static_diags .or. not_static)) then
18490 if (associated(diag%axes%mask3d)) locmask => diag%axes%mask3d
1850 endif
1851
185272 dl = 1
185372 if (not_static .and. associated(diag%axes)) &
185472 dl = diag%axes%downsample_level ! Static field downsampling is not supported yet.
1855 ! Downsample the diag field and mask as appropriate.
185672 if (dl > 1) then
18570 isv_o = isv ; jsv_o = jsv
18580 call downsample_diag_field(locfield, locfield_dsamp, dl, diag_cs, diag, isv, iev, jsv, jev, mask)
18590 if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.)) deallocate( locfield )
18600 locfield => locfield_dsamp
18610 if (present(mask)) then
1862 call downsample_field_3d(locmask, locmask_dsamp, dl, MSK, locmask, diag_cs, diag, &
18630 isv_o, jsv_o, isv, iev, jsv, jev)
18640 locmask => locmask_dsamp
18650 elseif (associated(diag%axes%dsamp(dl)%mask3d)) then
18660 locmask => diag%axes%dsamp(dl)%mask3d
1867 endif
1868 endif
186972 if (associated(locmask)) call assert(size(locfield) == size(locmask), &
1870504 'post_data_3d_low: mask size mismatch: '//trim(diag%debug_str))
1871
187272 if (diag%fms_diag_id>0) then
187372 if (diag_cs%diag_as_chksum) then
1874 ! Append timestep to mesg
18750 call get_time(diag_cs%time_end, time_seconds, days=time_days)
1876 write(debug_mesg, '(a, 1x, i0, 1x, i0)') &
18770 trim(diag%debug_str), time_days, time_seconds
1878
18790 if (diag%axes%is_h_point) then
1880 call hchksum(locfield, debug_mesg, diag_cs%G%HI, &
18810 logunit=diag_cs%chksum_iounit)
18820 elseif (diag%axes%is_u_point) then
1883 call uchksum(locfield, debug_mesg, diag_cs%G%HI, &
18840 logunit=diag_cs%chksum_iounit)
18850 elseif (diag%axes%is_v_point) then
1886 call vchksum(locfield, debug_mesg, diag_cs%G%HI, &
18870 logunit=diag_cs%chksum_iounit)
18880 elseif (diag%axes%is_q_point) then
1889 call Bchksum(locfield, debug_mesg, diag_cs%G%HI, &
18900 logunit=diag_cs%chksum_iounit)
1891 else
18920 call MOM_error(FATAL, "post_data_3d_low: unknown axis type.")
1893 endif
1894 else
189572 if (is_stat) then
18960 if (associated(locmask)) then
1897 used = send_data_infra(diag%fms_diag_id, locfield, &
18980 is_in=isv, ie_in=iev, js_in=jsv, je_in=jev, rmask=locmask)
1899 else
1900 used = send_data_infra(diag%fms_diag_id, locfield, &
19010 is_in=isv, ie_in=iev, js_in=jsv, je_in=jev)
1902 endif
190372 elseif (diag_cs%ave_enabled) then
190472 if (associated(locmask)) then
1905 used = send_data_infra(diag%fms_diag_id, locfield, &
1906 is_in=isv, ie_in=iev, js_in=jsv, je_in=jev, &
190772 time=diag_cs%time_end, weight=diag_cs%time_int, rmask=locmask)
1908 else
1909 used = send_data_infra(diag%fms_diag_id, locfield, &
1910 is_in=isv, ie_in=iev, js_in=jsv, je_in=jev, &
19110 time=diag_cs%time_end, weight=diag_cs%time_int)
1912 endif
1913 endif
1914 endif
1915 endif
1916
191772 if (diag%fms_xyave_diag_id>0) then
19180 call post_xy_average(diag_cs, diag, locfield)
1919 endif
1920
192172 if ((diag%conversion_factor /= 0.) .and. (diag%conversion_factor /= 1.) .and. dl<2) &
19220 deallocate( locfield )
1923
1924144end subroutine post_data_3d_low
1925
1926!> Put data into the buffer for a diagnostic one column at a time
19270subroutine post_data_3d_by_column(diag_field_id, field, diag_cs, i, j)
1928 integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
1929 !! previous call to register_diag_field.
1930 real, dimension(:), intent(in) :: field !< 3-d array being offered for output or averaging
1931 !! in internally scaled arbitrary units [A ~> a]
1932 type(diag_ctrl), target, intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1933 integer, intent(in) :: i !< The i-index to post the data in the buffer
1934 integer, intent(in) :: j !< The j-index to post the data in the buffer
1935
1936 type(diag_type), pointer :: diag => null()
1937 integer :: buffer_slot
1938
19390 diag => diag_cs%diags(diag_field_id)
19400 buffer_slot = diag%axes%piecemeal_3d%check_capacity_by_id(diag_field_id)
19410 diag%axes%piecemeal_3d%buffer(buffer_slot)%field(i,j,:) = field(:)
19420end subroutine post_data_3d_by_column
1943
1944!> Put data into the buffer for a diagnostic one point at a time
19450subroutine post_data_3d_by_point(diag_field_id, field, diag_cs, i, j, k)
1946 integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
1947 !! previous call to register_diag_field.
1948 real, intent(in) :: field !< 3-d array being offered for output or averaging
1949 !! in internally scaled arbitrary units [A ~> a]
1950 type(diag_ctrl), target, intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1951 integer, intent(in) :: i !< The i-index to post the data in the buffer
1952 integer, intent(in) :: j !< The j-index to post the data in the buffer
1953 integer, intent(in) :: k !< The k-index to post the data in the buffer
1954
1955 type(diag_type), pointer :: diag => null()
1956 integer :: buffer_slot
1957
19580 diag => diag_cs%diags(diag_field_id)
19590 buffer_slot = diag%axes%piecemeal_3d%check_capacity_by_id(diag_field_id)
19600 diag%axes%piecemeal_3d%buffer(buffer_slot)%field(i,j,k) = field
19610end subroutine post_data_3d_by_point
1962
1963!> Post the final buffer using the standard post_data interface
19640subroutine post_data_3d_final(diag_field_id, diag_cs)
1965 integer, intent(in) :: diag_field_id !< The id for an output variable returned by a
1966 !! previous call to register_diag_field.
1967 type(diag_ctrl), target, intent(in) :: diag_CS !< Structure used to regulate diagnostic output
1968
1969 type(diag_type), pointer :: diag => null()
1970 integer :: buffer_slot
1971
19720 diag => diag_cs%diags(diag_field_id)
19730 buffer_slot = diag%axes%piecemeal_3d%find_buffer_slot(diag_field_id)
1974 ! Only perform an action if the buffer slot was actually used
19750 if (buffer_slot>0) then
19760 call post_data(diag_field_id, diag%axes%piecemeal_3d%buffer(buffer_slot)%field(:,:,:), diag_CS)
19770 call diag%axes%piecemeal_3d%mark_available(diag_field_id)
1978 endif
19790end subroutine post_data_3d_final
1980
1981!> Calculate and write out diagnostics that are the product of two 3-d arrays at u-points
19820subroutine post_product_u(id, u_a, u_b, G, nz, diag, mask, alt_h)
1983 integer, intent(in) :: id !< The ID for this diagnostic
1984 type(ocean_grid_type), intent(in) :: G !< ocean grid structure
1985 integer, intent(in) :: nz !< The size of the arrays in the vertical
1986 real, dimension(G%IsdB:G%IedB, G%jsd:G%jed, nz), &
1987 intent(in) :: u_a !< The first u-point array in arbitrary units [A]
1988 real, dimension(G%IsdB:G%IedB, G%jsd:G%jed, nz), &
1989 intent(in) :: u_b !< The second u-point array in arbitrary units [B]
1990 type(diag_ctrl), intent(in) :: diag !< regulates diagnostic output
1991 real, optional, intent(in) :: mask(:,:,:) !< If present, use this real array as the data mask [nondim]
1992 real, target, optional, intent(in) :: alt_h(:,:,:) !< An alternate thickness to use for vertically
1993 !! remapping this diagnostic [H ~> m or kg m-2]
1994
1995 ! Local variables
19960 real, dimension(G%IsdB:G%IedB, G%jsd:G%jed, nz) :: u_prod ! The product of u_a and u_b [A B]
1997 integer :: i, j, k
1998
19990 if (id <= 0) return
2000
20010 do k=1,nz ; do j=G%jsc,G%jec ; do I=G%IscB,G%IecB
20020 u_prod(I,j,k) = u_a(I,j,k) * u_b(I,j,k)
2003 enddo ; enddo ; enddo
20040 call post_data(id, u_prod, diag, mask=mask, alt_h=alt_h)
2005
2006end subroutine post_product_u
2007
2008!> Calculate and write out diagnostics that are the vertical sum of the product of two 3-d arrays at u-points
20090subroutine post_product_sum_u(id, u_a, u_b, G, nz, diag)
2010 integer, intent(in) :: id !< The ID for this diagnostic
2011 type(ocean_grid_type), intent(in) :: G !< ocean grid structure
2012 integer, intent(in) :: nz !< The size of the arrays in the vertical
2013 real, dimension(G%IsdB:G%IedB, G%jsd:G%jed, nz), &
2014 intent(in) :: u_a !< The first u-point array in arbitrary units [A]
2015 real, dimension(G%IsdB:G%IedB, G%jsd:G%jed, nz), &
2016 intent(in) :: u_b !< The second u-point array in arbitrary units [B]
2017 type(diag_ctrl), intent(in) :: diag !< regulates diagnostic output
2018
20190 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]
2020 integer :: i, j, k
2021
20220 if (id <= 0) return
2023
20240 u_sum(:,:) = 0.0
20250 do k=1,nz ; do j=G%jsc,G%jec ; do I=G%IscB,G%IecB
20260 u_sum(I,j) = u_sum(I,j) + u_a(I,j,k) * u_b(I,j,k)
2027 enddo ; enddo ; enddo
20280 call post_data(id, u_sum, diag)
2029
2030end subroutine post_product_sum_u
2031
2032!> Calculate and write out diagnostics that are the product of two 3-d arrays at v-points
20330subroutine post_product_v(id, v_a, v_b, G, nz, diag, mask, alt_h)
2034 integer, intent(in) :: id !< The ID for this diagnostic
2035 type(ocean_grid_type), intent(in) :: G !< ocean grid structure
2036 integer, intent(in) :: nz !< The size of the arrays in the vertical
2037 real, dimension(G%isd:G%ied, G%JsdB:G%JedB, nz), &
2038 intent(in) :: v_a !< The first v-point array in arbitrary units [A]
2039 real, dimension(G%isd:G%ied, G%JsdB:G%JedB, nz), &
2040 intent(in) :: v_b !< The second v-point array in arbitrary units [B]
2041 type(diag_ctrl), intent(in) :: diag !< regulates diagnostic output
2042 real, optional, intent(in) :: mask(:,:,:) !< If present, use this real array as the data mask [nondim]
2043 real, target, optional, intent(in) :: alt_h(:,:,:) !< An alternate thickness to use for vertically
2044 !! remapping this diagnostic [H ~> m or kg m-2]
2045
2046 ! Local variables
20470 real, dimension(G%isd:G%ied, G%JsdB:G%JedB, nz) :: v_prod ! The product of v_a and v_b [A B]
2048 integer :: i, j, k
2049
20500 if (id <= 0) return
2051
20520 do k=1,nz ; do J=G%JscB,G%JecB ; do i=G%isc,G%iec
20530 v_prod(i,J,k) = v_a(i,J,k) * v_b(i,J,k)
2054 enddo ; enddo ; enddo
20550 call post_data(id, v_prod, diag, mask=mask, alt_h=alt_h)
2056
2057end subroutine post_product_v
2058
2059!> Calculate and write out diagnostics that are the vertical sum of the product of two 3-d arrays at v-points
20600subroutine post_product_sum_v(id, v_a, v_b, G, nz, diag)
2061 integer, intent(in) :: id !< The ID for this diagnostic
2062 type(ocean_grid_type), intent(in) :: G !< ocean grid structure
2063 integer, intent(in) :: nz !< The size of the arrays in the vertical
2064 real, dimension(G%isd:G%ied, G%JsdB:G%JedB, nz), &
2065 intent(in) :: v_a !< The first v-point array in arbitrary units [A]
2066 real, dimension(G%isd:G%ied, G%JsdB:G%JedB, nz), &
2067 intent(in) :: v_b !< The second v-point array in arbitrary units [B]
2068 type(diag_ctrl), intent(in) :: diag !< regulates diagnostic output
2069
20700 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]
2071 integer :: i, j, k
2072
20730 if (id <= 0) return
2074
20750 v_sum(:,:) = 0.0
20760 do k=1,nz ; do J=G%JscB,G%JecB ; do i=G%isc,G%iec
20770 v_sum(i,J) = v_sum(i,J) + v_a(i,J,k) * v_b(i,J,k)
2078 enddo ; enddo ; enddo
20790 call post_data(id, v_sum, diag)
2080
2081end subroutine post_product_sum_v
2082
2083!> Post the horizontally area-averaged diagnostic
20840subroutine post_xy_average(diag_cs, diag, field)
2085 type(diag_type), intent(in) :: diag !< This diagnostic
2086 real, target, intent(in) :: field(:,:,:) !< Diagnostic field in arbitrary units [A ~> a]
2087 type(diag_ctrl), intent(in) :: diag_cs !< Diagnostics mediator control structure
2088 ! Local variable
20890 real, dimension(size(field,3)) :: averaged_field ! The horizontally averaged field [A ~> a]
20900 logical, dimension(size(field,3)) :: averaged_mask
2091 logical :: staggered_in_x, staggered_in_y, used
2092 integer :: nz, remap_nz, coord
2093
2094 integer :: time_days
2095 integer :: time_seconds
2096 character(len=300) :: debug_mesg
2097
20980 if (.not. diag_cs%ave_enabled) then
20990 return
2100 endif
2101
21020 staggered_in_x = diag%axes%is_u_point .or. diag%axes%is_q_point
21030 staggered_in_y = diag%axes%is_v_point .or. diag%axes%is_q_point
2104
21050 if (diag%axes%is_native) then
2106 call horizontally_average_diag_field(diag_cs%G, diag_cs%GV, diag_cs%h, &
2107 staggered_in_x, staggered_in_y, &
2108 diag%axes%is_layer, diag%v_extensive, &
21090 field, averaged_field, averaged_mask)
2110 else
21110 nz = size(field, 3)
21120 coord = diag%axes%vertical_coordinate_number
21130 remap_nz = diag_cs%diag_remap_cs(coord)%nz
2114
2115 call assert(diag_cs%diag_remap_cs(coord)%initialized, &
21160 'post_xy_average: remap_cs not initialized.')
2117
2118 call assert(IMPLIES(diag%axes%is_layer, nz == remap_nz), &
21190 'post_xy_average: layer field dimension mismatch.')
2120 call assert(IMPLIES(.not. diag%axes%is_layer, nz == remap_nz+1), &
21210 'post_xy_average: interface field dimension mismatch.')
2122
2123 call horizontally_average_diag_field(diag_cs%G, diag_cs%GV, &
2124 diag_cs%diag_remap_cs(coord)%h, &
2125 staggered_in_x, staggered_in_y, &
2126 diag%axes%is_layer, diag%v_extensive, &
21270 field, averaged_field, averaged_mask)
2128 endif
2129
21300 if (diag_cs%diag_as_chksum) then
2131 ! Append timestep to mesg
21320 call get_time(diag_cs%time_end, time_seconds, days=time_days)
2133 write(debug_mesg, '(a, 1x, i0, 1x, i0)') &
21340 trim(diag%debug_str)//'_xyave', time_days, time_seconds
2135
21360 call zchksum(averaged_field, debug_mesg, logunit=diag_CS%chksum_iounit)
2137 else
2138 used = send_data_infra(diag%fms_xyave_diag_id, averaged_field, &
21390 time=diag_cs%time_end, weight=diag_cs%time_int, mask=averaged_mask)
2140 endif
2141end subroutine post_xy_average
2142
2143!> This subroutine enables the accumulation of time averages over the specified time interval.
21440subroutine enable_averaging(time_int_in, time_end_in, diag_cs)
2145 real, intent(in) :: time_int_in !< The time interval [s] over which any
2146 !! values that are offered are valid.
2147 type(time_type), intent(in) :: time_end_in !< The end time of the valid interval
2148 type(diag_ctrl), intent(inout) :: diag_CS !< Structure used to regulate diagnostic output
2149
2150! This subroutine enables the accumulation of time averages over the specified time interval.
2151
2152! if (num_file==0) return
21530 diag_cs%time_int = time_int_in
21540 diag_cs%time_end = time_end_in
21550 diag_cs%ave_enabled = .true.
21560end subroutine enable_averaging
2157
2158!> Enable the accumulation of time averages over the specified time interval in time units.
2159300subroutine enable_averages(time_int, time_end, diag_CS, T_to_s)
2160 real, intent(in) :: time_int !< The time interval over which any values
2161 !! that are offered are valid [T ~> s].
2162 type(time_type), intent(in) :: time_end !< The end time of the valid interval.
2163 type(diag_ctrl), intent(inout) :: diag_CS !< A structure that is used to regulate diagnostic output
2164 real, optional, intent(in) :: T_to_s !< A conversion factor for time_int to seconds [s T-1 ~> 1].
2165 ! This subroutine enables the accumulation of time averages over the specified time interval.
2166
2167300 if (present(T_to_s)) then
21680 diag_cs%time_int = time_int*T_to_s
2169300 elseif (associated(diag_CS%US)) then
2170300 diag_cs%time_int = time_int*diag_CS%US%T_to_s
2171 else
21720 diag_cs%time_int = time_int
2173 endif
2174300 diag_cs%time_end = time_end
2175300 diag_cs%ave_enabled = .true.
2176300end subroutine enable_averages
2177
2178!> Call this subroutine to avoid averaging any offered fields.
2179312subroutine disable_averaging(diag_cs)
2180 type(diag_ctrl), intent(inout) :: diag_CS !< Structure used to regulate diagnostic output
2181
2182312 diag_cs%time_int = 0.0
2183312 diag_cs%ave_enabled = .false.
2184312end subroutine disable_averaging
2185
2186!> Call this subroutine to determine whether the averaging is
2187!! currently enabled. .true. is returned if it is.
2188409function query_averaging_enabled(diag_cs, time_int, time_end)
2189 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
2190 real, optional, intent(out) :: time_int !< Current setting of diag%time_int [s]
2191 type(time_type), optional, intent(out) :: time_end !< Current setting of diag%time_end
2192 logical :: query_averaging_enabled
2193
2194409 if (present(time_int)) time_int = diag_cs%time_int
2195409 if (present(time_end)) time_end = diag_cs%time_end
2196409 query_averaging_enabled = diag_cs%ave_enabled
2197409end function query_averaging_enabled
2198
2199!> This function returns the valid end time for use with diagnostics that are
2200!! handled outside of the MOM6 diagnostics infrastructure.
220112function get_diag_time_end(diag_cs)
2202 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
2203 type(time_type) :: get_diag_time_end
2204 ! This function returns the valid end time for diagnostics that are handled
2205 ! outside of the MOM6 infrastructure, such as via the generic tracer code.
2206
220712 get_diag_time_end = diag_cs%time_end
220812end function get_diag_time_end
2209
2210!> Returns the "diag_mediator" handle for a group (native, CMOR, z-coord, ...) of diagnostics
2211!! derived from one field.
2212656integer function register_diag_field(module_name, field_name, axes_in, init_time, &
2213 long_name, units, missing_value, range, mask_variant, standard_name, &
2214 verbose, do_not_log, err_msg, interp_method, tile_count, cmor_field_name, &
2215 cmor_long_name, cmor_units, cmor_standard_name, cell_methods, &
2216 x_cell_method, y_cell_method, v_cell_method, conversion, v_extensive)
2217 character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model"
2218 !! or "ice_shelf_model"
2219 character(len=*), intent(in) :: field_name !< Name of the diagnostic field
2220 type(axes_grp), target, intent(in) :: axes_in !< Container w/ up to 3 integer handles that
2221 !! indicates axes for this field
2222 type(time_type), intent(in) :: init_time !< Time at which a field is first available?
2223 character(len=*), optional, intent(in) :: long_name !< Long name of a field.
2224 character(len=*), optional, intent(in) :: units !< Units of a field.
2225 character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
2226 real, optional, intent(in) :: missing_value !< A value that indicates missing values in
2227 !! output files, in unscaled arbitrary units [a]
2228 real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
2229 !! in arbitrary units [a]
2230 logical, optional, intent(in) :: mask_variant !< If true a logical mask must be provided with
2231 !! post_data calls (not used in MOM?)
2232 logical, optional, intent(in) :: verbose !< If true, FMS is verbose (not used in MOM?)
2233 logical, optional, intent(in) :: do_not_log !< If true, do not log something (not used in MOM?)
2234 character(len=*), optional, intent(out):: err_msg !< String into which an error message might be
2235 !! placed (not used in MOM?)
2236 character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should not
2237 !! be interpolated as a scalar
2238 integer, optional, intent(in) :: tile_count !< no clue (not used in MOM?)
2239 character(len=*), optional, intent(in) :: cmor_field_name !< CMOR name of a field
2240 character(len=*), optional, intent(in) :: cmor_long_name !< CMOR long name of a field
2241 character(len=*), optional, intent(in) :: cmor_units !< CMOR units of a field
2242 character(len=*), optional, intent(in) :: cmor_standard_name !< CMOR standardized name associated with a field
2243 character(len=*), optional, intent(in) :: cell_methods !< String to append as cell_methods attribute. Use '' to
2244 !! have no attribute. If present, this overrides the
2245 !! default constructed from the default for
2246 !! each individual axis direction.
2247 character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction.
2248 !! Use '' have no method.
2249 character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction.
2250 !! Use '' have no method.
2251 character(len=*), optional, intent(in) :: v_cell_method !< Specifies the cell method for the vertical direction.
2252 !! Use '' have no method.
2253 real, optional, intent(in) :: conversion !< A value to multiply data by before writing to files,
2254 !! often including factors to undo internal scaling and
2255 !! in units of [a A-1 ~> 1]
2256 logical, optional, intent(in) :: v_extensive !< True for vertically extensive fields (vertically
2257 !! integrated). Default/absent for intensive.
2258 ! Local variables
2259 real :: MOM_missing_value ! A value used to indicate missing values in output files, in arbitrary units [a]
2260 type(diag_ctrl), pointer :: diag_cs => NULL() ! A structure that is used to regulate diagnostic output
2261 type(axes_grp), pointer :: remap_axes
2262 type(axes_grp), pointer :: axes
2263 type(axes_grp), pointer :: axes_d2
2264 integer :: dm_id, i, dl
2265 character(len=256) :: msg, cm_string
2266 character(len=256) :: new_module_name
2267 character(len=480) :: module_list, var_list
2268 character(len=24) :: dimensions
2269 integer :: num_modnm, num_varnm
2270 logical :: active
2271
2272656 diag_cs => axes_in%diag_cs
2273
2274 ! Check if the axes match a standard grid axis.
2275 ! If not, allocate the new axis and copy the contents.
2276656 if (axes_in%id == diag_cs%axesTL%id) then
2277141 axes => diag_cs%axesTL
2278515 elseif (axes_in%id == diag_cs%axesBL%id) then
22798 axes => diag_cs%axesBL
2280507 elseif (axes_in%id == diag_cs%axesCuL%id) then
228157 axes => diag_cs%axesCuL
2282450 elseif (axes_in%id == diag_cs%axesCvL%id) then
228357 axes => diag_cs%axesCvL
2284393 elseif (axes_in%id == diag_cs%axesTi%id) then
228541 axes => diag_cs%axesTi
2286352 elseif (axes_in%id == diag_cs%axesBi%id) then
22876 axes => diag_cs%axesBi
2288346 elseif (axes_in%id == diag_cs%axesCui%id) then
22898 axes => diag_cs%axesCui
2290338 elseif (axes_in%id == diag_cs%axesCvi%id) then
22918 axes => diag_cs%axesCvi
2292330 elseif (axes_in%id == diag_cs%axesT1%id) then
2293207 axes => diag_cs%axesT1
2294123 elseif (axes_in%id == diag_cs%axesB1%id) then
22951 axes => diag_cs%axesB1
2296122 elseif (axes_in%id == diag_cs%axesCu1%id) then
229759 axes => diag_cs%axesCu1
229863 elseif (axes_in%id == diag_cs%axesCv1%id) then
229959 axes => diag_cs%axesCv1
2300 else
23018 allocate(axes)
23024 axes = axes_in
2303 endif
2304
2305656 MOM_missing_value = axes%diag_cs%missing_value
2306656 if (present(missing_value)) MOM_missing_value = missing_value
2307
2308656 diag_cs => axes%diag_cs
2309656 dm_id = -1
2310
2311656 module_list = "{"//trim(module_name)
2312656 num_modnm = 1
2313
2314 ! Register the native diagnostic
2315 active = register_diag_field_expand_cmor(dm_id, module_name, field_name, axes, &
2316 init_time, long_name=long_name, units=units, missing_value=MOM_missing_value, &
2317 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2318 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2319 interp_method=interp_method, tile_count=tile_count, &
2320 cmor_field_name=cmor_field_name, cmor_long_name=cmor_long_name, &
2321 cmor_units=cmor_units, cmor_standard_name=cmor_standard_name, &
2322 cell_methods=cell_methods, x_cell_method=x_cell_method, &
2323 y_cell_method=y_cell_method, v_cell_method=v_cell_method, &
2324656 conversion=conversion, v_extensive=v_extensive)
2325656 if (associated(axes%xyave_axes)) then
2326312 num_varnm = 2 ; var_list = "{"//trim(field_name)//","//trim(field_name)//"_xyave"
2327 else
2328344 num_varnm = 1 ; var_list = "{"//trim(field_name)
2329 endif
2330656 if (present(cmor_field_name)) then
233152 if (associated(axes%xyave_axes)) then
233217 num_varnm = num_varnm + 2
233317 var_list = trim(var_list)//","//trim(cmor_field_name)//","//trim(cmor_field_name)//"_xyave"
2334 else
233535 num_varnm = num_varnm + 1
233635 var_list = trim(var_list)//","//trim(cmor_field_name)
2337 endif
2338 endif
2339656 var_list = trim(var_list)//"}"
2340
2341 ! For each diagnostic coordinate register the diagnostic again under a different module name
23421312 do i=1,diag_cs%num_diag_coords
2343656 new_module_name = trim(module_name)//'_'//trim(diag_cs%diag_remap_cs(i)%diag_module_suffix)
2344
2345 ! Register diagnostics remapped to z vertical coordinate
23461312 if (axes_in%rank == 3) then
2347326 remap_axes => null()
2348326 if ((axes_in%id == diag_cs%axesTL%id)) then
2349141 remap_axes => diag_cs%remap_axesTL(i)
2350185 elseif (axes_in%id == diag_cs%axesBL%id) then
23518 remap_axes => diag_cs%remap_axesBL(i)
2352177 elseif (axes_in%id == diag_cs%axesCuL%id ) then
235357 remap_axes => diag_cs%remap_axesCuL(i)
2354120 elseif (axes_in%id == diag_cs%axesCvL%id) then
235557 remap_axes => diag_cs%remap_axesCvL(i)
235663 elseif (axes_in%id == diag_cs%axesTi%id) then
235741 remap_axes => diag_cs%remap_axesTi(i)
235822 elseif (axes_in%id == diag_cs%axesBi%id) then
23596 remap_axes => diag_cs%remap_axesBi(i)
236016 elseif (axes_in%id == diag_cs%axesCui%id ) then
23618 remap_axes => diag_cs%remap_axesCui(i)
23628 elseif (axes_in%id == diag_cs%axesCvi%id) then
23638 remap_axes => diag_cs%remap_axesCvi(i)
2364 endif
2365 ! When the MOM_diag_to_Z module has been obsoleted we can assume remap_axes will
2366 ! always exist but in the mean-time we have to do this check:
2367 ! call assert(associated(remap_axes), 'register_diag_field: remap_axes not set')
2368326 if (associated(remap_axes)) then
2369326 if (remap_axes%needs_remapping .or. remap_axes%needs_interpolating) then
2370 active = register_diag_field_expand_cmor(dm_id, new_module_name, field_name, remap_axes, &
2371 init_time, long_name=long_name, units=units, missing_value=MOM_missing_value, &
2372 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2373 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2374 interp_method=interp_method, tile_count=tile_count, &
2375 cmor_field_name=cmor_field_name, cmor_long_name=cmor_long_name, &
2376 cmor_units=cmor_units, cmor_standard_name=cmor_standard_name, &
2377 cell_methods=cell_methods, x_cell_method=x_cell_method, &
2378 y_cell_method=y_cell_method, v_cell_method=v_cell_method, &
2379312 conversion=conversion, v_extensive=v_extensive)
2380312 if (active) then
23816 call diag_remap_set_active(diag_cs%diag_remap_cs(i))
2382 endif
2383312 module_list = trim(module_list)//","//trim(new_module_name)
2384312 num_modnm = num_modnm + 1
2385 endif ! remap_axes%needs_remapping
2386 endif ! associated(remap_axes)
2387 endif ! axes%rank == 3
2388 enddo ! i
2389
2390 ! Register downsampled diagnostics
23911312 do dl=2,MAX_DSAMP_LEV
2392 ! Do not attempt to checksum the downsampled diagnostics
2393656 if (diag_cs%diag_as_chksum) cycle
2394
2395656 new_module_name = trim(module_name)//'_d2'
2396
2397656 axes_d2 => null()
2398656 if (axes_in%rank == 3 .or. axes_in%rank == 2 ) then
2399652 if (axes_in%id == diag_cs%axesTL%id) then
2400141 axes_d2 => diag_cs%dsamp(dl)%axesTL
2401511 elseif (axes_in%id == diag_cs%axesBL%id) then
24028 axes_d2 => diag_cs%dsamp(dl)%axesBL
2403503 elseif (axes_in%id == diag_cs%axesCuL%id ) then
240457 axes_d2 => diag_cs%dsamp(dl)%axesCuL
2405446 elseif (axes_in%id == diag_cs%axesCvL%id) then
240657 axes_d2 => diag_cs%dsamp(dl)%axesCvL
2407389 elseif (axes_in%id == diag_cs%axesTi%id) then
240841 axes_d2 => diag_cs%dsamp(dl)%axesTi
2409348 elseif (axes_in%id == diag_cs%axesBi%id) then
24106 axes_d2 => diag_cs%dsamp(dl)%axesBi
2411342 elseif (axes_in%id == diag_cs%axesCui%id ) then
24128 axes_d2 => diag_cs%dsamp(dl)%axesCui
2413334 elseif (axes_in%id == diag_cs%axesCvi%id) then
24148 axes_d2 => diag_cs%dsamp(dl)%axesCvi
2415326 elseif (axes_in%id == diag_cs%axesT1%id) then
2416207 axes_d2 => diag_cs%dsamp(dl)%axesT1
2417119 elseif (axes_in%id == diag_cs%axesB1%id) then
24181 axes_d2 => diag_cs%dsamp(dl)%axesB1
2419118 elseif (axes_in%id == diag_cs%axesCu1%id ) then
242059 axes_d2 => diag_cs%dsamp(dl)%axesCu1
242159 elseif (axes_in%id == diag_cs%axesCv1%id) then
242259 axes_d2 => diag_cs%dsamp(dl)%axesCv1
2423 else
2424 !Niki: Should we worry about these, e.g., diag_to_Z_CS?
2425 call MOM_error(WARNING,"register_diag_field: Could not find a proper axes for " &
24260 //trim(new_module_name)//"-"//trim(field_name))
2427 endif
2428 endif
2429
2430 ! Register the native diagnostic
2431656 if (associated(axes_d2)) then
2432 active = register_diag_field_expand_cmor(dm_id, new_module_name, field_name, axes_d2, &
2433 init_time, long_name=long_name, units=units, missing_value=MOM_missing_value, &
2434 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2435 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2436 interp_method=interp_method, tile_count=tile_count, &
2437 cmor_field_name=cmor_field_name, cmor_long_name=cmor_long_name, &
2438 cmor_units=cmor_units, cmor_standard_name=cmor_standard_name, &
2439 cell_methods=cell_methods, x_cell_method=x_cell_method, &
2440 y_cell_method=y_cell_method, v_cell_method=v_cell_method, &
2441652 conversion=conversion, v_extensive=v_extensive)
2442652 module_list = trim(module_list)//","//trim(new_module_name)
2443652 num_modnm = num_modnm + 1
2444 endif
2445
2446 ! For each diagnostic coordinate register the diagnostic again under a different module name
24471968 do i=1,diag_cs%num_diag_coords
2448656 new_module_name = trim(module_name)//'_'//trim(diag_cs%diag_remap_cs(i)%diag_module_suffix)//'_d2'
2449
2450 ! Register diagnostics remapped to z vertical coordinate
24511312 if (axes_in%rank == 3) then
2452326 remap_axes => null()
2453326 if ((axes_in%id == diag_cs%axesTL%id)) then
2454141 remap_axes => diag_cs%dsamp(dl)%remap_axesTL(i)
2455185 elseif (axes_in%id == diag_cs%axesBL%id) then
24568 remap_axes => diag_cs%dsamp(dl)%remap_axesBL(i)
2457177 elseif (axes_in%id == diag_cs%axesCuL%id ) then
245857 remap_axes => diag_cs%dsamp(dl)%remap_axesCuL(i)
2459120 elseif (axes_in%id == diag_cs%axesCvL%id) then
246057 remap_axes => diag_cs%dsamp(dl)%remap_axesCvL(i)
246163 elseif (axes_in%id == diag_cs%axesTi%id) then
246241 remap_axes => diag_cs%dsamp(dl)%remap_axesTi(i)
246322 elseif (axes_in%id == diag_cs%axesBi%id) then
24646 remap_axes => diag_cs%dsamp(dl)%remap_axesBi(i)
246516 elseif (axes_in%id == diag_cs%axesCui%id ) then
24668 remap_axes => diag_cs%dsamp(dl)%remap_axesCui(i)
24678 elseif (axes_in%id == diag_cs%axesCvi%id) then
24688 remap_axes => diag_cs%dsamp(dl)%remap_axesCvi(i)
2469 endif
2470
2471 ! When the MOM_diag_to_Z module has been obsoleted we can assume remap_axes will
2472 ! always exist but in the mean-time we have to do this check:
2473 ! call assert(associated(remap_axes), 'register_diag_field: remap_axes not set')
2474326 if (associated(remap_axes)) then
2475326 if (remap_axes%needs_remapping .or. remap_axes%needs_interpolating) then
2476 active = register_diag_field_expand_cmor(dm_id, new_module_name, field_name, remap_axes, &
2477 init_time, long_name=long_name, units=units, missing_value=MOM_missing_value, &
2478 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2479 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2480 interp_method=interp_method, tile_count=tile_count, &
2481 cmor_field_name=cmor_field_name, cmor_long_name=cmor_long_name, &
2482 cmor_units=cmor_units, cmor_standard_name=cmor_standard_name, &
2483 cell_methods=cell_methods, x_cell_method=x_cell_method, &
2484 y_cell_method=y_cell_method, v_cell_method=v_cell_method, &
2485312 conversion=conversion, v_extensive=v_extensive)
2486312 if (active) then
24870 call diag_remap_set_active(diag_cs%diag_remap_cs(i))
2488 endif
2489312 module_list = trim(module_list)//","//trim(new_module_name)
2490312 num_modnm = num_modnm + 1
2491 endif ! remap_axes%needs_remapping
2492 endif ! associated(remap_axes)
2493 endif ! axes%rank == 3
2494 enddo ! i
2495 enddo
2496
2497656 dimensions = ""
2498656 if (axes_in%is_h_point) dimensions = trim(dimensions)//" xh, yh,"
2499656 if (axes_in%is_q_point) dimensions = trim(dimensions)//" xq, yq,"
2500656 if (axes_in%is_u_point) dimensions = trim(dimensions)//" xq, yh,"
2501656 if (axes_in%is_v_point) dimensions = trim(dimensions)//" xh, yq,"
2502656 if (axes_in%is_layer) dimensions = trim(dimensions)//" zl,"
2503656 if (axes_in%is_interface) dimensions = trim(dimensions)//" zi,"
2504656 if (len_trim(dimensions) > 0) dimensions = trim_trailing_commas(dimensions)
2505
2506656 if (is_root_pe() .and. (diag_CS%available_diag_doc_unit > 0)) then
2507656 msg = ''
2508656 if (present(cmor_field_name)) msg = 'CMOR equivalent is "'//trim(cmor_field_name)//'"'
2509 call attach_cell_methods(-1, axes, cm_string, cell_methods, &
2510 x_cell_method, y_cell_method, v_cell_method, &
2511656 v_extensive=v_extensive)
2512656 module_list = trim(module_list)//"}"
2513656 if (num_modnm <= 1) module_list = module_name
2514656 if (num_varnm <= 1) var_list = ''
2515
2516 call log_available_diag(dm_id>0, module_list, field_name, cm_string, msg, diag_CS, &
2517656 long_name, units, standard_name, variants=var_list, dimensions=dimensions)
2518 endif
2519
2520656 register_diag_field = dm_id
2521
25221312end function register_diag_field
2523
2524!> Returns True if either the native or CMOR version of the diagnostic were registered. Updates 'dm_id'
2525!! after calling register_diag_field_expand_axes() for both native and CMOR variants of the field.
25261932logical function register_diag_field_expand_cmor(dm_id, module_name, field_name, axes, init_time, &
2527 long_name, units, missing_value, range, mask_variant, standard_name, &
2528 verbose, do_not_log, err_msg, interp_method, tile_count, cmor_field_name, &
2529 cmor_long_name, cmor_units, cmor_standard_name, cell_methods, &
2530 x_cell_method, y_cell_method, v_cell_method, conversion, v_extensive)
2531 integer, intent(inout) :: dm_id !< The diag_mediator ID for this diagnostic group
2532 character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model" or "ice_shelf_model"
2533 character(len=*), intent(in) :: field_name !< Name of the diagnostic field
2534 type(axes_grp), intent(in) :: axes !< Container with up to 3 integer handles that indicates axes
2535 !! for this field
2536 type(time_type), intent(in) :: init_time !< Time at which a field is first available?
2537 character(len=*), optional, intent(in) :: long_name !< Long name of a field.
2538 character(len=*), optional, intent(in) :: units !< Units of a field.
2539 character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
2540 real, optional, intent(in) :: missing_value !< A value that indicates missing values in
2541 !! output files, in unscaled arbitrary units [a]
2542 real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
2543 !! in arbitrary units [a]
2544 logical, optional, intent(in) :: mask_variant !< If true a logical mask must be provided
2545 !! with post_data calls (not used in MOM?)
2546 logical, optional, intent(in) :: verbose !< If true, FMS is verbose (not used in MOM?)
2547 logical, optional, intent(in) :: do_not_log !< If true, do not log something (not used in MOM?)
2548 character(len=*), optional, intent(out):: err_msg !< String into which an error message might be
2549 !! placed (not used in MOM?)
2550 character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should
2551 !! not be interpolated as a scalar
2552 integer, optional, intent(in) :: tile_count !< no clue (not used in MOM?)
2553 character(len=*), optional, intent(in) :: cmor_field_name !< CMOR name of a field
2554 character(len=*), optional, intent(in) :: cmor_long_name !< CMOR long name of a field
2555 character(len=*), optional, intent(in) :: cmor_units !< CMOR units of a field
2556 character(len=*), optional, intent(in) :: cmor_standard_name !< CMOR standardized name associated with a field
2557 character(len=*), optional, intent(in) :: cell_methods !< String to append as cell_methods attribute.
2558 !! Use '' to have no attribute. If present, this
2559 !! overrides the default constructed from the default
2560 !! for each individual axis direction.
2561 character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction.
2562 !! Use '' have no method.
2563 character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction.
2564 !! Use '' have no method.
2565 character(len=*), optional, intent(in) :: v_cell_method !< Specifies the cell method for the vertical direction.
2566 !! Use '' have no method.
2567 real, optional, intent(in) :: conversion !< A value to multiply data by before writing to files,
2568 !! often including factors to undo internal scaling and
2569 !! in units of [a A-1 ~> 1]
2570 logical, optional, intent(in) :: v_extensive !< True for vertically extensive fields (vertically
2571 !! integrated). Default/absent for intensive.
2572 ! Local variables
2573 real :: MOM_missing_value ! A value used to indicate missing values in output files, in arbitrary units [a]
2574 type(diag_ctrl), pointer :: diag_cs => null()
2575 type(diag_type), pointer :: this_diag => null()
2576 integer :: fms_id, fms_xyave_id
2577 character(len=256) :: posted_cmor_units, posted_cmor_standard_name, posted_cmor_long_name, cm_string
2578
25791932 MOM_missing_value = axes%diag_cs%missing_value
25801932 if (present(missing_value)) MOM_missing_value = missing_value
2581
25821932 register_diag_field_expand_cmor = .false.
25831932 diag_cs => axes%diag_cs
2584
2585 ! Set up the 'primary' diagnostic, first get an underlying FMS id
2586 fms_id = register_diag_field_expand_axes(module_name, field_name, axes, init_time, &
2587 long_name=long_name, units=units, missing_value=MOM_missing_value, &
2588 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2589 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
25901932 interp_method=interp_method, tile_count=tile_count)
25911932 if (.not. diag_cs%diag_as_chksum) &
2592 call attach_cell_methods(fms_id, axes, cm_string, cell_methods, &
2593 x_cell_method, y_cell_method, v_cell_method, &
25941932 v_extensive=v_extensive)
2595 ! Associated horizontally area-averaged diagnostic
25961932 fms_xyave_id = DIAG_FIELD_NOT_FOUND
25971932 if (associated(axes%xyave_axes)) then
2598 fms_xyave_id = register_diag_field_expand_axes(module_name, trim(field_name)//'_xyave', &
2599 axes%xyave_axes, init_time, &
2600 long_name=long_name, units=units, missing_value=MOM_missing_value, &
2601 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2602 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
26031248 interp_method=interp_method, tile_count=tile_count)
26041248 if (.not. diag_cs%diag_as_chksum) &
2605 call attach_cell_methods(fms_xyave_id, axes%xyave_axes, cm_string, &
26061248 cell_methods, v_cell_method, v_extensive=v_extensive)
2607 endif
26081932 this_diag => null()
26091932 if (fms_id /= DIAG_FIELD_NOT_FOUND .or. fms_xyave_id /= DIAG_FIELD_NOT_FOUND) then
26107 call add_diag_to_list(diag_cs, dm_id, fms_id, this_diag, axes, module_name, field_name)
26117 this_diag%fms_xyave_diag_id = fms_xyave_id
2612 ! Encode and save the cell methods for this diagnostic
26137 call add_xyz_method(this_diag, axes, x_cell_method, y_cell_method, v_cell_method, v_extensive)
26147 if (present(v_extensive)) this_diag%v_extensive = v_extensive
26157 if (present(conversion)) this_diag%conversion_factor = conversion
26167 register_diag_field_expand_cmor = .true.
2617 endif
2618
2619 ! For the CMOR variation of the above diagnostic
26201932 if (present(cmor_field_name) .and. .not. diag_cs%diag_as_chksum) then
2621 ! Fallback values for strings set to "NULL"
2622138 posted_cmor_units = "not provided" !
2623138 posted_cmor_standard_name = "not provided" ! Values might be able to be replaced with a CS%missing field?
2624138 posted_cmor_long_name = "not provided" !
2625
2626 ! If attributes are present for MOM variable names, use them first for the register_diag_field
2627 ! call for CMOR version of the variable
2628138 if (present(units)) posted_cmor_units = units
2629138 if (present(standard_name)) posted_cmor_standard_name = standard_name
2630138 if (present(long_name)) posted_cmor_long_name = long_name
2631
2632 ! If specified in the call to register_diag_field, override attributes with the CMOR versions
2633138 if (present(cmor_units)) posted_cmor_units = cmor_units
2634138 if (present(cmor_standard_name)) posted_cmor_standard_name = cmor_standard_name
2635138 if (present(cmor_long_name)) posted_cmor_long_name = cmor_long_name
2636
2637 fms_id = register_diag_field_expand_axes(module_name, cmor_field_name, axes, init_time, &
2638 long_name=trim(posted_cmor_long_name), units=trim(posted_cmor_units), &
2639 missing_value=MOM_missing_value, range=range, mask_variant=mask_variant, &
2640 standard_name=trim(posted_cmor_standard_name), verbose=verbose, do_not_log=do_not_log, &
2641138 err_msg=err_msg, interp_method=interp_method, tile_count=tile_count)
2642 call attach_cell_methods(fms_id, axes, cm_string, &
2643 cell_methods, x_cell_method, y_cell_method, v_cell_method, &
2644138 v_extensive=v_extensive)
2645 ! Associated horizontally area-averaged diagnostic
2646138 fms_xyave_id = DIAG_FIELD_NOT_FOUND
2647138 if (associated(axes%xyave_axes)) then
2648 fms_xyave_id = register_diag_field_expand_axes(module_name, trim(cmor_field_name)//'_xyave', &
2649 axes%xyave_axes, init_time, &
2650 long_name=trim(posted_cmor_long_name), units=trim(posted_cmor_units), &
2651 missing_value=MOM_missing_value, range=range, mask_variant=mask_variant, &
2652 standard_name=trim(posted_cmor_standard_name), verbose=verbose, do_not_log=do_not_log, &
265368 err_msg=err_msg, interp_method=interp_method, tile_count=tile_count)
2654 call attach_cell_methods(fms_xyave_id, axes%xyave_axes, cm_string, &
265568 cell_methods, v_cell_method, v_extensive=v_extensive)
2656 endif
2657138 this_diag => null()
2658138 if (fms_id /= DIAG_FIELD_NOT_FOUND .or. fms_xyave_id /= DIAG_FIELD_NOT_FOUND) then
26597 call add_diag_to_list(diag_cs, dm_id, fms_id, this_diag, axes, module_name, field_name)
26607 this_diag%fms_xyave_diag_id = fms_xyave_id
2661 ! Encode and save the cell methods for this diagnostic
26627 call add_xyz_method(this_diag, axes, x_cell_method, y_cell_method, v_cell_method, v_extensive)
26637 if (present(v_extensive)) this_diag%v_extensive = v_extensive
26647 if (present(conversion)) this_diag%conversion_factor = conversion
26657 register_diag_field_expand_cmor = .true.
2666 endif
2667 endif
2668
26693864end function register_diag_field_expand_cmor
2670
2671!> Returns an FMS id from register_diag_field_fms (the diag_manager routine) after expanding axes
2672!! (axes-group) into handles and conditionally adding an FMS area_id for cell_measures.
26733386integer function register_diag_field_expand_axes(module_name, field_name, axes, init_time, &
2674 long_name, units, missing_value, range, mask_variant, standard_name, &
2675 verbose, do_not_log, err_msg, interp_method, tile_count)
2676 character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model"
2677 !! or "ice_shelf_model"
2678 character(len=*), intent(in) :: field_name !< Name of the diagnostic field
2679 type(axes_grp), target, intent(in) :: axes !< Container with up to 3 integer handles that indicates
2680 !! axes for this field
2681 type(time_type), intent(in) :: init_time !< Time at which a field is first available?
2682 character(len=*), optional, intent(in) :: long_name !< Long name of a field.
2683 character(len=*), optional, intent(in) :: units !< Units of a field.
2684 character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
2685 real, optional, intent(in) :: missing_value !< A value that indicates missing values in
2686 !! output files, in unscaled arbitrary units [a]
2687 real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
2688 !! in arbitrary units [a]
2689 logical, optional, intent(in) :: mask_variant !< If true a logical mask must be provided
2690 !! with post_data calls (not used in MOM?)
2691 logical, optional, intent(in) :: verbose !< If true, FMS is verbose (not used in MOM?)
2692 logical, optional, intent(in) :: do_not_log !< If true, do not log something
2693 !! (not used in MOM?)
2694 character(len=*), optional, intent(out):: err_msg !< String into which an error message might be
2695 !! placed (not used in MOM?)
2696 character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should
2697 !! not be interpolated as a scalar
2698 integer, optional, intent(in) :: tile_count !< no clue (not used in MOM?)
2699 ! Local variables
2700 integer :: fms_id, area_id, volume_id
2701
2702 ! This gets the cell area associated with the grid location of this variable
27033386 area_id = axes%id_area
27043386 volume_id = axes%id_volume
2705
2706 ! Get the FMS diagnostic id
27073386 if (axes%diag_cs%diag_as_chksum) then
27080 fms_id = axes%diag_cs%num_chksum_diags + 1
27090 axes%diag_cs%num_chksum_diags = fms_id
27103386 elseif (present(interp_method) .or. axes%is_h_point) then
2711 ! If interp_method is provided we must use it
27121926 if (area_id>0) then
2713632 if (volume_id>0) then
2714 fms_id = register_diag_field_infra(module_name, field_name, axes%handles, &
2715 init_time, long_name=long_name, units=units, missing_value=missing_value, &
2716 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2717 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
27180 interp_method=interp_method, tile_count=tile_count, area=area_id, volume=volume_id)
2719 else
2720 fms_id = register_diag_field_infra(module_name, field_name, axes%handles, &
2721 init_time, long_name=long_name, units=units, missing_value=missing_value, &
2722 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2723 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
2724632 interp_method=interp_method, tile_count=tile_count, area=area_id)
2725 endif
2726 else
27271294 if (volume_id>0) then
2728 fms_id = register_diag_field_infra(module_name, field_name, axes%handles, &
2729 init_time, long_name=long_name, units=units, missing_value=missing_value, &
2730 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2731 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
27320 interp_method=interp_method, tile_count=tile_count, volume=volume_id)
2733 else
2734 fms_id = register_diag_field_infra(module_name, field_name, axes%handles, &
2735 init_time, long_name=long_name, units=units, missing_value=missing_value, &
2736 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2737 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
27381294 interp_method=interp_method, tile_count=tile_count)
2739 endif
2740 endif
2741 else
2742 ! If interp_method is not provided and the field is not at an h-point then interp_method='none'
27431460 if (area_id>0) then
27440 if (volume_id>0) then
2745 fms_id = register_diag_field_infra(module_name, field_name, axes%handles, &
2746 init_time, long_name=long_name, units=units, missing_value=missing_value, &
2747 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2748 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
27490 interp_method='none', tile_count=tile_count, area=area_id, volume=volume_id)
2750 else
2751 fms_id = register_diag_field_infra(module_name, field_name, axes%handles, &
2752 init_time, long_name=long_name, units=units, missing_value=missing_value, &
2753 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2754 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
27550 interp_method='none', tile_count=tile_count, area=area_id)
2756 endif
2757 else
27581460 if (volume_id>0) then
2759 fms_id = register_diag_field_infra(module_name, field_name, axes%handles, &
2760 init_time, long_name=long_name, units=units, missing_value=missing_value, &
2761 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2762 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
27630 interp_method='none', tile_count=tile_count, volume=volume_id)
2764 else
2765 fms_id = register_diag_field_infra(module_name, field_name, axes%handles, &
2766 init_time, long_name=long_name, units=units, missing_value=missing_value, &
2767 range=range, mask_variant=mask_variant, standard_name=standard_name, &
2768 verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
27691460 interp_method='none', tile_count=tile_count)
2770 endif
2771 endif
2772 endif
2773
27743386 register_diag_field_expand_axes = fms_id
2775
27766772end function register_diag_field_expand_axes
2777
2778!> Create a diagnostic type and attached to list
277914subroutine add_diag_to_list(diag_cs, dm_id, fms_id, this_diag, axes, module_name, field_name)
2780 type(diag_ctrl), pointer :: diag_cs !< Diagnostics mediator control structure
2781 integer, intent(inout) :: dm_id !< The diag_mediator ID for this diagnostic group
2782 integer, intent(in) :: fms_id !< The FMS diag_manager ID for this diagnostic
2783 type(diag_type), pointer :: this_diag !< This diagnostic
2784 type(axes_grp), target, intent(in) :: axes !< Container with up to 3 integer handles that
2785 !! indicates axes for this field
2786 character(len=*), intent(in) :: module_name !< Name of this module, usually
2787 !! "ocean_model" or "ice_shelf_model"
2788 character(len=*), intent(in) :: field_name !< Name of diagnostic
2789
2790 ! If the diagnostic is needed obtain a diag_mediator ID (if needed)
279114 if (dm_id == -1) dm_id = get_new_diag_id(diag_cs)
2792 ! Create a new diag_type to store links in
279314 call alloc_diag_with_id(dm_id, diag_cs, this_diag)
279414 call assert(associated(this_diag), 'add_diag_to_list: allocation failed for '//trim(field_name))
2795 ! Record FMS id, masks and conversion factor, in diag_type
279614 this_diag%fms_diag_id = fms_id
279714 this_diag%debug_str = trim(module_name)//"-"//trim(field_name)
279814 this_diag%axes => axes
2799
280014end subroutine add_diag_to_list
2801
2802!> Adds the encoded "cell_methods" for a diagnostics as a diag% property
2803!! This allows access to the cell_method for a given diagnostics at the time of sending
280414subroutine add_xyz_method(diag, axes, x_cell_method, y_cell_method, v_cell_method, v_extensive)
2805 type(diag_type), pointer :: diag !< This diagnostic
2806 type(axes_grp), intent(in) :: axes !< Container w/ up to 3 integer handles that indicates
2807 !! axes for this field
2808 character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction.
2809 !! Use '' have no method.
2810 character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction.
2811 !! Use '' have no method.
2812 character(len=*), optional, intent(in) :: v_cell_method !< Specifies the cell method for the vertical direction.
2813 !! Use '' have no method.
2814 logical, optional, intent(in) :: v_extensive !< True for vertically extensive fields
2815 !! (vertically integrated). Default/absent for intensive.
2816 integer :: xyz_method
2817 character(len=9) :: mstr
2818
2819 ! This is a simple way to encode the cell method information made from 3 strings
2820 ! (x_cell_method,y_cell_method,v_cell_method) in a 3 digit integer xyz
2821 ! x_cell_method,y_cell_method,v_cell_method can each be 'point' or 'sum' or 'mean'
2822 ! We can encode these with setting 1 for 'point', 2 for 'sum, 3 for 'mean' in
2823 ! the 100s position for x, 10s position for y, 1s position for z
2824 ! E.g., x:sum,y:point,z:mean is 213
2825
282614 xyz_method = 111
2827
282814 mstr = diag%axes%v_cell_method
282914 if (present(v_extensive)) then
28300 if (present(v_cell_method)) call MOM_error(FATAL, "attach_cell_methods: " // &
28310 'Vertical cell method was specified along with the vertically extensive flag.')
28320 if (v_extensive) then
28330 mstr='sum'
2834 else
28350 mstr='mean'
2836 endif
283714 elseif (present(v_cell_method)) then
28380 mstr = v_cell_method
2839 endif
284014 if (trim(mstr)=='sum') then
28410 xyz_method = xyz_method + 1
284214 elseif (trim(mstr)=='mean') then
28435 xyz_method = xyz_method + 2
2844 endif
2845
284614 mstr = diag%axes%y_cell_method
284714 if (present(y_cell_method)) mstr = y_cell_method
284814 if (trim(mstr)=='sum') then
28490 xyz_method = xyz_method + 10
285014 elseif (trim(mstr)=='mean') then
285111 xyz_method = xyz_method + 20
2852 endif
2853
285414 mstr = diag%axes%x_cell_method
285514 if (present(x_cell_method)) mstr = x_cell_method
285614 if (trim(mstr)=='sum') then
28570 xyz_method = xyz_method + 100
285814 elseif (trim(mstr)=='mean') then
285910 xyz_method = xyz_method + 200
2860 endif
2861
286214 diag%xyz_method = xyz_method
286314end subroutine add_xyz_method
2864
2865!> Attaches "cell_methods" attribute to a variable based on defaults for axes_grp or optional arguments.
28664042subroutine attach_cell_methods(id, axes, ostring, cell_methods, &
2867 x_cell_method, y_cell_method, v_cell_method, v_extensive)
2868 integer, intent(in) :: id !< Handle to diagnostic
2869 type(axes_grp), intent(in) :: axes !< Container with up to 3 integer handles that indicates
2870 !! axes for this field
2871 character(len=*), intent(out) :: ostring !< The cell_methods strings that would appear in the file
2872 character(len=*), optional, intent(in) :: cell_methods !< String to append as cell_methods attribute.
2873 !! Use '' to have no attribute. If present, this
2874 !! overrides the default constructed from the default
2875 !! for each individual axis direction.
2876 character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction.
2877 !! Use '' have no method.
2878 character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction.
2879 !! Use '' have no method.
2880 character(len=*), optional, intent(in) :: v_cell_method !< Specifies the cell method for the vertical direction.
2881 !! Use '' have no method.
2882 logical, optional, intent(in) :: v_extensive !< True for vertically extensive fields
2883 !! (vertically integrated). Default/absent for intensive.
2884 ! Local variables
2885 character(len=9) :: axis_name
2886 logical :: x_mean, y_mean, x_sum, y_sum
2887
28884042 x_mean = .false.
28894042 y_mean = .false.
28904042 x_sum = .false.
28914042 y_sum = .false.
2892
28934042 ostring = ''
28944042 if (present(cell_methods)) then
2895 if (present(x_cell_method) .or. present(y_cell_method) .or. present(v_cell_method) &
28960 .or. present(v_extensive)) then
2897 call MOM_error(FATAL, "attach_cell_methods: " // &
28980 'Individual direction cell method was specified along with a "cell_methods" string.')
2899 endif
29000 if (len(trim(cell_methods))>0) then
29010 call MOM_diag_field_add_attribute(id, 'cell_methods', trim(cell_methods))
29020 ostring = trim(cell_methods)
2903 endif
2904 else
29054042 if (present(x_cell_method)) then
2906170 if (len(trim(x_cell_method))>0) then
2907170 call get_MOM_diag_axis_name(axes%handles(1), axis_name)
2908170 call MOM_diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(x_cell_method))
2909170 ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(x_cell_method)
2910170 if (trim(x_cell_method)=='mean') x_mean=.true.
2911170 if (trim(x_cell_method)=='sum') x_sum=.true.
2912 endif
2913 else
29143872 if (len(trim(axes%x_cell_method))>0) then
29152548 call get_MOM_diag_axis_name(axes%handles(1), axis_name)
29162548 call MOM_diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(axes%x_cell_method))
29172548 ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(axes%x_cell_method)
29182548 if (trim(axes%x_cell_method)=='mean') x_mean=.true.
29192548 if (trim(axes%x_cell_method)=='sum') x_sum=.true.
2920 endif
2921 endif
29224042 if (present(y_cell_method)) then
2923170 if (len(trim(y_cell_method))>0) then
2924170 call get_MOM_diag_axis_name(axes%handles(2), axis_name)
2925170 call MOM_diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(y_cell_method))
2926170 ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(y_cell_method)
2927170 if (trim(y_cell_method)=='mean') y_mean=.true.
2928170 if (trim(y_cell_method)=='sum') y_sum=.true.
2929 endif
2930 else
29313872 if (len(trim(axes%y_cell_method))>0) then
29322548 call get_MOM_diag_axis_name(axes%handles(2), axis_name)
29332548 call MOM_diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(axes%y_cell_method))
29342548 ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(axes%y_cell_method)
29352548 if (trim(axes%y_cell_method)=='mean') y_mean=.true.
29362548 if (trim(axes%y_cell_method)=='sum') y_sum=.true.
2937 endif
2938 endif
29394042 if (present(v_cell_method)) then
29400 if (present(v_extensive)) call MOM_error(FATAL, "attach_cell_methods: " // &
29410 'Vertical cell method was specified along with the vertically extensive flag.')
29420 if (len(trim(v_cell_method))>0) then
29430 if (axes%rank==1) then
29440 call get_MOM_diag_axis_name(axes%handles(1), axis_name)
29450 elseif (axes%rank==3) then
29460 call get_MOM_diag_axis_name(axes%handles(3), axis_name)
2947 endif
29480 call MOM_diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(v_cell_method))
29490 ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(v_cell_method)
2950 endif
29514042 elseif (present(v_extensive)) then
2952728 if (v_extensive) then
2953728 if (axes%rank==1) then
2954324 call get_MOM_diag_axis_name(axes%handles(1), axis_name)
2955404 elseif (axes%rank==3) then
2956399 call get_MOM_diag_axis_name(axes%handles(3), axis_name)
2957 endif
2958728 call MOM_diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':sum')
2959728 ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':sum'
2960 endif
2961 else
29623314 if (len(trim(axes%v_cell_method))>0) then
29632271 if (axes%rank==1) then
29641000 call get_MOM_diag_axis_name(axes%handles(1), axis_name)
29651271 elseif (axes%rank==3) then
29661271 call get_MOM_diag_axis_name(axes%handles(3), axis_name)
2967 endif
29682271 call MOM_diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(axes%v_cell_method))
29692271 ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(axes%v_cell_method)
2970 endif
2971 endif
29724042 if (x_mean .and. y_mean) then
29731592 call MOM_diag_field_add_attribute(id, 'cell_methods', 'area:mean')
29741592 ostring = trim(adjustl(ostring))//' area:mean'
29752450 elseif (x_sum .and. y_sum) then
297665 call MOM_diag_field_add_attribute(id, 'cell_methods', 'area:sum')
297765 ostring = trim(adjustl(ostring))//' area:sum'
2978 endif
2979 endif
29804042 ostring = adjustl(ostring)
29814042end subroutine attach_cell_methods
2982
2983
2984!> Registers a non-array scalar diagnostic, returning an integer handle
29850function register_scalar_field_axes(module_name, field_name, axes, init_time, &
2986 long_name, units, missing_value, range, standard_name, &
2987 do_not_log, err_msg, interp_method, cmor_field_name, &
2988 cmor_long_name, cmor_units, cmor_standard_name, conversion) result (register_scalar_field)
2989 integer :: register_scalar_field !< An integer handle for a diagnostic array.
2990 character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model"
2991 !! or "ice_shelf_model"
2992 character(len=*), intent(in) :: field_name !< Name of the diagnostic field
2993 type(axes_grp), target, intent(in) :: axes !< Container with up to 3 integer handles that
2994 !! indicates axes for this field
2995 type(time_type), intent(in) :: init_time !< Time at which a field is first available?
2996 character(len=*), optional, intent(in) :: long_name !< Long name of a field.
2997 character(len=*), optional, intent(in) :: units !< Units of a field.
2998 character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
2999 real, optional, intent(in) :: missing_value !< A value that indicates missing values in
3000 !! output files, in unscaled arbitrary units [a]
3001 real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
3002 !! in arbitrary units [a]
3003 logical, optional, intent(in) :: do_not_log !< If true, do not log something (not used in MOM?)
3004 character(len=*), optional, intent(out):: err_msg !< String into which an error message might be
3005 !! placed (not used in MOM?)
3006 character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should not
3007 !! be interpolated as a scalar
3008 character(len=*), optional, intent(in) :: cmor_field_name !< CMOR name of a field
3009 character(len=*), optional, intent(in) :: cmor_long_name !< CMOR long name of a field
3010 character(len=*), optional, intent(in) :: cmor_units !< CMOR units of a field
3011 character(len=*), optional, intent(in) :: cmor_standard_name !< CMOR standardized name associated with a field
3012 real, optional, intent(in) :: conversion !< A value to multiply data by before writing to files,
3013 !! often including factors to undo internal scaling and
3014 !! in units of [a A-1 ~> 1]
3015
3016 register_scalar_field = register_scalar_field_CS(module_name, field_name, init_time, axes%diag_cs, &
3017 long_name, units, missing_value, range, standard_name, &
3018 do_not_log, err_msg, interp_method, cmor_field_name, &
30190 cmor_long_name, cmor_units, cmor_standard_name, conversion)
3020
30210end function register_scalar_field_axes
3022
3023
3024!> Registers a scalar diagnostic, returning an integer handle
302583function register_scalar_field_CS(module_name, field_name, init_time, diag_cs, &
3026 long_name, units, missing_value, range, standard_name, &
3027 do_not_log, err_msg, interp_method, cmor_field_name, &
3028 cmor_long_name, cmor_units, cmor_standard_name, conversion) result (register_scalar_field)
3029 integer :: register_scalar_field !< An integer handle for a diagnostic array.
3030 character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model"
3031 !! or "ice_shelf_model"
3032 character(len=*), intent(in) :: field_name !< Name of the diagnostic field
3033 type(time_type), intent(in) :: init_time !< Time at which a field is first available?
3034 type(diag_ctrl), intent(inout) :: diag_CS !< Structure used to regulate diagnostic output
3035 character(len=*), optional, intent(in) :: long_name !< Long name of a field.
3036 character(len=*), optional, intent(in) :: units !< Units of a field.
3037 character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
3038 real, optional, intent(in) :: missing_value !< A value that indicates missing values in
3039 !! output files, in unscaled arbitrary units [a]
3040 real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
3041 !! in arbitrary units [a]
3042 logical, optional, intent(in) :: do_not_log !< If true, do not log something (not used in MOM?)
3043 character(len=*), optional, intent(out):: err_msg !< String into which an error message might be
3044 !! placed (not used in MOM?)
3045 character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should not
3046 !! be interpolated as a scalar
3047 character(len=*), optional, intent(in) :: cmor_field_name !< CMOR name of a field
3048 character(len=*), optional, intent(in) :: cmor_long_name !< CMOR long name of a field
3049 character(len=*), optional, intent(in) :: cmor_units !< CMOR units of a field
3050 character(len=*), optional, intent(in) :: cmor_standard_name !< CMOR standardized name associated with a field
3051 real, optional, intent(in) :: conversion !< A value to multiply data by before writing to files,
3052 !! often including factors to undo internal scaling and
3053 !! in units of [a A-1 ~> 1]
3054
3055 ! Local variables
3056 real :: MOM_missing_value ! A value used to indicate missing values in output files, in arbitrary units [a]
3057 integer :: dm_id, fms_id
3058 type(diag_type), pointer :: diag => null(), cmor_diag => null()
3059 character(len=256) :: posted_cmor_units, posted_cmor_standard_name, posted_cmor_long_name
3060 character(len=16) :: dimensions
3061
306283 MOM_missing_value = diag_cs%missing_value
30630 if (present(missing_value)) MOM_missing_value = missing_value
3064
306583 dm_id = -1
306683 diag => null()
306783 cmor_diag => null()
3068
306983 if (diag_cs%diag_as_chksum) then
30700 fms_id = diag_cs%num_chksum_diags + 1
30710 diag_cs%num_chksum_diags = fms_id
3072 else
3073 fms_id = register_diag_field_infra(module_name, field_name, init_time, &
3074 long_name=long_name, units=units, missing_value=MOM_missing_value, &
3075 range=range, standard_name=standard_name, do_not_log=do_not_log, &
307683 err_msg=err_msg)
3077 endif
3078
307983 if (fms_id /= DIAG_FIELD_NOT_FOUND) then
30800 dm_id = get_new_diag_id(diag_cs)
30810 call alloc_diag_with_id(dm_id, diag_cs, diag)
30820 call assert(associated(diag), 'register_scalar_field: diag allocation failed')
30830 diag%fms_diag_id = fms_id
30840 diag%debug_str = trim(module_name)//"-"//trim(field_name)
30850 if (present(conversion)) diag%conversion_factor = conversion
3086 endif
3087
308883 if (present(cmor_field_name)) then
3089 ! Fallback values for strings set to "not provided"
309030 posted_cmor_units = "not provided"
309130 posted_cmor_standard_name = "not provided"
309230 posted_cmor_long_name = "not provided"
3093
3094 ! If attributes are present for MOM variable names, use them as defaults for the
3095 ! register_diag_field_infra call for CMOR version of the variable
309630 if (present(units)) posted_cmor_units = units
309730 if (present(standard_name)) posted_cmor_standard_name = standard_name
309830 if (present(long_name)) posted_cmor_long_name = long_name
3099
3100 ! If specified in the call to register_scalar_field, override attributes with the CMOR versions
310130 if (present(cmor_units)) posted_cmor_units = cmor_units
310230 if (present(cmor_standard_name)) posted_cmor_standard_name = cmor_standard_name
310330 if (present(cmor_long_name)) posted_cmor_long_name = cmor_long_name
3104
3105 fms_id = register_diag_field_infra(module_name, cmor_field_name, init_time, &
3106 long_name=trim(posted_cmor_long_name), units=trim(posted_cmor_units), &
3107 missing_value=MOM_missing_value, range=range, &
310830 standard_name=trim(posted_cmor_standard_name), do_not_log=do_not_log, err_msg=err_msg)
310930 if (fms_id /= DIAG_FIELD_NOT_FOUND) then
31100 if (dm_id == -1) then
31110 dm_id = get_new_diag_id(diag_cs)
3112 endif
31130 call alloc_diag_with_id(dm_id, diag_cs, cmor_diag)
31140 cmor_diag%fms_diag_id = fms_id
31150 cmor_diag%debug_str = trim(module_name)//"-"//trim(cmor_field_name)
31160 if (present(conversion)) cmor_diag%conversion_factor = conversion
3117 endif
3118 endif
3119
312083 dimensions = "scalar"
3121
3122 ! Document diagnostics in list of available diagnostics
312383 if (is_root_pe() .and. diag_CS%available_diag_doc_unit > 0) then
312483 if (present(cmor_field_name)) then
3125 call log_available_diag(associated(diag), module_name, field_name, '', '', diag_CS, &
3126 long_name, units, standard_name, &
3127 variants="{"//trim(field_name)//","//trim(cmor_field_name)//"}", &
312830 dimensions=dimensions)
3129 else
3130 call log_available_diag(associated(diag), module_name, field_name, '', '', diag_CS, &
313153 long_name, units, standard_name, dimensions=dimensions)
3132 endif
3133 endif
3134
313583 register_scalar_field = dm_id
3136
3137166end function register_scalar_field_CS
3138
3139!> Registers a static diagnostic, returning an integer handle
314034function register_static_field(module_name, field_name, axes, &
3141 long_name, units, missing_value, range, mask_variant, standard_name, &
3142 do_not_log, interp_method, tile_count, &
3143 cmor_field_name, cmor_long_name, cmor_units, cmor_standard_name, area, &
3144 x_cell_method, y_cell_method, area_cell_method, conversion)
3145 integer :: register_static_field !< An integer handle for a diagnostic array.
3146 character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model"
3147 !! or "ice_shelf_model"
3148 character(len=*), intent(in) :: field_name !< Name of the diagnostic field
3149 type(axes_grp), target, intent(in) :: axes !< Container with up to 3 integer handles that
3150 !! indicates axes for this field
3151 character(len=*), optional, intent(in) :: long_name !< Long name of a field.
3152 character(len=*), optional, intent(in) :: units !< Units of a field.
3153 character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
3154 real, optional, intent(in) :: missing_value !< A value that indicates missing values in
3155 !! output files, in unscaled arbitrary units [a]
3156 real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
3157 !! in arbitrary units [a]
3158 logical, optional, intent(in) :: mask_variant !< If true a logical mask must be provided with
3159 !! post_data calls (not used in MOM?)
3160 logical, optional, intent(in) :: do_not_log !< If true, do not log something (not used in MOM?)
3161 character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should not
3162 !! be interpolated as a scalar
3163 integer, optional, intent(in) :: tile_count !< no clue (not used in MOM?)
3164 character(len=*), optional, intent(in) :: cmor_field_name !< CMOR name of a field
3165 character(len=*), optional, intent(in) :: cmor_long_name !< CMOR long name of a field
3166 character(len=*), optional, intent(in) :: cmor_units !< CMOR units of a field
3167 character(len=*), optional, intent(in) :: cmor_standard_name !< CMOR standardized name associated with a field
3168 integer, optional, intent(in) :: area !< fms_id for area_t
3169 character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction.
3170 character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction.
3171 character(len=*), optional, intent(in) :: area_cell_method !< Specifies the cell method for area
3172 real, optional, intent(in) :: conversion !< A value to multiply data by before writing to files,
3173 !! often including factors to undo internal scaling and
3174 !! in units of [a A-1 ~> 1]
3175
3176 ! Local variables
3177 real :: MOM_missing_value ! A value used to indicate missing values in output files, in arbitrary units [a]
3178 type(diag_ctrl), pointer :: diag_cs => null() !< A structure that is used to regulate diagnostic output
3179 type(diag_type), pointer :: diag => null(), cmor_diag => null()
3180 integer :: dm_id, fms_id
3181 character(len=256) :: posted_cmor_units, posted_cmor_standard_name, posted_cmor_long_name
3182 character(len=9) :: axis_name
3183 character(len=24) :: dimensions
3184
318534 MOM_missing_value = axes%diag_cs%missing_value
31860 if (present(missing_value)) MOM_missing_value = missing_value
3187
318834 diag_cs => axes%diag_cs
318934 dm_id = -1
319034 diag => null()
319134 cmor_diag => null()
3192
319334 if (diag_cs%diag_as_chksum) then
31940 fms_id = diag_cs%num_chksum_diags + 1
31950 diag_cs%num_chksum_diags = fms_id
3196 else
3197 fms_id = register_static_field_infra(module_name, field_name, axes%handles, &
3198 long_name=long_name, units=units, missing_value=MOM_missing_value, &
3199 range=range, mask_variant=mask_variant, standard_name=standard_name, &
3200 do_not_log=do_not_log, &
320134 interp_method=interp_method, tile_count=tile_count, area=area)
3202 endif
3203
320434 if (fms_id /= DIAG_FIELD_NOT_FOUND) then
320519 dm_id = get_new_diag_id(diag_cs)
320619 call alloc_diag_with_id(dm_id, diag_cs, diag)
320719 call assert(associated(diag), 'register_static_field: diag allocation failed')
320819 diag%fms_diag_id = fms_id
320919 diag%debug_str = trim(module_name)//"-"//trim(field_name)
321019 if (present(conversion)) diag%conversion_factor = conversion
3211
321219 if (diag_cs%diag_as_chksum) then
32130 diag%axes => axes
3214 else
321519 if (present(x_cell_method)) then
32160 call get_MOM_diag_axis_name(axes%handles(1), axis_name)
3217 call MOM_diag_field_add_attribute(fms_id, 'cell_methods', &
32180 trim(axis_name)//':'//trim(x_cell_method))
3219 endif
322019 if (present(y_cell_method)) then
32210 call get_MOM_diag_axis_name(axes%handles(2), axis_name)
3222 call MOM_diag_field_add_attribute(fms_id, 'cell_methods', &
32230 trim(axis_name)//':'//trim(y_cell_method))
3224 endif
322519 if (present(area_cell_method)) then
3226 call MOM_diag_field_add_attribute(fms_id, 'cell_methods', &
32270 'area:'//trim(area_cell_method))
3228 endif
3229 endif
3230 endif
3231
323234 if (present(cmor_field_name) .and. .not. diag_cs%diag_as_chksum) then
3233 ! Fallback values for strings set to "not provided"
32349 posted_cmor_units = "not provided"
32359 posted_cmor_standard_name = "not provided"
32369 posted_cmor_long_name = "not provided"
3237
3238 ! If attributes are present for MOM variable names, use them first for the register_static_field
3239 ! call for CMOR version of the variable
32409 if (present(units)) posted_cmor_units = units
32419 if (present(standard_name)) posted_cmor_standard_name = standard_name
32429 if (present(long_name)) posted_cmor_long_name = long_name
3243
3244 ! If specified in the call to register_static_field, override attributes with the CMOR versions
32459 if (present(cmor_units)) posted_cmor_units = cmor_units
32469 if (present(cmor_standard_name)) posted_cmor_standard_name = cmor_standard_name
32479 if (present(cmor_long_name)) posted_cmor_long_name = cmor_long_name
3248
3249 fms_id = register_static_field_infra(module_name, cmor_field_name, axes%handles, &
3250 long_name=trim(posted_cmor_long_name), units=trim(posted_cmor_units), &
3251 missing_value=MOM_missing_value, range=range, mask_variant=mask_variant, &
3252 standard_name=trim(posted_cmor_standard_name), do_not_log=do_not_log, &
32539 interp_method=interp_method, tile_count=tile_count, area=area)
32549 if (fms_id /= DIAG_FIELD_NOT_FOUND) then
32557 if (dm_id == -1) then
32567 dm_id = get_new_diag_id(diag_cs)
3257 endif
32587 call alloc_diag_with_id(dm_id, diag_cs, cmor_diag)
32597 cmor_diag%fms_diag_id = fms_id
32607 cmor_diag%debug_str = trim(module_name)//"-"//trim(cmor_field_name)
32617 if (present(conversion)) cmor_diag%conversion_factor = conversion
32627 if (present(x_cell_method)) then
32637 call get_MOM_diag_axis_name(axes%handles(1), axis_name)
32647 call MOM_diag_field_add_attribute(fms_id, 'cell_methods', trim(axis_name)//':'//trim(x_cell_method))
3265 endif
32667 if (present(y_cell_method)) then
32677 call get_MOM_diag_axis_name(axes%handles(2), axis_name)
32687 call MOM_diag_field_add_attribute(fms_id, 'cell_methods', trim(axis_name)//':'//trim(y_cell_method))
3269 endif
32707 if (present(area_cell_method)) then
32717 call MOM_diag_field_add_attribute(fms_id, 'cell_methods', 'area:'//trim(area_cell_method))
3272 endif
3273 endif
3274 endif
3275
327634 dimensions = ""
327734 if (axes%is_h_point) dimensions = trim(dimensions)//" xh, yh,"
327834 if (axes%is_q_point) dimensions = trim(dimensions)//" xq, yq,"
327934 if (axes%is_u_point) dimensions = trim(dimensions)//" xq, yh,"
328034 if (axes%is_v_point) dimensions = trim(dimensions)//" xh, yq,"
328134 if (axes%is_layer) dimensions = trim(dimensions)//" zl,"
328234 if (axes%is_interface) dimensions = trim(dimensions)//" zi,"
328334 if (len_trim(dimensions) > 0) dimensions = trim_trailing_commas(dimensions)
3284
3285 ! Document diagnostics in list of available diagnostics
328634 if (is_root_pe() .and. diag_CS%available_diag_doc_unit > 0) then
328734 if (present(cmor_field_name)) then
3288 call log_available_diag(associated(diag), module_name, field_name, '', '', diag_CS, &
3289 long_name, units, standard_name, &
3290 variants="{"//trim(field_name)//","//trim(cmor_field_name)//"}", &
32919 dimensions=dimensions)
3292 else
3293 call log_available_diag(associated(diag), module_name, field_name, '', '', diag_CS, &
329425 long_name, units, standard_name, dimensions=dimensions)
3295 endif
3296 endif
3297
329834 register_static_field = dm_id
3299
330068end function register_static_field
3301
3302!> Describe an option setting in the diagnostic files.
33034197subroutine describe_option(opt_name, value, diag_CS)
3304 character(len=*), intent(in) :: opt_name !< The name of the option
3305 character(len=*), intent(in) :: value !< A character string with the setting of the option.
3306 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
3307
3308 character(len=480) :: mesg
3309 integer :: len_ind
3310
33114197 len_ind = len_trim(value) ! Add error handling for long values?
3312
33134197 mesg = " ! "//trim(opt_name)//": "//trim(value)
33144197 write(diag_CS%available_diag_doc_unit, '(a)') trim(mesg)
33154197end subroutine describe_option
3316
3317!> Registers a diagnostic using the information encapsulated in the vardesc
3318!! type argument and returns an integer handle to this diagnostic. That
3319!! integer handle is negative if the diagnostic is unused.
33200function ocean_register_diag(var_desc, G, diag_CS, day)
3321 integer :: ocean_register_diag !< An integer handle to this diagnostic.
3322 type(vardesc), intent(in) :: var_desc !< The vardesc type describing the diagnostic
3323 type(ocean_grid_type), intent(in) :: G !< The ocean's grid type
3324 type(diag_ctrl), intent(in), target :: diag_CS !< The diagnostic control structure
3325 type(time_type), intent(in) :: day !< The current model time
3326
3327 character(len=64) :: var_name ! A variable's name.
3328 character(len=48) :: units ! A variable's units.
3329 character(len=240) :: longname ! A variable's longname.
3330 character(len=8) :: hor_grid, z_grid ! Variable grid info.
3331 real :: conversion ! A multiplicative factor for unit conversions for output,
3332 ! as might be needed to convert from intensive to extensive
3333 ! or for dimensional consistency testing [various] or [a A-1 ~> 1]
3334 type(axes_grp), pointer :: axes => NULL()
3335
3336 call query_vardesc(var_desc, units=units, longname=longname, hor_grid=hor_grid, &
33370 z_grid=z_grid, conversion=conversion, caller="ocean_register_diag")
3338
3339 ! Use the hor_grid and z_grid components of vardesc to determine the
3340 ! desired axes to register the diagnostic field for.
33410 select case (z_grid)
3342
3343 case ("L")
33440 select case (hor_grid)
33450 case ("q") ; axes => diag_cs%axesBL
33460 case ("h") ; axes => diag_cs%axesTL
33470 case ("u") ; axes => diag_cs%axesCuL
33480 case ("v") ; axes => diag_cs%axesCvL
33490 case ("Bu") ; axes => diag_cs%axesBL
33500 case ("T") ; axes => diag_cs%axesTL
33510 case ("Cu") ; axes => diag_cs%axesCuL
33520 case ("Cv") ; axes => diag_cs%axesCvL
33530 case ("z") ; axes => diag_cs%axeszL
3354 case default ; call MOM_error(FATAL, "ocean_register_diag: " // &
33550 "unknown hor_grid component "//trim(hor_grid))
3356 end select
3357
3358 case ("i")
33590 select case (hor_grid)
33600 case ("q") ; axes => diag_cs%axesBi
33610 case ("h") ; axes => diag_cs%axesTi
33620 case ("u") ; axes => diag_cs%axesCui
33630 case ("v") ; axes => diag_cs%axesCvi
33640 case ("Bu") ; axes => diag_cs%axesBi
33650 case ("T") ; axes => diag_cs%axesTi
33660 case ("Cu") ; axes => diag_cs%axesCui
33670 case ("Cv") ; axes => diag_cs%axesCvi
33680 case ("z") ; axes => diag_cs%axeszi
3369 case default ; call MOM_error(FATAL, "ocean_register_diag: " // &
33700 "unknown hor_grid component "//trim(hor_grid))
3371 end select
3372
3373 case ("1")
33740 select case (hor_grid)
33750 case ("q") ; axes => diag_cs%axesB1
33760 case ("h") ; axes => diag_cs%axesT1
33770 case ("u") ; axes => diag_cs%axesCu1
33780 case ("v") ; axes => diag_cs%axesCv1
33790 case ("Bu") ; axes => diag_cs%axesB1
33800 case ("T") ; axes => diag_cs%axesT1
33810 case ("Cu") ; axes => diag_cs%axesCu1
33820 case ("Cv") ; axes => diag_cs%axesCv1
3383 case default ; call MOM_error(FATAL, "ocean_register_diag: " // &
33840 "unknown hor_grid component "//trim(hor_grid))
3385 end select
3386
3387 case default
3388 call MOM_error(FATAL,&
33890 "ocean_register_diag: unknown z_grid component "//trim(z_grid))
3390 end select
3391
3392 ocean_register_diag = register_diag_field("ocean_model", trim(var_name), axes, day, &
33930 trim(longname), units=trim(units), conversion=conversion, missing_value=-1.0e+34)
3394
33950end function ocean_register_diag
3396
33971subroutine diag_mediator_infrastructure_init(err_msg)
3398 ! This subroutine initializes the FMS diag_manager.
3399 character(len=*), optional, intent(out) :: err_msg !< An error message
3400
34011 call MOM_diag_manager_init(err_msg=err_msg)
34021end subroutine diag_mediator_infrastructure_init
3403
3404!> diag_mediator_init initializes the MOM diag_mediator and opens the available
3405!! diagnostics file, if appropriate.
34061subroutine diag_mediator_init(G, GV, US, nz, param_file, diag_cs, doc_file_dir)
3407 type(ocean_grid_type), target, intent(inout) :: G !< The ocean grid type.
3408 type(verticalGrid_type), target, intent(in) :: GV !< The ocean vertical grid structure
3409 type(unit_scale_type), target, intent(in) :: US !< A dimensional unit scaling type
3410 integer, intent(in) :: nz !< The number of layers in the model's native grid.
3411 type(param_file_type), intent(in) :: param_file !< Parameter file structure
3412 type(diag_ctrl), intent(inout) :: diag_cs !< A pointer to a type with many variables
3413 !! used for diagnostics
3414 character(len=*), optional, intent(in) :: doc_file_dir !< A directory in which to create the
3415 !! file
3416
3417 ! This subroutine initializes the diag_mediator and the diag_manager.
3418 ! The grid type should have its dimensions set by this point, but it
3419 ! is not necessary that the metrics and axis labels be set up yet.
3420
3421 ! Local variables
3422 integer :: ios, i, new_unit
3423 logical :: opened, new_file
3424 integer :: remap_answer_date ! The vintage of the order of arithmetic and expressions to use
3425 ! for remapping. Values below 20190101 recover the remapping
3426 ! answers from 2018, while higher values use more robust
3427 ! forms of the same remapping expressions.
3428 integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags.
3429 logical :: om4_remap_via_sub_cells ! Use the OM4-era ramap_via_sub_cells for diagnostics
3430 logical :: dz_diag_needed ! Logical set True if we need to store dz_begin for reintegrating
3431 character(len=8) :: this_pe
3432 character(len=240) :: doc_file, doc_file_dflt, doc_path
34331 character(len=240), allocatable :: diag_coords(:)
3434 ! This include declares and sets the variable "version".
3435# include "version_variable.h"
3436 character(len=40) :: mdl = "MOM_diag_mediator" ! This module's name.
3437 character(len=32) :: filename_appendix = '' ! FMS appendix to filename for ensemble runs
3438
34392 id_clock_diag_mediator = cpu_clock_id('(Ocean diagnostics framework)', grain=CLOCK_MODULE)
34401 id_clock_diag_remap = cpu_clock_id('(Ocean diagnostics remapping)', grain=CLOCK_ROUTINE)
34411 id_clock_diag_grid_updates = cpu_clock_id('(Ocean diagnostics grid updates)', grain=CLOCK_ROUTINE)
3442
3443 ! Allocate and initialize list of all diagnostics (and variants)
3444101 allocate(diag_cs%diags(DIAG_ALLOC_CHUNK_SIZE))
34451 diag_cs%next_free_diag_id = 1
3446101 do i=1, DIAG_ALLOC_CHUNK_SIZE
3447101 call initialize_diag_type(diag_cs%diags(i))
3448 enddo
3449
34501 diag_cs%show_call_tree = callTree_showQuery()
3451
3452 ! Read all relevant parameters and write them to the model log.
34531 call log_version(param_file, mdl, version, "")
3454
3455 call get_param(param_file, mdl, 'NUM_DIAG_COORDS', diag_cs%num_diag_coords, &
3456 'The number of diagnostic vertical coordinates to use. '//&
3457 'For each coordinate, an entry in DIAG_COORDS must be provided.', &
34581 default=1)
3459 call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, &
3460 "This sets the default value for the various _ANSWER_DATE parameters.", &
34611 default=99991231)
3462 call get_param(param_file, mdl, "REMAPPING_USE_OM4_SUBCELLS", om4_remap_via_sub_cells, &
34631 do_not_log=.true., default=.true.)
3464 call get_param(param_file, mdl, "DIAG_REMAPPING_USE_OM4_SUBCELLS", om4_remap_via_sub_cells, &
3465 "If true, use the OM4 remapping-via-subcells algorithm for diagnostics. "//&
3466 "See REMAPPING_USE_OM4_SUBCELLS for details. "//&
34671 "We recommend setting this option to false.", default=om4_remap_via_sub_cells)
3468 call get_param(param_file, mdl, "REMAPPING_ANSWER_DATE", remap_answer_date, &
3469 "The vintage of the expressions and order of arithmetic to use for remapping. "//&
3470 "Values below 20190101 result in the use of older, less accurate expressions "//&
3471 "that were in use at the end of 2018. Higher values result in the use of more "//&
3472 "robust and accurate forms of mathematically equivalent expressions.", &
34731 default=default_answer_date, do_not_log=.not.GV%Boussinesq)
34741 if (.not.GV%Boussinesq) remap_answer_date = max(remap_answer_date, 20230701)
3475 call get_param(param_file, mdl, 'USE_INDEX_DIAGNOSTIC_AXES', diag_cs%index_space_axes, &
3476 'If true, use a grid index coordinate convention for diagnostic axes. ',&
34771 default=.false.)
3478
34791 dz_diag_needed = .false.
34801 if (diag_cs%num_diag_coords>0) then
34811 allocate(diag_coords(diag_cs%num_diag_coords))
34821 if (diag_cs%num_diag_coords==1) then ! The default is to provide just one instance of Z*
3483 call get_param(param_file, mdl, 'DIAG_COORDS', diag_coords, &
3484 'A list of string tuples associating diag_table modules to '//&
3485 'a coordinate definition used for diagnostics. Each string '//&
3486 'is of the form "MODULE_SUFFIX PARAMETER_SUFFIX COORDINATE_NAME".', &
34871 default='z Z ZSTAR')
3488 else ! If using more than 1 diagnostic coordinate, all must be explicitly defined
3489 call get_param(param_file, mdl, 'DIAG_COORDS', diag_coords, &
3490 'A list of string tuples associating diag_table modules to '//&
3491 'a coordinate definition used for diagnostics. Each string '//&
3492 'is of the form "MODULE_SUFFIX,PARAMETER_SUFFIX,COORDINATE_NAME".', &
34930 fail_if_missing=.true.)
3494 endif
34952 allocate(diag_cs%diag_remap_cs(diag_cs%num_diag_coords))
3496 ! Initialize each diagnostic vertical coordinate
34972 do i=1, diag_cs%num_diag_coords
34981 call diag_remap_init(diag_cs%diag_remap_cs(i), diag_coords(i), om4_remap_via_sub_cells, remap_answer_date, GV)
34992 if (diag_cs%diag_remap_cs(i)%Z_based_coord) dz_diag_needed = .true.
3500 enddo
35011 deallocate(diag_coords)
3502 endif
3503
3504 call get_param(param_file, mdl, 'DIAG_MISVAL', diag_cs%missing_value, &
3505 'Set the default missing value to use for diagnostics.', &
35061 units="various", default=1.e20)
3507 call get_param(param_file, mdl, 'DIAG_AS_CHKSUM', diag_cs%diag_as_chksum, &
3508 'Instead of writing diagnostics to the diag manager, write '//&
3509 'a text file containing the checksum (bitcount) of the array.', &
35101 default=.false.)
3511
35121 if (diag_cs%diag_as_chksum) &
35130 diag_cs%num_chksum_diags = 0
3514
3515 ! Keep pointers to the grid, h, T, S needed for diagnostic remapping
35161 diag_cs%G => G
35171 diag_cs%GV => GV
35181 diag_cs%US => US
35191 diag_cs%h => null()
35201 diag_cs%T => null()
35211 diag_cs%S => null()
35221 diag_cs%eqn_of_state => null()
35231 diag_cs%tv => null()
3524
35251 allocate(diag_cs%h_begin(G%isd:G%ied,G%jsd:G%jed,nz))
35261 if (dz_diag_needed) allocate(diag_cs%dz_begin(G%isd:G%ied,G%jsd:G%jed,nz))
3527#if defined(DEBUG) || defined(__DO_SAFETY_CHECKS__)
3528 allocate(diag_cs%h_old(G%isd:G%ied,G%jsd:G%jed,nz))
3529 diag_cs%h_old(:,:,:) = 0.0
3530#endif
3531
35321 diag_cs%is = G%isc - (G%isd-1) ; diag_cs%ie = G%iec - (G%isd-1)
35331 diag_cs%js = G%jsc - (G%jsd-1) ; diag_cs%je = G%jec - (G%jsd-1)
35341 diag_cs%isd = G%isd ; diag_cs%ied = G%ied
35351 diag_cs%jsd = G%jsd ; diag_cs%jed = G%jed
3536
3537 ! Downsample indices for dl=2 (should be generalized to arbitrary dl, perhaps via a G array)
35381 diag_cs%dsamp(2)%isc = G%HId2%isc - (G%HId2%isd-1) ; diag_cs%dsamp(2)%iec = G%HId2%iec - (G%HId2%isd-1)
35391 diag_cs%dsamp(2)%jsc = G%HId2%jsc - (G%HId2%jsd-1) ; diag_cs%dsamp(2)%jec = G%HId2%jec - (G%HId2%jsd-1)
35401 diag_cs%dsamp(2)%isd = G%HId2%isd ; diag_cs%dsamp(2)%ied = G%HId2%ied
35411 diag_cs%dsamp(2)%jsd = G%HId2%jsd ; diag_cs%dsamp(2)%jed = G%HId2%jed
35421 diag_cs%dsamp(2)%isg = G%HId2%isg ; diag_cs%dsamp(2)%ieg = G%HId2%ieg
35431 diag_cs%dsamp(2)%jsg = G%HId2%jsg ; diag_cs%dsamp(2)%jeg = G%HId2%jeg
35441 diag_cs%dsamp(2)%isgB = G%HId2%isgB ; diag_cs%dsamp(2)%iegB = G%HId2%iegB
35451 diag_cs%dsamp(2)%jsgB = G%HId2%jsgB ; diag_cs%dsamp(2)%jegB = G%HId2%jegB
3546
3547 ! Initialze available diagnostic log file
35481 if (is_root_pe() .and. (diag_CS%available_diag_doc_unit < 0)) then
35491 write(this_pe,'(i6.6)') PE_here()
35501 doc_file_dflt = "available_diags."//this_pe
3551 call get_param(param_file, mdl, "AVAILABLE_DIAGS_FILE", doc_file, &
3552 "A file into which to write a list of all available "//&
3553 "ocean diagnostics that can be included in a diag_table.", &
35541 default=doc_file_dflt, do_not_log=(diag_CS%available_diag_doc_unit/=-1))
35551 if (len_trim(doc_file) > 0) then
35561 new_file = .true. ; if (diag_CS%available_diag_doc_unit /= -1) new_file = .false.
3557 ! Find an unused unit number.
35585 do new_unit=512,42,-1
35595 inquire( new_unit, opened=opened)
35605 if (.not.opened) exit
3561 enddo
35621 if (opened) call MOM_error(FATAL, &
35630 "diag_mediator_init failed to find an unused unit number.")
3564
35651 doc_path = doc_file
35661 if (present(doc_file_dir)) then ; if (len_trim(doc_file_dir) > 0) then
35671 doc_path = trim(slasher(doc_file_dir))//trim(doc_file)
3568 endif ; endif
3569
35701 diag_CS%available_diag_doc_unit = new_unit
3571
35721 if (new_file) then
3573 open(diag_CS%available_diag_doc_unit, file=trim(doc_path), access='SEQUENTIAL', form='FORMATTED', &
35741 action='WRITE', status='REPLACE', iostat=ios)
3575 else ! This file is being reopened, and should be appended.
3576 open(diag_CS%available_diag_doc_unit, file=trim(doc_path), access='SEQUENTIAL', form='FORMATTED', &
35770 action='WRITE', status='OLD', position='APPEND', iostat=ios)
3578 endif
35791 inquire(diag_CS%available_diag_doc_unit, opened=opened)
35801 if ((.not.opened) .or. (ios /= 0)) then
35810 call MOM_error(FATAL, "Failed to open available diags file "//trim(doc_path)//".")
3582 endif
3583 endif
3584 endif
3585
35861 if (is_root_pe() .and. (diag_CS%chksum_iounit < 0) .and. diag_CS%diag_as_chksum) then
3587 ! write(this_pe,'(i6.6)') PE_here()
3588 ! doc_file_dflt = "chksum_diag."//this_pe
35890 doc_file_dflt = "chksum_diag"
3590 call get_param(param_file, mdl, "CHKSUM_DIAG_FILE", doc_file, &
3591 "A file into which to write all checksums of the "//&
3592 "diagnostics listed in the diag_table.", &
35930 default=doc_file_dflt, do_not_log=(diag_CS%chksum_iounit/=-1))
3594
35950 call get_filename_appendix(filename_appendix)
35960 if (len_trim(filename_appendix) > 0) then
35970 doc_file = trim(doc_file) //'.'//trim(filename_appendix)
3598 endif
3599#ifdef STATSLABEL
3600 doc_file = trim(doc_file)//"."//trim(adjustl(STATSLABEL))
3601#endif
3602
36030 if (len_trim(doc_file) > 0) then
36040 new_file = .true. ; if (diag_CS%chksum_iounit /= -1) new_file = .false.
3605 ! Find an unused unit number.
36060 do new_unit=512,42,-1
36070 inquire( new_unit, opened=opened)
36080 if (.not.opened) exit
3609 enddo
36100 if (opened) call MOM_error(FATAL, &
36110 "diag_mediator_init failed to find an unused unit number.")
3612
36130 doc_path = doc_file
36140 if (present(doc_file_dir)) then ; if (len_trim(doc_file_dir) > 0) then
36150 doc_path = trim(slasher(doc_file_dir))//trim(doc_file)
3616 endif ; endif
3617
36180 diag_CS%chksum_iounit = new_unit
3619
36200 if (new_file) then
3621 open(diag_CS%chksum_iounit, file=trim(doc_path), access='SEQUENTIAL', form='FORMATTED', &
36220 action='WRITE', status='REPLACE', iostat=ios)
3623 else ! This file is being reopened, and should be appended.
3624 open(diag_CS%chksum_iounit, file=trim(doc_path), access='SEQUENTIAL', form='FORMATTED', &
36250 action='WRITE', status='OLD', position='APPEND', iostat=ios)
3626 endif
36270 inquire(diag_CS%chksum_iounit, opened=opened)
36280 if ((.not.opened) .or. (ios /= 0)) then
36290 call MOM_error(FATAL, "Failed to open checksum diags file "//trim(doc_path)//".")
3630 endif
3631 endif
3632 endif
3633
36342end subroutine diag_mediator_init
3635
3636!> Set pointers to the default state fields used to remap diagnostics.
36371subroutine diag_set_state_ptrs(h, tv, diag_cs)
3638 real, dimension(:,:,:), target, intent(in ) :: h !< the model thickness array [H ~> m or kg m-2]
3639 type(thermo_var_ptrs), target, intent(in ) :: tv !< A structure with thermodynamic variables that are
3640 !! used to convert thicknesses to vertical extents
3641 type(diag_ctrl), intent(inout) :: diag_cs !< diag mediator control structure
3642
3643 ! Keep pointers to h, T, S needed for the diagnostic remapping
36441 diag_cs%h => h
36451 diag_cs%T => tv%T
36461 diag_cs%S => tv%S
36471 diag_cs%eqn_of_state => tv%eqn_of_state
36481 diag_cs%tv => tv
3649
36501end subroutine
3651
3652!> Build/update vertical grids for diagnostic remapping.
3653!! \note The target grids need to be updated whenever sea surface
3654!! height changes.
3655193subroutine diag_update_remap_grids(diag_cs, alt_h, alt_T, alt_S, update_intensive, update_extensive )
3656 type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
3657 real, target, optional, intent(in ) :: alt_h(:,:,:) !< Used if remapped grids should be something other than
3658 !! the current thicknesses [H ~> m or kg m-2]
3659 real, target, optional, intent(in ) :: alt_T(:,:,:) !< Used if remapped grids should be something other than
3660 !! the current temperatures [C ~> degC]
3661 real, target, optional, intent(in ) :: alt_S(:,:,:) !< Used if remapped grids should be something other than
3662 !! the current salinity [S ~> ppt]
3663 logical, optional, intent(in ) :: update_intensive !< If true (default), update the grids used for
3664 !! intensive diagnostics
3665 logical, optional, intent(in ) :: update_extensive !< If true (not default), update the grids used for
3666 !! intensive diagnostics
3667 ! Local variables
3668 integer :: m
3669 real, dimension(:,:,:), pointer :: h_diag => NULL() ! The layer thicknesses for diagnostics [H ~> m or kg m-2]
3670 real, dimension(:,:,:), pointer :: T_diag => NULL() ! The layer temperatures for diagnostics [C ~> degC]
3671 real, dimension(:,:,:), pointer :: S_diag => NULL() ! The layer salinities for diagnostics [S ~> ppt]
3672 real, dimension(diag_cs%G%isd:diag_cS%G%ied, diag_cs%G%jsd:diag_cS%G%jed, diag_cs%GV%ke) :: &
3673386 dz_diag ! Layer vertical extents for remapping [Z ~> m]
3674 logical :: update_intensive_local, update_extensive_local, dz_diag_needed
3675
3676193 if (diag_cs%show_call_tree) call callTree_enter("diag_update_remap_grids()")
3677
3678 ! Set values based on optional input arguments
3679193 if (present(alt_h)) then
36800 h_diag => alt_h
3681 else
3682193 h_diag => diag_cs%h
3683 endif
3684
3685193 if (present(alt_T)) then
36860 T_diag => alt_T
3687 else
3688193 T_diag => diag_CS%T
3689 endif
3690
3691193 if (present(alt_S)) then
36920 S_diag => alt_S
3693 else
3694193 S_diag => diag_CS%S
3695 endif
3696
3697 ! Defaults here are based on wanting to update intensive quantities frequently as soon as the model state changes.
3698 ! Conversely, for extensive quantities, in an effort to close budgets and to be consistent with the total time
3699 ! tendency, we construct the diagnostic grid at the beginning of the baroclinic timestep and remap all extensive
3700 ! quantities to the same grid
3701193 update_intensive_local = .true.
3702193 if (present(update_intensive)) update_intensive_local = update_intensive
3703193 update_extensive_local = .false.
3704193 if (present(update_extensive)) update_extensive_local = update_extensive
3705
3706193 if (id_clock_diag_grid_updates>0) call cpu_clock_begin(id_clock_diag_grid_updates)
3707
3708193 if (diag_cs%diag_grid_overridden) then
3709 call MOM_error(FATAL, "diag_update_remap_grids was called, but current grids in "// &
37100 "diagnostic structure have been overridden")
3711 endif
3712
3713 ! Determine the diagnostic grid spacing in height units, if it is needed.
3714193 dz_diag_needed = .false.
3715193 if (update_intensive_local .or. update_extensive_local) then
3716386 do m=1, diag_cs%num_diag_coords
3717386 if (diag_cs%diag_remap_cs(m)%Z_based_coord) dz_diag_needed = .true.
3718 enddo
3719 endif
3720193 if (dz_diag_needed) then
37210 call thickness_to_dz(h_diag, diag_cs%tv, dz_diag, diag_cs%G, diag_cs%GV, diag_cs%US, halo_size=1)
3722 endif
3723
3724193 if (update_intensive_local) then
3725338 do m=1, diag_cs%num_diag_coords
3726338 if (diag_cs%diag_remap_cs(m)%Z_based_coord) then
3727 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, &
37280 diag_cs%eqn_of_state, diag_cs%diag_remap_cs(m)%h)
3729 else
3730 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, &
3731169 diag_cs%eqn_of_state, diag_cs%diag_remap_cs(m)%h)
3732 endif
3733 enddo
3734 endif
3735193 if (update_extensive_local) then
373615791424 diag_cs%h_begin(:,:,:) = diag_cs%h(:,:,:)
373724 if (dz_diag_needed) diag_cs%dz_begin(:,:,:) = dz_diag(:,:,:)
373848 do m=1, diag_cs%num_diag_coords
373948 if (diag_cs%diag_remap_cs(m)%Z_based_coord) then
3740 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, &
37410 diag_cs%eqn_of_state, diag_cs%diag_remap_cs(m)%h_extensive)
3742 else
3743 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, &
374424 diag_cs%eqn_of_state, diag_cs%diag_remap_cs(m)%h_extensive)
3745 endif
3746 enddo
3747 endif
3748
3749#if defined(DEBUG) || defined(__DO_SAFETY_CHECKS__)
3750 ! Keep a copy of H - used to check whether grids are up-to-date
3751 ! when doing remapping.
3752 diag_cs%h_old(:,:,:) = diag_cs%h(:,:,:)
3753#endif
3754
3755193 if (id_clock_diag_grid_updates>0) call cpu_clock_end(id_clock_diag_grid_updates)
3756
3757193 if (diag_cs%show_call_tree) call callTree_leave("diag_update_remap_grids()")
3758
3759193end subroutine diag_update_remap_grids
3760
3761!> Sets up the 2d and 3d masks for native diagnostics
37621subroutine diag_masks_set(G, nz, diag_cs)
3763 type(ocean_grid_type), target, intent(in) :: G !< The ocean grid type.
3764 integer, intent(in) :: nz !< The number of layers in the model's native grid.
3765 type(diag_ctrl), pointer :: diag_cs !< A pointer to a type with many variables
3766 !! used for diagnostics
3767 ! Local variables
3768 integer :: k
3769
3770 ! 2d masks point to the model masks since they are identical
37711 diag_cs%mask2dT => G%mask2dT
37721 diag_cs%mask2dBu => G%mask2dBu
37731 diag_cs%mask2dCu => G%mask2dCu
37741 diag_cs%mask2dCv => G%mask2dCv
3775
3776 ! 3d native masks are needed by diag_manager but the native variables
3777 ! can only be masked 2d - for ocean points, all layers exists.
37781 allocate(diag_cs%mask3dTL(G%isd:G%ied,G%jsd:G%jed,1:nz))
37791 allocate(diag_cs%mask3dBL(G%IsdB:G%IedB,G%JsdB:G%JedB,1:nz))
37801 allocate(diag_cs%mask3dCuL(G%IsdB:G%IedB,G%jsd:G%jed,1:nz))
37811 allocate(diag_cs%mask3dCvL(G%isd:G%ied,G%JsdB:G%JedB,1:nz))
378276 do k=1,nz
3783657975 diag_cs%mask3dTL(:,:,k) = diag_cs%mask2dT(:,:)
3784672825 diag_cs%mask3dBL(:,:,k) = diag_cs%mask2dBu(:,:)
3785663075 diag_cs%mask3dCuL(:,:,k) = diag_cs%mask2dCu(:,:)
3786667651 diag_cs%mask3dCvL(:,:,k) = diag_cs%mask2dCv(:,:)
3787 enddo
37881 allocate(diag_cs%mask3dTi(G%isd:G%ied,G%jsd:G%jed,1:nz+1))
37891 allocate(diag_cs%mask3dBi(G%IsdB:G%IedB,G%JsdB:G%JedB,1:nz+1))
37901 allocate(diag_cs%mask3dCui(G%IsdB:G%IedB,G%jsd:G%jed,1:nz+1))
37911 allocate(diag_cs%mask3dCvi(G%isd:G%ied,G%JsdB:G%JedB,1:nz+1))
379277 do k=1,nz+1
3793666748 diag_cs%mask3dTi(:,:,k) = diag_cs%mask2dT(:,:)
3794681796 diag_cs%mask3dBi(:,:,k) = diag_cs%mask2dBu(:,:)
3795671916 diag_cs%mask3dCui(:,:,k) = diag_cs%mask2dCu(:,:)
3796676553 diag_cs%mask3dCvi(:,:,k) = diag_cs%mask2dCv(:,:)
3797 enddo
3798
3799 ! Allocate and initialize the downsampled masks
38001 call downsample_diag_masks_set(G, nz, diag_cs)
3801
38021end subroutine diag_masks_set
3803
3804!> Set the extents and fill values for the piecemeal buffers for all axes
38051subroutine set_piecemeal_extents(diag_cs)
3806 type(diag_ctrl), intent(inout) :: diag_cs !< A pointer to a type with many variables
3807 !! used for diagnostics
3808
3809 ! Piecemeal buffers for 2d axes
38101 call diag_cs%axesT1%piecemeal_2d%set_extents_from_array(diag_cs%mask2dT, diag_cs%missing_value)
38111 call diag_cs%axesB1%piecemeal_2d%set_extents_from_array(diag_cs%mask2dBu, diag_cs%missing_value)
38121 call diag_cs%axesCu1%piecemeal_2d%set_extents_from_array(diag_cs%mask2dCu, diag_cs%missing_value)
38131 call diag_cs%axesCv1%piecemeal_2d%set_extents_from_array(diag_cs%mask2dCv, diag_cs%missing_value)
3814
3815 ! Piecemeal buffers for 3d axes
38161 call diag_cs%axesTL%piecemeal_3d%set_extents_from_array(diag_cs%mask3dTL, diag_cs%missing_value)
38171 call diag_cs%axesBL%piecemeal_3d%set_extents_from_array(diag_cs%mask3dBL, diag_cs%missing_value)
38181 call diag_cs%axesCuL%piecemeal_3d%set_extents_from_array(diag_cs%mask3dCuL, diag_cs%missing_value)
38191 call diag_cs%axesCvL%piecemeal_3d%set_extents_from_array(diag_cs%mask3dCvL, diag_cs%missing_value)
38201 call diag_cs%axesTi%piecemeal_3d%set_extents_from_array(diag_cs%mask3dTi, diag_cs%missing_value)
38211 call diag_cs%axesBi%piecemeal_3d%set_extents_from_array(diag_cs%mask3dBi, diag_cs%missing_value)
38221 call diag_cs%axesCui%piecemeal_3d%set_extents_from_array(diag_cs%mask3dCui, diag_cs%missing_value)
38231 call diag_cs%axesCvi%piecemeal_3d%set_extents_from_array(diag_cs%mask3dCvi, diag_cs%missing_value)
3824
38251end subroutine set_piecemeal_extents
3826
38271subroutine diag_mediator_close_registration(diag_CS)
3828 type(diag_ctrl), intent(inout) :: diag_CS !< Structure used to regulate diagnostic output
3829
3830 integer :: i
3831
38321 if (diag_CS%available_diag_doc_unit > -1) then
38331 close(diag_CS%available_diag_doc_unit) ; diag_CS%available_diag_doc_unit = -2
3834 endif
3835
38362 do i=1, diag_cs%num_diag_coords
38372 call diag_remap_diag_registration_closed(diag_cs%diag_remap_cs(i))
3838 enddo
3839
38401end subroutine diag_mediator_close_registration
3841
384210subroutine axes_grp_end(axes)
3843 type(axes_grp), intent(inout) :: axes !< Axes group to be destroyed
3844
384510 deallocate(axes%handles)
384610 if (associated(axes%mask2d)) deallocate(axes%mask2d)
384710 if (associated(axes%mask3d)) deallocate(axes%mask3d)
384810end subroutine axes_grp_end
3849
38501subroutine diag_mediator_end(time, diag_CS, end_diag_manager)
3851 type(time_type), intent(in) :: time !< The current model time
3852 type(diag_ctrl), intent(inout) :: diag_CS !< Structure used to regulate diagnostic output
3853 logical, optional, intent(in) :: end_diag_manager !< If true, call diag_manager_end()
3854
3855 ! Local variables
3856 type(diag_type), pointer :: diag, next_diag
3857 integer :: i, dl
3858
38591 if (diag_CS%available_diag_doc_unit > -1) then
38600 close(diag_CS%available_diag_doc_unit) ; diag_CS%available_diag_doc_unit = -3
3861 endif
38621 if (diag_CS%chksum_iounit > -1) then
38630 close(diag_CS%chksum_iounit) ; diag_CS%chksum_iounit = -3
3864 endif
3865
386641 do i=1, diag_cs%next_free_diag_id - 1
386741 if (associated(diag_cs%diags(i)%next)) then
38680 next_diag => diag_cs%diags(i)%next
38690 do while (associated(next_diag))
38700 diag => next_diag
38710 next_diag => diag%next
38720 deallocate(diag)
3873 enddo
3874 endif
3875 enddo
3876
38771 deallocate(diag_cs%diags)
3878
38792 do i=1, diag_cs%num_diag_coords
38802 call diag_remap_end(diag_cs%diag_remap_cs(i))
3881 enddo
3882
38831 call diag_grid_storage_end(diag_cs%diag_grid_temp)
38841 if (associated(diag_cs%mask3dTL)) deallocate(diag_cs%mask3dTL)
38851 if (associated(diag_cs%mask3dBL)) deallocate(diag_cs%mask3dBL)
38861 if (associated(diag_cs%mask3dCuL)) deallocate(diag_cs%mask3dCuL)
38871 if (associated(diag_cs%mask3dCvL)) deallocate(diag_cs%mask3dCvL)
38881 if (associated(diag_cs%mask3dTi)) deallocate(diag_cs%mask3dTi)
38891 if (associated(diag_cs%mask3dBi)) deallocate(diag_cs%mask3dBi)
38901 if (associated(diag_cs%mask3dCui)) deallocate(diag_cs%mask3dCui)
38911 if (associated(diag_cs%mask3dCvi)) deallocate(diag_cs%mask3dCvi)
38922 do dl=2,MAX_DSAMP_LEV
38931 if (associated(diag_cs%dsamp(dl)%mask2dT)) deallocate(diag_cs%dsamp(dl)%mask2dT)
38941 if (associated(diag_cs%dsamp(dl)%mask2dBu)) deallocate(diag_cs%dsamp(dl)%mask2dBu)
38951 if (associated(diag_cs%dsamp(dl)%mask2dCu)) deallocate(diag_cs%dsamp(dl)%mask2dCu)
38961 if (associated(diag_cs%dsamp(dl)%mask2dCv)) deallocate(diag_cs%dsamp(dl)%mask2dCv)
38971 if (associated(diag_cs%dsamp(dl)%mask3dTL)) deallocate(diag_cs%dsamp(dl)%mask3dTL)
38981 if (associated(diag_cs%dsamp(dl)%mask3dBL)) deallocate(diag_cs%dsamp(dl)%mask3dBL)
38991 if (associated(diag_cs%dsamp(dl)%mask3dCuL)) deallocate(diag_cs%dsamp(dl)%mask3dCuL)
39001 if (associated(diag_cs%dsamp(dl)%mask3dCvL)) deallocate(diag_cs%dsamp(dl)%mask3dCvL)
39011 if (associated(diag_cs%dsamp(dl)%mask3dTi)) deallocate(diag_cs%dsamp(dl)%mask3dTi)
39021 if (associated(diag_cs%dsamp(dl)%mask3dBi)) deallocate(diag_cs%dsamp(dl)%mask3dBi)
39031 if (associated(diag_cs%dsamp(dl)%mask3dCui)) deallocate(diag_cs%dsamp(dl)%mask3dCui)
39041 if (associated(diag_cs%dsamp(dl)%mask3dCvi)) deallocate(diag_cs%dsamp(dl)%mask3dCvi)
3905
39063 do i=1,diag_cs%num_diag_coords
39071 if (associated(diag_cs%dsamp(dl)%remap_axesTL(i)%dsamp(dl)%mask3d)) &
39081 deallocate(diag_cs%dsamp(dl)%remap_axesTL(i)%dsamp(dl)%mask3d)
39091 if (associated(diag_cs%dsamp(dl)%remap_axesCuL(i)%dsamp(dl)%mask3d)) &
39101 deallocate(diag_cs%dsamp(dl)%remap_axesCuL(i)%dsamp(dl)%mask3d)
39111 if (associated(diag_cs%dsamp(dl)%remap_axesCvL(i)%dsamp(dl)%mask3d)) &
39121 deallocate(diag_cs%dsamp(dl)%remap_axesCvL(i)%dsamp(dl)%mask3d)
39131 if (associated(diag_cs%dsamp(dl)%remap_axesBL(i)%dsamp(dl)%mask3d)) &
39141 deallocate(diag_cs%dsamp(dl)%remap_axesBL(i)%dsamp(dl)%mask3d)
39151 if (associated(diag_cs%dsamp(dl)%remap_axesTi(i)%dsamp(dl)%mask3d)) &
39161 deallocate(diag_cs%dsamp(dl)%remap_axesTi(i)%dsamp(dl)%mask3d)
39171 if (associated(diag_cs%dsamp(dl)%remap_axesCui(i)%dsamp(dl)%mask3d)) &
39181 deallocate(diag_cs%dsamp(dl)%remap_axesCui(i)%dsamp(dl)%mask3d)
39191 if (associated(diag_cs%dsamp(dl)%remap_axesCvi(i)%dsamp(dl)%mask3d)) &
39201 deallocate(diag_cs%dsamp(dl)%remap_axesCvi(i)%dsamp(dl)%mask3d)
39211 if (associated(diag_cs%dsamp(dl)%remap_axesBi(i)%dsamp(dl)%mask3d)) &
39222 deallocate(diag_cs%dsamp(dl)%remap_axesBi(i)%dsamp(dl)%mask3d)
3923 enddo
3924 enddo
3925
3926 ! axes_grp masks may point to diag_cs masks, so do these after mask dealloc
39272 do i=1, diag_cs%num_diag_coords
39281 call axes_grp_end(diag_cs%remap_axesZL(i))
39291 call axes_grp_end(diag_cs%remap_axesZi(i))
39301 call axes_grp_end(diag_cs%remap_axesTL(i))
39311 call axes_grp_end(diag_cs%remap_axesTi(i))
39321 call axes_grp_end(diag_cs%remap_axesBL(i))
39331 call axes_grp_end(diag_cs%remap_axesBi(i))
39341 call axes_grp_end(diag_cs%remap_axesCuL(i))
39351 call axes_grp_end(diag_cs%remap_axesCui(i))
39361 call axes_grp_end(diag_cs%remap_axesCvL(i))
39372 call axes_grp_end(diag_cs%remap_axesCvi(i))
3938 enddo
3939
39401 if (diag_cs%num_diag_coords > 0) then
39413 deallocate(diag_cs%remap_axesZL)
39423 deallocate(diag_cs%remap_axesZi)
39433 deallocate(diag_cs%remap_axesTL)
39443 deallocate(diag_cs%remap_axesTi)
39453 deallocate(diag_cs%remap_axesBL)
39463 deallocate(diag_cs%remap_axesBi)
39473 deallocate(diag_cs%remap_axesCuL)
39483 deallocate(diag_cs%remap_axesCui)
39493 deallocate(diag_cs%remap_axesCvL)
39503 deallocate(diag_cs%remap_axesCvi)
3951 endif
3952
39532 do dl=2,MAX_DSAMP_LEV
39541 if (allocated(diag_cs%dsamp(dl)%remap_axesTL)) &
39553 deallocate(diag_cs%dsamp(dl)%remap_axesTL)
39561 if (allocated(diag_cs%dsamp(dl)%remap_axesTi)) &
39573 deallocate(diag_cs%dsamp(dl)%remap_axesTi)
39581 if (allocated(diag_cs%dsamp(dl)%remap_axesBL)) &
39593 deallocate(diag_cs%dsamp(dl)%remap_axesBL)
39601 if (allocated(diag_cs%dsamp(dl)%remap_axesBi)) &
39613 deallocate(diag_cs%dsamp(dl)%remap_axesBi)
39621 if (allocated(diag_cs%dsamp(dl)%remap_axesCuL)) &
39633 deallocate(diag_cs%dsamp(dl)%remap_axesCuL)
39641 if (allocated(diag_cs%dsamp(dl)%remap_axesCui)) &
39653 deallocate(diag_cs%dsamp(dl)%remap_axesCui)
39661 if (allocated(diag_cs%dsamp(dl)%remap_axesCvL)) &
39673 deallocate(diag_cs%dsamp(dl)%remap_axesCvL)
39681 if (allocated(diag_cs%dsamp(dl)%remap_axesCvi)) &
39694 deallocate(diag_cs%dsamp(dl)%remap_axesCvi)
3970 enddo
3971
3972
3973#if defined(DEBUG) || defined(__DO_SAFETY_CHECKS__)
3974 deallocate(diag_cs%h_old)
3975#endif
3976
39771 if (present(end_diag_manager)) then
39781 if (end_diag_manager) call MOM_diag_manager_end(time)
3979 endif
3980
39811end subroutine diag_mediator_end
3982
3983!> Returns a new diagnostic id, it may be necessary to expand the diagnostics array.
398440integer function get_new_diag_id(diag_cs)
3985 type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
3986 ! Local variables
398740 type(diag_type), dimension(:), allocatable :: tmp
3988 integer :: i
3989
399040 if (diag_cs%next_free_diag_id > size(diag_cs%diags)) then
3991 call assert(diag_cs%next_free_diag_id - size(diag_cs%diags) == 1, &
39920 'get_new_diag_id: inconsistent diag id')
3993
3994 ! Increase the size of diag_cs%diags and copy data over.
3995 ! Do not use move_alloc() because it is not supported by Fortran 90
39960 allocate(tmp(size(diag_cs%diags)))
39970 tmp(:) = diag_cs%diags(:)
39980 deallocate(diag_cs%diags)
39990 allocate(diag_cs%diags(size(tmp) + DIAG_ALLOC_CHUNK_SIZE))
40000 diag_cs%diags(1:size(tmp)) = tmp(:)
40010 deallocate(tmp)
4002
4003 ! Initialize new part of the diag array.
40040 do i=diag_cs%next_free_diag_id, size(diag_cs%diags)
40050 call initialize_diag_type(diag_cs%diags(i))
4006 enddo
4007 endif
4008
400940 get_new_diag_id = diag_cs%next_free_diag_id
401040 diag_cs%next_free_diag_id = diag_cs%next_free_diag_id + 1
4011
401240end function get_new_diag_id
4013
4014!> Initializes a diag_type (used after allocating new memory)
4015100subroutine initialize_diag_type(diag)
4016 type(diag_type), intent(inout) :: diag !< diag_type to be initialized
4017
4018100 diag%in_use = .false.
4019100 diag%fms_diag_id = -1
4020100 diag%axes => null()
4021100 diag%next => null()
4022100 diag%conversion_factor = 0.
4023
4024100end subroutine initialize_diag_type
4025
4026!> Make a new diagnostic. Either use memory which is in the array of 'primary'
4027!! diagnostics, or if that is in use, insert it to the list of secondary diagnostics.
402840subroutine alloc_diag_with_id(diag_id, diag_cs, diag)
4029 integer, intent(in ) :: diag_id !< id for the diagnostic
4030 type(diag_ctrl), target, intent(inout) :: diag_cs !< structure used to regulate diagnostic output
4031 type(diag_type), pointer :: diag !< structure representing a diagnostic (inout)
4032
4033 type(diag_type), pointer :: tmp => NULL()
4034
403540 if (.not. diag_cs%diags(diag_id)%in_use) then
403640 diag => diag_cs%diags(diag_id)
4037 else
40380 allocate(diag)
40390 tmp => diag_cs%diags(diag_id)%next
40400 diag_cs%diags(diag_id)%next => diag
40410 diag%next => tmp
4042 endif
404340 diag%in_use = .true.
4044
404540end subroutine alloc_diag_with_id
4046
4047!> Log a diagnostic to the available diagnostics file.
4048773subroutine log_available_diag(used, module_name, field_name, cell_methods_string, comment, &
4049 diag_CS, long_name, units, standard_name, variants, dimensions)
4050 logical, intent(in) :: used !< Whether this diagnostic was in the diag_table or not
4051 character(len=*), intent(in) :: module_name !< Name of the diagnostic module
4052 character(len=*), intent(in) :: field_name !< Name of this diagnostic field
4053 character(len=*), intent(in) :: cell_methods_string !< The spatial component of the CF cell_methods attribute
4054 character(len=*), intent(in) :: comment !< A comment to append after [Used|Unused]
4055 type(diag_ctrl), intent(in) :: diag_CS !< The diagnostics control structure
4056 character(len=*), optional, intent(in) :: dimensions !< Descriptor of the horizontal and vertical dimensions
4057 character(len=*), optional, intent(in) :: long_name !< CF long name of diagnostic
4058 character(len=*), optional, intent(in) :: units !< Units for diagnostic
4059 character(len=*), optional, intent(in) :: standard_name !< CF standardized name of diagnostic
4060 character(len=*), optional, intent(in) :: variants !< Alternate modules and variable names for
4061 !! this diagnostic and derived diagnostics
4062 ! Local variables
4063 character(len=240) :: mesg
4064
4065773 if (used) then
406633 mesg = '"'//trim(field_name)//'" [Used]'
4067 else
4068740 mesg = '"'//trim(field_name)//'" [Unused]'
4069 endif
4070773 if (len(trim((comment)))>0) then
407152 write(diag_CS%available_diag_doc_unit, '(a,1x,"(",a,")")') trim(mesg),trim(comment)
4072 else
4073721 write(diag_CS%available_diag_doc_unit, '(a)') trim(mesg)
4074 endif
4075773 call describe_option("modules", module_name, diag_CS)
4076773 if (present(dimensions)) then
4077773 if (len(trim(dimensions)) > 0) then
4078771 call describe_option("dimensions", dimensions, diag_CS)
4079 endif
4080 endif
4081773 if (present(long_name)) call describe_option("long_name", long_name, diag_CS)
4082773 if (present(units)) call describe_option("units", units, diag_CS)
4083773 if (present(standard_name)) &
408465 call describe_option("standard_name", standard_name, diag_CS)
4085773 if (len(trim((cell_methods_string)))>0) &
4086656 call describe_option("cell_methods", trim(cell_methods_string), diag_CS)
4087773 if (present(variants)) then ; if (len(trim(variants)) > 0) then
4088386 call describe_option("variants", variants, diag_CS)
4089 endif ; endif
4090773end subroutine log_available_diag
4091
4092!> Log the diagnostic chksum to the chksum diag file
40930subroutine log_chksum_diag(docunit, description, chksum)
4094 integer, intent(in) :: docunit !< Handle of the log file
4095 character(len=*), intent(in) :: description !< Name of the diagnostic module
4096 integer, intent(in) :: chksum !< chksum of the diagnostic
4097
40980 write(docunit, '(a,1x,i9.8)') description, chksum
40990 flush(docunit)
4100
41010end subroutine log_chksum_diag
4102
4103!> Allocates fields necessary to store diagnostic remapping fields
41044subroutine diag_grid_storage_init(grid_storage, G, GV, diag)
4105 type(diag_grid_storage), intent(inout) :: grid_storage !< Structure containing a snapshot of the target grids
4106 type(ocean_grid_type), intent(in) :: G !< Horizontal grid
4107 type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure
4108 type(diag_ctrl), intent(in) :: diag !< Diagnostic control structure used as the constructor
4109 !! template for this routine
4110
4111 integer :: m, nz
41124 grid_storage%num_diag_coords = diag%num_diag_coords
4113
4114 ! Don't do anything else if there are no remapped coordinates
41154 if (grid_storage%num_diag_coords < 1) return
4116
4117 ! Allocate memory for the native space
41184 allocate( grid_storage%h_state(G%isd:G%ied, G%jsd:G%jed, GV%ke))
4119 ! Allocate diagnostic remapping structures
41208 allocate(grid_storage%diag_grids(diag%num_diag_coords))
4121 ! Loop through and allocate memory for the grid on each target coordinate
41228 do m = 1, diag%num_diag_coords
41234 nz = diag%diag_remap_cs(m)%nz
41244 allocate(grid_storage%diag_grids(m)%h(G%isd:G%ied,G%jsd:G%jed, nz))
4125 enddo
4126
4127end subroutine diag_grid_storage_init
4128
4129!> Copy from the main diagnostic arrays to the grid storage as well as the native thicknesses
413025subroutine diag_copy_diag_to_storage(grid_storage, h_state, diag)
4131 type(diag_grid_storage), intent(inout) :: grid_storage !< Structure containing a snapshot of the target grids
4132 real, dimension(:,:,:), intent(in) :: h_state !< Current model thicknesses [H ~> m or kg m-2]
4133 type(diag_ctrl), intent(in) :: diag !< Diagnostic control structure used as the constructor
4134
4135 integer :: m
4136
4137 ! Don't do anything else if there are no remapped coordinates
413825 if (grid_storage%num_diag_coords < 1) return
4139
414016449400 grid_storage%h_state(:,:,:) = h_state(:,:,:)
414150 do m = 1,grid_storage%num_diag_coords
414225 if (diag%diag_remap_cs(m)%nz > 0) &
41437237750 grid_storage%diag_grids(m)%h(:,:,:) = diag%diag_remap_cs(m)%h(:,:,:)
4144 enddo
4145
4146end subroutine diag_copy_diag_to_storage
4147
4148!> Copy from the stored diagnostic arrays to the main diagnostic grids
414936subroutine diag_copy_storage_to_diag(diag, grid_storage)
4150 type(diag_ctrl), intent(inout) :: diag !< Diagnostic control structure used as the constructor
4151 type(diag_grid_storage), intent(in) :: grid_storage !< Structure containing a snapshot of the target grids
4152
4153 integer :: m
4154
4155 ! Don't do anything else if there are no remapped coordinates
415636 if (grid_storage%num_diag_coords < 1) return
4157
415836 diag%diag_grid_overridden = .true.
415972 do m = 1,grid_storage%num_diag_coords
416036 if (diag%diag_remap_cs(m)%nz > 0) &
416110422360 diag%diag_remap_cs(m)%h(:,:,:) = grid_storage%diag_grids(m)%h(:,:,:)
4162 enddo
4163
4164end subroutine diag_copy_storage_to_diag
4165
4166!> Save the current diagnostic grids in the temporary structure within diag
416736subroutine diag_save_grids(diag)
4168 type(diag_ctrl), intent(inout) :: diag !< Diagnostic control structure used as the constructor
4169
4170 integer :: m
4171
4172 ! Don't do anything else if there are no remapped coordinates
417336 if (diag%num_diag_coords < 1) return
4174
417572 do m = 1,diag%num_diag_coords
417636 if (diag%diag_remap_cs(m)%nz > 0) &
417710422360 diag%diag_grid_temp%diag_grids(m)%h(:,:,:) = diag%diag_remap_cs(m)%h(:,:,:)
4178 enddo
4179
4180end subroutine diag_save_grids
4181
4182!> Restore the diagnostic grids from the temporary structure within diag
418336subroutine diag_restore_grids(diag)
4184 type(diag_ctrl), intent(inout) :: diag !< Diagnostic control structure used as the constructor
4185
4186 integer :: m
4187
4188 ! Don't do anything else if there are no remapped coordinates
418936 if (diag%num_diag_coords < 1) return
4190
419136 diag%diag_grid_overridden = .false.
419272 do m = 1,diag%num_diag_coords
419336 if (diag%diag_remap_cs(m)%nz > 0) &
419410422360 diag%diag_remap_cs(m)%h(:,:,:) = diag%diag_grid_temp%diag_grids(m)%h(:,:,:)
4195 enddo
4196
4197end subroutine diag_restore_grids
4198
4199!> Deallocates the fields in the remapping fields container
42001subroutine diag_grid_storage_end(grid_storage)
4201 type(diag_grid_storage), intent(inout) :: grid_storage !< Structure containing a snapshot of the target grids
4202 ! Local variables
4203 integer :: m
4204
4205 ! Don't do anything else if there are no remapped coordinates
42061 if (grid_storage%num_diag_coords < 1) return
4207
4208 ! Deallocate memory for the native space
42091 deallocate(grid_storage%h_state)
4210 ! Loop through and deallocate memory for the grid on each target coordinate
42112 do m = 1, grid_storage%num_diag_coords
42122 deallocate(grid_storage%diag_grids(m)%h)
4213 enddo
4214 ! Deallocate diagnostic remapping structures
42153 deallocate(grid_storage%diag_grids)
4216end subroutine diag_grid_storage_end
4217
4218!< Allocate and initialize the masks for downsampled diagnostics in diag_cs
4219!! The downsampled masks in the axes would later "point" to these.
42201subroutine downsample_diag_masks_set(G, nz, diag_cs)
4221 type(ocean_grid_type), target, intent(in) :: G !< The ocean grid type.
4222 integer, intent(in) :: nz !< The number of layers in the model's native grid.
4223 type(diag_ctrl), pointer :: diag_cs !< A pointer to a type with many variables
4224 !! used for diagnostics
4225 ! Local variables
4226 integer :: k, dl
4227
4228!print*,'original c extents ',G%isc,G%iec,G%jsc,G%jec
4229!print*,'original c extents ',G%iscb,G%iecb,G%jscb,G%jecb
4230!print*,'coarse c extents ',G%HId2%isc,G%HId2%iec,G%HId2%jsc,G%HId2%jec
4231!print*,'original d extents ',G%isd,G%ied,G%jsd,G%jed
4232!print*,'coarse d extents ',G%HId2%isd,G%HId2%ied,G%HId2%jsd,G%HId2%jed
4233! original c extents 5 52 5 52
4234! original cB-nonsym extents 5 52 5 52
4235! original cB-sym extents 4 52 4 52
4236! coarse c extents 3 26 3 26
4237! original d extents 1 56 1 56
4238! original dB-nonsym extents 1 56 1 56
4239! original dB-sym extents 0 56 0 56
4240! coarse d extents 1 28 1 28
4241
42422 do dl=2,MAX_DSAMP_LEV
4243 ! 2d mask
4244 call downsample_mask(G%mask2dT, diag_cs%dsamp(dl)%mask2dT, dl, G%isc, G%jsc, G%isd, G%jsd, &
42451 G%HId2%isc, G%HId2%iec, G%HId2%jsc, G%HId2%jec, G%HId2%isd, G%HId2%ied, G%HId2%jsd, G%HId2%jed)
4246 call downsample_mask(G%mask2dBu, diag_cs%dsamp(dl)%mask2dBu, dl,G%IscB, G%JscB, G%IsdB, G%JsdB, &
42471 G%HId2%IscB,G%HId2%IecB, G%HId2%JscB,G%HId2%JecB,G%HId2%IsdB,G%HId2%IedB,G%HId2%JsdB,G%HId2%JedB)
4248 call downsample_mask(G%mask2dCu, diag_cs%dsamp(dl)%mask2dCu, dl, G%IscB, G%jsc, G%IsdB, G%jsd, &
42491 G%HId2%IscB,G%HId2%IecB, G%HId2%jsc, G%HId2%jec,G%HId2%IsdB,G%HId2%IedB,G%HId2%jsd, G%HId2%jed)
4250 call downsample_mask(G%mask2dCv, diag_cs%dsamp(dl)%mask2dCv, dl,G %isc ,G%JscB, G%isd, G%JsdB, &
42511 G%HId2%isc ,G%HId2%iec, G%HId2%JscB,G%HId2%JecB,G%HId2%isd ,G%HId2%ied, G%HId2%JsdB,G%HId2%JedB)
4252 ! 3d native masks are needed by diag_manager but the native variables
4253 ! can only be masked 2d - for ocean points, all layers exists.
42541 allocate(diag_cs%dsamp(dl)%mask3dTL(G%HId2%isd:G%HId2%ied,G%HId2%jsd:G%HId2%jed,1:nz))
42551 allocate(diag_cs%dsamp(dl)%mask3dBL(G%HId2%IsdB:G%HId2%IedB,G%HId2%JsdB:G%HId2%JedB,1:nz))
42561 allocate(diag_cs%dsamp(dl)%mask3dCuL(G%HId2%IsdB:G%HId2%IedB,G%HId2%jsd:G%HId2%jed,1:nz))
42571 allocate(diag_cs%dsamp(dl)%mask3dCvL(G%HId2%isd:G%HId2%ied,G%HId2%JsdB:G%HId2%JedB,1:nz))
425876 do k=1,nz
4259165825 diag_cs%dsamp(dl)%mask3dTL(:,:,k) = diag_cs%dsamp(dl)%mask2dT(:,:)
4260173325 diag_cs%dsamp(dl)%mask3dBL(:,:,k) = diag_cs%dsamp(dl)%mask2dBu(:,:)
4261168375 diag_cs%dsamp(dl)%mask3dCuL(:,:,k) = diag_cs%dsamp(dl)%mask2dCu(:,:)
4262170701 diag_cs%dsamp(dl)%mask3dCvL(:,:,k) = diag_cs%dsamp(dl)%mask2dCv(:,:)
4263 enddo
42641 allocate(diag_cs%dsamp(dl)%mask3dTi(G%HId2%isd:G%HId2%ied,G%HId2%jsd:G%HId2%jed,1:nz+1))
42651 allocate(diag_cs%dsamp(dl)%mask3dBi(G%HId2%IsdB:G%HId2%IedB,G%HId2%JsdB:G%HId2%JedB,1:nz+1))
42661 allocate(diag_cs%dsamp(dl)%mask3dCui(G%HId2%IsdB:G%HId2%IedB,G%HId2%jsd:G%HId2%jed,1:nz+1))
42671 allocate(diag_cs%dsamp(dl)%mask3dCvi(G%HId2%isd:G%HId2%ied,G%HId2%JsdB:G%HId2%JedB,1:nz+1))
426878 do k=1,nz+1
4269168036 diag_cs%dsamp(dl)%mask3dTi(:,:,k) = diag_cs%dsamp(dl)%mask2dT(:,:)
4270175636 diag_cs%dsamp(dl)%mask3dBi(:,:,k) = diag_cs%dsamp(dl)%mask2dBu(:,:)
4271170620 diag_cs%dsamp(dl)%mask3dCui(:,:,k) = diag_cs%dsamp(dl)%mask2dCu(:,:)
4272172977 diag_cs%dsamp(dl)%mask3dCvi(:,:,k) = diag_cs%dsamp(dl)%mask2dCv(:,:)
4273 enddo
4274 enddo
42751end subroutine downsample_diag_masks_set
4276
4277!> Get the diagnostics-compute indices (to be passed to send_data) based on the shape of
4278!! the diagnostic field (the same way they are deduced for non-downsampled fields)
42790subroutine downsample_diag_indices_get(fo1, fo2, dl, diag_cs, isv, iev, jsv, jev)
4280 integer, intent(in) :: fo1 !< The size of the diag field in x
4281 integer, intent(in) :: fo2 !< The size of the diag field in y
4282 integer, intent(in) :: dl !< Integer downsample level
4283 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
4284 integer, intent(out) :: isv !< i-start index for diagnostics
4285 integer, intent(out) :: iev !< i-end index for diagnostics
4286 integer, intent(out) :: jsv !< j-start index for diagnostics
4287 integer, intent(out) :: jev !< j-end index for diagnostics
4288 ! Local variables
4289 integer :: dszi, cszi, dszj, cszj, f1, f2
4290 character(len=500) :: mesg
4291 logical, save :: first_check = .true.
4292
4293 ! The current implementation of the downsampled diagnostics assumes that the tracer-point
4294 ! computational domain on each processor can be evenly divided by dL in each direction, which
4295 ! avoids the need for halo updates or checks that the halo regions are up-to-date. The following
4296 ! check that this assumption is true is only relevant if there are in fact downsampled diagnostics,
4297 ! which is why it occurs during the first call to this routine instead of during initialization.
42980 if (first_check) then
42990 if (mod(diag_cs%ie-diag_cs%is+1, dl) /= 0 .OR. mod(diag_cs%je-diag_cs%js+1, dl) /= 0) then
4300 write (mesg,*) "Non-commensurate downsampled domain is not supported. "//&
43010 "Please choose a layout such that NIGLOBAL/Layout_X and NJGLOBAL/Layout_Y are both divisible by dl=",dl,&
43020 " Current domain extents: ", diag_cs%is,diag_cs%ie, diag_cs%js,diag_cs%je
43030 call MOM_error(FATAL,"downsample_diag_indices_get: "//trim(mesg))
4304 endif
43050 first_check = .false.
4306 endif
4307
43080 cszi = diag_cs%dsamp(dl)%iec-diag_cs%dsamp(dl)%isc +1 ; dszi = diag_cs%dsamp(dl)%ied-diag_cs%dsamp(dl)%isd +1
43090 cszj = diag_cs%dsamp(dl)%jec-diag_cs%dsamp(dl)%jsc +1 ; dszj = diag_cs%dsamp(dl)%jed-diag_cs%dsamp(dl)%jsd +1
43100 isv = diag_cs%dsamp(dl)%isc ; iev = diag_cs%dsamp(dl)%iec
43110 jsv = diag_cs%dsamp(dl)%jsc ; jev = diag_cs%dsamp(dl)%jec
43120 f1 = fo1/dl
43130 f2 = fo2/dl
4314 ! Correction for the symmetric case
43150 if (diag_cs%G%symmetric) then
43160 f1 = f1 + mod(fo1,dl)
43170 f2 = f2 + mod(fo2,dl)
4318 endif
4319
4320 ! Find the range of indices in the downscaled computational domain.
43210 if ( f1 == dszi ) then
43220 isv = diag_cs%dsamp(dl)%isc ; iev = diag_cs%dsamp(dl)%iec ! Field on Data domain, take compute domain indices
43230 elseif ( f1 == dszi + 1 ) then
43240 isv = diag_cs%dsamp(dl)%isc ; iev = diag_cs%dsamp(dl)%iec+1 ! Symmetric data domain
43250 elseif ( f1 == cszi) then
43260 isv = 1 ; iev = (diag_cs%dsamp(dl)%iec-diag_cs%dsamp(dl)%isc) +1 ! Computational domain
43270 elseif ( f1 == cszi + 1 ) then
43280 isv = 1 ; iev = (diag_cs%dsamp(dl)%iec-diag_cs%dsamp(dl)%isc) +2 ! Symmetric computational domain
4329 else
43300 write (mesg,*) " peculiar size ",f1," in i-direction\n"//&
43310 "does not match one of ", cszi, cszi+1, dszi, dszi+1
43320 call MOM_error(FATAL,"downsample_diag_indices_get: "//trim(mesg))
4333 endif
43340 if ( f2 == dszj ) then
43350 jsv = diag_cs%dsamp(dl)%jsc ; jev = diag_cs%dsamp(dl)%jec ! Data domain
43360 elseif ( f2 == dszj + 1 ) then
43370 jsv = diag_cs%dsamp(dl)%jsc ; jev = diag_cs%dsamp(dl)%jec+1 ! Symmetric data domain
43380 elseif ( f2 == cszj) then
43390 jsv = 1 ; jev = (diag_cs%dsamp(dl)%jec-diag_cs%dsamp(dl)%jsc) +1 ! Computational domain
43400 elseif ( f2 == cszj + 1 ) then
43410 jsv = 1 ; jev = (diag_cs%dsamp(dl)%jec-diag_cs%dsamp(dl)%jsc) +2 ! Symmetric computational domain
4342 else
43430 write (mesg,*) " peculiar size ",f2," in j-direction\n"//&
43440 "does not match one of ", cszj, cszj+1, dszj, dszj+1
43450 call MOM_error(FATAL,"downsample_diag_indices_get: "//trim(mesg))
4346 endif
43470end subroutine downsample_diag_indices_get
4348
4349!> This subroutine allocates and computes a downsampled array from an input array.
4350!! It also determines the diagnostic computational grid indices for the downsampled array.
4351!! 3d interface
43520subroutine downsample_diag_field_3d(locfield, locfield_dsamp, dl, diag_cs, diag, isv, iev, jsv, jev, mask)
4353 real, dimension(:,:,:), pointer :: locfield !< Input array pointer in arbitrary units [A ~> a]
4354 real, dimension(:,:,:), allocatable, intent(inout) :: locfield_dsamp !< Output (downsampled) array [A ~> a]
4355 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
4356 type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
4357 integer, intent(in) :: dl !< Level of down sampling
4358 integer, intent(inout) :: isv !< i-start index for diagnostics
4359 integer, intent(inout) :: iev !< i-end index for diagnostics
4360 integer, intent(inout) :: jsv !< j-start index for diagnostics
4361 integer, intent(inout) :: jev !< j-end index for diagnostics
4362 real, optional,target, intent(in) :: mask(:,:,:) !< If present, use this real array as the data mask [nondim]
4363 ! Local variables
43640 real, dimension(:,:,:), pointer :: locmask ! A pointer to the mask [nondim]
4365 integer :: f1, f2, isv_o, jsv_o
4366
43670 locmask => NULL()
4368 ! Get the correct indices corresponding to input field based on its shape.
43690 f1 = size(locfield, 1)
43700 f2 = size(locfield, 2)
4371 ! Save the extents of the original (fine) domain
43720 isv_o = isv ; jsv_o = jsv
4373 ! Get the shape of the downsampled field and overwrite isv, iev, jsv and jev with them
43740 call downsample_diag_indices_get(f1, f2, dl, diag_cs, isv, iev, jsv, jev)
4375 ! Set the pointer to the non-downsampled mask, which must be associated and initialized
43760 if (present(mask)) then
43770 locmask => mask
43780 elseif (associated(diag%axes%mask3d)) then
43790 locmask => diag%axes%mask3d
4380 else
43810 call MOM_error(FATAL, "downsample_diag_field_3d: Cannot downsample without a mask!!! ")
4382 endif
4383
4384 call downsample_field(locfield, locfield_dsamp, dl, diag%xyz_method, locmask, diag_cs, diag, &
43850 isv_o, jsv_o, isv, iev, jsv, jev)
4386
43870end subroutine downsample_diag_field_3d
4388
4389!> This subroutine allocates and computes a downsampled array from an input array.
4390!! It also determines the diagnostic computational grid indices for the downsampled array.
4391!! 2d interface
43920subroutine downsample_diag_field_2d(locfield, locfield_dsamp, dl, diag_cs, diag, isv, iev, jsv, jev, mask)
4393 real, dimension(:,:), pointer :: locfield !< Input array pointer in arbitrary units [A ~> a]
4394 real, dimension(:,:), allocatable, intent(inout) :: locfield_dsamp !< Output (downsampled) array [A ~> a]
4395 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
4396 type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
4397 integer, intent(in) :: dl !< Level of down sampling
4398 integer, intent(inout) :: isv !< i-start index for diagnostics
4399 integer, intent(inout) :: iev !< i-end index for diagnostics
4400 integer, intent(inout) :: jsv !< j-start index for diagnostics
4401 integer, intent(inout) :: jev !< j-end index for diagnostics
4402 real, optional,target, intent(in) :: mask(:,:) !< If present, use this real array as the data mask [nondim].
4403 ! Local variables
44040 real, dimension(:,:), pointer :: locmask ! A pointer to the mask [nondim]
4405 integer :: f1, f2, isv_o, jsv_o
4406
44070 locmask => NULL()
4408 ! Get the correct indices corresponding to input field based on its shape.
44090 f1 = size(locfield,1)
44100 f2 = size(locfield,2)
4411 ! Save the extents of the original (fine) domain
44120 isv_o = isv ; jsv_o = jsv
4413 ! Get the shape of the downsampled field and overwrite isv, iev, jsv and jev with them
44140 call downsample_diag_indices_get(f1, f2, dl, diag_cs, isv, iev, jsv, jev)
4415 ! Set the non-downsampled mask, it must be associated and initialized
44160 if (present(mask)) then
44170 locmask => mask
44180 elseif (associated(diag%axes%mask2d)) then
44190 locmask => diag%axes%mask2d
4420 else
44210 call MOM_error(FATAL, "downsample_diag_field_2d: Cannot downsample without a mask!!! ")
4422 endif
4423
4424 call downsample_field(locfield, locfield_dsamp, dl, diag%xyz_method, locmask, diag_cs,diag, &
44250 isv_o, jsv_o, isv, iev, jsv, jev)
4426
44270end subroutine downsample_diag_field_2d
4428
4429!> \section downsampling The down sample algorithm
4430!!
4431!! The down sample method could be deduced (before send_data call)
4432!! from the diag%x_cell_method, diag%y_cell_method and diag%v_cell_method
4433!!
4434!! This is the summary of the down sample algorithm for a diagnostic field f:
4435!! \f[
4436!! f(Id,Jd) = \sum_{i,j} f(Id+i,Jd+j) * weight(Id+i,Jd+j) / [ \sum_{i,j} weight(Id+i,Jd+j)]
4437!! \f]
4438!! Here, i and j run from 0 to dl-1 (dl being the down sample level).
4439!! Id,Jd are the down sampled (coarse grid) indices run over the coarsened compute grid,
4440!! if and jf are the original (fine grid) indices.
4441!!
4442!! \verbatim
4443!! Example x_cell y_cell v_cell algorithm_id implemented weight(if,jf)
4444!! ---------------------------------------------------------------------------------------
4445!! theta mean mean mean MMM =222 G%areaT(if,jf)*h(if,jf)
4446!! u point mean mean PMM =022 dyCu(if,jf)*h(if,jf)*delta(if,Id)
4447!! v mean point mean MPM =202 dxCv(if,jf)*h(if,jf)*delta(jf,Jd)
4448!! ? point sum mean PSM =012 h(if,jf)*delta(if,Id)
4449!! volcello sum sum sum SSS =111 1
4450!! T_dfxy_co sum sum point SSP =110 1
4451!! umo point sum sum PSS =011 1*delta(if,Id)
4452!! vmo sum point sum SPS =101 1*delta(jf,Jd)
4453!! umo_2d point sum point PSP =010 1*delta(if,Id)
4454!! vmo_2d sum point point SPP =100 1*delta(jf,Jd)
4455!! ? point mean point PMP =020 dyCu(if,jf)*delta(if,Id)
4456!! ? mean point point MPP =200 dxCv(if,jf)*delta(jf,Jd)
4457!! w mean mean point MMP =220 G%areaT(if,jf)
4458!! h*theta mean mean sum MMS =221 G%areaT(if,jf)
4459!!
4460!! delta is the Kronecker delta
4461!! \endverbatim
4462
4463!> This subroutine allocates and computes a down sampled 3d array given an input array
4464!! The down sample method is based on the "cell_methods" for the diagnostics as explained
4465!! in the above table
44660subroutine downsample_field_3d(field_in, field_out, dl, method, mask, diag_cs, diag, &
4467 isv_o, jsv_o, isv_d, iev_d, jsv_d, jev_d)
4468 real, dimension(:,:,:), pointer :: field_in !< Original field to be downsampled in arbitrary units [A ~> a]
4469 real, dimension(:,:,:), allocatable :: field_out !< Downsampled field in the same arbitrary units [A ~> a]
4470 integer, intent(in) :: dl !< Level of down sampling
4471 integer, intent(in) :: method !< Sampling method
4472 real, dimension(:,:,:), pointer :: mask !< Mask for field [nondim]
4473 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
4474 type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
4475 integer, intent(in) :: isv_o !< Original i-start index
4476 integer, intent(in) :: jsv_o !< Original j-start index
4477 integer, intent(in) :: isv_d !< i-start index of down sampled data
4478 integer, intent(in) :: iev_d !< i-end index of down sampled data
4479 integer, intent(in) :: jsv_d !< j-start index of down sampled data
4480 integer, intent(in) :: jev_d !< j-end index of down sampled data
4481 ! Local variables
4482 character(len=240) :: mesg
4483 integer :: i, j, ii, jj, i0, j0, f1, f2, f_in1, f_in2
4484 integer :: k, ks, ke
4485 real :: ave ! The running sum of the average, in [A ~> a], [A L2 ~> a m2],
4486 ! [A H L ~> a m2 or a kg m-1] or [A H L2 ~> a m3 or a kg]
4487 real :: weight ! The nondimensional, area-, volume- or mass-based weight for an input
4488 ! value [nondim], [L2 ~> m2], [H L ~> m2 or kg m-1] or [H L2 ~> m3 or kg]
4489 real :: total_weight ! The sum of weights contributing to a point [nondim], [L2 ~> m2],
4490 ! [H L ~> m2 or kg m-1] or [H L2 ~> m3 or kg]
4491 real :: eps_vol ! A negligibly small volume or mass [H L2 ~> m3 or kg]
4492 real :: eps_area ! A negligibly small area [L2 ~> m2]
4493 real :: eps_face ! A negligibly small face area [H L ~> m2 or kg m-1]
4494
44950 ks = 1 ; ke = size(field_in,3)
44960 eps_face = 1.0e-20 * diag_cs%G%US%m_to_L * diag_cs%GV%m_to_H
44970 eps_area = 1.0e-20 * diag_cs%G%US%m_to_L**2
44980 eps_vol = 1.0e-20 * diag_cs%G%US%m_to_L**2 * diag_cs%GV%m_to_H
4499
4500 ! Allocate the down sampled field on the down sampled data domain
4501! 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))
4502! allocate(field_out(1:size(field_in,1)/dl,1:size(field_in,2)/dl,ks:ke))
45030 f_in1 = size(field_in,1)
45040 f_in2 = size(field_in,2)
45050 f1 = f_in1/dl
45060 f2 = f_in2/dl
4507 ! Correction for the symmetric case
45080 if (diag_cs%G%symmetric) then
45090 f1 = f1 + mod(f_in1,dl)
45100 f2 = f2 + mod(f_in2,dl)
4511 endif
45120 allocate(field_out(1:f1,1:f2,ks:ke))
4513
4514 ! Fill the down sampled field on the down sampled diagnostics (almost always compute) domain
4515 !### The averaging used here is not rotationally invariant.
4516 ! Also, it would be better to use a max with eps_vol instead of adding it in the denominator.
45170 if (method == MMM) then
45180 do k=ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
45190 i0 = isv_o+dl*(i-isv_d)
45200 j0 = jsv_o+dl*(j-jsv_d)
45210 ave = 0.0
45220 total_weight = 0.0
45230 do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
45240 weight = mask(ii,jj,k) * diag_cs%G%areaT(ii,jj) * diag_cs%h(ii,jj,k)
45250 total_weight = total_weight + weight
45260 ave = ave+field_in(ii,jj,k) * weight
4527 enddo ; enddo
45280 field_out(i,j,k) = ave / (total_weight + eps_vol) ! Eps_vol avoids division by 0.
4529 enddo ; enddo ; enddo
45300 elseif (method == SSS) then ! e.g., volcello
45310 do k=ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
45320 i0 = isv_o+dl*(i-isv_d)
45330 j0 = jsv_o+dl*(j-jsv_d)
45340 ave = 0.0
45350 do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
45360 weight = mask(ii,jj,k)
45370 ave = ave+field_in(ii,jj,k)*weight
4538 enddo ; enddo
45390 field_out(i,j,k) = ave ! This is a masked sum, and total_weight = 1.
4540 enddo ; enddo ; enddo
45410 elseif (method == MMP .or. method == MMS) then ! e.g., T_advection_xy
45420 do k=ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
45430 i0 = isv_o+dl*(i-isv_d)
45440 j0 = jsv_o+dl*(j-jsv_d)
45450 ave = 0.0
45460 total_weight = 0.0
45470 do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
45480 weight = mask(ii,jj,k) * diag_cs%G%areaT(ii,jj)
45490 total_weight = total_weight + weight
45500 ave = ave+field_in(ii,jj,k) * weight
4551 enddo ; enddo
45520 field_out(i,j,k) = ave / (total_weight + eps_area) ! Eps_area avoids division by 0.
4553 enddo ; enddo ; enddo
45540 elseif (method == PMM) then
45550 do k=ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
45560 i0 = isv_o+dl*(i-isv_d)
45570 j0 = jsv_o+dl*(j-jsv_d)
45580 ave = 0.0
45590 total_weight = 0.0
45600 ii=i0
45610 do jj=j0,j0+dl-1
45620 weight = mask(ii,jj,k) * diag_cs%G%dyCu(ii,jj) * diag_cs%h(ii,jj,k)
45630 total_weight = total_weight + weight
45640 ave = ave+field_in(ii,jj,k) * weight
4565 enddo
45660 field_out(i,j,k) = ave / (total_weight + eps_face) ! Eps_face avoids division by 0.
4567 enddo ; enddo ; enddo
45680 elseif (method == PSS) then ! e.g. umo
45690 do k=ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
45700 i0 = isv_o+dl*(i-isv_d)
45710 j0 = jsv_o+dl*(j-jsv_d)
45720 ave = 0.0
45730 ii=i0
45740 do jj=j0,j0+dl-1
45750 weight = mask(ii,jj,k)
45760 ave = ave+field_in(ii,jj,k)*weight
4577 enddo
45780 field_out(i,j,k) = ave ! This is a masked sum, and total_weight = 1.
4579 enddo ; enddo ; enddo
45800 elseif (method == SPS) then ! e.g. vmo
45810 do k=ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
45820 i0 = isv_o+dl*(i-isv_d)
45830 j0 = jsv_o+dl*(j-jsv_d)
45840 ave = 0.0
45850 jj=j0
45860 do ii=i0,i0+dl-1
45870 weight = mask(ii,jj,k)
45880 ave = ave+field_in(ii,jj,k)*weight
4589 enddo
45900 field_out(i,j,k) = ave ! This is a masked sum, and total_weight = 1.
4591 enddo ; enddo ; enddo
45920 elseif (method == MPM) then
45930 do k=ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
45940 i0 = isv_o+dl*(i-isv_d)
45950 j0 = jsv_o+dl*(j-jsv_d)
45960 ave = 0.0
45970 total_weight = 0.0
45980 jj=j0
45990 do ii=i0,i0+dl-1
46000 weight = mask(ii,jj,k) * diag_cs%G%dxCv(ii,jj) * diag_cs%h(ii,jj,k)
46010 total_weight = total_weight + weight
46020 ave = ave+field_in(ii,jj,k) * weight
4603 enddo
46040 field_out(i,j,k) = ave / (total_weight + eps_face) ! Eps_face avoids division by 0.
4605 enddo ; enddo ; enddo
46060 elseif (method == MSK) then ! The input field is a mask, so subsample it instead of averaging.
46070 field_out(:,:,:) = 0.0
46080 do k=ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d
46090 i0 = isv_o+dl*(i-isv_d)
46100 j0 = jsv_o+dl*(j-jsv_d)
46110 ave = 0.0
46120 do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
46130 ave = ave+field_in(ii,jj,k)
4614 enddo ; enddo
46150 if (ave > 0.0) field_out(i,j,k)=1.0
4616 enddo ; enddo ; enddo
4617 else
46180 write (mesg,*) " unknown sampling method: ",method
46190 call MOM_error(FATAL, "downsample_field_3d: "//trim(mesg)//" "//trim(diag%debug_str))
4620 endif
4621
46220end subroutine downsample_field_3d
4623
4624!> This subroutine allocates and computes a down sampled 2d array given an input array
4625!! The down sample method is based on the "cell_methods" for the diagnostics as explained
4626!! in the above table
46270subroutine downsample_field_2d(field_in, field_out, dl, method, mask, diag_cs, diag, &
4628 isv_o, jsv_o, isv_d, iev_d, jsv_d, jev_d)
4629 real, dimension(:,:), pointer :: field_in !< Original field to be downsampled in arbitrary units [A ~> a]
4630 real, dimension(:,:), allocatable :: field_out !< Downsampled field in the same arbitrary units [A ~> a]
4631 integer, intent(in) :: dl !< Level of down sampling
4632 integer, intent(in) :: method !< Sampling method
4633 real, dimension(:,:), pointer :: mask !< Mask for field [nondim]
4634 type(diag_ctrl), intent(in) :: diag_CS !< Structure used to regulate diagnostic output
4635 type(diag_type), intent(in) :: diag !< A structure describing the diagnostic to post
4636 integer, intent(in) :: isv_o !< Original i-start index
4637 integer, intent(in) :: jsv_o !< Original j-start index
4638 integer, intent(in) :: isv_d !< i-start index of down sampled data
4639 integer, intent(in) :: iev_d !< i-end index of down sampled data
4640 integer, intent(in) :: jsv_d !< j-start index of down sampled data
4641 integer, intent(in) :: jev_d !< j-end index of down sampled data
4642 ! Local variables
4643 character(len=240) :: mesg
4644 integer :: i, j, ii, jj, i0, j0, f1, f2, f_in1, f_in2
4645 real :: ave ! The running sum of the average, in [A ~> a] or [A L2 ~> a m2]
4646 real :: weight ! The nondimensional or area-weighted weight for an input value [nondim] or [L2 ~> m2]
4647 real :: total_weight ! The sum of weights contributing to a point [nondim] or [L2 ~> m2]
4648 real :: eps_area ! A negligibly small area [L2 ~> m2]
4649 real :: eps_len ! A negligibly small horizontal length [L ~> m]
4650
46510 eps_len = 1.0e-20 * diag_cs%G%US%m_to_L
46520 eps_area = 1.0e-20 * diag_cs%G%US%m_to_L**2
4653
4654 ! Allocate the down sampled field on the down sampled data domain
4655! allocate(field_out(diag_cs%dsamp(dl)%isd:diag_cs%dsamp(dl)%ied,diag_cs%dsamp(dl)%jsd:diag_cs%dsamp(dl)%jed))
4656! allocate(field_out(1:size(field_in,1)/dl,1:size(field_in,2)/dl))
4657 ! Fill the down sampled field on the down sampled diagnostics (almost always compute) domain
46580 f_in1 = size(field_in,1)
46590 f_in2 = size(field_in,2)
46600 f1 = f_in1/dl
46610 f2 = f_in2/dl
4662 ! Correction for the symmetric case
46630 if (diag_cs%G%symmetric) then
46640 f1 = f1 + mod(f_in1,dl)
46650 f2 = f2 + mod(f_in2,dl)
4666 endif
46670 allocate(field_out(1:f1,1:f2))
4668
46690 if (method == MMP) then
46700 do j=jsv_d,jev_d ; do i=isv_d,iev_d
46710 i0 = isv_o+dl*(i-isv_d)
46720 j0 = jsv_o+dl*(j-jsv_d)
46730 ave = 0.0
46740 total_weight = 0.0
46750 do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
46760 weight = mask(ii,jj)*diag_cs%G%areaT(ii,jj)
46770 total_weight = total_weight + weight
46780 ave = ave+field_in(ii,jj) * weight
4679 enddo ; enddo
46800 field_out(i,j) = ave / (total_weight + eps_area) ! Eps_area avoids division by 0.
4681 enddo ; enddo
46820 elseif (method == SSP) then ! e.g., T_dfxy_cont_tendency_2d
46830 do j=jsv_d,jev_d ; do i=isv_d,iev_d
46840 i0 = isv_o+dl*(i-isv_d)
46850 j0 = jsv_o+dl*(j-jsv_d)
46860 ave = 0.0
46870 do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
46880 weight = mask(ii,jj)
46890 ave = ave+field_in(ii,jj)*weight
4690 enddo ; enddo
46910 field_out(i,j) = ave ! This is a masked sum, and total_weight = 1.
4692 enddo ; enddo
46930 elseif (method == PSP) then ! e.g., umo_2d
46940 do j=jsv_d,jev_d ; do i=isv_d,iev_d
46950 i0 = isv_o+dl*(i-isv_d)
46960 j0 = jsv_o+dl*(j-jsv_d)
46970 ave = 0.0
46980 ii=i0
46990 do jj=j0,j0+dl-1
47000 weight = mask(ii,jj)
47010 ave = ave+field_in(ii,jj)*weight
4702 enddo
47030 field_out(i,j) = ave ! This is a masked sum, and total_weight = 1.
4704 enddo ; enddo
47050 elseif (method == SPP) then ! e.g., vmo_2d
47060 do j=jsv_d,jev_d ; do i=isv_d,iev_d
47070 i0 = isv_o+dl*(i-isv_d)
47080 j0 = jsv_o+dl*(j-jsv_d)
47090 ave = 0.0
47100 jj=j0
47110 do ii=i0,i0+dl-1
47120 weight = mask(ii,jj)
47130 ave = ave+field_in(ii,jj)*weight
4714 enddo
47150 field_out(i,j) = ave ! This is a masked sum, and total_weight = 1.
4716 enddo ; enddo
47170 elseif (method == PMP) then
47180 do j=jsv_d,jev_d ; do i=isv_d,iev_d
47190 i0 = isv_o+dl*(i-isv_d)
47200 j0 = jsv_o+dl*(j-jsv_d)
47210 ave = 0.0
47220 total_weight = 0.0
47230 ii=i0
47240 do jj=j0,j0+dl-1
47250 weight = mask(ii,jj) * diag_cs%G%dyCu(ii,jj)!*diag_cs%h(ii,jj,1) !Niki?
47260 total_weight = total_weight + weight
47270 ave = ave+field_in(ii,jj) * weight
4728 enddo
47290 field_out(i,j) = ave / (total_weight + eps_len) ! Eps_len avoids division by 0.
4730 enddo ; enddo
47310 elseif (method == MPP) then
47320 do j=jsv_d,jev_d ; do i=isv_d,iev_d
47330 i0 = isv_o+dl*(i-isv_d)
47340 j0 = jsv_o+dl*(j-jsv_d)
47350 ave = 0.0
47360 total_weight = 0.0
47370 jj=j0
47380 do ii=i0,i0+dl-1
47390 weight = mask(ii,jj)* diag_cs%G%dxCv(ii,jj)!*diag_cs%h(ii,jj,1) !Niki?
47400 total_weight = total_weight +weight
47410 ave = ave+field_in(ii,jj)*weight
4742 enddo
47430 field_out(i,j) = ave / (total_weight + eps_len) ! Eps_len avoids division by 0.
4744 enddo ; enddo
47450 elseif (method == MSK) then ! The input field is a mask, so subsample it instead of averaging.
47460 field_out(:,:) = 0.0
47470 do j=jsv_d,jev_d ; do i=isv_d,iev_d
47480 i0 = isv_o+dl*(i-isv_d)
47490 j0 = jsv_o+dl*(j-jsv_d)
47500 ave = 0.0
47510 do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
47520 ave = ave+field_in(ii,jj)
4753 enddo ; enddo
47540 if (ave > 0.0) field_out(i,j)=1.0
4755 enddo ; enddo
4756 else
47570 write (mesg,*) " unknown sampling method: ",method
47580 call MOM_error(FATAL, "downsample_field_2d: "//trim(mesg)//" "//trim(diag%debug_str))
4759 endif
4760
47610end subroutine downsample_field_2d
4762
4763!> Allocate and compute the 2d down sampled mask
4764!! The masks are down sampled based on a minority rule, i.e., a coarse cell is open (1)
4765!! if at least one of the sub-cells are open, otherwise it's closed (0)
47664subroutine downsample_mask_2d(field_in, field_out, dl, isc_o, jsc_o, isd_o, jsd_o, &
4767 isc_d, iec_d, jsc_d, jec_d, isd_d, ied_d, jsd_d, jed_d)
4768 integer, intent(in) :: isd_o !< Original data domain i-start index
4769 integer, intent(in) :: jsd_o !< Original data domain j-start index
4770 real, dimension(isd_o:,jsd_o:), intent(in) :: field_in !< Original field to be down sampled in arbitrary units [A]
4771 real, dimension(:,:), pointer :: field_out !< Down sampled field mask [nondim]
4772 integer, intent(in) :: dl !< Level of down sampling
4773 integer, intent(in) :: isc_o !< Original i-start index
4774 integer, intent(in) :: jsc_o !< Original j-start index
4775 integer, intent(in) :: isc_d !< Computational i-start index of down sampled data
4776 integer, intent(in) :: iec_d !< Computational i-end index of down sampled data
4777 integer, intent(in) :: jsc_d !< Computational j-start index of down sampled data
4778 integer, intent(in) :: jec_d !< Computational j-end index of down sampled data
4779 integer, intent(in) :: isd_d !< Data domain i-start index of down sampled data
4780 integer, intent(in) :: ied_d !< Data domain i-end index of down sampled data
4781 integer, intent(in) :: jsd_d !< Data domain j-start index of down sampled data
4782 integer, intent(in) :: jed_d !< Data domain j-end index of down sampled data
4783 ! Local variables
4784 integer :: i, j, ii, jj, i0, j0
4785 real :: tot_non_zero ! The sum of values in the down-scaled cell [A]
4786
4787 ! down sampled mask = 0 unless the mask value of one of the down sampling cells is 1
47884 allocate(field_out(isd_d:ied_d,jsd_d:jed_d))
47899043 field_out(:,:) = 0.0
47907507 do j=jsc_d,jec_d ; do i=isc_d,iec_d
47917381 i0 = isc_o+dl*(i-isc_d)
47927381 j0 = jsc_o+dl*(j-jsc_d)
47937381 tot_non_zero = 0.0
479451667 do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
479544286 tot_non_zero = tot_non_zero + field_in(ii,jj)
4796 enddo ; enddo
47977503 if (tot_non_zero > 0.0) field_out(i,j)=1.0
4798 enddo ; enddo
47994end subroutine downsample_mask_2d
4800
4801!> Allocate and compute the 3d down sampled mask
4802!! The masks are down sampled based on a minority rule, i.e., a coarse cell is open (1)
4803!! if at least one of the sub-cells are open, otherwise it's closed (0)
48048subroutine downsample_mask_3d(field_in, field_out, dl, isc_o, jsc_o, isd_o, jsd_o, &
4805 isc_d, iec_d, jsc_d, jec_d, isd_d, ied_d, jsd_d, jed_d)
4806 integer, intent(in) :: isd_o !< Original data domain i-start index
4807 integer, intent(in) :: jsd_o !< Original data domain j-start index
4808 real, dimension(isd_o:,jsd_o:,:), intent(in) :: field_in !< Original field to be down sampled in arbitrary units [A]
4809 real, dimension(:,:,:), pointer :: field_out !< down sampled field mask [nondim]
4810 integer, intent(in) :: dl !< Level of down sampling
4811 integer, intent(in) :: isc_o !< Original i-start index
4812 integer, intent(in) :: jsc_o !< Original j-start index
4813 integer, intent(in) :: isc_d !< Computational i-start index of down sampled data
4814 integer, intent(in) :: iec_d !< Computational i-end index of down sampled data
4815 integer, intent(in) :: jsc_d !< Computational j-start index of down sampled data
4816 integer, intent(in) :: jec_d !< Computational j-end index of down sampled data
4817 integer, intent(in) :: isd_d !< Computational i-start index of down sampled data
4818 integer, intent(in) :: ied_d !< Computational i-end index of down sampled data
4819 integer, intent(in) :: jsd_d !< Computational j-start index of down sampled data
4820 integer, intent(in) :: jed_d !< Computational j-end index of down sampled data
4821
4822 ! Local variables
4823 integer :: i, j, ii, jj, i0, j0, k, ks, ke
4824 real :: tot_non_zero ! The sum of values in the down-scaled cell [A]
4825
4826 ! down sampled mask = 0 unless the mask value of one of the down sampling cells is 1
48278 ks = lbound(field_in,3) ; ke = ubound(field_in,3)
48288 allocate(field_out(isd_d:ied_d,jsd_d:jed_d,ks:ke))
4829605889 field_out(:,:,:) = 0.0
4830502977 do k=ks,ke ; do j=jsc_d,jec_d ; do i=isc_d,iec_d
4831494527 i0 = isc_o+dl*(i-isc_d)
4832494527 j0 = jsc_o+dl*(j-jsc_d)
4833494527 tot_non_zero = 0.0
48343461689 do jj=j0,j0+dl-1 ; do ii=i0,i0+dl-1
48352967162 tot_non_zero = tot_non_zero + field_in(ii,jj,k)
4836 enddo ; enddo
4837502701 if (tot_non_zero > 0.0) field_out(i,j,k)=1.0
4838 enddo ; enddo ; enddo
48398end subroutine downsample_mask_3d
4840
4841!> Fakes a register of a diagnostic to find out if an obsolete
4842!! parameter appears in the diag_table.
484316logical function found_in_diagtable(diag, varName)
4844 type(diag_ctrl), intent(in) :: diag !< A structure used to control diagnostics.
4845 character(len=*), intent(in) :: varName !< The obsolete diagnostic name
4846 ! Local
4847 integer :: handle ! Integer handle returned from diag_manager
4848
4849 ! We use register_static_field_fms() instead of register_static_field() so
4850 ! that the diagnostic does not appear in the available diagnostics list.
485116 handle = register_static_field_infra('ocean_model', varName, diag%axesT1%handles)
4852
485316 found_in_diagtable = (handle>0)
4854
485516end function found_in_diagtable
4856
4857!> Finishes the diag manager reduction methods as needed for the time_step
48580subroutine MOM_diag_send_complete()
48590 call diag_send_complete_infra()
48600end subroutine MOM_diag_send_complete
4861
48620end module MOM_diag_mediator