Solvers
Guide to linear and nonlinear solvers in Rodin.
Introduction
After assembling a finite element problem, the resulting linear system
must be solved. Rodin provides several solvers in the Rodin::
Available Solvers
Rodin provides a wide range of solvers. They are organized into direct solvers (exact up to floating-point precision), iterative solvers (approximate via successive refinement), and nonlinear solvers.
Direct Solvers
| Solver | Class | Matrix requirement | Notes |
|---|---|---|---|
| SparseLU | Solver::SparseLU | Any non-singular | General-purpose, good default for small/medium problems |
| UMFPack | Solver::UMFPack | Any non-singular | SuiteSparse-based, often faster than SparseLU |
| CHOLMOD | Solver::CHOLMOD | Symmetric positive definite | SuiteSparse Cholesky factorization |
| SimplicialLLT | Solver::SimplicialLLT | Symmetric positive definite | Eigen's simplicial Cholesky |
| SimplicialLDLT | Solver::SimplicialLDLT | Symmetric | Eigen's simplicial factorization |
| LDLT | Solver::LDLT | Symmetric | Dense factorization |
| HouseholderQR | Solver::HouseholderQR | Any | Dense QR factorization |
| SparseQR | Solver::SparseQR | Any | Sparse QR factorization |
| SPQR | Solver::SPQR | Any | SuiteSparse sparse QR |
Iterative Solvers
| Solver | Class | Matrix requirement | Notes |
|---|---|---|---|
| CG | Solver::CG | Symmetric positive definite | Conjugate Gradient — optimal for SPD systems |
| BiCGSTAB | Solver::BiCGSTAB | Any | Bi-conjugate gradient stabilized |
| GMRES | Solver::GMRES | Any | Generalized Minimum Residual |
| DGMRES | Solver::DGMRES | Any | Deflated GMRES variant |
| MINRES | Solver::MINRES | Symmetric | Minimum Residual method |
| IDRS | Solver::IDRS | Any | Induced Dimension Reduction |
| IDRSTABL | Solver::IDRSTABL | Any | IDR(s) with higher-order stabilization |
| LeastSquaresCG | Solver::LeastSquaresCG | Any | Least-squares CG for rectangular systems |
Direct Solvers
Direct solvers compute the exact solution (up to floating-point precision) by factoring the system matrix. They are reliable but can be expensive for very large systems.
SparseLU
The SparseLU solver performs a sparse LU decomposition. It works for any non-singular matrix, making it the most general-purpose choice:
#include <Rodin/Solver.h> #include <Rodin/Geometry.h> #include <Rodin/Variational.h> using namespace Rodin; using namespace Rodin::Geometry; using namespace Rodin::Variational; int main() { Mesh mesh; mesh = mesh.UniformGrid(Polytope::Type::Triangle, {16, 16}); mesh.getConnectivity().compute(1, 2); P1 Vh(mesh); TrialFunction u(Vh); TestFunction v(Vh); Problem poisson(u, v); poisson = Integral(Grad(u), Grad(v)) - Integral(RealFunction(1.0), v) + DirichletBC(u, Zero()); // Inline usage — construct solver with the problem, then solve Solver::SparseLU(poisson).solve(); return 0; }
SparseLU is a good default choice when:
- The system is not too large (up to roughly 100,000 DOFs)
- You need a robust solver that works for any matrix structure
- The system matrix is non-symmetric or indefinite
UMFPack
UMFPack is an alternative direct solver from the SuiteSparse library. It is especially effective for general unsymmetric sparse systems and often outperforms SparseLU on larger problems:
#include <Rodin/Solver/UMFPack.h> Solver::UMFPack(problem).solve();
UMFPack requires the SuiteSparse library to be installed. It is the recommended direct solver for:
- Saddle-point systems (e.g. Stokes, Navier–Stokes)
- Coupled multi-physics problems
- Systems where SparseLU is too slow
Iterative Solvers
Iterative solvers approximate the solution through successive iterations. They are more efficient for large systems but may require tuning.
Conjugate Gradient (CG)
The CG method is optimal for symmetric positive definite (SPD) systems, which arise from many common PDEs (Poisson, elasticity, heat equation):
// Inline usage — construct with the problem and solve immediately Solver::CG(poisson).solve();
The CG solver supports several configuration options:
Solver::CG solver(poisson); solver.setTolerance(1e-10); // Relative convergence threshold solver.setMaxIterations(1000); // Maximum number of iterations solver.solve(); if (solver.success()) std::cout << "CG converged." << std::endl;
CG is the recommended solver when:
- The system matrix is symmetric positive definite
- The system is large (100,000+ DOFs)
- You want memory-efficient iterative solving
Newton Solver
For nonlinear variational problems (e.g. hyperelasticity, Navier–Stokes), Rodin provides a NewtonSolver<LinearSolver> that wraps a linear solver and performs Newton-Raphson iteration. At each iterate , the tangent system is assembled and solved:
where is the Jacobian (tangent matrix) and is the nonlinear residual.
#include <Rodin/Solver/NewtonSolver.h> #include <Rodin/Solver/SparseLU.h> Solver::SparseLU linearSolver(problem); Solver::NewtonSolver newton(linearSolver); newton.setMaxIterations(20); newton.setAbsoluteTolerance(1e-10); newton.setRelativeTolerance(1e-8); newton.solve(u); // updates u in place
Configuration methods:
| Method | Default | Description |
|---|---|---|
setMaxIterations(n) | 100 | Maximum Newton iterations |
setAbsoluteTolerance(tol) | Converged when | |
setRelativeTolerance(tol) | Converged when |
Convergence is declared when either criterion is satisfied (not both).
Hyperelastic Example
For hyperelastic problems, the Newton system is built from Solid::MaterialTangent (tangent stiffness) and Solid::InternalForce (nonlinear residual), both linearized about the current displacement:
#include <Rodin/Solid.h> #include <Rodin/Solver/NewtonSolver.h> #include <Rodin/Solver/SparseLU.h> Solid::NeoHookean law(lambda, mu); GridFunction u(Vh); // accumulated displacement Solid::MaterialTangent tangent(law, du, v); tangent.setDisplacement(u); Solid::InternalForce residual(law, v); residual.setDisplacement(u); Problem newton(du, v); newton = tangent + residual - Integral(bodyForce, v) + DirichletBC(du, VectorFunction{Zero(), Zero()}).on(fixedBC); SparseLU linearSolver(newton); NewtonSolver solver(linearSolver); solver.setMaxIterations(50) .setAbsoluteTolerance(1e-10); solver.solve(u);
The Newton solver is used for:
- Hyperelastic solid mechanics (with
Solid::InternalForce/MaterialTangent) - Nonlinear PDEs (Navier–Stokes, nonlinear diffusion)
- Any problem where the bilinear form depends on the current solution
Usage Patterns
Rodin solvers support two main usage patterns.
Inline Solving
For quick one-shot use, construct the solver with the problem and call solve() in a single expression. This is the most common pattern in Rodin:
Solver::CG(poisson).solve(); Solver::SparseLU(elasticity).solve();
Configured Solving
When you need to tune parameters, construct the solver object first, configure it, then call solve():
Solver::CG solver(poisson); solver.setMaxIterations(500); solver.setTolerance(1e-12); solver.solve();
Solver Reuse via problem.solve()
You can also pass a pre-configured solver to problem.solve(). This is useful when the same solver is applied to multiple problems (e.g.\ in an optimization loop):
Solver::SparseLU solver; for (size_t i = 0; i < nIterations; ++i) { // ... update problem ... problem.solve(solver); }
Choosing a Solver
| Criterion | Recommended solver | Why |
|---|---|---|
| Small system (< 100k DOFs), any matrix | SparseLU or UMFPack | Robust direct factorization |
| Large SPD system (Poisson, elasticity) | CG | Memory-efficient, optimal for SPD |
| Large non-symmetric system | BiCGSTAB or GMRES | General iterative methods |
| Symmetric indefinite (saddle-point) | UMFPack or MINRES | UMFPack is most robust |
| Saddle-point (Stokes, mixed FEM) | UMFPack | Handles indefinite structure |
| SPD with SuiteSparse available | CHOLMOD or SimplicialLLT | Exploit symmetric structure |
| Need guaranteed convergence | Any direct solver | No iteration needed |
| Memory-constrained large system | CG (if SPD), GMRES (otherwise) | Matrix-free possible |
| Nonlinear PDE | Newton + any linear solver | Successive linearization |
Complete Example
#include <Rodin/Solver.h> #include <Rodin/Geometry.h> #include <Rodin/Variational.h> #include <Rodin/IO/XDMF.h> using namespace Rodin; using namespace Rodin::Geometry; using namespace Rodin::Variational; int main() { Mesh mesh; mesh = mesh.UniformGrid(Polytope::Type::Triangle, {32, 32}); mesh.getConnectivity().compute(1, 2); P1 Vh(mesh); TrialFunction u(Vh); TestFunction v(Vh); Problem poisson(u, v); poisson = Integral(Grad(u), Grad(v)) - Integral(RealFunction(1.0), v) + DirichletBC(u, Zero()); // Solve with CG Solver::CG solver(poisson); solver.setMaxIterations(1000); solver.setTolerance(1e-10); solver.solve(); // Export result IO::XDMF xdmf("SolverExample"); xdmf.grid().setMesh(mesh).add("u", u.getSolution()); xdmf.write(); return 0; }
PETSc Solvers
When Rodin is built with PETSc support (RODIN_USE_PETSC=ON), additional solvers become available that support both sequential and MPI-distributed linear systems. PETSc solvers work with PETSc::PETSc::Variational::TrialFunction / PETSc::Variational::TestFunction instead of the standard trial/test functions.
KSP (Krylov Subspace)
KSP is the base PETSc linear solver wrapper. It provides access to the full PETSc Krylov solver library and can be configured both programmatically and via PETSc command-line options.
| Method | Description |
|---|---|
setType(KSPType) | Select algorithm (e.g. KSPCG, KSPGMRES, KSPBCGS) |
setTolerances(rtol, abstol, dtol, maxIt) | Set convergence tolerances and iteration limit |
setPreconditioner(Mat) | Supply an explicit preconditioner matrix |
setPrefix(string) | Set option prefix for multi-solver configurations |
#include <Rodin/PETSc.h> // Use the KSP base solver directly Solver::KSP(problem).solve(); // Configure via PETSc command-line options: // ./myprogram -ksp_type gmres -pc_type ilu -ksp_rtol 1e-8 -ksp_monitor
PETSc CG
CG<PETSc::KSPCG. It is the recommended iterative solver for SPD systems when using PETSc:
PETSc::Variational::TrialFunction u(Vh); PETSc::Variational::TestFunction v(Vh); Problem poisson(u, v); poisson = Integral(Grad(u), Grad(v)) - Integral(f, v) + DirichletBC(u, Zero()); Solver::CG(poisson).solve();
PETSc GMRES
GMRES<PETSc::KSPGMRES. Suitable for non-symmetric and indefinite systems:
Solver::GMRES(stokes).solve();
SNES (Nonlinear)
SNES wraps the PETSc Scalable Nonlinear Equations Solver for Newton-type methods. It uses an associated KSP for the inner linear solve at each Newton step:
// Create the KSP linear solver Solver::KSP ksp(problem); ksp.setTolerances(1e-10, 1e-14, 1e5, 200); // Wrap it in SNES for Newton iteration Solver::SNES snes(ksp); snes.setTolerances(1e-8, 1e-10, 1e-6, 50, 10000); snes.solve(x);
Multi-Field Problems
PETSc solvers support block systems with multiple trial/test function pairs. The resulting linear system can be solved with field-split preconditioning configured via PETSc command-line options:
PETSc::Variational::TrialFunction u(Vh_velocity); PETSc::Variational::TrialFunction p(Vh_pressure); PETSc::Variational::TestFunction v(Vh_velocity); PETSc::Variational::TestFunction q(Vh_pressure); Problem stokes(u, p, v, q); stokes = Integral(Jacobian(u), Jacobian(v)) - Integral(p, Div(v)) + Integral(Div(u), q) - Integral(f, v) + DirichletBC(u, g); Solver::KSP(stokes).solve(); // Run with: -ksp_type gmres -pc_type fieldsplit -ksp_monitor
PETSc Solver Summary
| Solver | Class | System type | Notes |
|---|---|---|---|
| KSP | Solver::KSP | Any (via PETSc type selection) | Base wrapper, full PETSc configuration |
| CG | Solver::CG<PETSc::Math::LinearSystem> | SPD | Sets KSPCG |
| GMRES | Solver::GMRES<PETSc::Math::LinearSystem> | Non-symmetric, indefinite | Sets KSPGMRES |
| SNES | Solver::SNES | Nonlinear | Newton-type, uses KSP internally |
Troubleshooting
- CG does not converge: The matrix may not be SPD. Switch to SparseLU or UMFPack.
- SparseLU runs out of memory: The system is too large for a direct solver. Use CG instead (if SPD) or consider PETSc solvers.
- Solution is incorrect: Check that boundary conditions are applied correctly and that the mesh connectivity is computed before assembling.
- PETSc solver does not converge: Use
-ksp_monitorto inspect residual history. Try-pc_type gamgfor AMG preconditioning or-ksp_type gmres -pc_type ilufor non-SPD systems.