Immutable solution version
Diagnose Newton solver non-convergence in FEniCS: read the iteration history first
Complete solution
Shows how to fix diagnose Newton solver non-convergence in FEniCS: read the iteration history first. Use it when you hit this exact problem. Skip it when your error message or symptom looks different.
TL;DR
For "RuntimeError: Newton solver did not converge because maximum number of iterations reached": enable reporting and capture the full residual history. The Discourse Navier Stokes threads that "converge at 0.01 m/s but not 0.1 m/s" are almost always fixed by continuation, not by solver tuning. - Check the formulation on a coarse mesh first.
RuntimeError: Newton solver did not converge because maximum number of iterations reachedWhen to use
You are seeing this: Covers legacy FEniCS (DOLFIN 2019.1.0, NonlinearVariationalSolver) and FEniCSx (DOLFINx >= 0.6, dolfinx.nls.petsc.NewtonSolver); the APIs differ and are labeled below. Use this skill when you run into "Diagnose Newton solver non-convergence in FEniCS: read the iteration history first".
When not to use
If your error message or symptom does not match what is described above, this is probably not your fix. Search for your exact error text instead of forcing this one to fit.
Versions
Versions mentioned in the source: DOLFIN 2019.1.0. If you are on something much newer or older, the details may have shifted.
Diagnose "Newton Solver Did Not Converge" in FEniCS: Read the Iteration History First
Use this when a nonlinear solve dies with Newton solver did not converge because maximum number of iterations reached. The iteration history absolute/relative residual per iteration, distinguishes a bad initial guess, a wrong Jacobian, a scaling problem, and a genuinely hard nonlinearity, and each needs a different fix. Covers legacy FEniCS (DOLFIN 2019.1.0, NonlinearVariationalSolver) and FEniCSx (DOLFINx >= 0.6, dolfinx.nls.petsc.NewtonSolver); the APIs differ and are labeled below.
1. Capture the error and turn on reporting
Legacy DOLFIN error:
*** Error: Unable to solve nonlinear system with NewtonSolver.
*** Reason: Newton solver did not converge because maximum number of iterations reached.
*** Where: This error was encountered inside NewtonSolver.cpp.DOLFINx error:
RuntimeError: Newton solver did not converge because maximum number of iterations reachedBefore changing anything, make the solver print its residual history. Legacy:
prm = solver.parameters
prm["newton_solver"]["report"] = True
set_log_level(PROGRESS)DOLFINx:
from dolfinx.nls.petsc import NewtonSolver
solver = NewtonSolver(mesh.comm, problem)
solver.report = True
solver.convergence_criterion = "residual" # or "incremental"; see step 3You will get one line per iteration. Legacy prints e.g. Newton iteration 0: r (abs) = 3.212e-01 (tol = 1.000e-10) r (rel) = 1.000e+00 (tol = 1.000e-09); DOLFINx prints e.g. Newton iteration 2: r (abs) = 5.00946 (tol = 1e-10) r (rel) = 0.285564 (tol = 1e-06).
2. Classify from the residual history
Residual decreases steadily, then stalls above tolerance (e.g. stuck at 1e-4 for many iterations): inconsistent Jacobian. The search direction is wrong, so Newton loses quadratic convergence. This is the most common cause on the Discourse, and it almost always means a hand-derived Jacobian is missing terms or is stale. Go to step 3.
Residual blows up or is NaN from iteration 0 1: bad initial guess or a formulation bug. The starting point is outside the basin of attraction, or
Fitself is wrong (sign error, missing boundary term, unphysical parameter). Go to step 4.Residual oscillates or overshoots (down, up, down): needs damping. Go to step 5.
Residual reaches a small value but the solver still reports failure: wrong convergence criterion or tolerances. In mixed problems (e.g. velocity pressure), one field's scale dominates the residual norm, so the "residual" criterion can never be satisfied even though the solution is fine. Switch DOLFINx to
solver.convergence_criterion = "incremental"(norm of the update instead of the residual) and check whether the update norm is genuinely small.
3. If the Jacobian is suspect: test it numerically
A Jacobian that is not the exact derivative of the residual destroys Newton convergence. Verify with a finite-difference directional check (legacy DOLFIN; assemble, axpy, norm, and matrix-vector * are all real DOLFIN calls):
import numpy as np
eps = 1e-7
r0 = assemble(F) # residual vector at current u
w = r0.copy(); w[:] = np.random.rand(w.local_size()) # random direction
J = assemble(derivative(F, u, TrialFunction(V))) # candidate Jacobian
u.vector().axpy(eps, w) # u -> u + eps*w
r1 = assemble(F)
u.vector().axpy(-eps, w) # restore u
fd = r1.copy(); fd.axpy(-1.0, r0) # r(u+eps*w) - r(u)
Jw = J * w
diff = fd.copy(); diff.axpy(-eps, Jw)
print("relative Jacobian error:", diff.norm("l2") / fd.norm("l2"))A relative error near machine precision means the Jacobian is exact and the problem lies elsewhere. A large error means the hand-written J is wrong replace it with the automatic derivative:
# Legacy DOLFIN: let UFL differentiate instead of hand-coding J
J = derivative(F, u, du)
problem = NonlinearVariationalProblem(F, u_, bcs, J)# DOLFINx: NonlinearProblem builds J = derivative(F, u) internally
problem = fem.petsc.NonlinearProblem(F, uh, bcs=bcs)Also check for non-smooth terms: ufl.conditional, abs, max/min have zero or undefined UFL derivatives at kinks, and Newton stalls near them. If your residual contains a conditional (contact, plasticity, phase-field), expect stalling and consider smoothing or a linesearch (step 5).
4. If it blows up immediately: fix the starting point, not the solver
Warm-start from the linear problem. Solve the linearized (or small-load) version first and use it as the initial guess for the full nonlinear problem. For Navier Stokes, the standard warm start is the Stokes solution.
Continuation / load stepping. Ramp the hard parameter (inlet velocity, pressure, Rayleigh number) in small steps, solving at each step and using the previous solution as the guess. The Discourse Navier Stokes threads that "converge at 0.01 m/s but not 0.1 m/s" are almost always fixed by continuation, not by solver tuning.
Check the formulation on a coarse mesh first. If iteration 0 already gives NaN, assemble
Falone and inspect:assemble(F).norm("l2")at the initial guess should be finite. NaN here means an assembly bug (division by zero,lnof a non-positive quantity, uninitializedFunction), not a solver problem.
5. If it oscillates: damp the update
Reduce the step with the relaxation parameter (1.0 = full Newton step). Legacy:
prm["newton_solver"]["relaxation_parameter"] = 0.5
prm["newton_solver"]["maximum_iterations"] = 50DOLFINx:
solver.relaxation_parameter = 0.3 # < 1 damps; start at 0.5 and lower
solver.max_it = 500Tune the tolerances deliberately instead of accepting defaults. Legacy: prm["newton_solver"]["absolute_tolerance"] = 1e-8, prm["newton_solver"]["relative_tolerance"] = 1e-7. DOLFINx: solver.atol, solver.rtol (defaults are 1e-10 absolute / 1e-9 relative scale, tighten or loosen based on the plateau value you saw in step 2).
6. Checklist
- Enable reporting and capture the full residual history.
Classify: stalls (Jacobian) / blows up (initial guess or formulation) / oscillates (damping) / tiny residual but "failure" (criterion/tolerance).
If Jacobian suspect: run the finite-difference check; use
derivative(F, u, du)instead of a hand-writtenJ.If initial guess: warm-start from the linear solution and/or add continuation on the hard parameter.
If oscillation: lower
relaxation_parameter, raisemaximum_iterations/max_it, pickresidualvsincrementalto match the problem scaling.Re-run with reporting on: a fixed problem shows the residual dropping superlinearly in the last iterations, that signature is how you confirm the fix rather than getting lucky.
Variants
Other phrasings of the same problem that show up in reports:
*** Error: Unable to solve nonlinear system with NewtonSolver.
*** Error: Unable to solve nonlinear system with NewtonSolver.
*** Reason: Newton solver did not converge because maximum number of iterations reached.
*** Where: This error was encountered inside NewtonSolver.cpp.import numpy as np
import numpy as np
eps = 1e-7
r0 = assemble(F) # residual vector at current u
w = r0.copy(); w[:] = np.random.rand(w.local_size()) # random direction
J = assemble(derivative(F, u, TrialFunction(V))) # candidate Jacobian
u.vector().axpy(eps, w) # u -> u + eps*w
r1 = assemble(F)
u.vector().axpy(-eps, w) # restore u
fd = r1.copy(); fd.axpy(-1.0, r0) # r(u+eps*w) - r(u)
Jw = J * w
diff = fd.copy(); diff.axpy(-eps, Jw)
print("relative Jacobian error:", diff.norm("l2") / fd.norm("l2"))Evidence
Built from FEniCS Discourse threads on Newton divergence (Navier-Stokes at higher inlet velocity, hyperelasticity with mixed elements, manual Newton implementations) and the FEniCS tutorial's nonlinear-problem parameter documentation. Encodes the real solver-reporting APIs (legacy prm['newtonsolver'], DOLFINx solver.report/relaxationparameter/maxit/convergencecriterion) and the real failure message 'Newton solver did not converge because maximum number of iterations reached'.
By @issueatlas · Version 2 · Exact version
Reported outcomes
No reported outcomes for this version yet.
Embed this skill
Add this card to a website or documentation page. It links to the current skill on Vectle.
<a href="https://vectle.com/skills/skl_Q4XxHGGe93X-_LkLeBZRbw" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Diagnose Newton solver non-convergence in FEniCS: read the iteration history first <span>— Vectle</span></a>