fix: Auto-compute optimal SOR omega from grid dimensions (#173)#173
Conversation
Hard-coded omega=1.5 was suboptimal for larger grids, causing Red-Black SOR to fail convergence on 33x33 grids within 1000 iterations. The optimal omega depends on the Jacobi spectral radius which varies with grid size (e.g., ~1.83 for 33x33 vs ~1.67 for 17x17). All SOR/Red-Black SOR solvers now auto-compute optimal omega using ω = 2/(1+√(1-ρ_J²)) when the default omega=0.0 is used. Users can still override by setting params.omega > 0. Projection backends remain on CG (grid-size-independent convergence).
There was a problem hiding this comment.
Pull request overview
This PR fixes a convergence issue where Red-Black SOR solvers failed on 33×33 grids due to a hard-coded omega=1.5 that was suboptimal for larger grids. The solution auto-computes the optimal SOR relaxation parameter using Young's formula based on the Jacobi spectral radius, while preserving backward compatibility by allowing users to override with an explicit positive omega value.
Changes:
- Default omega changed from 1.5 to 0.0 (sentinel for auto-compute), with
poisson_solver_compute_optimal_omega()computing ω = 2/(1+√(1-ρ_J²)) from grid dimensions - All 6 SOR/Red-Black SOR solver backends (scalar, AVX2, NEON, OMP) updated to use
poisson_solver_resolve_omega()consistently - New test suite (
test_optimal_omega.c) validating convergence on multiple grid sizes and the explicit override mechanism
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
lib/src/solvers/linear/linear_solver_internal.h |
Adds poisson_solver_compute_optimal_omega() and poisson_solver_resolve_omega() inline helpers |
lib/src/solvers/linear/linear_solver.c |
Changes default omega from 1.5 to 0.0 |
lib/include/cfd/solvers/poisson_solver.h |
Updates omega field documentation and default param docstring |
lib/src/solvers/linear/cpu/linear_solver_sor.c |
Uses poisson_solver_resolve_omega() |
lib/src/solvers/linear/cpu/linear_solver_redblack.c |
Uses poisson_solver_resolve_omega() |
lib/src/solvers/linear/avx2/linear_solver_sor_avx2.c |
Uses poisson_solver_resolve_omega() |
lib/src/solvers/linear/avx2/linear_solver_redblack_avx2.c |
Uses poisson_solver_resolve_omega() |
lib/src/solvers/linear/neon/linear_solver_sor_neon.c |
Uses poisson_solver_resolve_omega() |
lib/src/solvers/linear/neon/linear_solver_redblack_neon.c |
Uses poisson_solver_resolve_omega() |
lib/src/solvers/linear/omp/linear_solver_redblack_omp.c |
Uses poisson_solver_resolve_omega() |
tests/math/test_optimal_omega.c |
New test file for auto-omega convergence and explicit override |
tests/solvers/test_linear_solver.c |
Updates default omega assertion to 0.0 |
tests/math/test_omp_consistency.c |
Removes "known-fragile" comments, reduces max_iterations to 1000 |
CMakeLists.txt |
Registers new test executable and CTest target |
ROADMAP.md |
Marks OMP Red-Black SOR bug as resolved with explanation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
The annotation said "residual history logging" but the test verifies convergence via iteration counts, not residual logging.
When nz > 1 and dz > 0, include the z-component in the Jacobi spectral radius formula. For 2D grids (nz <= 1 or dz <= 0) behavior is unchanged. Previously a 3D user relying on auto-omega (omega=0.0) would get a suboptimal 2D-only value.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
Hard-coded omega=1.5 was suboptimal for larger grids, causing Red-Black SOR to fail convergence on 33x33 grids within 1000 iterations. The optimal omega depends on the Jacobi spectral radius which varies with grid size (e.g., ~1.83 for 33x33 vs ~1.67 for 17x17).
All SOR/Red-Black SOR solvers now auto-compute optimal omega using ω = 2/(1+√(1-ρ_J²)) when the default omega=0.0 is used. Users can still override by setting params.omega > 0. Projection backends remain on CG (grid-size-independent convergence).