Add ColumnwiseTridiagonalPreconditioner for the CG Poisson solver#5650
Open
xkykai wants to merge 15 commits into
Open
Add ColumnwiseTridiagonalPreconditioner for the CG Poisson solver#5650xkykai wants to merge 15 commits into
ColumnwiseTridiagonalPreconditioner for the CG Poisson solver#5650xkykai wants to merge 15 commits into
Conversation
ColumnwiseTridiagonalPreconditioner for the CG Poisson solver
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds a new block-diagonal preconditioner for ConjugateGradientPoissonSolver that inverts the vertical (columnwise) tridiagonal subsystem of the symmetric volume-weighted Laplacian V∇², intended to significantly reduce CG iterations in strongly anisotropic (ocean-like) grids. It also fixes the existing DiagonallyDominantPreconditioner so its coefficients match the V∇² operator on non-uniform grids.
Changes:
- Implement
ColumnwiseTridiagonalPreconditioner, backed by aBatchedTridiagonalSolverinZDirection, with masking and regularization to handle immersed/BC effects and per-column nullspaces. - Fix
DiagonallyDominantPreconditionercoefficient definitions by removing an incorrectV⁻¹ᶜᶜᶜfactor. - Extend CG Poisson solver tests to cover anisotropic grids and
PartialCellBottomcases with multiple preconditioners.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/Solvers/conjugate_gradient_poisson_solver.jl |
Adds the new ColumnwiseTridiagonalPreconditioner and corrects coefficient definitions for DiagonallyDominantPreconditioner to align with V∇². |
test/test_conjugate_gradient_poisson_solver.jl |
Adds new testsets for anisotropic grids and immersed PartialCellBottom to validate CG solver + preconditioner behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
tomchor
reviewed
Jun 4, 2026
| @test iteration(solver.conjugate_gradient_solver) <= solver.conjugate_gradient_solver.maxiter | ||
| end | ||
|
|
||
| function test_conjugate_gradient_partial_cell_bottom(underlying_grid, make_preconditioner) |
Member
There was a problem hiding this comment.
How hard would it be to create a test that actually tests the result?
tomchor
reviewed
Jun 4, 2026
tomchor
reviewed
Jun 4, 2026
simone-silvestri
approved these changes
Jun 6, 2026
… divergence-free solution verification
Co-authored-by: Simone Silvestri <silvestri.simone0@gmail.com>
…t functions and improve documentation
…proved stability in tests
…onditioner and adjust grid setup for improved simulation configuration
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a block-diagonal preconditioner for
ConjugateGradientPoissonSolverbased on Marshall et al. (1997, §4.2.2). For each horizontal column(i, j)it solves the vertical (k-direction) sub-system of the symmetric volume-weighted LaplacianV∇²exactly while discarding horizontal couplings — i.e.M = Lz⁻¹, applied as one batched Thomas sweep per column (reusing the existingBatchedTridiagonalSolveroverZDirection).For ocean-like problems (large
Nz, thin layers, stretched vertical grids) this is a much stronger preconditioner thanDiagonallyDominantPreconditioner. On thestaircase_convectionconfiguration (16×16×128,Bounded³+ slope,Δz/Δx = 0.125) it gives a 4–5× reduction in CG iterations (478 → 85 in Float64, 184 → 28 in Float32).What's in the kernel
A naïve transcription of K₃D fails even in its target regime; the implementation includes three corrections:
Lzdirectly where Bounded-domain and immersed face couplings are masked withinactive_cell(i, j, k±1, grid).Lzis singular (its rows sum to zero), and its null space isNx·Ny-dimensional vs. the operator's single global constant — so the global gauge fix cannot cover it. A multiplicative diagonal shift(1 + ε)withε = 1/100lifts the column-constant modes off the kernel (a Helmholtz-style screening) without disturbing the rough vertical modes K₃D needs.ε = 1/100is empirically found to be useful for both Float32 and Float64.PartialCellBottomthin surface cell with both vertical neighbors inactive has no vertical sub-system; the diagonal would collapse to exactly zero and stall CG. These are treated as identity (b = 1), like inactive cells.Regime of effectiveness
κ(MA) ≈ 1 + (Δz/Δx)²·Nz²/π²:(Δz/Δx)²Nz² ≪ 1): near-exact, CG terminates inO(1)iterations.Δz/Δx ≈ 1): conditioning is worse than no preconditioner — useDiagonallyDominantPreconditioneror the FFT solver instead. This is expected, not a bug.Also in this PR
DiagonallyDominantPreconditioner: remove the spuriousV⁻¹ᶜᶜᶜfactor from theAx±/Ay±/Az±coefficients so the preconditioner matches theV∇²operator on non-uniform grids.heuristic_residualis unchanged.