Normalize wing section order to +y → -y - #258
Merged
Merged
Conversation
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
compute_refined_section_interpolation! reblended every refined section's SectionAero from the unrefined sections unconditionally, while aero_data was preserved under use_prior_polar. A wing rebuilt onto fewer structural stations therefore kept full-resolution polars and lost the contour/Cp/cf tables that pressure integration reads: on the TU Delft V3, 37 generated tables collapsed to the 10 sampled onto its struts, and both the pressure loads and the lofted airfoil plot were interpolations of those 10. The reblend is now skipped whenever the polars are being preserved and the refined sections already carry tables, so it still fills them the first time round. obj_to_yaml gains geometry_path, naming the geometry YAML instead of always writing output_dir/geometry.yaml. Emitted table references carry the path from the YAML's directory to output_dir, which is what the loader resolves them against, so a generated dataset can keep its bulk in a subdirectory while the geometry sits with the hand-written ones. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
reinit! re-derived each panel's flip from dot(y_airf, spanwise_direction) on every call. A deforming wing can carry one panel's y_airf across that plane while its neighbours stay put, so the panel's z_airf inverted by 180° in a single step — and often back again the next one, chattering across the threshold. The traction pattern AeroPressure freezes is built on z_airf, so the aero force on that panel reversed with it. The orientation follows from the order a wing's sections run in, which is a property of its definition, so wing_span_flip computes it at BodyAerodynamics construction and every panel of that wing is reinitialized with it. Panels of one wing can no longer disagree and the value cannot change mid-run. reinit!(panel, …) drops spanwise_direction and takes flip::Bool instead: it no longer decides anything, it applies what the caller decided. Also carries the obj_to_yaml wingtip fix, the read_node_table speedup and the named-window plotting change that were already in the tree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The panel method of reinit! takes internal panel_props slices and is not user-facing; users call reinit!(wing) and reinit!(body_aero). An internal signature change needs no changelog entry of its own. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Panel y_airf and z_airf are derived from the order sections are stored in, but that order was never an invariant: refine!'s sort was the only normalization, and callers that build particle wings pass sort_sections=false. A geometry file written -y -> +y therefore inverted every panel normal, and a wing whose sections were replaced after its panels were built inverted them mid-run, one panel at a time as each crossed spanwise_direction. normalize_span_order! settles it at the boundaries: on YAML load, before any structural pairing exists, and in refine! for wings assembled through add_section!. The refine! hook is gated on recompute_mapping so the per-step call that only updates positions still never reorders. sort_sections stays for a scrambled list. obj_to_yaml and surfplan_to_aero_yaml emit +y -> -y as well. Not breaking: files of either order load the same now. With the order canonical, nothing has to decide a per-panel or per-wing flip from live geometry, so BodyAerodynamics drops the span_flip field it cached — which serialized into model bins and went stale — and reinit! reads wing_span_flip directly as a guard for hand-built wings. Measured on a V3 beam wing whose aero geometry was written the wrong way round: 72 of 72 panel normals inverted after init! and 56 after bridle relaxation, both now 0, with the sections staying monotonic through relaxation where they did not before. Spanwise distribution plots put +y on the left to match, through one span_axis helper replacing nine repeated Axis constructions. Also carries the shrink_wrap rolling-ball rewrite, the V3_neuralfoil and ram_air_kite example updates already in the tree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1-Bart-1
force-pushed
the
fix/refined-surface-tables
branch
from
August 17, 2026 09:45
8352468 to
f9e4529
Compare
Documenter's missing_docs check fails the docs build on any docstring in a checked module that no @docs block lists, and four new ones were not listed. span_order_key subsumes _section_sort_key, which named the same coordinate for the same purpose, and both it and can_reuse_prior_refined_surface_tables lose the underscore prefix now that they are documented names. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1-Bart-1
force-pushed
the
fix/refined-surface-tables
branch
from
August 17, 2026 09:53
f9e4529 to
89641dd
Compare
The rolling-ball rewrite rode along in the section-order commit and is unrelated to it. It also regresses the XFoil path: the wrapped NACA0012 of test/solver/test_backend_comparison.jl no longer converges at any angle, so generate_aero_matrices has nothing to interpolate the NaNs from and throws. The distance-field wrap comes back for now. The rewrite continues on wrap/rolling-ball, together with the two resampling fixes it needs and a record of what still fails. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Its positional defaults make the docstring's signature a Union of four arities, which no call-form @docs entry can match, so Documenter counted the docstring as missing however it was listed. The text was an orphan attached to the sort-key helper before this branch, and the sibling refined-section overload carries none either. Verified with a local docs build. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Problem
Panel
y_airfandz_airfare derived from the order a wing's sections are stored in (y_airf = normalize(bound_1 - bound_2),bound_1from section i,bound_2from section i+1). That order was never an invariant:refine!'s sort (_section_sort_key,rev=true) was the only normalization anywhere, and it is skipped whenever a caller passessort_sections=false— which is what building a particle wing does.-y → +ytherefore travelled unmodified intoBodyAerodynamicsand inverted every panel normal.spanwise_direction.Panel.reinit!compensated by re-deriving a flip from live geometry on every call. That is what produced the observed chatter — measured on a V3 particle lattice, trackingdot(z_now, z_prev)per panel per step:180° in a single step, neighbouring panels disagreeing at the same step.
AeroPressurebuilds its frozen traction pattern onz_airf, so a flipped panel's aero force reverses with it.Every scheme that samples the order fails, because the order legitimately changes: per panel (neighbours disagree under deformation), at construction (the sections are replaced afterwards), cached in a struct field (it serializes into model bins and goes stale).
Change
Normalize instead of compensate.
normalize_span_order!puts sections in+y → -yat the boundaries:refine!, for wings assembled throughadd_section!. Gated onrecompute_mapping, so the per-step call that only updates positions still never reorders.sort_sectionsstays for a genuinely scrambled list.obj_to_yamlandsurfplan_to_aero_yamlemit+y → -yas well. Not breaking — files of either order load identically now.With the order canonical, nothing has to decide a flip from live geometry.
BodyAerodynamicsdrops thespan_flipfield it cached, andreinit!readswing_span_flipdirectly as a guard for a hand-built wing.+y → -yis the convention already in use: SurfPlan exports it (SK100aero_geometry.yaml, +8.03 → −8.03),cfd_aero_geometry.yaml(+4.11 → −4.11), the V3 structural geometry (2.873 → −3.892) andrefine!'s own sort all agree. It is also the kite seen from the front, so spanwise distribution plots now put+yon the left — via onespan_axishelper replacing nine repeatedAxisconstructions.Verification
V3 beam wing, whose aero geometry file is the one written the wrong way round:
init!relax_bridle!Sections also stay monotonic through relaxation now, where they did not before. The V3 particle lattice, already correctly ordered, is unchanged (0 both before and after).
Both section orderings give identical aero with the flip branch exercised:
Also included
The
shrink_wraprolling-ball rewrite, theobj_to_yamlwingtip fix, theread_node_tablespeedup, the named-window plotting change, and theV3_neuralfoil/ram_air_kiteexample updates that were already in the tree.reinit!(panel, …)loses itsspanwise_directionpositional argument and takesflip::Boolinstead. That method takes internalpanel_propsslices and is not user-facing, so it is not a breaking change;test/panel/test_panel.jlis updated.🤖 Generated with Claude Code