Skip to content

Add SparseSolv iterative solvers (ICCG, ICMRTR, SGSMRTR)#86

Open
ksugahar wants to merge 4 commits intoNGSolve:masterfrom
ksugahar:feature/sparsesolv
Open

Add SparseSolv iterative solvers (ICCG, ICMRTR, SGSMRTR)#86
ksugahar wants to merge 4 commits intoNGSolve:masterfrom
ksugahar:feature/sparsesolv

Conversation

@ksugahar
Copy link

Summary

This PR integrates SparseSolv, a header-only C++ library for iterative sparse linear solvers, into NGSolve. It provides:

  • Preconditioners: Incomplete Cholesky (IC), Incomplete LU (ILU), Symmetric Gauss-Seidel (SGS) — usable as standalone BaseMatrix preconditioners with NGSolve's existing Krylov solvers (e.g. CGSolver, GMRESSolver)
  • Integrated solver: SparseSolvSolver class combining preconditioner + Krylov method in a single BaseMatrix operator, supporting:
    • ICCG (IC + Conjugate Gradient)
    • ICMRTR (IC + MRTR method)
    • SGSMRTR (SGS + MRTR method)
  • Auto-shift IC for semi-definite systems (e.g. HCurl curl-curl with nograds=True): automatically increments the IC shift when negative pivots are detected during factorization
  • Diagonal scaling for improved conditioning

Python API

from ngsolve.la import SparseSolvSolver, ICPreconditioner

# All-in-one solver (BaseMatrix, use as: gfu.vec.data = solver * f.vec)
solver = SparseSolvSolver(a.mat, method="ICCG",
                           freedofs=fes.FreeDofs(),
                           tol=1e-10, maxiter=2000)
gfu.vec.data = solver * f.vec

# Standalone preconditioner (use with NGSolve CGSolver)
from ngsolve.krylovspace import CGSolver
pre = ICPreconditioner(a.mat, freedofs=fes.FreeDofs(), shift=1.05)
pre.Update()
inv = CGSolver(a.mat, pre, tol=1e-10, maxiter=2000)
gfu.vec.data = inv * f.vec

Files added/modified

New files (header-only library, no new build dependencies):

  • linalg/sparsesolv/ — 14 header files (core types, preconditioners, solvers)
  • linalg/sparsesolv_precond.hpp — NGSolve wrapper classes (ICPreconditioner, ILUPreconditioner, SGSPreconditioner, SparseSolvSolver)
  • tests/pytest/test_sparsesolv.py — 14 pytest tests

Modified files:

  • linalg/CMakeLists.txt — install SparseSolv headers
  • linalg/la.hpp — include sparsesolv_precond.hpp
  • linalg/python_linalg.cpp — Python bindings (~400 lines)

Test results

All 14 new tests pass, existing solver tests unaffected:

Test Description
test_sparsesolv_solver_2d_poisson[ICCG/ICMRTR/SGSMRTR] All methods converge on 2D Poisson (order=2)
test_sparsesolv_solver_3d_poisson ICCG on 3D Poisson
test_sparsesolv_solver_vs_direct ICCG matches direct solver (rel err < 1e-6)
test_preconditioners_with_ngsolve_cg[IC/ILU/SGS] Preconditioners work with NGSolve CGSolver
test_auto_shift_curl_curl Auto-shift IC for 3D curl-curl (HCurl, nograds=True)
test_residual_history Residual history recording
test_no_residual_history Residual history disabled by default
test_properties Property getters/setters
test_invalid_method Invalid method raises RuntimeError
test_operator_interface solver * vec matches solver.Solve()

Test plan

  • All 14 new pytest tests pass (python -m pytest test_sparsesolv.py -v)
  • Existing 3 solver tests pass (python -m pytest test_solvers.py -v)
  • Verified on 2D/3D Poisson, 3D curl-curl (HCurl), with exact solution comparison
  • CI pipeline validation

🤖 Generated with Claude Code

ksugahar and others added 4 commits February 11, 2026 00:49
- Add IC (Incomplete Cholesky) preconditioner with shift parameter
- Add ILU (Incomplete LU) preconditioner for general matrices
- Add SGS (Symmetric Gauss-Seidel) preconditioner
- Support FreeDofs (Dirichlet BC) handling for all preconditioners
- Add OpenMP parallelization for setup and apply operations
- Expose preconditioners to Python via pybind11

Based on JP-MARs/SparseSolv library.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Expose all SparseSolv iterative solvers through a unified SparseSolvSolver
class with Python bindings:
- ICCG: CG + Incomplete Cholesky preconditioner
- ICMRTR: MRTR + Incomplete Cholesky preconditioner
- SGSMRTR: MRTR with built-in Symmetric Gauss-Seidel (split formula)
- CG/MRTR: solvers without preconditioner

Features:
- save_best_result: tracks best solution during iteration (default on)
- save_residual_history: records convergence history
- FreeDofs support for Dirichlet boundary conditions
- BaseMatrix interface for use as inverse operator
- SparseSolvResult for detailed convergence info

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ability

IC Preconditioner:
- Add auto-shift restart: when diagonal drops below threshold during IC
  factorization, increment shift parameter and restart (MATLAB reference)
- Add diagonal scaling (1/sqrt(A[i,i])) before factorization
- Clamp zero/negative diagonals to replacement value (instead of 1/epsilon)
- New SolverConfig parameters: auto_shift, shift_increment, max_shift_value,
  min_diagonal_threshold, zero_diagonal_replacement, max_shift_trials

CG Solver:
- Check convergence before reporting breakdown (pAp < 1e-30)

MRTR Solver:
- Regularize near-zero denominator (like SGS-MRTR) instead of aborting

Python bindings:
- Add auto_shift and diagonal_scaling properties to SparseSolvSolver

Revert fork-specific README changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests cover:
- SparseSolvSolver with all methods (ICCG, ICMRTR, SGSMRTR) on 2D/3D Poisson
- IC/ILU/SGS preconditioners with NGSolve CGSolver
- Auto-shift + diagonal scaling for 3D curl-curl (HCurl nograds=True)
- Residual history tracking
- Property accessors and error handling
- Operator interface (solver * vec vs solver.Solve())

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

1 participant