Add Jacobi and BiCGSTAB OpenMP linear solvers - #202
Merged
Conversation
Completes the OpenMP linear-solver tier (previously only CG, GMRES, and Red-Black SOR), so the parallel projection/pressure paths can select these methods instead of falling back to unavailable. Both are selectable via poisson_solver_create(method, POISSON_BACKEND_OMP) and follow the existing cg_omp/redblack_omp pattern: per-element updates are identical to the scalar reference, so results agree within reduction rounding (verified by new scalar-vs-OMP consistency tests). BiCGSTAB keeps the scalar solve loop and breakdown checks verbatim, parallelizing only the primitives. Plain lexicographic SOR stays scalar/SIMD-only: it is inherently sequential, and its parallel form is the existing Red-Black SOR OMP solver. Also updates the stale BiCGSTAB backend test, which asserted OMP create() returns NULL, to the runtime availability contract.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR completes the OpenMP (“OMP”) tier for Poisson/linear solvers by adding Jacobi and BiCGSTAB OMP backends, enabling parallel pressure/projection paths to select these methods instead of falling back to unavailable backends.
Changes:
- Add new OpenMP implementations for Jacobi and BiCGSTAB linear solvers and register them in the solver factory/build.
- Add scalar-vs-OMP consistency tests for Jacobi and BiCGSTAB, and update the BiCGSTAB backend-availability test to follow the runtime contract.
- Update documentation (ROADMAP/CHANGELOG) to reflect the completed OMP linear-solver tier and clarify why plain lexicographic SOR remains non-OMP.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/math/test_omp_consistency.c | Adds Jacobi + BiCGSTAB scalar-vs-OMP consistency tests and wires them into the test runner. |
| tests/math/test_bicgstab.c | Updates backend-availability expectations to match runtime poisson_solver_backend_available() contract. |
| ROADMAP.md | Marks Jacobi/BiCGSTAB OMP as done and documents why plain SOR remains sequential. |
| lib/src/solvers/linear/omp/linear_solver_jacobi_omp.c | New Jacobi OpenMP backend (parallel interior sweep, common solve loop). |
| lib/src/solvers/linear/omp/linear_solver_bicgstab_omp.c | New BiCGSTAB OpenMP backend (OMP-parallel primitives, scalar-identical solve loop). |
| lib/src/solvers/linear/linear_solver.c | Registers the new OMP solver factories for Jacobi and BiCGSTAB. |
| lib/src/solvers/linear/linear_solver_internal.h | Exposes new OMP factory prototypes under CFD_ENABLE_OPENMP. |
| lib/include/cfd/solvers/poisson_solver.h | Adds solver type string constants for the new OMP variants. |
| lib/CMakeLists.txt | Adds new OMP solver sources to the OpenMP build source list. |
| CHANGELOG.md | Documents the new OpenMP backends and their selection/consistency guarantees. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The interior sweep casts nx/ny to int for its OpenMP loop bounds; a larger dimension overflows the cast and the sweep silently does no work. Fail init with CFD_ERROR_LIMIT_EXCEEDED so the solver never runs on unsupported dims.
bicgstab_size_to_int() maps an nx/ny above INT_MAX to 0, so every OMP primitive skips its sweep, the initial residual reads zero, and the solver reports false convergence. Fail init with CFD_ERROR_LIMIT_EXCEEDED before allocating the work vectors.
Master's #206 renamed bicgstab_size_to_int, so once master is merged the OMP BiCGSTAB primitives call an undeclared function and every CI build fails to link.
Master's linear_solver_primitives_omp.h already provides dot_product, axpy, apply_laplacian, compute_residual and copy_vector for the OMP Krylov solvers; private copies here would let the implementations drift apart.
Master's poisson_solver_validate_grid_size also rejects grids whose nx*ny*nz wraps size_t, which the hand-rolled INT_MAX checks missed: BiCGSTAB OMP would allocate undersized work vectors and Jacobi OMP would memcpy a wrapped size. Cover both in the shared oversized-grid table test instead of one-off tests.
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
Add 3D OpenMP coverage for the new Jacobi and BiCGSTAB paths.
Review details
Suppressed comments (3)
tests/math/test_omp_consistency.c:410
- These new consistency tests only initialize
nz=1, but both implementations add a separatenz>1path usingk_start/k_end/stride_z. The existing 3D OMP suite covers only CG and Red-Black SOR (tests/math/test_poisson_3d.c:765-770), so z-plane indexing and boundary handling for the new Jacobi and BiCGSTAB backends can regress without being detected. Please add 3D OMP coverage for both methods, preferably comparing them with the scalar backends.
void test_jacobi_omp_vs_scalar(void) {
tests/math/test_omp_consistency.c:413
- This new consistency test only initializes a 2D problem (
nz == 1), so it never exercises the new Jacobi OMP 3Dk_start/k_endandstride_zpath. The existing 3D OMP suite covers CG and Red-Black SOR but not Jacobi; add a 3D Jacobi OMP-vs-scalar or convergence case before relying on this backend for 3D projection solves.
void test_jacobi_omp_vs_scalar(void) {
double dx = (XMAX - XMIN) / (NX - 1);
double dy = (YMAX - YMIN) / (NY - 1);
size_t n = NX * NY;
tests/math/test_omp_consistency.c:514
- This new consistency test also hard-codes
nz == 1, leaving the new BiCGSTAB OMP 3D stencil and per-plane parallel primitives untested. The existing 3D OMP tests register only CG and Red-Black SOR, so add a 3D BiCGSTAB OMP-vs-scalar or convergence case to cover the path used by 3D callers.
void test_bicgstab_omp_vs_scalar(void) {
double dx = (XMAX - XMIN) / (NX - 1);
double dy = (YMAX - YMIN) / (NY - 1);
size_t n = NX * NY;
- Files reviewed: 11/11 changed files
- Comments generated: 0 new
- Review effort level: Lite
shaia
added a commit
that referenced
this pull request
Sep 12, 2026
…lper Merging master brings #202's Jacobi and BiCGSTAB comparisons, which call init_sinusoidal_rhs with the old 2D signature and do not compile against this branch's nz/dz version. Pass the 2D arguments explicitly, fail rather than skip when an available OMP backend returns a NULL solver (the policy this branch introduces), and reuse interior_rms_diff for the solution difference.
shaia
added a commit
that referenced
this pull request
Sep 12, 2026
…age (#207) * Fail OMP consistency tests on a NULL solver and widen GMRES OMP coverage Unity counts TEST_IGNORE as a pass, so a regressed OMP dispatch arm skipped the only consistency check while CI stayed green. The GMRES OMP comparison now covers Jacobi, restart 5 and 1, 3D, non-square, zero-RHS and max-iteration cases, run at 1, 2 and 4 threads through OMP_NUM_THREADS because on MSVC the test cannot reach the library's OpenMP runtime in-process. GMRES also joins the 3D Poisson and MG-preconditioner rejection tests. * Compare 3D backend solutions field by field, not by error norm The OMP- and SIMD-vs-scalar 3D checks compared each run's L2 error against the analytical field, so two different solutions with equal error norms passed. Keep both solutions and assert their interior RMS difference against a 1e-9 same-solver tolerance (measured 1.6e-15..4.0e-15 across OMP at 1-8 threads and AVX2). The CG OMP and SIMD comparisons predate this PR; GMRES inherited the flaw. * Adapt master's Jacobi/BiCGSTAB OMP consistency tests to the 3D RHS helper Merging master brings #202's Jacobi and BiCGSTAB comparisons, which call init_sinusoidal_rhs with the old 2D signature and do not compile against this branch's nz/dz version. Pass the 2D arguments explicitly, fail rather than skip when an available OMP backend returns a NULL solver (the policy this branch introduces), and reuse interior_rms_diff for the solution difference.
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.
Completes the OpenMP linear-solver tier (previously only CG, GMRES, and Red-Black SOR), so the parallel projection/pressure paths can select these methods instead of falling back to unavailable.
Both are selectable via poisson_solver_create(method, POISSON_BACKEND_OMP) and follow the existing cg_omp/redblack_omp pattern: per-element updates are identical to the scalar reference, so results agree within reduction rounding (verified by new scalar-vs-OMP consistency tests). BiCGSTAB keeps the scalar solve loop and breakdown checks verbatim, parallelizing only the primitives.
Plain lexicographic SOR stays scalar/SIMD-only: it is inherently sequential, and its parallel form is the existing Red-Black SOR OMP solver.
Also updates the stale BiCGSTAB backend test, which asserted OMP create() returns NULL, to the runtime availability contract.