Skip to content

Fix spurious sea-surface height from p-star partial-cell snapping - #674

Merged
xylar merged 5 commits into
E3SM-Project:mainfrom
xylar:fix-p-star-partial-cells
Aug 1, 2026
Merged

Fix spurious sea-surface height from p-star partial-cell snapping#674
xylar merged 5 commits into
E3SM-Project:mainfrom
xylar:fix-p-star-partial-cells

Conversation

@xylar

@xylar xylar commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

With coord_type = p-star and partial_cell_type = partial, a resting, unforced column with zero surface pressure could be initialized with a non-zero sea-surface height, which is a spurious barotropic pressure gradient.

Cause: partial-cell snapping quantizes the achievable pseudo bottom depth (min_pc_fraction forbids thin bottom cells by design), so some target bathymetries are unreachable and the run_pstar_init iteration stagnates on them. geom_height_from_pseudo_height anchored the column at the requested seafloor, so bottomDepth came out exactly on target and the whole quantization residual surfaced as ssh.

Fix: flip the invariant to match z-star partial cells — ssh is prescribed, bottomDepth is diagnosed. The converged column is shifted so its top interface lands on sea_surface_height, and bottomDepth follows from the shifted bottom interface. Where the iteration converges the shift is zero and bottomDepth still equals the target to machine precision; where it cannot, the residual moves the seafloor (the representable bathymetry, which is what partial_cell_type is documented to alter) instead of the sea surface.

The first two commits here are cherry-picked from #643, where this was independently hit and fixed on realistic global setups; 8427a63 drops that branch's polaris/tasks/ocean/realistic_global/ hunks.

Checklist

  • Testing comment in the PR documents testing used to verify the changes

@xylar xylar self-assigned this Jul 28, 2026
@xylar xylar added bug Something isn't working ocean Related to the ocean component labels Jul 28, 2026
@xylar
xylar force-pushed the fix-p-star-partial-cells branch from f3ce936 to 1228295 Compare July 28, 2026 11:55
@xylar

xylar commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

@katsmith133, this bug fix relates to the realistic, global initialization. I know it's not really in your wheelhouse yet but could I ask you to review nonetheless? There's not really anyone else who is more familiar than you at the moment anyway that I can turn to. If you just give the code a look over and let me know if things are clearly wrong or confusing, that would be a help.

@xylar
xylar requested a review from katsmith133 July 28, 2026 21:04
@xylar
xylar force-pushed the fix-p-star-partial-cells branch from 1228295 to 03627a1 Compare July 29, 2026 10:16
xylar and others added 5 commits July 31, 2026 10:45
The p-star init iteration matched the geometric water-column thickness to
the target bathymetry and pinned bottomDepth to the raw target, letting ssh
float as a diagnostic. For cells whose seafloor lands in the partial-cell
snap band, the snap freezes the pseudo bottom depth so the geometric column
cannot equal the target, and the entire quantization residual surfaced as a
nonzero ssh (up to ~24 m on ~2% of cells).

Flip the invariant to match z-star partial cells: ssh is prescribed
(sea_surface_height) and bottomDepth is diagnosed from the column. The
geometric column is shifted so its top interface lands on the prescribed
ssh; the snap residual then adjusts bottomDepth (the representable
bathymetry) instead of ssh. For representable cells the shift is zero and
bottomDepth still equals the target to machine precision; ssh is now exactly
its prescribed value everywhere.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Being at rest (zero velocity) does not imply ssh = 0: the sea surface is
depressed by surface loading (atmospheric pressure, sea ice, ice shelves)
even at rest. The zero-surface-pressure coordinate is the reference
coordinate, not the resting coordinate.

Remove the resting-vs-zero-ssh conflation from comments and docstrings in
the p-star vertical coordinate and design doc. No behavior change.

Cherry-picked from add-realistic-global-ocean-general-forward (9f60956),
dropping that commit's polaris/tasks/ocean/realistic_global/ hunks, which
do not exist on main.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The message said "full-cell snap" when partial-cell snapping triggers it
too, and framed an unreachable bathymetry as a failure. Since the column
is now anchored at the prescribed sea surface, snapping to the nearest
representable depth is the intended behaviour: ssh stays exact and
bottomDepth absorbs the residual.

Reword accordingly, drop it from warning to info, and report how far the
sea floor had to move.

Note that this branch does not fire often: the check requires every
column to be frozen at once ((...).all()), so on a multi-column mesh a
handful of snapped columns among many converging ones never reaches it.
Making the report per-column would be a separate change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The stagnation branch requires *every* column to be frozen at once, so on a mesh where some columns are still converging it never fires and the loop simply runs out of iterations, silently. A post-loop `info` message now reports how many columns cell snapping could not place on the requested bathymetry and how far `bottomDepth` moved, however the loop ended.
Polaris and Omega build the geometric column from opposite ends:
PStarInitStep anchors at the prescribed sea surface and diagnoses
bottomDepth, while VertCoord::computeGeomZHeight anchors at
BottomGeomDepth and diagnoses ssh.  They agree only because
run_pstar_init writes back the shifted seafloor rather than the raw
target bathymetry, which until now was guaranteed by a comment.

Add a test that reimplements Omega's upward accumulation in NumPy --
deliberately not calling geom_height_from_pseudo_height, since
re-anchoring the written column with the helper that built it is an
identity -- and checks it reproduces GeomZInterface and ssh from the
written bottomDepth, SpecVol and PseudoThickness.

The check only has content where the surface-anchoring shift is nonzero,
so the snapped cases use partial cells with a target bathymetry between
layer interfaces (455 m snaps up to 450 m, 460 m down to 465 m) and
assert the shift is nonzero before asserting anything else.  A
converged, unsnapped control marks the boundary of what those cases add.

Writing the raw pre-shift bathymetry instead of the shifted one breaks
the reconstruction by 5 m (the snap residual) on all three snapped cases
and leaves the control at 6e-14, confirming the test fails against the
defect it exists to catch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@xylar
xylar force-pushed the fix-p-star-partial-cells branch from 03627a1 to fa10bff Compare July 31, 2026 15:45
@xylar

xylar commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator Author

@katsmith133, another ping that I could use your review on this. Could you let me know when you might have time?

@xylar

xylar commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator Author

I'll report my testing shortly...

@katsmith133

Copy link
Copy Markdown
Contributor

Apologies, I don't remember getting pinged the first time. I just looked through and it all makes sense to me. I think if the testing looks good, I am good with approving it.

@xylar

xylar commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator Author

Testing

Polaris omega_pr suite

  • Baseline workdir: /lcrc/group/e3sm/ac.xylar/polaris_1.0/chrysalis/test_20260731/omega-pr-baseline
  • Baseline build: /lcrc/group/e3sm/ac.xylar/polaris_1.0/chrysalis/test_20260731/omega-pr-baseline/build
  • PR build: /lcrc/group/e3sm/ac.xylar/polaris_1.0/chrysalis/test_20260731/omega-pr-baseline/build
  • PR workdir: /lcrc/group/e3sm/ac.xylar/polaris_1.0/chrysalis/test_20260731/omega-pr-fix-p-star-partial-cells
  • Machine: chrysalis
  • Partition: compute
  • Compiler: intel
  • Build type: Release
  • Log: /lcrc/group/e3sm/ac.xylar/polaris_1.0/chrysalis/test_20260731/omega-pr-fix-p-star-partial-cells/polaris_omega_pr.o1261272
  • Result:
    • Diffs (2 of 13):
      • ocean/column/horiz_press_grad/salinity_gradient
      • ocean/planar/overflow/nonlinear/pstar/smoke_test_horiz_adv_order_4_del4

The salinity_gradient test has round-off differences.

The smoke_test_horiz_adv_order_4_del4 test has bigger diffs. This is presumably a case where the bug really had an impact.

Independent of this testing, I have verified in two downstream branches that this fix leads to partial steps that are correct and an SSH that is zero when it's supposed to be.

Comment on lines -158 to +160
the resting surface-pressure depression for a reference-density fluid
the sea-surface depression that balances the surface pressure for a
reference-density fluid (being at rest does not imply $\eta_0 = 0$; surface
loading depresses the sea surface even at rest)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't usually retroactively update design docs but this seems fine for clarity.

@xylar

xylar commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks @katsmith133. Take a quick look when you can, please.

@katsmith133 katsmith133 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved based upon visual inspection and testing by @xylar

@xylar
xylar merged commit e47f6b6 into E3SM-Project:main Aug 1, 2026
5 checks passed
@xylar
xylar deleted the fix-p-star-partial-cells branch August 1, 2026 06:48
@xylar

xylar commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks @katsmith133!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working ocean Related to the ocean component

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants