Skip to content

5. Nonlinear solution

5. Nonlinear solution

Primary strategy: monolithic damped Newton. The author reports efficient convergence in COMSOL for this exact system using PARDISO direct + Newton with a lower-bounded variable damping factor. That is direct empirical evidence and outranks the general literature warning. Confirmed from the model report: Fully Coupled, PARDISO (pivoting perturbation 1e-13), initial damping 0.2, minimum damping 0.01, recovery damping 0.2, max 100 iterations, relative tolerance 1e-6, sparsity pattern reused. Continuation on V_bias. → knowledge/09-comsol-reference-settings/03-solver-3-4.md.

Implement: 1. Damped Newton, damping factor adapted on residual reduction, with a lower bound (COMSOL's scheme: if the damped step still fails, accept the minimum damping rather than aborting). 2. Cap ‖δφ‖_∞ ≤ V_T per step. 3. Monitor min_i c_i every Newton step; fail loudly on negativity with the location. 4. Assert the packing fraction Σ_j N_A a_j³ c_j < 1 every step — β_i is singular at 1.

Fallbacks, in order: - Pseudo-transient continuation (Kelley & Keyes, SIAM J. Numer. Anal. 35, 508, 1998): add M/Δt to the Poisson and NP diagonal blocks, grow Δt by SER. Trivial to add to a steady code and the most robust safety net. - Hybrid segregated (Mitscha-Baude et al., J. Comput. Phys. 338, 452, 2017): Newton on the PNP block, fixed point for Stokes, with a corrected Poisson step — add the linearised-PB screening term χ_F(2q²c₀/kT)φ^{k+1} to the LHS, evaluated at φ^k on the RHS. This is the semiconductor Gummel map. They report both segregated schemes clearly outperform monolithic Newton at high surface charge with constant properties. - PB initial guess. Initialise from the Poisson–Boltzmann solution rather than φ=0, u=0, c_i=c₀. Specifically cures the high-surface-charge failure mode. Note: because ePNP-NS violates the Einstein relation (see knowledge/02-electrokinetics-background/index.md), PB is an approximate initialiser here, not the exact zero-bias limit.

The convergence criterion must not be the residual alone. A relative test measured against the residual on entry — ‖r‖ ≤ 1e-6 ‖r₀‖ — has a pathology that only shows up once continuation is wired in: re-solving an already-converged state demands another six orders of magnitude from a residual that is already at its floor, so a warm start onto its own solution either burns the iteration cap or fails outright. That is precisely the operation every rung of the ladder performs. [tested: tests/tier1/test_newton.py::test_num16_a_warm_start_onto_its_own_solution_is_idempotent]

COMSOL's own "relative tolerance" is on the solution update, not on the residual. A relative residual test ‖r‖ ≤ 1e-6 ‖r₀‖ hides under-converged fields: one norm over all fields is dominated by the largest, and on the seven gated walks it closed every rung but one, leaving example 03's flow 8.2 × 10⁻⁶ and pressure 2.0 × 10⁻⁶ relatively unconverged with the ions at 2 × 10⁻⁸ and the global relative update at 9 × 10⁻⁹ (WP41 D4, REV-08). The relative residual test closes nothing. Testing the absolute residual floor ‖r‖ ≤ 1e-12, or the per-field relative Newton update max_f ‖δu_f‖ / max(‖u_f‖, 10⁻⁶ · max(‖u‖, 1)) ≤ rtol, restores idempotence and tests every field to the tolerance [tested: tests/tier1/test_newton.py::test_ver44_every_step_records_the_undamped_relative_update]. Use the undamped Newton direction in that test, not the damped step: a heavily damped step is small for reasons that have nothing to do with convergence, and testing it would report success in the middle of a difficult ramp. Guard it further by never accepting convergence on a step that failed to reduce the residual.

The floor of 1 on ‖u‖ is not arbitrary: NUM-09 leaves every field O(1), so a state near zero is genuinely small rather than merely badly scaled; and the ratio 10⁻⁶ tests fields whose root is zero (e.g. potential at zero bias, velocity in an uncharged pore) against a fraction of the state norm.

5.1 Continuation ladder

linear PB → nonlinear PB → equilibrium PNP (V=0, u=0)
  → ramp ρ_pore 0 → target
  → ramp V 0 → ±200 mV (≈10 mV steps near onset)
  → enable Stokes coupling
  → enable ⟨c⟩- and d-dependent D, μ, ε, ϱ    ← last, deliberately
  → enable steric flux β_i                     ← last of the last
  → sweep salt 0.05 → 3 M

Warm-start each rung from the previous. Adapt the mesh between rungs, never within them. Enabling corrections last isolates their contribution to any convergence failure.

A warm start across a changed field set is not free. Three transitions change it — PB to equilibrium PNP adds the concentrations, the Stokes rung adds u and p, and any change of element order — and a solver that warm-starts by reusing the previous space cannot cross them. The target space has to be built, cold-started so the new fields begin somewhere the positivity gate accepts, and the shared fields interpolated by name. Where the field sets match, copy the vector rather than interpolating: interpolation of a function already in the space is the identity only to round-off, and most rungs of the ladder are same-space.

Re-solving a converged rung costs one Newton iteration, not zero. [tested: tests/tier2/test_ladder.py::test_num18_re_solving_a_converged_rung_costs_one_iteration_and_moves_nothing] The update-based criterion cannot be evaluated without assembling the Jacobian and solving once; the entry-side residual test is measured relative to the entry residual, so a converged warm start never passes it. The state then moves by ~5 × 10⁻⁹ relative. This is the property to test a cross-space transfer with: a transfer that scrambles a component needs many iterations to recover.

Changing c₀ between rungs is a vector copy, not an interpolation. [tested: tests/tier1/test_knowledge_arithmetic.py::test_concentration_rung_transfer_copies_vector_entries] It changes the whole NUM-09 scale set — S, Pe, every nondimensional coefficient — but not the field set, and the previous solution stays a good guess because the nondimensionalisation leaves every field O(1) in the new scaling too: c̃_i = 1 is bulk by the definition of c₀, and φ̃ is in V_T, which does not move with the salt. Note also that the charge scales ε V_T/a² and ε V_T/a carry no c₀ at all, so a fixed or surface charge ramped at one concentration keeps its dimensionless value at another.

The wall grading decides whether the charge ramp converges, not only how accurate it is. [tested: tests/tier2/test_envelope.py::test_num18_the_ablation_ladder_reaches_the_same_corner_without_the_corrections] [tested: tests/tier2/test_envelope.py::test_82_criterion_two_the_hard_corner_from_cold_costs_what_it_costs] The ladder raises σ_s while the model is still classical, and the field that fails first is the co-ion, not the counter-ion: against a negative wall the anion is depleted as exp(−|φ̃|), so a Newton step in the primitive variable of the concentration overshoots a small positive number straight through zero. At λ_D(3 M)/5 = 0.035 nm the ramp climbs −0.05 C/m²; at 0.09 nm the positivity gate aborts on the first sub-step with c_Cl⁻ = −0.76 at r = 2 nm, on the wall. Both are correct: the coarse mesh does not resolve the layer, the step is too long, and the gate stops a negative concentration becoming a plausible wrong current. So a reduced wall mesh costs robustness at the charged end and not only accuracy; the log branch is the structural cure, and a strongly charged pore also wants the Poisson–Boltzmann initial guess the fallback list names.

The whole 0.05-3 M x +/-200 mV envelope, measured. [tested: tests/tier2/test_envelope.py::test_82_criterion_two_the_whole_envelope_converges_through_the_ladder] Thirty operating points on a 2 nm x 13 nm pore at -0.02 C/m^2, every correction active against a real distance field, reached by climbing the ladder once to the easiest corner and warm-starting every other point from a converged neighbour: 13 rungs and 51 iterations for the climb, then 205 iterations over 621 s for the grid, no gate violation and no rung below 0.1 damping. Conductance and selectivity against salt, at +50 mV:

salt G (S) t+ bulk t+ excess
0.05 M 4.80e-10 0.870 0.388 0.482
0.15 M 1.06e-9 0.662 0.383 0.280
0.50 M 3.11e-9 0.486 0.373 0.113
1.0 M 5.77e-9 0.432 0.366 0.066
3.0 M 1.33e-8 0.384 0.356 0.028

Two things worth carrying. The bulk transport number is not a constant: the two ions carry different mobility corrections, so t+ of the unconfined electrolyte drifts from 0.388 at 0.05 M to 0.356 at 3 M, and a pore's selectivity has to be measured against the value at its own concentration or the wall gets credit for what the electrolyte did. And NaCl is anion-selective to begin with (D_Cl exceeds D_Na by 52 %), so "a negatively charged pore has t+ > 1/2" is false for any wall charge weak enough not to overturn that -- at -0.02 C/m^2 it holds only below about 0.3 M.

The correction set is worth a factor of two in conductance at 3 M. [tested: tests/tier2/test_envelope.py::test_num18_the_ablation_ladder_reaches_the_same_corner_without_the_corrections] [tested: tests/tier2/test_access_conductance.py::test_ver17_access_conductance_matches_maxwell_hall] ePNP-NS against classical PNP-NS at 3 M, +200 mV, -0.02 C/m^2, same mesh and same code path: G falls by a factor 0.452, which is the mobility correction (mu/mu0 = 0.465 for Na+, 0.552 for Cl-) showing through almost undiluted, and t+ moves 0.433 -> 0.384. The classical arm doubles as a Maxwell-Hall check at the top of the salt range: with the double layer a tenth of the pore radius it gives 2.9495e-8 S against the closed form's 2.95e-8 S.

A rung reports no Newton step for two unrelated reasons, and the silence is identical. [tested: tests/tier2/test_ladder.py::test_num18_every_nonlinear_rung_carries_a_convergence_record] Measured 21 September 2026 on the default ladder of an epnp-ns case at 0.1 M and 20 mV, twelve rungs on a cheap cylindrical pore. Two rungs (1-pb-linear, 2-pb) are not coupled models, so SolveStage._instrumented injects no damped-Newton callback into them at all — a callback is a keyword their solve would reject. Three more (3-equilibrium-pnp, 7-corrections, 8-steric) are coupled and still record iterations: 0: damped_newton found the transferred state's residual already at or below max(rtol * initial, atol) and returned before its first step. Only the remaining seven took steps, five each on the bias and salt rungs and four on the flow rung.

So five of twelve rungs keep no Newton history, and a consumer told only "no steps" cannot say which kind of rung it is looking at. Anything reporting the ladder has to carry that distinction explicitly — it is knowable only where the callback is injected — or it will describe a warm-started rung that converged on entry as one that is not a Newton solve.

Measured, on a charged 2 nm × 13 nm pore. [tested: tests/tier2/test_envelope.py::test_82_criterion_two_the_hard_corner_from_cold_costs_what_it_costs] The only rungs needing damping below the 0.2 initial value were the surface-charge ramp, at 0.08; every other rung of every ladder tried held at 0.2. A full climb to the hard corner — 3 M, ±200 mV, every correction active, wall graded to λ_D(3 M)/5, 66 000 DOF — is 22 rungs, 106 Newton iterations and 500 s. At λ_D(3 M)/2 (25 000 DOF) the same climb is 74 iterations and 134 s, so the reduction costs nothing in convergence.