Solver Backends And Performance¶
voids exposes sparse linear solver choices in two layers:
- PNM and TPFA use the shared
voids.linalg.solve.solve_linear_systeminterface. - FEM uses DOLFINx assembly and selects a linear algebra path through
FEniCSSolverOptions.linear_backend.
The solver backend is a numerical linear algebra choice. It should not change the pore-network equations, the TPFA discretization, the FEM weak form, the boundary conditions, or the porosity/permeability map closure. If two direct backends disagree beyond roundoff on the same assembled problem, treat that as a numerical diagnostic before interpreting the permeability physically.
Installation¶
The Pixi default and test environments include the solver feature:
Plain pip install voids keeps optional solver stacks separate. Install Python
solver helpers with:
FEM still requires a compatible DOLFINx/FEniCSx installation. On native Windows,
the conda-forge DOLFINx stack used by voids does not provide the PETSc-backed
dolfinx.fem.petsc path, so FEM linear_backend="auto" falls back to the
serial SciPy sparse direct backend when PETSc is unavailable.
PNM And TPFA Backends¶
PNM and TPFA call solve_linear_system(A, b, method=...) after assembling their
sparse systems.
| Method | Backend | Typical use | Main caveat |
|---|---|---|---|
"direct" |
scipy.sparse.linalg.spsolve |
default sparse direct solve | SciPy may use UMFPACK internally when configured that way |
"umfpack" |
scikits.umfpack.spsolve |
explicit SuiteSparse/UMFPACK direct solve, including Windows fallback studies | requires scikit-umfpack and UMFPACK libraries |
"pardiso" |
pypardiso.spsolve |
Linux MKL/PARDISO direct solve | not a portable Windows fallback |
"cg" |
SciPy conjugate gradient | symmetric positive systems | convergence depends on conditioning and tolerances |
"gmres" |
SciPy GMRES | nonsymmetric or harder systems | restart and tolerance choices matter |
cg and gmres accept optional PyAMG preconditioning:
from voids.physics.singlephase import SinglePhaseOptions
options = SinglePhaseOptions(
solver="gmres",
solver_parameters={
"rtol": 1.0e-10,
"maxiter": 800,
"restart": 80,
"preconditioner": "pyamg",
},
)
Explicit UMFPACK for PNM:
from voids.physics.singlephase import SinglePhaseOptions
options = SinglePhaseOptions(solver="umfpack")
Explicit UMFPACK for TPFA:
from voids.fvm.singlephase import solve_tpfa
result = solve_tpfa(permeability_map, solver_method="umfpack")
FEM Backends¶
FEM backends use the same DOLFINx/UFL forms and coefficient maps, then select the linear solve path:
linear_backend |
Linear algebra path | Platform role | Main caveat |
|---|---|---|---|
"auto" |
PETSc when available; SciPy direct fallback on native Windows when PETSc is missing | recommended default for portable scripts | resolved backend can differ by platform |
"petsc" |
DOLFINx PETSc LinearProblem with PETSc options from FEniCSSolverOptions |
production Linux/macOS path; supports PETSc/MPI workflows | unavailable in native Windows conda-forge DOLFINx stack used by voids |
"scipy" |
DOLFINx assembly converted to SciPy sparse format and solved with spsolve |
serial direct fallback and comparison backend | serial-only |
"umfpack" |
DOLFINx assembly converted to SciPy sparse format and solved with scikits.umfpack.spsolve |
explicit serial SuiteSparse/UMFPACK path | requires scikit-umfpack; serial-only |
Default portable behavior:
from voids.fem.singlephase import FEniCSSolverOptions, upscale_permeability_fem
result = upscale_permeability_fem(problem, options=FEniCSSolverOptions())
print(result.results["x"].metadata["linear_backend"])
Force PETSc, SciPy, or UMFPACK:
from voids.fem.singlephase import FEniCSSolverOptions
petsc_options = FEniCSSolverOptions.direct_lu("mumps")
scipy_options = FEniCSSolverOptions.scipy_direct()
umfpack_options = FEniCSSolverOptions.umfpack_direct()
For reproducible reports, store the requested backend, the resolved backend from result metadata, the formulation name, pressure drop, map shape, permeability and porosity floors, and the numerical thread environment.
Benchmark Design¶
The executable benchmark is
17_mwe_solver_options_benchmark.
It writes stable plots and CSV tables under docs/assets/solver_backends/.
The PNM section compares:
- SciPy direct solve,
- explicit UMFPACK direct solve,
- CG and GMRES,
- CG/GMRES with PyAMG preconditioning,
- Picard and Newton outer iterations for pressure-dependent viscosity.
The FEM section compares PETSc/MUMPS, SciPy direct sparse, and UMFPACK on homogeneous 2-D maps for:
- Taylor-Hood Darcy-Darcy,
- Taylor-Hood Darcy-Brinkman,
- stabilized USFEM Darcy-Brinkman.
It reports permeability, flow rate, pressure field relative \(L^2\) difference, velocity field relative \(L^2\) difference, and repeated-run wall time.
Benchmark Results¶
The current benchmark data are available as CSV artifacts:
- PNM constant-viscosity solver table
- PNM variable-viscosity solver table
- Solver speedup table
- FEM backend benchmark table
- FEM backend summary table
The headline results from the generated tables are:
| Benchmark slice | Best local median runtime | Agreement with reference |
|---|---|---|
| PNM constant-viscosity network solve | CG, 0.0093 s | \(K\) relative difference \(3.8 \times 10^{-14}\) |
| PNM constant-viscosity direct solve | UMFPACK, 0.0101 s | \(K\) relative difference \(3.7 \times 10^{-15}\) |
| PNM variable-viscosity Newton solve | SciPy direct, 0.0180 s | reference row |
| FEM backend summary | UMFPACK, 0.0099 s median over 9 cases | max field relative \(L^2\) difference \(2.5 \times 10^{-15}\) |
| FEM backend summary | SciPy direct, 0.0104 s median over 9 cases | max field relative \(L^2\) difference \(2.6 \times 10^{-15}\) |
| FEM backend summary | PETSc/MUMPS, 0.0211 s median over 9 cases | reference row |
These numbers are from the local serial notebook run used to generate the committed assets. They are performance evidence for this benchmark size and machine, not a universal backend ranking.






On the homogeneous benchmark systems, the direct sparse backends recover the same PNM permeability and flow rate to roundoff. The FEM backends recover the same permeability, flow rate, pressure field, and velocity field to numerical roundoff when all requested backends are installed. Runtime ranking is local to the benchmark machine and problem size; for larger heterogeneous 3-D maps, repeat the notebook at the target resolution before choosing a production backend.
Performance Guidance¶
- Use
"direct"for the most portable PNM/TPFA baseline. - Use
"umfpack"when you want to request SuiteSparse/UMFPACK explicitly, especially for Windows-compatible direct-solver studies. - Use
"pardiso"only when the Linux MKL/PARDISO stack is available and has been checked against the same system. - Use Krylov methods with PyAMG when direct factorizations become too expensive, but record convergence tolerances and residuals.
- Use FEM
"petsc"for PETSc/MPI or heavily configured production runs. - Use FEM
"scipy"or"umfpack"for serial Windows-compatible FEM solves when DOLFINx core is available but PETSc is not.
Scientific Caveats¶
Solver agreement does not prove that the physical closure is correct. Permeability estimates still depend on pore/throat geometry, map construction, permeability floors, porosity floors, pressure conventions, side-wall conditions, and representative-volume assumptions.