The shared print layout for the periodic conservation + stability
console line, used by BOTH the ocean and coastal drivers so the two
regimes report identically. Each regime computes its own totals
(area-weighted device reductions over its own state), then hands the
scalars here; this module owns the reference-snapshot latching, the
relative-drift arithmetic, the exact line format, and the NaN / CFL
panic guards.
Layout (mirrors MOM6’s “MOM Day N:” status line):
[stats] Day D step N En E MaxCFL C Mass M [Salt S Temp T]
Mass : Error
Salt : Error (thermodynamics on)
Heat : Error (thermodynamics on)
Age : days (ideal-age tracer on)
En : Growth x
Initial values are captured on the first call (is_initialised);
every later call reports drift relative to them.
Nodes of different colours represent the following:
Solid arrows point from a submodule to the (sub)module which it is
descended from. Dashed arrows point from a module or program unit to
modules which it uses.
Where possible, edges connecting nodes are
given different colours to make them easier to distinguish in
large graphs.
Nodes of different colours represent the following:
Solid arrows point from a submodule to the (sub)module which it is
descended from. Dashed arrows point from a module or program unit to
modules which it uses.
Where possible, edges connecting nodes are
given different colours to make them easier to distinguish in
large graphs.
Variables
Type
Visibility
Attributes
Name
Initial
real(kind=wp),
private,
parameter
::
CFL_PANIC_THRESHOLD
=
0.9_wp
MaxCFL above this triggers an early-stop panic. Just below the
stability limit (1.0) so we abort before NaN on doomed runs.
Cumulative (time-integrated, since t=0) budget terms that CLOSE the
domain integral of each extensive quantity, so the reported Error
is a true numerical-leak residual even with open boundaries + surface
forcing. For quantity Q the residual is
Holds initial-snapshot values for relative-drift reporting.
is_initialised flips on the first report call.
Components
Type
Visibility
Attributes
Name
Initial
logical,
public
::
exact_sums
=
.false.
.true. once mass_efp/salt_efp/heat_efp have been
latched (the ocean EFP path is active); gates whether
emit_drift_line’s caller may rely on mass0_efp etc. being
meaningful. Coastal callers never set this (stays .false.
for the lifetime of a coastal run’s console_stats_t).
alongside mass0/salt0/heat0 on the first report when the
caller supplies mass_efp/salt_efp/heat_efp (the ocean
reproducing_sums = .true. path). Their sole purpose is
efp_real_diff(current_efp, mass0_efp) – a difference formed
in FIXED POINT, not current - mass0 in double. This is the
SS2.2 fix: latching the reference as a real(wp) alone (as
mass0 does) already quantises it to ~1 ulp of a ~1e21 total
(~1.3e5 kg) before any subtraction happens, so an exact SUM
feeding an unchanged double-difference would not move the
drift floor at all.
alongside mass0/salt0/heat0 on the first report when the
caller supplies mass_efp/salt_efp/heat_efp (the ocean
reproducing_sums = .true. path). Their sole purpose is
efp_real_diff(current_efp, mass0_efp) – a difference formed
in FIXED POINT, not current - mass0 in double. This is the
SS2.2 fix: latching the reference as a real(wp) alone (as
mass0 does) already quantises it to ~1 ulp of a ~1e21 total
(~1.3e5 kg) before any subtraction happens, so an exact SUM
feeding an unchanged double-difference would not move the
drift floor at all.
real(kind=wp),
public
::
mass_out0
=
0.0_wp
real(kind=wp),
public
::
mass_src0
=
0.0_wp
logical,
public
::
panic_on_cfl
=
.true.
Early-stop abort when MaxCFL exceeds the panic threshold. Right
for the ocean (advective CFL IS its stability criterion), but
the coastal path sets this .false.: coastal stability is
gravity-wave-limited, so a high advective CFL is normal (fast
shallow flows) and must not trip a spurious abort.
logical,
public
::
panic_on_nan
=
.true.
Early-stop abort when any stat is NaN (a NaN is always fatal).
logical,
public
::
report_thermodynamics
=
.true.
.false. suppresses the Salt / Temp columns + detail blocks
(a 2D-barotropic / adiabatic run has no meaningful S/T, so it
doesn’t print misleading lines).
alongside mass0/salt0/heat0 on the first report when the
caller supplies mass_efp/salt_efp/heat_efp (the ocean
reproducing_sums = .true. path). Their sole purpose is
efp_real_diff(current_efp, mass0_efp) – a difference formed
in FIXED POINT, not current - mass0 in double. This is the
SS2.2 fix: latching the reference as a real(wp) alone (as
mass0 does) already quantises it to ~1 ulp of a ~1e21 total
(~1.3e5 kg) before any subtraction happens, so an exact SUM
feeding an unchanged double-difference would not move the
drift floor at all.
real(kind=wp),
public
::
salt_out0
=
0.0_wp
real(kind=wp),
public
::
salt_src0
=
0.0_wp
Functions
private pure function any_nan(a, b, c, d, e) result(res)
ieee_is_nan OR-reduced across the five stats scalars.
Arguments
Type
Intent
Optional
Attributes
Name
real(kind=wp),
intent(in)
::
a
real(kind=wp),
intent(in)
::
b
real(kind=wp),
intent(in)
::
c
real(kind=wp),
intent(in)
::
d
real(kind=wp),
intent(in)
::
e
Return Value
logical
private pure function relative_drift(change, init) result(rel)
change / init with a zero guard for a never-set baseline.
Emit one MOM6-style console block from pre-computed totals. The
caller has already area-weighted + reduced each scalar over its
own state; this routine only latches the t=0 reference (first
call), formats the lines, and runs the panic guards. When a
budget with active=.true. is passed, the Error column becomes
the boundary-flux + surface-source-corrected residual (see
conservation_budget_t) and the outflux / source are shown.
Cumulative boundary outflux + surface source that close the
extensive budgets; absent / inactive ⇒ raw drift.
logical,
intent(in),
optional
::
is_root
Multi-rank print gate: .false. on non-root ranks suppresses all
logger output while the collective panic error stop still fires
on every rank (so a multi-rank abort stays consistent). The
caller must have globally reduced the scalars first. Absent ⇒
.true. (single-rank / coastal path, bit-identical).
logical,
intent(in),
optional
::
advective_cfl
Whether the reported CFL is the ADVECTIVE CFL (|u|dt/dx+|v|dt/dy)
rather than a wave-CFL stability limit. Absent ⇒ .false.
(explicit / ocean paths: label “MaxCFL”, F8.5, CFL panic active —
bit-identical). The semi-implicit coastal path passes .true.:
it makes the free surface implicit, so the fast gravity-wave CFL
is not a constraint and only the advective CFL remains — labelled
“AdvCFL” (wider field, since dt can legitimately push it O(10))
and the wave-CFL panic is skipped (the NaN guard still fires).
total_mass/total_salt/total_heat (already globally
combined by the caller via halo_allreduce_efp_list). Absent
(default — every existing caller) ⇒ exact_sums stays
.false. and the console block is byte-identical to pre-PR-32.
When present, latches mass0_efp/etc. on the first call and
makes emit_drift_line form its residual via efp_real_diff
against the EFP reference rather than (total - ref) in
double — the SS2.2 fix.
total_mass/total_salt/total_heat (already globally
combined by the caller via halo_allreduce_efp_list). Absent
(default — every existing caller) ⇒ exact_sums stays
.false. and the console block is byte-identical to pre-PR-32.
When present, latches mass0_efp/etc. on the first call and
makes emit_drift_line form its residual via efp_real_diff
against the EFP reference rather than (total - ref) in
double — the SS2.2 fix.
total_mass/total_salt/total_heat (already globally
combined by the caller via halo_allreduce_efp_list). Absent
(default — every existing caller) ⇒ exact_sums stays
.false. and the console block is byte-identical to pre-PR-32.
When present, latches mass0_efp/etc. on the first call and
makes emit_drift_line form its residual via efp_real_diff
against the EFP reference rather than (total - ref) in
double — the SS2.2 fix.
One <Label> : <total> Error <residual> conservation line. With
budget_on the residual closes the budget — (total − ref) + out −
src — and the cumulative outflux / surface source are appended;
without it the residual is raw drift total − ref and the line is
byte-identical to the pre-budget format. label is a 4-char tag
(Mass / Salt / Heat) so the colons align.