rdb_console_stats Module

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.


Uses

  • module~~rdb_console_stats~~UsesGraph module~rdb_console_stats rdb_console_stats ieee_arithmetic ieee_arithmetic module~rdb_console_stats->ieee_arithmetic module~rdb_constants rdb_constants module~rdb_console_stats->module~rdb_constants module~rdb_efp rdb_efp module~rdb_console_stats->module~rdb_efp pic_logger pic_logger module~rdb_console_stats->pic_logger pic_strings pic_strings module~rdb_console_stats->pic_strings pic_types pic_types module~rdb_constants->pic_types module~rdb_efp->ieee_arithmetic iso_fortran_env iso_fortran_env module~rdb_efp->iso_fortran_env

Used by

  • module~~rdb_console_stats~~UsedByGraph module~rdb_console_stats rdb_console_stats module~rdb_driver rdb_driver module~rdb_driver->module~rdb_console_stats module~rdb_ocean_console_stats rdb_ocean_console_stats module~rdb_driver->module~rdb_ocean_console_stats module~rdb_ocean_dyn rdb_ocean_dyn module~rdb_driver->module~rdb_ocean_dyn module~rdb_ocean_engine rdb_ocean_engine module~rdb_driver->module~rdb_ocean_engine module~rdb_ocean_state rdb_ocean_state module~rdb_driver->module~rdb_ocean_state module~rdb_ocean_console_stats->module~rdb_console_stats module~rdb_ocean_dyn->module~rdb_ocean_console_stats module~rdb_ocean_api rdb_ocean_api module~rdb_ocean_api->module~rdb_ocean_dyn module~rdb_ocean_api->module~rdb_ocean_engine module~rdb_handle rdb_handle module~rdb_ocean_api->module~rdb_handle module~rdb_ocean_diag_derived rdb_ocean_diag_derived module~rdb_ocean_api->module~rdb_ocean_diag_derived module~rdb_ocean_diag_fills rdb_ocean_diag_fills module~rdb_ocean_api->module~rdb_ocean_diag_fills module~rdb_ocean_engine->module~rdb_ocean_dyn module~rdb_ocean_setup rdb_ocean_setup module~rdb_ocean_engine->module~rdb_ocean_setup module~rdb_ocean_engine->module~rdb_ocean_state module~rdb_ocean_engine->module~rdb_ocean_diag_derived module~rdb_ocean_engine->module~rdb_ocean_diag_fills module~rdb_ocean_setup->module~rdb_ocean_dyn module~rdb_ocean_setup->module~rdb_ocean_state module~rdb_ocean_state->module~rdb_ocean_dyn module~rdb_handle->module~rdb_ocean_engine module~rdb_handle->module~rdb_ocean_state module~rdb_ocean_diag_derived->module~rdb_ocean_state module~rdb_ocean_diag_derived->module~rdb_ocean_diag_fills module~rdb_ocean_diag_fills->module~rdb_ocean_state

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.


Derived Types

type, public ::  conservation_budget_t

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

Read more…

Components

Type Visibility Attributes Name Initial
logical, public :: heat_active = .false.

budget residual + shows out/src; .false. ⇒ plain drift. Per-quantity so a phased rollout (mass first) doesn’t print misleading out 0 for a quantity whose flux isn’t tracked yet.

real(kind=wp), public :: heat_out = 0.0_wp

Net OUTFLUX through open boundaries (positive = left the domain).

real(kind=wp), public :: heat_src = 0.0_wp

Net SURFACE source (positive = added to the domain: precip, surface heat/salt flux).

logical, public :: mass_active = .false.
real(kind=wp), public :: mass_out = 0.0_wp
real(kind=wp), public :: mass_src = 0.0_wp
logical, public :: salt_active = .false.
real(kind=wp), public :: salt_out = 0.0_wp
real(kind=wp), public :: salt_src = 0.0_wp

type, public ::  console_stats_t

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).

real(kind=wp), public :: heat0 = 0.0_wp
type(efp_t), public :: heat0_efp

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 :: heat_out0 = 0.0_wp
real(kind=wp), public :: heat_src0 = 0.0_wp
logical, public :: is_initialised = .false.
real(kind=wp), public :: ke0 = 0.0_wp
real(kind=wp), public :: mass0 = 0.0_wp
type(efp_t), public :: mass0_efp

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).

real(kind=wp), public :: salt0 = 0.0_wp
type(efp_t), public :: salt0_efp

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 IntentOptional 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.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: change
real(kind=wp), intent(in) :: init

Return Value real(kind=wp)


Subroutines

public subroutine console_stats_report(this, t, step, total_mass, total_ke, mean_S, mean_T, max_cfl, total_salt, total_heat, has_salt, has_temp, mean_age, has_age, budget, is_root, advective_cfl, mass_efp, salt_efp, heat_efp)

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.

Arguments

Type IntentOptional Attributes Name
type(console_stats_t), intent(inout) :: this
real(kind=wp), intent(in) :: t

Simulation time (s); the compact line reports Day = t/86400.

integer, intent(in) :: step
real(kind=wp), intent(in) :: total_mass
real(kind=wp), intent(in) :: total_ke

Absolute kinetic energy (J); the compact “En” column reports KE per unit mass (m²/s²), the detail block the absolute value.

real(kind=wp), intent(in) :: mean_S

Volume-mean salinity / temperature (compact-line columns).

real(kind=wp), intent(in) :: mean_T

Volume-mean salinity / temperature (compact-line columns).

real(kind=wp), intent(in) :: max_cfl
real(kind=wp), intent(in) :: total_salt

Column-integrated salt / heat (drift-block totals).

real(kind=wp), intent(in) :: total_heat

Column-integrated salt / heat (drift-block totals).

logical, intent(in) :: has_salt

Whether a salinity / temperature tracer is registered.

logical, intent(in) :: has_temp

Whether a salinity / temperature tracer is registered.

real(kind=wp), intent(in) :: mean_age

Volume-mean ideal age (s); printed in days when has_age.

logical, intent(in) :: has_age
type(conservation_budget_t), intent(in), optional :: budget

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).

type(efp_t), intent(in), optional :: mass_efp

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.

type(efp_t), intent(in), optional :: salt_efp

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.

type(efp_t), intent(in), optional :: heat_efp

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.

private subroutine emit_drift_line(label, total, ref, q_out, q_src, budget_on, residual_exact)

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.

Read more…

Arguments

Type IntentOptional Attributes Name
character(len=*), intent(in) :: label
real(kind=wp), intent(in) :: total
real(kind=wp), intent(in) :: ref
real(kind=wp), intent(in) :: q_out
real(kind=wp), intent(in) :: q_src
logical, intent(in) :: budget_on
real(kind=wp), intent(in), optional :: residual_exact