Conversation
A self-crossing contour can reach a bare Fortran STOP inside XFoil, which ends the Julia process with exit code 0 and no exception. `analyze_sweep` now checks the contour before `set_coordinates` and throws `ArgumentError` instead, so a section XFoil has no solution for is one failed sweep rather than the end of the session. The same check covers a contour with more nodes than XFoil's panel arrays hold: ABCOPY refused it and left the previously loaded airfoil in place, so every angle came back solved on the wrong shape. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018rHHzpuPmi8pBjVk3r5QZD
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Member
|
Merge main into this branch |
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
analyze_sweep(::XFoilSolver, ...)checks the contour before handing it to XFoil and throwsArgumentError, because a self-crossing contour can reach a bare FortranSTOPin there and end the Julia process with exit code 0 and no exception. The same check catches a contour with more nodes than XFoil's panel arrays hold, which XFoil refused while leaving the previously loaded airfoil in place, so every angle came back solved on the wrong shape.What was wrong
Found from 1-Bart-1/BeyondTheSim.jl#53: an example script ended its Julia process partway through, silently, exit code 0, no stacktrace — in a REPL it takes the session away, in a script it looks like a successful run that printed half its output. Bisecting the call,
Xfoil.set_coordinatesreturns normally andXfoil.solve_alpha(0.0, Re; reinit=true)never does. Exit 0 with nothing on stdout or stderr is a gfortranSTOPwith no message.The contours that do it come out of
deform_section, whose re-wrap of an already-wrapped thin section produces a curve whose upper and lower surfaces cross. That is not an airfoil, and a panel method has no solution for it, so which of them terminates the process and which returns is not something the caller can predict — of nine shapes sliced from one mesh section at nine rolling-ball radii, four returned and five took the process with them, while every geometric statistic I measured (node count, shortest panel, neighbouring-panel ratio, maximum turn, crossing count) put survivors and killers in the same range:.dat, one wrap of the same cloudThe rows at 0.05 and 0.10 are the point: they agree on every column and disagree on life and death. So the guard cannot be a predictor of the
STOP. What separates the one safe contour from the rest is that it is the only simple closed curve in the set, and that is the precondition a panel method actually has — which is whatvalidate_xfoil_contournow enforces.I do not claim this makes
analyze_sweepproof against every contour XFoil mightSTOPon. It ends this class, which is the one a shrink wrap can produce, and it converts an unpredictable process death into a failure the caller can catch.The second defect in the same function
While bisecting I found that a contour with more than
Xfoil.IQX - 5= 281 nodes is refused by XFoil'sABCOPY, which prints to stdout and returns, leaving the previously loaded airfoil in the panel arrays.analyze_sweepnever looked, so it solved the sweep on whatever shape was loaded before and returned it as this one. Feeding a NACA 0012 at 299, 339, 399, 499 and 569 nodes gavecl = 0.5604994052186286every time — the 279-node answer, to the last digit. Same function, same idea (say when XFoil did not take the contour), so it rides here.Where I would push back
The real bug is upstream of this:
deform_section's re-wrap should not produce a self-crossing contour in the first place, and until it stops doing so these sections have no XFoil polar at all rather than a poor one. That is the shrink wrap's own concern and would change what every generated polar means, so it is #321 rather than this diff.The contour test is O(n²) in the node count — 239 nodes is ~28k segment pairs, run once per sweep against a viscous march of many angles. I did not benchmark it; it is not on any hot path I can find, and
compare_live_polar's per-panel call is still one XFoil sweep per check.Verification
Xfoil.set_coordinatesok, thensolve_alpha(0.0, Re; reinit=true)never returns — process gone, exit 0, no Julia error and no Fortran messagetest/airfoil_aero/test_airfoil_aero.jlred before, green after (5/5 new assertions, 108/108 file, exit 0). Red was run with the source change stashed: the folded contour returned instead of throwing, and the 399-node contour printedMaximum number of panel nodes : 281 / Current airfoil cannot be set.and still returned a solutionArgumentErrornaming the crossing panels, all nine in one process where each killer previously needed its own; the valid one still runstest/solver/test_backend_comparison.jlPASS 15/15 (42.5 s) — the suite's only XFoil march ·test/airfoil_aero/test_live_polar.jlPASS 79/79origin/mainat 23f129bbin/reuse_lintScope
+96 / -1 across 5 files: the check and its two geometry helpers in
airfoil_aero/, one regression testset, one changelog entry under## Unreleased, four@docsentries. The regression test uses a synthetic folded Kulfan section rather than the mesh section that found this — that geometry is in a private repo and this one is public. Closes #322.From 1-Bart-1/BeyondTheSim.jl#53 · task
BeyondTheSim.jl-53