Discrete Hydrostatic Consistency in horiz_press_grad
Creation date: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
Summary
The Omega design document for the higher-order horizontal pressure gradient
(components/omega/doc/design/PGradHighOrder.md, §2.3 and §3.7) requires
discrete hydrostatic consistency: for horizontally uniform conservative
temperature and absolute salinity, with coordinate surfaces tilted arbitrarily
between adjacent columns, the scheme must return exactly zero horizontal
pressure-gradient acceleration (HPGA), to machine precision, with no background
reference state. Its §5.2 makes that a gating test and names the Polaris
two-column task as one of the places it should be exercised.
This design adds two variants to the ocean/horiz_press_grad task family that
measure that property, together with the reference-free analysis they need. The
new capability is:
Two exact resting states in which the true HPGA is identically zero, so the model’s HPGA is the error and no reference solution is required:
hydrostatic_consistency(tilted coordinate, flat sea floor) andbathymetry_step(flat coordinate, stepped sea floor).A tilt scan: at fixed resolution, the tilt is swept over roughly three decades and the exponent \(q\) in \(\lVert\text{HPGA}\rVert \sim (\text{tilt})^{q}\) is measured. The exponent, not a single pass/fail, is the discriminating output.
A reference-free analysis step,
RestingAnalysis, which compares against zero rather than againstReferenceColumn, retains the Omega-vs-Polaris consistency check, and guards the resting-state premise itself.
The primary challenge is not the numerics but the severity of the
configuration. The existing ztilde_gradient variant is already structurally
a resting state with a tilted coordinate, and the centered scheme passes it with
an RMS HPGA of \(\sim 7\times10^{-12}\) m s\(^{-2}\) — six orders of magnitude below
the \(\sim 3\times10^{-6}\) m s\(^{-2}\) bottom-layer error seen in realistic global
Omega runs. A new variant that inherits that configuration would pass trivially
and measure nothing. The design is successful if the centered scheme is
driven to an RMS HPGA of at least \(10^{-6}\) m s\(^{-2}\) somewhere in the sweep
(the sensitivity gate), if the measured tilt exponent is stable and
interpretable, and if the resulting diagnostic is ready to be pointed at the
FiniteVolume scheme when it exists.
Two stages, and a note on the word “phase”
This design was written and first implemented before Omega’s FiniteVolume
scheme existed, and extended once it did. The two Polaris stages are named
rather than numbered, because PGradHighOrder.md uses Phase 1 and Phase
2 for the scheme’s order — Phase 1 being HorzOrder: 2 with a linear
vertical reconstruction, Phase 2 order 4 with PPM — and two independent pairs of
ordinals in one document is a reliable source of confusion.
stage |
what it delivered |
|---|---|
the calibration stage |
the two resting-state variants, the reference-free analysis, and the |
the rollout stage |
scheme selection and the §5.1 gates set from a |
Everything here concerns Omega Phase 1. “Phase” below always means the Omega sense.
The calibration stage’s output is what makes the rollout readable: with the
centered exponent measured at 1.000 to five digits, a FiniteVolume
measurement is read against a known-good value rather than against an
expectation.
What the rollout stage adds
A scheme axis. Each variant runs both
CenteredandFiniteVolumeover one shared initial condition, so the two are compared at an identical state.A second Python kernel to compare against.
init.pywritesHPGAFiniteVolumealongsideHPGA, and each analysis step compares Omega against whichever matches the scheme that produced its output.
The two variants deliberately isolate two different mechanisms, which the sizing work below showed are not the same mechanism:
|
|
|
|---|---|---|
what tilts |
the p-star reference grid, via |
nothing; only the sea floor moves |
where the error lives |
every layer, growing with depth |
the deepest layer valid in both columns, only |
behaviour vs. the swept parameter |
a clean power law |
a staircase, set by where |
what it represents |
thin, steeply sloped layers in a general ALE coordinate |
the bottom partial cell at a bathymetry step |
Requirements
Requirement: The test configuration is an exact resting state
Date last modified: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
Each new variant shall be a configuration whose true, continuous HPGA is identically zero at every point in the water column, so that the model’s computed HPGA is entirely error and no reference solution is needed. The resting-state property shall be a consequence of the configuration (horizontally uniform conservative temperature and absolute salinity specified as functions of pseudo-height, with no surface-pressure gradient), not an approximation.
Requirement: The geometry under test is verified, not assumed
Date last modified: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
The analysis shall confirm at run time that the geometry actually built is the geometry the configuration asked for, and shall fail loudly if it is not. A sweep whose swept parameter has been silently altered by the vertical-grid construction reports an error level for a configuration that was never tested, in either direction — too large or too small.
Requirement: The test reaches the severity at which the centered scheme fails
Date last modified: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
At its most severe configuration, the sweep shall drive the existing centered scheme to an RMS HPGA of at least \(10^{-6}\) m s\(^{-2}\) — the order of the bottom-layer HPGA error observed in realistic global Omega runs with a uniform temperature and salinity state. A sweep that does not reach this level is not exercising the failure mode the higher-order scheme is meant to fix, and any conclusion drawn from it about the new scheme would be unsupported.
Requirement: The measurement discriminates between mechanisms, not just pass and fail
Date last modified: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
The analysis shall report the exponent \(q\) in \(\lVert\text{HPGA}\rVert \sim (\text{tilt})^{q}\), restricted to the portion of the sweep where the result is well above the round-off floor, in addition to absolute error levels. A single pass/fail number cannot distinguish “the scheme represents specific volume as piecewise constant in pressure” from “the scheme has a higher-order residual” from “the scheme is exact”, and those are the outcomes the test exists to separate.
Requirement: The deepest layer valid in both columns is measured, not discarded
Date last modified: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
The error metric shall include the deepest layer that is valid in both columns bounding the edge. That layer is the bottom partial cell, and it is where the pressure-gradient error concentrates; excluding it removes the signal these tests exist to measure.
This applies to the reference-based Analysis step as well as to the new
resting-state analysis. Analysis currently drops that layer, which is a
leftover from the previous five-column finite-difference reference whose
stencil could not be formed where a neighbouring column’s collocation point
fell below its own bathymetry. The reference is now a single analytic column at
the edge, documented as valid to the seafloor, so the exclusion no longer has a
basis. Fixing it is in scope here because it is the same decision, and because
leaving the two analysis steps disagreeing about which layers count would be
incoherent.
Requirement: The existing variants and their pass criteria are untouched
Date last modified: 2026/08/02
Contributors: Xylar Asay-Davis, Claude
The four existing variants (salinity_gradient, surface_pressure_gradient,
temperature_gradient, ztilde_gradient), the ReferenceColumn evaluator, and
the convergence-rate and reference thresholds in horiz_press_grad.cfg shall
behave exactly as they do today. The new capability shall not change their step
names, work directories, or results.
Amended for the rollout stage. This requirement held for the calibration stage and is deliberately relaxed by the rollout, in two respects:
the scheme axis puts a scheme label in every forward step’s directory, so the step names do change and the recorded centered baselines have to be regenerated. This is the cost of running both schemes over one shared
Initrather than duplicating the initial condition per scheme;the convergence bands become per-variant and per-scheme. A single band cannot serve four variants whose measured centered rates are 1.6, 1.8, 2.0 and 1.1.
What the rollout does not change is the four variants’ results under
Centered, or the meaning of HPGA. Any change there is a regression.
Requirement: The cross-implementation check compares the same algorithm
Date last modified: 2026/08/02
Contributors: Xylar Asay-Davis, Claude
omega_vs_polaris_rms_threshold is the only check in the family that compares
Omega’s arithmetic against an independent implementation of the same scheme, and
it is worth exactly as much as the claim that the two sides are the same
algorithm. Where the Python kernel makes a discretization choice that Omega
makes differently — the quadrature rule and its order, which end of the column
the scan is anchored at, how the reconstruction is closed at the ends of a
column, how the edge-shared expansion point is formed — the check stops
measuring implementation disagreement and starts measuring the gap between two
different algorithms, which is not distinguishable from a bug in either.
Every such choice shall therefore be reconciled against PGradHighOrder.md
before the check is run in anger, and each resolution recorded with its
provenance. Two constraints on how:
The design is the source, not the C++. The Python kernel was written from
PGradHighOrder.mdprecisely so that this comparison has content. Reading the Omega implementation to settle a question of intent the design leaves open is legitimate; transcribing it is not, because a Python kernel derived from the C++ would compare an implementation against itself.Reconcile up front rather than by bisection. The check produces one scalar per resolution. It can report that the two sides disagree; it cannot report which of several choices disagrees, and several of them first bite in the same configurations.
Where a reconciliation finds that the Python follows the design and Omega does not, it stops being a Polaris change and is handed back rather than papered over by adjusting the Python to match.
Algorithm Design
Algorithm Design: Why these configurations are exact resting states
Date last modified: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
Omega’s vertical coordinate is pseudo-height \(\tilde z = -p/(\rho_0 g)\), so
pressure is an exactly linear function of \(\tilde z\) and a surface of constant
\(\tilde z\) is a surface of constant \(p\). Init interpolates conservative
temperature \(\Theta\) and absolute salinity \(S_A\) against \(\tilde z\) from
piecewise profiles whose node values and node pseudo-heights are set by
*_mid and *_grad configuration options. Setting every *_grad to zero makes
\(\Theta\) and \(S_A\) the same functions of \(\tilde z\), and therefore of \(p\), in
both columns. Specific volume is then a function of pressure alone,
isobars are level surfaces, and the true HPGA is identically zero everywhere in the fluid — for any sea-floor shape and any tilt of the coordinate surfaces. This is what makes both variants valid resting states, and it is the discrete analogue of the property §3.7 of the Omega design asserts.
Two configuration choices are therefore forced:
temperature_grad,salinity_gradandz_tilde_gradare all zero (inherited from the basehoriz_press_grad.cfg), andsurface_pressure_gradis zero, so the sea surface is flat.The deepest profile node lies below the deepest reference-grid interface in either column, so the PCHIP interpolant is never asked to extrapolate when the tilt makes one column’s reference grid deeper than the other’s.
Algorithm Design: What actually tilts the coordinate in a p-star column
Date last modified: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
This is the design’s central finding, and it determined the shape of both variants.
init_pstar_vertical_coord builds each column by clipping a reference 1-D grid
at that column’s BottomPressure. In Init, the reference grid for column \(i\)
is uniform over \([0, -\tilde z^{\text{bot}}_i]\) with a level count fixed by the
mid-column value, so z_tilde_bot_grad stretches one column’s reference
grid relative to the other’s. The cross-edge offset of interface \(k\) then grows
linearly with depth,
which is the tilted-coordinate configuration §3.7 describes. This is the only mechanism in the two-column task that tilts interior coordinate surfaces.
A sea-floor step does not tilt them. With z_tilde_bot_grad = 0 both columns
share one reference grid, and moving the sea floor changes only where that grid
is clipped. Every interior interface sits at an identical pseudo-height — and
hence an identical pressure — in both columns, so the cross-edge pressure
difference is exactly zero above the bottom. Offline replication of the Polaris
Init arithmetic confirms this: with a sea-floor gradient of 10–200 m km\(^{-1}\)
and a flat coordinate, the RMS HPGA over the interior layers is
\(\sim 10^{-15}\) m s\(^{-2}\) (round-off), while the deepest layer valid in both
columns carries \(2\times10^{-6}\) to \(8\times10^{-5}\) m s\(^{-2}\).
Two consequences follow:
bathymetry_stepis a test of the bottom partial cell, not of interior layer tilt, and its whole signal is in the one layer the reference-based analysis discards. Hence the requirement to include that layer.Its error is not a power law in the sea-floor gradient. The signal is set by how the two columns’
maxLevelCelland clipped bottom thicknesses relate, which changes in steps. Fitting an exponent to it would be meaningless, so the fit is configurable and is disabled for that variant.
Algorithm Design: The tilt scan and the exponent
Date last modified: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
Because the exact answer is zero there is nothing to converge to, so the
discriminating measurement is not a convergence rate against a reference but a
scan of the tilt at fixed resolution. For hydrostatic_consistency the swept
parameter is z_tilde_bot_grad; the analysis fits
over the points below tilt_fit_max, and reports \(q\). The readings are:
measured \(q\) |
reading |
|---|---|
\(q \approx 1\) |
\(\alpha\) is effectively piecewise constant in pressure — no better than the centered scheme |
\(q \approx 2\) |
consistent with a residual set by the per-cell Taylor expansion point of |
round-off, independent of tilt |
consistent with §3.7 holding as written |
An exponent that is neither, or one that drifts across the sweep, is a real possible outcome and would say the mechanism is not one of the three above.
Restricting the fit to tilt_fit_max matters for two reasons: at small tilt the
error approaches the round-off floor and flattens the fit, and at large tilt the
two columns’ maxLevelCell values begin to differ, which adds a staircase
component. This mirrors the existing
omega_vs_reference_convergence_fit_max_resolution option.
The scan is meaningful immediately, against the existing Centered scheme
and before any new Omega code exists. Offline replication of the Polaris
arithmetic gives \(q = 1.00\) for the centered scheme, which is the expected
reading for a scheme that holds \(\alpha\) constant within a layer, and which
calibrates the diagnostic.
Algorithm Design: Sizing the sweep so that it is severe enough
Date last modified: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
The existing variants use a 500 m water column resolved at 0.5–4 m, which is
between one and three orders of magnitude finer than the ~250 m layers in the
deep ocean of a realistic global run. Offline replication of the Polaris Init
arithmetic (which reproduces the recorded \(7\times10^{-12}\) m s\(^{-2}\) for the
existing ztilde_gradient to within 4%) gives, at a fixed coordinate tilt of
5 m km\(^{-1}\):
layer thickness |
RMS HPGA (centered) |
|---|---|
256 m |
\(1.6\times10^{-6}\) |
128 m |
\(5.0\times10^{-7}\) |
64 m |
\(9.6\times10^{-8}\) |
16 m |
\(6.4\times10^{-9}\) |
i.e. roughly second order in layer thickness, and only the coarsest rows are near the target severity. The new variants therefore use a deep, coarsely resolved column: a sea floor at \(-3500\) m, a reference pseudo-bottom at \(-4096\) m, and 16, 32 or 64 levels (256, 128 or 64 m). The level counts are multiples of 16, which Omega prefers.
The gap between the sea floor and the reference pseudo-bottom is deliberate.
The p-star iteration converges to a pseudo-bottom depth slightly greater than
the geometric water-column thickness (because in-situ density exceeds \(\rho_0\)
at depth), and the reference grid must extend below it in both columns. The
576 m of head room here permits tilts up to a few hundred m km\(^{-1}\); without
it the shallower column’s reference grid runs out, the p-star iteration
diverges, and the reported HPGA jumps to \(O(1)\) m s\(^{-2}\) — a failure mode the
Init guard below is designed to catch.
A further measured result shapes the sweep: at a fixed tilt gradient, the RMS HPGA is independent of horizontal resolution to five digits (1.5570e-06 at 8, 4, 2 and 1 km). The offset and the cell spacing scale together, so the difference quotient is unchanged. The horizontal sweep is therefore reduced to two resolutions — enough to confirm the same independence holds in Omega, which has not been checked — rather than the seven the existing variants use.
Algorithm Design: The bathymetry guard
Date last modified: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
The swept tilt has to be the geometry actually under test. The analysis therefore checks
at every point in the sweep and fails otherwise, with Init writing the
requested bathymetry alongside the achieved one.
The thing this guards against is partial-cell snapping. min_pc_fraction
forbids bottom cells thinner than a set fraction of a layer, so the achievable
pseudo-bottom depths are quantized and a requested bathymetry can fall in a gap
between them; _snap_partial_cells then moves the sea floor to the nearest
representable depth.
That does not break the resting state. Since run_pstar_init anchors the
geometric column at the prescribed sea surface, ssh is exact by construction,
the sea surface stays level, and the state remains exactly at rest. What breaks
is the meaning of the sweep. This matters most for bathymetry_step, where
the swept parameter is the sea floor itself: offline replication shows that at
a nominal gradient of 100 m km\(^{-1}\) over 4 km, snapping moves both columns to
the same representable depth, so a nominal 400 m step becomes no step, and the
measured RMS HPGA falls to \(4\times10^{-16}\) m s\(^{-2}\) — which would read as a
spectacular pass rather than a test that had stopped testing anything.
A guard on ssh would be useless here, and was: an earlier version of this
design checked \(\lvert \eta_1 - \eta_0 \rvert\), which is identically zero once
the column is surface-anchored and therefore can never fire.
Both new variants set partial_cell_type = None, which leaves the requested
bathymetry untouched. This does not cost partial-cell coverage: in a p-star
column the reference grid is clipped at the pseudo-bottom depth regardless, so
the deepest layer is a partial cell either way. The option controls only
whether the topography is snapped. The guard remains in place so that anyone
who later enables snapping finds out immediately.
Historical note. Before
run_pstar_initanchored the column at the prescribed sea surface, the same snapping surfaced as a spurious sea-surface tilt — up to 12 m between adjacent columns in this configuration, producing an apparent “error” of \(3\times10^{-2}\) m s\(^{-2}\) that was entirely real barotropic physics. That is fixed in the framework (see the p-star partial-cell work), and this branch is rebased onto that fix.
Implementation
Implementation: including the bottom layer in the reference-based analysis
Date last modified: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
In analysis.py, take one more interface and one more layer:
# was: [: max_level_index + 1] and hpga_forward[:max_level_index]
z_tilde_inter_for_ref = z_tilde_inter_edge[: max_level_index + 2]
hpga_ref_diff = hpga_forward[: max_level_index + 1] - ref_layer_mean
This is safe with respect to interface masking: ZTildeInterface is valid for
indices 0 .. maxLevelCell in each column and max_level_index is
min(maxLevelCell) - 1, so interfaces 0 .. max_level_index + 1 are finite in
both columns by construction. The omega_vs_python comparison in the same
function already used [: max_level_index + 1], so after this change the two
comparisons agree about which layers count.
Measured effect. Running the real Init arithmetic and the real
ReferenceColumn against a synthetic two-cell mesh (only mesh generation
bypassed), with the Polaris HPGA standing in for Omega’s tendency:
variant |
fitted slope |
RMS at 0.5 km |
|---|---|---|
|
1.787 → 1.799 |
2.600e-07 → 2.601e-07 |
|
2.014 → 2.012 |
1.137e-11 → 1.136e-11 |
|
1.625 → 1.625 |
2.770e-07 → 2.770e-07 |
|
2.003 → 1.119 |
6.779e-12 → 4.954e-11 |
The first three are unaffected. ztilde_gradient is the only variant with a
tilted coordinate, so its two columns clip the bottom layer at different
thicknesses; that cell carries -1.55e-09 m s\(^{-2}\) at 0.5 km against an
interior RMS of 6.8e-12, converges at roughly first order, and pulls the
fitted slope down. That is a real first-order error in the bottom partial cell
which the exclusion was hiding — the same behaviour hydrostatic_consistency
measures directly, and what the higher-order scheme exists to fix.
ztilde_gradient.cfg therefore takes its own convergence band, provisionally
[0.9, 1.4], marked in the file as requiring re-tuning from the first real
Omega run. The shared omega_vs_reference_high_res_rms_threshold of 1e-6 needs
no change.
Implementation: the reference-free analysis step
Date last modified: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
Add polaris/tasks/ocean/horiz_press_grad/resting_analysis.py defining
RestingAnalysis, a sibling of Analysis rather than a mode of it, so the
existing step is untouched. Per sweep point it:
locates the internal edge with
get_internal_edge;takes
maxLevelCellfor the two bounding cells and forms the valid range0 .. min(maxLevelCell) - 1inclusive of the deepest common layer;reads Omega’s
NormalVelocityTendat that edge and takes its RMS over the valid layers — the truth is zero, so this is the error;does the same for the Polaris-side
HPGAfrominit.nc;RMS-differences the two and applies
omega_vs_polaris_rms_threshold, which is retained: it confirms Omega implements the discretization Polaris thinks it does, independently of the resting-state property;applies the bathymetry guard, comparing
bottomDepthfromvert_coord.ncagainstbottomDepthRequestedfrominit.nc.
It then groups the sweep by resolution pair, fits power_law_fit over the
points with tilt \(\le\) tilt_fit_max when tilt_fit is true, writes
resting_state.nc and resting_state.png (log-log, one series per resolution
pair), logs the exponents, and applies the gates:
sensitivity gate — the largest RMS over the whole sweep must be at least
resting_state_sensitivity_min_rms; this is a hard prerequisite, and failing it means the sweep must be redesigned before anything is concluded from it;consistency gate — RMS below
resting_state_max_rms, deliberately disabled (none) until the rollout stage measures a value to set it from;Omega-vs-Polaris and resting-state checks as above.
Implementation: the tilt axis and the resting-state task
Date last modified: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
Init and Forward gain an optional subdir_suffix, and Init gains optional
tilt_option / tilt. When a tilt is given, Init.run sets that option in its
own config before building the columns, exactly as it already sets
vertical_grid:vert_levels. Init.run also gains a guard that raises when a
column’s reference pseudo-bottom does not extend below its converged
pseudo-bottom depth — the divergence mode described above — with an error
naming the offending column and the head room available.
Task wiring goes in a new module resting_state_task.py defining
HorizPressGradRestingStateTask, rather than branching HorizPressGradTask.
The existing task pairs horiz_resolutions[i] with vert_resolutions[i] and
keys its step dictionaries by horizontal resolution alone, so it cannot express
the repeated horizontal resolutions the new sweep needs; the new task keys by
the (horiz_res, vert_res, tilt) triple. Init and Forward are reused
unchanged apart from the optional arguments above.
This is a deliberate departure from the working plan, which leaned toward adding
the tilt axis inside HorizPressGradTask._setup_steps. Doing so would have
required renaming the existing variants’ steps to disambiguate the vertical
resolution, changing their work directories for no benefit to them.
Implementation: configuration
Date last modified: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
horiz_press_grad.cfg gains the resting-state options with inert defaults, so
the four existing variants are unaffected. Which task class a variant uses is
decided by name in __init__.py, so no separate “this is a resting state” flag
is needed. The new variant .cfg files set the deep column, the coarse
vertical resolutions, and the sweep:
# hydrostatic_consistency.cfg (abridged)
geom_z_bot_mid = -3500.0
z_tilde_bot_mid = -4096.0
horiz_resolutions = [4.0, 4.0, 4.0, 1.0]
vert_resolutions = [256.0, 128.0, 64.0, 256.0]
tilt_option = z_tilde_bot_grad
tilt_values = [0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 50.0]
tilt_fit = True
tilt_fit_max = 10.0
# bathymetry_step.cfg (abridged)
geom_z_bot_mid = -3500.0
z_tilde_bot_mid = -4096.0
horiz_resolutions = [4.0, 4.0]
vert_resolutions = [256.0, 128.0]
tilt_option = geom_z_bot_grad
tilt_values = [1.0, 5.0, 10.0, 25.0, 50.0, 100.0, 200.0]
tilt_fit = False
Both set partial_cell_type = None in [vertical_grid], for the reason given
under the bathymetry guard above.
forward.yaml was unchanged for the calibration stage: every variant ran the
existing Centered scheme. The rollout gives it a PressureGrad block, filled
in per step — see below.
Implementation: the scheme axis
Date last modified: 2026/08/02
Contributors: Xylar Asay-Davis, Claude
Forward gains a scheme argument, and forward.yaml a PressureGrad block
filled in through add_yaml_file’s template_replacements:
PressureGrad:
PressureGradType: {{ pressure_grad_type }}
QuadraturePoints: {{ quadrature_points }}
HorzOrder and VerticalReconstruction are left at their Omega defaults of 2
and linear, which are the only Phase 1 values; the Phase 2 values exist as
keys but are rejected with an error, so a configuration written for Phase 2
cannot quietly run as Phase 1. An unrecognized PressureGradType is likewise
fatal rather than falling back to Centered, so a typo aborts the run instead
of producing centered answers that read as a pass.
Both task classes key their step dictionaries by (resolution, scheme) — or
(horiz_res, vert_res, tilt, scheme) for the resting-state sweep — and build
one Forward per scheme over one shared Init. Init is
scheme-independent: it writes both HPGA and HPGAFiniteVolume from the same
state, so duplicating it per scheme would cost run time and, worse, would leave
the two schemes’ comparison open to the objection that they were run on
different initial conditions.
This is the departure from the calibration stage’s structure noted under the
amended requirement above: it changes the existing variants’ step directories.
The alternative — a task per (variant, scheme) pair — preserves them, but runs
Init twice per state and leaves no analysis step that sees both schemes, which
is the comparison the rollout exists to make.
quadrature_points is a single config option that drives both this template and
the Python kernel’s Gauss order. It is deliberately not two options: the two
sides using different quadrature would make them different algorithms, which is
what the cross-implementation requirement above forbids. Sweeping it for
accuracy remains available and moves both sides together.
Testing
Testing and Validation: the sensitivity gate is the prerequisite
Date last modified: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
Run both variants with Centered and confirm the sensitivity gate passes.
Offline replication of the Polaris arithmetic predicts the centered RMS HPGA
listed below; the first job of the real run is to confirm Omega agrees, via the
retained omega_vs_polaris_rms_threshold check.
variant |
configuration |
predicted centered RMS |
|---|---|---|
|
4 km, 256 m, tilt 50 m km\(^{-1}\) |
\(2.1\times10^{-5}\) |
|
4 km, 64 m, tilt 50 m km\(^{-1}\) |
\(9.8\times10^{-7}\) |
|
4 km, 256 m, 100 m km\(^{-1}\) |
\(2.3\times10^{-5}\) |
|
4 km, 128 m, 25 m km\(^{-1}\) |
\(3.8\times10^{-6}\) |
If the gate fails, the sweep is not exercising the failure mode and must be redesigned before the variants are used to judge any scheme. This is the single most consequential check in the calibration stage.
Testing and Validation: the tilt exponent
Date last modified: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
Confirm that hydrostatic_consistency returns \(q \approx 1\) for Centered at
every resolution pair, and that the value is stable when tilt_fit_max is
varied. Offline replication gives \(q = 0.95\)–\(1.00\), the droop below 1 coming
from the largest tilts where maxLevelCell starts to differ between columns —
which is what tilt_fit_max is there to exclude. A centered exponent far from 1
should be understood before the scan is used on the new scheme.
Confirm that bathymetry_step produces the expected staircase and that its
interior layers sit at round-off, isolating the signal to the bottom cell.
Testing and Validation: resting-state and consistency checks
Date last modified: 2026/07/28
Contributors: Xylar Asay-Davis, Claude
Bathymetry guard. Confirm it passes for both variants as configured, and confirm it fires for a deliberately broken configuration — set
partial_cell_type = partialonbathymetry_stepwith a sea-floor gradient of 32 m km\(^{-1}\), which offline replication shows moves the sea floor 12.33 m from the request.Zero-tilt sanity. With the swept parameter set to zero the RMS HPGA must be exactly zero (offline replication gives 0.0, not merely round-off), since the two columns become identical.
Omega-vs-Polaris. The retained check at
omega_vs_polaris_rms_thresholdmust pass at every sweep point. Its present value of \(10^{-10}\) was tuned for errors of order \(10^{-7}\); it may need re-tuning for a signal of order \(10^{-5}\) and should be set from the observed differences.Regression. Run the four existing variants and confirm their step names and work directories are unchanged. Their results change, because the bottom layer is now included: three are unaffected in practice, while
ztilde_gradient’s convergence slope drops from ~2.0 to ~1.1 and its provisional band must be re-tuned from the observed value.
Testing and Validation: the rollout stage
Date last modified: 2026/08/02
Contributors: Xylar Asay-Davis, Claude
Two corrections to how this section read before Omega’s FiniteVolume landed,
both from omega_finite_volume_handoff.md. The keys are HorzOrder: 2 and
VerticalReconstruction: linear, not ReconstructionOrder and constant —
constant truncates the equation-of-state expansion to \(\alpha_0\) and would run
the centered scheme under a new name. And there is no “reduced configuration”
or centered limit of the new scheme: the two are separate implementations and
no setting reduces one to the other. Phase 1 offers exactly two configurations,
Centered and FiniteVolume with its defaults.
The Python counterpart also already exists — it was written during the calibration stage, from the design rather than from the C++ — so what this stage tests is not whether it can be built but whether it and Omega agree.
Reconciliation, before anything is run. Work through every discretization choice named in the cross-implementation requirement above, record its resolution and provenance, and give each confirmed mismatch its own fix and its own test. Two are already confirmed: the Python kernel defaults to a 4-point Gauss rule where Omega defaults to 2, and it anchors its column scan at the sea surface where Omega anchors at the sea floor. Neither shows up on the exact set, where the integrand is zero at every quadrature point; both bite off it, which is where the §5.1 gates live.
Gates, set from measurement and not before. Three points, of which the first two would otherwise have been got wrong:
bands are per variant, not per scheme alone. The measured
Centeredrates are 1.6, 1.8, 2.0 and 1.1 across the four gradient variants, so a single “≈ 2 for centered” band fails three of them;the accuracy gate is a ratio at the coarse end, set from the measured advantage — 6.5× at 256 m layers, 2.4× at 64 m, ~400× on stepped bathymetry;
do not gate on relative convergence order. The two schemes converge at similar rates on smooth profiles; the advantage is a constant factor that narrows under refinement, so a gate requiring the new scheme’s slope to beat the centered one would fail where nothing is wrong.
resting_state_max_rms is set from the measured FiniteVolume level at a
stated tilt, as an explicit multiple. The improvement gate — FiniteVolume
RMS below Centered RMS at every sweep point — needs no prior measurement and
ships with the analysis change.
Report absolutes. \(\max\lvert\mathcal{S}\rvert\) behaved unexpectedly under vertical refinement on the linear variant during the calibration stage, so absolute residuals and tendencies in m s\(^{-2}\) are reported alongside any ratio, not in place of them.
The variant to expect surprises from is bathymetry_step. Every Omega-side
measurement of the new scheme so far used equal layer counts in both columns, so
this is where the rule for evaluating a column’s reconstruction below its own
floor is first genuinely exercised.
Finally, the result — whichever way it falls — is written back into
PGradHighOrder.md as a measured property with its configuration and tilt range
attached. That is an Omega-side commit in a separate repository and is not
folded into the Polaris work.