Skip to content

Acoustic substepping cleanup: typed AcousticDampingStrategy + new default + BW example#622

Open
glwagner wants to merge 19 commits into
mainfrom
glw/hevi-imex-docs
Open

Acoustic substepping cleanup: typed AcousticDampingStrategy + new default + BW example#622
glwagner wants to merge 19 commits into
mainfrom
glw/hevi-imex-docs

Conversation

@glwagner
Copy link
Copy Markdown
Member

@glwagner glwagner commented Apr 8, 2026

Summary

Substepper damping infrastructure: typed AcousticDampingStrategy, two new
damping mechanisms (UpperSponge, VorticityDamping), an alternative
vector-invariant momentum-advection scheme (CompressibleVectorInvariant),
and a constructor-time check that rejects HydrostaticSphericalCoriolis on
non-hydrostatic dynamics.

What's new in the substepper

field on SplitExplicitTimeDiscretization new types
damping NoDivergenceDamping, ThermalDivergenceDamping, HyperdiffusiveDivergenceDamping, VorticityDamping (new), or a Tuple of these
sponge newUpperSponge(damping_rate, depth, ramp=CubicRamp()) for implicit Rayleigh damping at the lid, folded into the column tridiag (Klemp–Dudhia–Hassiotis 2008 form, as in WRF / MPAS-A)
sponge = UpperSponge(damping_rate = 0.2, depth = 5kilometers)
damping = (ThermalDivergenceDamping(coefficient = 0.1),
           VorticityDamping(coefficient = 0.05))
SplitExplicitTimeDiscretization(; damping, sponge)

AbstractRamp family for the sponge profile: CubicRamp (Hermite
smoothstep, default — GPU-cheap), Sin2Ramp (WRF/MPAS literature form),
LinearRamp. Custom shapes via <:AbstractRamp.

Vector-invariant momentum advection (FV3/MPAS-style)

CompressibleVectorInvariant <: AbstractAdvectionScheme decomposes the
flux-form momentum advection as

∇·(ρ𝐯⊗u) = ρ (𝐯·∇)u + u (∇·ρ𝐯)

with (𝐯·∇)u computed in vector-invariant form (vorticity flux +
Bernoulli + vertical advection) by an inner WENOVectorInvariant. Pair
with scalar_advection = WENO():

AtmosphereModel(grid;
                momentum_advection = CompressibleVectorInvariant(),
                scalar_advection   = WENO(),
                ...)

Coriolis safety check

AtmosphereModel now errors at construction if HydrostaticSphericalCoriolis
is passed: it drops the 2Ω cos(φ) cross-terms that couple horizontal
momentum to w, and Breeze always carries prognostic ρw. The
SphericalCoriolis(rotation_rate=Ω) non-hydrostatic form is the only
self-consistent choice on the sphere.

Example

examples/baroclinic_wave.jl drops the explicit timestepper kwarg —
AtmosphereModel already auto-selects :AcousticRungeKutta3 for
CompressibleDynamics(SplitExplicitTimeDiscretization()).

Tests

  • test/substepper_structural.jl — 17/17 pass (sponge added to
    get_coefficient signature; S1 updated to pass nothing).
  • test/latitude_longitude_grid.jl — switched from
    HydrostaticSphericalCoriolis to SphericalCoriolis.
  • test/acoustic_substepping.jl — passes with new damping API.

Note on branch scope

This branch (glw/hevi-imex-docs) carries a long line of acoustic-substepper
cleanup work plus an unrelated older HEVI/IMEX track that's not the focus of
this PR. Reviewers should focus on the recent commits — the typed damping
API, UpperSponge, VorticityDamping, CompressibleVectorInvariant, and
the HydrostaticSphericalCoriolis guard.

🤖 Generated with Claude Code

@glwagner glwagner marked this pull request as draft April 8, 2026 19:48
@glwagner glwagner added the build all examples 🏗️ PRs which should build all examples label Apr 9, 2026
Copy link
Copy Markdown

@numterra-bot numterra-bot Bot left a comment

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Breeze.jl Benchmarks'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.10.

Benchmark suite Current: 6ef1a8b Previous: b0e3e87 Ratio
CBL; Dynamics: anelastic; Microphysics: nothing [Float32]/Compare advections/NVIDIA L4/WENO5 [256, 256, 128] 106590562.60741785 points/s 121640055.53819701 points/s 1.14
CBL; Dynamics: anelastic; Microphysics: nothing [Float32]/Advection: WENO5/NVIDIA L4/256x256x128 106590562.60741785 points/s 121640055.53819701 points/s 1.14

This comment was automatically generated by workflow using github-action-benchmark.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 23, 2026

Comment thread examples/baroclinic_wave.jl
Comment thread examples/moist_baroclinic_wave.jl Outdated
Comment thread examples/moist_baroclinic_wave.jl Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This file isn't added to docs/make.jl, is that intentional?

@giordano giordano force-pushed the glw/hevi-imex-docs branch 2 times, most recently from 3744152 to 2bc4670 Compare April 25, 2026 11:01
Comment thread test/substepper_structural.jl Outdated
nothing, ZDirection(),
Π⁰, θ⁰, γRᵈ, g, δτ_new)

@info @sprintf("[S1] b[1] = %.6f, c[1] = %.6e (must be 1.0 and 0.0)", b₁, c₁)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are these prints really necessary? I'm not a fan of printing during tests, if there's any useful information that'd should be tested, not printed. Same applies to all other new tests which print stuff

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

probably not. I agree, printing during testing is annoying. Learned that lesson with Oceananigans.

Comment thread src/Thermodynamics/reference_states.jl Outdated
Comment on lines +508 to +622
g = constants.gravitational_acceleration
Nz = size(grid, 3)
loc = (nothing, nothing, Center())
g Tᵣstants.gravitational_acceleration
NzTᵣze(grid, 3)
Tᵣ
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Something went wrong here

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

argh. tried to search and replace

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I reverted 511a875 entirely, I think many things went wrong with that change

Comment thread examples/baroclinic_wave.jl Outdated
@glwagner glwagner marked this pull request as ready for review May 5, 2026 18:49
@glwagner glwagner requested review from ewquon and kaiyuan-cheng May 7, 2026 00:17
Comment thread ext/BreezeReactantExt/Timesteppers.jl Outdated
glwagner added a commit that referenced this pull request May 7, 2026
Per Giordano's review on PR #622: tests shouldn't print. The diagnostic
@info @sprintf("[Sx] ...", value) lines added for debugging substepper
behavior had no role beyond noise — every quantity printed has an
accompanying @test that does the actual check.

Cleared from:
- test/substepper_structural.jl (15 prints, plus a dead growth-rate
  regression block whose only output was an @info)
- test/substepper_rest_state.jl (6 prints)
- test/atmosphere_model_construction.jl (1 "Testing NaN Checker..."
  banner)

`using Printf` removed from both substepper test files (no remaining
sprintf consumers).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment on lines 456 to 473
function SplitExplicitTimeDiscretization(; substeps = nothing,
forward_weight = 0.65,
damping = ThermalDivergenceDamping(coefficient = 0.1),
sponge = nothing,
substep_distribution = ProportionalSubsteps())

damping isa Union{AcousticDampingStrategy, Tuple} ||
throw(ArgumentError("`damping` must be an `AcousticDampingStrategy` or a tuple of them"))

sponge isa Union{Nothing, UpperSponge} ||
throw(ArgumentError("`sponge` must be `nothing` or an `UpperSponge`"))

return SplitExplicitTimeDiscretization(substeps,
forward_weight,
divergence_damping_coefficient,
acoustic_damping_coefficient)
damping,
sponge,
substep_distribution)
end
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I just realised #678 conflicts with this PR, but this code has the same problem as what that PR was trying to solve: it's hard to enforce the same float type everywhere. For example running on Metal GPU it's hard to consistently use Float32 everywhere.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

mm yes! We need to add an FT option to the constructor. Is that what you mean?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, and make sure that all fields have the same floating point type.

Comment thread src/CompressibleEquations/acoustic_substepping.jl Outdated
Comment on lines +507 to +511
@inline function stage_substep_count_and_size(::ProportionalSubsteps, β_stage, Δt, N)
Δτ = Δt / N
Nτ = max(1, round(Int, β_stage * N))
return Nτ, Δτ
end
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think it'd be better to take also the grid as argument and do

FT = eltype(grid)
Δτ = FT(Δt) / N

because it's generally more accurate to do the conversion before the division. Same for the other methods below. And then you can replace all the FT(Δτ) -> Δτ

@glwagner glwagner force-pushed the glw/hevi-imex-docs branch from 6de3601 to d92a092 Compare May 9, 2026 12:03
[`CompressibleDynamics`](@ref) solves the fully compressible Euler equations with prognostic density ``ρ``.
This formulation retains acoustic waves and is suitable for problems where full compressibility is important.
[`CompressibleDynamics`](@ref) solves the fully compressible Euler equations with prognostic
density ``ρ``. The formulation retains acoustic waves and is suitable for problems where full
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
density ``ρ``. The formulation retains acoustic waves and is suitable for problems where full
total density ``ρ`` (including dry air, vapor, and condensate). The formulation retains acoustic waves and is suitable for problems where full

double-counts the ``\theta`` change from earlier stages (since ``\theta(U^*) = \theta^n +
\beta_1 \Delta t \, \dot{\theta}^s`` already includes the stage 1 contribution), producing
an ``O(\Delta t)`` error per time step that causes instability at larger ``\Delta t``.
Breeze can also fold the vertical component into the column tridiag by setting
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
Breeze can also fold the vertical component into the column tridiag by setting
This horizontal divergence damping is applied by default. Breeze can also fold the vertical component into the column tridiag by setting

Comment on lines +284 to +285
Δ(ρu)' &= - γ_h \, ∂_x D_τ , \\
Δ(ρv)' &= - γ_h \, ∂_y D_τ .
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
Δ(ρu)' &= - γ_h \,_x D_τ , \\
Δ(ρv)' &= - γ_h \,_y D_τ .
Δ(ρu)' &= - γ_x \,_x D_τ , \\
Δ(ρv)' &= - γ_y \,_y D_τ .

Comment on lines +242 to +247
The off-centering parameter ``ω = 1/2`` is classical centered Crank–Nicolson — neutrally
stable for the linearized inviscid system but susceptible to amplification of distributed
floating-point noise through the non-normal substep operator (see
[Stability analysis](@ref stability-analysis)). The default ``ω = 0.65`` adds modest
dissipation; the dimensionless parameter ``ε = 2ω - 1 = 0.3`` quantifies the deviation from
centered.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
The off-centering parameter ``ω = 1/2`` is classical centered Crank–Nicolson — neutrally
stable for the linearized inviscid system but susceptible to amplification of distributed
floating-point noise through the non-normal substep operator (see
[Stability analysis](@ref stability-analysis)). The default ``ω = 0.65`` adds modest
dissipation; the dimensionless parameter ``ε = 2ω - 1 = 0.3`` quantifies the deviation from
centered.
The off-centering parameter ``ω = 1/2`` is classical centered Crank–Nicolson — neutrally
stable for the linearized inviscid system but susceptible to amplification of distributed
floating-point noise through the non-normal substep operator (see
[Stability analysis](@ref stability-analysis)).
A fully implicit backward Euler scheme is obtained with ``ω = 1`` and offers the most dissipation.
The default ``ω = 0.65`` adds modest dissipation; the dimensionless parameter ``ε = 2ω - 1 = 0.3`` quantifies the deviation from centered.

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

Labels

build all examples 🏗️ PRs which should build all examples

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants