Skip to content

Start ELLIPTIC circulation on each wing's own ellipse, so multi-wing bodies solve - #366

Merged
1-Bart-1 merged 6 commits into
mainfrom
agent/357-elliptic-initial-circulation-for-a-body-
Sep 21, 2026
Merged

1-Bart-1 merged 6 commits into
mainfrom
agent/357-elliptic-initial-circulation-for-a-body-

Conversation

@1-Bort-1

@1-Bort-1 1-Bort-1 commented Sep 21, 2026

Copy link
Copy Markdown
Member

TL;DR

solve! with type_initial_gamma_distribution = ELLIPTIC now works on a BodyAerodynamics with more than one wing, where it threw ArgumentError("Multiple wings not yet implemented"). Each wing gets an ellipse over its own span, along its own spanwise_direction, centred on its own mid-span; that also unblocks linearize on a multi-wing body with that setting.

What changed

calculate_circulation_distribution_elliptical_wing loops over the wings with a running panel offset, as the issue proposed. A control point's position is its projection on the wing's normalised spanwise_direction minus the mid-point of the wing's span, and the ellipse is taken over that wing's span. On one wing centred on y = 0 with span along y, that is the same formula as before.

The span's lower and upper ends come from a new private spanwise_extent(wing). It is the projection calculate_span already did, now allocation-free, and calculate_span is hi - lo of it. So the span and the mid-span come from one place.

Where I'd push back

  • A single wing off y = 0 changes too. It used to be measured from y = 0 with the absolute y of each control point. Now it is measured from its own mid-span. I count that as the same bug. It changes only the starting guess, never the converged gamma, but it is in the changelog. For a 4 m wing centred on y = 5, the old formula gave all zeros, the same as ZEROS, and a new test pins the ellipse.
  • BodyAerodynamics.y is removed. It was a private scratch buffer that only this function filled, so it gets no changelog entry.
  • calc_forces! and calculate_results use the first wing's spanwise_direction for the panels of every wing #358 (calc_forces! and calculate_results use wings[1].spanwise_direction for every wing) is the same class of bug in other functions. It is left to that issue.

Searched for span, spanwise_direction, extrema and panel_offset before adding spanwise_extent. unrefined_section_range is the only offset helper, and it indexes sections, not panels.

Verification

  • Reproduced first: both new testsets errored on unchanged code with ArgumentError: Multiple wings not yet implemented.
  • test/body_aerodynamics/test_body_aerodynamics.jl "solve! on a two-wing body": 27 pass + 2 errored before, 31/31 after (juliaserver). New tests:
    • "ELLIPTIC starts each wing on its own ellipse": two wings, the second shifted 5 m in y, plus a vertical fin (spanwise_direction = z, 5 m up). Each wing's slice equals sqrt(1 - (2y/span)^2) of the centred wing. A lone wing shifted 5 m in y gets that ellipse too (the old formula gives [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] there).
    • "ELLIPTIC and ZEROS converge to the same gamma": the largest relative difference is 3.3e-8 (the test allows 1e-4, the solver rtol is 1e-5).
  • Other test files, all green before the merge: test/solver/test_solver.jl (single-wing ELLIPTIC), test/solver/test_forwarddiff.jl (ELLIPTIC through linearize), test/wing_geometry/test_wing_geometry.jl (calculate_span).
  • Docs build: not run locally. The docs env has no manifest. CI's Documentation job passed, with spanwise_extent on private_functions.md.
  • Merged main three times, each conflict in CHANGELOG.md only, both sides kept each time: 1d399ad (the vava_vec BREAKING entry; the new test's va= keyword became va_vec=), e122ede (the polar-precision Fixed entry from Write polar CSV coefficients at 16 significant digits instead of 4 decimals #369), 0cc70c0 (the get_lower_upper Fixed entry from Take the flap hinge heights from where the contour crosses the crease line #370; Spread sliced sections evenly over the span, not over leading-edge arc length #350 and stability_derivatives gives roll, pitch and yaw rate derivatives about solver.reference_point #356 merged cleanly, and neither touches the ellipse or spanwise_extent). On 0cc70c0, test_body_aerodynamics.jl and test/solver/test_stability.jl pass 4936/4936 (juliaserver).
  • GitHub CI's Windows 1.12 cell went red twice on AutoForwardDiff matches AutoFiniteDiff (LOOP, POLAR_MATRICES): at 0.046430340012943265 on 1d399ad and at 0.057102607132145794 on 0cc70c0. Every other cell passed. That is test_forwarddiff.jl's POLAR_MATRICES check flakes at ~4%, and the oper #287. main failed on the same assertion with the same bits at 086ee32, and again at 0.05570289722278327 on its own push run at adff953. On 0cc70c0 the Windows cell's norm_fd (2.0741507267422183) is the table main's passing macOS cell used at a059747, but its norm_fwd came out 2.05758 against 2.07415. This diff cannot move that test. It runs the ZEROS start, and on e122ede its linearize jacobians were bit-identical (==) with this branch's src and with origin/main's.
  • agent ci-local: PASS on 1d399ad (7 min) and on 0cc70c0 (8 min), Julia 1.13 · GitHub CI on 83b5ef6: all 7 checks green, Windows 1.12 included.
  • Risk: none known beyond the starting guess moving for a lone wing off y = 0; converged results are unchanged.

Scope

+72 / −37 across 5 files. The source change is +27 / −34: the loop replaces the single-wing guard and the global ellipse, and spanwise_extent replaces the body of calculate_span. The other lines are the two testsets (+34), the changelog lines and the docs entry.

Closes #357 · task VortexStepMethod.jl-357

calculate_circulation_distribution_elliptical_wing loops over the wings
with a running panel offset and measures each control point along its
wing's spanwise_direction from that wing's mid-span, so a body with more
than one wing no longer throws. spanwise_extent gives the (lo, hi)
projection that calculate_span and the ellipse both take; the scratch
field BodyAerodynamics.y it used to fill goes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@1-Bort-1 1-Bort-1 added agent:running Agent task state agent:ci Agent task state and removed agent:running Agent task state labels Sep 21, 2026

@1-Bort-1 1-Bort-1 left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Independent review (advisory)

Verdict: APPROVE WITH COMMENTS · 1 inline, 0 off the diff

Good

  • Matches the card's plan: the loop with a running panel offset replaces the single-wing guard, which is what #357 proposed; #358 is left out on purpose and named
  • One source for the span: calculate_span is now hi - lo of spanwise_extent, so the span and the mid-span can't drift apart (wing_geometry.jl:1693-1707)
  • The per-wing offset loop follows the idiom already used at body_aerodynamics.jl:119/319 and solver.jl:532; no new offset helper was invented (checked with rg)
  • Removing BodyAerodynamics.y is safe inside the repo: rg over src/ext/test/examples/docs finds no reader of body_aero.y
  • Tests cover what the card claims: the fin along z at 5 m and the wing shifted 5 m in y check the per-wing axis and mid-span, and ELLIPTIC and ZEROS converging to the same gamma shows the converged result doesn't depend on the start
  • The @debug lines and the stale 'Returns: nothing' docstring go away; the new docstrings are short and state what the code does

Not good

  • CHANGELOG.md:38 — The changelog doesn't mention that the documented BodyAerodynamics field y was removed. This CHANGELOG marks removed public surface as BREAKING:, so code that passed y= to the keyword constructor breaks with nothing written down. Add a BREAKING: line, or keep the field until Bart decides as the card asks.
  • normalize(wing.spanwise_direction) is computed both in spanwise_extent and in the caller at body_aerodynamics.jl:508; having spanwise_extent also return the axis, or take it, would leave one copy
  • No test covers a lone wing off y = 0, the behaviour change the card calls out; it is only covered indirectly as the second wing of the pair
  • The docs build and ci-local are unverified; spanwise_extent is listed in private_functions.md, but only CI will confirm that make.jl is happy

claude, rubric CLEAN_CODE.md. A different lab from the implementer
on purpose: a reviewer sharing its blind spots would not flag its mistakes.

Comment thread CHANGELOG.md
@1-Bort-1 1-Bort-1 added agent:queued Agent task state agent:running Agent task state and removed agent:ci Agent task state agent:queued Agent task state labels Sep 21, 2026
@codecov

codecov Bot commented Sep 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@1-Bort-1 1-Bort-1 added agent:queued Agent task state agent:running Agent task state and removed agent:running Agent task state agent:queued Agent task state labels Sep 21, 2026
…AKING

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@1-Bort-1 1-Bort-1 added agent:ci Agent task state agent:queued Agent task state agent:running Agent task state and removed agent:running Agent task state agent:ci Agent task state agent:queued Agent task state labels Sep 21, 2026
… the two-wing test

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@1-Bort-1

1-Bort-1 commented Sep 21, 2026

Copy link
Copy Markdown
Member Author

Local full suite: PASS (6 min, Julia 1.13.0, one cell of the matrix)

@1-Bort-1 1-Bort-1 added agent:queued Agent task state agent:running Agent task state and removed agent:running Agent task state agent:queued Agent task state labels Sep 21, 2026
…itial-circulation-for-a-body-

# Conflicts:
#	CHANGELOG.md
@1-Bort-1 1-Bort-1 added agent:ci Agent task state and removed agent:running Agent task state labels Sep 21, 2026
@1-Bort-1 1-Bort-1 added the agent:review Agent task state label Sep 21, 2026
@1-Bort-1
1-Bort-1 requested a review from 1-Bart-1 September 21, 2026 15:31
@1-Bort-1 1-Bort-1 added agent:queued Agent task state agent:running Agent task state and removed agent:review Agent task state agent:queued Agent task state labels Sep 21, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment thread CHANGELOG.md Outdated
@1-Bort-1 1-Bort-1 added agent:queued Agent task state agent:running Agent task state and removed agent:running Agent task state agent:queued Agent task state labels Sep 21, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@1-Bort-1 1-Bort-1 added agent:queued Agent task state agent:running Agent task state agent:ci Agent task state agent:review Agent task state and removed agent:running Agent task state agent:queued Agent task state agent:ci Agent task state labels Sep 21, 2026
@1-Bort-1
1-Bort-1 requested a review from 1-Bart-1 September 21, 2026 17:25
@1-Bart-1
1-Bart-1 merged commit 46adf3d into main Sep 21, 2026
7 checks passed
@1-Bart-1
1-Bart-1 deleted the agent/357-elliptic-initial-circulation-for-a-body- branch September 21, 2026 20:46
@1-Bort-1 1-Bort-1 added agent:done Agent task state and removed agent:review Agent task state labels Sep 21, 2026
1-Bort-1 added a commit that referenced this pull request Sep 21, 2026
#366 added spanwise_extent(wing) beside this branch's calculate_span(wings,
direction); both projected the sections on a span axis. spanwise_extent now
takes the wings and a direction, and calculate_span is its hi - lo. The
elliptic circulation loop walks the wings with panel_range. CHANGELOG keeps
both Fixed entries.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent:done Agent task state

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ELLIPTIC initial circulation for a body with more than one wing

2 participants