LOOP converges on the relaxed step, so rtol is 33x looser than it reads - #306
Merged
1-Bart-1 merged 3 commits intoSep 12, 2026
Merged
Conversation
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0123e9orAr632QxtAMPULtSi
Contributor
Author
|
Local full suite: PASS (6 min, Julia 1.12.7, one cell of the matrix) |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
7 tasks
1-Bart-1
approved these changes
Sep 11, 2026
7 tasks
Member
|
Resolve conflicts |
…ges-on-the-relaxed-step-so-rt # Conflicts: # CHANGELOG.md
Contributor
Author
1-Bart-1
deleted the
agent/284-loop-converges-on-the-relaxed-step-so-rt
branch
September 12, 2026 11:12
This was referenced Sep 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR
LOOPtested convergence on the under-relaxed step rather than on the fixed-point residual, sortolwas silentlyrtol / relaxation_factor— 33x looser than it reads at the defaults. Dividing the measured step by the relaxation actually used makesrtolmean what it says, and makessolve_base!'s half-relaxation retry stop loosening the tolerance a second time.What was wrong
gamma_loop!'s LOOP branch formsgamma_new = (1 - relaxation_factor) * gamma + relaxation_factor * F(gamma)and then converged onmax|gamma_new - gamma| / ref < rtol. That difference isrelaxation_factor * (F(gamma) - gamma), so the loop stopped at a relative fixed-point residual ofrtol / relaxation_factor.I measured it by evaluating one unrelaxed step
F(g)from each converged answer ontest/solver/solver_test_wing.yaml(4 panels,alpha=5.0, beta=0.0, wind_speed=10.0,ELLIPTICstart). The ratio is flat across the sweep, as the algebra says it should be, andNONLIN— which already tested the Newton step against the residual — sits at 1e-12 or better everywhere.Peak circulation at 26.6° was
LOOP9.106263 againstNONLIN9.107758 —LOOP's own error, 16x its statedrtol. It is now 9.107713, 5e-6 relative fromNONLIN.What changed
One line: the measured step is divided by
relaxation_factorbefore it is compared tortol. The divisor is the relaxation of that call, notsolver.relaxation_factor, so the retry at half relaxation now converges to the same residual as the attempt that failed instead of to twice as loose an answer.Solver.rtol,SolverSettings.rtoland thegamma_loop!docstring say what the tolerance is measured on; the residual also stops being callederror, which shadowedBase.error.The regression test solves at 0° and at 26.6° past stall, then evaluates one unrelaxed step from each converged answer and asserts the residual is under
rtol— the quantity that was wrong, rather than a reference number that happens to move with it. Before the fix it reports0.000769 < 2.60e-5and0.002704 < 9.11e-5.What this costs, and what it moves
Every
LOOPsolve tightens by1 / relaxation_factorand takes about 1.4x the iterations — on the test wing 134/137/146/148/183 → 189/193/205/208/243 againstmax_iterations = 1500.The worst case in the repo is the shipped
rtol=1e-6, relaxation_factor=0.01settings, 100x tighter on the largest geometries. Ondata/TUDELFT_V3_KITE/vsm_settings.yaml(50 panels,max_iterations=5000) every solve still converges with room to spare, and CL moves in the fifth digit:V3 kite, before → after
No reference number in the suite needed re-fitting. The hard-coded coefficients in
test/body_aerodynamics/test_body_aerodynamics.jlstill hold at their tolerances: that solve already ran atrtol=1e-8, so its effective residual tolerance went from 3.3e-7 to 1e-8, while the tightest assertion on it is 1.5e-5 relative.test/solver/test_flow_curvature.jl:151compares twoLOOPsolves at the default≈and passes for the same reason — both sides moved together.Where I would push back
relaxation_factorno longer buys looser convergence, only more iterations, which is what it should always have meant — but anyone who had tuned a difficult case by lowering it was also buying an earlier stop, and will now pay for that in wall time. The V3 numbers above are the measurement of that cost on the largest shipped geometry.is_with_artificial_dampingaddsdampto the step, so with damping on the criterion now weights that term by1 / relaxation_factor. It stays zero exactly where the iteration stops moving, so convergence still means convergence; nothing in the suite exercises that path.Verification
max|F(g)-g| / max|g|= 2.96e-4 … 3.10e-4 againstrtol = 1e-5test/solver/test_solver.jlred before (0.000769 < 2.60e-5,0.002704 < 9.11e-5), green after (juliaserver, exit 0)test/solver/{test_flow_curvature,test_unrefined_dist,test_backend_comparison,test_forwarddiff}.jlandtest/body_aerodynamics/test_body_aerodynamics.jl: PASS872ee8c(agent ci-local, one matrix cell, Julia 1.12.7): 6336 pass, 0 fail, 0 error, 1 pre-existing@test_skip, 9m16s872ee8c: all nine checks green — Julia 1.11 and 1.12 across ubuntu, macOS and windows, both coverage runs, Documentation,Test end-user and developer setup, codecov/patch. That last one was the red check on the previous head, and it was write_section_aero writes an all-NaN deflected .dat, and read_section_ #300 rather than this branch; the fix for it landed onmainas Setup Test is failing #312 and this merge brings it in.mainat50b1454, merged as872ee8c:CHANGELOG.mdwas the only conflict — this branch'sLOOPentry against main'splot_slices_3done, both kept · REUSE lint: n/a, the repo carries no REUSE setup ·jetlsis not installed on this box, so static analysis was not runrelaxation_factorand amax_iterationssized to the old early stop will now hit the iteration cap and reportFAILUREwhere it used to report a looseFEASIBLE.Scope
+55 / -8 across 4 files: one line of solver logic, three docstrings, a
CHANGELOGentry and one testset.test/solver/test_solver.jlrepeats the settings →Wing→refine!→BodyAerodynamicssetup in four testsets now; a helper would have rewritten three testsets this change has no other reason to open, so it is left for acleanup:PR.Closes #284 · task
VortexStepMethod.jl-284