Skip to content

Fix NONLIN solver returning stale gamma on repeat solve! calls#228

Merged
1-Bart-1 merged 9 commits intomainfrom
fix-multi-solve-nonlin
May 2, 2026
Merged

Fix NONLIN solver returning stale gamma on repeat solve! calls#228
1-Bart-1 merged 9 commits intomainfrom
fix-multi-solve-nonlin

Conversation

@1-Bart-1
Copy link
Copy Markdown
Member

@1-Bart-1 1-Bart-1 commented Apr 30, 2026

Summary

The NONLIN solver path returned stale gamma on every solve! call after the first. The cached NewtonRaphson SciML cache was reused without reinit!, and NonlinearSolve short-circuits caches that already terminated with ReturnCode.Success, so the second call onward did no work — even when geometry, AIC, and inflow had all changed. Effectively only the first solve on a given solver was real.

What changed

  • Replaced the SciML cache with a hand-rolled Newton. SciML reinit! cascades through inner LinearSolve/Jacobian caches and allocates ~250 k per call, breaking the bench allocation budget. The hand-rolled loop uses pre-allocated buffers and LinearAlgebra.LAPACK.getrf! / getrs! for the LU step, all in-place. Re-solves correctly on every call, no allocations in the hot path.
  • Convergence check rewritten to SciML-style on the Newton increment: ‖Δγ‖_∞ < atol + rtol · max(‖γ‖_∞, tol_reference_error). rtol now has the same meaning as in the LOOP solver.
  • linearize returns a third value converged::Bool that is true iff every internal solve reached the tolerance; emits a warning when not. Existing callers using jac, res = linearize(...) keep working (Julia destructures the head of the tuple).
  • linearize gained fd_absstep / fd_relstep kwargs for the inner FiniteDiff Jacobian step.

Tests

  • test/solver/test_solver.jl — regression test that runs two NONLIN solves with different va and asserts gamma actually changes (was bit-identical pre-fix).
  • test/body_aerodynamics/test_results.jl — replaced the old per-input error_ratio < 0.002 checks with a two-scale Jacobian validation: at scale=1 the predicted change must match the observed change to FD precision (sanity), at scale=2 it must match within an O(h²)-dominated tolerance (verifies the Jacobian reflects true sensitivity, not noise). Also asserts lin_converged.
  • test/polars/test_polars.jl — populated with an XFoil polar generation test (small range, ~3 s) so that path is still covered now that the linearize test no longer regenerates polars.
  • The cached ram_air_kite polar CSVs are committed as test fixtures (42 KB total) and removed from .gitignore. Without them, CI was generating tiny 3×3 polars from alpha_range=deg2rad.(-1:1) that did not cover the actual flow regime, causing Newton to fail.

Test plan

  • julia --project=. -e 'using Pkg; Pkg.test()' — full suite green
  • Bench allocation budget respected (no 250 k regression from SciML reinit!)

🤖 Generated with Claude Code

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 30, 2026

Codecov Report

❌ Patch coverage is 98.07692% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/solver.jl 98.07% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a correctness issue where the NONLIN solver could return a stale circulation (gamma) on subsequent solve! calls, and adds regression coverage for repeated solves on the same Solver instance.

Changes:

  • Adds a regression test ensuring two consecutive NONLIN solve! calls with different inflow (va) produce different gamma.
  • Reworks the NONLIN path in gamma_loop! to use an in-house Newton iteration with finite-difference Jacobian + in-place LU solve.
  • Adjusts nonlinear-vs-linear test tolerances and marks several linearization accuracy checks as @test_broken.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
src/solver.jl Replaces the previous SciML-based NONLIN implementation with a custom Newton + LU-based solver loop.
test/solver/test_solver.jl Adds regression coverage for repeat NONLIN solves on the same solver instance.
test/body_aerodynamics/test_results.jl Loosens tolerances and marks some linearization validation assertions as broken.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/solver.jl
Comment thread src/solver.jl
Comment thread src/solver.jl Outdated
Comment thread src/solver.jl Outdated
Comment thread test/body_aerodynamics/test_results.jl Outdated
Comment thread test/body_aerodynamics/test_results.jl Outdated
@1-Bart-1 1-Bart-1 requested a review from ufechner7 May 1, 2026 22:11
@1-Bart-1 1-Bart-1 merged commit 161dec6 into main May 2, 2026
9 checks passed
@1-Bart-1 1-Bart-1 deleted the fix-multi-solve-nonlin branch May 2, 2026 08:30
@1-Bart-1 1-Bart-1 mentioned this pull request May 2, 2026
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.

3 participants