Skip to content

Give the semi-infinite trailing vortex the bound kernel's linear core, from one shared core definition - #408

Draft
1-Bort-1 wants to merge 2 commits into
agent/318-correct-aoa-defaults-to-false-so-the-solfrom
agent/399-velocity-3d-trailing-vortex-semiinfinite
Draft

1-Bort-1 wants to merge 2 commits into
agent/318-correct-aoa-defaults-to-false-so-the-solfrom
agent/399-velocity-3d-trailing-vortex-semiinfinite

Conversation

@1-Bort-1

@1-Bort-1 1-Bort-1 commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

TL;DR

velocity_3D_trailing_vortex_semiinfinite! now returns zero at its start point instead of NaN. Inside its core the velocity now grows linearly with the distance to the axis instead of staying at the core-boundary value. Its core is built from the same helpers as velocity_3D_vortex_segment! in #400, so all three filament kernels use one core model and one copy of the code for it.

What was wrong

The Lamb–Oseen core radius of a semi-infinite trailing vortex grows from zero at x1. At XVP == x1, the "outside the core" test and the < 1e-12 * epsilon guard were both false. The core branch then normalised a zero radial vector and returned [NaN, NaN, NaN], and an existing test pinned that NaN. Inside the core the kernel returned the core-boundary velocity unscaled. A point at a quarter of the core radius got the same speed as one at half of it (ratio 1.0), and ForwardDiff saw a slope of 0.0 on the axis.

What changed

  • New private helpers in src/filament.jl, called by the bound/finite-trailing kernel and by the semi-infinite kernel: lamb_oseen_core_radius, on_axis_without_core (the zero-core guard), core_end_term (one end's d/√(d²/|r0|²+ε²)) and core_coefficient. The semi-infinite core is core_coefficient(Γ, 1 + core_end_term(r1·Vf, …), …): Default correct_aoa to true, with the aero-centre flow leaving out only the section's own bound vortex #400's closed form with the far end's term at its limit.
  • Semi-infinite kernel: when both the distance to the axis and the core radius are within 1e-12·|r1|, the velocity is zero. Inside the core it matches the Biot–Savart value at ε and scales linearly with the distance to the axis. Its docstring now lists Vf, XVP, GAMMA, va and work_vectors with units. The docstring also named the function without its !, so @docs did not match it. That is fixed.
  • The test gets its core radius from lamb_oseen_core_radius instead of keeping its own copy of the formula. The test's analytical_solution lost its inside-core branch, which was the old model and was never called.
  • Before this change, sqrt(4·ALPHA0·NU·…) was written out in three places and the core term in two. Before extracting the helpers I grepped src/ and test/ for ALPHA0, epsilon^2, 1e-12 and core_radius. Nothing else computes them.

This is the model change #399 asked about. It moves any velocity evaluated inside a trailing wake core. Nothing the solver builds today is evaluated there. In the previous round the V3 kite polar (verification setup, 36 panels, α = 3/6/9°) gave bit-identical CL and CD under VSM and LLT, old kernel against new, in the same session. This round's refactor leaves velocity_3D_bound_vortex! and velocity_3D_trailing_vortex! bit-identical. The semi-infinite kernel moves by at most 1.4e-15 relative, over 200 000 random points from 1e-6 m to 10 m off the axis.

Review

I took all seven findings. The shared core helpers and the shared radius function are above. The redundant @test slope != 0 is deleted, because the finite-difference ≈ right below it already fails for a zero slope. The docstring now lists the arguments. "One core model" is now true of the code as well as the model.

Verification

  • Reproduced first: start point → [NaN, NaN, NaN]; v(ε/4)/v(ε/2) = 1.0; dv/dd on the axis = 0.0 (ε = 6.10e-3 m at 0.5 m, va = 1)
  • test/filament/test_semi_infinite_filament.jl: red before (4 failed / 63), green after (62/62, the one fewer being the deleted slope != 0). New testsets check that the velocity is linear inside the core, continuous at the core boundary, and has a nonzero ForwardDiff slope on the axis. The start-point test now asserts zero.
  • test/filament/test_bound_filament.jl 227/227, test/body_aerodynamics/test_body_aerodynamics.jl 4925/4925, test/verification/test_verification.jl 24/24, test/solver/test_forwarddiff.jl 10/10 (juliaserver, on 32243fa)
  • Kernels, 32243fa against 0008f2a: bound and finite trailing bit-identical, semi-infinite ≤ 1.4e-15 relative
  • Docs build clean. This repo has no REUSE setup
  • Local full suite on 32243fa: PASS (9 min, Julia 1.13.0) · GitHub CI on 32243fa: PASS. An earlier local run failed with UndefVarError: lamb_oseen_core_radius. It had started at 14:19, while this round's edits were in progress, and loaded the new test file against the old src. It did not test a committed state.
  • Benchmark: n/a. The branches are the same and the helpers are @inline
  • Risk: velocities evaluated inside a trailing core (e.g. a far-field probe near the wake) come out smaller than before. That is the intended model change.

Scope

+123 / −61 across 4 files against #400. Of the src/filament.jl growth, 34 lines are the four helpers and their docstrings. The kernels themselves are shorter. The rest is three new testsets and their helper, and 4 lines in docs/src/private_functions.md. Stack: on #400 (the bound-kernel closed form), and this PR targets its branch.

Opened by 1-Bort-1, an AI agent working for @1-Bart-1.
Closes #399 · task VortexStepMethod.jl-399

velocity_3D_trailing_vortex_semiinfinite! now uses the same zero-core guard
and closed-form core as velocity_3D_vortex_segment!: zero at its start point
rather than NaN, and inside the core the velocity scales linearly with the
distance to the axis, so ForwardDiff sees its slope there.

Co-Authored-By: Claude Opus 5.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 30, 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: REQUEST CHANGES · 2 inline, 0 off the diff

Good

  • The math is right: at axis_distance = ε, |r1|² = (r1·Vf)²/|Vf|² + ε², so the new inside-core K equals the outside Biot–Savart K there; checked by hand against lines 211 and 213–214.
  • The zero-core guard at lines 206–207 is copied exactly from velocity_3D_vortex_segment! (src/filament.jl:113–115). At x1, nr1 = ε = 0, so the kernel returns zero instead of NaN.
  • ForwardDiff works on the axis: in the core branch the non-differentiable axis_distance is only compared, never multiplied, so the slope comes from the linear r1×Vf.
  • Work vectors 4 and 5 are really gone: rg 'work_vectors\[' in src shows the kernel now reads only [1] and [3].
  • The docstring now includes the !, and docs/src/private_functions.md:81 lists the ! name, so @docs now matches it.
  • The test that pinned NaN at the start point now asserts == zeros(3). The dead inside-core branch of analytical_solution and its va argument are removed. The new testsets name the behaviour they protect.
  • The changelog entry splits into Changed (model) and Fixed (NaN, zero slope), which matches the card.

Not good

  • src/filament.jl:213 — This inside-core closed form and the guard at 206–207 are a second copy of the per-end core term d/√(d²/|r0|²+ε²)/(ε²|r0|²) and the guard in velocity_3D_vortex_segment! (src/filament.jl:113–134). §2 'one source per equation' calls that the defect. Extract the guard and the per-end core term into one helper that both kernels call; as written, a later fix to one kernel will silently miss the other.
  • test/filament/test_semi_infinite_filament.jl:31 — core_radius copies the kernel's Lamb–Oseen formula into the test, so the continuity and linearity tests pass whenever the two copies agree, not whenever the kernel is right. Call a shared src function instead, or hard-code the ε = 6.10e-3 m value the card measured.
  • The card does not say what was searched for before the kernel code was written (§2 'Say in the PR what you searched for'). Had it searched, the duplication in the finding below would have shown up.
  • sqrt(4 * ALPHA0 * NU * axial / va) now exists in three places: line 200, velocity_3D_trailing_vortex! at src/filament.jl:86, and the test helper core_radius. It should be one function that the test calls too.
  • In the ForwardDiff testset, @test slope != 0 adds nothing because the ≈ finite-difference check below it already covers it; delete it.
  • The new docstring still does not say what Vf, va and work_vectors are or which units they use (va [m/s]). The bound trailing kernel's docstring lists its arguments.
  • The card calls this 'one core model' for all three kernels. The model is shared, but the code is two copies, so the claim is only half true.

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 src/filament.jl Outdated
@inbounds for k in 1:3
vel[k] = K * cross_tmp[k]
end
K = GAMMA / (4π) / (nVf * epsilon)^2 *

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.

MAJOR: This inside-core closed form and the guard at 206–207 are a second copy of the per-end core term d/√(d²/|r0|²+ε²)/(ε²|r0|²) and the guard in velocity_3D_vortex_segment! (src/filament.jl:113–134). §2 'one source per equation' calls that the defect. Extract the guard and the per-end core term into one helper that both kernels call; as written, a later fix to one kernel will silently miss the other.

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.

Fixed in 32243fa: both kernels now build the core from core_end_term and core_coefficient and check the zero-core case with on_axis_without_core (src/filament.jl). Bound and finite-trailing kernels are bit-identical to before, and the semi-infinite one is within 1.4e-15 relative (200k random points).

Comment thread test/filament/test_semi_infinite_filament.jl Outdated
@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 30, 2026
@1-Bort-1

1-Bort-1 commented Sep 30, 2026 •

Copy link
Copy Markdown
Member Author

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

…ament kernels

Both velocity_3D_vortex_segment! and velocity_3D_trailing_vortex_semiinfinite! now
assemble their inside-core velocity from core_end_term and core_coefficient and guard
the zero-core case with on_axis_without_core. lamb_oseen_core_radius replaces the two
copies of the Lamb–Oseen formula and the test's own copy. The semi-infinite docstring
lists its arguments and units.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@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 30, 2026
@1-Bort-1 1-Bort-1 changed the title Give the semi-infinite trailing vortex the bound kernel's linear core Give the semi-infinite trailing vortex the bound kernel's linear core, from one shared core definition Sep 30, 2026
@1-Bort-1 1-Bort-1 added agent:ci Agent task state agent:review Agent task state and removed agent:running Agent task state agent:ci Agent task state labels Sep 30, 2026

This branch has not been deployed

No deployments
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.

1 participant