Linear solvers
A nonlinear solver solves one linear system per Newton iteration. Which linear solver it uses is its linsolver
option: the name of a *linearSolver block, or a type of linear solver with its defaults.
*solver, solver=NIST, name=theSolver
linsolver=pardiso
A *linearSolver block defines a linear solver with options, as *material defines a material: the keyword line
holds only its identity, name and type; the data lines are the options of that type, one key=value per line
(an option the type does not have is an error):
*linearSolver, name=fast, type=blockamg
cornerNodeInterpolation=false
verbosity=info
*solver, solver=NIST, name=theSolver
linsolver=fast
A type name in linsolver= means that type with its defaults, so a block must not be named like a type.
linsolver unset means pardiso if it was built, else superlu. From Python, the same linear solver is
getLinSolverByName("blockamg", {"rigidBodyModesPerAggregate": False}) (edelweissfe.config.linsolve).
Type |
Direct |
What it is |
|---|---|---|
|
✓ |
SciPy’s sparse LU (SuperLU), always available |
|
✓ |
UMFPACK through SciPy (SuperLU if |
|
✓ |
Intel MKL PARDISO ( |
|
✓ |
Panua PARDISO ( |
|
✓ |
SuiteSparse’s KLU ( |
|
✓ |
PETSc’s LU, through |
|
✓ |
MUMPS, through its Python bindings |
|
✗ |
the preset |
|
— |
a composed solver defined by you, in a Python script (see Composing a linear solver) |
Direct or iterative
Direct solvers factorize the matrix and back-substitute: superlu and umfpack (SciPy, always available),
pardiso (Intel MKL, the production default), panuapardiso, klu, petsclu and mumps. Pick one of
these unless the factorization has become the run’s bottleneck or exceeds available memory. A direct solver keeps its
backend for the whole step: PARDISO with reuseSymbolicFactorization=true keeps its reordering and symbolic
factorization while only the values of the matrix change. A singular matrix gives NaN, which the nonlinear solver
turns into a cutback.
Iterative solvers never factorize the full matrix, trading exactness for O(n) memory: past roughly a million DOFs
the fill-in of a direct factorization exceeds memory, whereas algebraic multigrid stays linear. blockamg is the
field-split solver for large coupled multi-field systems (e.g. displacement + gradient-enhanced damage): one AMG
hierarchy per physical field, combined by a block Gauss–Seidel sweep to precondition an outer LGMRES. The block
structure – which DOFs belong to which field, and each field’s dimension – is discovered automatically from the
model. It requires the optional amgcl extension.
*linearSolver, name=fieldSplit, type=blockamg
sweeps=1
symmetric=false
*solver, solver=NIST, name=theSolver
linsolver=fieldSplit
A custom solver script composes any other tree: a Schur complement that condenses the dofs on which AMG alone
stalls (bars tied to their host elements by bond springs, a softening zone), other smoothers, other coarse spaces.
How to write one, and the tree of blockamg, are on Composing a linear solver; why the field-split method works,
and each of its steps, on Theory: the field-split AMG preconditioner.
One adapter for every type
Every type is a composed solver (Composing a linear solver) behind one adapter,
ComposedLinearSolver: blockamg is the tree of the blockAMG()
preset, and a direct solver is a tree of one leaf, type=pardiso being DirectSolver("pardiso"). What the adapter
does therefore works the same for every type, with the same data lines
(LinearSolverSchema, described in The adapter):
Data line |
Default |
What it sets |
|---|---|---|
|
|
|
|
100 |
a Krylov solve with more outer iterations is warned about, and is a slow solve |
|
4 |
a right-hand side this many times larger than the previous one marks a new increment |
|
|
which solves are written to disk, as a sequence of authentic systems for an offline replay (Dumps of the solves) |
The options of each type
Option |
Type / Default |
Description |
|---|---|---|
verbosity |
str, default=’warning’ |
silent, warning (a warning for a slow or unconverged solve), info (one line per solve, the structure of the solver, and the ‘linear solve’ column of the nonlinear solver) or debug (also why a preconditioner was rebuilt or reused). |
warningOuterIterations |
int, default=100 |
A solve with more outer Krylov iterations than this is warned about, and is a slow solve. |
residualGrowthFactor |
float, default=4.0 |
A right-hand side this many times larger than the previous one marks a new increment. |
dumps |
str, default=’none’ |
Which solves are written to disk for an offline replay: none, slow (above warningOuterIterations, or stopped at the iteration cap), every, or the numbers of the solves separated by spaces, e.g. ‘12 13 14’. |
dumpDirectory |
str, default=’linsolveDumps’ |
Where the dumps go. |
maxDumps |
int, default=10 |
The most systems dumped to dumpDirectory in the run (a disk guard). |
dumpContextSolves |
int, default=0 |
How many of the solves preceding a dumped one are dumped with it. |
reuseSymbolicFactorization |
bool, default=False |
Keep PARDISO’s reordering and symbolic factorization while the sparsity pattern stays the same. Off by default: it has been observed to give silently wrong results for some coupled problems. |
The other direct solvers take only the common data lines above.
Option |
Type / Default |
Description |
|---|---|---|
verbosity |
str, default=’warning’ |
silent, warning (a warning for a slow or unconverged solve), info (one line per solve, the structure of the solver, and the ‘linear solve’ column of the nonlinear solver) or debug (also why a preconditioner was rebuilt or reused). |
warningOuterIterations |
int, default=100 |
A solve with more outer Krylov iterations than this is warned about, and is a slow solve. |
residualGrowthFactor |
float, default=4.0 |
A right-hand side this many times larger than the previous one marks a new increment. |
dumps |
str, default=’none’ |
Which solves are written to disk for an offline replay: none, slow (above warningOuterIterations, or stopped at the iteration cap), every, or the numbers of the solves separated by spaces, e.g. ‘12 13 14’. |
dumpDirectory |
str, default=’linsolveDumps’ |
Where the dumps go. |
maxDumps |
int, default=10 |
The most systems dumped to dumpDirectory in the run (a disk guard). |
dumpContextSolves |
int, default=0 |
How many of the solves preceding a dumped one are dumped with it. |
rigidBodyModesPerAggregate |
bool, default=True |
A (linear) 3D displacement field gets the two-level cycle on its rigid-body modes (else one AMG hierarchy, on the translations). |
cornerNodeInterpolation |
bool, default=True |
A quadratic 3D displacement field gets the two-level cycle on its corner nodes (else the case of a linear one). |
tolerance |
float, default=None |
A fixed relative tolerance per solve; unset: the Eisenstat-Walker forcing tolerance. |
maxIterations |
int, default=100 |
The outer iteration cap. |
reusePreconditioner |
bool, default=False |
Keep the preconditioner across solves until a new increment or a stale preconditioner asks for a rebuild (false: a new preconditioner for every solve). |
sweeps |
int, default=1 |
The block Gauss-Seidel sweeps over the fields. |
symmetric |
bool, default=False |
A symmetric block Gauss-Seidel sweep (forward, then backward). |
krylovOnScaledSystem |
bool, default=False |
Run LGMRES on the diagonally scaled matrix, with true-residual restarts. |
trueResidualRestarts |
int, default=2 |
The true-residual restarts (only with krylovOnScaledSystem). |
residualGapCompensation |
bool, default=False |
Tighten the first pass by the learnt gap between the scaled and the true residual (only with krylovOnScaledSystem). |
dropTolerance |
float, default=0.0 |
Build every field’s preconditioner on its block without the entries below this (relative). |
lumpDroppedEntries |
bool, default=False |
Add the dropped entries to the diagonal (keeps the row sums). |
Option |
Type / Default |
Description |
|---|---|---|
verbosity |
str, default=’warning’ |
silent, warning (a warning for a slow or unconverged solve), info (one line per solve, the structure of the solver, and the ‘linear solve’ column of the nonlinear solver) or debug (also why a preconditioner was rebuilt or reused). |
warningOuterIterations |
int, default=100 |
A solve with more outer Krylov iterations than this is warned about, and is a slow solve. |
residualGrowthFactor |
float, default=4.0 |
A right-hand side this many times larger than the previous one marks a new increment. |
dumps |
str, default=’none’ |
Which solves are written to disk for an offline replay: none, slow (above warningOuterIterations, or stopped at the iteration cap), every, or the numbers of the solves separated by spaces, e.g. ‘12 13 14’. |
dumpDirectory |
str, default=’linsolveDumps’ |
Where the dumps go. |
maxDumps |
int, default=10 |
The most systems dumped to dumpDirectory in the run (a disk guard). |
dumpContextSolves |
int, default=0 |
How many of the solves preceding a dumped one are dumped with it. |
script |
str (required) |
The solver script: a Python file that binds the composed solver, a tree of components, to its global linearSolver (a path relative to the working directory). |
hotReload |
bool, default=False |
Build the solver again whenever the script changes on disk, before a solve. |