MOM6
MOM_diagnostics.F90
1 !> Calculates any requested diagnostic quantities
2 !! that are not calculated in the various subroutines.
3 !! Diagnostic quantities are requested by allocating them memory.
5 
6 ! This file is part of MOM6. See LICENSE.md for the license.
7 
8 use mom_coms, only : reproducing_sum
9 use mom_diag_mediator, only : post_data, get_diag_time_end
10 use mom_diag_mediator, only : register_diag_field, register_scalar_field
11 use mom_diag_mediator, only : register_static_field, diag_register_area_ids
12 use mom_diag_mediator, only : diag_ctrl, time_type, safe_alloc_ptr
13 use mom_diag_mediator, only : diag_get_volume_cell_measure_dm_id
15 use mom_diag_mediator, only : diag_save_grids, diag_restore_grids, diag_copy_storage_to_diag
16 use mom_domains, only : create_group_pass, do_group_pass, group_pass_type
17 use mom_domains, only : to_north, to_east
18 use mom_eos, only : calculate_density, int_density_dz
19 use mom_eos, only : gsw_sp_from_sr, gsw_pt_from_ct
20 use mom_error_handler, only : mom_error, fatal, warning
22 use mom_grid, only : ocean_grid_type
24 use mom_spatial_means, only : global_area_mean, global_layer_mean
25 use mom_spatial_means, only : global_volume_mean, global_area_integral
26 use mom_tracer_registry, only : tracer_registry_type, post_tracer_transport_diagnostics
30 use mom_verticalgrid, only : verticalgrid_type, get_thickness_units
31 use mom_wave_speed, only : wave_speed, wave_speed_cs, wave_speed_init
32 use coupler_types_mod, only : coupler_type_send_data
33 
34 implicit none ; private
35 
36 #include <MOM_memory.h>
37 
38 public calculate_diagnostic_fields, register_time_deriv, write_static_fields
39 public find_eta
40 public register_surface_diags, post_surface_dyn_diags, post_surface_thermo_diags
41 public register_transport_diags, post_transport_diagnostics
42 public mom_diagnostics_init, mom_diagnostics_end
43 
44 ! A note on unit descriptions in comments: MOM6 uses units that can be rescaled for dimensional
45 ! consistency testing. These are noted in comments with units like Z, H, L, and T, along with
46 ! their mks counterparts with notation like "a velocity [Z T-1 ~> m s-1]". If the units
47 ! vary with the Boussinesq approximation, the Boussinesq variant is given first.
48 
49 !> The control structure for the MOM_diagnostics module
50 type, public :: diagnostics_cs ; private
51  real :: mono_n2_column_fraction = 0. !< The lower fraction of water column over which N2 is limited as
52  !! monotonic for the purposes of calculating the equivalent
53  !! barotropic wave speed.
54  real :: mono_n2_depth = -1. !< The depth below which N2 is limited as monotonic for the purposes of
55  !! calculating the equivalent barotropic wave speed [m].
56 
57  type(diag_ctrl), pointer :: diag => null() !< A structure that is used to
58  !! regulate the timing of diagnostic output.
59 
60  ! following arrays store diagnostics calculated here and unavailable outside.
61 
62  ! following fields have nz+1 levels.
63  real, pointer, dimension(:,:,:) :: &
64  e => null(), & !< interface height [Z ~> m]
65  e_d => null() !< interface height above bottom [Z ~> m]
66 
67  ! following fields have nz layers.
68  real, pointer, dimension(:,:,:) :: &
69  du_dt => null(), & !< net i-acceleration [m s-2]
70  dv_dt => null(), & !< net j-acceleration [m s-2]
71  dh_dt => null(), & !< thickness rate of change [H s-1 ~> m s-1 or kg m-2 s-1]
72  p_ebt => null() !< Equivalent barotropic modal structure [nondim]
73 
74  real, pointer, dimension(:,:,:) :: h_rlay => null() !< Layer thicknesses in potential density
75  !! coordinates [H ~> m or kg m-2]
76  real, pointer, dimension(:,:,:) :: uh_rlay => null() !< Zonal transports in potential density
77  !! coordinates [H m2 s-1 ~> m3 s-1 or kg s-1]
78  real, pointer, dimension(:,:,:) :: vh_rlay => null() !< Meridional transports in potential density
79  !! coordinates [H m2 s-1 ~> m3 s-1 or kg s-1]
80  real, pointer, dimension(:,:,:) :: uhgm_rlay => null() !< Zonal Gent-McWilliams transports in potential density
81  !! coordinates [H m2 s-1 ~> m3 s-1 or kg s-1]
82  real, pointer, dimension(:,:,:) :: vhgm_rlay => null() !< Meridional Gent-McWilliams transports in potential density
83  !! coordinates [H m2 s-1 ~> m3 s-1 or kg s-1]
84 
85  ! following fields are 2-D.
86  real, pointer, dimension(:,:) :: &
87  cg1 => null(), & !< First baroclinic gravity wave speed [m s-1]
88  rd1 => null(), & !< First baroclinic deformation radius [m]
89  cfl_cg1 => null(), & !< CFL for first baroclinic gravity wave speed, nondim
90  cfl_cg1_x => null(), & !< i-component of CFL for first baroclinic gravity wave speed, nondim
91  cfl_cg1_y => null() !< j-component of CFL for first baroclinic gravity wave speed, nondim
92 
93  ! The following arrays hold diagnostics in the layer-integrated energy budget.
94  real, pointer, dimension(:,:,:) :: &
95  ke => null(), & !< KE per unit mass [m2 s-2]
96  dke_dt => null(), & !< time derivative of the layer KE [m3 s-3]
97  pe_to_ke => null(), & !< potential energy to KE term [m3 s-3]
98  ke_coradv => null(), & !< KE source from the combined Coriolis and advection terms [m3 s-3].
99  !! The Coriolis source should be zero, but is not due to truncation
100  !! errors. There should be near-cancellation of the global integral
101  !! of this spurious Coriolis source.
102  ke_adv => null(), & !< KE source from along-layer advection [m3 s-3]
103  ke_visc => null(), & !< KE source from vertical viscosity [m3 s-3]
104  ke_horvisc => null(), & !< KE source from horizontal viscosity [m3 s-3]
105  ke_dia => null() !< KE source from diapycnal diffusion [m3 s-3]
106 
107  !>@{ Diagnostic IDs
108  integer :: id_u = -1, id_v = -1, id_h = -1
109  integer :: id_e = -1, id_e_d = -1
110  integer :: id_du_dt = -1, id_dv_dt = -1
111  integer :: id_col_ht = -1, id_dh_dt = -1
112  integer :: id_ke = -1, id_dkedt = -1
113  integer :: id_pe_to_ke = -1, id_ke_coradv = -1
114  integer :: id_ke_adv = -1, id_ke_visc = -1
115  integer :: id_ke_horvisc = -1, id_ke_dia = -1
116  integer :: id_uh_rlay = -1, id_vh_rlay = -1
117  integer :: id_uhgm_rlay = -1, id_vhgm_rlay = -1
118  integer :: id_h_rlay = -1, id_rd1 = -1
119  integer :: id_rml = -1, id_rcv = -1
120  integer :: id_cg1 = -1, id_cfl_cg1 = -1
121  integer :: id_cfl_cg1_x = -1, id_cfl_cg1_y = -1
122  integer :: id_cg_ebt = -1, id_rd_ebt = -1
123  integer :: id_p_ebt = -1
124  integer :: id_temp_int = -1, id_salt_int = -1
125  integer :: id_mass_wt = -1, id_col_mass = -1
126  integer :: id_masscello = -1, id_masso = -1
127  integer :: id_volcello = -1
128  integer :: id_tpot = -1, id_sprac = -1
129  integer :: id_tob = -1, id_sob = -1
130  integer :: id_thetaoga = -1, id_soga = -1
131  integer :: id_sosga = -1, id_tosga = -1
132  integer :: id_temp_layer_ave = -1, id_salt_layer_ave = -1
133  integer :: id_pbo = -1
134  integer :: id_thkcello = -1, id_rhoinsitu = -1
135  integer :: id_rhopot0 = -1, id_rhopot2 = -1
136  integer :: id_h_pre_sync = -1 !!@}
137  !> The control structure for calculating wave speed.
138  type(wave_speed_cs), pointer :: wave_speed_csp => null()
139 
140  type(p3d) :: var_ptr(max_fields_) !< pointers to variables used in the calculation
141  !! of time derivatives
142  type(p3d) :: deriv(max_fields_) !< Time derivatives of various fields
143  type(p3d) :: prev_val(max_fields_) !< Previous values of variables used in the calculation
144  !! of time derivatives
145  !< previous values of variables used in calculation of time derivatives
146  integer :: nlay(max_fields_) !< The number of layers in each diagnostics
147  integer :: num_time_deriv = 0 !< The number of time derivative diagnostics
148 
149  type(group_pass_type) :: pass_ke_uv !< A handle used for group halo passes
150 
151 end type diagnostics_cs
152 
153 
154 !> A structure with diagnostic IDs of the surface and integrated variables
155 type, public :: surface_diag_ids ; private
156  !>@{ Diagnostic IDs for 2-d surface and bottom flux and state fields
157  !Diagnostic IDs for 2-d surface and bottom fields
158  integer :: id_zos = -1, id_zossq = -1
159  integer :: id_volo = -1, id_speed = -1
160  integer :: id_ssh = -1, id_ssh_ga = -1
161  integer :: id_sst = -1, id_sst_sq = -1, id_sstcon = -1
162  integer :: id_sss = -1, id_sss_sq = -1, id_sssabs = -1
163  integer :: id_ssu = -1, id_ssv = -1
164 
165  ! Diagnostic IDs for heat and salt flux fields
166  integer :: id_fraz = -1
167  integer :: id_salt_deficit = -1
168  integer :: id_heat_pme = -1
169  integer :: id_intern_heat = -1
170  !!@}
171 end type surface_diag_ids
172 
173 
174 !> A structure with diagnostic IDs of mass transport related diagnostics
175 type, public :: transport_diag_ids ; private
176  !>@{ Diagnostics for tracer horizontal transport
177  integer :: id_uhtr = -1, id_umo = -1, id_umo_2d = -1
178  integer :: id_vhtr = -1, id_vmo = -1, id_vmo_2d = -1
179  integer :: id_dynamics_h = -1, id_dynamics_h_tendency = -1 !!@}
180 end type transport_diag_ids
181 
182 
183 contains
184 !> Diagnostics not more naturally calculated elsewhere are computed here.
185 subroutine calculate_diagnostic_fields(u, v, h, uh, vh, tv, ADp, CDp, p_surf, &
186  dt, diag_pre_sync, G, GV, US, CS, eta_bt)
187  type(ocean_grid_type), intent(inout) :: g !< The ocean's grid structure.
188  type(verticalgrid_type), intent(in) :: gv !< The ocean's vertical grid structure.
189  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
190  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), &
191  intent(in) :: u !< The zonal velocity [m s-1].
192  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), &
193  intent(in) :: v !< The meridional velocity [m s-1].
194  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), &
195  intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2].
196  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), &
197  intent(in) :: uh !< Transport through zonal faces = u*h*dy,
198  !! [H m2 s-1 ~> m3 s-1 or kg s-1].
199  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), &
200  intent(in) :: vh !< Transport through meridional faces = v*h*dx,
201  !! [H m2 s-1 ~> m3 s-1 or kg s-1].
202  type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various
203  !! thermodynamic variables.
204  type(accel_diag_ptrs), intent(in) :: adp !< structure with pointers to
205  !! accelerations in momentum equation.
206  type(cont_diag_ptrs), intent(in) :: cdp !< structure with pointers to
207  !! terms in continuity equation.
208  real, dimension(:,:), pointer :: p_surf !< A pointer to the surface pressure [Pa].
209  !! If p_surf is not associated, it is the same
210  !! as setting the surface pressure to 0.
211  real, intent(in) :: dt !< The time difference since the last
212  !! call to this subroutine [s].
213  type(diag_grid_storage), intent(in) :: diag_pre_sync !< Target grids from previous timestep
214  type(diagnostics_cs), intent(inout) :: cs !< Control structure returned by a
215  !! previous call to diagnostics_init.
216  real, dimension(SZI_(G),SZJ_(G)), &
217  optional, intent(in) :: eta_bt !< An optional barotropic
218  !! variable that gives the "correct" free surface height (Boussinesq) or total water column
219  !! mass per unit area (non-Boussinesq). This is used to dilate the layer thicknesses when
220  !! calculating interface heights [H ~> m or kg m-2].
221  ! Local variables
222  integer i, j, k, is, ie, js, je, isq, ieq, jsq, jeq, nz, nkmb
223 
224  ! coordinate variable potential density [kg m-3].
225  real :: rcv(szi_(g),szj_(g),szk_(g))
226  ! Two temporary work arrays
227  real :: work_3d(szi_(g),szj_(g),szk_(g))
228  real :: work_2d(szi_(g),szj_(g))
229 
230  ! tmp array for surface properties
231  real :: surface_field(szi_(g),szj_(g))
232  real :: pressure_1d(szi_(g)) ! Temporary array for pressure when calling EOS
233  real :: wt, wt_p
234 
235  ! squared Coriolis parameter at to h-points [s-2]
236  real :: f2_h
237 
238  ! magnitude of the gradient of f [s-1 m-1]
239  real :: mag_beta
240 
241  ! frequency squared used to avoid division by 0 [s-2]
242  ! value is roughly (pi / (the age of the universe) )^2.
243  real, parameter :: absurdly_small_freq2 = 1e-34
244 
245  integer :: k_list
246 
247  real, dimension(SZK_(G)) :: temp_layer_ave, salt_layer_ave
248  real :: thetaoga, soga, masso, tosga, sosga
249 
250  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
251  isq = g%IscB ; ieq = g%IecB ; jsq = g%JscB ; jeq = g%JecB
252  nz = g%ke ; nkmb = gv%nk_rho_varies
253 
254  if (dt > 0.0) then
255  call diag_save_grids(cs%diag)
256  call diag_copy_storage_to_diag(cs%diag, diag_pre_sync)
257 
258  if (cs%id_h_pre_sync > 0) &
259  call post_data(cs%id_h_pre_sync, diag_pre_sync%h_state, cs%diag, alt_h = diag_pre_sync%h_state)
260 
261  if (cs%id_du_dt>0) call post_data(cs%id_du_dt, cs%du_dt, cs%diag, alt_h = diag_pre_sync%h_state)
262 
263  if (cs%id_dv_dt>0) call post_data(cs%id_dv_dt, cs%dv_dt, cs%diag, alt_h = diag_pre_sync%h_state)
264 
265  if (cs%id_dh_dt>0) call post_data(cs%id_dh_dt, cs%dh_dt, cs%diag, alt_h = diag_pre_sync%h_state)
266 
267  call diag_restore_grids(cs%diag)
268 
269  call calculate_energy_diagnostics(u, v, h, uh, vh, adp, cdp, g, gv, us, cs)
270  endif
271 
272  ! smg: is the following robust to ALE? It seems a bit opaque.
273  ! If the model is NOT in isopycnal mode then nkmb=0. But we need all the
274  ! following diagnostics to treat all layers as variable density, so we set
275  ! nkmb = nz, on the expectation that loops nkmb+1,nz will not iterate.
276  ! This behavior is ANSI F77 but some compiler options can force at least
277  ! one iteration that would break the following one-line workaround!
278  if (nkmb==0 .and. nz > 1) nkmb = nz
279 
280  if (loc(cs)==0) call mom_error(fatal, &
281  "calculate_diagnostic_fields: Module must be initialized before used.")
282 
283  call calculate_derivs(dt, g, cs)
284 
285  if (cs%id_u > 0) call post_data(cs%id_u, u, cs%diag)
286 
287  if (cs%id_v > 0) call post_data(cs%id_v, v, cs%diag)
288 
289  if (cs%id_h > 0) call post_data(cs%id_h, h, cs%diag)
290 
291  if (associated(cs%e)) then
292  call find_eta(h, tv, g, gv, us, cs%e, eta_bt)
293  if (cs%id_e > 0) call post_data(cs%id_e, cs%e, cs%diag)
294  endif
295 
296  if (associated(cs%e_D)) then
297  if (associated(cs%e)) then
298  do k=1,nz+1 ; do j=js,je ; do i=is,ie
299  cs%e_D(i,j,k) = cs%e(i,j,k) + g%bathyT(i,j)
300  enddo ; enddo ; enddo
301  else
302  call find_eta(h, tv, g, gv, us, cs%e_D, eta_bt)
303  do k=1,nz+1 ; do j=js,je ; do i=is,ie
304  cs%e_D(i,j,k) = cs%e_D(i,j,k) + g%bathyT(i,j)
305  enddo ; enddo ; enddo
306  endif
307 
308  if (cs%id_e_D > 0) call post_data(cs%id_e_D, cs%e_D, cs%diag)
309  endif
310 
311  ! mass per area of grid cell (for Bouss, use Rho0)
312  if (cs%id_masscello > 0) then
313  do k=1,nz; do j=js,je ; do i=is,ie
314  work_3d(i,j,k) = gv%H_to_kg_m2*h(i,j,k)
315  enddo ; enddo ; enddo
316  call post_data(cs%id_masscello, work_3d, cs%diag)
317  endif
318 
319  ! mass of liquid ocean (for Bouss, use Rho0)
320  if (cs%id_masso > 0) then
321  work_2d(:,:) = 0.0
322  do k=1,nz ; do j=js,je ; do i=is,ie
323  work_2d(i,j) = work_2d(i,j) + (gv%H_to_kg_m2*h(i,j,k)) * g%areaT(i,j)
324  enddo ; enddo ; enddo
325  masso = reproducing_sum(work_2d)
326  call post_data(cs%id_masso, masso, cs%diag)
327  endif
328 
329  ! diagnose thickness/volumes of grid cells [m]
330  if (cs%id_thkcello>0 .or. cs%id_volcello>0) then
331  if (gv%Boussinesq) then ! thkcello = h for Boussinesq
332  if (cs%id_thkcello > 0) then ; if (gv%H_to_m == 1.0) then
333  call post_data(cs%id_thkcello, h, cs%diag)
334  else
335  do k=1,nz; do j=js,je ; do i=is,ie
336  work_3d(i,j,k) = gv%H_to_m*h(i,j,k)
337  enddo ; enddo ; enddo
338  call post_data(cs%id_thkcello, work_3d, cs%diag)
339  endif ; endif
340  if (cs%id_volcello > 0) then ! volcello = h*area for Boussinesq
341  do k=1,nz; do j=js,je ; do i=is,ie
342  work_3d(i,j,k) = ( gv%H_to_m*h(i,j,k) ) * g%areaT(i,j)
343  enddo ; enddo ; enddo
344  call post_data(cs%id_volcello, work_3d, cs%diag)
345  endif
346  else ! thkcello = dp/(rho*g) for non-Boussinesq
347  do j=js,je
348  if (associated(p_surf)) then ! Pressure loading at top of surface layer [Pa]
349  do i=is,ie
350  pressure_1d(i) = p_surf(i,j)
351  enddo
352  else
353  do i=is,ie
354  pressure_1d(i) = 0.0
355  enddo
356  endif
357  do k=1,nz ! Integrate vertically downward for pressure
358  do i=is,ie ! Pressure for EOS at the layer center [Pa]
359  pressure_1d(i) = pressure_1d(i) + 0.5*gv%H_to_Pa*h(i,j,k)
360  enddo
361  ! Store in-situ density [kg m-3] in work_3d
362  call calculate_density(tv%T(:,j,k),tv%S(:,j,k), pressure_1d, &
363  work_3d(:,j,k), is, ie-is+1, tv%eqn_of_state)
364  do i=is,ie ! Cell thickness = dz = dp/(g*rho) (meter); store in work_3d
365  work_3d(i,j,k) = (gv%H_to_kg_m2*h(i,j,k)) / work_3d(i,j,k)
366  enddo
367  do i=is,ie ! Pressure for EOS at the bottom interface [Pa]
368  pressure_1d(i) = pressure_1d(i) + 0.5*gv%H_to_Pa*h(i,j,k)
369  enddo
370  enddo ! k
371  enddo ! j
372  if (cs%id_thkcello > 0) call post_data(cs%id_thkcello, work_3d, cs%diag)
373  if (cs%id_volcello > 0) then
374  do k=1,nz; do j=js,je ; do i=is,ie ! volcello = dp/(rho*g)*area for non-Boussinesq
375  work_3d(i,j,k) = g%areaT(i,j) * work_3d(i,j,k)
376  enddo ; enddo ; enddo
377  call post_data(cs%id_volcello, work_3d, cs%diag)
378  endif
379  endif
380  endif
381 
382  ! Calculate additional, potentially derived temperature diagnostics
383  if (tv%T_is_conT) then
384  ! Internal T&S variables are conservative temperature & absolute salinity,
385  ! so they need to converted to potential temperature and practical salinity
386  ! for some diagnostics using TEOS-10 function calls.
387  if ((cs%id_Tpot > 0) .or. (cs%id_tob > 0)) then
388  do k=1,nz ; do j=js,je ; do i=is,ie
389  work_3d(i,j,k) = gsw_pt_from_ct(tv%S(i,j,k),tv%T(i,j,k))
390  enddo ; enddo ; enddo
391  if (cs%id_Tpot > 0) call post_data(cs%id_Tpot, work_3d, cs%diag)
392  if (cs%id_tob > 0) call post_data(cs%id_tob, work_3d(:,:,nz), cs%diag, mask=g%mask2dT)
393  endif
394  else
395  ! Internal T&S variables are potential temperature & practical salinity
396  if (cs%id_tob > 0) call post_data(cs%id_tob, tv%T(:,:,nz), cs%diag, mask=g%mask2dT)
397  endif
398 
399  ! Calculate additional, potentially derived salinity diagnostics
400  if (tv%S_is_absS) then
401  ! Internal T&S variables are conservative temperature & absolute salinity,
402  ! so they need to converted to potential temperature and practical salinity
403  ! for some diagnostics using TEOS-10 function calls.
404  if ((cs%id_Sprac > 0) .or. (cs%id_sob > 0)) then
405  do k=1,nz ; do j=js,je ; do i=is,ie
406  work_3d(i,j,k) = gsw_sp_from_sr(tv%S(i,j,k))
407  enddo ; enddo ; enddo
408  if (cs%id_Sprac > 0) call post_data(cs%id_Sprac, work_3d, cs%diag)
409  if (cs%id_sob > 0) call post_data(cs%id_sob, work_3d(:,:,nz), cs%diag, mask=g%mask2dT)
410  endif
411  else
412  ! Internal T&S variables are potential temperature & practical salinity
413  if (cs%id_sob > 0) call post_data(cs%id_sob, tv%S(:,:,nz), cs%diag, mask=g%mask2dT)
414  endif
415 
416  ! volume mean potential temperature
417  if (cs%id_thetaoga>0) then
418  thetaoga = global_volume_mean(tv%T, h, g, gv)
419  call post_data(cs%id_thetaoga, thetaoga, cs%diag)
420  endif
421 
422  ! area mean SST
423  if (cs%id_tosga > 0) then
424  do j=js,je ; do i=is,ie
425  surface_field(i,j) = tv%T(i,j,1)
426  enddo ; enddo
427  tosga = global_area_mean(surface_field, g)
428  call post_data(cs%id_tosga, tosga, cs%diag)
429  endif
430 
431  ! volume mean salinity
432  if (cs%id_soga>0) then
433  soga = global_volume_mean(tv%S, h, g, gv)
434  call post_data(cs%id_soga, soga, cs%diag)
435  endif
436 
437  ! area mean SSS
438  if (cs%id_sosga > 0) then
439  do j=js,je ; do i=is,ie
440  surface_field(i,j) = tv%S(i,j,1)
441  enddo ; enddo
442  sosga = global_area_mean(surface_field, g)
443  call post_data(cs%id_sosga, sosga, cs%diag)
444  endif
445 
446  ! layer mean potential temperature
447  if (cs%id_temp_layer_ave>0) then
448  temp_layer_ave = global_layer_mean(tv%T, h, g, gv)
449  call post_data(cs%id_temp_layer_ave, temp_layer_ave, cs%diag)
450  endif
451 
452  ! layer mean salinity
453  if (cs%id_salt_layer_ave>0) then
454  salt_layer_ave = global_layer_mean(tv%S, h, g, gv)
455  call post_data(cs%id_salt_layer_ave, salt_layer_ave, cs%diag)
456  endif
457 
458  call calculate_vertical_integrals(h, tv, p_surf, g, gv, us, cs)
459 
460  if ((cs%id_Rml > 0) .or. (cs%id_Rcv > 0) .or. associated(cs%h_Rlay) .or. &
461  associated(cs%uh_Rlay) .or. associated(cs%vh_Rlay) .or. &
462  associated(cs%uhGM_Rlay) .or. associated(cs%vhGM_Rlay)) then
463 
464  if (associated(tv%eqn_of_state)) then
465  pressure_1d(:) = tv%P_Ref
466 !$OMP parallel do default(none) shared(tv,Rcv,is,ie,js,je,nz,pressure_1d)
467  do k=1,nz ; do j=js-1,je+1
468  call calculate_density(tv%T(:,j,k), tv%S(:,j,k), pressure_1d, &
469  rcv(:,j,k), is-1, ie-is+3, tv%eqn_of_state)
470  enddo ; enddo
471  else ! Rcv should not be used much in this case, so fill in sensible values.
472  do k=1,nz ; do j=js-1,je+1 ; do i=is-1,ie+1
473  rcv(i,j,k) = gv%Rlay(k)
474  enddo ; enddo ; enddo
475  endif
476  if (cs%id_Rml > 0) call post_data(cs%id_Rml, rcv, cs%diag)
477  if (cs%id_Rcv > 0) call post_data(cs%id_Rcv, rcv, cs%diag)
478 
479  if (associated(cs%h_Rlay)) then
480  k_list = nz/2
481 !$OMP parallel do default(none) shared(is,ie,js,je,nz,nkmb,CS,Rcv,h,GV) &
482 !$OMP private(wt,wt_p) firstprivate(k_list)
483  do j=js,je
484  do k=1,nkmb ; do i=is,ie
485  cs%h_Rlay(i,j,k) = 0.0
486  enddo ; enddo
487  do k=nkmb+1,nz ; do i=is,ie
488  cs%h_Rlay(i,j,k) = h(i,j,k)
489  enddo ; enddo
490  do k=1,nkmb ; do i=is,ie
491  call find_weights(gv%Rlay, rcv(i,j,k), k_list, nz, wt, wt_p)
492  cs%h_Rlay(i,j,k_list) = cs%h_Rlay(i,j,k_list) + h(i,j,k)*wt
493  cs%h_Rlay(i,j,k_list+1) = cs%h_Rlay(i,j,k_list+1) + h(i,j,k)*wt_p
494  enddo ; enddo
495  enddo
496 
497  if (cs%id_h_Rlay > 0) call post_data(cs%id_h_Rlay, cs%h_Rlay, cs%diag)
498  endif
499 
500  if (associated(cs%uh_Rlay)) then
501  k_list = nz/2
502 !$OMP parallel do default(none) shared(Isq,Ieq,js,je,nz,nkmb,Rcv,CS,GV,uh) &
503 !$OMP private(wt,wt_p) firstprivate(k_list)
504  do j=js,je
505  do k=1,nkmb ; do i=isq,ieq
506  cs%uh_Rlay(i,j,k) = 0.0
507  enddo ; enddo
508  do k=nkmb+1,nz ; do i=isq,ieq
509  cs%uh_Rlay(i,j,k) = uh(i,j,k)
510  enddo ; enddo
511  k_list = nz/2
512  do k=1,nkmb ; do i=isq,ieq
513  call find_weights(gv%Rlay, 0.5*(rcv(i,j,k)+rcv(i+1,j,k)), k_list, nz, wt, wt_p)
514  cs%uh_Rlay(i,j,k_list) = cs%uh_Rlay(i,j,k_list) + uh(i,j,k)*wt
515  cs%uh_Rlay(i,j,k_list+1) = cs%uh_Rlay(i,j,k_list+1) + uh(i,j,k)*wt_p
516  enddo ; enddo
517  enddo
518 
519  if (cs%id_uh_Rlay > 0) call post_data(cs%id_uh_Rlay, cs%uh_Rlay, cs%diag)
520  endif
521 
522  if (associated(cs%vh_Rlay)) then
523  k_list = nz/2
524 !$OMP parallel do default(none) shared(Jsq,Jeq,is,ie,nz,nkmb,Rcv,CS,GV,vh) &
525 !$OMP private(wt,wt_p) firstprivate(k_list)
526  do j=jsq,jeq
527  do k=1,nkmb ; do i=is,ie
528  cs%vh_Rlay(i,j,k) = 0.0
529  enddo ; enddo
530  do k=nkmb+1,nz ; do i=is,ie
531  cs%vh_Rlay(i,j,k) = vh(i,j,k)
532  enddo ; enddo
533  do k=1,nkmb ; do i=is,ie
534  call find_weights(gv%Rlay, 0.5*(rcv(i,j,k)+rcv(i,j+1,k)), k_list, nz, wt, wt_p)
535  cs%vh_Rlay(i,j,k_list) = cs%vh_Rlay(i,j,k_list) + vh(i,j,k)*wt
536  cs%vh_Rlay(i,j,k_list+1) = cs%vh_Rlay(i,j,k_list+1) + vh(i,j,k)*wt_p
537  enddo ; enddo
538  enddo
539 
540  if (cs%id_vh_Rlay > 0) call post_data(cs%id_vh_Rlay, cs%vh_Rlay, cs%diag)
541  endif
542 
543  if (associated(cs%uhGM_Rlay) .and. associated(cdp%uhGM)) then
544  k_list = nz/2
545 !$OMP parallel do default(none) shared(Isq,Ieq,js,je,nz,nkmb,Rcv,CDP,CS,GV) &
546 !$OMP private(wt,wt_p) firstprivate(k_list)
547  do j=js,je
548  do k=1,nkmb ; do i=isq,ieq
549  cs%uhGM_Rlay(i,j,k) = 0.0
550  enddo ; enddo
551  do k=nkmb+1,nz ; do i=isq,ieq
552  cs%uhGM_Rlay(i,j,k) = cdp%uhGM(i,j,k)
553  enddo ; enddo
554  do k=1,nkmb ; do i=isq,ieq
555  call find_weights(gv%Rlay, 0.5*(rcv(i,j,k)+rcv(i+1,j,k)), k_list, nz, wt, wt_p)
556  cs%uhGM_Rlay(i,j,k_list) = cs%uhGM_Rlay(i,j,k_list) + cdp%uhGM(i,j,k)*wt
557  cs%uhGM_Rlay(i,j,k_list+1) = cs%uhGM_Rlay(i,j,k_list+1) + cdp%uhGM(i,j,k)*wt_p
558  enddo ; enddo
559  enddo
560 
561  if (cs%id_uh_Rlay > 0) call post_data(cs%id_uhGM_Rlay, cs%uhGM_Rlay, cs%diag)
562  endif
563 
564  if (associated(cs%vhGM_Rlay) .and. associated(cdp%vhGM)) then
565  k_list = nz/2
566 !$OMP parallel do default(none) shared(is,ie,Jsq,Jeq,nz,nkmb,CS,CDp,Rcv,GV) &
567 !$OMP private(wt,wt_p) firstprivate(k_list)
568  do j=jsq,jeq
569  do k=1,nkmb ; do i=is,ie
570  cs%vhGM_Rlay(i,j,k) = 0.0
571  enddo ; enddo
572  do k=nkmb+1,nz ; do i=is,ie
573  cs%vhGM_Rlay(i,j,k) = cdp%vhGM(i,j,k)
574  enddo ; enddo
575  do k=1,nkmb ; do i=is,ie
576  call find_weights(gv%Rlay, 0.5*(rcv(i,j,k)+rcv(i,j+1,k)), k_list, nz, wt, wt_p)
577  cs%vhGM_Rlay(i,j,k_list) = cs%vhGM_Rlay(i,j,k_list) + cdp%vhGM(i,j,k)*wt
578  cs%vhGM_Rlay(i,j,k_list+1) = cs%vhGM_Rlay(i,j,k_list+1) + cdp%vhGM(i,j,k)*wt_p
579  enddo ; enddo
580  enddo
581 
582  if (cs%id_vhGM_Rlay > 0) call post_data(cs%id_vhGM_Rlay, cs%vhGM_Rlay, cs%diag)
583  endif
584  endif
585 
586  if (associated(tv%eqn_of_state)) then
587  if (cs%id_rhopot0 > 0) then
588  pressure_1d(:) = 0.
589 !$OMP parallel do default(none) shared(tv,Rcv,is,ie,js,je,nz,pressure_1d)
590  do k=1,nz ; do j=js,je
591  call calculate_density(tv%T(:,j,k),tv%S(:,j,k),pressure_1d, &
592  rcv(:,j,k),is,ie-is+1, tv%eqn_of_state)
593  enddo ; enddo
594  if (cs%id_rhopot0 > 0) call post_data(cs%id_rhopot0, rcv, cs%diag)
595  endif
596  if (cs%id_rhopot2 > 0) then
597  pressure_1d(:) = 2.e7 ! 2000 dbars
598 !$OMP parallel do default(none) shared(tv,Rcv,is,ie,js,je,nz,pressure_1d)
599  do k=1,nz ; do j=js,je
600  call calculate_density(tv%T(:,j,k),tv%S(:,j,k),pressure_1d, &
601  rcv(:,j,k),is,ie-is+1, tv%eqn_of_state)
602  enddo ; enddo
603  if (cs%id_rhopot2 > 0) call post_data(cs%id_rhopot2, rcv, cs%diag)
604  endif
605  if (cs%id_rhoinsitu > 0) then
606 !$OMP parallel do default(none) shared(tv,Rcv,is,ie,js,je,nz,pressure_1d,h,GV)
607  do j=js,je
608  pressure_1d(:) = 0. ! Start at p=0 Pa at surface
609  do k=1,nz
610  pressure_1d(:) = pressure_1d(:) + 0.5 * h(:,j,k) * gv%H_to_Pa ! Pressure in middle of layer k
611  call calculate_density(tv%T(:,j,k),tv%S(:,j,k),pressure_1d, &
612  rcv(:,j,k),is,ie-is+1, tv%eqn_of_state)
613  pressure_1d(:) = pressure_1d(:) + 0.5 * h(:,j,k) * gv%H_to_Pa ! Pressure at bottom of layer k
614  enddo
615  enddo
616  if (cs%id_rhoinsitu > 0) call post_data(cs%id_rhoinsitu, rcv, cs%diag)
617  endif
618  endif
619 
620  if ((cs%id_cg1>0) .or. (cs%id_Rd1>0) .or. (cs%id_cfl_cg1>0) .or. &
621  (cs%id_cfl_cg1_x>0) .or. (cs%id_cfl_cg1_y>0)) then
622  call wave_speed(h, tv, g, gv, us, cs%cg1, cs%wave_speed_CSp)
623  if (cs%id_cg1>0) call post_data(cs%id_cg1, cs%cg1, cs%diag)
624  if (cs%id_Rd1>0) then
625 !$OMP parallel do default(none) shared(is,ie,js,je,G,CS) &
626 !$OMP private(f2_h,mag_beta)
627  do j=js,je ; do i=is,ie
628  ! Blend the equatorial deformation radius with the standard one.
629  f2_h = absurdly_small_freq2 + 0.25 * us%s_to_T**2 * &
630  ((g%CoriolisBu(i,j)**2 + g%CoriolisBu(i-1,j-1)**2) + &
631  (g%CoriolisBu(i-1,j)**2 + g%CoriolisBu(i,j-1)**2))
632  mag_beta = sqrt(0.5 * us%s_to_T**2 * ( &
633  (((g%CoriolisBu(i,j)-g%CoriolisBu(i-1,j)) * g%IdxCv(i,j))**2 + &
634  ((g%CoriolisBu(i,j-1)-g%CoriolisBu(i-1,j-1)) * g%IdxCv(i,j-1))**2) + &
635  (((g%CoriolisBu(i,j)-g%CoriolisBu(i,j-1)) * g%IdyCu(i,j))**2 + &
636  ((g%CoriolisBu(i-1,j)-g%CoriolisBu(i-1,j-1)) * g%IdyCu(i-1,j))**2) ))
637  cs%Rd1(i,j) = cs%cg1(i,j) / sqrt(f2_h + cs%cg1(i,j) * mag_beta)
638 
639  enddo ; enddo
640  call post_data(cs%id_Rd1, cs%Rd1, cs%diag)
641  endif
642  if (cs%id_cfl_cg1>0) then
643  do j=js,je ; do i=is,ie
644  cs%cfl_cg1(i,j) = (dt*cs%cg1(i,j)) * (g%IdxT(i,j) + g%IdyT(i,j))
645  enddo ; enddo
646  call post_data(cs%id_cfl_cg1, cs%cfl_cg1, cs%diag)
647  endif
648  if (cs%id_cfl_cg1_x>0) then
649  do j=js,je ; do i=is,ie
650  cs%cfl_cg1_x(i,j) = (dt*cs%cg1(i,j)) * g%IdxT(i,j)
651  enddo ; enddo
652  call post_data(cs%id_cfl_cg1_x, cs%cfl_cg1_x, cs%diag)
653  endif
654  if (cs%id_cfl_cg1_y>0) then
655  do j=js,je ; do i=is,ie
656  cs%cfl_cg1_y(i,j) = (dt*cs%cg1(i,j)) * g%IdyT(i,j)
657  enddo ; enddo
658  call post_data(cs%id_cfl_cg1_y, cs%cfl_cg1_y, cs%diag)
659  endif
660  endif
661  if ((cs%id_cg_ebt>0) .or. (cs%id_Rd_ebt>0) .or. (cs%id_p_ebt>0)) then
662  if (cs%id_p_ebt>0) then
663  call wave_speed(h, tv, g, gv, us, cs%cg1, cs%wave_speed_CSp, use_ebt_mode=.true., &
664  mono_n2_column_fraction=cs%mono_N2_column_fraction, &
665  mono_n2_depth=cs%mono_N2_depth, modal_structure=cs%p_ebt)
666  call post_data(cs%id_p_ebt, cs%p_ebt, cs%diag)
667  else
668  call wave_speed(h, tv, g, gv, us, cs%cg1, cs%wave_speed_CSp, use_ebt_mode=.true., &
669  mono_n2_column_fraction=cs%mono_N2_column_fraction, &
670  mono_n2_depth=cs%mono_N2_depth)
671  endif
672  if (cs%id_cg_ebt>0) call post_data(cs%id_cg_ebt, cs%cg1, cs%diag)
673  if (cs%id_Rd_ebt>0) then
674 !$OMP parallel do default(none) shared(is,ie,js,je,G,CS) &
675 !$OMP private(f2_h,mag_beta)
676  do j=js,je ; do i=is,ie
677  ! Blend the equatorial deformation radius with the standard one.
678  f2_h = absurdly_small_freq2 + 0.25 * us%s_to_T**2 * &
679  ((g%CoriolisBu(i,j)**2 + g%CoriolisBu(i-1,j-1)**2) + &
680  (g%CoriolisBu(i-1,j)**2 + g%CoriolisBu(i,j-1)**2))
681  mag_beta = sqrt(0.5 * us%s_to_T**2 * ( &
682  (((g%CoriolisBu(i,j)-g%CoriolisBu(i-1,j)) * g%IdxCv(i,j))**2 + &
683  ((g%CoriolisBu(i,j-1)-g%CoriolisBu(i-1,j-1)) * g%IdxCv(i,j-1))**2) + &
684  (((g%CoriolisBu(i,j)-g%CoriolisBu(i,j-1)) * g%IdyCu(i,j))**2 + &
685  ((g%CoriolisBu(i-1,j)-g%CoriolisBu(i-1,j-1)) * g%IdyCu(i-1,j))**2) ))
686  cs%Rd1(i,j) = cs%cg1(i,j) / sqrt(f2_h + cs%cg1(i,j) * mag_beta)
687 
688  enddo ; enddo
689  call post_data(cs%id_Rd_ebt, cs%Rd1, cs%diag)
690  endif
691  endif
692 
693 end subroutine calculate_diagnostic_fields
694 
695 !> This subroutine finds the location of R_in in an increasing ordered
696 !! list, Rlist, returning as k the element such that
697 !! Rlist(k) <= R_in < Rlist(k+1), and where wt and wt_p are the linear
698 !! weights that should be assigned to elements k and k+1.
699 subroutine find_weights(Rlist, R_in, k, nz, wt, wt_p)
700  real, dimension(:), &
701  intent(in) :: Rlist !< The list of target densities [kg m-3]
702  real, intent(in) :: R_in !< The density being inserted into Rlist [kg m-3]
703  integer, intent(inout) :: k !< The value of k such that Rlist(k) <= R_in < Rlist(k+1)
704  !! The input value is a first guess
705  integer, intent(in) :: nz !< The number of layers in Rlist
706  real, intent(out) :: wt !< The weight of layer k for interpolation, nondim
707  real, intent(out) :: wt_p !< The weight of layer k+1 for interpolation, nondim
708 
709  ! This subroutine finds location of R_in in an increasing ordered
710  ! list, Rlist, returning as k the element such that
711  ! Rlist(k) <= R_in < Rlist(k+1), and where wt and wt_p are the linear
712  ! weights that should be assigned to elements k and k+1.
713 
714  integer :: k_upper, k_lower, k_new, inc
715 
716  ! First, bracket the desired point.
717  if ((k < 1) .or. (k > nz)) k = nz/2
718 
719  k_upper = k ; k_lower = k ; inc = 1
720  if (r_in < rlist(k)) then
721  do
722  k_lower = max(k_lower-inc, 1)
723  if ((k_lower == 1) .or. (r_in >= rlist(k_lower))) exit
724  k_upper = k_lower
725  inc = inc*2
726  enddo
727  else
728  do
729  k_upper = min(k_upper+inc, nz)
730  if ((k_upper == nz) .or. (r_in < rlist(k_upper))) exit
731  k_lower = k_upper
732  inc = inc*2
733  enddo
734  endif
735 
736  if ((k_lower == 1) .and. (r_in <= rlist(k_lower))) then
737  k = 1 ; wt = 1.0 ; wt_p = 0.0
738  elseif ((k_upper == nz) .and. (r_in >= rlist(k_upper))) then
739  k = nz-1 ; wt = 0.0 ; wt_p = 1.0
740  else
741  do
742  if (k_upper <= k_lower+1) exit
743  k_new = (k_upper + k_lower) / 2
744  if (r_in < rlist(k_new)) then
745  k_upper = k_new
746  else
747  k_lower = k_new
748  endif
749  enddo
750 
751 ! Uncomment this as a code check
752 ! if ((R_in < Rlist(k_lower)) .or. (R_in >= Rlist(k_upper)) .or. (k_upper-k_lower /= 1)) &
753 ! write (*,*) "Error: ",R_in," is not between R(",k_lower,") = ", &
754 ! Rlist(k_lower)," and R(",k_upper,") = ",Rlist(k_upper),"."
755  k = k_lower
756  wt = (rlist(k_upper) - r_in) / (rlist(k_upper) - rlist(k_lower))
757  wt_p = 1.0 - wt
758 
759  endif
760 
761 end subroutine find_weights
762 
763 !> This subroutine calculates vertical integrals of several tracers, along
764 !! with the mass-weight of these tracers, the total column mass, and the
765 !! carefully calculated column height.
766 subroutine calculate_vertical_integrals(h, tv, p_surf, G, GV, US, CS)
767  type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure.
768  type(verticalgrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
769  type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
770  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), &
771  intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2].
772  type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various
773  !! thermodynamic variables.
774  real, dimension(:,:), pointer :: p_surf !< A pointer to the surface pressure [Pa].
775  !! If p_surf is not associated, it is the same
776  !! as setting the surface pressure to 0.
777  type(diagnostics_cs), intent(inout) :: CS !< Control structure returned by a
778  !! previous call to diagnostics_init.
779 
780  real, dimension(SZI_(G), SZJ_(G)) :: &
781  z_top, & ! Height of the top of a layer or the ocean [Z ~> m].
782  z_bot, & ! Height of the bottom of a layer (for id_mass) or the
783  ! (positive) depth of the ocean (for id_col_ht) [Z ~> m].
784  mass, & ! integrated mass of the water column [kg m-2]. For
785  ! non-Boussinesq models this is rho*dz. For Boussinesq
786  ! models, this is either the integral of in-situ density
787  ! (rho*dz for col_mass) or reference density (Rho_0*dz for mass_wt).
788  btm_pres,&! The pressure at the ocean bottom, or CMIP variable 'pbo'.
789  ! This is the column mass multiplied by gravity plus the pressure
790  ! at the ocean surface [Pa].
791  dpress, & ! Change in hydrostatic pressure across a layer [Pa].
792  tr_int ! vertical integral of a tracer times density,
793  ! (Rho_0 in a Boussinesq model) [TR kg m-2].
794  real :: IG_Earth ! Inverse of gravitational acceleration [s2 m-1].
795 
796  integer :: i, j, k, is, ie, js, je, nz
797  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
798 
799  if (cs%id_mass_wt > 0) then
800  do j=js,je ; do i=is,ie ; mass(i,j) = 0.0 ; enddo ; enddo
801  do k=1,nz ; do j=js,je ; do i=is,ie
802  mass(i,j) = mass(i,j) + gv%H_to_kg_m2*h(i,j,k)
803  enddo ; enddo ; enddo
804  call post_data(cs%id_mass_wt, mass, cs%diag)
805  endif
806 
807  if (cs%id_temp_int > 0) then
808  do j=js,je ; do i=is,ie ; tr_int(i,j) = 0.0 ; enddo ; enddo
809  do k=1,nz ; do j=js,je ; do i=is,ie
810  tr_int(i,j) = tr_int(i,j) + (gv%H_to_kg_m2*h(i,j,k))*tv%T(i,j,k)
811  enddo ; enddo ; enddo
812  call post_data(cs%id_temp_int, tr_int, cs%diag)
813  endif
814 
815  if (cs%id_salt_int > 0) then
816  do j=js,je ; do i=is,ie ; tr_int(i,j) = 0.0 ; enddo ; enddo
817  do k=1,nz ; do j=js,je ; do i=is,ie
818  tr_int(i,j) = tr_int(i,j) + (gv%H_to_kg_m2*h(i,j,k))*tv%S(i,j,k)
819  enddo ; enddo ; enddo
820  call post_data(cs%id_salt_int, tr_int, cs%diag)
821  endif
822 
823  if (cs%id_col_ht > 0) then
824  call find_eta(h, tv, g, gv, us, z_top)
825  do j=js,je ; do i=is,ie
826  z_bot(i,j) = z_top(i,j) + g%bathyT(i,j)
827  enddo ; enddo
828  call post_data(cs%id_col_ht, z_bot, cs%diag)
829  endif
830 
831  if (cs%id_col_mass > 0 .or. cs%id_pbo > 0) then
832  do j=js,je ; do i=is,ie ; mass(i,j) = 0.0 ; enddo ; enddo
833  if (gv%Boussinesq) then
834  if (associated(tv%eqn_of_state)) then
835  ig_earth = 1.0 / gv%mks_g_Earth
836 ! do j=js,je ; do i=is,ie ; z_bot(i,j) = -P_SURF(i,j)/GV%H_to_Pa ; enddo ; enddo
837  do j=js,je ; do i=is,ie ; z_bot(i,j) = 0.0 ; enddo ; enddo
838  do k=1,nz
839  do j=js,je ; do i=is,ie
840  z_top(i,j) = z_bot(i,j)
841  z_bot(i,j) = z_top(i,j) - gv%H_to_Z*h(i,j,k)
842  enddo ; enddo
843  call int_density_dz(tv%T(:,:,k), tv%S(:,:,k), &
844  z_top, z_bot, 0.0, gv%Rho0, gv%mks_g_Earth*us%Z_to_m, &
845  g%HI, g%HI, tv%eqn_of_state, dpress)
846  do j=js,je ; do i=is,ie
847  mass(i,j) = mass(i,j) + dpress(i,j) * ig_earth
848  enddo ; enddo
849  enddo
850  else
851  do k=1,nz ; do j=js,je ; do i=is,ie
852  mass(i,j) = mass(i,j) + (gv%H_to_m*gv%Rlay(k))*h(i,j,k)
853  enddo ; enddo ; enddo
854  endif
855  else
856  do k=1,nz ; do j=js,je ; do i=is,ie
857  mass(i,j) = mass(i,j) + gv%H_to_kg_m2*h(i,j,k)
858  enddo ; enddo ; enddo
859  endif
860  if (cs%id_col_mass > 0) then
861  call post_data(cs%id_col_mass, mass, cs%diag)
862  endif
863  if (cs%id_pbo > 0) then
864  do j=js,je ; do i=is,ie ; btm_pres(i,j) = 0.0 ; enddo ; enddo
865  ! 'pbo' is defined as the sea water pressure at the sea floor
866  ! pbo = (mass * g) + p_surf
867  ! where p_surf is the sea water pressure at sea water surface.
868  do j=js,je ; do i=is,ie
869  btm_pres(i,j) = mass(i,j) * gv%mks_g_Earth
870  if (associated(p_surf)) then
871  btm_pres(i,j) = btm_pres(i,j) + p_surf(i,j)
872  endif
873  enddo ; enddo
874  call post_data(cs%id_pbo, btm_pres, cs%diag)
875  endif
876  endif
877 
878 end subroutine calculate_vertical_integrals
879 
880 !> This subroutine calculates terms in the mechanical energy budget.
881 subroutine calculate_energy_diagnostics(u, v, h, uh, vh, ADp, CDp, G, GV, US, CS)
882  type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure.
883  type(verticalgrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
884  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), &
885  intent(in) :: u !< The zonal velocity [m s-1].
886  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), &
887  intent(in) :: v !< The meridional velocity [m s-1].
888  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), &
889  intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2].
890  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), &
891  intent(in) :: uh !< Transport through zonal faces=u*h*dy,
892  !! [H m2 s-1 ~> m3 s-1 or kg s-1].
893  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), &
894  intent(in) :: vh !< Transport through merid faces=v*h*dx,
895  !! [H m2 s-1 ~> m3 s-1 or kg s-1].
896  type(accel_diag_ptrs), intent(in) :: ADp !< Structure pointing to accelerations in momentum equation.
897  type(cont_diag_ptrs), intent(in) :: CDp !< Structure pointing to terms in continuity equations.
898  type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
899  type(diagnostics_cs), intent(inout) :: CS !< Control structure returned by a previous call to
900  !! diagnostics_init.
901 
902 ! This subroutine calculates terms in the mechanical energy budget.
903 
904  ! Local variables
905  real :: KE_u(SZIB_(G),SZJ_(G))
906  real :: KE_v(SZI_(G),SZJB_(G))
907  real :: KE_h(SZI_(G),SZJ_(G))
908 
909  integer :: i, j, k, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz
910  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
911  isq = g%IscB ; ieq = g%IecB ; jsq = g%JscB ; jeq = g%JecB
912 
913  do j=js-1,je ; do i=is-1,ie
914  ke_u(i,j) = 0.0 ; ke_v(i,j) = 0.0
915  enddo ; enddo
916 
917  if (associated(cs%KE)) then
918  do k=1,nz ; do j=js,je ; do i=is,ie
919  cs%KE(i,j,k) = ((u(i,j,k)*u(i,j,k) + u(i-1,j,k)*u(i-1,j,k)) + &
920  (v(i,j,k)*v(i,j,k) + v(i,j-1,k)*v(i,j-1,k)))*0.25
921  ! DELETE THE FOLLOWING... Make this 0 to test the momentum balance,
922  ! or a huge number to test the continuity balance.
923  ! CS%KE(i,j,k) *= 1e20
924  enddo ; enddo ; enddo
925  if (cs%id_KE > 0) call post_data(cs%id_KE, cs%KE, cs%diag)
926  endif
927 
928  if (.not.g%symmetric) then
929  if (associated(cs%dKE_dt) .OR. associated(cs%PE_to_KE) .OR. associated(cs%KE_CorAdv) .OR. &
930  associated(cs%KE_adv) .OR. associated(cs%KE_visc) .OR. associated(cs%KE_horvisc).OR. &
931  associated(cs%KE_dia) ) then
932  call create_group_pass(cs%pass_KE_uv, ke_u, ke_v, g%Domain, to_north+to_east)
933  endif
934  endif
935 
936  if (associated(cs%dKE_dt)) then
937  do k=1,nz
938  do j=js,je ; do i=isq,ieq
939  ke_u(i,j) = uh(i,j,k)*g%dxCu(i,j)*cs%du_dt(i,j,k)
940  enddo ; enddo
941  do j=jsq,jeq ; do i=is,ie
942  ke_v(i,j) = vh(i,j,k)*g%dyCv(i,j)*cs%dv_dt(i,j,k)
943  enddo ; enddo
944  do j=js,je ; do i=is,ie
945  ke_h(i,j) = cs%KE(i,j,k)*cs%dh_dt(i,j,k)
946  enddo ; enddo
947  if (.not.g%symmetric) &
948  call do_group_pass(cs%pass_KE_uv, g%domain)
949  do j=js,je ; do i=is,ie
950  cs%dKE_dt(i,j,k) = gv%H_to_m * (ke_h(i,j) + 0.5 * g%IareaT(i,j) * &
951  (ke_u(i,j) + ke_u(i-1,j) + ke_v(i,j) + ke_v(i,j-1)))
952  enddo ; enddo
953  enddo
954  if (cs%id_dKEdt > 0) call post_data(cs%id_dKEdt, cs%dKE_dt, cs%diag)
955  endif
956 
957  if (associated(cs%PE_to_KE)) then
958  do k=1,nz
959  do j=js,je ; do i=isq,ieq
960  ke_u(i,j) = uh(i,j,k)*g%dxCu(i,j)*adp%PFu(i,j,k)
961  enddo ; enddo
962  do j=jsq,jeq ; do i=is,ie
963  ke_v(i,j) = vh(i,j,k)*g%dyCv(i,j)*adp%PFv(i,j,k)
964  enddo ; enddo
965  if (.not.g%symmetric) &
966  call do_group_pass(cs%pass_KE_uv, g%domain)
967  do j=js,je ; do i=is,ie
968  cs%PE_to_KE(i,j,k) = gv%H_to_m * 0.5 * g%IareaT(i,j) * &
969  (ke_u(i,j) + ke_u(i-1,j) + ke_v(i,j) + ke_v(i,j-1))
970  enddo ; enddo
971  enddo
972  if (cs%id_PE_to_KE > 0) call post_data(cs%id_PE_to_KE, cs%PE_to_KE, cs%diag)
973  endif
974 
975  if (associated(cs%KE_CorAdv)) then
976  do k=1,nz
977  do j=js,je ; do i=isq,ieq
978  ke_u(i,j) = uh(i,j,k)*g%dxCu(i,j)*adp%CAu(i,j,k)
979  enddo ; enddo
980  do j=jsq,jeq ; do i=is,ie
981  ke_v(i,j) = vh(i,j,k)*g%dyCv(i,j)*adp%CAv(i,j,k)
982  enddo ; enddo
983  do j=js,je ; do i=is,ie
984  ke_h(i,j) = -cs%KE(i,j,k) * g%IareaT(i,j) * &
985  (uh(i,j,k) - uh(i-1,j,k) + vh(i,j,k) - vh(i,j-1,k))
986  enddo ; enddo
987  if (.not.g%symmetric) &
988  call do_group_pass(cs%pass_KE_uv, g%domain)
989  do j=js,je ; do i=is,ie
990  cs%KE_CorAdv(i,j,k) = gv%H_to_m * (ke_h(i,j) + 0.5 * g%IareaT(i,j) * &
991  (ke_u(i,j) + ke_u(i-1,j) + ke_v(i,j) + ke_v(i,j-1)))
992  enddo ; enddo
993  enddo
994  if (cs%id_KE_Coradv > 0) call post_data(cs%id_KE_Coradv, cs%KE_Coradv, cs%diag)
995  endif
996 
997  if (associated(cs%KE_adv)) then
998  ! NOTE: All terms in KE_adv are multipled by -1, which can easily produce
999  ! negative zeros and may signal a reproducibility issue over land.
1000  ! We resolve this by re-initializing and only evaluating over water points.
1001  ke_u(:,:) = 0. ; ke_v(:,:) = 0.
1002  do k=1,nz
1003  do j=js,je ; do i=isq,ieq
1004  if (g%mask2dCu(i,j) /= 0.) &
1005  ke_u(i,j) = uh(i,j,k)*g%dxCu(i,j)*adp%gradKEu(i,j,k)
1006  enddo ; enddo
1007  do j=jsq,jeq ; do i=is,ie
1008  if (g%mask2dCv(i,j) /= 0.) &
1009  ke_v(i,j) = vh(i,j,k)*g%dyCv(i,j)*adp%gradKEv(i,j,k)
1010  enddo ; enddo
1011  do j=js,je ; do i=is,ie
1012  ke_h(i,j) = -cs%KE(i,j,k) * g%IareaT(i,j) * &
1013  (uh(i,j,k) - uh(i-1,j,k) + vh(i,j,k) - vh(i,j-1,k))
1014  enddo ; enddo
1015  if (.not.g%symmetric) &
1016  call do_group_pass(cs%pass_KE_uv, g%domain)
1017  do j=js,je ; do i=is,ie
1018  cs%KE_adv(i,j,k) = gv%H_to_m * (ke_h(i,j) + 0.5 * g%IareaT(i,j) * &
1019  (ke_u(i,j) + ke_u(i-1,j) + ke_v(i,j) + ke_v(i,j-1)))
1020  enddo ; enddo
1021  enddo
1022  if (cs%id_KE_adv > 0) call post_data(cs%id_KE_adv, cs%KE_adv, cs%diag)
1023  endif
1024 
1025  if (associated(cs%KE_visc)) then
1026  do k=1,nz
1027  do j=js,je ; do i=isq,ieq
1028  ke_u(i,j) = uh(i,j,k)*g%dxCu(i,j)*adp%du_dt_visc(i,j,k)
1029  enddo ; enddo
1030  do j=jsq,jeq ; do i=is,ie
1031  ke_v(i,j) = vh(i,j,k)*g%dyCv(i,j)*adp%dv_dt_visc(i,j,k)
1032  enddo ; enddo
1033  if (.not.g%symmetric) &
1034  call do_group_pass(cs%pass_KE_uv, g%domain)
1035  do j=js,je ; do i=is,ie
1036  cs%KE_visc(i,j,k) = gv%H_to_m * (0.5 * g%IareaT(i,j) * &
1037  (ke_u(i,j) + ke_u(i-1,j) + ke_v(i,j) + ke_v(i,j-1)))
1038  enddo ; enddo
1039  enddo
1040  if (cs%id_KE_visc > 0) call post_data(cs%id_KE_visc, cs%KE_visc, cs%diag)
1041  endif
1042 
1043  if (associated(cs%KE_horvisc)) then
1044  do k=1,nz
1045  do j=js,je ; do i=isq,ieq
1046  ke_u(i,j) = uh(i,j,k)*g%dxCu(i,j)*us%s_to_T*adp%diffu(i,j,k)
1047  enddo ; enddo
1048  do j=jsq,jeq ; do i=is,ie
1049  ke_v(i,j) = vh(i,j,k)*g%dyCv(i,j)*us%s_to_T*adp%diffv(i,j,k)
1050  enddo ; enddo
1051  if (.not.g%symmetric) &
1052  call do_group_pass(cs%pass_KE_uv, g%domain)
1053  do j=js,je ; do i=is,ie
1054  cs%KE_horvisc(i,j,k) = gv%H_to_m * 0.5 * g%IareaT(i,j) * &
1055  (ke_u(i,j) + ke_u(i-1,j) + ke_v(i,j) + ke_v(i,j-1))
1056  enddo ; enddo
1057  enddo
1058  if (cs%id_KE_horvisc > 0) call post_data(cs%id_KE_horvisc, cs%KE_horvisc, cs%diag)
1059  endif
1060 
1061  if (associated(cs%KE_dia)) then
1062  do k=1,nz
1063  do j=js,je ; do i=isq,ieq
1064  ke_u(i,j) = uh(i,j,k)*g%dxCu(i,j)*adp%du_dt_dia(i,j,k)
1065  enddo ; enddo
1066  do j=jsq,jeq ; do i=is,ie
1067  ke_v(i,j) = vh(i,j,k)*g%dyCv(i,j)*adp%dv_dt_dia(i,j,k)
1068  enddo ; enddo
1069  do j=js,je ; do i=is,ie
1070  ke_h(i,j) = cs%KE(i,j,k) * &
1071  (cdp%diapyc_vel(i,j,k) - cdp%diapyc_vel(i,j,k+1))
1072  enddo ; enddo
1073  if (.not.g%symmetric) &
1074  call do_group_pass(cs%pass_KE_uv, g%domain)
1075  do j=js,je ; do i=is,ie
1076  cs%KE_dia(i,j,k) = ke_h(i,j) + gv%H_to_m * 0.5 * g%IareaT(i,j) * &
1077  (ke_u(i,j) + ke_u(i-1,j) + ke_v(i,j) + ke_v(i,j-1))
1078  enddo ; enddo
1079  enddo
1080  if (cs%id_KE_dia > 0) call post_data(cs%id_KE_dia, cs%KE_dia, cs%diag)
1081  endif
1082 
1083 end subroutine calculate_energy_diagnostics
1084 
1085 !> This subroutine registers fields to calculate a diagnostic time derivative.
1086 subroutine register_time_deriv(lb, f_ptr, deriv_ptr, CS)
1087  integer, intent(in), dimension(3) :: lb !< Lower index bound of f_ptr
1088  real, dimension(lb(1):,lb(2):,:), target :: f_ptr
1089  !< Time derivative operand
1090  real, dimension(lb(1):,lb(2):,:), target :: deriv_ptr
1091  !< Time derivative of f_ptr
1092  type(diagnostics_cs), pointer :: cs !< Control structure returned by previous call to
1093  !! diagnostics_init.
1094 
1095  ! This subroutine registers fields to calculate a diagnostic time derivative.
1096  ! NOTE: Lower bound is required for grid indexing in calculate_derivs().
1097  ! We assume that the vertical axis is 1-indexed.
1098 
1099  integer :: m !< New index of deriv_ptr in CS%deriv
1100  integer :: ub(3) !< Upper index bound of f_ptr, based on shape.
1101 
1102  if (.not.associated(cs)) call mom_error(fatal, &
1103  "register_time_deriv: Module must be initialized before it is used.")
1104 
1105  if (cs%num_time_deriv >= max_fields_) then
1106  call mom_error(warning,"MOM_diagnostics: Attempted to register more than " // &
1107  "MAX_FIELDS_ diagnostic time derivatives via register_time_deriv.")
1108  return
1109  endif
1110 
1111  m = cs%num_time_deriv+1 ; cs%num_time_deriv = m
1112 
1113  ub(:) = lb(:) + shape(f_ptr) - 1
1114 
1115  cs%nlay(m) = size(f_ptr, 3)
1116  cs%deriv(m)%p => deriv_ptr
1117  allocate(cs%prev_val(m)%p(lb(1):ub(1), lb(2):ub(2), cs%nlay(m)))
1118 
1119  cs%var_ptr(m)%p => f_ptr
1120  cs%prev_val(m)%p(:,:,:) = f_ptr(:,:,:)
1121 
1122 end subroutine register_time_deriv
1123 
1124 !> This subroutine calculates all registered time derivatives.
1125 subroutine calculate_derivs(dt, G, CS)
1126  real, intent(in) :: dt !< The time interval over which differences occur [s].
1127  type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure.
1128  type(diagnostics_cs), intent(inout) :: CS !< Control structure returned by previous call to
1129  !! diagnostics_init.
1130 
1131 ! This subroutine calculates all registered time derivatives.
1132  integer i, j, k, m
1133  real Idt
1134 
1135  if (dt > 0.0) then ; idt = 1.0/dt
1136  else ; return ; endif
1137 
1138  ! Because the field is unknown, its grid index bounds are also unknown.
1139  ! Additionally, two of the fields (dudt, dvdt) require calculation of spatial
1140  ! derivatives when computing d(KE)/dt. This raises issues in non-symmetric
1141  ! mode, where the symmetric boundaries (west, south) may not be updated.
1142 
1143  ! For this reason, we explicitly loop from isc-1:iec and jsc-1:jec, in order
1144  ! to force boundary value updates, even though it may not be strictly valid
1145  ! for all fields. Note this assumes a halo, and that it has been updated.
1146 
1147  do m=1,cs%num_time_deriv
1148  do k=1,cs%nlay(m) ; do j=g%jsc-1,g%jec ; do i=g%isc-1,g%iec
1149  cs%deriv(m)%p(i,j,k) = (cs%var_ptr(m)%p(i,j,k) - cs%prev_val(m)%p(i,j,k)) * idt
1150  cs%prev_val(m)%p(i,j,k) = cs%var_ptr(m)%p(i,j,k)
1151  enddo ; enddo ; enddo
1152  enddo
1153 
1154 end subroutine calculate_derivs
1155 
1156 !> This routine posts diagnostics of various dynamic ocean surface quantities,
1157 !! including velocities, speed and sea surface height, at the time the ocean
1158 !! state is reported back to the caller
1159 subroutine post_surface_dyn_diags(IDs, G, diag, sfc_state, ssh)
1160  type(surface_diag_ids), intent(in) :: ids !< A structure with the diagnostic IDs.
1161  type(ocean_grid_type), intent(in) :: g !< ocean grid structure
1162  type(diag_ctrl), intent(in) :: diag !< regulates diagnostic output
1163  type(surface), intent(in) :: sfc_state !< structure describing the ocean surface state
1164  real, dimension(SZI_(G),SZJ_(G)), &
1165  intent(in) :: ssh !< Time mean surface height without corrections for ice displacement [m]
1166 
1167  ! Local variables
1168  real, dimension(SZI_(G),SZJ_(G)) :: work_2d ! A 2-d work array
1169  integer :: i, j, is, ie, js, je
1170 
1171  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
1172 
1173  if (ids%id_ssh > 0) &
1174  call post_data(ids%id_ssh, ssh, diag, mask=g%mask2dT)
1175 
1176  if (ids%id_ssu > 0) &
1177  call post_data(ids%id_ssu, sfc_state%u, diag, mask=g%mask2dCu)
1178 
1179  if (ids%id_ssv > 0) &
1180  call post_data(ids%id_ssv, sfc_state%v, diag, mask=g%mask2dCv)
1181 
1182  if (ids%id_speed > 0) then
1183  do j=js,je ; do i=is,ie
1184  work_2d(i,j) = sqrt(0.5*(sfc_state%u(i-1,j)**2 + sfc_state%u(i,j)**2) + &
1185  0.5*(sfc_state%v(i,j-1)**2 + sfc_state%v(i,j)**2))
1186  enddo ; enddo
1187  call post_data(ids%id_speed, work_2d, diag, mask=g%mask2dT)
1188  endif
1189 
1190 end subroutine post_surface_dyn_diags
1191 
1192 
1193 !> This routine posts diagnostics of various ocean surface and integrated
1194 !! quantities at the time the ocean state is reported back to the caller
1195 subroutine post_surface_thermo_diags(IDs, G, GV, US, diag, dt_int, sfc_state, tv, &
1196  ssh, ssh_ibc)
1197  type(surface_diag_ids), intent(in) :: ids !< A structure with the diagnostic IDs.
1198  type(ocean_grid_type), intent(in) :: g !< ocean grid structure
1199  type(verticalgrid_type), intent(in) :: gv !< ocean vertical grid structure
1200  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
1201  type(diag_ctrl), intent(in) :: diag !< regulates diagnostic output
1202  real, intent(in) :: dt_int !< total time step associated with these diagnostics [s].
1203  type(surface), intent(in) :: sfc_state !< structure describing the ocean surface state
1204  type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various thermodynamic variables
1205  real, dimension(SZI_(G),SZJ_(G)), &
1206  intent(in) :: ssh !< Time mean surface height without corrections for ice displacement [m]
1207  real, dimension(SZI_(G),SZJ_(G)), intent(in) :: ssh_ibc !< Time mean surface height with corrections
1208  !! for ice displacement and the inverse barometer [m]
1209 
1210  real, dimension(SZI_(G),SZJ_(G)) :: work_2d ! A 2-d work array
1211  real, dimension(SZI_(G),SZJ_(G)) :: &
1212  zos ! dynamic sea lev (zero area mean) from inverse-barometer adjusted ssh [m]
1213  real :: i_time_int ! The inverse of the time interval [s-1].
1214  real :: zos_area_mean, volo, ssh_ga
1215  integer :: i, j, is, ie, js, je
1216 
1217  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
1218 
1219  ! area mean SSH
1220  if (ids%id_ssh_ga > 0) then
1221  ssh_ga = global_area_mean(ssh, g)
1222  call post_data(ids%id_ssh_ga, ssh_ga, diag)
1223  endif
1224 
1225  ! post the dynamic sea level, zos, and zossq.
1226  ! zos is ave_ssh with sea ice inverse barometer removed,
1227  ! and with zero global area mean.
1228  if (ids%id_zos > 0 .or. ids%id_zossq > 0) then
1229  zos(:,:) = 0.0
1230  do j=js,je ; do i=is,ie
1231  zos(i,j) = ssh_ibc(i,j)
1232  enddo ; enddo
1233  zos_area_mean = global_area_mean(zos, g)
1234  do j=js,je ; do i=is,ie
1235  zos(i,j) = zos(i,j) - g%mask2dT(i,j)*zos_area_mean
1236  enddo ; enddo
1237  if (ids%id_zos > 0) call post_data(ids%id_zos, zos, diag, mask=g%mask2dT)
1238  if (ids%id_zossq > 0) then
1239  do j=js,je ; do i=is,ie
1240  work_2d(i,j) = zos(i,j)*zos(i,j)
1241  enddo ; enddo
1242  call post_data(ids%id_zossq, work_2d, diag, mask=g%mask2dT)
1243  endif
1244  endif
1245 
1246  ! post total volume of the liquid ocean
1247  if (ids%id_volo > 0) then
1248  do j=js,je ; do i=is,ie
1249  work_2d(i,j) = g%mask2dT(i,j)*(ssh(i,j) + us%Z_to_m*g%bathyT(i,j))
1250  enddo ; enddo
1251  volo = global_area_integral(work_2d, g)
1252  call post_data(ids%id_volo, volo, diag)
1253  endif
1254 
1255  ! Use Adcroft's rule of reciprocals; it does the right thing here.
1256  i_time_int = 0.0 ; if (dt_int > 0.0) i_time_int = 1.0 / dt_int
1257 
1258  ! post time-averaged rate of frazil formation
1259  if (associated(tv%frazil) .and. (ids%id_fraz > 0)) then
1260  do j=js,je ; do i=is,ie
1261  work_2d(i,j) = tv%frazil(i,j) * i_time_int
1262  enddo ; enddo
1263  call post_data(ids%id_fraz, work_2d, diag, mask=g%mask2dT)
1264  endif
1265 
1266  ! post time-averaged salt deficit
1267  if (associated(tv%salt_deficit) .and. (ids%id_salt_deficit > 0)) then
1268  do j=js,je ; do i=is,ie
1269  work_2d(i,j) = tv%salt_deficit(i,j) * i_time_int
1270  enddo ; enddo
1271  call post_data(ids%id_salt_deficit, work_2d, diag, mask=g%mask2dT)
1272  endif
1273 
1274  ! post temperature of P-E+R
1275  if (associated(tv%TempxPmE) .and. (ids%id_Heat_PmE > 0)) then
1276  do j=js,je ; do i=is,ie
1277  work_2d(i,j) = tv%TempxPmE(i,j) * (tv%C_p * i_time_int)
1278  enddo ; enddo
1279  call post_data(ids%id_Heat_PmE, work_2d, diag, mask=g%mask2dT)
1280  endif
1281 
1282  ! post geothermal heating or internal heat source/sinks
1283  if (associated(tv%internal_heat) .and. (ids%id_intern_heat > 0)) then
1284  do j=js,je ; do i=is,ie
1285  work_2d(i,j) = tv%internal_heat(i,j) * (tv%C_p * i_time_int)
1286  enddo ; enddo
1287  call post_data(ids%id_intern_heat, work_2d, diag, mask=g%mask2dT)
1288  endif
1289 
1290  if (tv%T_is_conT) then
1291  ! Internal T&S variables are conservative temperature & absolute salinity
1292  if (ids%id_sstcon > 0) call post_data(ids%id_sstcon, sfc_state%SST, diag, mask=g%mask2dT)
1293  ! Use TEOS-10 function calls convert T&S diagnostics from conservative temp
1294  ! to potential temperature.
1295  do j=js,je ; do i=is,ie
1296  work_2d(i,j) = gsw_pt_from_ct(sfc_state%SSS(i,j),sfc_state%SST(i,j))
1297  enddo ; enddo
1298  if (ids%id_sst > 0) call post_data(ids%id_sst, work_2d, diag, mask=g%mask2dT)
1299  else
1300  ! Internal T&S variables are potential temperature & practical salinity
1301  if (ids%id_sst > 0) call post_data(ids%id_sst, sfc_state%SST, diag, mask=g%mask2dT)
1302  endif
1303 
1304  if (tv%S_is_absS) then
1305  ! Internal T&S variables are conservative temperature & absolute salinity
1306  if (ids%id_sssabs > 0) call post_data(ids%id_sssabs, sfc_state%SSS, diag, mask=g%mask2dT)
1307  ! Use TEOS-10 function calls convert T&S diagnostics from absolute salinity
1308  ! to practical salinity.
1309  do j=js,je ; do i=is,ie
1310  work_2d(i,j) = gsw_sp_from_sr(sfc_state%SSS(i,j))
1311  enddo ; enddo
1312  if (ids%id_sss > 0) call post_data(ids%id_sss, work_2d, diag, mask=g%mask2dT)
1313  else
1314  ! Internal T&S variables are potential temperature & practical salinity
1315  if (ids%id_sss > 0) call post_data(ids%id_sss, sfc_state%SSS, diag, mask=g%mask2dT)
1316  endif
1317 
1318  if (ids%id_sst_sq > 0) then
1319  do j=js,je ; do i=is,ie
1320  work_2d(i,j) = sfc_state%SST(i,j)*sfc_state%SST(i,j)
1321  enddo ; enddo
1322  call post_data(ids%id_sst_sq, work_2d, diag, mask=g%mask2dT)
1323  endif
1324  if (ids%id_sss_sq > 0) then
1325  do j=js,je ; do i=is,ie
1326  work_2d(i,j) = sfc_state%SSS(i,j)*sfc_state%SSS(i,j)
1327  enddo ; enddo
1328  call post_data(ids%id_sss_sq, work_2d, diag, mask=g%mask2dT)
1329  endif
1330 
1331  call coupler_type_send_data(sfc_state%tr_fields, get_diag_time_end(diag))
1332 
1333 end subroutine post_surface_thermo_diags
1334 
1335 
1336 !> This routine posts diagnostics of the transports, including the subgridscale
1337 !! contributions.
1338 subroutine post_transport_diagnostics(G, GV, uhtr, vhtr, h, IDs, diag_pre_dyn, &
1339  diag, dt_trans, Reg)
1340  type(ocean_grid_type), intent(inout) :: g !< ocean grid structure
1341  type(verticalgrid_type), intent(in) :: gv !< ocean vertical grid structure
1342  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), intent(in) :: uhtr !< Accumulated zonal thickness fluxes
1343  !! used to advect tracers [H m2 ~> m3 or kg]
1344  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), intent(in) :: vhtr !< Accumulated meridional thickness fluxes
1345  !! used to advect tracers [H m2 ~> m3 or kg]
1346  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), &
1347  intent(in) :: h !< The updated layer thicknesses [H ~> m or kg m-2]
1348  type(transport_diag_ids), intent(in) :: ids !< A structure with the diagnostic IDs.
1349  type(diag_grid_storage), intent(inout) :: diag_pre_dyn !< Stored grids from before dynamics
1350  type(diag_ctrl), intent(inout) :: diag !< regulates diagnostic output
1351  real, intent(in) :: dt_trans !< total time step associated with the transports [s].
1352  type(tracer_registry_type), pointer :: reg !< Pointer to the tracer registry
1353 
1354  ! Local variables
1355  real, dimension(SZIB_(G), SZJ_(G)) :: umo2d ! Diagnostics of integrated mass transport [kg s-1]
1356  real, dimension(SZI_(G), SZJB_(G)) :: vmo2d ! Diagnostics of integrated mass transport [kg s-1]
1357  real, dimension(SZIB_(G), SZJ_(G), SZK_(G)) :: umo ! Diagnostics of layer mass transport [kg s-1]
1358  real, dimension(SZI_(G), SZJB_(G), SZK_(G)) :: vmo ! Diagnostics of layer mass transport [kg s-1]
1359  real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: h_tend ! Change in layer thickness due to dynamics
1360  ! [H s-1 ~> m s-1 or kg m-2 s-1].
1361  real :: idt ! The inverse of the time interval [s-1]
1362  real :: h_to_kg_m2_dt ! A conversion factor from accumulated transports to fluxes
1363  ! [kg m-2 H-1 s-1 ~> kg m-3 s-1 or s-1].
1364  integer :: i, j, k, is, ie, js, je, nz
1365  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
1366 
1367  idt = 1. / dt_trans
1368  h_to_kg_m2_dt = gv%H_to_kg_m2 * idt
1369 
1370  call diag_save_grids(diag)
1371  call diag_copy_storage_to_diag(diag, diag_pre_dyn)
1372 
1373  if (ids%id_umo_2d > 0) then
1374  umo2d(:,:) = 0.0
1375  do k=1,nz ; do j=js,je ; do i=is-1,ie
1376  umo2d(i,j) = umo2d(i,j) + uhtr(i,j,k) * h_to_kg_m2_dt
1377  enddo ; enddo ; enddo
1378  call post_data(ids%id_umo_2d, umo2d, diag)
1379  endif
1380  if (ids%id_umo > 0) then
1381  ! Convert to kg/s.
1382  do k=1,nz ; do j=js,je ; do i=is-1,ie
1383  umo(i,j,k) = uhtr(i,j,k) * h_to_kg_m2_dt
1384  enddo ; enddo ; enddo
1385  call post_data(ids%id_umo, umo, diag, alt_h = diag_pre_dyn%h_state)
1386  endif
1387  if (ids%id_vmo_2d > 0) then
1388  vmo2d(:,:) = 0.0
1389  do k=1,nz ; do j=js-1,je ; do i=is,ie
1390  vmo2d(i,j) = vmo2d(i,j) + vhtr(i,j,k) * h_to_kg_m2_dt
1391  enddo ; enddo ; enddo
1392  call post_data(ids%id_vmo_2d, vmo2d, diag)
1393  endif
1394  if (ids%id_vmo > 0) then
1395  ! Convert to kg/s.
1396  do k=1,nz ; do j=js-1,je ; do i=is,ie
1397  vmo(i,j,k) = vhtr(i,j,k) * h_to_kg_m2_dt
1398  enddo ; enddo ; enddo
1399  call post_data(ids%id_vmo, vmo, diag, alt_h = diag_pre_dyn%h_state)
1400  endif
1401 
1402  if (ids%id_uhtr > 0) call post_data(ids%id_uhtr, uhtr, diag, alt_h = diag_pre_dyn%h_state)
1403  if (ids%id_vhtr > 0) call post_data(ids%id_vhtr, vhtr, diag, alt_h = diag_pre_dyn%h_state)
1404  if (ids%id_dynamics_h > 0) call post_data(ids%id_dynamics_h, diag_pre_dyn%h_state, diag, &
1405  alt_h = diag_pre_dyn%h_state)
1406  ! Post the change in thicknesses
1407  if (ids%id_dynamics_h_tendency > 0) then
1408  h_tend(:,:,:) = 0.
1409  do k=1,nz ; do j=js,je ; do i=is,ie
1410  h_tend(i,j,k) = (h(i,j,k) - diag_pre_dyn%h_state(i,j,k))*idt
1411  enddo ; enddo ; enddo
1412  call post_data(ids%id_dynamics_h_tendency, h_tend, diag, alt_h = diag_pre_dyn%h_state)
1413  endif
1414 
1415  call post_tracer_transport_diagnostics(g, gv, reg, diag_pre_dyn%h_state, diag)
1416 
1417  call diag_restore_grids(diag)
1418 
1419 end subroutine post_transport_diagnostics
1420 
1421 !> This subroutine registers various diagnostics and allocates space for fields
1422 !! that other diagnostis depend upon.
1423 subroutine mom_diagnostics_init(MIS, ADp, CDp, Time, G, GV, US, param_file, diag, CS, tv)
1424  type(ocean_internal_state), intent(in) :: mis !< For "MOM Internal State" a set of pointers to
1425  !! the fields and accelerations that make up the
1426  !! ocean's internal physical state.
1427  type(accel_diag_ptrs), intent(inout) :: adp !< Structure with pointers to momentum equation
1428  !! terms.
1429  type(cont_diag_ptrs), intent(inout) :: cdp !< Structure with pointers to continuity
1430  !! equation terms.
1431  type(time_type), intent(in) :: time !< Current model time.
1432  type(ocean_grid_type), intent(in) :: g !< The ocean's grid structure.
1433  type(verticalgrid_type), intent(in) :: gv !< The ocean's vertical grid structure.
1434  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
1435  type(param_file_type), intent(in) :: param_file !< A structure to parse for run-time
1436  !! parameters.
1437  type(diag_ctrl), target, intent(inout) :: diag !< Structure to regulate diagnostic output.
1438  type(diagnostics_cs), pointer :: cs !< Pointer set to point to control structure
1439  !! for this module.
1440  type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various
1441  !! thermodynamic variables.
1442 
1443  ! Local variables
1444  real :: omega, f2_min, convert_h
1445  ! This include declares and sets the variable "version".
1446 # include "version_variable.h"
1447  character(len=40) :: mdl = "MOM_diagnostics" ! This module's name.
1448  character(len=48) :: thickness_units, flux_units
1449  logical :: use_temperature, adiabatic
1450  integer :: isd, ied, jsd, jed, isdb, iedb, jsdb, jedb, nz, nkml, nkbl
1451  integer :: is, ie, js, je, isq, ieq, jsq, jeq, i, j
1452 
1453  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
1454  isq = g%IscB ; ieq = g%IecB ; jsq = g%JscB ; jeq = g%JecB
1455  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed ; nz = g%ke
1456  isdb = g%IsdB ; iedb = g%IedB ; jsdb = g%JsdB ; jedb = g%JedB
1457 
1458  if (associated(cs)) then
1459  call mom_error(warning, "MOM_diagnostics_init called with an associated "// &
1460  "control structure.")
1461  return
1462  endif
1463  allocate(cs)
1464 
1465  cs%diag => diag
1466  use_temperature = associated(tv%T)
1467  call get_param(param_file, mdl, "ADIABATIC", adiabatic, default=.false., &
1468  do_not_log=.true.)
1469 
1470  ! Read all relevant parameters and write them to the model log.
1471  call log_version(param_file, mdl, version)
1472  call get_param(param_file, mdl, "DIAG_EBT_MONO_N2_COLUMN_FRACTION", cs%mono_N2_column_fraction, &
1473  "The lower fraction of water column over which N2 is limited as monotonic "// &
1474  "for the purposes of calculating the equivalent barotropic wave speed.", &
1475  units='nondim', default=0.)
1476  call get_param(param_file, mdl, "DIAG_EBT_MONO_N2_DEPTH", cs%mono_N2_depth, &
1477  "The depth below which N2 is limited as monotonic for the "// &
1478  "purposes of calculating the equivalent barotropic wave speed.", &
1479  units='m', default=-1.)
1480 
1481  if (gv%Boussinesq) then
1482  thickness_units = "m" ; flux_units = "m3 s-1" ; convert_h = gv%H_to_m
1483  else
1484  thickness_units = "kg m-2" ; flux_units = "kg s-1" ; convert_h = gv%H_to_kg_m2
1485  endif
1486 
1487  cs%id_masscello = register_diag_field('ocean_model', 'masscello', diag%axesTL,&
1488  time, 'Mass per unit area of liquid ocean grid cell', 'kg m-2', &
1489  standard_name='sea_water_mass_per_unit_area', v_extensive=.true.)
1490 
1491  cs%id_masso = register_scalar_field('ocean_model', 'masso', time, &
1492  diag, 'Mass of liquid ocean', 'kg', standard_name='sea_water_mass')
1493 
1494  cs%id_thkcello = register_diag_field('ocean_model', 'thkcello', diag%axesTL, time, &
1495  long_name = 'Cell Thickness', standard_name='cell_thickness', units='m', v_extensive=.true.)
1496  cs%id_h_pre_sync = register_diag_field('ocean_model', 'h_pre_sync', diag%axesTL, time, &
1497  long_name = 'Cell thickness from the previous timestep', units='m', v_extensive=.true.)
1498 
1499  ! Note that CS%id_volcello would normally be registered here but because it is a "cell measure" and
1500  ! must be registered first. We earlier stored the handle of volcello but need it here for posting
1501  ! by this module.
1502  cs%id_volcello = diag_get_volume_cell_measure_dm_id(diag)
1503 
1504  if (use_temperature) then
1505  if (tv%T_is_conT) then
1506  cs%id_Tpot = register_diag_field('ocean_model', 'temp', diag%axesTL, &
1507  time, 'Potential Temperature', 'degC')
1508  endif
1509  if (tv%S_is_absS) then
1510  cs%id_Sprac = register_diag_field('ocean_model', 'salt', diag%axesTL, &
1511  time, 'Salinity', 'psu')
1512  endif
1513 
1514  cs%id_tob = register_diag_field('ocean_model','tob', diag%axesT1, time, &
1515  long_name='Sea Water Potential Temperature at Sea Floor', &
1516  standard_name='sea_water_potential_temperature_at_sea_floor', units='degC')
1517  cs%id_sob = register_diag_field('ocean_model','sob',diag%axesT1, time, &
1518  long_name='Sea Water Salinity at Sea Floor', &
1519  standard_name='sea_water_salinity_at_sea_floor', units='psu')
1520 
1521  cs%id_temp_layer_ave = register_diag_field('ocean_model', 'temp_layer_ave', &
1522  diag%axesZL, time, 'Layer Average Ocean Temperature', 'degC')
1523  cs%id_salt_layer_ave = register_diag_field('ocean_model', 'salt_layer_ave', &
1524  diag%axesZL, time, 'Layer Average Ocean Salinity', 'psu')
1525 
1526  cs%id_thetaoga = register_scalar_field('ocean_model', 'thetaoga', &
1527  time, diag, 'Global Mean Ocean Potential Temperature', 'degC',&
1528  standard_name='sea_water_potential_temperature')
1529  cs%id_soga = register_scalar_field('ocean_model', 'soga', &
1530  time, diag, 'Global Mean Ocean Salinity', 'psu', &
1531  standard_name='sea_water_salinity')
1532 
1533  cs%id_tosga = register_scalar_field('ocean_model', 'sst_global', time, diag,&
1534  long_name='Global Area Average Sea Surface Temperature', &
1535  units='degC', standard_name='sea_surface_temperature', &
1536  cmor_field_name='tosga', cmor_standard_name='sea_surface_temperature', &
1537  cmor_long_name='Sea Surface Temperature')
1538  cs%id_sosga = register_scalar_field('ocean_model', 'sss_global', time, diag,&
1539  long_name='Global Area Average Sea Surface Salinity', &
1540  units='psu', standard_name='sea_surface_salinity', &
1541  cmor_field_name='sosga', cmor_standard_name='sea_surface_salinity', &
1542  cmor_long_name='Sea Surface Salinity')
1543  endif
1544 
1545  cs%id_u = register_diag_field('ocean_model', 'u', diag%axesCuL, time, &
1546  'Zonal velocity', 'm s-1', cmor_field_name='uo', &
1547  cmor_standard_name='sea_water_x_velocity', cmor_long_name='Sea Water X Velocity')
1548  cs%id_v = register_diag_field('ocean_model', 'v', diag%axesCvL, time, &
1549  'Meridional velocity', 'm s-1', cmor_field_name='vo', &
1550  cmor_standard_name='sea_water_y_velocity', cmor_long_name='Sea Water Y Velocity')
1551  cs%id_h = register_diag_field('ocean_model', 'h', diag%axesTL, time, &
1552  'Layer Thickness', thickness_units, v_extensive=.true., conversion=convert_h)
1553 
1554  cs%id_e = register_diag_field('ocean_model', 'e', diag%axesTi, time, &
1555  'Interface Height Relative to Mean Sea Level', 'm', conversion=us%Z_to_m)
1556  if (cs%id_e>0) call safe_alloc_ptr(cs%e,isd,ied,jsd,jed,nz+1)
1557 
1558  cs%id_e_D = register_diag_field('ocean_model', 'e_D', diag%axesTi, time, &
1559  'Interface Height above the Seafloor', 'm', conversion=us%Z_to_m)
1560  if (cs%id_e_D>0) call safe_alloc_ptr(cs%e_D,isd,ied,jsd,jed,nz+1)
1561 
1562  cs%id_Rml = register_diag_field('ocean_model', 'Rml', diag%axesTL, time, &
1563  'Mixed Layer Coordinate Potential Density', 'kg m-3')
1564 
1565  cs%id_Rcv = register_diag_field('ocean_model', 'Rho_cv', diag%axesTL, time, &
1566  'Coordinate Potential Density', 'kg m-3')
1567 
1568  cs%id_rhopot0 = register_diag_field('ocean_model', 'rhopot0', diag%axesTL, time, &
1569  'Potential density referenced to surface', 'kg m-3')
1570  cs%id_rhopot2 = register_diag_field('ocean_model', 'rhopot2', diag%axesTL, time, &
1571  'Potential density referenced to 2000 dbar', 'kg m-3')
1572  cs%id_rhoinsitu = register_diag_field('ocean_model', 'rhoinsitu', diag%axesTL, time, &
1573  'In situ density', 'kg m-3')
1574 
1575  cs%id_du_dt = register_diag_field('ocean_model', 'dudt', diag%axesCuL, time, &
1576  'Zonal Acceleration', 'm s-2')
1577  if ((cs%id_du_dt>0) .and. .not.associated(cs%du_dt)) then
1578  call safe_alloc_ptr(cs%du_dt,isdb,iedb,jsd,jed,nz)
1579  call register_time_deriv(lbound(mis%u), mis%u, cs%du_dt, cs)
1580  endif
1581 
1582  cs%id_dv_dt = register_diag_field('ocean_model', 'dvdt', diag%axesCvL, time, &
1583  'Meridional Acceleration', 'm s-2')
1584  if ((cs%id_dv_dt>0) .and. .not.associated(cs%dv_dt)) then
1585  call safe_alloc_ptr(cs%dv_dt,isd,ied,jsdb,jedb,nz)
1586  call register_time_deriv(lbound(mis%v), mis%v, cs%dv_dt, cs)
1587  endif
1588 
1589  cs%id_dh_dt = register_diag_field('ocean_model', 'dhdt', diag%axesTL, time, &
1590  'Thickness tendency', trim(thickness_units)//" s-1", v_extensive = .true.)
1591  if ((cs%id_dh_dt>0) .and. .not.associated(cs%dh_dt)) then
1592  call safe_alloc_ptr(cs%dh_dt,isd,ied,jsd,jed,nz)
1593  call register_time_deriv(lbound(mis%h), mis%h, cs%dh_dt, cs)
1594  endif
1595 
1596  ! layer thickness variables
1597  !if (GV%nk_rho_varies > 0) then
1598  cs%id_h_Rlay = register_diag_field('ocean_model', 'h_rho', diag%axesTL, time, &
1599  'Layer thicknesses in pure potential density coordinates', thickness_units, &
1600  conversion=convert_h)
1601  if (cs%id_h_Rlay>0) call safe_alloc_ptr(cs%h_Rlay,isd,ied,jsd,jed,nz)
1602 
1603  cs%id_uh_Rlay = register_diag_field('ocean_model', 'uh_rho', diag%axesCuL, time, &
1604  'Zonal volume transport in pure potential density coordinates', flux_units, &
1605  conversion=convert_h)
1606  if (cs%id_uh_Rlay>0) call safe_alloc_ptr(cs%uh_Rlay,isdb,iedb,jsd,jed,nz)
1607 
1608  cs%id_vh_Rlay = register_diag_field('ocean_model', 'vh_rho', diag%axesCvL, time, &
1609  'Meridional volume transport in pure potential density coordinates', flux_units, &
1610  conversion=convert_h)
1611  if (cs%id_vh_Rlay>0) call safe_alloc_ptr(cs%vh_Rlay,isd,ied,jsdb,jedb,nz)
1612 
1613  cs%id_uhGM_Rlay = register_diag_field('ocean_model', 'uhGM_rho', diag%axesCuL, time, &
1614  'Zonal volume transport due to interface height diffusion in pure potential &
1615  &density coordinates', flux_units, conversion=convert_h)
1616  if (cs%id_uhGM_Rlay>0) call safe_alloc_ptr(cs%uhGM_Rlay,isdb,iedb,jsd,jed,nz)
1617 
1618  cs%id_vhGM_Rlay = register_diag_field('ocean_model', 'vhGM_rho', diag%axesCvL, time, &
1619  'Meridional volume transport due to interface height diffusion in pure &
1620  &potential density coordinates', flux_units, conversion=convert_h)
1621  if (cs%id_vhGM_Rlay>0) call safe_alloc_ptr(cs%vhGM_Rlay,isd,ied,jsdb,jedb,nz)
1622  !endif
1623 
1624 
1625  ! terms in the kinetic energy budget
1626  cs%id_KE = register_diag_field('ocean_model', 'KE', diag%axesTL, time, &
1627  'Layer kinetic energy per unit mass', 'm2 s-2')
1628  if (cs%id_KE>0) call safe_alloc_ptr(cs%KE,isd,ied,jsd,jed,nz)
1629 
1630  cs%id_dKEdt = register_diag_field('ocean_model', 'dKE_dt', diag%axesTL, time, &
1631  'Kinetic Energy Tendency of Layer', 'm3 s-3')
1632  if (cs%id_dKEdt>0) call safe_alloc_ptr(cs%dKE_dt,isd,ied,jsd,jed,nz)
1633 
1634  cs%id_PE_to_KE = register_diag_field('ocean_model', 'PE_to_KE', diag%axesTL, time, &
1635  'Potential to Kinetic Energy Conversion of Layer', 'm3 s-3')
1636  if (cs%id_PE_to_KE>0) call safe_alloc_ptr(cs%PE_to_KE,isd,ied,jsd,jed,nz)
1637 
1638  cs%id_KE_Coradv = register_diag_field('ocean_model', 'KE_Coradv', diag%axesTL, time, &
1639  'Kinetic Energy Source from Coriolis and Advection', 'm3 s-3')
1640  if (cs%id_KE_Coradv>0) call safe_alloc_ptr(cs%KE_Coradv,isd,ied,jsd,jed,nz)
1641 
1642  cs%id_KE_adv = register_diag_field('ocean_model', 'KE_adv', diag%axesTL, time, &
1643  'Kinetic Energy Source from Advection', 'm3 s-3')
1644  if (cs%id_KE_adv>0) call safe_alloc_ptr(cs%KE_adv,isd,ied,jsd,jed,nz)
1645 
1646  cs%id_KE_visc = register_diag_field('ocean_model', 'KE_visc', diag%axesTL, time, &
1647  'Kinetic Energy Source from Vertical Viscosity and Stresses', 'm3 s-3')
1648  if (cs%id_KE_visc>0) call safe_alloc_ptr(cs%KE_visc,isd,ied,jsd,jed,nz)
1649 
1650  cs%id_KE_horvisc = register_diag_field('ocean_model', 'KE_horvisc', diag%axesTL, time, &
1651  'Kinetic Energy Source from Horizontal Viscosity', 'm3 s-3')
1652  if (cs%id_KE_horvisc>0) call safe_alloc_ptr(cs%KE_horvisc,isd,ied,jsd,jed,nz)
1653 
1654  if (.not. adiabatic) then
1655  cs%id_KE_dia = register_diag_field('ocean_model', 'KE_dia', diag%axesTL, time, &
1656  'Kinetic Energy Source from Diapycnal Diffusion', 'm3 s-3')
1657  if (cs%id_KE_dia>0) call safe_alloc_ptr(cs%KE_dia,isd,ied,jsd,jed,nz)
1658  endif
1659 
1660  ! gravity wave CFLs
1661  cs%id_cg1 = register_diag_field('ocean_model', 'cg1', diag%axesT1, time, &
1662  'First baroclinic gravity wave speed', 'm s-1')
1663  cs%id_Rd1 = register_diag_field('ocean_model', 'Rd1', diag%axesT1, time, &
1664  'First baroclinic deformation radius', 'm')
1665  cs%id_cfl_cg1 = register_diag_field('ocean_model', 'CFL_cg1', diag%axesT1, time, &
1666  'CFL of first baroclinic gravity wave = dt*cg1*(1/dx+1/dy)', 'nondim')
1667  cs%id_cfl_cg1_x = register_diag_field('ocean_model', 'CFL_cg1_x', diag%axesT1, time, &
1668  'i-component of CFL of first baroclinic gravity wave = dt*cg1*/dx', 'nondim')
1669  cs%id_cfl_cg1_y = register_diag_field('ocean_model', 'CFL_cg1_y', diag%axesT1, time, &
1670  'j-component of CFL of first baroclinic gravity wave = dt*cg1*/dy', 'nondim')
1671  cs%id_cg_ebt = register_diag_field('ocean_model', 'cg_ebt', diag%axesT1, time, &
1672  'Equivalent barotropic gravity wave speed', 'm s-1')
1673  cs%id_Rd_ebt = register_diag_field('ocean_model', 'Rd_ebt', diag%axesT1, time, &
1674  'Equivalent barotropic deformation radius', 'm')
1675  cs%id_p_ebt = register_diag_field('ocean_model', 'p_ebt', diag%axesTL, time, &
1676  'Equivalent barotropic modal strcuture', 'nondim')
1677 
1678  if ((cs%id_cg1>0) .or. (cs%id_Rd1>0) .or. (cs%id_cfl_cg1>0) .or. &
1679  (cs%id_cfl_cg1_x>0) .or. (cs%id_cfl_cg1_y>0) .or. &
1680  (cs%id_cg_ebt>0) .or. (cs%id_Rd_ebt>0) .or. (cs%id_p_ebt>0)) then
1681  call wave_speed_init(cs%wave_speed_CSp)
1682  call safe_alloc_ptr(cs%cg1,isd,ied,jsd,jed)
1683  if (cs%id_Rd1>0) call safe_alloc_ptr(cs%Rd1,isd,ied,jsd,jed)
1684  if (cs%id_Rd_ebt>0) call safe_alloc_ptr(cs%Rd1,isd,ied,jsd,jed)
1685  if (cs%id_cfl_cg1>0) call safe_alloc_ptr(cs%cfl_cg1,isd,ied,jsd,jed)
1686  if (cs%id_cfl_cg1_x>0) call safe_alloc_ptr(cs%cfl_cg1_x,isd,ied,jsd,jed)
1687  if (cs%id_cfl_cg1_y>0) call safe_alloc_ptr(cs%cfl_cg1_y,isd,ied,jsd,jed)
1688  if (cs%id_p_ebt>0) call safe_alloc_ptr(cs%p_ebt,isd,ied,jsd,jed,nz)
1689  endif
1690 
1691  cs%id_mass_wt = register_diag_field('ocean_model', 'mass_wt', diag%axesT1, time, &
1692  'The column mass for calculating mass-weighted average properties', 'kg m-2')
1693 
1694  if (use_temperature) then
1695  cs%id_temp_int = register_diag_field('ocean_model', 'temp_int', diag%axesT1, time, &
1696  'Density weighted column integrated potential temperature', 'degC kg m-2', &
1697  cmor_field_name='opottempmint', &
1698  cmor_long_name='integral_wrt_depth_of_product_of_sea_water_density_and_potential_temperature',&
1699  cmor_standard_name='Depth integrated density times potential temperature')
1700 
1701  cs%id_salt_int = register_diag_field('ocean_model', 'salt_int', diag%axesT1, time, &
1702  'Density weighted column integrated salinity', 'psu kg m-2', &
1703  cmor_field_name='somint', &
1704  cmor_long_name='integral_wrt_depth_of_product_of_sea_water_density_and_salinity',&
1705  cmor_standard_name='Depth integrated density times salinity')
1706  endif
1707 
1708  cs%id_col_mass = register_diag_field('ocean_model', 'col_mass', diag%axesT1, time, &
1709  'The column integrated in situ density', 'kg m-2')
1710 
1711  cs%id_col_ht = register_diag_field('ocean_model', 'col_height', diag%axesT1, time, &
1712  'The height of the water column', 'm', conversion=us%Z_to_m)
1713  cs%id_pbo = register_diag_field('ocean_model', 'pbo', diag%axesT1, time, &
1714  long_name='Sea Water Pressure at Sea Floor', standard_name='sea_water_pressure_at_sea_floor', &
1715  units='Pa')
1716 
1717  call set_dependent_diagnostics(mis, adp, cdp, g, cs)
1718 
1719 end subroutine mom_diagnostics_init
1720 
1721 
1722 !> Register diagnostics of the surface state and integrated quantities
1723 subroutine register_surface_diags(Time, G, IDs, diag, tv)
1724  type(time_type), intent(in) :: time !< current model time
1725  type(ocean_grid_type), intent(in) :: g !< ocean grid structure
1726  type(surface_diag_ids), intent(inout) :: ids !< A structure with the diagnostic IDs.
1727  type(diag_ctrl), intent(inout) :: diag !< regulates diagnostic output
1728  type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various thermodynamic variables
1729 
1730  ! Vertically integrated, budget, and surface state diagnostics
1731  ids%id_volo = register_scalar_field('ocean_model', 'volo', time, diag,&
1732  long_name='Total volume of liquid ocean', units='m3', &
1733  standard_name='sea_water_volume')
1734  ids%id_zos = register_diag_field('ocean_model', 'zos', diag%axesT1, time,&
1735  standard_name = 'sea_surface_height_above_geoid', &
1736  long_name= 'Sea surface height above geoid', units='m')
1737  ids%id_zossq = register_diag_field('ocean_model', 'zossq', diag%axesT1, time,&
1738  standard_name='square_of_sea_surface_height_above_geoid', &
1739  long_name='Square of sea surface height above geoid', units='m2')
1740  ids%id_ssh = register_diag_field('ocean_model', 'SSH', diag%axesT1, time, &
1741  'Sea Surface Height', 'm')
1742  ids%id_ssh_ga = register_scalar_field('ocean_model', 'ssh_ga', time, diag,&
1743  long_name='Area averaged sea surface height', units='m', &
1744  standard_name='area_averaged_sea_surface_height')
1745  ids%id_ssu = register_diag_field('ocean_model', 'SSU', diag%axesCu1, time, &
1746  'Sea Surface Zonal Velocity', 'm s-1')
1747  ids%id_ssv = register_diag_field('ocean_model', 'SSV', diag%axesCv1, time, &
1748  'Sea Surface Meridional Velocity', 'm s-1')
1749  ids%id_speed = register_diag_field('ocean_model', 'speed', diag%axesT1, time, &
1750  'Sea Surface Speed', 'm s-1')
1751 
1752  if (associated(tv%T)) then
1753  ids%id_sst = register_diag_field('ocean_model', 'SST', diag%axesT1, time, &
1754  'Sea Surface Temperature', 'degC', cmor_field_name='tos', &
1755  cmor_long_name='Sea Surface Temperature', &
1756  cmor_standard_name='sea_surface_temperature')
1757  ids%id_sst_sq = register_diag_field('ocean_model', 'SST_sq', diag%axesT1, time, &
1758  'Sea Surface Temperature Squared', 'degC2', cmor_field_name='tossq', &
1759  cmor_long_name='Square of Sea Surface Temperature ', &
1760  cmor_standard_name='square_of_sea_surface_temperature')
1761  ids%id_sss = register_diag_field('ocean_model', 'SSS', diag%axesT1, time, &
1762  'Sea Surface Salinity', 'psu', cmor_field_name='sos', &
1763  cmor_long_name='Sea Surface Salinity', &
1764  cmor_standard_name='sea_surface_salinity')
1765  ids%id_sss_sq = register_diag_field('ocean_model', 'SSS_sq', diag%axesT1, time, &
1766  'Sea Surface Salinity Squared', 'psu', cmor_field_name='sossq', &
1767  cmor_long_name='Square of Sea Surface Salinity ', &
1768  cmor_standard_name='square_of_sea_surface_salinity')
1769  if (tv%T_is_conT) then
1770  ids%id_sstcon = register_diag_field('ocean_model', 'conSST', diag%axesT1, time, &
1771  'Sea Surface Conservative Temperature', 'Celsius')
1772  endif
1773  if (tv%S_is_absS) then
1774  ids%id_sssabs = register_diag_field('ocean_model', 'absSSS', diag%axesT1, time, &
1775  'Sea Surface Absolute Salinity', 'g kg-1')
1776  endif
1777  if (associated(tv%frazil)) then
1778  ids%id_fraz = register_diag_field('ocean_model', 'frazil', diag%axesT1, time, &
1779  'Heat from frazil formation', 'W m-2', cmor_field_name='hfsifrazil', &
1780  cmor_standard_name='heat_flux_into_sea_water_due_to_frazil_ice_formation', &
1781  cmor_long_name='Heat Flux into Sea Water due to Frazil Ice Formation')
1782  endif
1783  endif
1784 
1785  ids%id_salt_deficit = register_diag_field('ocean_model', 'salt_deficit', diag%axesT1, time, &
1786  'Salt sink in ocean due to ice flux', 'psu m-2 s-1')
1787  ids%id_Heat_PmE = register_diag_field('ocean_model', 'Heat_PmE', diag%axesT1, time, &
1788  'Heat flux into ocean from mass flux into ocean', 'W m-2')
1789  ids%id_intern_heat = register_diag_field('ocean_model', 'internal_heat', diag%axesT1, time,&
1790  'Heat flux into ocean from geothermal or other internal sources', 'W m-2')
1791 
1792 end subroutine register_surface_diags
1793 
1794 !> Register certain diagnostics related to transports
1795 subroutine register_transport_diags(Time, G, GV, IDs, diag)
1796  type(time_type), intent(in) :: time !< current model time
1797  type(ocean_grid_type), intent(in) :: g !< ocean grid structure
1798  type(verticalgrid_type), intent(in) :: gv !< ocean vertical grid structure
1799  type(transport_diag_ids), intent(inout) :: ids !< A structure with the diagnostic IDs.
1800  type(diag_ctrl), intent(inout) :: diag !< regulates diagnostic output
1801 
1802  real :: h_convert
1803  character(len=48) :: thickness_units
1804 
1805  thickness_units = get_thickness_units(gv)
1806  if (gv%Boussinesq) then
1807  h_convert = gv%H_to_m
1808  else
1809  h_convert = gv%H_to_kg_m2
1810  endif
1811 
1812  ! Diagnostics related to tracer and mass transport
1813  ids%id_uhtr = register_diag_field('ocean_model', 'uhtr', diag%axesCuL, time, &
1814  'Accumulated zonal thickness fluxes to advect tracers', 'kg', &
1815  y_cell_method='sum', v_extensive=.true., conversion=h_convert)
1816  ids%id_vhtr = register_diag_field('ocean_model', 'vhtr', diag%axesCvL, time, &
1817  'Accumulated meridional thickness fluxes to advect tracers', 'kg', &
1818  x_cell_method='sum', v_extensive=.true., conversion=h_convert)
1819  ids%id_umo = register_diag_field('ocean_model', 'umo', &
1820  diag%axesCuL, time, 'Ocean Mass X Transport', 'kg s-1', &
1821  standard_name='ocean_mass_x_transport', y_cell_method='sum', v_extensive=.true.)
1822  ids%id_vmo = register_diag_field('ocean_model', 'vmo', &
1823  diag%axesCvL, time, 'Ocean Mass Y Transport', 'kg s-1', &
1824  standard_name='ocean_mass_y_transport', x_cell_method='sum', v_extensive=.true.)
1825  ids%id_umo_2d = register_diag_field('ocean_model', 'umo_2d', &
1826  diag%axesCu1, time, 'Ocean Mass X Transport Vertical Sum', 'kg s-1', &
1827  standard_name='ocean_mass_x_transport_vertical_sum', y_cell_method='sum')
1828  ids%id_vmo_2d = register_diag_field('ocean_model', 'vmo_2d', &
1829  diag%axesCv1, time, 'Ocean Mass Y Transport Vertical Sum', 'kg s-1', &
1830  standard_name='ocean_mass_y_transport_vertical_sum', x_cell_method='sum')
1831  ids%id_dynamics_h = register_diag_field('ocean_model','dynamics_h', &
1832  diag%axesTl, time, 'Change in layer thicknesses due to horizontal dynamics', &
1833  'm s-1', v_extensive = .true.)
1834  ids%id_dynamics_h_tendency = register_diag_field('ocean_model','dynamics_h_tendency', &
1835  diag%axesTl, time, 'Change in layer thicknesses due to horizontal dynamics', &
1836  'm s-1', v_extensive = .true.)
1837 
1838 end subroutine register_transport_diags
1839 
1840 !> Offers the static fields in the ocean grid type for output via the diag_manager.
1841 subroutine write_static_fields(G, GV, US, tv, diag)
1842  type(ocean_grid_type), intent(in) :: g !< ocean grid structure
1843  type(verticalgrid_type), intent(in) :: gv !< ocean vertical grid structure
1844  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
1845  type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various thermodynamic variables
1846  type(diag_ctrl), target, intent(inout) :: diag !< regulates diagnostic output
1847 
1848  ! Local variables
1849  integer :: id
1850 
1851  id = register_static_field('ocean_model', 'geolat', diag%axesT1, &
1852  'Latitude of tracer (T) points', 'degrees_north')
1853  if (id > 0) call post_data(id, g%geoLatT, diag, .true.)
1854 
1855  id = register_static_field('ocean_model', 'geolon', diag%axesT1, &
1856  'Longitude of tracer (T) points', 'degrees_east')
1857  if (id > 0) call post_data(id, g%geoLonT, diag, .true.)
1858 
1859  id = register_static_field('ocean_model', 'geolat_c', diag%axesB1, &
1860  'Latitude of corner (Bu) points', 'degrees_north', interp_method='none')
1861  if (id > 0) call post_data(id, g%geoLatBu, diag, .true.)
1862 
1863  id = register_static_field('ocean_model', 'geolon_c', diag%axesB1, &
1864  'Longitude of corner (Bu) points', 'degrees_east', interp_method='none')
1865  if (id > 0) call post_data(id, g%geoLonBu, diag, .true.)
1866 
1867  id = register_static_field('ocean_model', 'geolat_v', diag%axesCv1, &
1868  'Latitude of meridional velocity (Cv) points', 'degrees_north', interp_method='none')
1869  if (id > 0) call post_data(id, g%geoLatCv, diag, .true.)
1870 
1871  id = register_static_field('ocean_model', 'geolon_v', diag%axesCv1, &
1872  'Longitude of meridional velocity (Cv) points', 'degrees_east', interp_method='none')
1873  if (id > 0) call post_data(id, g%geoLonCv, diag, .true.)
1874 
1875  id = register_static_field('ocean_model', 'geolat_u', diag%axesCu1, &
1876  'Latitude of zonal velocity (Cu) points', 'degrees_north', interp_method='none')
1877  if (id > 0) call post_data(id, g%geoLatCu, diag, .true.)
1878 
1879  id = register_static_field('ocean_model', 'geolon_u', diag%axesCu1, &
1880  'Longitude of zonal velocity (Cu) points', 'degrees_east', interp_method='none')
1881  if (id > 0) call post_data(id, g%geoLonCu, diag, .true.)
1882 
1883  id = register_static_field('ocean_model', 'area_t', diag%axesT1, &
1884  'Surface area of tracer (T) cells', 'm2', &
1885  cmor_field_name='areacello', cmor_standard_name='cell_area', &
1886  cmor_long_name='Ocean Grid-Cell Area', &
1887  x_cell_method='sum', y_cell_method='sum', area_cell_method='sum')
1888  if (id > 0) then
1889  call post_data(id, g%areaT, diag, .true.)
1890  call diag_register_area_ids(diag, id_area_t=id)
1891  endif
1892 
1893  id = register_static_field('ocean_model', 'area_u', diag%axesCu1, &
1894  'Surface area of x-direction flow (U) cells', 'm2', &
1895  cmor_field_name='areacello_cu', cmor_standard_name='cell_area', &
1896  cmor_long_name='Ocean Grid-Cell Area', &
1897  x_cell_method='sum', y_cell_method='sum', area_cell_method='sum')
1898  if (id > 0) call post_data(id, g%areaCu, diag, .true.)
1899 
1900  id = register_static_field('ocean_model', 'area_v', diag%axesCv1, &
1901  'Surface area of y-direction flow (V) cells', 'm2', &
1902  cmor_field_name='areacello_cv', cmor_standard_name='cell_area', &
1903  cmor_long_name='Ocean Grid-Cell Area', &
1904  x_cell_method='sum', y_cell_method='sum', area_cell_method='sum')
1905  if (id > 0) call post_data(id, g%areaCv, diag, .true.)
1906 
1907  id = register_static_field('ocean_model', 'area_q', diag%axesB1, &
1908  'Surface area of B-grid flow (Q) cells', 'm2', &
1909  cmor_field_name='areacello_bu', cmor_standard_name='cell_area', &
1910  cmor_long_name='Ocean Grid-Cell Area', &
1911  x_cell_method='sum', y_cell_method='sum', area_cell_method='sum')
1912  if (id > 0) call post_data(id, g%areaBu, diag, .true.)
1913 
1914  id = register_static_field('ocean_model', 'depth_ocean', diag%axesT1, &
1915  'Depth of the ocean at tracer points', 'm', &
1916  standard_name='sea_floor_depth_below_geoid', &
1917  cmor_field_name='deptho', cmor_long_name='Sea Floor Depth', &
1918  cmor_standard_name='sea_floor_depth_below_geoid', area=diag%axesT1%id_area, &
1919  x_cell_method='mean', y_cell_method='mean', area_cell_method='mean', &
1920  conversion=us%Z_to_m)
1921  if (id > 0) call post_data(id, g%bathyT, diag, .true., mask=g%mask2dT)
1922 
1923  id = register_static_field('ocean_model', 'wet', diag%axesT1, &
1924  '0 if land, 1 if ocean at tracer points', 'none', area=diag%axesT1%id_area)
1925  if (id > 0) call post_data(id, g%mask2dT, diag, .true.)
1926 
1927  id = register_static_field('ocean_model', 'wet_c', diag%axesB1, &
1928  '0 if land, 1 if ocean at corner (Bu) points', 'none', interp_method='none')
1929  if (id > 0) call post_data(id, g%mask2dBu, diag, .true.)
1930 
1931  id = register_static_field('ocean_model', 'wet_u', diag%axesCu1, &
1932  '0 if land, 1 if ocean at zonal velocity (Cu) points', 'none', interp_method='none')
1933  if (id > 0) call post_data(id, g%mask2dCu, diag, .true.)
1934 
1935  id = register_static_field('ocean_model', 'wet_v', diag%axesCv1, &
1936  '0 if land, 1 if ocean at meridional velocity (Cv) points', 'none', interp_method='none')
1937  if (id > 0) call post_data(id, g%mask2dCv, diag, .true.)
1938 
1939  id = register_static_field('ocean_model', 'Coriolis', diag%axesB1, &
1940  'Coriolis parameter at corner (Bu) points', 's-1', interp_method='none', conversion=us%s_to_T)
1941  if (id > 0) call post_data(id, g%CoriolisBu, diag, .true.)
1942 
1943  id = register_static_field('ocean_model', 'dxt', diag%axesT1, &
1944  'Delta(x) at thickness/tracer points (meter)', 'm', interp_method='none')
1945  if (id > 0) call post_data(id, g%dxt, diag, .true.)
1946 
1947  id = register_static_field('ocean_model', 'dyt', diag%axesT1, &
1948  'Delta(y) at thickness/tracer points (meter)', 'm', interp_method='none')
1949  if (id > 0) call post_data(id, g%dyt, diag, .true.)
1950 
1951  id = register_static_field('ocean_model', 'dxCu', diag%axesCu1, &
1952  'Delta(x) at u points (meter)', 'm', interp_method='none')
1953  if (id > 0) call post_data(id, g%dxCu, diag, .true.)
1954 
1955  id = register_static_field('ocean_model', 'dyCu', diag%axesCu1, &
1956  'Delta(y) at u points (meter)', 'm', interp_method='none')
1957  if (id > 0) call post_data(id, g%dyCu, diag, .true.)
1958 
1959  id = register_static_field('ocean_model', 'dxCv', diag%axesCv1, &
1960  'Delta(x) at v points (meter)', 'm', interp_method='none')
1961  if (id > 0) call post_data(id, g%dxCv, diag, .true.)
1962 
1963  id = register_static_field('ocean_model', 'dyCv', diag%axesCv1, &
1964  'Delta(y) at v points (meter)', 'm', interp_method='none')
1965  if (id > 0) call post_data(id, g%dyCv, diag, .true.)
1966 
1967  id = register_static_field('ocean_model', 'dyCuo', diag%axesCu1, &
1968  'Open meridional grid spacing at u points (meter)', 'm', interp_method='none')
1969  if (id > 0) call post_data(id, g%dy_Cu, diag, .true.)
1970 
1971  id = register_static_field('ocean_model', 'dxCvo', diag%axesCv1, &
1972  'Open zonal grid spacing at v points (meter)', 'm', interp_method='none')
1973  if (id > 0) call post_data(id, g%dx_Cv, diag, .true.)
1974 
1975  id = register_static_field('ocean_model', 'sin_rot', diag%axesT1, &
1976  'sine of the clockwise angle of the ocean grid north to true north', 'none')
1977  if (id > 0) call post_data(id, g%sin_rot, diag, .true.)
1978 
1979  id = register_static_field('ocean_model', 'cos_rot', diag%axesT1, &
1980  'cosine of the clockwise angle of the ocean grid north to true north', 'none')
1981  if (id > 0) call post_data(id, g%cos_rot, diag, .true.)
1982 
1983 
1984  ! This static diagnostic is from CF 1.8, and is the fraction of a cell
1985  ! covered by ocean, given as a percentage (poorly named).
1986  id = register_static_field('ocean_model', 'area_t_percent', diag%axesT1, &
1987  'Percentage of cell area covered by ocean', '%', &
1988  cmor_field_name='sftof', cmor_standard_name='SeaAreaFraction', &
1989  cmor_long_name='Sea Area Fraction', &
1990  x_cell_method='mean', y_cell_method='mean', area_cell_method='mean', &
1991  conversion=100.0)
1992  if (id > 0) call post_data(id, g%mask2dT, diag, .true.)
1993 
1994 
1995  id = register_static_field('ocean_model','Rho_0', diag%axesNull, &
1996  'mean ocean density used with the Boussinesq approximation', &
1997  'kg m-3', cmor_field_name='rhozero', &
1998  cmor_standard_name='reference_sea_water_density_for_boussinesq_approximation', &
1999  cmor_long_name='reference sea water density for boussinesq approximation')
2000  if (id > 0) call post_data(id, gv%Rho0, diag, .true.)
2001 
2002  id = register_static_field('ocean_model','C_p', diag%axesNull, &
2003  'heat capacity of sea water', 'J kg-1 K-1', cmor_field_name='cpocean', &
2004  cmor_standard_name='specific_heat_capacity_of_sea_water', &
2005  cmor_long_name='specific_heat_capacity_of_sea_water')
2006  if (id > 0) call post_data(id, tv%C_p, diag, .true.)
2007 
2008 end subroutine write_static_fields
2009 
2010 
2011 !> This subroutine sets up diagnostics upon which other diagnostics depend.
2012 subroutine set_dependent_diagnostics(MIS, ADp, CDp, G, CS)
2013  type(ocean_internal_state), intent(in) :: MIS !< For "MOM Internal State" a set of pointers to
2014  !! the fields and accelerations making up ocean
2015  !! internal physical state.
2016  type(accel_diag_ptrs), intent(inout) :: ADp !< Structure pointing to accelerations in
2017  !! momentum equation.
2018  type(cont_diag_ptrs), intent(inout) :: CDp !< Structure pointing to terms in continuity
2019  !! equation.
2020  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure.
2021  type(diagnostics_cs), pointer :: CS !< Pointer to the control structure for this
2022  !! module.
2023 
2024 ! This subroutine sets up diagnostics upon which other diagnostics depend.
2025  integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB, nz
2026  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed ; nz = g%ke
2027  isdb = g%IsdB ; iedb = g%IedB ; jsdb = g%JsdB ; jedb = g%JedB
2028 
2029  if (associated(cs%dKE_dt) .or. associated(cs%PE_to_KE) .or. &
2030  associated(cs%KE_CorAdv) .or. associated(cs%KE_adv) .or. &
2031  associated(cs%KE_visc) .or. associated(cs%KE_horvisc) .or. &
2032  associated(cs%KE_dia)) &
2033  call safe_alloc_ptr(cs%KE,isd,ied,jsd,jed,nz)
2034 
2035  if (associated(cs%dKE_dt)) then
2036  if (.not.associated(cs%du_dt)) then
2037  call safe_alloc_ptr(cs%du_dt,isdb,iedb,jsd,jed,nz)
2038  call register_time_deriv(lbound(mis%u), mis%u, cs%du_dt, cs)
2039  endif
2040  if (.not.associated(cs%dv_dt)) then
2041  call safe_alloc_ptr(cs%dv_dt,isd,ied,jsdb,jedb,nz)
2042  call register_time_deriv(lbound(mis%v), mis%v, cs%dv_dt, cs)
2043  endif
2044  if (.not.associated(cs%dh_dt)) then
2045  call safe_alloc_ptr(cs%dh_dt,isd,ied,jsd,jed,nz)
2046  call register_time_deriv(lbound(mis%h), mis%h, cs%dh_dt, cs)
2047  endif
2048  endif
2049 
2050  if (associated(cs%KE_adv)) then
2051  call safe_alloc_ptr(adp%gradKEu,isdb,iedb,jsd,jed,nz)
2052  call safe_alloc_ptr(adp%gradKEv,isd,ied,jsdb,jedb,nz)
2053  endif
2054 
2055  if (associated(cs%KE_visc)) then
2056  call safe_alloc_ptr(adp%du_dt_visc,isdb,iedb,jsd,jed,nz)
2057  call safe_alloc_ptr(adp%dv_dt_visc,isd,ied,jsdb,jedb,nz)
2058  endif
2059 
2060  if (associated(cs%KE_dia)) then
2061  call safe_alloc_ptr(adp%du_dt_dia,isdb,iedb,jsd,jed,nz)
2062  call safe_alloc_ptr(adp%dv_dt_dia,isd,ied,jsdb,jedb,nz)
2063  endif
2064 
2065  if (associated(cs%uhGM_Rlay)) call safe_alloc_ptr(cdp%uhGM,isdb,iedb,jsd,jed,nz)
2066  if (associated(cs%vhGM_Rlay)) call safe_alloc_ptr(cdp%vhGM,isd,ied,jsdb,jedb,nz)
2067 
2068 end subroutine set_dependent_diagnostics
2069 
2070 !> Deallocate memory associated with the diagnostics module
2071 subroutine mom_diagnostics_end(CS, ADp)
2072  type(diagnostics_cs), pointer :: cs !< Control structure returned by a
2073  !! previous call to diagnostics_init.
2074  type(accel_diag_ptrs), intent(inout) :: adp !< structure with pointers to
2075  !! accelerations in momentum equation.
2076  integer :: m
2077 
2078  if (associated(cs%e)) deallocate(cs%e)
2079  if (associated(cs%e_D)) deallocate(cs%e_D)
2080  if (associated(cs%KE)) deallocate(cs%KE)
2081  if (associated(cs%dKE_dt)) deallocate(cs%dKE_dt)
2082  if (associated(cs%PE_to_KE)) deallocate(cs%PE_to_KE)
2083  if (associated(cs%KE_Coradv)) deallocate(cs%KE_Coradv)
2084  if (associated(cs%KE_adv)) deallocate(cs%KE_adv)
2085  if (associated(cs%KE_visc)) deallocate(cs%KE_visc)
2086  if (associated(cs%KE_horvisc)) deallocate(cs%KE_horvisc)
2087  if (associated(cs%KE_dia)) deallocate(cs%KE_dia)
2088  if (associated(cs%dv_dt)) deallocate(cs%dv_dt)
2089  if (associated(cs%dh_dt)) deallocate(cs%dh_dt)
2090  if (associated(cs%du_dt)) deallocate(cs%du_dt)
2091  if (associated(cs%h_Rlay)) deallocate(cs%h_Rlay)
2092  if (associated(cs%uh_Rlay)) deallocate(cs%uh_Rlay)
2093  if (associated(cs%vh_Rlay)) deallocate(cs%vh_Rlay)
2094  if (associated(cs%uhGM_Rlay)) deallocate(cs%uhGM_Rlay)
2095  if (associated(cs%vhGM_Rlay)) deallocate(cs%vhGM_Rlay)
2096 
2097  if (associated(adp%gradKEu)) deallocate(adp%gradKEu)
2098  if (associated(adp%gradKEu)) deallocate(adp%gradKEu)
2099  if (associated(adp%du_dt_visc)) deallocate(adp%du_dt_visc)
2100  if (associated(adp%dv_dt_visc)) deallocate(adp%dv_dt_visc)
2101  if (associated(adp%du_dt_dia)) deallocate(adp%du_dt_dia)
2102  if (associated(adp%dv_dt_dia)) deallocate(adp%dv_dt_dia)
2103  if (associated(adp%du_other)) deallocate(adp%du_other)
2104  if (associated(adp%dv_other)) deallocate(adp%dv_other)
2105 
2106  do m=1,cs%num_time_deriv ; deallocate(cs%prev_val(m)%p) ; enddo
2107 
2108  deallocate(cs)
2109 
2110 end subroutine mom_diagnostics_end
2111 
2112 end module mom_diagnostics
mom_diagnostics
Calculates any requested diagnostic quantities that are not calculated in the various subroutines....
Definition: MOM_diagnostics.F90:4
mom_variables::surface
Pointers to various fields which may be used describe the surface state of MOM, and which will be ret...
Definition: MOM_variables.F90:38
mom_spatial_means
Functions and routines to take area, volume, mass-weighted, layerwise, zonal or meridional means.
Definition: MOM_spatial_means.F90:2
mom_verticalgrid
Provides a transparent vertical ocean grid type and supporting routines.
Definition: MOM_verticalGrid.F90:2
mom_file_parser::log_version
An overloaded interface to log version information about modules.
Definition: MOM_file_parser.F90:109
mom_diag_mediator
The subroutines here provide convenient wrappers to the fms diag_manager interfaces with additional d...
Definition: MOM_diag_mediator.F90:3
mom_variables::thermo_var_ptrs
Pointers to an assortment of thermodynamic fields that may be available, including potential temperat...
Definition: MOM_variables.F90:82
mom_eos
Provides subroutines for quantities specific to the equation of state.
Definition: MOM_EOS.F90:2
mom_tracer_registry
This module contains the tracer_registry_type and the subroutines that handle registration of tracers...
Definition: MOM_tracer_registry.F90:5
mom_file_parser::param_file_type
A structure that can be parsed to read and document run-time parameters.
Definition: MOM_file_parser.F90:54
mom_file_parser::get_param
An overloaded interface to read and log the values of various types of parameters.
Definition: MOM_file_parser.F90:102
mom_diagnostics::diagnostics_cs
The control structure for the MOM_diagnostics module.
Definition: MOM_diagnostics.F90:50
mom_unit_scaling::unit_scale_type
Describes various unit conversion factors.
Definition: MOM_unit_scaling.F90:14
mom_coms
Interfaces to non-domain-oriented communication subroutines, including the MOM6 reproducing sums faci...
Definition: MOM_coms.F90:3
mom_diag_mediator::post_data
Make a diagnostic available for averaging or output.
Definition: MOM_diag_mediator.F90:70
mom_diag_mediator::diag_grid_storage
Stores all the remapping grids and the model's native space thicknesses.
Definition: MOM_diag_mediator.F90:146
mom_verticalgrid::verticalgrid_type
Describes the vertical ocean grid, including unit conversion factors.
Definition: MOM_verticalGrid.F90:24
mom_domains
Describes the decomposed MOM domain and has routines for communications across PEs.
Definition: MOM_domains.F90:2
mom_variables
Provides transparent structures with groups of MOM6 variables and supporting routines.
Definition: MOM_variables.F90:2
mom_wave_speed::wave_speed_cs
Control structure for MOM_wave_speed.
Definition: MOM_wave_speed.F90:28
mom_diagnostics::transport_diag_ids
A structure with diagnostic IDs of mass transport related diagnostics.
Definition: MOM_diagnostics.F90:175
mom_file_parser
The MOM6 facility to parse input files for runtime parameters.
Definition: MOM_file_parser.F90:2
mom_diagnostics::surface_diag_ids
A structure with diagnostic IDs of the surface and integrated variables.
Definition: MOM_diagnostics.F90:155
mom_variables::cont_diag_ptrs
Pointers to arrays with transports, which can later be used for derived diagnostics,...
Definition: MOM_variables.F90:185
mom_variables::accel_diag_ptrs
Pointers to arrays with accelerations, which can later be used for derived diagnostics,...
Definition: MOM_variables.F90:155
mom_tracer_registry::tracer_registry_type
Type to carry basic tracer information.
Definition: MOM_tracer_registry.F90:122
mom_interface_heights::find_eta
Calculates the heights of sruface or all interfaces from layer thicknesses.
Definition: MOM_interface_heights.F90:21
mom_grid
Provides the ocean grid type.
Definition: MOM_grid.F90:2
mom_unit_scaling
Provides a transparent unit rescaling type to facilitate dimensional consistency testing.
Definition: MOM_unit_scaling.F90:2
mom_wave_speed
Routines for calculating baroclinic wave speeds.
Definition: MOM_wave_speed.F90:2
mom_variables::p3d
A structure for creating arrays of pointers to 3D arrays.
Definition: MOM_variables.F90:28
mom_coms::reproducing_sum
Find an accurate and order-invariant sum of distributed 2d or 3d fields.
Definition: MOM_coms.F90:54
mom_variables::ocean_internal_state
Pointers to all of the prognostic variables allocated in MOM_variables.F90 and MOM....
Definition: MOM_variables.F90:126
mom_domains::create_group_pass
Set up a group of halo updates.
Definition: MOM_domains.F90:79
mom_error_handler
Routines for error handling and I/O management.
Definition: MOM_error_handler.F90:2
mom_interface_heights
Functions for calculating interface heights, including free surface height.
Definition: MOM_interface_heights.F90:2
mom_grid::ocean_grid_type
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:25
mom_diag_mediator::diag_ctrl
The following data type a list of diagnostic fields an their variants, as well as variables that cont...
Definition: MOM_diag_mediator.F90:239
mom_eos::calculate_density
Calculates density of sea water from T, S and P.
Definition: MOM_EOS.F90:60