MOM6
mom_sum_output Module Reference

Detailed Description

Reports integrated quantities for monitoring the model state.

By Robert Hallberg, April 1994 - June 2002

This file contains the subroutine (write_energy) that writes horizontally integrated quantities, such as energies and layer volumes, and other summary information to an output file. Some of these quantities (APE or resting interface height) are defined relative to the global histogram of topography. The subroutine that compiles that histogram (depth_list_setup) is also included in this file.

In addition, if the number of velocity truncations since the previous call to write_energy exceeds maxtrunc or the total energy exceeds a very large threshold, a fatal termination is triggered.

Data Types

type  depth_list
 A list of depths and corresponding globally integrated ocean area at each depth and the ocean volume below each depth. More...
 
type  sum_output_cs
 The control structure for the MOM_sum_output module. More...
 

Functions/Subroutines

subroutine, public mom_sum_output_init (G, US, param_file, directory, ntrnc, Input_start_time, CS)
 MOM_sum_output_init initializes the parameters and settings for the MOM_sum_output module. More...
 
subroutine mom_sum_output_end (CS)
 MOM_sum_output_end deallocates memory used by the MOM_sum_output module. More...
 
subroutine, public write_energy (u, v, h, tv, day, n, G, GV, US, CS, tracer_CSp, OBC, dt_forcing)
 This subroutine calculates and writes the total model energy, the energy and mass of each layer, and other globally integrated physical quantities. More...
 
subroutine, public accumulate_net_input (fluxes, sfc_state, dt, G, CS)
 This subroutine accumates the net input of volume, salt and heat, through the ocean surface for use in diagnosing conservation. More...
 
subroutine depth_list_setup (G, US, CS)
 This subroutine sets up an ordered list of depths, along with the cross sectional areas at each depth and the volume of fluid deeper than each depth. This might be read from a previously created file or it might be created anew. (For now only new creation occurs. More...
 
subroutine create_depth_list (G, CS)
 create_depth_list makes an ordered list of depths, along with the cross sectional areas at each depth and the volume of fluid deeper than each depth. More...
 
subroutine write_depth_list (G, US, CS, filename, list_size)
 This subroutine writes out the depth list to the specified file. More...
 
subroutine read_depth_list (G, US, CS, filename)
 This subroutine reads in the depth list to the specified file and allocates and sets up CSDL and CSlist_size . More...
 
subroutine get_depth_list_checksums (G, depth_chksum, area_chksum)
 Return the checksums required to verify DEPTH_LIST_FILE contents. More...
 

Variables

integer, parameter num_fields = 17
 Number of diagnostic fields.
 
character(*), parameter depth_chksum_attr = "bathyT_checksum"
 Checksum attribute name of GbathyT over the compute domain.
 
character(*), parameter area_chksum_attr = "mask2dT_areaT_checksum"
 Checksum attribute of name of Gmask2dT * GareaT over the compute domain.
 

Function/Subroutine Documentation

◆ accumulate_net_input()

subroutine, public mom_sum_output::accumulate_net_input ( type(forcing), intent(in)  fluxes,
type(surface), intent(in)  sfc_state,
real, intent(in)  dt,
type(ocean_grid_type), intent(in)  G,
type(sum_output_cs), pointer  CS 
)

This subroutine accumates the net input of volume, salt and heat, through the ocean surface for use in diagnosing conservation.

Parameters
[in]fluxesA structure containing pointers to any possible forcing fields. Unused fields are unallocated.
[in]sfc_stateA structure containing fields that describe the surface state of the ocean.
[in]dtThe amount of time over which to average [s].
[in]gThe ocean's grid structure.
csThe control structure returned by a previous call to MOM_sum_output_init.

Definition at line 937 of file MOM_sum_output.F90.

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 

◆ create_depth_list()

subroutine mom_sum_output::create_depth_list ( type(ocean_grid_type), intent(in)  G,
type(sum_output_cs), pointer  CS 
)
private

create_depth_list makes an ordered list of depths, along with the cross sectional areas at each depth and the volume of fluid deeper than each depth.

Parameters
[in]gThe ocean's grid structure.
csThe control structure set up in MOM_sum_output_init, in which the ordered depth list is stored.

Definition at line 1098 of file MOM_sum_output.F90.

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 

◆ depth_list_setup()

subroutine mom_sum_output::depth_list_setup ( type(ocean_grid_type), intent(in)  G,
type(unit_scale_type), intent(in)  US,
type(sum_output_cs), pointer  CS 
)
private

This subroutine sets up an ordered list of depths, along with the cross sectional areas at each depth and the volume of fluid deeper than each depth. This might be read from a previously created file or it might be created anew. (For now only new creation occurs.

Parameters
[in]gThe ocean's grid structure
[in]usA dimensional unit scaling type
csThe control structure returned by a previous call to MOM_sum_output_init.

Definition at line 1068 of file MOM_sum_output.F90.

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 

◆ get_depth_list_checksums()

subroutine mom_sum_output::get_depth_list_checksums ( type(ocean_grid_type), intent(in)  G,
character(len=16), intent(out)  depth_chksum,
character(len=16), intent(out)  area_chksum 
)
private

Return the checksums required to verify DEPTH_LIST_FILE contents.

This function computes checksums for the bathymetry (GbathyT) and masked area (mask2dT * areaT) fields of the model grid G, which are used to compute the depth list. A difference in checksum indicates that a different method was used to compute the grid data, and that any results using the depth list, such as APE, will not be reproducible.

Checksums are saved as hexadecimal strings, in order to avoid potential datatype issues with netCDF attributes.

Parameters
[in]gOcean grid structure
[out]depth_chksumDepth checksum hexstring
[out]area_chksumArea checksum hexstring

Definition at line 1474 of file MOM_sum_output.F90.

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)

◆ mom_sum_output_end()

subroutine mom_sum_output::mom_sum_output_end ( type(sum_output_cs), pointer  CS)
private

MOM_sum_output_end deallocates memory used by the MOM_sum_output module.

Parameters
csThe control structure returned by a previous call to MOM_sum_output_init.

Definition at line 290 of file MOM_sum_output.F90.

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

◆ mom_sum_output_init()

subroutine, public mom_sum_output::mom_sum_output_init ( type(ocean_grid_type), intent(in)  G,
type(unit_scale_type), intent(in)  US,
type(param_file_type), intent(in)  param_file,
character(len=*), intent(in)  directory,
integer, intent(inout), target  ntrnc,
type(time_type), intent(in)  Input_start_time,
type(sum_output_cs), pointer  CS 
)

MOM_sum_output_init initializes the parameters and settings for the MOM_sum_output module.

Parameters
[in]gThe ocean's grid structure.
[in]usA dimensional unit scaling type
[in]param_fileA structure to parse for run-time parameters.
[in]directoryThe directory where the energy file goes.
[in,out]ntrncThe integer that stores the number of times the velocity has been truncated since the last call to write_energy.
[in]input_start_timeThe start time of the simulation.
csA pointer that is set to point to the control structure for this module.

Definition at line 142 of file MOM_sum_output.F90.

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 

◆ read_depth_list()

subroutine mom_sum_output::read_depth_list ( type(ocean_grid_type), intent(in)  G,
type(unit_scale_type), intent(in)  US,
type(sum_output_cs), pointer  CS,
character(len=*), intent(in)  filename 
)
private

This subroutine reads in the depth list to the specified file and allocates and sets up CSDL and CSlist_size .

Parameters
[in]gThe ocean's grid structure
[in]usA dimensional unit scaling type
csThe control structure returned by a previous call to MOM_sum_output_init.
[in]filenameThe path to the depth list file to read.

Definition at line 1318 of file MOM_sum_output.F90.

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 

◆ write_depth_list()

subroutine mom_sum_output::write_depth_list ( type(ocean_grid_type), intent(in)  G,
type(unit_scale_type), intent(in)  US,
type(sum_output_cs), pointer  CS,
character(len=*), intent(in)  filename,
integer, intent(in)  list_size 
)
private

This subroutine writes out the depth list to the specified file.

Parameters
[in]gThe ocean's grid structure.
[in]usA dimensional unit scaling type
csThe control structure returned by a previous call to MOM_sum_output_init.
[in]filenameThe path to the depth list file to write.
[in]list_sizeThe size of the depth list.

Definition at line 1223 of file MOM_sum_output.F90.

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 

◆ write_energy()

subroutine, public mom_sum_output::write_energy ( real, dimension(szib_(g),szj_(g),szk_(g)), intent(in)  u,
real, dimension(szi_(g),szjb_(g),szk_(g)), intent(in)  v,
real, dimension(szi_(g),szj_(g),szk_(g)), intent(in)  h,
type(thermo_var_ptrs), intent(in)  tv,
type(time_type), intent(in)  day,
integer, intent(in)  n,
type(ocean_grid_type), intent(in)  G,
type(verticalgrid_type), intent(in)  GV,
type(unit_scale_type), intent(in)  US,
type(sum_output_cs), pointer  CS,
type(tracer_flow_control_cs), optional, pointer  tracer_CSp,
type(ocean_obc_type), optional, pointer  OBC,
type(time_type), intent(in), optional  dt_forcing 
)

This subroutine calculates and writes the total model energy, the energy and mass of each layer, and other globally integrated physical quantities.

Parameters
[in]gThe ocean's grid structure.
[in]gvThe ocean's vertical grid structure.
[in]usA dimensional unit scaling type
[in]uThe zonal velocity [m s-1].
[in]vThe meridional velocity [m s-1].
[in]hLayer thicknesses [H ~> m or kg m-2].
[in]tvA structure pointing to various thermodynamic variables.
[in]dayThe current model time.
[in]nThe time step number of the current execution.
csThe control structure returned by a previous call to MOM_sum_output_init.
tracer_csptracer control structure.
obcOpen boundaries control structure.
[in]dt_forcingThe forcing time step

Definition at line 304 of file MOM_sum_output.F90.

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
mom_coms::reproducing_sum
Find an accurate and order-invariant sum of distributed 2d or 3d fields.
Definition: MOM_coms.F90:54