MOM6
MOM_diag_mediator.F90
Go to the documentation of this file.
2 
3 !***********************************************************************
4 !* GNU General Public License *
5 !* This file is a part of MOM. *
6 !* *
7 !* MOM is free software; you can redistribute it and/or modify it and *
8 !* are expected to follow the terms of the GNU General Public License *
9 !* as published by the Free Software Foundation; either version 2 of *
10 !* the License, or (at your option) any later version. *
11 !* *
12 !* MOM is distributed in the hope that it will be useful, but WITHOUT *
13 !* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
14 !* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public *
15 !* License for more details. *
16 !* *
17 !* For the full text of the GNU General Public License, *
18 !* write to: Free Software Foundation, Inc., *
19 !* 675 Mass Ave, Cambridge, MA 02139, USA. *
20 !* or see: http://www.gnu.org/licenses/gpl.html *
21 !***********************************************************************
22 
23 !********+*********+*********+*********+*********+*********+*********+**
24 !* *
25 !* The subroutines here provide convenient wrappers to the fms *
26 !* diag_manager interfaces with additional diagnostic capabilies. *
27 !* *
28 !********+*********+*********+*********+*********+*********+*********+**
29 
30 use mom_coms, only : pe_here
31 use mom_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end
32 use mom_cpu_clock, only : clock_module, clock_routine
33 use mom_error_handler, only : mom_error, fatal, is_root_pe, assert
35 use mom_grid, only : ocean_grid_type
36 use mom_io, only : slasher, vardesc, query_vardesc, mom_read_data
39 use mom_time_manager, only : time_type
41 use mom_eos, only : eos_type
50 
51 use diag_axis_mod, only : get_diag_axis_name
52 use diag_manager_mod, only : diag_manager_init, diag_manager_end
53 use diag_manager_mod, only : send_data, diag_axis_init, diag_field_add_attribute
54 ! The following module is needed for PGI since the following line does not compile with PGI 6.5.0
55 ! was: use diag_manager_mod, only : register_diag_field_fms=>register_diag_field
57 use diag_manager_mod, only : register_static_field_fms=>register_static_field
58 use diag_manager_mod, only : get_diag_field_id_fms=>get_diag_field_id
59 use diag_manager_mod, only : diag_field_not_found
60 
61 implicit none ; private
62 
63 #define __DO_SAFETY_CHECKS__
64 #define IMPLIES(A, B) ((.not. (A)) .or. (B))
65 
67 public post_data_1d_k
73 public diag_axis_init, ocean_register_diag, register_static_field
79 
80 interface post_data
81  module procedure post_data_3d, post_data_2d, post_data_0d
82 end interface post_data
83 
84 !> A group of 1D axes that comprise a 1D/2D/3D mesh
85 type, public :: axes_grp
86  character(len=15) :: id !< The id string for this particular combination of handles.
87  integer :: rank !< Number of dimensions in the list of axes.
88  integer, dimension(:), allocatable :: handles !< Handles to 1D axes.
89  type(diag_ctrl), pointer :: diag_cs => null() !< Circular link back to the main diagnostics control structure
90  !! (Used to avoid passing said structure into every possible call).
91  ! ID's for cell_methods
92  character(len=9) :: x_cell_method = '' !< Default nature of data representation, if axes group includes x-direction.
93  character(len=9) :: y_cell_method = '' !< Default nature of data representation, if axes group includes y-direction.
94  character(len=9) :: v_cell_method = '' !< Default nature of data representation, if axes group includes vertical direction.
95  ! For remapping
96  integer :: nz = 0 !< Vertical dimension of diagnostic
97  integer :: vertical_coordinate_number = 0 !< Index of the corresponding diag_remap_ctrl for this axis group
98  ! For detecting position on the grid
99  logical :: is_h_point = .false. !< If true, indicates that this axes group is for an h-point located field.
100  logical :: is_q_point = .false. !< If true, indicates that this axes group is for a q-point located field.
101  logical :: is_u_point = .false. !< If true, indicates that this axes group is for a u-point located field.
102  logical :: is_v_point = .false. !< If true, indicates that this axes group is for a v-point located field.
103  logical :: is_layer = .false. !< If true, indicates that this axes group is for a layer vertically-located field.
104  logical :: is_interface = .false. !< If true, indicates that this axes group is for an interface vertically-located field.
105  logical :: is_native = .true. !< If true, indicates that this axes group is for a native model grid. False for any other
106  !! grid. Used for rank>2.
107  logical :: needs_remapping = .false. !< If true, indicates that this axes group is for a intensive layer-located field
108  !! that must be remapped to these axes. Used for rank>2.
109  logical :: needs_interpolating = .false. !< If true, indicates that this axes group is for a sampled interface-located field
110  !! that must be interpolated to these axes. Used for rank>2.
111  ! For horizontally averaged diagnositcs (applies to 2d and 3d fields only)
112  type(axes_grp), pointer :: xyave_axes => null() !< The associated 1d axes for horizontall area-averaged diagnostics
113  ! ID's for cell_measures
114  integer :: id_area = -1 !< The diag_manager id for area to be used for cell_measure of variables with this axes_grp.
115  integer :: id_volume = -1 !< The diag_manager id for volume to be used for cell_measure of variables with this axes_grp.
116 end type axes_grp
117 
118 !> This type is used to represent a diagnostic at the diag_mediator level.
119 !! There can be both 'primary' and 'seconday' diagnostics. The primaries
120 !! reside in the diag_cs%diags array. They have an id which is an index
121 !! into this array. The secondaries are 'variations' on the primary diagnostic.
122 !! For example the CMOR diagnostics are secondary. The secondary diagnostics
123 !! are kept in a list with the primary diagnostic as the head.
124 type, private :: diag_type
125  logical :: in_use !< True if this entry is being used.
126  integer :: fms_diag_id !< Underlying FMS diag_manager id.
127  integer :: fms_xyave_diag_id = -1 !< For a horizontally area-averaged diagnostic.
128  character(32) :: debug_str = '' !< For FATAL errors and debugging.
129  type(axes_grp), pointer :: axes => null()
130  real, pointer, dimension(:,:) :: mask2d => null()
131  real, pointer, dimension(:,:,:) :: mask3d => null()
132  type(diag_type), pointer :: next => null() !< Pointer to the next diag.
133  real :: conversion_factor = 0. !< A factor to multiply data by before posting to FMS, if non-zero.
134  logical :: v_extensive = .false. !< True for vertically extensive fields (vertically integrated). False for intensive (concentrations).
135 end type diag_type
136 
137 !> The following data type a list of diagnostic fields an their variants,
138 !! as well as variables that control the handling of model output.
139 type, public :: diag_ctrl
140  integer :: doc_unit = -1 !< The unit number of a diagnostic documentation file.
141  !! This file is open if doc_unit is > 0.
142 
143 ! The following fields are used for the output of the data.
144  integer :: is, ie, js, je
145  integer :: isd, ied, jsd, jed
146  real :: time_int !< The time interval in s for any fields
147  !! that are offered for averaging.
148  type(time_type) :: time_end !< The end time of the valid
149  !! interval for any offered field.
150  logical :: ave_enabled = .false. !< True if averaging is enabled.
151 
152  ! The following are axis types defined for output.
153  type(axes_grp) :: axesbl, axestl, axescul, axescvl
154  type(axes_grp) :: axesbi, axesti, axescui, axescvi
155  type(axes_grp) :: axesb1, axest1, axescu1, axescv1
156  type(axes_grp) :: axeszi, axeszl
157 
158  ! Mask arrays for diagnostics
159  real, dimension(:,:), pointer :: mask2dt => null()
160  real, dimension(:,:), pointer :: mask2dbu => null()
161  real, dimension(:,:), pointer :: mask2dcu => null()
162  real, dimension(:,:), pointer :: mask2dcv => null()
163  real, dimension(:,:,:), pointer :: mask3dtl => null()
164  real, dimension(:,:,:), pointer :: mask3dbl => null()
165  real, dimension(:,:,:), pointer :: mask3dcul => null()
166  real, dimension(:,:,:), pointer :: mask3dcvl => null()
167  real, dimension(:,:,:), pointer :: mask3dti => null()
168  real, dimension(:,:,:), pointer :: mask3dbi => null()
169  real, dimension(:,:,:), pointer :: mask3dcui => null()
170  real, dimension(:,:,:), pointer :: mask3dcvi => null()
171 
172 ! Space for diagnostics is dynamically allocated as it is needed.
173 ! The chunk size is how much the array should grow on each new allocation.
174 #define DIAG_ALLOC_CHUNK_SIZE 100
175  type(diag_type), dimension(:), allocatable :: diags
176  integer :: next_free_diag_id
177 
178  !default missing value to be sent to ALL diagnostics registrations
179  real :: missing_value = -1.0e+34
180 
181  !> Number of diagnostic vertical coordinates (remapped)
182  integer :: num_diag_coords
183  !> Control structure for each possible coordinate
184  type(diag_remap_ctrl), dimension(:), allocatable :: diag_remap_cs
185 
186  !> Axes groups for each possible coordinate (these will all be 3D groups)
187  type(axes_grp), dimension(:), allocatable :: remap_axeszl, remap_axeszi
188  type(axes_grp), dimension(:), allocatable :: remap_axestl, remap_axesbl, remap_axescul, remap_axescvl
189  type(axes_grp), dimension(:), allocatable :: remap_axesti, remap_axesbi, remap_axescui, remap_axescvi
190 
191  ! Pointer to H, G and T&S needed for remapping
192  real, dimension(:,:,:), pointer :: h => null()
193  real, dimension(:,:,:), pointer :: t => null()
194  real, dimension(:,:,:), pointer :: s => null()
195  type(eos_type), pointer :: eqn_of_state => null()
196  type(ocean_grid_type), pointer :: g => null()
197 
198 #if defined(DEBUG) || defined(__DO_SAFETY_CHECKS__)
199  ! Keep a copy of h so that we know whether it has changed. If it has then
200  ! need the target grid for vertical remapping needs to have been updated.
201  real, dimension(:,:,:), allocatable :: h_old
202 #endif
203 
204 end type diag_ctrl
205 
206 ! CPU clocks
208 
209 contains
210 
211 !> Sets up diagnostics axes
212 subroutine set_axes_info(G, GV, param_file, diag_cs, set_vertical)
213  type(ocean_grid_type), intent(inout) :: G !< Ocean grid structure
214  type(verticalgrid_type), intent(in) :: GV !< ocean vertical grid structure
215  type(param_file_type), intent(in) :: param_file !< Parameter file structure
216  type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
217  logical, optional, intent(in) :: set_vertical !< If true or missing, set up
218  !! vertical axes
219  ! Local variables
220  integer :: id_xq, id_yq, id_zl, id_zi, id_xh, id_yh
221  integer :: i, k, nz
222  real :: zlev(gv%ke), zinter(gv%ke+1)
223  logical :: set_vert
224 
225  set_vert = .true. ; if (present(set_vertical)) set_vert = set_vertical
226 
227  if(g%symmetric) then
228  id_xq = diag_axis_init('xq', g%gridLonB(g%isgB:g%iegB), g%x_axis_units, 'x', &
229  'q point nominal longitude', domain2=g%Domain%mpp_domain)
230  id_yq = diag_axis_init('yq', g%gridLatB(g%jsgB:g%jegB), g%y_axis_units, 'y', &
231  'q point nominal latitude', domain2=g%Domain%mpp_domain)
232  else
233  id_xq = diag_axis_init('xq', g%gridLonB(g%isg:g%ieg), g%x_axis_units, 'x', &
234  'q point nominal longitude', domain2=g%Domain%mpp_domain)
235  id_yq = diag_axis_init('yq', g%gridLatB(g%jsg:g%jeg), g%y_axis_units, 'y', &
236  'q point nominal latitude', domain2=g%Domain%mpp_domain)
237  endif
238  id_xh = diag_axis_init('xh', g%gridLonT(g%isg:g%ieg), g%x_axis_units, 'x', &
239  'h point nominal longitude', domain2=g%Domain%mpp_domain)
240  id_yh = diag_axis_init('yh', g%gridLatT(g%jsg:g%jeg), g%y_axis_units, 'y', &
241  'h point nominal latitude', domain2=g%Domain%mpp_domain)
242 
243  if (set_vert) then
244  nz = gv%ke
245  zinter(1:nz+1) = gv%sInterface(1:nz+1)
246  zlev(1:nz) = gv%sLayer(1:nz)
247  id_zl = diag_axis_init('zl', zlev, trim(gv%zAxisUnits), 'z', &
248  'Layer '//trim(gv%zAxisLongName), &
249  direction=gv%direction)
250  id_zi = diag_axis_init('zi', zinter, trim(gv%zAxisUnits), 'z', &
251  'Interface '//trim(gv%zAxisLongName), &
252  direction=gv%direction)
253  else
254  id_zl = -1 ; id_zi = -1
255  endif
256 
257  ! Vertical axes for the interfaces and layers
258  call define_axes_group(diag_cs, (/ id_zi /), diag_cs%axesZi, &
259  v_cell_method='point', is_interface=.true.)
260  call define_axes_group(diag_cs, (/ id_zl /), diag_cs%axesZL, &
261  v_cell_method='mean', is_layer=.true.)
262 
263  ! Axis groupings for the model layers
264  call define_axes_group(diag_cs, (/ id_xh, id_yh, id_zl /), diag_cs%axesTL, &
265  x_cell_method='mean', y_cell_method='mean', v_cell_method='mean', &
266  is_h_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
267  call define_axes_group(diag_cs, (/ id_xq, id_yq, id_zl /), diag_cs%axesBL, &
268  x_cell_method='point', y_cell_method='point', v_cell_method='mean', &
269  is_q_point=.true., is_layer=.true.)
270  call define_axes_group(diag_cs, (/ id_xq, id_yh, id_zl /), diag_cs%axesCuL, &
271  x_cell_method='point', y_cell_method='mean', v_cell_method='mean', &
272  is_u_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
273  call define_axes_group(diag_cs, (/ id_xh, id_yq, id_zl /), diag_cs%axesCvL, &
274  x_cell_method='mean', y_cell_method='point', v_cell_method='mean', &
275  is_v_point=.true., is_layer=.true., xyave_axes=diag_cs%axesZL)
276 
277  ! Axis groupings for the model interfaces
278  call define_axes_group(diag_cs, (/ id_xh, id_yh, id_zi /), diag_cs%axesTi, &
279  x_cell_method='mean', y_cell_method='mean', v_cell_method='point', &
280  is_h_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
281  call define_axes_group(diag_cs, (/ id_xq, id_yq, id_zi /), diag_cs%axesBi, &
282  x_cell_method='point', y_cell_method='point', v_cell_method='point', &
283  is_q_point=.true., is_interface=.true.)
284  call define_axes_group(diag_cs, (/ id_xq, id_yh, id_zi /), diag_cs%axesCui, &
285  x_cell_method='point', y_cell_method='mean', v_cell_method='point', &
286  is_u_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
287  call define_axes_group(diag_cs, (/ id_xh, id_yq, id_zi /), diag_cs%axesCvi, &
288  x_cell_method='mean', y_cell_method='point', v_cell_method='point', &
289  is_v_point=.true., is_interface=.true., xyave_axes=diag_cs%axesZi)
290 
291  ! Axis groupings for 2-D arrays
292  call define_axes_group(diag_cs, (/ id_xh, id_yh /), diag_cs%axesT1, &
293  x_cell_method='mean', y_cell_method='mean', is_h_point=.true.)
294  call define_axes_group(diag_cs, (/ id_xq, id_yq /), diag_cs%axesB1, &
295  x_cell_method='point', y_cell_method='point', is_q_point=.true.)
296  call define_axes_group(diag_cs, (/ id_xq, id_yh /), diag_cs%axesCu1, &
297  x_cell_method='point', y_cell_method='mean', is_u_point=.true.)
298  call define_axes_group(diag_cs, (/ id_xh, id_yq /), diag_cs%axesCv1, &
299  x_cell_method='mean', y_cell_method='point', is_v_point=.true.)
300 
301  if (diag_cs%num_diag_coords>0) then
302  allocate(diag_cs%remap_axesZL(diag_cs%num_diag_coords))
303  allocate(diag_cs%remap_axesTL(diag_cs%num_diag_coords))
304  allocate(diag_cs%remap_axesBL(diag_cs%num_diag_coords))
305  allocate(diag_cs%remap_axesCuL(diag_cs%num_diag_coords))
306  allocate(diag_cs%remap_axesCvL(diag_cs%num_diag_coords))
307  allocate(diag_cs%remap_axesZi(diag_cs%num_diag_coords))
308  allocate(diag_cs%remap_axesTi(diag_cs%num_diag_coords))
309  allocate(diag_cs%remap_axesBi(diag_cs%num_diag_coords))
310  allocate(diag_cs%remap_axesCui(diag_cs%num_diag_coords))
311  allocate(diag_cs%remap_axesCvi(diag_cs%num_diag_coords))
312  endif
313 
314  do i=1, diag_cs%num_diag_coords
315  ! For each possible diagnostic coordinate
316  call diag_remap_configure_axes(diag_cs%diag_remap_cs(i), gv, param_file)
317 
318  ! This vertical coordinate has been configured so can be used.
319  if (diag_remap_axes_configured(diag_cs%diag_remap_cs(i))) then
320 
321  ! This fetches the 1D-axis id for layers and interfaces and overwrite
322  ! id_zl and id_zi from above. It also returns the number of layers.
323  call diag_remap_get_axes_info(diag_cs%diag_remap_cs(i), nz, id_zl, id_zi)
324 
325  ! Axes for z layers
326  call define_axes_group(diag_cs, (/ id_zl /), diag_cs%remap_axesZL(i), &
327  nz=nz, vertical_coordinate_number=i, &
328  v_cell_method='mean', &
329  is_h_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true.)
330  call define_axes_group(diag_cs, (/ id_xh, id_yh, id_zl /), diag_cs%remap_axesTL(i), &
331  nz=nz, vertical_coordinate_number=i, &
332  x_cell_method='mean', y_cell_method='mean', v_cell_method='mean', &
333  is_h_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
334  xyave_axes=diag_cs%remap_axesZL(i))
335 
336  !! \note Remapping for B points is not yet implemented so needs_remapping is not provided for remap_axesBL
337  call define_axes_group(diag_cs, (/ id_xq, id_yq, id_zl /), diag_cs%remap_axesBL(i), &
338  nz=nz, vertical_coordinate_number=i, &
339  x_cell_method='point', y_cell_method='point', v_cell_method='mean', &
340  is_q_point=.true., is_layer=.true., is_native=.false.)
341 
342  call define_axes_group(diag_cs, (/ id_xq, id_yh, id_zl /), diag_cs%remap_axesCuL(i), &
343  nz=nz, vertical_coordinate_number=i, &
344  x_cell_method='point', y_cell_method='mean', v_cell_method='mean', &
345  is_u_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
346  xyave_axes=diag_cs%remap_axesZL(i))
347 
348  call define_axes_group(diag_cs, (/ id_xh, id_yq, id_zl /), diag_cs%remap_axesCvL(i), &
349  nz=nz, vertical_coordinate_number=i, &
350  x_cell_method='mean', y_cell_method='point', v_cell_method='mean', &
351  is_v_point=.true., is_layer=.true., is_native=.false., needs_remapping=.true., &
352  xyave_axes=diag_cs%remap_axesZL(i))
353 
354  ! Axes for z interfaces
355  call define_axes_group(diag_cs, (/ id_zi /), diag_cs%remap_axesZi(i), &
356  nz=nz, vertical_coordinate_number=i, &
357  v_cell_method='point', &
358  is_h_point=.true., is_interface=.true., is_native=.false., needs_interpolating=.true.)
359  call define_axes_group(diag_cs, (/ id_xh, id_yh, id_zi /), diag_cs%remap_axesTi(i), &
360  nz=nz, vertical_coordinate_number=i, &
361  x_cell_method='mean', y_cell_method='mean', v_cell_method='point', &
362  is_h_point=.true., is_interface=.true., is_native=.false., needs_interpolating=.true., &
363  xyave_axes=diag_cs%remap_axesZi(i))
364 
365  !! \note Remapping for B points is not yet implemented so needs_remapping is not provided for remap_axesBi
366  call define_axes_group(diag_cs, (/ id_xq, id_yq, id_zi /), diag_cs%remap_axesBi(i), &
367  nz=nz, vertical_coordinate_number=i, &
368  x_cell_method='point', y_cell_method='point', v_cell_method='point', &
369  is_q_point=.true., is_interface=.true., is_native=.false.)
370 
371  call define_axes_group(diag_cs, (/ id_xq, id_yh, id_zi /), diag_cs%remap_axesCui(i), &
372  nz=nz, vertical_coordinate_number=i, &
373  x_cell_method='point', y_cell_method='mean', v_cell_method='point', &
374  is_u_point=.true., is_interface=.true., is_native=.false., &
375  needs_interpolating=.true., xyave_axes=diag_cs%remap_axesZi(i))
376 
377  call define_axes_group(diag_cs, (/ id_xh, id_yq, id_zi /), diag_cs%remap_axesCvi(i), &
378  nz=nz, vertical_coordinate_number=i, &
379  x_cell_method='mean', y_cell_method='point', v_cell_method='point', &
380  is_v_point=.true., is_interface=.true., is_native=.false., &
381  needs_interpolating=.true., xyave_axes=diag_cs%remap_axesZi(i))
382  endif
383  enddo
384 
385 end subroutine set_axes_info
386 
387 !> Attaches the id of cell areas to axes groups for use with cell_measures
388 subroutine diag_register_area_ids(diag_cs, id_area_t, id_area_q)
389  type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
390  integer, optional, intent(in) :: id_area_t !< Diag_mediator id for area of h-cells
391  integer, optional, intent(in) :: id_area_q !< Diag_mediator id for area of q-cells
392  ! Local variables
393  integer :: fms_id, i
394  if (present(id_area_t)) then
395  fms_id = diag_cs%diags(id_area_t)%fms_diag_id
396  diag_cs%axesT1%id_area = fms_id
397  diag_cs%axesTi%id_area = fms_id
398  diag_cs%axesTL%id_area = fms_id
399  do i=1, diag_cs%num_diag_coords
400  diag_cs%remap_axesTL(i)%id_area = fms_id
401  ! Note to AJA: why am I not doing TZi too?
402  enddo
403  endif
404  if (present(id_area_q)) then
405  fms_id = diag_cs%diags(id_area_q)%fms_diag_id
406  diag_cs%axesB1%id_area = fms_id
407  diag_cs%axesBi%id_area = fms_id
408  diag_cs%axesBL%id_area = fms_id
409  do i=1, diag_cs%num_diag_coords
410  diag_cs%remap_axesBL(i)%id_area = fms_id
411  enddo
412  endif
413 end subroutine diag_register_area_ids
414 
415 !> Attaches the id of cell volumes to axes groups for use with cell_measures
416 subroutine diag_register_volume_ids(diag_cs, id_vol_t)
417  type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
418  integer, optional, intent(in) :: id_vol_t !< Diag_manager id for volume of h-cells
419  ! Local variables
420  integer :: fms_id
421  if (present(id_vol_t)) then
422  fms_id = diag_cs%diags(id_vol_t)%fms_diag_id
423  call mom_error(fatal,"diag_register_volume_ids: not implemented yet!")
424  endif
425 end subroutine diag_register_volume_ids
426 
427 !> Defines a group of "axes" from list of handles
428 subroutine define_axes_group(diag_cs, handles, axes, nz, vertical_coordinate_number, &
429  x_cell_method, y_cell_method, v_cell_method, &
430  is_h_point, is_q_point, is_u_point, is_v_point, &
431  is_layer, is_interface, &
432  is_native, needs_remapping, needs_interpolating, &
433  xyave_axes)
434  type(diag_ctrl), target, intent(in) :: diag_cs !< Diagnostics control structure
435  integer, dimension(:), intent(in) :: handles !< A list of 1D axis handles
436  type(axes_grp), intent(out) :: axes !< The group of 1D axes
437  integer, optional, intent(in) :: nz !< Number of layers in this diagnostic grid
438  integer, optional, intent(in) :: vertical_coordinate_number !< Index number for vertical coordinate
439  character(len=*), optional, intent(in) :: x_cell_method !< A x-direction cell method used to construct the "cell_methods" attribute in CF convention
440  character(len=*), optional, intent(in) :: y_cell_method !< A y-direction cell method used to construct the "cell_methods" attribute in CF convention
441  character(len=*), optional, intent(in) :: v_cell_method !< A vertical direction cell method used to construct the "cell_methods" attribute in CF convention
442  logical, optional, intent(in) :: is_h_point !< If true, indicates this axes group for h-point located fields
443  logical, optional, intent(in) :: is_q_point !< If true, indicates this axes group for q-point located fields
444  logical, optional, intent(in) :: is_u_point !< If true, indicates this axes group for u-point located fields
445  logical, optional, intent(in) :: is_v_point !< If true, indicates this axes group for v-point located fields
446  logical, optional, intent(in) :: is_layer !< If true, indicates that this axes group is for a layer vertically-located field.
447  logical, optional, intent(in) :: is_interface !< If true, indicates that this axes group is for an interface vertically-located field.
448  logical, optional, intent(in) :: is_native !< If true, indicates that this axes group is for a native model grid. False for any other grid.
449  logical, optional, intent(in) :: needs_remapping !< If true, indicates that this axes group is for a intensive layer-located field
450  !! that must be remapped to these axes. Used for rank>2.
451  logical, optional, intent(in) :: needs_interpolating !< If true, indicates that this axes group is for a sampled interface-located field
452  !! that must be interpolated to these axes. Used for rank>2.
453  type(axes_grp), optional, target :: xyave_axes !< The corresponding axes group for horizontally area-average diagnostics
454  ! Local variables
455  integer :: n
456 
457  n = size(handles)
458  if (n<1 .or. n>3) call mom_error(fatal, "define_axes_group: wrong size for list of handles!")
459  allocate( axes%handles(n) )
460  axes%id = i2s(handles, n) ! Identifying string
461  axes%rank = n
462  axes%handles(:) = handles(:)
463  axes%diag_cs => diag_cs ! A [circular] link back to the diag_cs structure
464  if (present(x_cell_method)) then
465  if (axes%rank<2) call mom_error(fatal, 'define_axes_group: ' // &
466  'Can not set x_cell_method for rank<2.')
467  axes%x_cell_method = trim(x_cell_method)
468  else
469  axes%x_cell_method = ''
470  endif
471  if (present(y_cell_method)) then
472  if (axes%rank<2) call mom_error(fatal, 'define_axes_group: ' // &
473  'Can not set y_cell_method for rank<2.')
474  axes%y_cell_method = trim(y_cell_method)
475  else
476  axes%y_cell_method = ''
477  endif
478  if (present(v_cell_method)) then
479  if (axes%rank/=1 .and. axes%rank/=3) call mom_error(fatal, 'define_axes_group: ' // &
480  'Can not set v_cell_method for rank<>1 or 3.')
481  axes%v_cell_method = trim(v_cell_method)
482  else
483  axes%v_cell_method = ''
484  endif
485  if (present(nz)) axes%nz = nz
486  if (present(vertical_coordinate_number)) axes%vertical_coordinate_number = vertical_coordinate_number
487  if (present(is_h_point)) axes%is_h_point = is_h_point
488  if (present(is_q_point)) axes%is_q_point = is_q_point
489  if (present(is_u_point)) axes%is_u_point = is_u_point
490  if (present(is_v_point)) axes%is_v_point = is_v_point
491  if (present(is_layer)) axes%is_layer = is_layer
492  if (present(is_interface)) axes%is_interface = is_interface
493  if (present(is_native)) axes%is_native = is_native
494  if (present(needs_remapping)) axes%needs_remapping = needs_remapping
495  if (present(needs_interpolating)) axes%needs_interpolating = needs_interpolating
496  if (present(xyave_axes)) axes%xyave_axes => xyave_axes
497 
498 end subroutine define_axes_group
499 
500 subroutine set_diag_mediator_grid(G, diag_cs)
501  type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure
502  type(diag_ctrl), intent(inout) :: diag_cs
503 
504 ! Arguments:
505 ! (inout) G - ocean grid structure
506 ! (inout) diag - structure used to regulate diagnostic output
507 
508  diag_cs%is = g%isc - (g%isd-1) ; diag_cs%ie = g%iec - (g%isd-1)
509  diag_cs%js = g%jsc - (g%jsd-1) ; diag_cs%je = g%jec - (g%jsd-1)
510  diag_cs%isd = g%isd ; diag_cs%ied = g%ied
511  diag_cs%jsd = g%jsd ; diag_cs%jed = g%jed
512 
513 end subroutine set_diag_mediator_grid
514 
515 subroutine post_data_0d(diag_field_id, field, diag_cs, is_static)
516  integer, intent(in) :: diag_field_id
517  real, intent(in) :: field
518  type(diag_ctrl), target, intent(in) :: diag_cs
519  logical, optional, intent(in) :: is_static
520 
521 ! Arguments:
522 ! (in) diag_field_id - the id for an output variable returned by a
523 ! previous call to register_diag_field.
524 ! (in) field - 0-d array being offered for output or averaging.
525 ! (inout) diag_cs - structure used to regulate diagnostic output.
526 ! (in,opt) is_static - If true, this is a static field that is always offered.
527 ! (in,opt) mask - If present, use this real array as the data mask.
528 
529  logical :: used, is_stat
530  type(diag_type), pointer :: diag => null()
531 
532  if (id_clock_diag_mediator>0) call cpu_clock_begin(id_clock_diag_mediator)
533  is_stat = .false. ; if (present(is_static)) is_stat = is_static
534 
535  ! Iterate over list of diag 'variants', e.g. CMOR aliases, call send_data
536  ! for each one.
537  call assert(diag_field_id < diag_cs%next_free_diag_id, &
538  'post_data_0d: Unregistered diagnostic id')
539  diag => diag_cs%diags(diag_field_id)
540  do while (associated(diag))
541  if (is_stat) then
542  used = send_data(diag%fms_diag_id, field)
543  elseif (diag_cs%ave_enabled) then
544  used = send_data(diag%fms_diag_id, field, diag_cs%time_end)
545  endif
546  diag => diag%next
547  enddo
548 
549  if (id_clock_diag_mediator>0) call cpu_clock_end(id_clock_diag_mediator)
550 end subroutine post_data_0d
551 
552 subroutine post_data_1d_k(diag_field_id, field, diag_cs, is_static)
553  integer, intent(in) :: diag_field_id
554  real, intent(in) :: field(:)
555  type(diag_ctrl), target, intent(in) :: diag_cs
556  logical, optional, intent(in) :: is_static
557 
558 ! Arguments:
559 ! (in) diag_field_id - id for an output variable returned by a
560 ! previous call to register_diag_field.
561 ! (in) field - 3-d array being offered for output or averaging
562 ! (inout) diag_cs - structure used to regulate diagnostic output
563 ! (in) static - If true, this is a static field that is always offered.
564 
565  logical :: used ! The return value of send_data is not used for anything.
566  logical :: is_stat
567  integer :: isv, iev, jsv, jev
568  type(diag_type), pointer :: diag => null()
569 
570  if (id_clock_diag_mediator>0) call cpu_clock_begin(id_clock_diag_mediator)
571  is_stat = .false. ; if (present(is_static)) is_stat = is_static
572 
573  ! Iterate over list of diag 'variants', e.g. CMOR aliases.
574  call assert(diag_field_id < diag_cs%next_free_diag_id, &
575  'post_data_1d_k: Unregistered diagnostic id')
576  diag => diag_cs%diags(diag_field_id)
577  do while (associated(diag))
578  if (is_stat) then
579  used = send_data(diag%fms_diag_id, field)
580  elseif (diag_cs%ave_enabled) then
581  used = send_data(diag%fms_diag_id, field, diag_cs%time_end, weight=diag_cs%time_int)
582  endif
583  diag => diag%next
584  enddo
585 
586  if (id_clock_diag_mediator>0) call cpu_clock_end(id_clock_diag_mediator)
587 end subroutine post_data_1d_k
588 
589 subroutine post_data_2d(diag_field_id, field, diag_cs, is_static, mask)
590  integer, intent(in) :: diag_field_id
591  real, intent(in) :: field(:,:)
592  type(diag_ctrl), target, intent(in) :: diag_cs
593  logical, optional, intent(in) :: is_static
594  real, optional, intent(in) :: mask(:,:)
595 
596 ! Arguments:
597 ! (in) diag_field_id - id for an output variable returned by a
598 ! previous call to register_diag_field.
599 ! (in) field - 2-d array being offered for output or averaging.
600 ! (inout) diag_cs - structure used to regulate diagnostic output.
601 ! (in,opt) is_static - If true, this is a static field that is always offered.
602 ! (in,opt) mask - If present, use this real array as the data mask.
603 
604  type(diag_type), pointer :: diag => null()
605 
606  if (id_clock_diag_mediator>0) call cpu_clock_begin(id_clock_diag_mediator)
607 
608  ! Iterate over list of diag 'variants' (e.g. CMOR aliases) and post each.
609  call assert(diag_field_id < diag_cs%next_free_diag_id, &
610  'post_data_2d: Unregistered diagnostic id')
611  diag => diag_cs%diags(diag_field_id)
612  do while (associated(diag))
613  call post_data_2d_low(diag, field, diag_cs, is_static, mask)
614  diag => diag%next
615  enddo
616 
617  if (id_clock_diag_mediator>0) call cpu_clock_end(id_clock_diag_mediator)
618 end subroutine post_data_2d
619 
620 subroutine post_data_2d_low(diag, field, diag_cs, is_static, mask)
621  type(diag_type), intent(in) :: diag
622  real, target, intent(in) :: field(:,:)
623  type(diag_ctrl), intent(in) :: diag_cs
624  logical, optional, intent(in) :: is_static
625  real, optional, intent(in) :: mask(:,:)
626 
627 ! Arguments:
628 ! (in) diag - structure representing the diagnostic to post
629 ! (in) field - 2-d array being offered for output or averaging
630 ! (inout) diag_cs - structure used to regulate diagnostic output
631 ! (in,opt) is_static - If true, this is a static field that is always offered.
632 ! (in,opt) mask - If present, use this real array as the data mask.
633 
634  real, dimension(:,:), pointer :: locfield => null()
635  logical :: used, is_stat
636  integer :: isv, iev, jsv, jev
637 
638  is_stat = .false. ; if (present(is_static)) is_stat = is_static
639 
640  ! Determine the propery array indices, noting that because of the (:,:)
641  ! declaration of field, symmetric arrays are using a SW-grid indexing,
642  ! but non-symmetric arrays are using a NE-grid indexing. Send_data
643  ! actually only uses the difference between ie and is to determine
644  ! the output data size and assumes that halos are symmetric.
645  isv = diag_cs%is ; iev = diag_cs%ie ; jsv = diag_cs%js ; jev = diag_cs%je
646 
647  if ( size(field,1) == diag_cs%ied-diag_cs%isd +1 ) then
648  isv = diag_cs%is ; iev = diag_cs%ie ! Data domain
649  elseif ( size(field,1) == diag_cs%ied-diag_cs%isd +2 ) then
650  isv = diag_cs%is ; iev = diag_cs%ie+1 ! Symmetric data domain
651  elseif ( size(field,1) == diag_cs%ie-diag_cs%is +1 ) then
652  isv = 1 ; iev = diag_cs%ie + 1-diag_cs%is ! Computational domain
653  elseif ( size(field,1) == diag_cs%ie-diag_cs%is +2 ) then
654  isv = 1 ; iev = diag_cs%ie + 2-diag_cs%is ! Symmetric computational domain
655  else
656  call mom_error(fatal,"post_data_2d_low: peculiar size in i-direction")
657  endif
658  if ( size(field,2) == diag_cs%jed-diag_cs%jsd +1 ) then
659  jsv = diag_cs%js ; jev = diag_cs%je ! Data domain
660  elseif ( size(field,2) == diag_cs%jed-diag_cs%jsd +2 ) then
661  jsv = diag_cs%js ; jev = diag_cs%je+1 ! Symmetric data domain
662  elseif ( size(field,2) == diag_cs%je-diag_cs%js +1 ) then
663  jsv = 1 ; jev = diag_cs%je + 1-diag_cs%js ! Computational domain
664  elseif ( size(field,1) == diag_cs%je-diag_cs%js +2 ) then
665  jsv = 1 ; jev = diag_cs%je + 2-diag_cs%js ! Symmetric computational domain
666  else
667  call mom_error(fatal,"post_data_2d_low: peculiar size in j-direction")
668  endif
669 
670  if (diag%conversion_factor/=0.) then
671  allocate( locfield( lbound(field,1):ubound(field,1), lbound(field,2):ubound(field,2) ) )
672  locfield(isv:iev,jsv:jev) = field(isv:iev,jsv:jev) * diag%conversion_factor
673  else
674  locfield => field
675  endif
676 
677  if (is_stat) then
678  if (present(mask)) then
679  call assert(size(locfield) == size(mask), &
680  'post_data_2d_low is_stat: mask size mismatch: '//diag%debug_str)
681  used = send_data(diag%fms_diag_id, locfield, &
682  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, rmask=mask)
683  !elseif(associated(diag%mask2d)) then
684  ! used = send_data(diag%fms_diag_id, locfield, &
685  ! is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, rmask=diag%mask2d)
686  else
687  used = send_data(diag%fms_diag_id, locfield, &
688  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev)
689  endif
690  elseif (diag_cs%ave_enabled) then
691  if (present(mask)) then
692  call assert(size(locfield) == size(mask), &
693  'post_data_2d_low: mask size mismatch: '//diag%debug_str)
694  used = send_data(diag%fms_diag_id, locfield, diag_cs%time_end, &
695  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, &
696  weight=diag_cs%time_int, rmask=mask)
697  elseif(associated(diag%mask2d)) then
698  used = send_data(diag%fms_diag_id, locfield, diag_cs%time_end, &
699  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, &
700  weight=diag_cs%time_int, rmask=diag%mask2d)
701  else
702  used = send_data(diag%fms_diag_id, locfield, diag_cs%time_end, &
703  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, &
704  weight=diag_cs%time_int)
705  endif
706  endif
707  if (diag%conversion_factor/=0.) deallocate( locfield )
708 
709 end subroutine post_data_2d_low
710 
711 subroutine post_data_3d(diag_field_id, field, diag_cs, is_static, mask, alt_h)
713  integer, intent(in) :: diag_field_id
714  real, intent(in) :: field(:,:,:)
715  type(diag_ctrl), target, intent(in) :: diag_cs
716  logical, optional, intent(in) :: is_static
717  real, optional, intent(in) :: mask(:,:,:)
718  real, target, optional, intent(in) :: alt_h(:,:,:)
719 
720 ! Arguments:
721 ! (in) diag_field_id - id for an output variable returned by a
722 ! previous call to register_diag_field.
723 ! (in) field - 3-d array being offered for output or averaging
724 ! (inout) diag - structure used to regulate diagnostic output
725 ! (in) static - If true, this is a static field that is always offered.
726 ! (in,opt) mask - If present, use this real array as the data mask.
727 
728  type(diag_type), pointer :: diag => null()
729  integer :: nz, i, j, k
730  real, dimension(:,:,:), allocatable :: remapped_field
731  logical :: staggered_in_x, staggered_in_y
732  real, dimension(:,:,:), pointer :: h_diag
733 
734  if(present(alt_h)) then
735  h_diag => alt_h
736  else
737  h_diag => diag_cs%h
738  endif
739 
740  if (id_clock_diag_mediator>0) call cpu_clock_begin(id_clock_diag_mediator)
741 
742  ! Iterate over list of diag 'variants', e.g. CMOR aliases, different vertical
743  ! grids, and post each.
744  call assert(diag_field_id < diag_cs%next_free_diag_id, &
745  'post_data_3d: Unregistered diagnostic id')
746  diag => diag_cs%diags(diag_field_id)
747  do while (associated(diag))
748  call assert(associated(diag%axes), 'post_data_3d: axes is not associated')
749 
750  staggered_in_x = diag%axes%is_u_point .or. diag%axes%is_q_point
751  staggered_in_y = diag%axes%is_v_point .or. diag%axes%is_q_point
752 
753  if (diag%v_extensive .and. .not.diag%axes%is_native) then
754  ! The field is vertically integrated and needs to be re-gridded
755  if (present(mask)) then
756  call mom_error(fatal,"post_data_3d: no mask for regridded field.")
757  endif
758 
759  if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
760  allocate(remapped_field(size(field,1), size(field,2), diag%axes%nz))
761  call vertically_reintegrate_diag_field( &
762  diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), &
763  diag_cs%G, h_diag, staggered_in_x, staggered_in_y, &
764  diag%mask3d, diag_cs%missing_value, field, remapped_field)
765  if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
766  if (associated(diag%mask3d)) then
767  ! Since 3d masks do not vary in the vertical, just use as much as is
768  ! needed.
769  call post_data_3d_low(diag, remapped_field, diag_cs, is_static, &
770  mask=diag%mask3d(:,:,:diag%axes%nz))
771  else
772  call post_data_3d_low(diag, remapped_field, diag_cs, is_static)
773  endif
774  if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
775  deallocate(remapped_field)
776  if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
777  elseif (diag%axes%needs_remapping) then
778  ! Remap this field to another vertical coordinate.
779  if (present(mask)) then
780  call mom_error(fatal,"post_data_3d: no mask for regridded field.")
781  endif
782 
783  if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
784  allocate(remapped_field(size(field,1), size(field,2), diag%axes%nz))
785  call diag_remap_do_remap(diag_cs%diag_remap_cs( &
786  diag%axes%vertical_coordinate_number), &
787  diag_cs%G, h_diag, staggered_in_x, staggered_in_y, &
788  diag%mask3d, diag_cs%missing_value, field, remapped_field)
789  if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
790  if (associated(diag%mask3d)) then
791  ! Since 3d masks do not vary in the vertical, just use as much as is
792  ! needed.
793  call post_data_3d_low(diag, remapped_field, diag_cs, is_static, &
794  mask=diag%mask3d(:,:,:diag%axes%nz))
795  else
796  call post_data_3d_low(diag, remapped_field, diag_cs, is_static)
797  endif
798  if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
799  deallocate(remapped_field)
800  if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
801  elseif (diag%axes%needs_interpolating) then
802  ! Interpolate this field to another vertical coordinate.
803  if (present(mask)) then
804  call mom_error(fatal,"post_data_3d: no mask for regridded field.")
805  endif
806 
807  if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
808  allocate(remapped_field(size(field,1), size(field,2), diag%axes%nz+1))
809  call vertically_interpolate_diag_field(diag_cs%diag_remap_cs( &
810  diag%axes%vertical_coordinate_number), &
811  diag_cs%G, h_diag, staggered_in_x, staggered_in_y, &
812  diag%mask3d, diag_cs%missing_value, field, remapped_field)
813  if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
814  if (associated(diag%mask3d)) then
815  ! Since 3d masks do not vary in the vertical, just use as much as is
816  ! needed.
817  call post_data_3d_low(diag, remapped_field, diag_cs, is_static, &
818  mask=diag%mask3d(:,:,:diag%axes%nz+1))
819  else
820  call post_data_3d_low(diag, remapped_field, diag_cs, is_static)
821  endif
822  if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap)
823  deallocate(remapped_field)
824  if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap)
825  else
826  call post_data_3d_low(diag, field, diag_cs, is_static, mask)
827  endif
828  diag => diag%next
829  enddo
830  if (id_clock_diag_mediator>0) call cpu_clock_end(id_clock_diag_mediator)
831 
832 end subroutine post_data_3d
833 
834 subroutine post_data_3d_low(diag, field, diag_cs, is_static, mask)
835  type(diag_type), intent(in) :: diag
836  real, target, intent(in) :: field(:,:,:)
837  type(diag_ctrl), intent(in) :: diag_cs
838  logical, optional, intent(in) :: is_static
839  real, optional, intent(in) :: mask(:,:,:)
840 
841 ! Arguments:
842 ! (in) diag - the diagnostic to post.
843 ! (in) field - 3-d array being offered for output or averaging
844 ! (inout) diag_cs - structure used to regulate diagnostic output
845 ! (in) static - If true, this is a static field that is always offered.
846 ! (in,opt) mask - If present, use this real array as the data mask.
847 
848  real, dimension(:,:,:), pointer :: locfield => null()
849  logical :: used ! The return value of send_data is not used for anything.
850  logical :: is_stat
851  integer :: isv, iev, jsv, jev
852 
853  is_stat = .false. ; if (present(is_static)) is_stat = is_static
854 
855  ! Determine the proper array indices, noting that because of the (:,:)
856  ! declaration of field, symmetric arrays are using a SW-grid indexing,
857  ! but non-symmetric arrays are using a NE-grid indexing. Send_data
858  ! actually only uses the difference between ie and is to determine
859  ! the output data size and assumes that halos are symmetric.
860  isv = diag_cs%is ; iev = diag_cs%ie ; jsv = diag_cs%js ; jev = diag_cs%je
861 
862  if ( size(field,1) == diag_cs%ied-diag_cs%isd +1 ) then
863  isv = diag_cs%is ; iev = diag_cs%ie ! Data domain
864  elseif ( size(field,1) == diag_cs%ied-diag_cs%isd +2 ) then
865  isv = diag_cs%is ; iev = diag_cs%ie+1 ! Symmetric data domain
866  elseif ( size(field,1) == diag_cs%ie-diag_cs%is +1 ) then
867  isv = 1 ; iev = diag_cs%ie + 1-diag_cs%is ! Computational domain
868  elseif ( size(field,1) == diag_cs%ie-diag_cs%is +2 ) then
869  isv = 1 ; iev = diag_cs%ie + 2-diag_cs%is ! Symmetric computational domain
870  else
871  call mom_error(fatal,"post_data_3d_low: peculiar size in i-direction")
872  endif
873  if ( size(field,2) == diag_cs%jed-diag_cs%jsd +1 ) then
874  jsv = diag_cs%js ; jev = diag_cs%je ! Data domain
875  elseif ( size(field,2) == diag_cs%jed-diag_cs%jsd +2 ) then
876  jsv = diag_cs%js ; jev = diag_cs%je+1 ! Symmetric data domain
877  elseif ( size(field,2) == diag_cs%je-diag_cs%js +1 ) then
878  jsv = 1 ; jev = diag_cs%je + 1-diag_cs%js ! Computational domain
879  elseif ( size(field,1) == diag_cs%je-diag_cs%js +2 ) then
880  jsv = 1 ; jev = diag_cs%je + 2-diag_cs%js ! Symmetric computational domain
881  else
882  call mom_error(fatal,"post_data_3d_low: peculiar size in j-direction")
883  endif
884 
885  if (diag%conversion_factor/=0.) then
886  allocate( locfield( lbound(field,1):ubound(field,1), lbound(field,2):ubound(field,2), &
887  lbound(field,3):ubound(field,3) ) )
888  locfield(isv:iev,jsv:jev,:) = field(isv:iev,jsv:jev,:) * diag%conversion_factor
889  else
890  locfield => field
891  endif
892 
893  if (diag%fms_diag_id>0) then
894  if (is_stat) then
895  if (present(mask)) then
896  call assert(size(locfield) == size(mask), &
897  'post_data_3d_low is_stat: mask size mismatch: '//diag%debug_str)
898  used = send_data(diag%fms_diag_id, locfield, &
899  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, rmask=mask)
900  !elseif(associated(diag%mask3d)) then
901  ! used = send_data(diag_field_id, locfield, &
902  ! is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, rmask=diag%mask3d)
903  else
904  used = send_data(diag%fms_diag_id, locfield, &
905  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev)
906  endif
907  elseif (diag_cs%ave_enabled) then
908  if (present(mask)) then
909  call assert(size(locfield) == size(mask), &
910  'post_data_3d_low: mask size mismatch: '//diag%debug_str)
911  used = send_data(diag%fms_diag_id, locfield, diag_cs%time_end, &
912  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, &
913  weight=diag_cs%time_int, rmask=mask)
914  elseif(associated(diag%mask3d)) then
915  call assert(size(locfield) == size(diag%mask3d), &
916  'post_data_3d_low: mask3d size mismatch: '//diag%debug_str)
917  used = send_data(diag%fms_diag_id, locfield, diag_cs%time_end, &
918  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, &
919  weight=diag_cs%time_int, rmask=diag%mask3d)
920  else
921  used = send_data(diag%fms_diag_id, locfield, diag_cs%time_end, &
922  is_in=isv, js_in=jsv, ie_in=iev, je_in=jev, &
923  weight=diag_cs%time_int)
924  endif
925  endif
926  endif
927  if (diag%fms_xyave_diag_id>0) then
928  call post_xy_average(diag_cs, diag, locfield)
929  endif
930  if (diag%conversion_factor/=0.) deallocate( locfield )
931 
932 end subroutine post_data_3d_low
933 
934 !> Post the horizontally area-averaged diagnostic
935 subroutine post_xy_average(diag_cs, diag, field)
936  type(diag_type), intent(in) :: diag !< This diagnostic
937  real, target, intent(in) :: field(:,:,:) !< Diagnostic field
938  type(diag_ctrl), intent(in) :: diag_cs !< Diagnostics mediator control structure
939  ! Local variable
940  real, dimension(size(field,3)) :: averaged_field
941  logical :: staggered_in_x, staggered_in_y, used
942  integer :: nz, remap_nz, coord
943 
944  if (.not. diag_cs%ave_enabled) then
945  return
946  endif
947 
948  staggered_in_x = diag%axes%is_u_point .or. diag%axes%is_q_point
949  staggered_in_y = diag%axes%is_v_point .or. diag%axes%is_q_point
950 
951  if (diag%axes%is_native) then
952  call horizontally_average_diag_field(diag_cs%G, diag_cs%h, &
953  staggered_in_x, staggered_in_y, &
954  diag%axes%is_layer, diag%v_extensive, &
955  diag_cs%missing_value, field, averaged_field)
956  else
957  nz = size(field, 3)
958  coord = diag%axes%vertical_coordinate_number
959  remap_nz = diag_cs%diag_remap_cs(coord)%nz
960 
961  call assert(diag_cs%diag_remap_cs(coord)%initialized, &
962  'post_xy_average: remap_cs not initialized.')
963 
964  call assert(implies(diag%axes%is_layer, nz == remap_nz), &
965  'post_xy_average: layer field dimension mismatch.')
966  call assert(implies(.not. diag%axes%is_layer, nz == remap_nz+1), &
967  'post_xy_average: interface field dimension mismatch.')
968 
969  call horizontally_average_diag_field(diag_cs%G, diag_cs%diag_remap_cs(coord)%h, &
970  staggered_in_x, staggered_in_y, &
971  diag%axes%is_layer, diag%v_extensive, &
972  diag_cs%missing_value, field, averaged_field)
973  endif
974 
975  used = send_data(diag%fms_xyave_diag_id, averaged_field, diag_cs%time_end, &
976  weight=diag_cs%time_int)
977 end subroutine post_xy_average
978 
979 subroutine enable_averaging(time_int_in, time_end_in, diag_cs)
980  real, intent(in) :: time_int_in
981  type(time_type), intent(in) :: time_end_in
982  type(diag_ctrl), intent(inout) :: diag_cs
983 
984 ! This subroutine enables the accumulation of time averages over the
985 ! specified time interval.
986 
987 ! Arguments:
988 ! (in) time_int_in - time interval in s over which any
989 ! values that are offered are valid.
990 ! (in) time_end_in - end time in s of the valid interval
991 ! (inout) diag - structure used to regulate diagnostic output
992 
993 ! if (num_file==0) return
994  diag_cs%time_int = time_int_in
995  diag_cs%time_end = time_end_in
996  diag_cs%ave_enabled = .true.
997 end subroutine enable_averaging
998 
999 ! Call this subroutine to avoid averaging any offered fields.
1000 subroutine disable_averaging(diag_cs)
1001  type(diag_ctrl), intent(inout) :: diag_cs
1002 
1003 ! Argument:
1004 ! diag - structure used to regulate diagnostic output
1005 
1006  diag_cs%time_int = 0.0
1007  diag_cs%ave_enabled = .false.
1008 
1009 end subroutine disable_averaging
1010 
1011 ! Call this subroutine to determine whether the averaging is
1012 ! currently enabled. .true. is returned if it is.
1013 function query_averaging_enabled(diag_cs, time_int, time_end)
1014  type(diag_ctrl), intent(in) :: diag_cs
1015  real, optional, intent(out) :: time_int
1016  type(time_type), optional, intent(out) :: time_end
1017  logical :: query_averaging_enabled
1018 
1019 ! Arguments:
1020 ! (in) diag - structure used to regulate diagnostic output
1021 ! (out,opt) time_int - current setting of diag%time_int, in s
1022 ! (out,opt) time_end - current setting of diag%time_end
1023 
1024  if (present(time_int)) time_int = diag_cs%time_int
1025  if (present(time_end)) time_end = diag_cs%time_end
1026  query_averaging_enabled = diag_cs%ave_enabled
1027 end function query_averaging_enabled
1028 
1029 function get_diag_time_end(diag_cs)
1030  type(diag_ctrl), intent(in) :: diag_cs
1031  type(time_type) :: get_diag_time_end
1032 
1033 ! Argument:
1034 ! (in) diag - structure used to regulate diagnostic output
1035 
1036 ! This function returns the valid end time for diagnostics that are handled
1037 ! outside of the MOM6 infrastructure, such as via the generic tracer code.
1038 
1039  get_diag_time_end = diag_cs%time_end
1040 end function get_diag_time_end
1041 
1042 !> Returns the "diag_mediator" handle for a group (native, CMOR, z-coord, ...) of diagnostics derived from one field.
1043 integer function register_diag_field(module_name, field_name, axes, init_time, &
1044  long_name, units, missing_value, range, mask_variant, standard_name, &
1045  verbose, do_not_log, err_msg, interp_method, tile_count, cmor_field_name, &
1046  cmor_long_name, cmor_units, cmor_standard_name, cell_methods, &
1047  x_cell_method, y_cell_method, v_cell_method, conversion, v_extensive)
1048  character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model" or "ice_shelf_model"
1049  character(len=*), intent(in) :: field_name !< Name of the diagnostic field
1050  type(axes_grp), target, intent(in) :: axes !< Container w/ up to 3 integer handles that indicates axes for this field
1051  type(time_type), intent(in) :: init_time !< Time at which a field is first available?
1052  character(len=*), optional, intent(in) :: long_name !< Long name of a field.
1053  character(len=*), optional, intent(in) :: units !< Units of a field.
1054  character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
1055  real, optional, intent(in) :: missing_value !< A value that indicates missing values.
1056  real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
1057  logical, optional, intent(in) :: mask_variant !< If true a logical mask must be provided with post_data calls (not used in MOM?)
1058  logical, optional, intent(in) :: verbose !< If true, FMS is verbose (not used in MOM?)
1059  logical, optional, intent(in) :: do_not_log !< If true, do not log something (not used in MOM?)
1060  character(len=*), optional, intent(out):: err_msg !< String into which an error message might be placed (not used in MOM?)
1061  character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should not be interpolated as a scalar
1062  integer, optional, intent(in) :: tile_count !< no clue (not used in MOM?)
1063  character(len=*), optional, intent(in) :: cmor_field_name !< CMOR name of a field
1064  character(len=*), optional, intent(in) :: cmor_long_name !< CMOR long name of a field
1065  character(len=*), optional, intent(in) :: cmor_units !< CMOR units of a field
1066  character(len=*), optional, intent(in) :: cmor_standard_name !< CMOR standardized name associated with a field
1067  character(len=*), optional, intent(in) :: cell_methods !< String to append as cell_methods attribute. Use '' to have no attribute.
1068  !! If present, this overrides the default constructed from the default for
1069  !! each individual axis direction.
1070  character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction. Use '' have no method.
1071  character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction. Use '' have no method.
1072  character(len=*), optional, intent(in) :: v_cell_method !< Specifies the cell method for the vertical direction. Use '' have no method.
1073  real, optional, intent(in) :: conversion !< A value to multiply data by before writing to file
1074  logical, optional, intent(in) :: v_extensive !< True for vertically extensive fields (vertically integrated). Default/absent for intensive.
1075  ! Local variables
1076  real :: MOM_missing_value
1077  type(diag_ctrl), pointer :: diag_cs
1078  type(axes_grp), pointer :: remap_axes => null()
1079  integer :: dm_id, i
1080  character(len=256) :: new_module_name
1081  logical :: active
1082 
1083  mom_missing_value = axes%diag_cs%missing_value
1084  if(present(missing_value)) mom_missing_value = missing_value
1085 
1086  diag_cs => axes%diag_cs
1087  dm_id = -1
1088 
1089  ! Register the native diagnostic
1090  active = register_diag_field_expand_cmor(dm_id, module_name, field_name, axes, &
1091  init_time, long_name=long_name, units=units, missing_value=mom_missing_value, &
1092  range=range, mask_variant=mask_variant, standard_name=standard_name, &
1093  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
1094  interp_method=interp_method, tile_count=tile_count, &
1095  cmor_field_name=cmor_field_name, cmor_long_name=cmor_long_name, &
1096  cmor_units=cmor_units, cmor_standard_name=cmor_standard_name, &
1097  cell_methods=cell_methods, x_cell_method=x_cell_method, &
1098  y_cell_method=y_cell_method, v_cell_method=v_cell_method, &
1099  conversion=conversion, v_extensive=v_extensive)
1100 
1101  ! For each diagnostic coordinate register the diagnostic again under a different module name
1102  do i=1,diag_cs%num_diag_coords
1103  new_module_name = trim(module_name)//'_'//trim(diag_cs%diag_remap_cs(i)%diag_module_suffix)
1104 
1105  ! Register diagnostics remapped to z vertical coordinate
1106  if (axes%rank == 3) then
1107  remap_axes => null()
1108  if ((axes%id .eq. diag_cs%axesTL%id)) then
1109  remap_axes => diag_cs%remap_axesTL(i)
1110  elseif(axes%id .eq. diag_cs%axesBL%id) then
1111  remap_axes => diag_cs%remap_axesBL(i)
1112  elseif(axes%id .eq. diag_cs%axesCuL%id ) then
1113  remap_axes => diag_cs%remap_axesCuL(i)
1114  elseif(axes%id .eq. diag_cs%axesCvL%id) then
1115  remap_axes => diag_cs%remap_axesCvL(i)
1116  elseif(axes%id .eq. diag_cs%axesTi%id) then
1117  remap_axes => diag_cs%remap_axesTi(i)
1118  elseif(axes%id .eq. diag_cs%axesBi%id) then
1119  remap_axes => diag_cs%remap_axesBi(i)
1120  elseif(axes%id .eq. diag_cs%axesCui%id ) then
1121  remap_axes => diag_cs%remap_axesCui(i)
1122  elseif(axes%id .eq. diag_cs%axesCvi%id) then
1123  remap_axes => diag_cs%remap_axesCvi(i)
1124  endif
1125  ! When the MOM_diag_to_Z module has been obsoleted we can assume remap_axes will
1126  ! always exist but in the mean-time we have to do this check:
1127  ! call assert(associated(remap_axes), 'register_diag_field: remap_axes not set')
1128  if (associated(remap_axes)) then
1129  if (remap_axes%needs_remapping .or. remap_axes%needs_interpolating) then
1130  active = register_diag_field_expand_cmor(dm_id, new_module_name, field_name, remap_axes, &
1131  init_time, long_name=long_name, units=units, missing_value=mom_missing_value, &
1132  range=range, mask_variant=mask_variant, standard_name=standard_name, &
1133  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
1134  interp_method=interp_method, tile_count=tile_count, &
1135  cmor_field_name=cmor_field_name, cmor_long_name=cmor_long_name, &
1136  cmor_units=cmor_units, cmor_standard_name=cmor_standard_name, &
1137  cell_methods=cell_methods, x_cell_method=x_cell_method, &
1138  y_cell_method=y_cell_method, v_cell_method=v_cell_method, &
1139  conversion=conversion, v_extensive=v_extensive)
1140  if (active) then
1141  call diag_remap_set_active(diag_cs%diag_remap_cs(i))
1142  endif
1143  endif ! remap_axes%needs_remapping
1144  endif ! associated(remap_axes)
1145  endif ! axes%rank == 3
1146  enddo ! i
1147 
1148  register_diag_field = dm_id
1149 
1150 end function register_diag_field
1151 
1152 !> Returns True if either the native of CMOr version of the diagnostic were registered. Updates 'dm_id'
1153 !! after calling register_diag_field_expand_axes() for both native and CMOR variants of the field.
1154 logical function register_diag_field_expand_cmor(dm_id, module_name, field_name, axes, init_time, &
1155  long_name, units, missing_value, range, mask_variant, standard_name, &
1156  verbose, do_not_log, err_msg, interp_method, tile_count, cmor_field_name, &
1157  cmor_long_name, cmor_units, cmor_standard_name, cell_methods, &
1158  x_cell_method, y_cell_method, v_cell_method, conversion, v_extensive)
1159  integer, intent(inout) :: dm_id !< The diag_mediator ID for this diagnostic group
1160  character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model" or "ice_shelf_model"
1161  character(len=*), intent(in) :: field_name !< Name of the diagnostic field
1162  type(axes_grp), target, intent(in) :: axes !< Container w/ up to 3 integer handles that indicates axes for this field
1163  type(time_type), intent(in) :: init_time !< Time at which a field is first available?
1164  character(len=*), optional, intent(in) :: long_name !< Long name of a field.
1165  character(len=*), optional, intent(in) :: units !< Units of a field.
1166  character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
1167  real, optional, intent(in) :: missing_value !< A value that indicates missing values.
1168  real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
1169  logical, optional, intent(in) :: mask_variant !< If true a logical mask must be provided with post_data calls (not used in MOM?)
1170  logical, optional, intent(in) :: verbose !< If true, FMS is verbose (not used in MOM?)
1171  logical, optional, intent(in) :: do_not_log !< If true, do not log something (not used in MOM?)
1172  character(len=*), optional, intent(out):: err_msg !< String into which an error message might be placed (not used in MOM?)
1173  character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should not be interpolated as a scalar
1174  integer, optional, intent(in) :: tile_count !< no clue (not used in MOM?)
1175  character(len=*), optional, intent(in) :: cmor_field_name !< CMOR name of a field
1176  character(len=*), optional, intent(in) :: cmor_long_name !< CMOR long name of a field
1177  character(len=*), optional, intent(in) :: cmor_units !< CMOR units of a field
1178  character(len=*), optional, intent(in) :: cmor_standard_name !< CMOR standardized name associated with a field
1179  character(len=*), optional, intent(in) :: cell_methods !< String to append as cell_methods attribute. Use '' to have no attribute.
1180  !! If present, this overrides the default constructed from the default for
1181  !! each individual axis direction.
1182  character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction. Use '' have no method.
1183  character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction. Use '' have no method.
1184  character(len=*), optional, intent(in) :: v_cell_method !< Specifies the cell method for the vertical direction. Use '' have no method.
1185  real, optional, intent(in) :: conversion !< A value to multiply data by before writing to file
1186  logical, optional, intent(in) :: v_extensive !< True for vertically extensive fields (vertically integrated). Default/absent for intensive.
1187  ! Local variables
1188  real :: MOM_missing_value
1189  type(diag_ctrl), pointer :: diag_cs
1190  type(diag_type), pointer :: this_diag => null()
1191  integer :: fms_id, fms_xyave_id
1192  character(len=256) :: posted_cmor_units, posted_cmor_standard_name, posted_cmor_long_name, cm_string, msg
1193 
1194  mom_missing_value = axes%diag_cs%missing_value
1195  if(present(missing_value)) mom_missing_value = missing_value
1196 
1198  diag_cs => axes%diag_cs
1199 
1200  ! Set up the 'primary' diagnostic, first get an underlying FMS id
1201  fms_id = register_diag_field_expand_axes(module_name, field_name, axes, init_time, &
1202  long_name=long_name, units=units, missing_value=mom_missing_value, &
1203  range=range, mask_variant=mask_variant, standard_name=standard_name, &
1204  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
1205  interp_method=interp_method, tile_count=tile_count)
1206  call attach_cell_methods(fms_id, axes, cm_string, &
1207  cell_methods, x_cell_method, y_cell_method, v_cell_method, &
1208  v_extensive=v_extensive)
1209  if (is_root_pe() .and. diag_cs%doc_unit > 0) then
1210  msg = ''
1211  if (present(cmor_field_name)) msg = 'CMOR equivalent is "'//trim(cmor_field_name)//'"'
1212  call log_available_diag(fms_id>0, module_name, field_name, cm_string, &
1213  msg, diag_cs, long_name, units, standard_name)
1214  endif
1215  ! Associated horizontally area-averaged diagnostic
1216  fms_xyave_id = diag_field_not_found
1217  if (associated(axes%xyave_axes)) then
1218  fms_xyave_id = register_diag_field_expand_axes(module_name, trim(field_name)//'_xyave', &
1219  axes%xyave_axes, init_time, &
1220  long_name=long_name, units=units, missing_value=mom_missing_value, &
1221  range=range, mask_variant=mask_variant, standard_name=standard_name, &
1222  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
1223  interp_method=interp_method, tile_count=tile_count)
1224  call attach_cell_methods(fms_xyave_id, axes%xyave_axes, cm_string, &
1225  cell_methods, v_cell_method, v_extensive=v_extensive)
1226  if (is_root_pe() .and. diag_cs%doc_unit > 0) then
1227  msg = ''
1228  if (present(cmor_field_name)) msg = 'CMOR equivalent is "'//trim(cmor_field_name)//'_xyave"'
1229  call log_available_diag(fms_xyave_id>0, module_name, trim(field_name)//'_xyave', cm_string, &
1230  msg, diag_cs, long_name, units, standard_name)
1231  endif
1232  endif
1233  this_diag => null()
1234  if (fms_id /= diag_field_not_found .or. fms_xyave_id /= diag_field_not_found) then
1235  call add_diag_to_list(diag_cs, dm_id, fms_id, this_diag, axes, module_name, field_name, msg)
1236  this_diag%fms_xyave_diag_id = fms_xyave_id
1237 
1238  if (present(v_extensive)) this_diag%v_extensive = v_extensive
1239  if (present(conversion)) this_diag%conversion_factor = conversion
1241  endif
1242 
1243  ! For the CMOR variation of the above diagnostic
1244  if (present(cmor_field_name)) then
1245  ! Fallback values for strings set to "NULL"
1246  posted_cmor_units = "not provided" !
1247  posted_cmor_standard_name = "not provided" ! Values might be able to be replaced with a CS%missing field?
1248  posted_cmor_long_name = "not provided" !
1249 
1250  ! If attributes are present for MOM variable names, use them first for the register_diag_field
1251  ! call for CMOR verison of the variable
1252  if (present(units)) posted_cmor_units = units
1253  if (present(standard_name)) posted_cmor_standard_name = standard_name
1254  if (present(long_name)) posted_cmor_long_name = long_name
1255 
1256  ! If specified in the call to register_diag_field, override attributes with the CMOR versions
1257  if (present(cmor_units)) posted_cmor_units = cmor_units
1258  if (present(cmor_standard_name)) posted_cmor_standard_name = cmor_standard_name
1259  if (present(cmor_long_name)) posted_cmor_long_name = cmor_long_name
1260 
1261  fms_id = register_diag_field_expand_axes(module_name, cmor_field_name, axes, init_time, &
1262  long_name=trim(posted_cmor_long_name), units=trim(posted_cmor_units), &
1263  missing_value=mom_missing_value, range=range, mask_variant=mask_variant, &
1264  standard_name=trim(posted_cmor_standard_name), verbose=verbose, do_not_log=do_not_log, &
1265  err_msg=err_msg, interp_method=interp_method, tile_count=tile_count)
1266  call attach_cell_methods(fms_id, axes, cm_string, &
1267  cell_methods, x_cell_method, y_cell_method, v_cell_method, &
1268  v_extensive=v_extensive)
1269  if (is_root_pe() .and. diag_cs%doc_unit > 0) then
1270  msg = 'native name is "'//trim(field_name)//'"'
1271  call log_available_diag(fms_id>0, module_name, cmor_field_name, cm_string, &
1272  msg, diag_cs, posted_cmor_long_name, posted_cmor_units, &
1273  posted_cmor_standard_name)
1274  endif
1275  ! Associated horizontally area-averaged diagnostic
1276  fms_xyave_id = diag_field_not_found
1277  if (associated(axes%xyave_axes)) then
1278  fms_xyave_id = register_diag_field_expand_axes(module_name, trim(cmor_field_name)//'_xyave', &
1279  axes%xyave_axes, init_time, &
1280  long_name=trim(posted_cmor_long_name), units=trim(posted_cmor_units), &
1281  missing_value=mom_missing_value, range=range, mask_variant=mask_variant, &
1282  standard_name=trim(posted_cmor_standard_name), verbose=verbose, do_not_log=do_not_log, &
1283  err_msg=err_msg, interp_method=interp_method, tile_count=tile_count)
1284  call attach_cell_methods(fms_xyave_id, axes%xyave_axes, cm_string, &
1285  cell_methods, v_cell_method, v_extensive=v_extensive)
1286  if (is_root_pe() .and. diag_cs%doc_unit > 0) then
1287  msg = 'native name is "'//trim(field_name)//'_xyave"'
1288  call log_available_diag(fms_xyave_id>0, module_name, trim(cmor_field_name)//'_xyave', cm_string, &
1289  msg, diag_cs, posted_cmor_long_name, posted_cmor_units, &
1290  posted_cmor_standard_name)
1291  endif
1292  endif
1293  this_diag => null()
1294  if (fms_id /= diag_field_not_found .or. fms_xyave_id /= diag_field_not_found) then
1295  call add_diag_to_list(diag_cs, dm_id, fms_id, this_diag, axes, module_name, field_name, msg)
1296  this_diag%fms_xyave_diag_id = fms_xyave_id
1297 
1298  if (present(v_extensive)) this_diag%v_extensive = v_extensive
1299  if (present(conversion)) this_diag%conversion_factor = conversion
1301  endif
1302  endif
1303 
1305 
1306 !> Returns an FMS id from register_diag_field_fms (the diag_manager routine) after expanding axes (axes-group)
1307 !! into handles and conditionally adding an FMS area_id for cell_measures.
1308 integer function register_diag_field_expand_axes(module_name, field_name, axes, init_time, &
1309  long_name, units, missing_value, range, mask_variant, standard_name, &
1310  verbose, do_not_log, err_msg, interp_method, tile_count)
1311  character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model" or "ice_shelf_model"
1312  character(len=*), intent(in) :: field_name !< Name of the diagnostic field
1313  type(axes_grp), target, intent(in) :: axes !< Container w/ up to 3 integer handles that indicates axes for this field
1314  type(time_type), intent(in) :: init_time !< Time at which a field is first available?
1315  character(len=*), optional, intent(in) :: long_name !< Long name of a field.
1316  character(len=*), optional, intent(in) :: units !< Units of a field.
1317  character(len=*), optional, intent(in) :: standard_name !< Standardized name associated with a field
1318  real, optional, intent(in) :: missing_value !< A value that indicates missing values.
1319  real, optional, intent(in) :: range(2) !< Valid range of a variable (not used in MOM?)
1320  logical, optional, intent(in) :: mask_variant !< If true a logical mask must be provided with post_data calls (not used in MOM?)
1321  logical, optional, intent(in) :: verbose !< If true, FMS is verbose (not used in MOM?)
1322  logical, optional, intent(in) :: do_not_log !< If true, do not log something (not used in MOM?)
1323  character(len=*), optional, intent(out):: err_msg !< String into which an error message might be placed (not used in MOM?)
1324  character(len=*), optional, intent(in) :: interp_method !< If 'none' indicates the field should not be interpolated as a scalar
1325  integer, optional, intent(in) :: tile_count !< no clue (not used in MOM?)
1326  ! Local variables
1327  integer :: fms_id, area_id
1328 
1329  ! This gets the cell area associated with the grid location of this variable
1330  area_id = axes%id_area
1331 
1332  ! Get the FMS diagnostic id
1333  if (present(interp_method) .or. axes%is_h_point) then
1334  ! If interp_method is provided we must use it
1335  if (area_id>0) then
1336  fms_id = register_diag_field_fms(module_name, field_name, axes%handles, &
1337  init_time, long_name=long_name, units=units, missing_value=missing_value, &
1338  range=range, mask_variant=mask_variant, standard_name=standard_name, &
1339  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
1340  interp_method=interp_method, tile_count=tile_count, area=area_id)
1341  else
1342  fms_id = register_diag_field_fms(module_name, field_name, axes%handles, &
1343  init_time, long_name=long_name, units=units, missing_value=missing_value, &
1344  range=range, mask_variant=mask_variant, standard_name=standard_name, &
1345  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
1346  interp_method=interp_method, tile_count=tile_count)
1347  endif
1348  else
1349  ! If interp_method is not provided and the field is not at an h-point then interp_method='none'
1350  if (area_id>0) then
1351  fms_id = register_diag_field_fms(module_name, field_name, axes%handles, &
1352  init_time, long_name=long_name, units=units, missing_value=missing_value, &
1353  range=range, mask_variant=mask_variant, standard_name=standard_name, &
1354  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
1355  interp_method='none', tile_count=tile_count, area=area_id)
1356  else
1357  fms_id = register_diag_field_fms(module_name, field_name, axes%handles, &
1358  init_time, long_name=long_name, units=units, missing_value=missing_value, &
1359  range=range, mask_variant=mask_variant, standard_name=standard_name, &
1360  verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, &
1361  interp_method='none', tile_count=tile_count)
1362  endif
1363  endif
1364 
1366 
1368 
1369 !> Create a diagnostic type and attached to list
1370 subroutine add_diag_to_list(diag_cs, dm_id, fms_id, this_diag, axes, module_name, field_name, msg)
1371  type(diag_ctrl), pointer :: diag_cs !< Diagnostics mediator control structure
1372  integer, intent(inout) :: dm_id !< The diag_mediator ID for this diagnostic group
1373  integer, intent(in) :: fms_id !< The FMS diag_manager ID for this diagnostic
1374  type(diag_type), pointer :: this_diag !< This diagnostic
1375  type(axes_grp), target, intent(in) :: axes !< Container w/ up to 3 integer handles that indicates axes for this field
1376  character(len=*), intent(in) :: module_name !< Name of this module, usually "ocean_model" or "ice_shelf_model"
1377  character(len=*), intent(in) :: field_name !< Name of diagnostic
1378  character(len=*), intent(in) :: msg !< Message for errors
1379 
1380  ! If the diagnostic is needed obtain a diag_mediator ID (if needed)
1381  if (dm_id == -1) dm_id = get_new_diag_id(diag_cs)
1382  ! Create a new diag_type to store links in
1383  call alloc_diag_with_id(dm_id, diag_cs, this_diag)
1384  call assert(associated(this_diag), trim(msg)//': diag_type allocation failed')
1385  ! Record FMS id, masks and conversion factor, in diag_type
1386  this_diag%fms_diag_id = fms_id
1387  this_diag%debug_str = trim(module_name)//"-"//trim(field_name)
1388  call set_diag_mask(this_diag, diag_cs, axes)
1389  this_diag%axes => axes
1390 
1391 end subroutine add_diag_to_list
1392 
1393 !> Attaches "cell_methods" attribute to a variable based on defaults for axes_grp or optional arguments.
1394 subroutine attach_cell_methods(id, axes, ostring, cell_methods, &
1395  x_cell_method, y_cell_method, v_cell_method, v_extensive)
1396  integer, intent(in) :: id !< Handle to diagnostic
1397  type(axes_grp), intent(in) :: axes !< Container w/ up to 3 integer handles that indicates axes for this field
1398  character(len=*), intent(out) :: ostring !< The cell_methods strings that would appear in the file
1399  character(len=*), optional, intent(in) :: cell_methods !< String to append as cell_methods attribute. Use '' to have no attribute.
1400  !! If present, this overrides the default constructed from the default for
1401  !! each individual axis direction.
1402  character(len=*), optional, intent(in) :: x_cell_method !< Specifies the cell method for the x-direction. Use '' have no method.
1403  character(len=*), optional, intent(in) :: y_cell_method !< Specifies the cell method for the y-direction. Use '' have no method.
1404  character(len=*), optional, intent(in) :: v_cell_method !< Specifies the cell method for the vertical direction. Use '' have no method.
1405  logical, optional, intent(in) :: v_extensive !< True for vertically extensive fields (vertically integrated). Default/absent for intensive.
1406  ! Local variables
1407  character(len=9) :: axis_name
1408 
1409  ostring = ''
1410  if (present(cell_methods)) then
1411  if (present(x_cell_method) .or. present(y_cell_method) .or. present(v_cell_method) &
1412  .or. present(v_extensive)) then
1413  call mom_error(fatal, "attach_cell_methods: " // &
1414  'Individual direction cell method was specified along with a "cell_methods" string.')
1415  endif
1416  if (len(trim(cell_methods))>0) then
1417  call diag_field_add_attribute(id, 'cell_methods', trim(cell_methods))
1418  ostring = trim(cell_methods)
1419  endif
1420  else
1421  if (present(x_cell_method)) then
1422  if (len(trim(x_cell_method))>0) then
1423  call get_diag_axis_name(axes%handles(1), axis_name)
1424  call diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(x_cell_method))
1425  ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(x_cell_method)
1426  endif
1427  else
1428  if (len(trim(axes%x_cell_method))>0) then
1429  call get_diag_axis_name(axes%handles(1), axis_name)
1430  call diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(axes%x_cell_method))
1431  ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(axes%x_cell_method)
1432  endif
1433  endif
1434  if (present(y_cell_method)) then
1435  if (len(trim(y_cell_method))>0) then
1436  call get_diag_axis_name(axes%handles(2), axis_name)
1437  call diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(y_cell_method))
1438  ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(y_cell_method)
1439  endif
1440  else
1441  if (len(trim(axes%y_cell_method))>0) then
1442  call get_diag_axis_name(axes%handles(2), axis_name)
1443  call diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(axes%y_cell_method))
1444  ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(axes%y_cell_method)
1445  endif
1446  endif
1447  if (present(v_cell_method)) then
1448  if (present(v_extensive)) call mom_error(fatal, "attach_cell_methods: " // &
1449  'Vertical cell method was specified along with the vertically extensive flag.')
1450  if (len(trim(v_cell_method))>0) then
1451  if (axes%rank==1) then
1452  call get_diag_axis_name(axes%handles(1), axis_name)
1453  elseif (axes%rank==3) then
1454  call get_diag_axis_name(axes%handles(3), axis_name)
1455  endif
1456  call diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(v_cell_method))
1457  ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(v_cell_method)
1458  endif
1459  elseif (present(v_extensive)) then
1460  if (axes%rank==1) then
1461  call get_diag_axis_name(axes%handles(1), axis_name)
1462  elseif (axes%rank==3) then
1463  call get_diag_axis_name(axes%handles(3), axis_name)
1464  endif
1465  call diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':sum')
1466  ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':sum'
1467  else
1468  if (len(trim(axes%v_cell_method))>0) then
1469  if (axes%rank==1) then
1470  call get_diag_axis_name(axes%handles(1), axis_name)
1471  elseif (axes%rank==3) then
1472  call get_diag_axis_name(axes%handles(3), axis_name)
1473  endif
1474  call diag_field_add_attribute(id, 'cell_methods', trim(axis_name)//':'//trim(axes%v_cell_method))
1475  ostring = trim(adjustl(ostring))//' '//trim(axis_name)//':'//trim(axes%v_cell_method)
1476  endif
1477  endif
1478  endif
1479  ostring = adjustl(ostring)
1480 end subroutine attach_cell_methods
1481 
1482 function register_scalar_field(module_name, field_name, init_time, diag_cs, &
1483  long_name, units, missing_value, range, standard_name, &
1484  do_not_log, err_msg, interp_method, cmor_field_name, &
1485  cmor_long_name, cmor_units, cmor_standard_name)
1486  integer :: register_scalar_field
1487  character(len=*), intent(in) :: module_name, field_name
1488  type(time_type), intent(in) :: init_time
1489  type(diag_ctrl), intent(inout) :: diag_cs
1490  character(len=*), optional, intent(in) :: long_name, units, standard_name
1491  real, optional, intent(in) :: missing_value, range(2)
1492  logical, optional, intent(in) :: do_not_log
1493  character(len=*), optional, intent(out):: err_msg
1494  character(len=*), optional, intent(in) :: interp_method
1495  character(len=*), optional, intent(in) :: cmor_field_name, cmor_long_name
1496  character(len=*), optional, intent(in) :: cmor_units, cmor_standard_name
1497 
1498  ! Output: An integer handle for a diagnostic array.
1499  ! Arguments:
1500  ! (in) module_name - name of this module, usually "ocean_model" or "ice_shelf_model".
1501  ! (in) field_name - name of the diagnostic field.
1502  ! (in) init_time - time at which a field is first available?
1503  ! (inout) diag_cs - structure used to regulate diagnostic output
1504  ! (in,opt) long_name - long name of a field
1505  ! (in,opt) units - units of a field
1506  ! (in,opt) missing_value - indicates missing values
1507  ! (in,opt) standard_name - standardized name associated with a field
1508 
1509  ! Following params have yet to be used in MOM.
1510  ! (in,opt) range - valid range of a variable
1511  ! (in,opt) verbose - If true, FMS is verbosed
1512  ! (in,opt) do_not_log - If true, do not log something
1513  ! (out,opt) err_msg - character string into which an error message might be placed
1514  ! (in,opt) interp_method - If 'none' indicates the field should not be interpolated as a scalar
1515  ! (in,opt) tile_count - no clue
1516 
1517  real :: MOM_missing_value
1518  integer :: dm_id, fms_id
1519  type(diag_type), pointer :: diag => null(), cmor_diag => null()
1520  character(len=256) :: posted_cmor_units, posted_cmor_standard_name, posted_cmor_long_name
1521 
1522  mom_missing_value = diag_cs%missing_value
1523  if(present(missing_value)) mom_missing_value = missing_value
1524 
1525  dm_id = -1
1526  diag => null()
1527  cmor_diag => null()
1528 
1529  fms_id = register_diag_field_fms(module_name, field_name, init_time, &
1530  long_name=long_name, units=units, missing_value=mom_missing_value, &
1531  range=range, standard_name=standard_name, do_not_log=do_not_log, err_msg=err_msg)
1532  if (fms_id /= diag_field_not_found) then
1533  dm_id = get_new_diag_id(diag_cs)
1534  call alloc_diag_with_id(dm_id, diag_cs, diag)
1535  call assert(associated(diag), 'register_scalar_field: diag allocation failed')
1536  diag%fms_diag_id = fms_id
1537  diag%debug_str = trim(module_name)//"-"//trim(field_name)
1538  endif
1539 
1540  if (present(cmor_field_name)) then
1541  ! Fallback values for strings set to "not provided"
1542  posted_cmor_units = "not provided"
1543  posted_cmor_standard_name = "not provided"
1544  posted_cmor_long_name = "not provided"
1545 
1546  ! If attributes are present for MOM variable names, use them first for the register_static_field
1547  ! call for CMOR verison of the variable
1548  if (present(units)) posted_cmor_units = units
1549  if (present(standard_name)) posted_cmor_standard_name = standard_name
1550  if (present(long_name)) posted_cmor_long_name = long_name
1551 
1552  ! If specified in the call to register_static_field, override attributes with the CMOR versions
1553  if (present(cmor_units)) posted_cmor_units = cmor_units
1554  if (present(cmor_standard_name)) posted_cmor_standard_name = cmor_standard_name
1555  if (present(cmor_long_name)) posted_cmor_long_name = cmor_long_name
1556 
1557  fms_id = register_diag_field_fms(module_name, cmor_field_name, init_time, &
1558  long_name=trim(posted_cmor_long_name), units=trim(posted_cmor_units), &
1559  missing_value=mom_missing_value, range=range, &
1560  standard_name=trim(posted_cmor_standard_name), do_not_log=do_not_log, err_msg=err_msg)
1561  if (fms_id /= diag_field_not_found) then
1562  if (dm_id == -1) then
1563  dm_id = get_new_diag_id(diag_cs)
1564  endif
1565  call alloc_diag_with_id(dm_id, diag_cs, cmor_diag)
1566  cmor_diag%fms_diag_id = fms_id
1567  cmor_diag%debug_str = trim(module_name)//"-"//trim(cmor_field_name)
1568  endif
1569  endif
1570 
1571  ! Document diagnostics in list of available diagnostics
1572  if (is_root_pe() .and. diag_cs%doc_unit > 0) then
1573  call log_available_diag(associated(diag), module_name, field_name, '', '', diag_cs, &
1574  long_name, units, standard_name)
1575  if (present(cmor_field_name)) then
1576  call log_available_diag(associated(cmor_diag), module_name, cmor_field_name, &
1577  '', '', diag_cs, posted_cmor_long_name, posted_cmor_units, &
1578  posted_cmor_standard_name)
1579  endif
1580  endif
1581 
1582  register_scalar_field = dm_id
1583 
1584 end function register_scalar_field
1585 
1586 !> Registers a static diagnostic, returning an integer handle
1587 function register_static_field(module_name, field_name, axes, &
1588  long_name, units, missing_value, range, mask_variant, standard_name, &
1589  do_not_log, interp_method, tile_count, &
1590  cmor_field_name, cmor_long_name, cmor_units, cmor_standard_name, area)
1591  integer :: register_static_field
1592  character(len=*), intent(in) :: module_name, field_name
1593  type(axes_grp), intent(in) :: axes
1594  character(len=*), optional, intent(in) :: long_name, units, standard_name
1595  real, optional, intent(in) :: missing_value, range(2)
1596  logical, optional, intent(in) :: mask_variant, do_not_log
1597  character(len=*), optional, intent(in) :: interp_method
1598  integer, optional, intent(in) :: tile_count
1599  character(len=*), optional, intent(in) :: cmor_field_name, cmor_long_name
1600  character(len=*), optional, intent(in) :: cmor_units, cmor_standard_name
1601  integer, optional, intent(in) :: area !< fms_id for area_t
1602 
1603  ! Output: An integer handle for a diagnostic array.
1604  ! Arguments:
1605  ! (in) module_name - name of this module, usually "ocean_model" or "ice_shelf_model".
1606  ! (in) field_name - name of the diagnostic field
1607  ! (in) axes - container with up to 3 integer handles that indicates axes for this field
1608  ! (in,opt) long_name - long name of a field
1609  ! (in,opt) units - units of a field
1610  ! (in,opt) missing_value - A value that indicates missing values.
1611  ! (in,opt) standard_name - standardized name associated with a field
1612 
1613  ! Following params have yet to be used in MOM.
1614  ! (in,opt) range - valid range of a variable
1615  ! (in,opt) mask_variant - If true a logical mask must be provided with post_data calls
1616  ! (in,opt) do_not_log - If true, do not log something
1617  ! (in,opt) interp_method - If 'none' indicates the field should not be interpolated as a scalar
1618  ! (in,opt) tile_count - no clue
1619 
1620  real :: MOM_missing_value
1621  type(diag_ctrl), pointer :: diag_cs
1622  type(diag_type), pointer :: diag => null(), cmor_diag => null()
1623  integer :: dm_id, fms_id, cmor_id
1624  character(len=256) :: posted_cmor_units, posted_cmor_standard_name, posted_cmor_long_name
1625 
1626  mom_missing_value = axes%diag_cs%missing_value
1627  if(present(missing_value)) mom_missing_value = missing_value
1628 
1629  diag_cs => axes%diag_cs
1630  dm_id = -1
1631  diag => null()
1632  cmor_diag => null()
1633 
1634  fms_id = register_static_field_fms(module_name, field_name, axes%handles, &
1635  long_name=long_name, units=units, missing_value=mom_missing_value, &
1636  range=range, mask_variant=mask_variant, standard_name=standard_name, &
1637  do_not_log=do_not_log, &
1638  interp_method=interp_method, tile_count=tile_count, area=area)
1639  if (fms_id /= diag_field_not_found) then
1640  dm_id = get_new_diag_id(diag_cs)
1641  call alloc_diag_with_id(dm_id, diag_cs, diag)
1642  call assert(associated(diag), 'register_static_field: diag allocation failed')
1643  diag%fms_diag_id = fms_id
1644  diag%debug_str = trim(module_name)//"-"//trim(field_name)
1645  endif
1646 
1647  if (present(cmor_field_name)) then
1648  ! Fallback values for strings set to "not provided"
1649  posted_cmor_units = "not provided"
1650  posted_cmor_standard_name = "not provided"
1651  posted_cmor_long_name = "not provided"
1652 
1653  ! If attributes are present for MOM variable names, use them first for the register_static_field
1654  ! call for CMOR verison of the variable
1655  if (present(units)) posted_cmor_units = units
1656  if (present(standard_name)) posted_cmor_standard_name = standard_name
1657  if (present(long_name)) posted_cmor_long_name = long_name
1658 
1659  ! If specified in the call to register_static_field, override attributes with the CMOR versions
1660  if (present(cmor_units)) posted_cmor_units = cmor_units
1661  if (present(cmor_standard_name)) posted_cmor_standard_name = cmor_standard_name
1662  if (present(cmor_long_name)) posted_cmor_long_name = cmor_long_name
1663 
1664  fms_id = register_static_field_fms(module_name, cmor_field_name, &
1665  axes%handles, long_name=trim(posted_cmor_long_name), units=trim(posted_cmor_units), &
1666  missing_value=mom_missing_value, range=range, mask_variant=mask_variant, &
1667  standard_name=trim(posted_cmor_standard_name), do_not_log=do_not_log, &
1668  interp_method=interp_method, tile_count=tile_count, area=area)
1669  if (fms_id /= diag_field_not_found) then
1670  if (dm_id == -1) then
1671  dm_id = get_new_diag_id(diag_cs)
1672  endif
1673  call alloc_diag_with_id(dm_id, diag_cs, cmor_diag)
1674  cmor_diag%fms_diag_id = fms_id
1675  cmor_diag%debug_str = trim(module_name)//"-"//trim(cmor_field_name)
1676  endif
1677  endif
1678 
1679  ! Document diagnostics in list of available diagnostics
1680  if (is_root_pe() .and. diag_cs%doc_unit > 0) then
1681  call log_available_diag(associated(diag), module_name, field_name, '', '', diag_cs, &
1682  long_name, units, standard_name)
1683  if (present(cmor_field_name)) then
1684  call log_available_diag(associated(cmor_diag), module_name, cmor_field_name, &
1685  '', '', diag_cs, posted_cmor_long_name, posted_cmor_units, &
1686  posted_cmor_standard_name)
1687  endif
1688  endif
1689 
1690  register_static_field = dm_id
1691 
1692 end function register_static_field
1693 
1694 subroutine describe_option(opt_name, value, diag_CS)
1695  character(len=*), intent(in) :: opt_name, value
1696  type(diag_ctrl), intent(in) :: diag_CS
1697 
1698  character(len=240) :: mesg
1699  integer :: len_ind
1700 
1701  len_ind = len_trim(value) ! Add error handling for long values?
1702 
1703  mesg = " ! "//trim(opt_name)//": "//trim(value)
1704  write(diag_cs%doc_unit, '(a)') trim(mesg)
1705 end subroutine describe_option
1706 
1707 !> Registers a diagnostic using the information encapsulated in the vardesc
1708 !! type argument and returns an integer handle to this diagostic. That
1709 !! integer handle is negative if the diagnostic is unused.
1710 function ocean_register_diag(var_desc, G, diag_CS, day)
1711  integer :: ocean_register_diag !< An integer handle to this diagnostic.
1712  type(vardesc), intent(in) :: var_desc !< The vardesc type describing the diagnostic
1713  type(ocean_grid_type), intent(in) :: G !< The ocean's grid type
1714  type(diag_ctrl), intent(in), target :: diag_CS !< The diagnotic control structure
1715  type(time_type), intent(in) :: day !< The current model time
1716 
1717  character(len=64) :: var_name ! A variable's name.
1718  character(len=48) :: units ! A variable's units.
1719  character(len=240) :: longname ! A variable's longname.
1720  character(len=8) :: hor_grid, z_grid ! Variable grid info.
1721  type(axes_grp), pointer :: axes
1722 
1723  call query_vardesc(var_desc, units=units, longname=longname, hor_grid=hor_grid, &
1724  z_grid=z_grid, caller="ocean_register_diag")
1725 
1726  ! Use the hor_grid and z_grid components of vardesc to determine the
1727  ! desired axes to register the diagnostic field for.
1728  select case (z_grid)
1729 
1730  case ("L")
1731  select case (hor_grid)
1732  case ("q")
1733  axes => diag_cs%axesBL
1734  case ("h")
1735  axes => diag_cs%axesTL
1736  case ("u")
1737  axes => diag_cs%axesCuL
1738  case ("v")
1739  axes => diag_cs%axesCvL
1740  case ("Bu")
1741  axes => diag_cs%axesBL
1742  case ("T")
1743  axes => diag_cs%axesTL
1744  case ("Cu")
1745  axes => diag_cs%axesCuL
1746  case ("Cv")
1747  axes => diag_cs%axesCvL
1748  case ("z")
1749  axes => diag_cs%axeszL
1750  case default
1751  call mom_error(fatal, "ocean_register_diag: " // &
1752  "unknown hor_grid component "//trim(hor_grid))
1753  end select
1754 
1755  case ("i")
1756  select case (hor_grid)
1757  case ("q")
1758  axes => diag_cs%axesBi
1759  case ("h")
1760  axes => diag_cs%axesTi
1761  case ("u")
1762  axes => diag_cs%axesCui
1763  case ("v")
1764  axes => diag_cs%axesCvi
1765  case ("Bu")
1766  axes => diag_cs%axesBi
1767  case ("T")
1768  axes => diag_cs%axesTi
1769  case ("Cu")
1770  axes => diag_cs%axesCui
1771  case ("Cv")
1772  axes => diag_cs%axesCvi
1773  case ("z")
1774  axes => diag_cs%axeszi
1775  case default
1776  call mom_error(fatal, "ocean_register_diag: " // &
1777  "unknown hor_grid component "//trim(hor_grid))
1778  end select
1779 
1780  case ("1")
1781  select case (hor_grid)
1782  case ("q")
1783  axes => diag_cs%axesB1
1784  case ("h")
1785  axes => diag_cs%axesT1
1786  case ("u")
1787  axes => diag_cs%axesCu1
1788  case ("v")
1789  axes => diag_cs%axesCv1
1790  case ("Bu")
1791  axes => diag_cs%axesB1
1792  case ("T")
1793  axes => diag_cs%axesT1
1794  case ("Cu")
1795  axes => diag_cs%axesCu1
1796  case ("Cv")
1797  axes => diag_cs%axesCv1
1798  case default
1799  call mom_error(fatal, "ocean_register_diag: " // &
1800  "unknown hor_grid component "//trim(hor_grid))
1801  end select
1802 
1803  case default
1804  call mom_error(fatal,&
1805  "ocean_register_diag: unknown z_grid component "//trim(z_grid))
1806  end select
1807 
1808  ocean_register_diag = register_diag_field("ocean_model", trim(var_name), &
1809  axes, day, trim(longname), trim(units), missing_value = -1.0e+34)
1810 
1811 end function ocean_register_diag
1812 
1813 subroutine diag_mediator_infrastructure_init(err_msg)
1814  ! This subroutine initializes the FMS diag_manager.
1815  character(len=*), optional, intent(out) :: err_msg
1816 
1817  call diag_manager_init(err_msg=err_msg)
1818 end subroutine diag_mediator_infrastructure_init
1819 
1820 !> diag_mediator_init initializes the MOM diag_mediator and opens the available
1821 !! diagnostics file, if appropriate.
1822 subroutine diag_mediator_init(G, nz, param_file, diag_cs, doc_file_dir)
1823  type(ocean_grid_type), target, intent(inout) :: G !< The ocean grid type.
1824  integer, intent(in) :: nz !< The number of layers in the model's native grid.
1825  type(param_file_type), intent(in) :: param_file !< Parameter file structure
1826  type(diag_ctrl), intent(inout) :: diag_cs !< A pointer to a type with many variables
1827  !! used for diagnostics
1828  character(len=*), optional, intent(in) :: doc_file_dir !< A directory in which to create the
1829  !! file
1830 
1831  ! This subroutine initializes the diag_mediator and the diag_manager.
1832  ! The grid type should have its dimensions set by this point, but it
1833  ! is not necessary that the metrics and axis labels be set up yet.
1834  integer :: ios, i, new_unit
1835  logical :: opened, new_file
1836  character(len=8) :: this_pe
1837  character(len=240) :: doc_file, doc_file_dflt, doc_path
1838  character(len=240), allocatable :: diag_coords(:)
1839 ! This include declares and sets the variable "version".
1840 #include "version_variable.h"
1841  character(len=40) :: mod = "MOM_diag_mediator" ! This module's name.
1842 
1843  id_clock_diag_mediator = cpu_clock_id('(Ocean diagnostics framework)', grain=clock_module)
1844  id_clock_diag_remap = cpu_clock_id('(Ocean diagnostics remapping)', grain=clock_routine)
1845  id_clock_diag_grid_updates = cpu_clock_id('(Ocean diagnostics grid updates)', grain=clock_routine)
1846 
1847  ! Allocate and initialize list of all diagnostics (and variants)
1848  allocate(diag_cs%diags(diag_alloc_chunk_size))
1849  diag_cs%next_free_diag_id = 1
1850  do i=1, diag_alloc_chunk_size
1851  call initialize_diag_type(diag_cs%diags(i))
1852  enddo
1853 
1854  ! Read all relevant parameters and write them to the model log.
1855  call log_version(param_file, mod, version, "")
1856 
1857  call get_param(param_file, mod, 'NUM_DIAG_COORDS', diag_cs%num_diag_coords, &
1858  'The number of diagnostic vertical coordinates to use.\n'//&
1859  'For each coordinate, an entry in DIAG_COORDS must be provided.', &
1860  default=1)
1861  if (diag_cs%num_diag_coords>0) then
1862  allocate(diag_coords(diag_cs%num_diag_coords))
1863  if (diag_cs%num_diag_coords==1) then ! The default is to provide just one instance of Z*
1864  call get_param(param_file, mod, 'DIAG_COORDS', diag_coords, &
1865  'A list of string tuples associating diag_table modules to\n'//&
1866  'a coordinate definition used for diagnostics. Each string\n'//&
1867  'is of the form "MODULE_SUFFIX PARAMETER_SUFFIX COORDINATE_NAME".', &
1868  default='z Z ZSTAR')
1869  else ! If using more than 1 diagnostic coordinate, all must be explicitly defined
1870  call get_param(param_file, mod, 'DIAG_COORDS', diag_coords, &
1871  'A list of string tuples associating diag_table modules to\n'//&
1872  'a coordinate definition used for diagnostics. Each string\n'//&
1873  'is of the form "MODULE_SUFFIX,PARAMETER_SUFFIX,COORDINATE_NAME".', &
1874  fail_if_missing=.true.)
1875  endif
1876  allocate(diag_cs%diag_remap_cs(diag_cs%num_diag_coords))
1877  ! Initialize each diagnostic vertical coordinate
1878  do i=1, diag_cs%num_diag_coords
1879  call diag_remap_init(diag_cs%diag_remap_cs(i), diag_coords(i))
1880  enddo
1881  deallocate(diag_coords)
1882  endif
1883 
1884  call get_param(param_file, mod, 'DIAG_MISVAL', diag_cs%missing_value, &
1885  'Set the default missing value to use for diagnostics.', &
1886  default=-1.e34)
1887 
1888  ! Keep pointers grid, h, T, S needed diagnostic remapping
1889  diag_cs%G => g
1890  diag_cs%h => null()
1891  diag_cs%T => null()
1892  diag_cs%S => null()
1893  diag_cs%eqn_of_state => null()
1894 
1895 #if defined(DEBUG) || defined(__DO_SAFETY_CHECKS__)
1896  allocate(diag_cs%h_old(g%isd:g%ied,g%jsd:g%jed,nz))
1897  diag_cs%h_old(:,:,:) = 0.0
1898 #endif
1899 
1900  diag_cs%is = g%isc - (g%isd-1) ; diag_cs%ie = g%iec - (g%isd-1)
1901  diag_cs%js = g%jsc - (g%jsd-1) ; diag_cs%je = g%jec - (g%jsd-1)
1902  diag_cs%isd = g%isd ; diag_cs%ied = g%ied
1903  diag_cs%jsd = g%jsd ; diag_cs%jed = g%jed
1904 
1905  if (is_root_pe() .and. (diag_cs%doc_unit < 0)) then
1906  write(this_pe,'(i6.6)') pe_here()
1907  doc_file_dflt = "available_diags."//this_pe
1908  call get_param(param_file, mod, "AVAILABLE_DIAGS_FILE", doc_file, &
1909  "A file into which to write a list of all available \n"//&
1910  "ocean diagnostics that can be included in a diag_table.", &
1911  default=doc_file_dflt, do_not_log=(diag_cs%doc_unit/=-1))
1912  if (len_trim(doc_file) > 0) then
1913  new_file = .true. ; if (diag_cs%doc_unit /= -1) new_file = .false.
1914  ! Find an unused unit number.
1915  do new_unit=512,42,-1
1916  inquire( new_unit, opened=opened)
1917  if (.not.opened) exit
1918  enddo
1919  if (opened) call mom_error(fatal, &
1920  "diag_mediator_init failed to find an unused unit number.")
1921 
1922  doc_path = doc_file
1923  if (present(doc_file_dir)) then ; if (len_trim(doc_file_dir) > 0) then
1924  doc_path = trim(slasher(doc_file_dir))//trim(doc_file)
1925  endif ; endif
1926 
1927  diag_cs%doc_unit = new_unit
1928 
1929  if (new_file) then
1930  open(diag_cs%doc_unit, file=trim(doc_path), access='SEQUENTIAL', form='FORMATTED', &
1931  action='WRITE', status='REPLACE', iostat=ios)
1932  else ! This file is being reopened, and should be appended.
1933  open(diag_cs%doc_unit, file=trim(doc_path), access='SEQUENTIAL', form='FORMATTED', &
1934  action='WRITE', status='OLD', position='APPEND', iostat=ios)
1935  endif
1936  inquire(diag_cs%doc_unit, opened=opened)
1937  if ((.not.opened) .or. (ios /= 0)) then
1938  call mom_error(fatal, "Failed to open available diags file "//trim(doc_path)//".")
1939  endif
1940  endif
1941  endif
1942 
1943 end subroutine diag_mediator_init
1944 
1945 subroutine diag_set_state_ptrs(h, T, S, eqn_of_state, diag_cs)
1947  real, dimension(:,:,:), target, intent(in) :: h, T, S
1948  type(eos_type), pointer, intent(in) :: eqn_of_state !< Equation of state structure
1949  type(diag_ctrl), intent(inout) :: diag_cs
1950 
1951  ! (inout) diag_cs - diag mediator control structure
1952  ! (in) h - a pointer to model thickness
1953  ! (in) T - a pointer to model temperature
1954  ! (in) S - a pointer to model salinity
1955 
1956  ! Keep pointers to h, T, S needed for the diagnostic remapping
1957  diag_cs%h => h
1958  diag_cs%T => t
1959  diag_cs%S => s
1960  diag_cs%eqn_of_state => eqn_of_state
1961 
1962 end subroutine
1963 
1964 !> Build/update vertical grids for diagnostic remapping.
1965 !! \note The target grids need to be updated whenever sea surface
1966 !! height changes.
1967 subroutine diag_update_remap_grids(diag_cs, alt_h)
1968  type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
1969  real, target, optional, intent(in ) :: alt_h(:,:,:) !< Used if remapped grids should be
1970  !! something other than the current
1971  !! thicknesses
1972  ! Local variables
1973  integer :: i
1974  real, dimension(:,:,:), pointer :: h_diag
1975 
1976  if(present(alt_h)) then
1977  h_diag => alt_h
1978  else
1979  h_diag => diag_cs%h
1980  endif
1981 
1982  if (id_clock_diag_grid_updates>0) call cpu_clock_begin(id_clock_diag_grid_updates)
1983 
1984  do i=1, diag_cs%num_diag_coords
1985  call diag_remap_update(diag_cs%diag_remap_cs(i), &
1986  diag_cs%G, h_diag, diag_cs%T, diag_cs%S, &
1987  diag_cs%eqn_of_state)
1988  enddo
1989 
1990 #if defined(DEBUG) || defined(__DO_SAFETY_CHECKS__)
1991  ! Keep a copy of H - used to check whether grids are up-to-date
1992  ! when doing remapping.
1993  diag_cs%h_old(:,:,:) = diag_cs%h(:,:,:)
1994 #endif
1995 
1996  if (id_clock_diag_grid_updates>0) call cpu_clock_end(id_clock_diag_grid_updates)
1997 
1998 end subroutine diag_update_remap_grids
1999 
2000 !> diag_masks_set sets up the 2d and 3d masks for diagnostics
2001 subroutine diag_masks_set(G, nz, missing_value, diag_cs)
2002  type(ocean_grid_type), target, intent(in) :: G !< The ocean grid type.
2003  integer, intent(in) :: nz !< The number of layers in the model's native grid.
2004  real, intent(in) :: missing_value !< A value to use for masked points.
2005  type(diag_ctrl), pointer :: diag_cs !< A pointer to a type with many variables
2006  !! used for diagnostics
2007  ! Local variables
2008  integer :: k
2009 
2010  diag_cs%mask2dT => g%mask2dT
2011  diag_cs%mask2dBu=> g%mask2dBu
2012  diag_cs%mask2dCu=> g%mask2dCu
2013  diag_cs%mask2dCv=> g%mask2dCv
2014  allocate(diag_cs%mask3dTL(g%isd:g%ied,g%jsd:g%jed,1:nz))
2015  allocate(diag_cs%mask3dBL(g%IsdB:g%IedB,g%JsdB:g%JedB,1:nz))
2016  allocate(diag_cs%mask3dCuL(g%IsdB:g%IedB,g%jsd:g%jed,1:nz))
2017  allocate(diag_cs%mask3dCvL(g%isd:g%ied,g%JsdB:g%JedB,1:nz))
2018  do k=1,nz
2019  diag_cs%mask3dTL(:,:,k) = diag_cs%mask2dT (:,:)
2020  diag_cs%mask3dBL(:,:,k) = diag_cs%mask2dBu(:,:)
2021  diag_cs%mask3dCuL(:,:,k) = diag_cs%mask2dCu(:,:)
2022  diag_cs%mask3dCvL(:,:,k) = diag_cs%mask2dCv(:,:)
2023  enddo
2024  allocate(diag_cs%mask3dTi(g%isd:g%ied,g%jsd:g%jed,1:nz+1))
2025  allocate(diag_cs%mask3dBi(g%IsdB:g%IedB,g%JsdB:g%JedB,1:nz+1))
2026  allocate(diag_cs%mask3dCui(g%IsdB:g%IedB,g%jsd:g%jed,1:nz+1))
2027  allocate(diag_cs%mask3dCvi(g%isd:g%ied,g%JsdB:g%JedB,1:nz+1))
2028  do k=1,nz+1
2029  diag_cs%mask3dTi(:,:,k) = diag_cs%mask2dT (:,:)
2030  diag_cs%mask3dBi(:,:,k) = diag_cs%mask2dBu(:,:)
2031  diag_cs%mask3dCui(:,:,k) = diag_cs%mask2dCu(:,:)
2032  diag_cs%mask3dCvi(:,:,k) = diag_cs%mask2dCv(:,:)
2033  enddo
2034 
2035 ! diag_cs%missing_value = missing_value
2036 
2037 end subroutine diag_masks_set
2038 
2039 subroutine diag_mediator_close_registration(diag_CS)
2040  type(diag_ctrl), intent(inout) :: diag_CS
2041 
2042  integer :: i
2043 
2044  if (diag_cs%doc_unit > -1) then
2045  close(diag_cs%doc_unit) ; diag_cs%doc_unit = -2
2046  endif
2047 
2048  do i=1, diag_cs%num_diag_coords
2049  call diag_remap_diag_registration_closed(diag_cs%diag_remap_cs(i))
2050  enddo
2051 
2052 end subroutine diag_mediator_close_registration
2053 
2054 subroutine diag_mediator_end(time, diag_CS, end_diag_manager)
2055  type(time_type), intent(in) :: time
2056  type(diag_ctrl), intent(inout) :: diag_cs
2057  logical, optional, intent(in) :: end_diag_manager !< If true, call diag_manager_end()
2058 
2059  ! Local variables
2060  integer :: i
2061 
2062  if (diag_cs%doc_unit > -1) then
2063  close(diag_cs%doc_unit) ; diag_cs%doc_unit = -3
2064  endif
2065 
2066  deallocate(diag_cs%diags)
2067 
2068  do i=1, diag_cs%num_diag_coords
2069  call diag_remap_end(diag_cs%diag_remap_cs(i))
2070  enddo
2071 
2072  deallocate(diag_cs%mask3dTL)
2073  deallocate(diag_cs%mask3dBL)
2074  deallocate(diag_cs%mask3dCuL)
2075  deallocate(diag_cs%mask3dCvL)
2076  deallocate(diag_cs%mask3dTi)
2077  deallocate(diag_cs%mask3dBi)
2078  deallocate(diag_cs%mask3dCui)
2079  deallocate(diag_cs%mask3dCvi)
2080 
2081 #if defined(DEBUG) || defined(__DO_SAFETY_CHECKS__)
2082  deallocate(diag_cs%h_old)
2083 #endif
2084 
2085  if (present(end_diag_manager)) then
2086  if (end_diag_manager) call diag_manager_end(time)
2087  endif
2088 
2089 end subroutine diag_mediator_end
2090 
2091 function i2s(a,n_in)
2092 ! "Convert the first n elements of an integer array to a string."
2093  integer, dimension(:), intent(in) :: a
2094  integer, optional , intent(in) :: n_in
2095  character(len=15) :: i2s
2096 
2097  character(len=15) :: i2s_temp
2098  integer :: i,n
2099 
2100  n=size(a)
2101  if(present(n_in)) n = n_in
2102 
2103  i2s = ''
2104  do i=1,n
2105  write (i2s_temp, '(I4.4)') a(i)
2106  i2s = trim(i2s) //'_'// trim(i2s_temp)
2107  enddo
2108  i2s = adjustl(i2s)
2109 end function i2s
2110 
2111 !> Associates the mask pointers within diag with the appropriate mask based on the axes group.
2112 subroutine set_diag_mask(diag, diag_cs, axes)
2113  type(diag_ctrl), target, intent(in) :: diag_cs !< Diag_mediator control structure
2114  type(diag_type), pointer, intent(inout) :: diag !< This diag type
2115  type(axes_grp), intent(in) :: axes !< Axes group
2116 
2117  diag%mask2d => null()
2118  diag%mask3d => null()
2119 
2120  if (axes%rank .eq. 3) then
2121  if (axes%is_layer) then
2122  if (axes%is_h_point) then
2123  diag%mask3d => diag_cs%mask3dTL
2124  elseif (axes%is_q_point) then
2125  diag%mask3d => diag_cs%mask3dBL
2126  elseif (axes%is_u_point) then
2127  diag%mask3d => diag_cs%mask3dCuL
2128  elseif (axes%is_v_point) then
2129  diag%mask3d => diag_cs%mask3dCvL
2130  endif
2131  elseif (axes%is_interface) then
2132  if (axes%is_h_point) then
2133  diag%mask3d => diag_cs%mask3dTi
2134  elseif (axes%is_q_point) then
2135  diag%mask3d => diag_cs%mask3dBi
2136  elseif (axes%is_u_point) then
2137  diag%mask3d => diag_cs%mask3dCui
2138  elseif (axes%is_v_point) then
2139  diag%mask3d => diag_cs%mask3dCvi
2140  endif
2141  endif
2142 
2143  !call assert(associated(diag%mask3d), "set_diag_mask: Invalid 3d axes id."// &
2144  ! " diag:"//diag%debug_str)
2145  elseif(axes%rank .eq. 2) then
2146 
2147  if (axes%is_h_point) then
2148  diag%mask2d => diag_cs%mask2dT
2149  elseif (axes%is_q_point) then
2150  diag%mask2d => diag_cs%mask2dBu
2151  elseif (axes%is_u_point) then
2152  diag%mask2d => diag_cs%mask2dCu
2153  elseif (axes%is_v_point) then
2154  diag%mask2d => diag_cs%mask2dCv
2155  endif
2156 
2157  !call assert(associated(diag%mask2d), "set_diag_mask.F90: Invalid 2d axes id."// &
2158  ! " diag:"//diag%debug_str)
2159  endif
2160 
2161 end subroutine set_diag_mask
2162 
2163 !> Returns a new diagnostic id, it may be necessary to expand the diagnostics array.
2164 integer function get_new_diag_id(diag_cs)
2165  type(diag_ctrl), intent(inout) :: diag_cs !< Diagnostics control structure
2166  ! Local variables
2167  type(diag_type), dimension(:), allocatable :: tmp
2168  integer :: i
2169 
2170  if (diag_cs%next_free_diag_id > size(diag_cs%diags)) then
2171  call assert(diag_cs%next_free_diag_id - size(diag_cs%diags) == 1, &
2172  'get_new_diag_id: inconsistent diag id')
2173 
2174  ! Increase the size of diag_cs%diags and copy data over.
2175  ! Do not use move_alloc() because it is not supported by Fortran 90
2176  allocate(tmp(size(diag_cs%diags)))
2177  tmp(:) = diag_cs%diags(:)
2178  deallocate(diag_cs%diags)
2179  allocate(diag_cs%diags(size(tmp) + diag_alloc_chunk_size))
2180  diag_cs%diags(1:size(tmp)) = tmp(:)
2181  deallocate(tmp)
2182 
2183  ! Initialize new part of the diag array.
2184  do i=diag_cs%next_free_diag_id, size(diag_cs%diags)
2185  call initialize_diag_type(diag_cs%diags(i))
2186  enddo
2187  endif
2188 
2189  get_new_diag_id = diag_cs%next_free_diag_id
2190  diag_cs%next_free_diag_id = diag_cs%next_free_diag_id + 1
2191 
2192 end function get_new_diag_id
2193 
2194 !> Initializes a diag_type (used after allocating new memory)
2195 subroutine initialize_diag_type(diag)
2196  type(diag_type), intent(inout) :: diag !< diag_type to be initialized
2197 
2198  diag%in_use = .false.
2199  diag%fms_diag_id = -1
2200  diag%axes => null()
2201  diag%mask2d => null()
2202  diag%mask3d => null()
2203  diag%next => null()
2204  diag%conversion_factor = 0.
2205 
2206 end subroutine initialize_diag_type
2207 
2208 ! Make a new diagnostic. Either use memory which is in the array of 'primary'
2209 ! diagnostics, or if that is in use, insert it to the list of secondary diags.
2210 subroutine alloc_diag_with_id(diag_id, diag_cs, diag)
2211  integer, intent(in) :: diag_id
2212  type(diag_ctrl), target, intent(inout) :: diag_cs
2213  type(diag_type), pointer, intent(out) :: diag
2214 
2215  ! Arguments:
2216  ! (in) diag_id - new id for the diag.
2217  ! (inout) diag_cs - structure used to regulate diagnostic output
2218  ! (inout) diag - structure representing a diagnostic
2219 
2220  type(diag_type), pointer :: tmp
2221 
2222  if (.not. diag_cs%diags(diag_id)%in_use) then
2223  diag => diag_cs%diags(diag_id)
2224  else
2225  allocate(diag)
2226  tmp => diag_cs%diags(diag_id)%next
2227  diag_cs%diags(diag_id)%next => diag
2228  diag%next => tmp
2229  endif
2230  diag%in_use = .true.
2231 
2232 end subroutine alloc_diag_with_id
2233 
2234 !> Log a diagnostic to the available diagnostics file.
2235 subroutine log_available_diag(used, module_name, field_name, cell_methods_string, comment, &
2236  diag_CS, long_name, units, standard_name)
2237  logical, intent(in) :: used !< Whether this diagnostic was in the diag_table or not
2238  character(len=*), intent(in) :: module_name !< Name of the diagnostic module
2239  character(len=*), intent(in) :: field_name !< Name of this diagnostic field
2240  character(len=*), intent(in) :: cell_methods_string !< The spatial component of the CF cell_methods attribute
2241  character(len=*), intent(in) :: comment !< A comment to append after [Used|Unused]
2242  type(diag_ctrl), intent(in) :: diag_CS !< The diagnotics control structure
2243  character(len=*), optional, intent(in) :: long_name !< CF long name of diagnostic
2244  character(len=*), optional, intent(in) :: units !< Units for diagnostic
2245  character(len=*), optional, intent(in) :: standard_name !< CF standardized name of diagnostic
2246  ! Local variables
2247  character(len=240) :: mesg
2248 
2249  if (used) then
2250  mesg = '"'//trim(module_name)//'", "'//trim(field_name)//'" [Used]'
2251  else
2252  mesg = '"'//trim(module_name)//'", "'//trim(field_name)//'" [Unused]'
2253  endif
2254  if (len(trim((comment)))>0) then
2255  write(diag_cs%doc_unit, '(a,x,"(",a,")")') trim(mesg),trim(comment)
2256  else
2257  write(diag_cs%doc_unit, '(a)') trim(mesg)
2258  endif
2259  if (present(long_name)) call describe_option("long_name", long_name, diag_cs)
2260  if (present(units)) call describe_option("units", units, diag_cs)
2261  if (present(standard_name)) &
2262  call describe_option("standard_name", standard_name, diag_cs)
2263  if (len(trim((cell_methods_string)))>0) &
2264  call describe_option("cell_methods", trim(cell_methods_string), diag_cs)
2265 
2266 end subroutine log_available_diag
2267 
2268 end module mom_diag_mediator
subroutine, public diag_mediator_infrastructure_init(err_msg)
subroutine post_data_3d(diag_field_id, field, diag_cs, is_static, mask, alt_h)
subroutine describe_option(opt_name, value, diag_CS)
subroutine, public diag_mediator_close_registration(diag_CS)
integer function register_diag_field_expand_axes(module_name, field_name, axes, init_time, long_name, units, missing_value, range, mask_variant, standard_name, verbose, do_not_log, err_msg, interp_method, tile_count)
Returns an FMS id from register_diag_field_fms (the diag_manager routine) after expanding axes (axes-...
subroutine, public set_diag_mediator_grid(G, diag_cs)
integer function, public ocean_register_diag(var_desc, G, diag_CS, day)
Registers a diagnostic using the information encapsulated in the vardesc type argument and returns an...
character(len=15) function i2s(a, n_in)
subroutine initialize_diag_type(diag)
Initializes a diag_type (used after allocating new memory)
This type is used to represent a diagnostic at the diag_mediator level. There can be both &#39;primary&#39; a...
integer function, public register_scalar_field(module_name, field_name, init_time, diag_cs, long_name, units, missing_value, range, standard_name, do_not_log, err_msg, interp_method, cmor_field_name, cmor_long_name, cmor_units, cmor_standard_name)
subroutine, public enable_averaging(time_int_in, time_end_in, diag_cs)
subroutine, public diag_remap_set_active(remap_cs)
Indicate that this remapping type is actually used by the diag manager. If this is never called then ...
subroutine, public diag_remap_init(remap_cs, coord_tuple)
Initialize a diagnostic remapping type with the given vertical coordinate.
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:19
logical function, public diag_remap_axes_configured(remap_cs)
Whether or not the axes for this vertical coordinated has been configured. Configuration is complete ...
The following data type a list of diagnostic fields an their variants, as well as variables that cont...
subroutine post_data_3d_low(diag, field, diag_cs, is_static, mask)
subroutine alloc_diag_with_id(diag_id, diag_cs, diag)
subroutine, public diag_remap_diag_registration_closed(remap_cs)
Inform that all diagnostics have been registered. If _set_active() has not been called on the remappi...
Provides the ocean grid type.
Definition: MOM_grid.F90:2
subroutine add_diag_to_list(diag_cs, dm_id, fms_id, this_diag, axes, module_name, field_name, msg)
Create a diagnostic type and attached to list.
subroutine log_available_diag(used, module_name, field_name, cell_methods_string, comment, diag_CS, long_name, units, standard_name)
Log a diagnostic to the available diagnostics file.
subroutine, public diag_remap_end(remap_cs)
De-init a diagnostic remapping type. Free allocated memory.
subroutine, public vertically_interpolate_diag_field(remap_cs, G, h, staggered_in_x, staggered_in_y, mask, missing_value, field, interpolated_field)
Vertically interpolate diagnostic field to alternative vertical grid.
A simple (very thin) wrapper for register_diag_field to avoid a compiler bug with PGI...
subroutine, public set_axes_info(G, GV, param_file, diag_cs, set_vertical)
Sets up diagnostics axes.
subroutine, public diag_mediator_init(G, nz, param_file, diag_cs, doc_file_dir)
diag_mediator_init initializes the MOM diag_mediator and opens the available diagnostics file...
subroutine, public define_axes_group(diag_cs, handles, axes, nz, vertical_coordinate_number, x_cell_method, y_cell_method, v_cell_method, is_h_point, is_q_point, is_u_point, is_v_point, is_layer, is_interface, is_native, needs_remapping, needs_interpolating, xyave_axes)
Defines a group of "axes" from list of handles.
This module contains I/O framework code.
Definition: MOM_io.F90:2
subroutine, public diag_register_area_ids(diag_cs, id_area_t, id_area_q)
Attaches the id of cell areas to axes groups for use with cell_measures.
subroutine, public diag_mediator_end(time, diag_CS, end_diag_manager)
logical function register_diag_field_expand_cmor(dm_id, module_name, field_name, axes, init_time, long_name, units, missing_value, range, mask_variant, standard_name, verbose, do_not_log, err_msg, interp_method, tile_count, cmor_field_name, cmor_long_name, cmor_units, cmor_standard_name, cell_methods, x_cell_method, y_cell_method, v_cell_method, conversion, v_extensive)
Returns True if either the native of CMOr version of the diagnostic were registered. Updates &#39;dm_id&#39; after calling register_diag_field_expand_axes() for both native and CMOR variants of the field.
subroutine, public diag_remap_get_axes_info(remap_cs, nz, id_layer, id_interface)
Get layer and interface axes ids for this coordinate Needed when defining axes groups.
character(len=len(input_string)) function, public lowercase(input_string)
logical function, public query_averaging_enabled(diag_cs, time_int, time_end)
subroutine, public diag_masks_set(G, nz, missing_value, diag_cs)
diag_masks_set sets up the 2d and 3d masks for diagnostics
subroutine post_data_0d(diag_field_id, field, diag_cs, is_static)
A wrapper for register_diag_field_array()
A group of 1D axes that comprise a 1D/2D/3D mesh.
integer function get_new_diag_id(diag_cs)
Returns a new diagnostic id, it may be necessary to expand the diagnostics array. ...
subroutine post_xy_average(diag_cs, diag, field)
Post the horizontally area-averaged diagnostic.
subroutine attach_cell_methods(id, axes, ostring, cell_methods, x_cell_method, y_cell_method, v_cell_method, v_extensive)
Attaches "cell_methods" attribute to a variable based on defaults for axes_grp or optional arguments...
This type represents remapping of diagnostics to a particular vertical coordinate. There is one of these types for each vertical coordinate. The vertical axes of a diagnostic will reference an instance of this type indicating how (or if) the diagnostic should be vertically remapped when being posted.
logical function, public is_root_pe()
subroutine, public assert(logical_arg, msg)
Issues a FATAL error if the assertion fails, i.e. the first argument is false.
subroutine, public diag_remap_update(remap_cs, G, h, T, S, eqn_of_state)
Build/update target vertical grids for diagnostic remapping.
subroutine post_data_2d_low(diag, field, diag_cs, is_static, mask)
subroutine, public horizontally_average_diag_field(G, h, staggered_in_x, staggered_in_y, is_layer, is_extensive, missing_value, field, averaged_field)
Horizontally average field.
This module is used for runtime remapping of diagnostics to z star, sigma and rho vertical coordinate...
Provides subroutines for quantities specific to the equation of state.
Definition: MOM_EOS.F90:2
subroutine post_data_2d(diag_field_id, field, diag_cs, is_static, mask)
Type for describing a variable, typically a tracer.
Definition: MOM_io.F90:51
integer function, public register_static_field(module_name, field_name, axes, long_name, units, missing_value, range, mask_variant, standard_name, do_not_log, interp_method, tile_count, cmor_field_name, cmor_long_name, cmor_units, cmor_standard_name, area)
Registers a static diagnostic, returning an integer handle.
type(time_type) function, public get_diag_time_end(diag_cs)
subroutine, public post_data_1d_k(diag_field_id, field, diag_cs, is_static)
subroutine, public diag_register_volume_ids(diag_cs, id_vol_t)
Attaches the id of cell volumes to axes groups for use with cell_measures.
subroutine, public diag_remap_do_remap(remap_cs, G, h, staggered_in_x, staggered_in_y, mask, missing_value, field, remapped_field)
Remap diagnostic field to alternative vertical grid.
subroutine, public vertically_reintegrate_diag_field(remap_cs, G, h, staggered_in_x, staggered_in_y, mask, missing_value, field, reintegrated_field)
Vertically re-grid an already vertically-integrated diagnostic field to alternative vertical grid...
subroutine, public disable_averaging(diag_cs)
subroutine, public diag_set_state_ptrs(h, T, S, eqn_of_state, diag_cs)
subroutine, public query_vardesc(vd, name, units, longname, hor_grid, z_grid, t_grid, cmor_field_name, cmor_units, conversion, caller)
This routine queries vardesc.
Definition: MOM_io.F90:664
subroutine set_diag_mask(diag, diag_cs, axes)
Associates the mask pointers within diag with the appropriate mask based on the axes group...
integer function, public register_diag_field(module_name, field_name, axes, init_time, long_name, units, missing_value, range, mask_variant, standard_name, verbose, do_not_log, err_msg, interp_method, tile_count, cmor_field_name, cmor_long_name, cmor_units, cmor_standard_name, cell_methods, x_cell_method, y_cell_method, v_cell_method, conversion, v_extensive)
Returns the "diag_mediator" handle for a group (native, CMOR, z-coord, ...) of diagnostics derived fr...
subroutine, public mom_error(level, message, all_print)
subroutine, public diag_update_remap_grids(diag_cs, alt_h)
Build/update vertical grids for diagnostic remapping.
subroutine, public diag_remap_configure_axes(remap_cs, GV, param_file)
Configure the vertical axes for a diagnostic remapping control structure. Reads a configuration param...
A control structure for the equation of state.
Definition: MOM_EOS.F90:55