Solvers
The solver section configures the nonlinear solve, the linear solve nested inside it, and the convergence criteria. It is required for quasi static and newmark, and ignored entirely by central difference, which is explicit.
solver:
type: newton
linear solver:
type: cg
preconditioner:
type: jacobi
termination:
converge when any:
- absolute residual: 1.0e-08
- relative residual: 1.0e-12
fail when any:
- maximum iterations: 16Nonlinear solvers
type | Aliases | Needs a linear solver? | GPU | Description |
|---|---|---|---|---|
newton | newton raphson, newton-raphson, hessian minimizer | yes | via linear solver | Newton–Raphson. Quadratic convergence near the solution. The default choice. |
nonlinear cg | nlcg, conjugate gradient | no | yes | Nonlinear CG. Matrix-free, linear convergence. |
steepest descent | gradient descent, sd | no | yes | Preconditioned steepest descent. Matrix-free, slowest, most robust. |
type is required; there is no default. hessian minimizer is accepted for Norma compatibility and builds a plain NewtonSolver.
Anything else aborts with the list above. In particular lbfgs is not a nonlinear solver type — it is a linear solver, selected with solver.linear solver.type: lbfgs. That mistake used to fall through to Newton silently, producing a run that looked fine but was not the algorithm requested:
ERROR: Unknown solver.type = "lbfgs". Supported: "newton" (aliases ...).
L-BFGS is a linear solver: set solver.linear solver.type = "lbfgs".For newton, a linear solver sub-section is required. For the two matrix-free solvers it is optional and defaults to type: none.
Keys common to all nonlinear solvers
| Key | Default | Description |
|---|---|---|
minimum iterations | 0 | Lower bound on iterations. |
maximum iterations | 20 | Upper bound. Overridden by a maximum iterations test in termination if one is present. |
absolute tolerance | 1e-10 | Used only when termination is absent. |
relative tolerance | 1e-14 | Used only when termination is absent. |
use line search | true | Armijo backtracking on ½‖R‖². Applies to newton only. |
line search backtrack factor | 0.5 | Step reduction per backtrack. |
line search decrease factor | 1e-4 | Armijo sufficient-decrease parameter. |
line search maximum iterations | 10 | Maximum backtracking steps. |
At large Δt or load increments the predictor can take element-inverting full steps; Armijo backtracking guards against that. It costs roughly one extra residual evaluation per iteration when the full step is already good (α = 1 is accepted immediately). Set use line search: false to disable.
use line search reaches newton only. NLCG and steepest descent always run their own line search, but they do honour the three line search * tuning keys.
Nonlinear CG only
| Key | Default | Description |
|---|---|---|
orthogonality tolerance | 0.5 | Restart when consecutive gradients lose orthogonality. |
restart interval | 0 | Force a restart every N iterations. 0 disables. |
preconditioner | none | type: jacobi (the default when the section is present) or type: none. |
Steepest descent accepts preconditioner with the same meaning. Jacobi is the only preconditioner implemented at this level; asking for anything else (chebyshev, ic, amg) is a hard error rather than silently running Jacobi.
Termination criteria
Carina accepts three syntaxes. Prefer the first.
Preferred: converge when / fail when
termination:
converge when any:
- absolute residual: 1.0e-08
- relative residual: 1.0e-12
fail when any:
- maximum iterations: 16
- divergence: 1.0e6Four block keys are recognised, each taking a list:
| Block key | Combines with |
|---|---|
converge when any | OR |
converge when all | AND |
fail when any | OR |
fail when all | AND |
Each list item is a single-key mapping of test name: value. Groups nest via any: / all: inside a list:
termination:
converge when all:
- minimum iterations: 2
- any:
- absolute residual: 1.0e-08
- relative residual: 1.0e-12Legacy: typed list
Still supported. Each entry carries an explicit type, with the value under tolerance, value, threshold, or window depending on the test, and combo/tests for nesting:
termination:
- type: combo
combo: and
tests:
- type: absolute residual
tolerance: 1.0e-06
- type: relative update
tolerance: 1.0e-12
- type: maximum iterations
value: 16combo must be and or or; the default when it is omitted is or. Any other value is an error — it previously fell through to or, which inverted the meaning of the group without saying so.
Oldest: flat tolerances
If termination is absent entirely, Carina builds OR(absolute residual, relative residual, finite value) from the flat absolute tolerance (default 1e-10) and relative tolerance (default 1e-14) keys.
Available tests
| Test name | Aliases | Value means | Signals |
|---|---|---|---|
absolute residual | abs_residual | tolerance | Converged when ‖R‖ < tol |
relative residual | rel_residual | tolerance | Converged when ‖R‖/‖R₀‖ < tol |
absolute update | abs_update | tolerance | Converged when ‖ΔU‖ < tol |
relative update | rel_update | tolerance | Converged when ‖ΔU‖/‖U‖ < tol |
maximum iterations | max iterations | iteration count | Failed when iter ≥ value |
minimum iterations | min iterations | iteration count | Converged when iter ≥ value |
finite value | nan check | (ignored) | Failed when ‖R‖ is not finite |
divergence | — | threshold | Failed when ‖R‖ > threshold·‖R₀‖ |
stagnation | — | window | Failed on insufficient residual reduction |
Points worth knowing:
minimum iterationsis a convergence test, not a floor. It reports Converged once the iteration count is reached. Placed in aconverge when anyblock it will force convergence on that iteration. It is only meaningful inside anallgroup, where it prevents premature convergence.- Relative tests normalise differently.
relative residualdivides by the initial residual ‖R₀‖;relative updatedivides by the current solution norm ‖U‖. Each is inert while its denominator is zero — arelative updatetest cannot converge on the first iteration from a zero initial guess. divergencecompares against ‖R₀‖, not the previous iterate, so it detects net growth from the start rather than a single bad step.stagnationuses its value twice: as the lookback distance and as the number of consecutive stagnant iterations required to fail. With the default window of 5 it needs at least 11 iterations before it can trigger. Its internal ratio floor is 0.95 — i.e. failure when ‖Rk‖/‖R{k−window}‖ > 0.95 — settable only through the legacy syntax'stolerancekey.- A
finite valuetest is appended automatically to every termination tree, in all three syntaxes. You do not need to add one. maximum iterationsin the tree wins. If the tree contains amaximum iterationstest, its value replaces the flatmaximum iterationskey as the solver loop bound.- In an OR group, Converged beats Failed. If one sub-test converges while another fails on the same iteration, the result is Converged. An AND group short-circuits on the first Failed.
Linear solvers
Configured under solver.linear solver.
type | Aliases | CPU | GPU | Description |
|---|---|---|---|---|
direct | — | yes | no | Sparse LU. Robust, no tuning. |
iterative | cg, krylov, minres, conjugate gradient | yes | yes | Conjugate gradient. All aliases produce CG — the stiffness is SPD. |
lbfgs | — | yes | yes | L-BFGS quasi-Newton. Matrix-free. |
none | — | yes | yes | No linear solve; for NLCG / steepest descent. |
Selecting direct on a GPU backend is a hard error:
solver.linear_solver.type = "direct" is CPU-only.Iterative keys
| Key | Default | Description |
|---|---|---|
maximum iterations | 1000 | CG iteration cap. |
tolerance | 1e-8 | Relative residual tolerance. |
preconditioner | none | See below. |
L-BFGS keys
| Key | Default | Description |
|---|---|---|
history size | 10 | Stored gradient pairs. More is a better inverse-Hessian approximation at higher memory cost. |
The L-BFGS path always builds a Jacobi preconditioner regardless of any preconditioner sub-section.
Use it for implicit dynamics only. On quasi-static problems it stalls roughly seven orders short of tolerance and the step fails — on CPU and GPU alike, so this is not a device limitation and no amount of history size fixes it.
L-BFGS models the inverse tangent from its last few secant pairs. Implicit dynamics adds the mass shift c_M = 1/(βΔt²), which at small Δt leaves the effective operator strongly diagonally dominant and easy to model at low rank — there L-BFGS is the fastest option Carina has on GPU. Quasi-static has no such term: the same system takes 787 AMG iterations to solve, and a rank-10 model cannot represent it. Use cg + amg for quasi-static.
assembled defaults to true on CPU and false on GPU. Setting assembled: false on the CPU forces the matrix-free operator path (the same operators the GPU runs); assembled: true on a GPU backend is a hard error — there is no device sparse matrix to assemble.
Preconditioners
Configured under solver.linear solver.preconditioner.
type | Aliases | CPU | GPU | Cost per iteration | Description |
|---|---|---|---|---|---|
jacobi | — | yes | yes | one vector scale | Diagonal scaling. Cheap, weak, always available. |
ic | incomplete cholesky, ildl, incomplete ldlt | yes | no | one triangular solve | Incomplete LDLᵀ. Strong for ill-conditioned systems. |
chebyshev | chebyshev polynomial | yes | yes | k matvecs | Polynomial preconditioner; needs only matvecs, so it works on GPU. |
amg | algebraic multigrid, multigrid | yes | yes | one V-cycle | Smoothed-aggregation AMG with rigid-body-mode near-nullspace. On GPU the hierarchy is built on the host and the V-cycle applies on the device. |
| (omitted) | — | yes | yes | none | No preconditioning. |
Chebyshev keys
| Key | Default | Description |
|---|---|---|
degree | 5 | Polynomial degree. Higher is stronger but costs degree matvecs per iteration. |
AMG
linear solver:
type: cg
tolerance: 1.0e-8
maximum iterations: 500
preconditioner:
type: amgAMG is the only preconditioner whose CG iteration count is nearly independent of the conditioning that defeats Jacobi — on a 530k-DOF torsion problem it holds 5–17 CG iterations across a range of Δt where Jacobi grows from 30 to
It runs on both CPU and GPU. On GPU the hierarchy is built on the host (the sparse pattern lives there anyway) and converted to device CSR; the V(2,2)-cycle then applies entirely on the device through KernelAbstractions kernels, with the fine level smoothed through the matrix-free stiffness action so the fine matrix is never formed on the device. It is the fastest quasi-static option Carina has — see benchmark_report.md.
Two behaviours worth knowing. The near-nullspace (six rigid-body modes) is rebuilt from the current nodal coordinates X + u at every hierarchy build, not frozen at the reference configuration — a frozen reference nullspace degrades badly once the body rotates substantially. And the hierarchy setup is expensive (seconds at 500k DOF), so it is lagged: built once, then rebuilt only when the effective-mass coefficient changes (a Δt change) or when CG iteration growth flags it as stale.
AMG targets SPD tangents — quasi-static and moderate-Δt dynamics. At very large Δt on violently dynamic problems the Newmark predictor can overshoot into near-inverted configurations whose tangent is indefinite, which breaks CG regardless of preconditioner; explicit integration is the right tool there.
An unrecognised type is a hard error listing the supported values:
Unknown preconditioner.type = "jacobbi". Supported: "jacobi", "ic" (aliases
"incomplete cholesky", "ildl", "incomplete ldlt"), "chebyshev", "amg"
(aliases "algebraic multigrid", "multigrid"), "none".Omitting the preconditioner section entirely, or writing type: none, means no preconditioning — that remains a valid, unremarkable choice.
Choosing a combination
Integrator and nonlinear solver
| Newton | Nonlinear CG | Steepest descent | |
|---|---|---|---|
| Quasi-static | recommended | ok | fallback |
| Newmark | recommended | ok | fallback |
| Central difference | n/a — explicit | n/a | n/a |
Newton, by device
direct | CG | CG + Jacobi | CG + IC | CG + Chebyshev | CG + AMG | L-BFGS | |
|---|---|---|---|---|---|---|---|
| CPU | best (small–medium) | weak | ok | good | ok | best (large) | dynamics only* |
| GPU | unavailable | weak | good | unavailable | ok | best | dynamics only* |
* L-BFGS is the fastest option for implicit dynamics on GPU, but fails on quasi-static on either device — see the warning above.
Practical guidance:
- CPU, small to medium —
direct. No tuning, always converges. - CPU, large —
cg+amg, orcg+icas a simpler alternative. - GPU, quasi-static —
cg+amg. It is the only GPU option that both converges its linear systems and beats the CPU direct solver at scale, and at 1.57M DOF it is the fastest option on either device. - GPU, implicit dynamics at small Δt —
cg+jacobi. The mass term conditions the system, so AMG's iteration reduction does not repay its per-application cost; seebenchmark_report.md. - Never run plain
cgwith no preconditioner on a real mesh. It is valid and very slow.
Combinations that fail or mislead
| Combination | Result |
|---|---|
GPU + direct | Hard error at startup. |
GPU + ic preconditioner | Requires an assembled matrix; unavailable. |
central difference + any solver block | Silently ignored; explicit has no nonlinear solve. |
NLCG / SD + linear solver | Ignored by design; these are matrix-free. |
Large Δt + j2 plasticity | Path-dependent; large steps miss the yield surface. Keep line search on and steps small. |