Skip to content

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

Open
1-Bort-1 wants to merge 6 commits into
mainfrom
agent/357-elliptic-initial-circulation-for-a-body-
Open

1-Bort-1 wants to merge 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
Contributor

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
Contributor 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

### Fixed

- `ELLIPTIC` initial circulation works on a body with more than one wing, where it threw

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

MINOR: 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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Bart ruled the field private (4063770108), so there is no changelog line. The line was removed in 83b5ef6.

@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
Contributor 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 agent:review Agent task state and removed 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 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
- BREAKING: `ObjAdapter.center_to_com!`, `calculate_inertia_tensor` and
`calc_inertia_y_rotation` are removed. Mesh mass properties are computed by
SymbolicAWEModels, which reads the mesh with `read_faces`.
- BREAKING: the `BodyAerodynamics` field `y` is removed; it was scratch space for the

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is this breaking? wasn't this a private field?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, it was private scratch and nothing read it. I dropped the BREAKING line in 83b5ef6.

@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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent:review 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