| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| logical, | public | :: | accel_visc_rem | = | .false. |
MOM6 |
|
| real(kind=wp), | public | :: | angstrom_h | = | 0.0_wp |
Phase-2/3 copy of |
|
| real(kind=wp), | public, | allocatable | :: | avr_u0(:,:,:) | |||
| real(kind=wp), | public, | allocatable | :: | avr_v0(:,:,:) |
accel_visc_rem stage-entry velocity snapshots. Eagerly
allocated at setup ( |
||
| integer, | public | :: | bt_halo | = | 0 |
Wide-halo march-in width (0 = per-substep v1, bit-identical).
Set by |
|
| integer, | public | :: | bt_nonfin_step | = | 0 |
Faces whose BT-correction Δ was non-finite this stage (fold write skipped; pdc 2d5338f9). 0 on a healthy run. |
|
| type(bt_wide_t), | public, | allocatable | :: | bt_wide |
Wide shadow state; allocated only when |
||
| type(barotropic_workstate_t), | public | :: | bt_work | ||||
| logical, | public | :: | cfl_ignore_vanished | = | .false. |
Phase 3: when |
|
| real(kind=wp), | public | :: | cfl_inner_max | = | 0.0_wp |
Max realised inner CFL within the last outer step. |
|
| real(kind=wp), | public | :: | cfl_outer | = | 0.0_wp |
Realised outer CFL (diagnostic). |
|
| real(kind=wp), | public | :: | cfl_trunc | = | 0.0_wp |
Advective-CFL truncation threshold (nondim). When > 0, any
face whose |
|
| logical, | public | :: | check_h_positive | = | .false. |
DEBUG copy of |
|
| type(chksum_probe_t), | public | :: | chksum_probe |
MOM6-style per-phase field checksums + HOTFACE argmax rows
( |
|||
| logical, | public | :: | debug_bt_budget | = | .false. |
When |
|
| real(kind=wp), | public | :: | dt_inner | = | 0.0_wp |
Inner barotropic substep length (s). |
|
| real(kind=wp), | public | :: | dt_outer | = | 0.0_wp |
Outer baroclinic timestep length (s). |
|
| integer, | public | :: | dt_therm_ratio | = | 1 |
Tracer/thermo step runs every |
|
| integer, | public | :: | dt_tracer_advect_ratio | = | 1 |
MOM6 DT_TRACER_ADVECT analogue. Horizontal tracer advection
fires every |
|
| logical, | public | :: | enable_thermodynamics | = | .true. |
Mirrors MOM6 |
|
| real(kind=wp), | public | :: | ideal_age_sfc_growth_rate | = | 0.0_wp |
Exponential growth rate of the surface value (1/s), set by
|
|
| real(kind=wp), | public | :: | ideal_age_young_val | = | 0.0_wp |
Surface-band age value (s), set by |
|
| logical, | public | :: | is_init | = | .false. |
True between |
|
| real(kind=wp), | public | :: | ke_outer | = | 0.0_wp |
Total kinetic energy after the last outer step (diagnostic). |
|
| type(ke_probe_t), | public | :: | ke_probe |
Per-segment layer-KE attribution meter ( |
|||
| real(kind=wp), | public | :: | maxvel | = | 0.0_wp |
When > 0, face velocities are clipped to |
|
| integer, | public | :: | n_inner | = | 0 |
Number of barotropic substeps per outer baroclinic step (0 = “auto, derived from CFL”). |
|
| integer, | public | :: | n_nanzero_step | = | 0 |
Non-finite (NaN/Inf) face velocities zeroed by the truncation’s NaN-catch in the most recent outer step. MUST be 0 on a healthy run; any non-zero is a producer 0/0 upstream (loud — NaNs used to launder to ±maxvel silently, pdc 8c2fd674). |
|
| integer(kind=int64), | public | :: | n_nanzero_total | = | 0_int64 |
Cumulative NaN-catch count over the run. |
|
| integer, | public | :: | ntrunc_step | = | 0 |
Face components CFL-truncated in the most recent outer step
(host-scalar reduction; reset each |
|
| integer, | public | :: | ntrunc_total | = | 0 |
Cumulative CFL-truncation count over the run (driver logs it). |
|
| integer, | public | :: | outer_step_count | = | 0 |
Outer (baroclinic) step counter. |
|
| real(kind=wp), | public | :: | pc_be | = | 0.6_wp |
pred_corr predictor fraction (MOM6 |
|
| logical, | public | :: | poison_ghosts | = | .false. |
When |
|
| logical, | public | :: | reset_vanished_u | = | .false. |
Phase 2: when |
|
| integer, | public | :: | split_scheme | = | SPLIT_SCHEME_PRED_CORR |
Outer time-scheme selector (see the module constants). Kept in
step with the Why the default moved (2026-09-14). |
Counted allocatable footprint of the split-RK2 driver (BT work state + wide-halo shadow state) slot (0 when unallocated).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_dyn_t), | intent(in) | :: | this |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_dyn_t), | intent(inout) | :: | this |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_dyn_t), | intent(inout) | :: | this |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_dyn_t), | intent(inout) | :: | this |
Initialise the barotropic working-state slot. Pass nz_ml
to also allocate the split-driver slow-tendency
accumulators (F_slow_u/v, F_bt_u/v, ubt_at_n/vbt_at_n).
Barotropic-substep unit tests can skip the optional argument since
they don’t exercise the split-driver coupling.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_dyn_t), | intent(inout) | :: | this | |||
| type(hgrid_t), | intent(in) | :: | grid | |||
| integer, | intent(in), | optional | :: | nz_ml |
Returns .true. if the thermodynamic / tracer kernels should
fire on this outer step. dt_therm_ratio <= 1 (default) →
always true (every step is a thermo step, bit-identical to
prior behaviour). ratio >= 2 → fires every Nth step,
aligned to outer_step_count = 0 for the IC snapshot.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_dyn_t), | intent(in) | :: | this |
Returns .true. when the windowed horizontal tracer-advect drain
should fire on the current outer step, evaluated with the
PRE-increment outer_step_count so it aligns bit-for-bit with
is_thermo_step() (which gates the ALE remap on the same count).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_dyn_t), | intent(in) | :: | this |
Effective dt for the thermo / tracer kernels. When
dt_therm_ratio = 1 returns dt exactly; otherwise returns
ratio · dt, since the kernels only fire every Nth step and
must advance by that aggregate interval.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_dyn_t), | intent(in) | :: | this | |||
| real(kind=wp), | intent(in) | :: | dt |
type :: ocean_dyn_t logical :: is_init = .false. !! True between `init` and `destroy`. Prefer this to !! `allocated(...)` — tracks GPU device attachment too. integer :: split_scheme = SPLIT_SCHEME_PRED_CORR !! Outer time-scheme selector (see the module constants). Kept in !! step with the `&ocean_bt_nml split_scheme` default in !! `rdb_config.F90` — `ocean_setup` assigns this field from the !! config on BOTH branches (`rdb_ocean_setup.F90`, "SPEC S3"), so no !! production run reads this value, but a bare `ocean_dyn_t` built by !! a unit test inherits it and a divergence would have the Fortran !! suite exercising a scheme the model does not ship by default. !! The two have diverged before; change them in the same commit. !! !! **Why the default moved (2026-09-14).** `ssp_rk2` grows !! internal gravity waves out of a stratified REST state — En !! 2.992E-05 against pred_corr's 1.739E-09 at day 25 on !! `resting_stratified_channel.nml`, 17 000× — and the three !! things that kept the predictor-corrector off the default are !! all closed: the land-mask NaN (the fast-loop Coriolis !! reference was evaluated on the stage-entry `u^n` while the !! slow Coriolis used `u_av`; see `set_cor_ref_velocity`), the !! `nz = 1` vdiff tridiagonal defect, and SEVEN GPU-only !! unit-test failures — `periodic`, `obc_baroclinic`, !! `dyn_split`, `ice_restart`, `wetdry` (non-finite within 1-5 !! steps), plus `restart` bit-exactness and uniform-`p_surf` !! gauge invariance — which had ONE cause, and it was not scheme !! semantics: `scratch_3d_buffer_t` attached its payload with !! `!$acc enter data create`, which carries no host value, and !! the pred_corr PREDICTOR deliberately reads !! `hv%du_visc`/`dv_visc` without recomputing them (MOM6's !! `diffu(u[n-1])` reuse). At step 1 there is no previous !! producer, so the host read the zero `init` promised and the !! device read the allocator's leftovers. `enter_data` now !! device-zeroes the payload (`rdb_test_scratch_3d_device` is the !! gate), and `ctest -R rdb` is 187/187 on BOTH toolchains. real(wp) :: pc_be = 0.6_wp !! pred_corr predictor fraction (MOM6 `BE`, 0.6 in the control !! run): the predictor advances the provisional velocity to !! `dt_pred = pc_be·dt` (SPEC §2 P8); only the corrector takes !! the full step. Unused under ssp_rk2. integer :: n_inner = 0 !! Number of barotropic substeps per outer baroclinic step !! (0 = "auto, derived from CFL"). integer :: outer_step_count = 0 !! Outer (baroclinic) step counter. real(wp) :: dt_inner = 0.0_wp !! Inner barotropic substep length (s). real(wp) :: dt_outer = 0.0_wp !! Outer baroclinic timestep length (s). ! ---- Barotropic working state ---- ! 2D barotropic-substep fields, time-mean accumulators, nonlinear ! scratch, and slow-tendency accumulators. The substep kernels ! (`rdb_barotropic_substep`) + coupling kernels ! (`rdb_barotropic_coupling`) operate on `dyn%bt_work` directly. type(barotropic_workstate_t) :: bt_work ! ---- Diagnostic accumulators ---- real(wp) :: ke_outer = 0.0_wp !! Total kinetic energy after the last outer step (diagnostic). real(wp) :: cfl_outer = 0.0_wp !! Realised outer CFL (diagnostic). real(wp) :: cfl_inner_max = 0.0_wp !! Max realised inner CFL within the last outer step. ! ---- Velocity-truncation clamp (MOM6 MAXVEL) ---- real(wp) :: maxvel = 0.0_wp !! When > 0, face velocities are clipped to `[-maxvel, +maxvel]` !! after each outer step (safety net, not a fix). 0 = off. real(wp) :: cfl_trunc = 0.0_wp !! Advective-CFL truncation threshold (nondim). When > 0, any !! face whose `|u|·dt/dx` exceeds it is clipped to !! `0.9·cfl_trunc·dx/dt` (sign preserved), then the `maxvel` !! cap is applied. 0 = off (bit-identical). integer :: ntrunc_step = 0 !! Face components CFL-truncated in the most recent outer step !! (host-scalar reduction; reset each `apply_velocity_truncation`). integer :: ntrunc_total = 0 !! Cumulative CFL-truncation count over the run (driver logs it). integer :: n_nanzero_step = 0 !! Non-finite (NaN/Inf) face velocities zeroed by the truncation's !! NaN-catch in the most recent outer step. MUST be 0 on a !! healthy run; any non-zero is a producer 0/0 upstream (loud — !! NaNs used to launder to ±maxvel silently, pdc 8c2fd674). integer(int64) :: n_nanzero_total = 0_int64 !! Cumulative NaN-catch count over the run. integer :: bt_nonfin_step = 0 !! Faces whose BT-correction Δ was non-finite this stage (fold !! write skipped; pdc 2d5338f9). 0 on a healthy run. ! ---- DT_THERM ratio (MOM6) ---- integer :: dt_therm_ratio = 1 !! Tracer/thermo step runs every `dt_therm_ratio` outer steps !! with effective dt = ratio · dt. 1 (default) = every step !! (bit-identical). Applies only when `enable_thermodynamics`. integer :: dt_tracer_advect_ratio = 1 !! MOM6 DT_TRACER_ADVECT analogue. Horizontal tracer advection !! fires every `dt_tracer_advect_ratio` outer steps over the !! accumulated face transports (`continuity_t%uhtr/vhtr`). !! 1 (default) ⇒ every-step path verbatim (bit-identical). !! Configure requires `dt_therm_ratio` to be an integer multiple !! so the ALE remap never fires mid-accumulation-window. ! ---- Debug probe ---- logical :: debug_bt_budget = .false. !! When `.true.`, calls the BT-budget probe once per RK2 stage !! after the slow tendencies. Heavy (D->H transfers + prints). !! Default `.false.`; driven by `ocean_debug_bt_budget` namelist. type(ke_probe_t) :: ke_probe !! Per-segment layer-KE attribution meter (`&ocean_debug_nml !! ke_attr`). Default off ⇒ bit-identical; when on, !! serialises the async velocity-apply chain (device waits + !! one reduction per segment) — debug only. type(chksum_probe_t) :: chksum_probe !! MOM6-style per-phase field checksums + HOTFACE argmax rows !! (`&ocean_debug_nml chksum` + step window). Default off ⇒ !! bit-identical; when on, waits the device + reduces at each !! phase seam — the first-diverging-operator attribution probe. logical :: accel_visc_rem = .false. !! MOM6 `MOM_dynamics_split_RK2` parity: attenuate the slow !! EXPLICIT accelerations by the per-layer viscous remnant — !! `u_new = u_entry + visc_rem·(u_applied − u_entry)` after the !! CorAdv/PGF/hvisc/drag applies, before the BT correction — !! so friction-dominated near-massless layers cannot receive a !! full-strength dt·F kick (the 2026-07-28 forensics: explicit !! force × dt on outcropped mm-layers is the dt=800 blow-up !! injector; the fold/vdiff mopped ±30 m/s per stage until !! escape). `&ocean_vdiff_nml accel_visc_rem` — RETIRED (D1 !! follow-up): no MOM6 state-update equivalent, fail-loud at !! configure. Default off ⇒ bit-identical. Split path only (v1). real(wp), allocatable :: avr_u0(:, :, :) real(wp), allocatable :: avr_v0(:, :, :) !! accel_visc_rem stage-entry velocity snapshots. Eagerly !! allocated at setup (`configure_ocean_bt`) and device-mapped in !! `ocean_dyn_enter_data_impl` — but ONLY when the `accel_visc_rem` !! knob is on (unallocated / zero-footprint on the default-off !! path); released in destroy/exit_data. ! ---- Ghost-band poison (debug) ---- logical :: poison_ghosts = .false. !! When `.true.`, sentinel-NaN the exchange-covered ghost bands at !! each outer-step start (before any exchange or kernel). Any kernel !! consuming an unexchanged ghost produces a loud NaN at that step. !! Default `.false.` = bit-identical (untaken branch per step). !! Driven by `&ocean_mpi_nml poison_ghosts` via `rdb_ocean_setup`. !! Requires the `bc` optional argument (edge topology); no-op when !! `bc` is absent. ! ---- Thermodynamic switch ---- logical :: enable_thermodynamics = .true. !! Mirrors MOM6 `ENABLE_THERMODYNAMICS`. When `.false.` the step !! skips EOS, tracer hdiff, tracer vertical advection/diffusion, !! KPP non-local transport, and surface tracer flux. Tracers !! still advect horizontally but with `alpha_T = beta_S = 0` can't !! influence dynamics ⇒ adiabatic. Wire via !! `&ocean_setup_nml ocean_enable_thermodynamics`. ! ---- Phase-2/3 Lagrangian grounding-stability knobs ---- real(wp) :: angstrom_h = 0.0_wp !! Phase-2/3 copy of `cfg%ocean%isopycnal%angstrom_h` (m). !! Used to compute `isopycnal_vanish_tol` for the reset and CFL !! gates; 0 ⇒ off ⇒ bit-identical. logical :: reset_vanished_u = .false. !! Phase 2: when `.true.` AND vcoord is VCOORD_LAGRANGIAN, call !! `reset_vanished_layer_velocities` after each !! `mask_layer_velocities` (per-stage + RK2-averaged site) to !! zero face velocities where BOTH adjacent cell-thicknesses are !! at or below `isopycnal_vanish_tol(angstrom_h)`. logical :: cfl_ignore_vanished = .false. !! Phase 3: when `.true.` AND vcoord is VCOORD_LAGRANGIAN, pass !! `vanish_tol` to `compute_max_cfl` and `apply_velocity_truncation` !! so vanished-layer face spikes are excluded from MaxCFL / !! panic and are zeroed (not CFL-clipped) by truncation. logical :: check_h_positive = .false. !! DEBUG copy of `cfg%ocean%isopycnal%check_h_positive`. When !! `.true.`, `check_h_positive_or_die` runs after each `h_layer`- !! writing stage and aborts on the first negative thickness, naming !! the stage. `.false.` ⇒ never called ⇒ bit-identical. ! ---- Wide-halo BT march-in (Phase 3c) ---- integer :: bt_halo = 0 !! Wide-halo march-in width (0 = per-substep v1, bit-identical). !! Set by `ocean_dyn_enable_bt_wide` after `init` and `enter_data`. type(bt_wide_t), allocatable :: bt_wide !! Wide shadow state; allocated only when `bt_halo > 0`. ! ---- Ideal-age tracer (PR-7) ---- real(wp) :: ideal_age_young_val = 0.0_wp !! Surface-band age value (s), set by `configure_ocean_tracers` !! from `&ocean_tracers_nml ideal_age_young_val`. 0 (default) = !! today's hard-coded zero reset ⇒ bit-identical. real(wp) :: ideal_age_sfc_growth_rate = 0.0_wp !! Exponential growth rate of the surface value (1/s), set by !! `configure_ocean_tracers` from !! `&ocean_tracers_nml ideal_age_sfc_growth_rate`. 0 (default) !! ⇒ young_val constant ⇒ no `exp()` ⇒ bit-identical. contains procedure, non_overridable :: init => ocean_dyn_init procedure, non_overridable :: destroy => ocean_dyn_destroy procedure, non_overridable :: enter_data => ocean_dyn_enter_data procedure, non_overridable :: exit_data => ocean_dyn_exit_data procedure, non_overridable :: is_thermo_step => ocean_dyn_is_thermo_step procedure, non_overridable :: therm_dt => ocean_dyn_therm_dt procedure, non_overridable :: is_tracer_advect_step => ocean_dyn_is_tracer_advect_step procedure, non_overridable :: bytes => ocean_dyn_bytes end type ocean_dyn_t