Skip to content

Keep uniform tracers uniform at open boundaries under z★ with split-explicit free surface - #6362

Open
aklocker42 wants to merge 7 commits into
CliMA:mainfrom
aklocker42:akl/zstar-obc-filtered-transport
Open

aklocker42 wants to merge 7 commits into
CliMA:mainfrom
aklocker42:akl/zstar-obc-filtered-transport

Conversation

@aklocker42

@aklocker42 aklocker42 commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

With ZStarCoordinate and a SplitExplicitFreeSurface, a tracer that starts at c ≡ 1 (and has value 1 at the boundary) doesn't stay at 1 next to open boundaries. In the interior it does, to round-off.

The cause is that σ and w̃ see different boundary transports. σ is advanced with η, and η is stepped with the substep U/V after the halo fill and transport pin. The filtered transport Ũ/Ṽ, which sets w̃ through ∂t σ = −∇·Ũ/H, disagrees with that at the boundary faces in two ways:

  1. Ũ/Ṽ are accumulated in the velocity kernel, before the substep fill/pin. So face 1 gets pre-fill values, and face N+1 is outside the kernel range and never accumulated.
  2. After substepping, fill_halo_regions!((Ũ, Ṽ)) overwrites the boundary faces with the open-boundary value.

So (σⁿ⁺¹ − σⁿ)/Δt ≠ −∇·Ũ/H in the boundary rows only. Because the tracer update divides by σ, the result is a multiplicative error in every tracer there.

This PR:

  • moves the Ũ/Ṽ accumulation into the free-surface kernel, using U★ (the same transport that advances η);
  • also accumulates face N+1 when the right side is Closed;
  • builds Ũ/Ṽ with boundary conditions that drop NormalFlow sides, so the post-substep fill keeps the accumulated faces.

Fixing only (1) gives results bit-identical to main, because (2) overwrites the faces anyway.

In a small channel (SplitRungeKutta3, GravityWaveRadiationBoundaryCondition on both open sides, 200 steps), max |c − 1| goes from ~2e-5 on main to ~3e-15.
The new test in split_explicit_boundaries.jl runs c ≡ 1 under z★ through open north/south and west/east boundaries. It fails on main (|c − 1| ≈ 7e-6) and passes here (< 1e-12).

In a 16-day realistic regional run with open boundaries, the fix also removes the spurious bottom-cell jets that develop along the open boundaries without it.

This is a fix for SplitRungeKutta3. Under QuasiAdamsBashforth2, z★ doesn't keep c ≡ 1 even with closed walls (~4e-5 after 50 steps, identical on main and here). With open boundaries there is an extra boundary error of similar size (~1e-4), which this PR doesn't fix.

Related: #5812 changes the same kernel.

🤖 Generated with Claude Code

Accumulate the filtered transport Ũ/Ṽ in the free-surface kernel from U★/V★,
the transport that advances η, including face N+1 on a Closed right side,
and give Ũ/Ṽ boundary conditions that skip NormalFlow sides so the
post-substep halo fill keeps the accumulated boundary faces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.47059% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.39%. Comparing base (591b0cf) to head (c235b35).

Files with missing lines Patch % Lines
...itFreeSurfaces/step_split_explicit_free_surface.jl 75.00% 6 Missing ⚠️
src/MultiRegion/multi_region_models.jl 50.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6362      +/-   ##
==========================================
- Coverage   75.70%   74.39%   -1.31%     
==========================================
  Files         452      452              
  Lines       29091    29097       +6     
==========================================
- Hits        22022    21647     -375     
- Misses       7069     7450     +381     
Flag Coverage Δ
buildkite 71.76% <100.00%> (+0.02%) ⬆️
distributed/output_combining 20.19% <43.75%> (+0.01%) ⬆️
distributed/tripolar 20.19% <43.75%> (+0.01%) ⬆️
julia 71.76% <100.00%> (+0.02%) ⬆️
makie ?
metal 16.25% <46.87%> (+0.02%) ⬆️
reactant ?
sharding ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@aklocker42
aklocker42 marked this pull request as ready for review October 28, 2026 11:21
@simone-silvestri

Copy link
Copy Markdown
Collaborator

There are a couple of fixes required. I will merge main, then push here directly.

@simone-silvestri

Copy link
Copy Markdown
Collaborator

I have cleaned up a bit, however, I am a bit hesitant to merge any open boundary fix before #6277 gets merged. I think we are close there to a merge, so I would keep this PR a sec to figure out if there is a better way to do this once radiation schemes do not use fill halo anymore

aklocker42 and others added 4 commits October 29, 2026 04:34
Parse error (unparenthesized @inbounds in ternary), call to undefined
V★ (use U★ for both components), U/V argument swap at the V call site,
dropped transport_weight parameter, and unimported Sides (now TwoSided
after CliMA#6276).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Resolves conflict in step_split_explicit_free_surface.jl between this
PR's relocation of transport_weight/Ũ,Ṽ plumbing from the velocity
kernel to the free-surface kernel, and main's (CliMA#6277) new
cache_boundary_conditions!/step_boundary_conditions! calls around the
same call sites. Both changes are kept.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants