MOM6
MOM_sum_output.F90
1 !> Reports integrated quantities for monitoring the model state
3 
4 ! This file is part of MOM6. See LICENSE.md for the license.
5 
6 use iso_fortran_env, only : int64
7 use mom_coms, only : sum_across_pes, pe_here, root_pe, num_pes, max_across_pes
8 use mom_coms, only : reproducing_sum, efp_to_real, real_to_efp
9 use mom_coms, only : efp_type, operator(+), operator(-), assignment(=)
10 use mom_error_handler, only : mom_error, fatal, warning, is_root_pe, mom_mesg
12 use mom_forcing_type, only : forcing
13 use mom_grid, only : ocean_grid_type
15 use mom_io, only : create_file, fieldtype, flush_file, open_file, reopen_file
16 use mom_io, only : file_exists, slasher, vardesc, var_desc, write_field, get_filename_appendix
17 use mom_io, only : append_file, ascii_file, single_file, writeonly_file
19 use mom_open_boundary, only : obc_direction_e, obc_direction_w, obc_direction_n, obc_direction_s
20 use mom_time_manager, only : time_type, get_time, get_date, set_time, operator(>)
21 use mom_time_manager, only : operator(+), operator(-), operator(*), operator(/)
22 use mom_time_manager, only : operator(/=), operator(<=), operator(>=), operator(<)
23 use mom_time_manager, only : get_calendar_type, time_type_to_real, no_calendar
24 use mom_tracer_flow_control, only : tracer_flow_control_cs, call_tracer_stocks
28 use mpp_mod, only : mpp_chksum
29 
30 use netcdf
31 
32 implicit none ; private
33 
34 #include <MOM_memory.h>
35 
36 public write_energy, accumulate_net_input, mom_sum_output_init
37 
38 ! A note on unit descriptions in comments: MOM6 uses units that can be rescaled for dimensional
39 ! consistency testing. These are noted in comments with units like Z, H, L, and T, along with
40 ! their mks counterparts with notation like "a velocity [Z T-1 ~> m s-1]". If the units
41 ! vary with the Boussinesq approximation, the Boussinesq variant is given first.
42 
43 integer, parameter :: num_fields = 17 !< Number of diagnostic fields
44 character (*), parameter :: depth_chksum_attr = "bathyT_checksum"
45  !< Checksum attribute name of G%bathyT
46  !! over the compute domain
47 character (*), parameter :: area_chksum_attr = "mask2dT_areaT_checksum"
48  !< Checksum attribute of name of
49  !! G%mask2dT * G%areaT over the compute
50  !! domain
51 
52 !> A list of depths and corresponding globally integrated ocean area at each
53 !! depth and the ocean volume below each depth.
54 type :: depth_list
55  real :: depth !< A depth [m].
56  real :: area !< The cross-sectional area of the ocean at that depth [m2].
57  real :: vol_below !< The ocean volume below that depth [m3].
58 end type depth_list
59 
60 !> The control structure for the MOM_sum_output module
61 type, public :: sum_output_cs ; private
62  type(depth_list), pointer, dimension(:) :: dl => null() !< The sorted depth list.
63  integer :: list_size !< length of sorting vector <= niglobal*njglobal
64 
65  integer, allocatable, dimension(:) :: lh
66  !< This saves the entry in DL with a volume just
67  !! less than the volume of fluid below the interface.
68  logical :: do_ape_calc !< If true, calculate the available potential energy of the
69  !! interfaces. Disabling this reduces the memory footprint of
70  !! high-PE-count models dramatically.
71  logical :: read_depth_list !< Read the depth list from a file if it exists
72  !! and write it if it doesn't.
73  character(len=200) :: depth_list_file !< The name of the depth list file.
74  real :: d_list_min_inc !< The minimum increment [Z ~> m], between the depths of the
75  !! entries in the depth-list file, 0 by default.
76  logical :: require_depth_list_chksum
77  !< Require matching checksums in Depth_list.nc when reading
78  !! the file.
79  logical :: update_depth_list_chksum
80  !< Automatically update the Depth_list.nc file if the
81  !! checksums are missing or do not match current values.
82  logical :: use_temperature !< If true, temperature and salinity are state variables.
83  real :: fresh_water_input !< The total mass of fresh water added by surface fluxes
84  !! since the last time that write_energy was called [kg].
85  real :: mass_prev !< The total ocean mass the last time that
86  !! write_energy was called [kg].
87  real :: salt_prev !< The total amount of salt in the ocean the last
88  !! time that write_energy was called [ppt kg].
89  real :: net_salt_input !< The total salt added by surface fluxes since the last
90  !! time that write_energy was called [ppt kg].
91  real :: heat_prev !< The total amount of heat in the ocean the last
92  !! time that write_energy was called [J].
93  real :: net_heat_input !< The total heat added by surface fluxes since the last
94  !! the last time that write_energy was called [J].
95  type(efp_type) :: fresh_water_in_efp !< An extended fixed point version of fresh_water_input
96  type(efp_type) :: net_salt_in_efp !< An extended fixed point version of net_salt_input
97  type(efp_type) :: net_heat_in_efp !< An extended fixed point version of net_heat_input
98  type(efp_type) :: heat_prev_efp !< An extended fixed point version of heat_prev
99  type(efp_type) :: salt_prev_efp !< An extended fixed point version of salt_prev
100  type(efp_type) :: mass_prev_efp !< An extended fixed point version of mass_prev
101  real :: dt !< The baroclinic dynamics time step [s].
102 
103  type(time_type) :: energysavedays !< The interval between writing the energies
104  !! and other integral quantities of the run.
105  type(time_type) :: energysavedays_geometric !< The starting interval for computing a geometric
106  !! progression of time deltas between calls to
107  !! write_energy. This interval will increase by a factor of 2.
108  !! after each call to write_energy.
109  logical :: energysave_geometric !< Logical to control whether calls to write_energy should
110  !! follow a geometric progression
111  type(time_type) :: write_energy_time !< The next time to write to the energy file.
112  type(time_type) :: geometric_end_time !< Time at which to stop the geometric progression
113  !! of calls to write_energy and revert to the standard
114  !! energysavedays interval
115 
116  real :: timeunit !< The length of the units for the time axis [s].
117  logical :: date_stamped_output !< If true, use dates (not times) in messages to stdout.
118  type(time_type) :: start_time !< The start time of the simulation.
119  ! Start_time is set in MOM_initialization.F90
120  integer, pointer :: ntrunc => null() !< The number of times the velocity has been
121  !! truncated since the last call to write_energy.
122  real :: max_energy !< The maximum permitted energy per unit mass. If there is
123  !! more energy than this, the model should stop [m2 s-2].
124  integer :: maxtrunc !< The number of truncations per energy save
125  !! interval at which the run is stopped.
126  logical :: write_stocks !< If true, write the integrated tracer amounts
127  !! to stdout when the energy files are written.
128  integer :: previous_calls = 0 !< The number of times write_energy has been called.
129  integer :: prev_n = 0 !< The value of n from the last call.
130  integer :: fileenergy_nc !< NetCDF id of the energy file.
131  integer :: fileenergy_ascii !< The unit number of the ascii version of the energy file.
132  type(fieldtype), dimension(NUM_FIELDS+MAX_FIELDS_) :: &
133  fields !< fieldtype variables for the output fields.
134  character(len=200) :: energyfile !< The name of the energy file with path.
135 end type sum_output_cs
136 
137 contains
138 
139 !> MOM_sum_output_init initializes the parameters and settings for the MOM_sum_output module.
140 subroutine mom_sum_output_init(G, US, param_file, directory, ntrnc, &
141  Input_start_time, CS)
142  type(ocean_grid_type), intent(in) :: g !< The ocean's grid structure.
143  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
144  type(param_file_type), intent(in) :: param_file !< A structure to parse for run-time
145  !! parameters.
146  character(len=*), intent(in) :: directory !< The directory where the energy file goes.
147  integer, target, intent(inout) :: ntrnc !< The integer that stores the number of times
148  !! the velocity has been truncated since the
149  !! last call to write_energy.
150  type(time_type), intent(in) :: input_start_time !< The start time of the simulation.
151  type(sum_output_cs), pointer :: cs !< A pointer that is set to point to the
152  !! control structure for this module.
153  ! Local variables
154  real :: time_unit ! The time unit in seconds for ENERGYSAVEDAYS.
155  real :: rho_0 ! A reference density [kg m-3]
156  real :: maxvel ! The maximum permitted velocity [m s-1]
157 ! This include declares and sets the variable "version".
158 #include "version_variable.h"
159  character(len=40) :: mdl = "MOM_sum_output" ! This module's name.
160  character(len=200) :: energyfile ! The name of the energy file.
161  character(len=32) :: filename_appendix = '' !fms appendix to filename for ensemble runs
162 
163  if (associated(cs)) then
164  call mom_error(warning, "MOM_sum_output_init called with associated control structure.")
165  return
166  endif
167  allocate(cs)
168 
169  ! Read all relevant parameters and write them to the model log.
170  call log_version(param_file, mdl, version, "")
171  call get_param(param_file, mdl, "CALCULATE_APE", cs%do_APE_calc, &
172  "If true, calculate the available potential energy of "//&
173  "the interfaces. Setting this to false reduces the "//&
174  "memory footprint of high-PE-count models dramatically.", &
175  default=.true.)
176  call get_param(param_file, mdl, "WRITE_STOCKS", cs%write_stocks, &
177  "If true, write the integrated tracer amounts to stdout "//&
178  "when the energy files are written.", default=.true.)
179  call get_param(param_file, mdl, "ENABLE_THERMODYNAMICS", cs%use_temperature, &
180  "If true, Temperature and salinity are used as state "//&
181  "variables.", default=.true.)
182  call get_param(param_file, mdl, "DT", cs%dt, &
183  "The (baroclinic) dynamics time step.", units="s", &
184  fail_if_missing=.true.)
185  call get_param(param_file, mdl, "MAXTRUNC", cs%maxtrunc, &
186  "The run will be stopped, and the day set to a very "//&
187  "large value if the velocity is truncated more than "//&
188  "MAXTRUNC times between energy saves. Set MAXTRUNC to 0 "//&
189  "to stop if there is any truncation of velocities.", &
190  units="truncations save_interval-1", default=0)
191 
192  call get_param(param_file, mdl, "MAX_ENERGY", cs%max_Energy, &
193  "The maximum permitted average energy per unit mass; the "//&
194  "model will be stopped if there is more energy than "//&
195  "this. If zero or negative, this is set to 10*MAXVEL^2.", &
196  units="m2 s-2", default=0.0)
197  if (cs%max_Energy <= 0.0) then
198  call get_param(param_file, mdl, "MAXVEL", maxvel, &
199  "The maximum velocity allowed before the velocity "//&
200  "components are truncated.", units="m s-1", default=3.0e8)
201  cs%max_Energy = 10.0 * maxvel**2
202  call log_param(param_file, mdl, "MAX_ENERGY as used", cs%max_Energy)
203  endif
204 
205  call get_param(param_file, mdl, "ENERGYFILE", energyfile, &
206  "The file to use to write the energies and globally "//&
207  "summed diagnostics.", default="ocean.stats")
208 
209  !query fms_io if there is a filename_appendix (for ensemble runs)
210  call get_filename_appendix(filename_appendix)
211  if (len_trim(filename_appendix) > 0) then
212  energyfile = trim(energyfile) //'.'//trim(filename_appendix)
213  endif
214 
215  cs%energyfile = trim(slasher(directory))//trim(energyfile)
216  call log_param(param_file, mdl, "output_path/ENERGYFILE", cs%energyfile)
217 #ifdef STATSLABEL
218  cs%energyfile = trim(cs%energyfile)//"."//trim(adjustl(statslabel))
219 #endif
220 
221  call get_param(param_file, mdl, "DATE_STAMPED_STDOUT", cs%date_stamped_output, &
222  "If true, use dates (not times) in messages to stdout", &
223  default=.true.)
224  call get_param(param_file, mdl, "TIMEUNIT", cs%Timeunit, &
225  "The time unit in seconds a number of input fields", &
226  units="s", default=86400.0)
227  if (cs%Timeunit < 0.0) cs%Timeunit = 86400.0
228 
229 
230 
231  if (cs%do_APE_calc) then
232  call get_param(param_file, mdl, "READ_DEPTH_LIST", cs%read_depth_list, &
233  "Read the depth list from a file if it exists or "//&
234  "create that file otherwise.", default=.false.)
235  call get_param(param_file, mdl, "DEPTH_LIST_MIN_INC", cs%D_list_min_inc, &
236  "The minimum increment between the depths of the "//&
237  "entries in the depth-list file.", &
238  units="m", default=1.0e-10, scale=us%m_to_Z)
239  if (cs%read_depth_list) then
240  call get_param(param_file, mdl, "DEPTH_LIST_FILE", cs%depth_list_file, &
241  "The name of the depth list file.", default="Depth_list.nc")
242  if (scan(cs%depth_list_file,'/') == 0) &
243  cs%depth_list_file = trim(slasher(directory))//trim(cs%depth_list_file)
244 
245  call get_param(param_file, mdl, "REQUIRE_DEPTH_LIST_CHECKSUMS", &
246  cs%require_depth_list_chksum, &
247  "Require that matching checksums be in Depth_list.nc "//&
248  "when reading the file.", default=.true.)
249  if (.not. cs%require_depth_list_chksum) &
250  call get_param(param_file, mdl, "UPDATE_DEPTH_LIST_CHECKSUMS", &
251  cs%update_depth_list_chksum, &
252  "Automatically update the Depth_list.nc file if the "//&
253  "checksums are missing or do not match current values.", &
254  default=.false.)
255  endif
256 
257  allocate(cs%lH(g%ke))
258  call depth_list_setup(g, us, cs)
259  else
260  cs%list_size = 0
261  endif
262 
263  call get_param(param_file, mdl, "TIMEUNIT", time_unit, &
264  "The time unit for ENERGYSAVEDAYS.", &
265  units="s", default=86400.0)
266  call get_param(param_file, mdl, "ENERGYSAVEDAYS",cs%energysavedays, &
267  "The interval in units of TIMEUNIT between saves of the "//&
268  "energies of the run and other globally summed diagnostics.",&
269  default=set_time(0,days=1), timeunit=time_unit)
270  call get_param(param_file, mdl, "ENERGYSAVEDAYS_GEOMETRIC",cs%energysavedays_geometric, &
271  "The starting interval in units of TIMEUNIT for the first call "//&
272  "to save the energies of the run and other globally summed diagnostics. "//&
273  "The interval increases by a factor of 2. after each call to write_energy.",&
274  default=set_time(seconds=0), timeunit=time_unit)
275 
276  if ((time_type_to_real(cs%energysavedays_geometric) > 0.) .and. &
277  (cs%energysavedays_geometric < cs%energysavedays)) then
278  cs%energysave_geometric = .true.
279  else
280  cs%energysave_geometric = .false.
281  endif
282 
283  cs%Start_time = input_start_time
284  cs%ntrunc => ntrnc
285 
286 end subroutine mom_sum_output_init
287 
288 !> MOM_sum_output_end deallocates memory used by the MOM_sum_output module.
289 subroutine mom_sum_output_end(CS)
290  type(sum_output_cs), pointer :: CS !< The control structure returned by a
291  !! previous call to MOM_sum_output_init.
292  if (associated(cs)) then
293  if (cs%do_APE_calc) then
294  deallocate(cs%lH, cs%DL)
295  endif
296 
297  deallocate(cs)
298  endif
299 end subroutine mom_sum_output_end
300 
301 !> This subroutine calculates and writes the total model energy, the energy and
302 !! mass of each layer, and other globally integrated physical quantities.
303 subroutine write_energy(u, v, h, tv, day, n, G, GV, US, CS, tracer_CSp, OBC, dt_forcing)
304  type(ocean_grid_type), intent(in) :: g !< The ocean's grid structure.
305  type(verticalgrid_type), intent(in) :: gv !< The ocean's vertical grid structure.
306  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
307  real, dimension(SZIB_(G),SZJ_(G),SZK_(G)), &
308  intent(in) :: u !< The zonal velocity [m s-1].
309  real, dimension(SZI_(G),SZJB_(G),SZK_(G)), &
310  intent(in) :: v !< The meridional velocity [m s-1].
311  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), &
312  intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2].
313  type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various
314  !! thermodynamic variables.
315  type(time_type), intent(in) :: day !< The current model time.
316  integer, intent(in) :: n !< The time step number of the
317  !! current execution.
318  type(sum_output_cs), pointer :: cs !< The control structure returned by a
319  !! previous call to MOM_sum_output_init.
320  type(tracer_flow_control_cs), &
321  optional, pointer :: tracer_csp !< tracer control structure.
322  type(ocean_obc_type), &
323  optional, pointer :: obc !< Open boundaries control structure.
324  type(time_type), optional, intent(in) :: dt_forcing !< The forcing time step
325  ! Local variables
326  real :: eta(szi_(g),szj_(g),szk_(g)+1) ! The height of interfaces [Z ~> m].
327  real :: areatm(szi_(g),szj_(g)) ! A masked version of areaT [m2].
328  real :: ke(szk_(g)) ! The total kinetic energy of a layer [J].
329  real :: pe(szk_(g)+1)! The available potential energy of an interface [J].
330  real :: ke_tot ! The total kinetic energy [J].
331  real :: pe_tot ! The total available potential energy [J].
332  real :: z_0ape(szk_(g)+1) ! The uniform depth which overlies the same
333  ! volume as is below an interface [Z ~> m].
334  real :: h_0ape(szk_(g)+1) ! A version of Z_0APE, converted to m, usually positive.
335  real :: toten ! The total kinetic & potential energies of
336  ! all layers [J] (i.e. kg m2 s-2).
337  real :: en_mass ! The total kinetic and potential energies divided by
338  ! the total mass of the ocean [m2 s-2].
339  real :: vol_lay(szk_(g)) ! The volume of fluid in a layer [Z m2 ~> m3].
340  real :: volbelow ! The volume of all layers beneath an interface [Z m2 ~> m3].
341  real :: mass_lay(szk_(g)) ! The mass of fluid in a layer [kg].
342  real :: mass_tot ! The total mass of the ocean [kg].
343  real :: vol_tot ! The total ocean volume [m3].
344  real :: mass_chg ! The change in total ocean mass of fresh water since
345  ! the last call to this subroutine [kg].
346  real :: mass_anom ! The change in fresh water that cannot be accounted for
347  ! by the surface fluxes [kg].
348  real :: salt ! The total amount of salt in the ocean [ppt kg].
349  real :: salt_chg ! The change in total ocean salt since the last call
350  ! to this subroutine [ppt kg].
351  real :: salt_anom ! The change in salt that cannot be accounted for by
352  ! the surface fluxes [ppt kg].
353  real :: salin ! The mean salinity of the ocean [ppt].
354  real :: salin_chg ! The change in total salt since the last call
355  ! to this subroutine divided by total mass [ppt].
356  real :: salin_anom ! The change in total salt that cannot be accounted for by
357  ! the surface fluxes divided by total mass [ppt].
358  real :: salin_mass_in ! The mass of salt input since the last call [kg].
359  real :: heat ! The total amount of Heat in the ocean [J].
360  real :: heat_chg ! The change in total ocean heat since the last call
361  ! to this subroutine [J].
362  real :: heat_anom ! The change in heat that cannot be accounted for by
363  ! the surface fluxes [J].
364  real :: temp ! The mean potential temperature of the ocean [degC].
365  real :: temp_chg ! The change in total heat divided by total heat capacity
366  ! of the ocean since the last call to this subroutine, degC.
367  real :: temp_anom ! The change in total heat that cannot be accounted for
368  ! by the surface fluxes, divided by the total heat
369  ! capacity of the ocean [degC].
370  real :: hint ! The deviation of an interface from H [Z ~> m].
371  real :: hbot ! 0 if the basin is deeper than H, or the
372  ! height of the basin depth over H otherwise [Z ~> m].
373  ! This makes PE only include real fluid.
374  real :: hbelow ! The depth of fluid in all layers beneath an interface [Z ~> m].
375  type(efp_type) :: &
376  mass_efp, & ! Extended fixed point sums of total mass, etc.
377  salt_efp, heat_efp, salt_chg_efp, heat_chg_efp, mass_chg_efp, &
378  mass_anom_efp, salt_anom_efp, heat_anom_efp
379  real :: cfl_trans ! A transport-based definition of the CFL number [nondim].
380  real :: cfl_lin ! A simpler definition of the CFL number [nondim].
381  real :: max_cfl(2) ! The maxima of the CFL numbers [nondim].
382  real :: irho0 ! The inverse of the reference density [m3 kg-1].
383  real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: &
384  tmp1 ! A temporary array
385  real, dimension(SZI_(G),SZJ_(G),SZK_(G)+1) :: &
386  pe_pt ! The potential energy at each point [J].
387  real, dimension(SZI_(G),SZJ_(G)) :: &
388  temp_int, salt_int ! Layer and cell integrated heat and salt [J] and [g Salt].
389  real :: h_to_kg_m2 ! Local copy of a unit conversion factor.
390  integer :: num_nc_fields ! The number of fields that will actually go into
391  ! the NetCDF file.
392  integer :: i, j, k, is, ie, js, je, ns, nz, m, isq, ieq, jsq, jeq
393  integer :: l, lbelow, labove ! indices of deep_area_vol, used to find Z_0APE.
394  ! lbelow & labove are lower & upper limits for l
395  ! in the search for the entry in lH to use.
396  integer :: start_of_day, num_days
397  real :: reday, var
398  character(len=240) :: energypath_nc
399  character(len=200) :: mesg
400  character(len=32) :: mesg_intro, time_units, day_str, n_str, date_str
401  logical :: date_stamped
402  type(time_type) :: dt_force ! A time_type version of the forcing timestep.
403  real :: tr_stocks(max_fields_)
404  real :: tr_min(max_fields_), tr_max(max_fields_)
405  real :: tr_min_x(max_fields_), tr_min_y(max_fields_), tr_min_z(max_fields_)
406  real :: tr_max_x(max_fields_), tr_max_y(max_fields_), tr_max_z(max_fields_)
407  logical :: tr_minmax_got(max_fields_) = .false.
408  character(len=40), dimension(MAX_FIELDS_) :: &
409  tr_names, tr_units
410  integer :: ntr_stocks
411  integer :: iyear, imonth, iday, ihour, iminute, isecond, itick ! For call to get_date()
412  logical :: local_open_bc
413  type(obc_segment_type), pointer :: segment => null()
414 
415  ! A description for output of each of the fields.
416  type(vardesc) :: vars(num_fields+max_fields_)
417 
418  ! write_energy_time is the next integral multiple of energysavedays.
419  dt_force = set_time(seconds=2) ; if (present(dt_forcing)) dt_force = dt_forcing
420  if (cs%previous_calls == 0) then
421  if (cs%energysave_geometric) then
422  if (cs%energysavedays_geometric < cs%energysavedays) then
423  cs%write_energy_time = day + cs%energysavedays_geometric
424  cs%geometric_end_time = cs%Start_time + cs%energysavedays * &
425  (1 + (day - cs%Start_time) / cs%energysavedays)
426  else
427  cs%write_energy_time = cs%Start_time + cs%energysavedays * &
428  (1 + (day - cs%Start_time) / cs%energysavedays)
429  endif
430  else
431  cs%write_energy_time = cs%Start_time + cs%energysavedays * &
432  (1 + (day - cs%Start_time) / cs%energysavedays)
433  endif
434  elseif (day + (dt_force/2) <= cs%write_energy_time) then
435  return ! Do not write this step
436  else ! Determine the next write time before proceeding
437  if (cs%energysave_geometric) then
438  if (cs%write_energy_time + cs%energysavedays_geometric >= &
439  cs%geometric_end_time) then
440  cs%write_energy_time = cs%geometric_end_time
441  cs%energysave_geometric = .false. ! stop geometric progression
442  else
443  cs%write_energy_time = cs%write_energy_time + cs%energysavedays_geometric
444  endif
445  cs%energysavedays_geometric = cs%energysavedays_geometric*2
446  else
447  cs%write_energy_time = cs%write_energy_time + cs%energysavedays
448  endif
449  endif
450 
451  num_nc_fields = 17
452  if (.not.cs%use_temperature) num_nc_fields = 11
453  vars(1) = var_desc("Ntrunc","Nondim","Number of Velocity Truncations",'1','1')
454  vars(2) = var_desc("En","Joules","Total Energy",'1','1')
455  vars(3) = var_desc("APE","Joules","Total Interface APE",'1','i')
456  vars(4) = var_desc("KE","Joules","Total Layer KE",'1','L')
457  vars(5) = var_desc("H0","meter","Zero APE Depth of Interface",'1','i')
458  vars(6) = var_desc("Mass_lay","kg","Total Layer Mass",'1','L')
459  vars(7) = var_desc("Mass","kg","Total Mass",'1','1')
460  vars(8) = var_desc("Mass_chg","kg","Total Mass Change between Entries",'1','1')
461  vars(9) = var_desc("Mass_anom","kg","Anomalous Total Mass Change",'1','1')
462  vars(10) = var_desc("max_CFL_trans","Nondim","Maximum finite-volume CFL",'1','1')
463  vars(11) = var_desc("max_CFL_lin","Nondim","Maximum finite-difference CFL",'1','1')
464  if (cs%use_temperature) then
465  vars(12) = var_desc("Salt","kg","Total Salt",'1','1')
466  vars(13) = var_desc("Salt_chg","kg","Total Salt Change between Entries",'1','1')
467  vars(14) = var_desc("Salt_anom","kg","Anomalous Total Salt Change",'1','1')
468  vars(15) = var_desc("Heat","Joules","Total Heat",'1','1')
469  vars(16) = var_desc("Heat_chg","Joules","Total Heat Change between Entries",'1','1')
470  vars(17) = var_desc("Heat_anom","Joules","Anomalous Total Heat Change",'1','1')
471  endif
472 
473  local_open_bc = .false.
474  if (present(obc)) then ; if (associated(obc)) then
475  local_open_bc = (obc%open_u_BCs_exist_globally .or. obc%open_v_BCs_exist_globally)
476  endif ; endif
477 
478  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
479  isq = g%IscB ; ieq = g%IecB ; jsq = g%JscB ; jeq = g%JecB
480  h_to_kg_m2 = gv%H_to_kg_m2
481 
482  if (.not.associated(cs)) call mom_error(fatal, &
483  "write_energy: Module must be initialized before it is used.")
484 
485  do j=js,je ; do i=is,ie
486  areatm(i,j) = g%mask2dT(i,j)*g%areaT(i,j)
487  enddo ; enddo
488 
489  if (gv%Boussinesq) then
490  tmp1(:,:,:) = 0.0
491  do k=1,nz ; do j=js,je ; do i=is,ie
492  tmp1(i,j,k) = h(i,j,k) * (h_to_kg_m2*areatm(i,j))
493  enddo ; enddo ; enddo
494 
495  ! This block avoids using the points beyond an open boundary condition
496  ! in the accumulation of mass, but perhaps it would be unnecessary if there
497  ! were a more judicious use of masks in the loops 4 or 7 lines above.
498  if (local_open_bc) then
499  do ns=1, obc%number_of_segments
500  segment => obc%segment(ns)
501  if (.not. segment%on_pe .or. segment%specified) cycle
502  i=segment%HI%IsdB ; j=segment%HI%JsdB
503  if (segment%direction == obc_direction_e) then
504  do k=1,nz ; do j=segment%HI%jsd,segment%HI%jed
505  tmp1(i+1,j,k) = 0.0
506  enddo ; enddo
507  elseif (segment%direction == obc_direction_w) then
508  do k=1,nz ; do j=segment%HI%jsd,segment%HI%jed
509  tmp1(i,j,k) = 0.0
510  enddo ; enddo
511  elseif (segment%direction == obc_direction_n) then
512  do k=1,nz ; do i=segment%HI%isd,segment%HI%ied
513  tmp1(i,j+1,k) = 0.0
514  enddo ; enddo
515  elseif (segment%direction == obc_direction_s) then
516  do k=1,nz ; do i=segment%HI%isd,segment%HI%ied
517  tmp1(i,j,k) = 0.0
518  enddo ; enddo
519  endif
520  enddo
521  endif
522 
523  mass_tot = reproducing_sum(tmp1, sums=mass_lay, efp_sum=mass_efp)
524  do k=1,nz ; vol_lay(k) = (gv%H_to_Z/h_to_kg_m2)*mass_lay(k) ; enddo
525  else
526  tmp1(:,:,:) = 0.0
527  if (cs%do_APE_calc) then
528  do k=1,nz ; do j=js,je ; do i=is,ie
529  tmp1(i,j,k) = h_to_kg_m2 * h(i,j,k) * areatm(i,j)
530  enddo ; enddo ; enddo
531  mass_tot = reproducing_sum(tmp1, sums=mass_lay, efp_sum=mass_efp)
532 
533  call find_eta(h, tv, g, gv, us, eta)
534  do k=1,nz ; do j=js,je ; do i=is,ie
535  tmp1(i,j,k) = (eta(i,j,k)-eta(i,j,k+1)) * areatm(i,j)
536  enddo ; enddo ; enddo
537  vol_tot = us%Z_to_m*reproducing_sum(tmp1, sums=vol_lay)
538  else
539  do k=1,nz ; do j=js,je ; do i=is,ie
540  tmp1(i,j,k) = h_to_kg_m2 * h(i,j,k) * areatm(i,j)
541  enddo ; enddo ; enddo
542  mass_tot = reproducing_sum(tmp1, sums=mass_lay, efp_sum=mass_efp)
543  do k=1,nz ; vol_lay(k) = us%m_to_Z * (mass_lay(k) / gv%Rho0) ; enddo
544  endif
545  endif ! Boussinesq
546 
547  ntr_stocks = 0
548  if (present(tracer_csp)) then
549  call call_tracer_stocks(h, tr_stocks, g, gv, tracer_csp, stock_names=tr_names, &
550  stock_units=tr_units, num_stocks=ntr_stocks,&
551  got_min_max=tr_minmax_got, global_min=tr_min, global_max=tr_max, &
552  xgmin=tr_min_x, ygmin=tr_min_y, zgmin=tr_min_z,&
553  xgmax=tr_max_x, ygmax=tr_max_y, zgmax=tr_max_z)
554  if (ntr_stocks > 0) then
555  do m=1,ntr_stocks
556  vars(num_nc_fields+m) = var_desc(tr_names(m), units=tr_units(m), &
557  longname=tr_names(m), hor_grid='1', z_grid='1')
558  enddo
559  num_nc_fields = num_nc_fields + ntr_stocks
560  endif
561  endif
562 
563  if (cs%previous_calls == 0) then
564  cs%mass_prev = mass_tot ; cs%fresh_water_input = 0.0
565 
566  cs%mass_prev_EFP = mass_efp
567  cs%fresh_water_in_EFP = real_to_efp(0.0)
568 
569  ! Reopen or create a text output file, with an explanatory header line.
570  if (is_root_pe()) then
571  if (day > cs%Start_time) then
572  call open_file(cs%fileenergy_ascii, trim(cs%energyfile), &
573  action=append_file, form=ascii_file, nohdrs=.true.)
574  else
575  call open_file(cs%fileenergy_ascii, trim(cs%energyfile), &
576  action=writeonly_file, form=ascii_file, nohdrs=.true.)
577  if (abs(cs%timeunit - 86400.0) < 1.0) then
578  if (cs%use_temperature) then
579  write(cs%fileenergy_ascii,'(" Step,",7x,"Day, Truncs, &
580  &Energy/Mass, Maximum CFL, Mean Sea Level, Total Mass, Mean Salin, &
581  &Mean Temp, Frac Mass Err, Salin Err, Temp Err")')
582  write(cs%fileenergy_ascii,'(12x,"[days]",17x,"[m2 s-2]",11x,"[Nondim]",7x,"[m]",13x,&
583  &"[kg]",9x,"[PSU]",6x,"[degC]",7x,"[Nondim]",8x,"[PSU]",8x,"[degC]")')
584  else
585  write(cs%fileenergy_ascii,'(" Step,",7x,"Day, Truncs, &
586  &Energy/Mass, Maximum CFL, Mean sea level, Total Mass, Frac Mass Err")')
587  write(cs%fileenergy_ascii,'(12x,"[days]",17x,"[m2 s-2]",11x,"[Nondim]",8x,"[m]",13x,&
588  &"[kg]",11x,"[Nondim]")')
589  endif
590  else
591  if ((cs%timeunit >= 0.99) .and. (cs%timeunit < 1.01)) then
592  time_units = " [seconds] "
593  elseif ((cs%timeunit >= 3599.0) .and. (cs%timeunit < 3601.0)) then
594  time_units = " [hours] "
595  elseif ((cs%timeunit >= 86399.0) .and. (cs%timeunit < 86401.0)) then
596  time_units = " [days] "
597  elseif ((cs%timeunit >= 3.0e7) .and. (cs%timeunit < 3.2e7)) then
598  time_units = " [years] "
599  else
600  write(time_units,'(9x,"[",es8.2," s] ")') cs%timeunit
601  endif
602 
603  if (cs%use_temperature) then
604  write(cs%fileenergy_ascii,'(" Step,",7x,"Time, Truncs, &
605  &Energy/Mass, Maximum CFL, Mean Sea Level, Total Mass, Mean Salin, &
606  &Mean Temp, Frac Mass Err, Salin Err, Temp Err")')
607  write(cs%fileenergy_ascii,'(A25,10x,"[m2 s-2]",11x,"[Nondim]",7x,"[m]",13x,&
608  &"[kg]",9x,"[PSU]",6x,"[degC]",7x,"[Nondim]",8x,"[PSU]",6x,&
609  &"[degC]")') time_units
610  else
611  write(cs%fileenergy_ascii,'(" Step,",7x,"Time, Truncs, &
612  &Energy/Mass, Maximum CFL, Mean sea level, Total Mass, Frac Mass Err")')
613  write(cs%fileenergy_ascii,'(A25,10x,"[m2 s-2]",11x,"[Nondim]",8x,"[m]",13x,&
614  &"[kg]",11x,"[Nondim]")') time_units
615  endif
616  endif
617  endif
618  endif
619 
620  energypath_nc = trim(cs%energyfile) // ".nc"
621  if (day > cs%Start_time) then
622  call reopen_file(cs%fileenergy_nc, trim(energypath_nc), vars, &
623  num_nc_fields, cs%fields, single_file, cs%timeunit, &
624  g=g, gv=gv)
625  else
626  call create_file(cs%fileenergy_nc, trim(energypath_nc), vars, &
627  num_nc_fields, cs%fields, single_file, cs%timeunit, &
628  g=g, gv=gv)
629  endif
630  endif
631 
632  if (cs%do_APE_calc) then
633  lbelow = 1 ; volbelow = 0.0
634  do k=nz,1,-1
635  volbelow = volbelow + vol_lay(k)
636  if ((volbelow >= cs%DL(cs%lH(k))%vol_below) .and. &
637  (volbelow < cs%DL(cs%lH(k)+1)%vol_below)) then
638  l = cs%lH(k)
639  else
640  labove=cs%list_size+1
641  l = (labove + lbelow) / 2
642  do while (l > lbelow)
643  if (volbelow < cs%DL(l)%vol_below) then ; labove = l
644  else ; lbelow = l ; endif
645  l = (labove + lbelow) / 2
646  enddo
647  cs%lH(k) = l
648  endif
649  lbelow = l
650  z_0ape(k) = cs%DL(l)%depth - (volbelow - cs%DL(l)%vol_below) / cs%DL(l)%area
651  enddo
652  z_0ape(nz+1) = cs%DL(2)%depth
653 
654  ! Calculate the Available Potential Energy integrated over each
655  ! interface. With a nonlinear equation of state or with a bulk
656  ! mixed layer this calculation is only approximate. With an ALE model
657  ! this does not make sense.
658  pe_pt(:,:,:) = 0.0
659  if (gv%Boussinesq) then
660  do j=js,je ; do i=is,ie
661  hbelow = 0.0
662  do k=nz,1,-1
663  hbelow = hbelow + h(i,j,k) * gv%H_to_Z
664  hint = z_0ape(k) + (hbelow - g%bathyT(i,j))
665  hbot = z_0ape(k) - g%bathyT(i,j)
666  hbot = (hbot + abs(hbot)) * 0.5
667  pe_pt(i,j,k) = 0.5 * areatm(i,j) * us%Z_to_m*(gv%Rho0*us%L_to_m**2*us%s_to_T**2*gv%g_prime(k)) * &
668  (hint * hint - hbot * hbot)
669  enddo
670  enddo ; enddo
671  else
672  do j=js,je ; do i=is,ie
673  do k=nz,1,-1
674  hint = z_0ape(k) + eta(i,j,k) ! eta and H_0 have opposite signs.
675  hbot = max(z_0ape(k) - g%bathyT(i,j), 0.0)
676  pe_pt(i,j,k) = 0.5 * (areatm(i,j) * us%Z_to_m*(gv%Rho0*us%L_to_m**2*us%s_to_T**2*gv%g_prime(k))) * &
677  (hint * hint - hbot * hbot)
678  enddo
679  enddo ; enddo
680  endif
681 
682  pe_tot = reproducing_sum(pe_pt, sums=pe)
683  do k=1,nz+1 ; h_0ape(k) = us%Z_to_m*z_0ape(k) ; enddo
684  else
685  pe_tot = 0.0
686  do k=1,nz+1 ; pe(k) = 0.0 ; h_0ape(k) = 0.0 ; enddo
687  endif
688 
689 ! Calculate the Kinetic Energy integrated over each layer.
690  tmp1(:,:,:) = 0.0
691  do k=1,nz ; do j=js,je ; do i=is,ie
692  tmp1(i,j,k) = (0.25 * h_to_kg_m2 * (areatm(i,j) * h(i,j,k))) * &
693  (u(i-1,j,k)**2 + u(i,j,k)**2 + v(i,j-1,k)**2 + v(i,j,k)**2)
694  enddo ; enddo ; enddo
695  ke_tot = reproducing_sum(tmp1, sums=ke)
696 
697  toten = ke_tot + pe_tot
698 
699  salt = 0.0 ; heat = 0.0
700  if (cs%use_temperature) then
701  temp_int(:,:) = 0.0 ; salt_int(:,:) = 0.0
702  do k=1,nz ; do j=js,je ; do i=is,ie
703  salt_int(i,j) = salt_int(i,j) + tv%S(i,j,k) * &
704  (h(i,j,k)*(h_to_kg_m2 * areatm(i,j)))
705  temp_int(i,j) = temp_int(i,j) + (tv%C_p * tv%T(i,j,k)) * &
706  (h(i,j,k)*(h_to_kg_m2 * areatm(i,j)))
707  enddo ; enddo ; enddo
708  salt = reproducing_sum(salt_int, efp_sum=salt_efp)
709  heat = reproducing_sum(temp_int, efp_sum=heat_efp)
710  endif
711 
712 ! Calculate the maximum CFL numbers.
713  max_cfl(1:2) = 0.0
714  do k=1,nz ; do j=js,je ; do i=isq,ieq
715  if (u(i,j,k) < 0.0) then
716  cfl_trans = (-u(i,j,k) * cs%dt) * (g%dy_Cu(i,j) * g%IareaT(i+1,j))
717  else
718  cfl_trans = (u(i,j,k) * cs%dt) * (g%dy_Cu(i,j) * g%IareaT(i,j))
719  endif
720  cfl_lin = abs(u(i,j,k) * cs%dt) * g%IdxCu(i,j)
721  max_cfl(1) = max(max_cfl(1), cfl_trans)
722  max_cfl(2) = max(max_cfl(2), cfl_lin)
723  enddo ; enddo ; enddo
724  do k=1,nz ; do j=jsq,jeq ; do i=is,ie
725  if (v(i,j,k) < 0.0) then
726  cfl_trans = (-v(i,j,k) * cs%dt) * (g%dx_Cv(i,j) * g%IareaT(i,j+1))
727  else
728  cfl_trans = (v(i,j,k) * cs%dt) * (g%dx_Cv(i,j) * g%IareaT(i,j))
729  endif
730  cfl_lin = abs(v(i,j,k) * cs%dt) * g%IdyCv(i,j)
731  max_cfl(1) = max(max_cfl(1), cfl_trans)
732  max_cfl(2) = max(max_cfl(2), cfl_lin)
733  enddo ; enddo ; enddo
734 
735  call sum_across_pes(cs%ntrunc)
736  ! Sum the various quantities across all the processors. This sum is NOT
737  ! guaranteed to be bitwise reproducible, even on the same decomposition.
738  ! The sum of Tr_stocks should be reimplemented using the reproducing sums.
739  if (ntr_stocks > 0) call sum_across_pes(tr_stocks,ntr_stocks)
740 
741  call max_across_pes(max_cfl(1))
742  call max_across_pes(max_cfl(2))
743  if (cs%use_temperature .and. cs%previous_calls == 0) then
744  cs%salt_prev = salt ; cs%net_salt_input = 0.0
745  cs%heat_prev = heat ; cs%net_heat_input = 0.0
746 
747  cs%salt_prev_EFP = salt_efp ; cs%net_salt_in_EFP = real_to_efp(0.0)
748  cs%heat_prev_EFP = heat_efp ; cs%net_heat_in_EFP = real_to_efp(0.0)
749  endif
750  irho0 = 1.0/gv%Rho0
751 
752  if (cs%use_temperature) then
753  salt_chg_efp = salt_efp - cs%salt_prev_EFP
754  salt_anom_efp = salt_chg_efp - cs%net_salt_in_EFP
755  salt_chg = efp_to_real(salt_chg_efp) ; salt_anom = efp_to_real(salt_anom_efp)
756  heat_chg_efp = heat_efp - cs%heat_prev_EFP
757  heat_anom_efp = heat_chg_efp - cs%net_heat_in_EFP
758  heat_chg = efp_to_real(heat_chg_efp) ; heat_anom = efp_to_real(heat_anom_efp)
759  endif
760 
761  mass_chg_efp = mass_efp - cs%mass_prev_EFP
762  salin_mass_in = 0.0
763  if (gv%Boussinesq) then
764  mass_anom_efp = mass_chg_efp - cs%fresh_water_in_EFP
765  else
766  ! net_salt_input needs to be converted from ppt m s-1 to kg m-2 s-1.
767  mass_anom_efp = mass_chg_efp - cs%fresh_water_in_EFP
768  if (cs%use_temperature) &
769  salin_mass_in = 0.001*efp_to_real(cs%net_salt_in_EFP)
770  endif
771  mass_chg = efp_to_real(mass_chg_efp)
772  mass_anom = efp_to_real(mass_anom_efp) - salin_mass_in
773 
774  if (cs%use_temperature) then
775  salin = salt / mass_tot ; salin_anom = salt_anom / mass_tot
776  ! salin_chg = Salt_chg / mass_tot
777  temp = heat / (mass_tot*tv%C_p) ; temp_anom = heat_anom / (mass_tot*tv%C_p)
778  endif
779  en_mass = toten / mass_tot
780 
781  call get_time(day, start_of_day, num_days)
782  date_stamped = (cs%date_stamped_output .and. (get_calendar_type() /= no_calendar))
783  if (date_stamped) &
784  call get_date(day, iyear, imonth, iday, ihour, iminute, isecond, itick)
785  if (abs(cs%timeunit - 86400.0) < 1.0) then
786  reday = real(num_days)+ (real(start_of_day)/86400.0)
787  mesg_intro = "MOM Day "
788  else
789  reday = real(num_days)*(86400.0/cs%timeunit) + &
790  REAL(start_of_day)/abs(cs%timeunit)
791  mesg_intro = "MOM Time "
792  endif
793  if (reday < 1.0e8) then ; write(day_str, '(F12.3)') reday
794  elseif (reday < 1.0e11) then ; write(day_str, '(F15.3)') reday
795  else ; write(day_str, '(ES15.9)') reday ; endif
796 
797  if (n < 1000000) then ; write(n_str, '(I6)') n
798  elseif (n < 10000000) then ; write(n_str, '(I7)') n
799  elseif (n < 100000000) then ; write(n_str, '(I8)') n
800  else ; write(n_str, '(I10)') n ; endif
801 
802  if (date_stamped) then
803  write(date_str,'("MOM Date",i7,2("/",i2.2)," ",i2.2,2(":",i2.2))') &
804  iyear, imonth, iday, ihour, iminute, isecond
805  else
806  date_str = trim(mesg_intro)//trim(day_str)
807  endif
808 
809  if (is_root_pe()) then
810  if (cs%use_temperature) then
811  write(*,'(A," ",A,": En ",ES12.6, ", MaxCFL ", F8.5, ", Mass ", &
812  & ES18.12, ", Salt ", F15.11,", Temp ", F15.11)') &
813  trim(date_str), trim(n_str), en_mass, max_cfl(1), mass_tot, salin, temp
814  else
815  write(*,'(A," ",A,": En ",ES12.6, ", MaxCFL ", F8.5, ", Mass ", &
816  & ES18.12)') &
817  trim(date_str), trim(n_str), en_mass, max_cfl(1), mass_tot
818  endif
819 
820  if (cs%use_temperature) then
821  write(cs%fileenergy_ascii,'(A,",",A,",", I6,", En ",ES22.16, &
822  &", CFL ", F8.5, ", SL ",&
823  &es11.4,", M ",ES11.5,", S",f8.4,", T",f8.4,&
824  &", Me ",ES9.2,", Se ",ES9.2,", Te ",ES9.2)') &
825  trim(n_str), trim(day_str), cs%ntrunc, en_mass, max_cfl(1), &
826  -h_0ape(1), mass_tot, salin, temp, mass_anom/mass_tot, salin_anom, &
827  temp_anom
828  else
829  write(cs%fileenergy_ascii,'(A,",",A,",", I6,", En ",ES22.16, &
830  &", CFL ", F8.5, ", SL ",&
831  &ES11.4,", Mass ",ES11.5,", Me ",ES9.2)') &
832  trim(n_str), trim(day_str), cs%ntrunc, en_mass, max_cfl(1), &
833  -h_0ape(1), mass_tot, mass_anom/mass_tot
834  endif
835 
836  if (cs%ntrunc > 0) then
837  write(*,'(A," Energy/Mass:",ES12.5," Truncations ",I0)') &
838  trim(date_str), en_mass, cs%ntrunc
839  endif
840 
841  if (cs%write_stocks) then
842  write(*,'(" Total Energy: ",Z16.16,ES24.16)') toten, toten
843  write(*,'(" Total Mass: ",ES24.16,", Change: ",ES24.16," Error: ",ES12.5," (",ES8.1,")")') &
844  mass_tot, mass_chg, mass_anom, mass_anom/mass_tot
845  if (cs%use_temperature) then
846  if (salt == 0.) then
847  write(*,'(" Total Salt: ",ES24.16,", Change: ",ES24.16," Error: ",ES12.5)') &
848  salt*0.001, salt_chg*0.001, salt_anom*0.001
849  else
850  write(*,'(" Total Salt: ",ES24.16,", Change: ",ES24.16," Error: ",ES12.5," (",ES8.1,")")') &
851  salt*0.001, salt_chg*0.001, salt_anom*0.001, salt_anom/salt
852  endif
853  if (heat == 0.) then
854  write(*,'(" Total Heat: ",ES24.16,", Change: ",ES24.16," Error: ",ES12.5)') &
855  heat, heat_chg, heat_anom
856  else
857  write(*,'(" Total Heat: ",ES24.16,", Change: ",ES24.16," Error: ",ES12.5," (",ES8.1,")")') &
858  heat, heat_chg, heat_anom, heat_anom/heat
859  endif
860  endif
861  do m=1,ntr_stocks
862 
863  write(*,'(" Total ",a,": ",ES24.16,X,a)') &
864  trim(tr_names(m)), tr_stocks(m), trim(tr_units(m))
865 
866  if (tr_minmax_got(m)) then
867  write(*,'(64X,"Global Min:",ES24.16,X,"at: (", f7.2,","f7.2,","f8.2,")" )') &
868  tr_min(m),tr_min_x(m),tr_min_y(m),tr_min_z(m)
869  write(*,'(64X,"Global Max:",ES24.16,X,"at: (", f7.2,","f7.2,","f8.2,")" )') &
870  tr_max(m),tr_max_x(m),tr_max_y(m),tr_max_z(m)
871  endif
872 
873  enddo
874  endif
875  endif
876 
877  var = real(cs%ntrunc)
878  call write_field(cs%fileenergy_nc, cs%fields(1), var, reday)
879  call write_field(cs%fileenergy_nc, cs%fields(2), toten, reday)
880  call write_field(cs%fileenergy_nc, cs%fields(3), pe, reday)
881  call write_field(cs%fileenergy_nc, cs%fields(4), ke, reday)
882  call write_field(cs%fileenergy_nc, cs%fields(5), h_0ape, reday)
883  call write_field(cs%fileenergy_nc, cs%fields(6), mass_lay, reday)
884 
885  call write_field(cs%fileenergy_nc, cs%fields(7), mass_tot, reday)
886  call write_field(cs%fileenergy_nc, cs%fields(8), mass_chg, reday)
887  call write_field(cs%fileenergy_nc, cs%fields(9), mass_anom, reday)
888  call write_field(cs%fileenergy_nc, cs%fields(10), max_cfl(1), reday)
889  call write_field(cs%fileenergy_nc, cs%fields(11), max_cfl(1), reday)
890  if (cs%use_temperature) then
891  call write_field(cs%fileenergy_nc, cs%fields(12), 0.001*salt, reday)
892  call write_field(cs%fileenergy_nc, cs%fields(13), 0.001*salt_chg, reday)
893  call write_field(cs%fileenergy_nc, cs%fields(14), 0.001*salt_anom, reday)
894  call write_field(cs%fileenergy_nc, cs%fields(15), heat, reday)
895  call write_field(cs%fileenergy_nc, cs%fields(16), heat_chg, reday)
896  call write_field(cs%fileenergy_nc, cs%fields(17), heat_anom, reday)
897  do m=1,ntr_stocks
898  call write_field(cs%fileenergy_nc, cs%fields(17+m), tr_stocks(m), reday)
899  enddo
900  else
901  do m=1,ntr_stocks
902  call write_field(cs%fileenergy_nc, cs%fields(11+m), tr_stocks(m), reday)
903  enddo
904  endif
905 
906  call flush_file(cs%fileenergy_nc)
907 
908  ! The second (impossible-looking) test looks for a NaN in En_mass.
909  if ((en_mass>cs%max_Energy) .or. &
910  ((en_mass>cs%max_Energy) .and. (en_mass<cs%max_Energy))) then
911  write(mesg,'("Energy per unit mass of ",ES11.4," exceeds ",ES11.4)') &
912  en_mass, cs%max_Energy
913  call mom_error(fatal, &
914  "write_energy : Excessive energy per unit mass or NaNs forced model termination.")
915  endif
916  if (cs%ntrunc>cs%maxtrunc) then
917  call mom_error(fatal, "write_energy : Ocean velocity has been truncated too many times.")
918  endif
919  cs%ntrunc = 0
920  cs%previous_calls = cs%previous_calls + 1
921  cs%mass_prev = mass_tot ; cs%fresh_water_input = 0.0
922  if (cs%use_temperature) then
923  cs%salt_prev = salt ; cs%net_salt_input = 0.0
924  cs%heat_prev = heat ; cs%net_heat_input = 0.0
925  endif
926 
927  cs%mass_prev_EFP = mass_efp ; cs%fresh_water_in_EFP = real_to_efp(0.0)
928  if (cs%use_temperature) then
929  cs%salt_prev_EFP = salt_efp ; cs%net_salt_in_EFP = real_to_efp(0.0)
930  cs%heat_prev_EFP = heat_efp ; cs%net_heat_in_EFP = real_to_efp(0.0)
931  endif
932 end subroutine write_energy
933 
934 !> This subroutine accumates the net input of volume, salt and heat, through
935 !! the ocean surface for use in diagnosing conservation.
936 subroutine accumulate_net_input(fluxes, sfc_state, dt, G, CS)
937  type(forcing), intent(in) :: fluxes !< A structure containing pointers to any possible
938  !! forcing fields. Unused fields are unallocated.
939  type(surface), intent(in) :: sfc_state !< A structure containing fields that
940  !! describe the surface state of the ocean.
941  real, intent(in) :: dt !< The amount of time over which to average [s].
942  type(ocean_grid_type), intent(in) :: g !< The ocean's grid structure.
943  type(sum_output_cs), pointer :: cs !< The control structure returned by a previous call
944  !! to MOM_sum_output_init.
945  ! Local variables
946  real, dimension(SZI_(G),SZJ_(G)) :: &
947  fw_in, & ! The net fresh water input, integrated over a timestep [kg].
948  salt_in, & ! The total salt added by surface fluxes, integrated
949  ! over a time step [ppt kg].
950  heat_in ! The total heat added by surface fluxes, integrated
951  ! over a time step [J].
952  real :: fw_input ! The net fresh water input, integrated over a timestep
953  ! and summed over space [kg].
954  real :: salt_input ! The total salt added by surface fluxes, integrated
955  ! over a time step and summed over space [ppt kg].
956  real :: heat_input ! The total heat added by boundary fluxes, integrated
957  ! over a time step and summed over space [J].
958  real :: c_p ! The heat capacity of seawater [J degC-1 kg-1].
959 
960  type(efp_type) :: &
961  fw_in_efp, & ! Extended fixed point version of FW_input [kg]
962  salt_in_efp, & ! Extended fixed point version of salt_input [ppt kg]
963  heat_in_efp ! Extended fixed point version of heat_input [J]
964 
965  real :: inputs(3) ! A mixed array for combining the sums
966  integer :: i, j, is, ie, js, je
967 
968  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
969  c_p = fluxes%C_p
970 
971  fw_in(:,:) = 0.0 ; fw_input = 0.0
972  if (associated(fluxes%evap)) then
973  if (associated(fluxes%lprec) .and. associated(fluxes%fprec)) then
974  do j=js,je ; do i=is,ie
975  fw_in(i,j) = dt*g%areaT(i,j)*(fluxes%evap(i,j) + &
976  (((fluxes%lprec(i,j) + fluxes%vprec(i,j)) + fluxes%lrunoff(i,j)) + &
977  (fluxes%fprec(i,j) + fluxes%frunoff(i,j))))
978  enddo ; enddo
979  else
980  call mom_error(warning, &
981  "accumulate_net_input called with associated evap field, but no precip field.")
982  endif
983  endif
984 
985  if (associated(fluxes%seaice_melt)) then ; do j=js,je ; do i=is,ie
986  fw_in(i,j) = fw_in(i,j) + dt * g%areaT(i,j) * fluxes%seaice_melt(i,j)
987  enddo ; enddo ; endif
988 
989  salt_in(:,:) = 0.0 ; heat_in(:,:) = 0.0
990  if (cs%use_temperature) then
991 
992  if (associated(fluxes%sw)) then ; do j=js,je ; do i=is,ie
993  heat_in(i,j) = heat_in(i,j) + dt*g%areaT(i,j) * (fluxes%sw(i,j) + &
994  (fluxes%lw(i,j) + (fluxes%latent(i,j) + fluxes%sens(i,j))))
995  enddo ; enddo ; endif
996 
997  if (associated(fluxes%seaice_melt_heat)) then ; do j=js,je ; do i=is,ie
998  heat_in(i,j) = heat_in(i,j) + dt*g%areaT(i,j) * fluxes%seaice_melt_heat(i,j)
999  enddo ; enddo ; endif
1000 
1001  ! smg: new code
1002  ! include heat content from water transport across ocean surface
1003 ! if (associated(fluxes%heat_content_lprec)) then ; do j=js,je ; do i=is,ie
1004 ! heat_in(i,j) = heat_in(i,j) + dt*G%areaT(i,j) * &
1005 ! (fluxes%heat_content_lprec(i,j) + (fluxes%heat_content_fprec(i,j) &
1006 ! + (fluxes%heat_content_lrunoff(i,j) + (fluxes%heat_content_frunoff(i,j) &
1007 ! + (fluxes%heat_content_cond(i,j) + (fluxes%heat_content_vprec(i,j) &
1008 ! + fluxes%heat_content_massout(i,j)))))))
1009 ! enddo ; enddo ; endif
1010 
1011  ! smg: old code
1012  if (associated(sfc_state%TempxPmE)) then
1013  do j=js,je ; do i=is,ie
1014  heat_in(i,j) = heat_in(i,j) + (c_p * g%areaT(i,j)) * sfc_state%TempxPmE(i,j)
1015  enddo ; enddo
1016  elseif (associated(fluxes%evap)) then
1017  do j=js,je ; do i=is,ie
1018  heat_in(i,j) = heat_in(i,j) + (c_p * sfc_state%SST(i,j)) * fw_in(i,j)
1019  enddo ; enddo
1020  endif
1021 
1022 
1023  ! The following heat sources may or may not be used.
1024  if (associated(sfc_state%internal_heat)) then
1025  do j=js,je ; do i=is,ie
1026  heat_in(i,j) = heat_in(i,j) + (c_p * g%areaT(i,j)) * &
1027  sfc_state%internal_heat(i,j)
1028  enddo ; enddo
1029  endif
1030  if (associated(sfc_state%frazil)) then ; do j=js,je ; do i=is,ie
1031  heat_in(i,j) = heat_in(i,j) + g%areaT(i,j) * sfc_state%frazil(i,j)
1032  enddo ; enddo ; endif
1033  if (associated(fluxes%heat_added)) then ; do j=js,je ; do i=is,ie
1034  heat_in(i,j) = heat_in(i,j) + dt*g%areaT(i,j)*fluxes%heat_added(i,j)
1035  enddo ; enddo ; endif
1036 ! if (associated(sfc_state%sw_lost)) then ; do j=js,je ; do i=is,ie
1037 ! heat_in(i,j) = heat_in(i,j) - G%areaT(i,j) * sfc_state%sw_lost(i,j)
1038 ! enddo ; enddo ; endif
1039 
1040  if (associated(fluxes%salt_flux)) then ; do j=js,je ; do i=is,ie
1041  ! convert salt_flux from kg (salt)/(m^2 s) to ppt * [m s-1].
1042  salt_in(i,j) = dt*g%areaT(i,j)*(1000.0*fluxes%salt_flux(i,j))
1043  enddo ; enddo ; endif
1044  endif
1045 
1046  if ((cs%use_temperature) .or. associated(fluxes%lprec) .or. &
1047  associated(fluxes%evap)) then
1048  fw_input = reproducing_sum(fw_in, efp_sum=fw_in_efp)
1049  heat_input = reproducing_sum(heat_in, efp_sum=heat_in_efp)
1050  salt_input = reproducing_sum(salt_in, efp_sum=salt_in_efp)
1051 
1052  cs%fresh_water_input = cs%fresh_water_input + fw_input
1053  cs%net_salt_input = cs%net_salt_input + salt_input
1054  cs%net_heat_input = cs%net_heat_input + heat_input
1055 
1056  cs%fresh_water_in_EFP = cs%fresh_water_in_EFP + fw_in_efp
1057  cs%net_salt_in_EFP = cs%net_salt_in_EFP + salt_in_efp
1058  cs%net_heat_in_EFP = cs%net_heat_in_EFP + heat_in_efp
1059  endif
1060 
1061 end subroutine accumulate_net_input
1062 
1063 !> This subroutine sets up an ordered list of depths, along with the
1064 !! cross sectional areas at each depth and the volume of fluid deeper
1065 !! than each depth. This might be read from a previously created file
1066 !! or it might be created anew. (For now only new creation occurs.
1067 subroutine depth_list_setup(G, US, CS)
1068  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
1069  type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1070  type(sum_output_cs), pointer :: CS !< The control structure returned by a
1071  !! previous call to MOM_sum_output_init.
1072  ! Local variables
1073  integer :: k
1074 
1075  if (cs%read_depth_list) then
1076  if (file_exists(cs%depth_list_file)) then
1077  call read_depth_list(g, us, cs, cs%depth_list_file)
1078  else
1079  if (is_root_pe()) call mom_error(warning, "depth_list_setup: "// &
1080  trim(cs%depth_list_file)//" does not exist. Creating a new file.")
1081  call create_depth_list(g, cs)
1082 
1083  call write_depth_list(g, us, cs, cs%depth_list_file, cs%list_size+1)
1084  endif
1085  else
1086  call create_depth_list(g, cs)
1087  endif
1088 
1089  do k=1,g%ke
1090  cs%lH(k) = cs%list_size
1091  enddo
1092 
1093 end subroutine depth_list_setup
1094 
1095 !> create_depth_list makes an ordered list of depths, along with the cross
1096 !! sectional areas at each depth and the volume of fluid deeper than each depth.
1097 subroutine create_depth_list(G, CS)
1098  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure.
1099  type(sum_output_cs), pointer :: CS !< The control structure set up in MOM_sum_output_init,
1100  !! in which the ordered depth list is stored.
1101  ! Local variables
1102  real, dimension(G%Domain%niglobal*G%Domain%njglobal + 1) :: &
1103  Dlist, & !< The global list of bottom depths [Z ~> m].
1104  AreaList !< The global list of cell areas [m2].
1105  integer, dimension(G%Domain%niglobal*G%Domain%njglobal+1) :: &
1106  indx2 !< The position of an element in the original unsorted list.
1107  real :: Dnow !< The depth now being considered for sorting [Z ~> m].
1108  real :: Dprev !< The most recent depth that was considered [Z ~> m].
1109  real :: vol !< The running sum of open volume below a deptn [Z m2 ~> m3].
1110  real :: area !< The open area at the current depth [m2].
1111  real :: D_list_prev !< The most recent depth added to the list [Z ~> m].
1112  logical :: add_to_list !< This depth should be included as an entry on the list.
1113 
1114  integer :: ir, indxt
1115  integer :: mls, list_size
1116  integer :: list_pos, i_global, j_global
1117  integer :: i, j, k, kl
1118 
1119  mls = g%Domain%niglobal*g%Domain%njglobal
1120 
1121 ! Need to collect the global data from compute domains to a 1D array for sorting.
1122  dlist(:) = 0.0
1123  arealist(:) = 0.0
1124  do j=g%jsc,g%jec ; do i=g%isc,g%iec
1125  ! Set global indices that start the global domain at 1 (Fortran convention).
1126  j_global = j + g%jdg_offset - (g%jsg-1)
1127  i_global = i + g%idg_offset - (g%isg-1)
1128 
1129  list_pos = (j_global-1)*g%Domain%niglobal + i_global
1130  dlist(list_pos) = g%bathyT(i,j)
1131  arealist(list_pos) = g%mask2dT(i,j) * g%areaT(i,j)
1132  enddo ; enddo
1133 
1134  ! These sums reproduce across PEs because the arrays are only nonzero on one PE.
1135  call sum_across_pes(dlist, mls+1)
1136  call sum_across_pes(arealist, mls+1)
1137 
1138  do j=1,mls+1 ; indx2(j) = j ; enddo
1139  k = mls / 2 + 1 ; ir = mls
1140  do
1141  if (k > 1) then
1142  k = k - 1
1143  indxt = indx2(k)
1144  dnow = dlist(indxt)
1145  else
1146  indxt = indx2(ir)
1147  dnow = dlist(indxt)
1148  indx2(ir) = indx2(1)
1149  ir = ir - 1
1150  if (ir == 1) then ; indx2(1) = indxt ; exit ; endif
1151  endif
1152  i=k ; j=k*2
1153  do ; if (j > ir) exit
1154  if (j < ir .AND. dlist(indx2(j)) < dlist(indx2(j+1))) j = j + 1
1155  if (dnow < dlist(indx2(j))) then ; indx2(i) = indx2(j) ; i = j ; j = j + i
1156  else ; j = ir+1 ; endif
1157  enddo
1158  indx2(i) = indxt
1159  enddo
1160 
1161 ! At this point, the lists should perhaps be culled to save memory.
1162 ! Culling: (1) identical depths (e.g. land) - take the last one.
1163 ! (2) the topmost and bottommost depths are always saved.
1164 ! (3) very close depths
1165 ! (4) equal volume changes.
1166 
1167  ! Count the unique elements in the list.
1168  d_list_prev = dlist(indx2(mls))
1169  list_size = 2
1170  do k=mls-1,1,-1
1171  if (dlist(indx2(k)) < d_list_prev-cs%D_list_min_inc) then
1172  list_size = list_size + 1
1173  d_list_prev = dlist(indx2(k))
1174  endif
1175  enddo
1176 
1177  cs%list_size = list_size
1178  allocate(cs%DL(cs%list_size+1))
1179 
1180  vol = 0.0 ; area = 0.0
1181  dprev = dlist(indx2(mls))
1182  d_list_prev = dprev
1183 
1184  kl = 0
1185  do k=mls,1,-1
1186  i = indx2(k)
1187  vol = vol + area * (dprev - dlist(i))
1188  area = area + arealist(i)
1189 
1190  add_to_list = .false.
1191  if ((kl == 0) .or. (k==1)) then
1192  add_to_list = .true.
1193  elseif (dlist(indx2(k-1)) < d_list_prev-cs%D_list_min_inc) then
1194  add_to_list = .true.
1195  d_list_prev = dlist(indx2(k-1))
1196  endif
1197 
1198  if (add_to_list) then
1199  kl = kl+1
1200  cs%DL(kl)%depth = dlist(i)
1201  cs%DL(kl)%area = area
1202  cs%DL(kl)%vol_below = vol
1203  endif
1204  dprev = dlist(i)
1205  enddo
1206 
1207  do while (kl < list_size)
1208  ! I don't understand why this is needed... RWH
1209  kl = kl+1
1210  cs%DL(kl)%vol_below = cs%DL(kl-1)%vol_below * 1.000001
1211  cs%DL(kl)%area = cs%DL(kl-1)%area
1212  cs%DL(kl)%depth = cs%DL(kl-1)%depth
1213  enddo
1214 
1215  cs%DL(cs%list_size+1)%vol_below = cs%DL(cs%list_size)%vol_below * 1000.0
1216  cs%DL(cs%list_size+1)%area = cs%DL(cs%list_size)%area
1217  cs%DL(cs%list_size+1)%depth = cs%DL(cs%list_size)%depth
1218 
1219 end subroutine create_depth_list
1220 
1221 !> This subroutine writes out the depth list to the specified file.
1222 subroutine write_depth_list(G, US, CS, filename, list_size)
1223  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure.
1224  type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1225  type(sum_output_cs), pointer :: CS !< The control structure returned by a
1226  !! previous call to MOM_sum_output_init.
1227  character(len=*), intent(in) :: filename !< The path to the depth list file to write.
1228  integer, intent(in) :: list_size !< The size of the depth list.
1229  ! Local variables
1230  real, allocatable :: tmp(:)
1231  integer :: ncid, dimid(1), Did, Aid, Vid, status, k
1232  character(len=16) :: depth_chksum, area_chksum
1233 
1234  ! All ranks are required to compute the global checksum
1235  call get_depth_list_checksums(g, depth_chksum, area_chksum)
1236 
1237  if (.not.is_root_pe()) return
1238 
1239  allocate(tmp(list_size)) ; tmp(:) = 0.0
1240 
1241  status = nf90_create(filename, 0, ncid)
1242  if (status /= nf90_noerr) then
1243  call mom_error(warning, filename//trim(nf90_strerror(status)))
1244  return
1245  endif
1246 
1247  status = nf90_def_dim(ncid, "list", list_size, dimid(1))
1248  if (status /= nf90_noerr) call mom_error(warning, &
1249  filename//trim(nf90_strerror(status)))
1250 
1251  status = nf90_def_var(ncid, "depth", nf90_double, dimid, did)
1252  if (status /= nf90_noerr) call mom_error(warning, &
1253  filename//" depth "//trim(nf90_strerror(status)))
1254  status = nf90_put_att(ncid, did, "long_name", "Sorted depth")
1255  if (status /= nf90_noerr) call mom_error(warning, &
1256  filename//" depth "//trim(nf90_strerror(status)))
1257  status = nf90_put_att(ncid, did, "units", "m")
1258  if (status /= nf90_noerr) call mom_error(warning, &
1259  filename//" depth "//trim(nf90_strerror(status)))
1260 
1261  status = nf90_def_var(ncid, "area", nf90_double, dimid, aid)
1262  if (status /= nf90_noerr) call mom_error(warning, &
1263  filename//" area "//trim(nf90_strerror(status)))
1264  status = nf90_put_att(ncid, aid, "long_name", "Open area at depth")
1265  if (status /= nf90_noerr) call mom_error(warning, &
1266  filename//" area "//trim(nf90_strerror(status)))
1267  status = nf90_put_att(ncid, aid, "units", "m2")
1268  if (status /= nf90_noerr) call mom_error(warning, &
1269  filename//" area "//trim(nf90_strerror(status)))
1270 
1271  status = nf90_def_var(ncid, "vol_below", nf90_double, dimid, vid)
1272  if (status /= nf90_noerr) call mom_error(warning, &
1273  filename//" vol_below "//trim(nf90_strerror(status)))
1274  status = nf90_put_att(ncid, vid, "long_name", "Open volume below depth")
1275  if (status /= nf90_noerr) call mom_error(warning, &
1276  filename//" vol_below "//trim(nf90_strerror(status)))
1277  status = nf90_put_att(ncid, vid, "units", "m3")
1278  if (status /= nf90_noerr) call mom_error(warning, &
1279  filename//" vol_below "//trim(nf90_strerror(status)))
1280 
1281  ! Dependency checksums
1282  status = nf90_put_att(ncid, nf90_global, depth_chksum_attr, depth_chksum)
1283  if (status /= nf90_noerr) call mom_error(warning, &
1284  filename//" "//depth_chksum_attr//" "//trim(nf90_strerror(status)))
1285 
1286  status = nf90_put_att(ncid, nf90_global, area_chksum_attr, area_chksum)
1287  if (status /= nf90_noerr) call mom_error(warning, &
1288  filename//" "//area_chksum_attr//" "//trim(nf90_strerror(status)))
1289 
1290  status = nf90_enddef(ncid)
1291  if (status /= nf90_noerr) call mom_error(warning, &
1292  filename//trim(nf90_strerror(status)))
1293 
1294  do k=1,list_size ; tmp(k) = us%Z_to_m*cs%DL(k)%depth ; enddo
1295  status = nf90_put_var(ncid, did, tmp)
1296  if (status /= nf90_noerr) call mom_error(warning, &
1297  filename//" depth "//trim(nf90_strerror(status)))
1298 
1299  do k=1,list_size ; tmp(k) = cs%DL(k)%area ; enddo
1300  status = nf90_put_var(ncid, aid, tmp)
1301  if (status /= nf90_noerr) call mom_error(warning, &
1302  filename//" area "//trim(nf90_strerror(status)))
1303 
1304  do k=1,list_size ; tmp(k) = us%Z_to_m*cs%DL(k)%vol_below ; enddo
1305  status = nf90_put_var(ncid, vid, tmp)
1306  if (status /= nf90_noerr) call mom_error(warning, &
1307  filename//" vol_below "//trim(nf90_strerror(status)))
1308 
1309  status = nf90_close(ncid)
1310  if (status /= nf90_noerr) call mom_error(warning, &
1311  filename//trim(nf90_strerror(status)))
1312 
1313 end subroutine write_depth_list
1314 
1315 !> This subroutine reads in the depth list to the specified file
1316 !! and allocates and sets up CS%DL and CS%list_size .
1317 subroutine read_depth_list(G, US, CS, filename)
1318  type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
1319  type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1320  type(sum_output_cs), pointer :: CS !< The control structure returned by a
1321  !! previous call to MOM_sum_output_init.
1322  character(len=*), intent(in) :: filename !< The path to the depth list file to read.
1323  ! Local variables
1324  character(len=32) :: mdl
1325  character(len=240) :: var_name, var_msg
1326  real, allocatable :: tmp(:)
1327  integer :: ncid, status, varid, list_size, k
1328  integer :: ndim, len, var_dim_ids(NF90_MAX_VAR_DIMS)
1329  character(len=16) :: depth_file_chksum, depth_grid_chksum
1330  character(len=16) :: area_file_chksum, area_grid_chksum
1331  integer :: depth_attr_status, area_attr_status
1332 
1333  mdl = "MOM_sum_output read_depth_list:"
1334 
1335  status = nf90_open(filename, nf90_nowrite, ncid)
1336  if (status /= nf90_noerr) then
1337  call mom_error(fatal,mdl//" Difficulties opening "//trim(filename)// &
1338  " - "//trim(nf90_strerror(status)))
1339  endif
1340 
1341  ! Check bathymetric consistency
1342  depth_attr_status = nf90_get_att(ncid, nf90_global, depth_chksum_attr, &
1343  depth_file_chksum)
1344  area_attr_status = nf90_get_att(ncid, nf90_global, area_chksum_attr, &
1345  area_file_chksum)
1346 
1347  if (any([depth_attr_status, area_attr_status] == nf90_enotatt)) then
1348  var_msg = trim(cs%depth_list_file) // " checksums are missing;"
1349  if (cs%require_depth_list_chksum) then
1350  call mom_error(fatal, trim(var_msg) // " aborting.")
1351  elseif (cs%update_depth_list_chksum) then
1352  call mom_error(warning, trim(var_msg) // " updating file.")
1353  call create_depth_list(g, cs)
1354  call write_depth_list(g, us, cs, cs%depth_list_file, cs%list_size+1)
1355  return
1356  else
1357  call mom_error(warning, &
1358  trim(var_msg) // " some diagnostics may not be reproducible.")
1359  endif
1360  else
1361  ! Validate netCDF call
1362  if (depth_attr_status /= nf90_noerr) then
1363  var_msg = mdl // "Failed to read " // trim(filename) // ":" &
1364  // depth_chksum_attr
1365  call mom_error(fatal, &
1366  trim(var_msg) // " - " // nf90_strerror(depth_attr_status))
1367  endif
1368 
1369  if (area_attr_status /= nf90_noerr) then
1370  var_msg = mdl // "Failed to read " // trim(filename) // ":" &
1371  // area_chksum_attr
1372  call mom_error(fatal, &
1373  trim(var_msg) // " - " // nf90_strerror(area_attr_status))
1374  endif
1375 
1376  call get_depth_list_checksums(g, depth_grid_chksum, area_grid_chksum)
1377 
1378  if (depth_grid_chksum /= depth_file_chksum &
1379  .or. area_grid_chksum /= area_file_chksum) then
1380  var_msg = trim(cs%depth_list_file) // " checksums do not match;"
1381  if (cs%require_depth_list_chksum) then
1382  call mom_error(fatal, trim(var_msg) // " aborting.")
1383  elseif (cs%update_depth_list_chksum) then
1384  call mom_error(warning, trim(var_msg) // " updating file.")
1385  call create_depth_list(g, cs)
1386  call write_depth_list(g, us, cs, cs%depth_list_file, cs%list_size+1)
1387  return
1388  else
1389  call mom_error(warning, &
1390  trim(var_msg) // " some diagnostics may not be reproducible.")
1391  endif
1392  endif
1393  endif
1394 
1395  var_name = "depth"
1396  var_msg = trim(var_name)//" in "//trim(filename)//" - "
1397  status = nf90_inq_varid(ncid, var_name, varid)
1398  if (status /= nf90_noerr) call mom_error(fatal,mdl// &
1399  " Difficulties finding variable "//trim(var_msg)//&
1400  trim(nf90_strerror(status)))
1401 
1402  status = nf90_inquire_variable(ncid, varid, ndims=ndim, dimids=var_dim_ids)
1403  if (status /= nf90_noerr) then
1404  call mom_error(fatal,mdl//" cannot inquire about "//trim(var_msg)//&
1405  trim(nf90_strerror(status)))
1406  elseif (ndim > 1) then
1407  call mom_error(fatal,mdl//" "//trim(var_msg)//&
1408  " has too many or too few dimensions.")
1409  endif
1410 
1411  ! Get the length of the list.
1412  status = nf90_inquire_dimension(ncid, var_dim_ids(1), len=list_size)
1413  if (status /= nf90_noerr) call mom_error(fatal,mdl// &
1414  " cannot inquire about dimension(1) of "//trim(var_msg)//&
1415  trim(nf90_strerror(status)))
1416 
1417  cs%list_size = list_size-1
1418  allocate(cs%DL(list_size))
1419  allocate(tmp(list_size))
1420 
1421  status = nf90_get_var(ncid, varid, tmp)
1422  if (status /= nf90_noerr) call mom_error(fatal,mdl// &
1423  " Difficulties reading variable "//trim(var_msg)//&
1424  trim(nf90_strerror(status)))
1425 
1426  do k=1,list_size ; cs%DL(k)%depth = us%m_to_Z*tmp(k) ; enddo
1427 
1428  var_name = "area"
1429  var_msg = trim(var_name)//" in "//trim(filename)//" - "
1430  status = nf90_inq_varid(ncid, var_name, varid)
1431  if (status /= nf90_noerr) call mom_error(fatal,mdl// &
1432  " Difficulties finding variable "//trim(var_msg)//&
1433  trim(nf90_strerror(status)))
1434  status = nf90_get_var(ncid, varid, tmp)
1435  if (status /= nf90_noerr) call mom_error(fatal,mdl// &
1436  " Difficulties reading variable "//trim(var_msg)//&
1437  trim(nf90_strerror(status)))
1438 
1439  do k=1,list_size ; cs%DL(k)%area = tmp(k) ; enddo
1440 
1441  var_name = "vol_below"
1442  var_msg = trim(var_name)//" in "//trim(filename)
1443  status = nf90_inq_varid(ncid, var_name, varid)
1444  if (status /= nf90_noerr) call mom_error(fatal,mdl// &
1445  " Difficulties finding variable "//trim(var_msg)//&
1446  trim(nf90_strerror(status)))
1447  status = nf90_get_var(ncid, varid, tmp)
1448  if (status /= nf90_noerr) call mom_error(fatal,mdl// &
1449  " Difficulties reading variable "//trim(var_msg)//&
1450  trim(nf90_strerror(status)))
1451 
1452  do k=1,list_size ; cs%DL(k)%vol_below = us%m_to_Z*tmp(k) ; enddo
1453 
1454  status = nf90_close(ncid)
1455  if (status /= nf90_noerr) call mom_error(warning, mdl// &
1456  " Difficulties closing "//trim(filename)//" - "//trim(nf90_strerror(status)))
1457 
1458  deallocate(tmp)
1459 
1460 end subroutine read_depth_list
1461 
1462 
1463 !> Return the checksums required to verify DEPTH_LIST_FILE contents.
1464 !!
1465 !! This function computes checksums for the bathymetry (G%bathyT) and masked
1466 !! area (mask2dT * areaT) fields of the model grid G, which are used to compute
1467 !! the depth list. A difference in checksum indicates that a different method
1468 !! was used to compute the grid data, and that any results using the depth
1469 !! list, such as APE, will not be reproducible.
1470 !!
1471 !! Checksums are saved as hexadecimal strings, in order to avoid potential
1472 !! datatype issues with netCDF attributes.
1473 subroutine get_depth_list_checksums(G, depth_chksum, area_chksum)
1474  type(ocean_grid_type), intent(in) :: G !< Ocean grid structure
1475  character(len=16), intent(out) :: depth_chksum !< Depth checksum hexstring
1476  character(len=16), intent(out) :: area_chksum !< Area checksum hexstring
1477 
1478  integer :: i, j
1479  real, allocatable :: field(:,:)
1480 
1481  allocate(field(g%isc:g%iec, g%jsc:g%jec))
1482 
1483  ! Depth checksum
1484  do j=g%jsc,g%jec ; do i=g%isc,g%iec
1485  field(i,j) = g%bathyT(i,j)
1486  enddo ; enddo
1487  write(depth_chksum, '(Z16)') mpp_chksum(field(:,:))
1488 
1489  ! Area checksum
1490  do j=g%jsc,g%jec ; do i=g%isc,g%iec
1491  field(i,j) = g%mask2dT(i,j) * g%areaT(i,j)
1492  enddo ; enddo
1493  write(area_chksum, '(Z16)') mpp_chksum(field(:,:))
1494 
1495  deallocate(field)
1496 end subroutine get_depth_list_checksums
1497 
1498 !> \namespace mom_sum_output
1499 !!
1500 !! By Robert Hallberg, April 1994 - June 2002
1501 !!
1502 !! This file contains the subroutine (write_energy) that writes
1503 !! horizontally integrated quantities, such as energies and layer
1504 !! volumes, and other summary information to an output file. Some
1505 !! of these quantities (APE or resting interface height) are defined
1506 !! relative to the global histogram of topography. The subroutine
1507 !! that compiles that histogram (depth_list_setup) is also included
1508 !! in this file.
1509 !!
1510 !! In addition, if the number of velocity truncations since the
1511 !! previous call to write_energy exceeds maxtrunc or the total energy
1512 !! exceeds a very large threshold, a fatal termination is triggered.
1513 
1514 end module mom_sum_output
mom_time_manager
Wraps the FMS time manager functions.
Definition: MOM_time_manager.F90:2
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_verticalgrid
Provides a transparent vertical ocean grid type and supporting routines.
Definition: MOM_verticalGrid.F90:2
mom_coms::efp_type
The Extended Fixed Point (EFP) type provides a public interface for doing sums and taking differences...
Definition: MOM_coms.F90:64
mom_file_parser::log_version
An overloaded interface to log version information about modules.
Definition: MOM_file_parser.F90:109
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_sum_output::depth_list
A list of depths and corresponding globally integrated ocean area at each depth and the ocean volume ...
Definition: MOM_sum_output.F90:54
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_io
This module contains I/O framework code.
Definition: MOM_io.F90:2
mom_sum_output
Reports integrated quantities for monitoring the model state.
Definition: MOM_sum_output.F90:2
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_forcing_type
This module implements boundary forcing for MOM6.
Definition: MOM_forcing_type.F90:2
mom_verticalgrid::verticalgrid_type
Describes the vertical ocean grid, including unit conversion factors.
Definition: MOM_verticalGrid.F90:24
mom_sum_output::sum_output_cs
The control structure for the MOM_sum_output module.
Definition: MOM_sum_output.F90:61
mom_variables
Provides transparent structures with groups of MOM6 variables and supporting routines.
Definition: MOM_variables.F90:2
mom_open_boundary
Controls where open boundary conditions are applied.
Definition: MOM_open_boundary.F90:2
mom_file_parser
The MOM6 facility to parse input files for runtime parameters.
Definition: MOM_file_parser.F90:2
mom_tracer_flow_control
Orchestrates the registration and calling of tracer packages.
Definition: MOM_tracer_flow_control.F90:2
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_open_boundary::ocean_obc_type
Open-boundary data.
Definition: MOM_open_boundary.F90:186
mom_unit_scaling
Provides a transparent unit rescaling type to facilitate dimensional consistency testing.
Definition: MOM_unit_scaling.F90:2
mom_tracer_flow_control::tracer_flow_control_cs
The control structure for orchestrating the calling of tracer packages.
Definition: MOM_tracer_flow_control.F90:75
mom_forcing_type::forcing
Structure that contains pointers to the boundary forcing used to drive the liquid ocean simulated by ...
Definition: MOM_forcing_type.F90:49
mom_io::vardesc
Type for describing a variable, typically a tracer.
Definition: MOM_io.F90:53
mom_io::file_exists
Indicate whether a file exists, perhaps with domain decomposition.
Definition: MOM_io.F90:68
mom_coms::reproducing_sum
Find an accurate and order-invariant sum of distributed 2d or 3d fields.
Definition: MOM_coms.F90:54
mom_file_parser::log_param
An overloaded interface to log the values of various types of parameters.
Definition: MOM_file_parser.F90:96
mom_open_boundary::obc_segment_type
Open boundary segment data structure.
Definition: MOM_open_boundary.F90:107
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