next_step! should gain a kwarg that makes it warn on failing vsm solve - #291
next_step! should gain a kwarg that makes it warn on failing vsm solve#2911-Bort-1 wants to merge 9 commits into
Conversation
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1-Bort-1
left a comment
There was a problem hiding this comment.
Independent review (advisory)
Verdict: APPROVE WITH COMMENTS · 2 inline, 0 off the diff
Good
- safe_vsm_solve! copies gamma before the solve and copyto!-restores it on failure (lines 1550-1568), because solve! clobbers gamma_distribution -- matches the PR's core claim exactly.
- VSMSolveFailure replaces AssertionError only at the VSM-convergence throw sites; the non-finite point-force/traction assertions stay AssertionError (direct.jl:120, pressure.jl:465), honoring the Decisions.
- warn_or_rethrow is a single shared codepath for both the rigid and particle loops and rethrows non-VSM failures, so unrelated exceptions still crash (common.jl:953-957).
- The test asserts solver.lr.gamma_new != gamma (line 111), proving the failed solve actually moved the iterate rather than masking a silent no-op.
- Reuses system_name="wing_test_REFINE" (line 84) so the model cache is shared with test_wing.jl, per rubric §8.
Not good
docs/src/vsm_coupling.md:258— "throws ... before writing any frozen state" is false for AeroLinearized: rigid_aero_baseline! writes wing.aero_x and ForwardDiff leaves partial aero_jac before the solve can fail, so the doc overstates the guarantee the card's Risk section admits.test/test_vsm_solve_failure.jl:9— The "forced through the solver rather than through a pose" rationale is design history that belongs in the PR description, not a comment block in code.- The try/catch+warn_or_rethrow block is duplicated verbatim for the rigid (918-923) and particle (935-940) loops; it could be one hoisted helper.
- The test-file header (lines 4-11) restates rationale that already lives in the PR Decisions (force non-convergence via rtol=0), against §3/§5.
- The unconditional copy per solve (1550-1551) allocates even on converging solves; disclosed in the card but not obviously avoidable since it must precede solve!.
opencode, rubric CLEAN_CODE.md. A different lab from the implementer
on purpose: a reviewer sharing its blind spots would not flag its mistakes.
| # keeps the circulation and the point forces of the last converged solve, holds | ||
| # the `vsm_interval` schedule, and solves again as soon as the solve recovers. | ||
| # | ||
| # Non-convergence is forced through the solver rather than through a pose: the |
There was a problem hiding this comment.
MINOR: The "forced through the solver rather than through a pose" rationale is design history that belongs in the PR description, not a comment block in code.
There was a problem hiding this comment.
Fixed. The claim now names what is actually restored — the circulation and the two angle-of-attack distributions — and docs/src/vsm_coupling.md states the AeroLinearized exception once: a solve that fails during the Jacobian pass keeps the baseline coefficients of the operating point it just refreshed, next to the Jacobian columns the pass had not reached. refresh_aero!, warn_or_rethrow and the next_step! docstring lost the absolute wording. What is true for every mode, and still stated, is that the forces stay those of the last converged solve: apply_direct_forces! runs after the Jacobian, so a failure there never rewrites them.
There was a problem hiding this comment.
@1-Bort-1 can't this just be added to an existing test? This is a lot of lines of code for just one kwarg to be tested.
There was a problem hiding this comment.
Folded in, file deleted. The contract is now driver (C) failed VSM solve inside the per-case loop of test_aero_modes.jl, gated on case.reference == :vsm: ~30 lines, no model of its own, and it runs on all five VSM cases (direct particle, direct rigid, continuous, pressure, linearized rigid) rather than the one the standalone file built — 809 assertions where the file had 779. Net 105 lines lighter. It also dropped the using Logging that had made the full suite red under Pkg.test; the counting now goes through Test.collect_test_logs.
|
@1-Bort-1 rebase this onto feat/live-polars and stack the PR |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…-next-step-should-gain-a-kwarg-that-makes # Conflicts: # CHANGELOG.md # src/aero_modes/common.jl
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AZ3Yt7fYWpdyC47NVg15EG
…-next-step-should-gain-a-kwarg-that-makes
…-next-step-should-gain-a-kwarg-that-makes # Conflicts: # CHANGELOG.md
AeroPressure: polars regenerated from the deformed shape, not a flap angle
|
merge main into here, fix conflicts @1-Bort-1 |
…hould-gain-a-kwarg-that-makes Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R1wuyncuDuZa7n7EegaqGP
| [`next_step!`](@ref)) downgrades it to a warning; the assertions on a corrupted | ||
| frozen state are `AssertionError` and stay fatal. | ||
| """ | ||
| struct VSMSolveFailure <: Exception |
There was a problem hiding this comment.
This error should exist and be thrown in vsm, not in symbolicawemodels
There was a problem hiding this comment.
Moved: OpenSourceAWE/VortexStepMethod.jl@544badd defines SolveFailure and throws it from solve!(...; throw_on_fail=true), and takes finite_full with it; this branch only catches now (50734e52). The throw is opt-in rather than the default because NONLIN solve! re-runs across calls and solve! artificial viscosity: attached no-op, post-stall finite both solve non-converging post-stall cases and assert on the result — say the word and I will flip it and move those two.
`VSMSolveFailure` and the Dual-aware `finite_full` move into VortexStepMethod, which now raises SolveFailure from solve! under throw_on_fail. safe_vsm_solve! asks for that, restores the last converged circulation and both angle-of-attack distributions, and rethrows; the three call sites that re-checked the solver's flag and threw are plain calls now, and warn_or_rethrow names the mode and the wing the message no longer can. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
CI: https://github.com/OpenSourceAWE/SymbolicAWEModels.jl/actions/runs/34338389787/job/102423090619 |
|
This check fails on the dependency chain, not the code. CI installs the registered VortexStepMethod 4.3.1 (this branch's [compat] is still "4.3"), which has neither |
|
Local full suite: FAIL (14 min, Julia 1.12.7, one cell of the matrix) |
TL;DR
next_step!(sam; vsm_warn_on_fail=true)turns a failed VSM solve into a warning and flies the wing on with the circulation, the angles of attack and the frozen forces of its last converged solve, instead of ending the run. The failure itself is nowVortexStepMethod.SolveFailure, raised by VSM's ownsolve!— this round moved it there, per the review comment oncommon.jl.What a failed solve used to cost
safe_vsm_solve!zeroed the circulation on a failure and every mode's refresh threw anAssertionError, so one bad step ended the simulation and the next solve started from zero rather than from the last good circulation. Both halves are fixed. The last converged circulation and both angle-of-attack distributions are put back before the failure propagates —solve!has already overwritten them with the diverged ones by the time anyone can look — andvsm_warn_on_faildowngrades the failure to a warning.vsm_intervalis untouched, so the next scheduled update solves again; the assertions on an already corrupted frozen state stay fatal, because a wing whose point forces are NaN cannot be flown on.AeroLinearizedis the one place where "nothing frozen is written before the solve can fail" is not true, anddocs/src/vsm_coupling.mdsays so: a solve that fails during the Jacobian pass keeps the baseline coefficients of the operating point it just refreshed, next to the Jacobian columns the pass had not reached.Where the error lives now
Review said the error should exist and be thrown in VortexStepMethod, not here. It does: OpenSourceAWE/VortexStepMethod.jl#282 adds
SolveFailureandsolve!(...; throw_on_fail=true), and takes this package's Dual-awarefinite_fullwith it — whether a solve came back usable is VSM's question, not a caller's. This branch deletesVSMSolveFailureandfinite_full, the threeif !safe_vsm_solve!(...) throw(...)blocks collapse to plain calls,safe_vsm_solve!catches, restores and rethrows, andwarn_or_rethrownames the mode and the wing that VSM's own message cannot.src/aero_modes/common.jlgets shorter by doing it.The throw is opt-in over there, not the default: two VSM testsets solve deliberately non-converging post-stall cases and assert on the result, one of them explicitly wanting a finite answer rather than a converged one. The reasoning is on that PR, and a NONLIN non-convergence it turned up is OpenSourceAWE/VortexStepMethod.jl#283.
This PR cannot merge before that one is released. SymbolicAWEModels pins
VortexStepMethod = "4.3"and its tracked manifest 4.3.1, while VSM main is already 5.0.0, so the compat entry andManifest-v*.defaultboth have to move onto the release that carriesSolveFailure— a dependency bump of its own (AGENTS §7), not this diff.Project.tomlis untouched here; it was relaxed only locally, to resolve against the dev'd worktree, and restored before the commit. Until that release exists, CI on this PR fails atthrow_on_fail.The one file the local suite is run without
test/test_helpers.jlasserts that both trackedManifest-v1.1x.toml.defaultare no older thanProject.toml, and it compares mtimes.Project.tomlhere is byte-identical tomain— its last commit iscb482f1e, on the base — butPkgrewrote it when the VortexStepMethod worktree wasdeved for this round, so it now sits 18 hours ahead of the manifests:mtime(Manifest-v1.12.toml.default) = 1.7888818752933135e9againstproject_mtime = 1.788948339274766e9. Agit archiveof the three files into an empty directory satisfies both assertions, which is what a CI checkout produces — and the CI cells that ran green on this branch in round 4 included that file. That is a false positive on any worktree with adeved dependency, which the cross-repo workflow requires; filed as #305 rather than fixed here.Verification
test/solver/test_solver.jlred before (UndefVarError: SolveFailure not defined in Main), green after — 27 assertions; full suitetest/runtests.jlPASS; docs build cleantest/test_aero_modes.jl809/809 (2m00.4s) against the dev'd VortexStepMethod — every model rebuilt from scratch, the caches were deleted firsttest_helpersfor the reason aboveagent ci-local: not run — both install the dependency from the tracked manifest, which pins VortexStepMethod 4.3.1. The last docs build on this branch failed only on the base'sprivate_functions.htmlsize threshold (reported on AeroPressure: polars regenerated from the deformed shape, not a flap angle #285, now Split the private API doc pages; three are over Documenter's warn threshold #301), and this change takes two entries off that page.bin/reuse_lintstill has noreuseon the boxrefresh_aero!on the 10-panel bench model is 391232 B and 0.203 ms median, of which the three distributions this change snapshots are 416 B and 0.275 µs — 0.11% of the allocations, 0.14% of the time. That is the whole cost on the success path; the rest of the diff is atrythat does not throw. Box: steady — 6 of 20 cores busy, 1.4–3.4 GHz, 23.6 GB free, no swap pressureScope
+147 / −61 across 8 files, of which
src/aero_modes/common.jlis most of both sides: the exception andfinite_fullleaving, andsafe_vsm_solve!becoming a try/restore/rethrow. Its three-branchgamma_initdispatch folds into one call while I am in there —solve!'s positional default issolver.sol.gamma_distribution. Second PR: OpenSourceAWE/VortexStepMethod.jl#282, which merges and releases first.Opened elsewhere
Closes #290 · task
SymbolicAWEModels.jl-290