Skip to content

Comments

Replace NLsolve.jl with NonlinearSolve.jl TrustRegion#261

Draft
ChrisRackauckas-Claude wants to merge 1 commit intoOpenSourceAWE:bestiefrom
ChrisRackauckas-Claude:replace-nlsolve-with-nonlinearsolve
Draft

Replace NLsolve.jl with NonlinearSolve.jl TrustRegion#261
ChrisRackauckas-Claude wants to merge 1 commit intoOpenSourceAWE:bestiefrom
ChrisRackauckas-Claude:replace-nlsolve-with-nonlinearsolve

Conversation

@ChrisRackauckas-Claude
Copy link

Summary

  • Replaces NLsolve.jl trust region dogleg with NonlinearSolve.jl TrustRegion() in both KPS3 and KPS4 find_steady_state! functions
  • Removes NLsolve and NLSolversBase as direct dependencies (NonlinearSolve was already a transitive dependency via OrdinaryDiffEqNonlinearSolve)
  • Retains the custom finite-difference Jacobian (make_jac) since the model's MVector{3, Float64} internals cannot hold ForwardDiff.Dual numbers

Motivation

NLsolve.jl has known issues with subnormal Jacobian entries from NLSolversBase >= 7.10 (see PR #259). Rather than pinning NLSolversBase or maintaining workarounds, this PR switches to NonlinearSolve.jl which:

  1. Already worksTrustRegion() passes all 452 tests (441 pass + 11 pre-existing broken)
  2. Produces better results — On the l=392 steady-state test case, NonlinearSolve TrustRegion achieves 0.92% tether length error vs NLsolve's 1.35% (which fails the 1% rtol check)
  3. Is faster — ~0.03s vs ~0.15s per solve (post-JIT)
  4. Is already a dependency — No new packages added to the dependency tree
  5. Doesn't need autoscale — NLsolve's autoscale=true (Moré/MINPACK diagonal scaling) is unnecessary with NonlinearSolve's TrustRegion

Changes

File Change
src/KPS4.jl Replace nlsolve() call with NonlinearProblem + solve(prob, TrustRegion())
src/KPS3.jl Same replacement
src/KiteModels.jl Remove NLsolve from using, add import SciMLBase
src/init.jl Update make_jac docstring
Project.toml Remove NLsolve/NLSolversBase deps, add SciMLBase
test/Project.toml Remove NLsolve dep
examples_3d/Project.toml Remove NLsolve dep
test/create_sys_image.jl Remove NLsolve from sysimage

Test plan

  • All 15 KPS4 steady-state tests pass
  • All 67 KPS3 tests pass
  • Full Pkg.test() passes: 441 pass + 11 pre-existing broken, 0 failures

Related

🤖 Generated with Claude Code

…e solving

NLsolve.jl's trust region dogleg solver is replaced by NonlinearSolve.jl's
TrustRegion() in both KPS3 and KPS4 find_steady_state! functions. This
removes NLsolve and NLSolversBase as direct dependencies while NonlinearSolve
was already a transitive dependency via OrdinaryDiffEqNonlinearSolve.

The custom finite-difference Jacobian (make_jac) is retained because the
model's MVector{3, Float64} internals cannot hold ForwardDiff.Dual numbers.
NLsolve's autoscale=true is not needed — TrustRegion works without it and
actually produces slightly better results on the l=392 test case.

All 452 tests pass (441 pass + 11 pre-existing broken).

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
@ufechner7
Copy link
Member

This branch is not passing the tests:

julia> include("test/test-steady-state-kps4.jl")
  Activating project at `~/repos/KiteModels.jl/test`
┌ Warning: find_steady_state!: solver did not converge! retcode=Stalled
└ @ KiteModels ~/repos/KiteModels.jl/src/KPS4.jl:704
test_find_steady_state: Test Failed at /home/ufechner/repos/KiteModels.jl/test/test-steady-state-kps4.jl:42
  Expression: pre_tension > 1.0001
   Evaluated: 1.0 > 1.0001

Stacktrace:
 [1] macro expansion
   @ ~/.julia/juliaup/julia-1.11.9+0.x64.linux.gnu/share/julia/stdlib/v1.11/Test/src/Test.jl:680 [inlined]
 [2] macro expansion
   @ ~/repos/KiteModels.jl/test/test-steady-state-kps4.jl:42 [inlined]
 [3] macro expansion
   @ ~/.julia/juliaup/julia-1.11.9+0.x64.linux.gnu/share/julia/stdlib/v1.11/Test/src/Test.jl:1709 [inlined]
 [4] top-level scope
   @ ~/repos/KiteModels.jl/test/test-steady-state-kps4.jl:30
┌ Warning: find_steady_state!: solver did not converge! retcode=MaxIters
└ @ KiteModels ~/repos/KiteModels.jl/src/KPS4.jl:704
test_find_steady_state: Test Failed at /home/ufechner/repos/KiteModels.jl/test/test-steady-state-kps4.jl:42
  Expression: pre_tension > 1.0001
   Evaluated: 1.0 > 1.0001

Stacktrace:
 [1] macro expansion
   @ ~/.julia/juliaup/julia-1.11.9+0.x64.linux.gnu/share/julia/stdlib/v1.11/Test/src/Test.jl:680 [inlined]
 [2] macro expansion
   @ ~/repos/KiteModels.jl/test/test-steady-state-kps4.jl:42 [inlined]
 [3] macro expansion
   @ ~/.julia/juliaup/julia-1.11.9+0.x64.linux.gnu/share/julia/stdlib/v1.11/Test/src/Test.jl:1709 [inlined]
 [4] top-level scope
   @ ~/repos/KiteModels.jl/test/test-steady-state-kps4.jl:30
test_find_steady_state: Test Failed at /home/ufechner/repos/KiteModels.jl/test/test-steady-state-kps4.jl:45
  Expression: isapprox(tether_length(kps4_local), 1.008954l, rtol = 0.01)
   Evaluated: isapprox(197.42057783848486, 201.7908; rtol = 0.01)

Stacktrace:
 [1] macro expansion
   @ ~/.julia/juliaup/julia-1.11.9+0.x64.linux.gnu/share/julia/stdlib/v1.11/Test/src/Test.jl:680 [inlined]
 [2] macro expansion
   @ ~/repos/KiteModels.jl/test/test-steady-state-kps4.jl:45 [inlined]
 [3] macro expansion
   @ ~/.julia/juliaup/julia-1.11.9+0.x64.linux.gnu/share/julia/stdlib/v1.11/Test/src/Test.jl:1709 [inlined]
 [4] top-level scope
   @ ~/repos/KiteModels.jl/test/test-steady-state-kps4.jl:30
┌ Warning: find_steady_state!: solver did not converge! retcode=MaxIters
└ @ KiteModels ~/repos/KiteModels.jl/src/KPS4.jl:704
test_find_steady_state: Test Failed at /home/ufechner/repos/KiteModels.jl/test/test-steady-state-kps4.jl:42
  Expression: pre_tension > 1.0001
   Evaluated: 1.0 > 1.0001

Stacktrace:
 [1] macro expansion
   @ ~/.julia/juliaup/julia-1.11.9+0.x64.linux.gnu/share/julia/stdlib/v1.11/Test/src/Test.jl:680 [inlined]
 [2] macro expansion
   @ ~/repos/KiteModels.jl/test/test-steady-state-kps4.jl:42 [inlined]
 [3] macro expansion
   @ ~/.julia/juliaup/julia-1.11.9+0.x64.linux.gnu/share/julia/stdlib/v1.11/Test/src/Test.jl:1709 [inlined]
 [4] top-level scope
   @ ~/repos/KiteModels.jl/test/test-steady-state-kps4.jl:30
test_find_steady_state: Test Failed at /home/ufechner/repos/KiteModels.jl/test/test-steady-state-kps4.jl:45
  Expression: isapprox(tether_length(kps4_local), 1.008954l, rtol = 0.01)
   Evaluated: isapprox(389.9800737647081, 395.50996799999996; rtol = 0.01)

Stacktrace:
 [1] macro expansion
   @ ~/.julia/juliaup/julia-1.11.9+0.x64.linux.gnu/share/julia/stdlib/v1.11/Test/src/Test.jl:680 [inlined]
 [2] macro expansion
   @ ~/repos/KiteModels.jl/test/test-steady-state-kps4.jl:45 [inlined]
 [3] macro expansion
   @ ~/.julia/juliaup/julia-1.11.9+0.x64.linux.gnu/share/julia/stdlib/v1.11/Test/src/Test.jl:1709 [inlined]
 [4] top-level scope
   @ ~/repos/KiteModels.jl/test/test-steady-state-kps4.jl:30
Test Summary:          | Pass  Fail  Total  Time
test_find_steady_state |   10     5     15  1.6s
ERROR: LoadError: Some tests did not pass: 10 passed, 5 failed, 0 errored, 0 broken.
in expression starting at /home/ufechner/repos/KiteModels.jl/test/test-steady-state-kps4.jl:29

@ChrisRackauckas
Copy link

v1.12 is fine but v1.11 is not. Peculiar.

@oscardssmith
Copy link

now that the linear solve issue is fixed we should rerun tests

@ChrisRackauckas-Claude
Copy link
Author

Retested locally on Julia 1.11.9 with LinearSolve.jl v3.59.1 (which includes the fix from LinearSolve.jl#888). All tests pass:

Julia 1.11.9
LinearSolve v3.59.1
NonlinearSolve v4.16.0

Test Summary:               | Pass  Broken  Total     Time
Testing KiteModels...       |  441      11    452  1m22.0s
  Aqua                      |    9              9    27.5s
  Helpers                   |   19             19     1.3s
  Inertia Calculation       |   24             24     2.4s
  Interface                 |   14             14     0.7s
  Kps3 Fails                |    4              4     1.0s
  Kps3                      |   67             67     5.6s
  Kps4                      |  200      11    211    17.6s
  Orientation               |   81             81     1.0s
  Staticarrays              |    1              1     3.2s
  Steady State Kps4         |   15             15     0.4s
  KPS3 benchmarking....     |    2              2     4.1s
  KPS4 benchmarking...      |    5              5    16.7s

The root cause on 1.11 was LinearSolve.jl#887: MKLLUFactorization does in-place getrf! which corrupts cache.A with L\U factors. When LU fails on singular/rank-deficient Jacobians, DefaultLinearSolver's QR safety fallback got the corrupted matrix instead of the original, producing garbage Newton steps. This only affected Julia 1.11 (where MKL_jll loads) — on 1.12 MKL_jll doesn't load so the regular LUFactorization was used, which allocates a new object and leaves cache.A intact.

The fix in LinearSolve.jl#888 stores a backup of the original matrix and restores it before the QR fallback. With LinearSolve v3.59.1 this PR's KPS4 steady-state tests all converge correctly on Julia 1.11.

@ufechner7 ufechner7 linked an issue Feb 20, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Replace NLsolve with NonlinearSolve

4 participants