Skip to content

Add the Gaunaa et al. 2024 spanwise-flow viscous drag correction as an opt-in solver setting - #352

Merged
1-Bart-1 merged 1 commit into
mainfrom
agent/332-add-the-optional-spanwise-flow-viscous-d
Sep 17, 2026
Merged

1-Bart-1 merged 1 commit into
mainfrom
agent/332-add-the-optional-spanwise-flow-viscous-d

Conversation

@1-Bort-1

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

Copy link
Copy Markdown
Contributor

TL;DR

is_with_viscous_drag_correction (default false, in SolverSettings and Solver) gives each panel the viscous drag increment and spanwise force that Gaunaa et al. 2024 (doi:10.1088/1742-6596/2767/2/022068) predict when flow crosses the span. It runs in both force loops, so solve!, solve and linearize all include it. The equations come from the paper rather than the Python code, because the Python version always adds zero force.

Why this doesn't copy the Python code

In the Python solver (awegroup/Vortex-Step-Method, the renamed ocayon repo), viscous_drag_correction gets β from dir_induced_va = cos(α)·y_airf + sin(α)·x_airf. That vector lies in the airfoil plane, and Python's z_airf is the span axis, so v_par = Umag·dot(dir_induced_va, z_airf) is always 0. So β = 0, and both ΔCd and C_par are 0 for every panel. The Umag it gets is |v_eff × z_airf|, which contains no spanwise part either. Its drag direction z × (z × d) is −d, pointing upstream, so a non-zero increment would have reduced drag. So matching Python's numbers would only prove that this port does nothing as well.

What the port does

  • spanwise_flow_drag(v_a, v_span, chord, density, mu) in panel_aerodynamics.jl computes the paper's equations: f0 = 0.062·Re^(-1/7), ΔCd = f0·(cos β^(-5/7) − 1), C_par = f0·tan β·cos β^(-5/7). Re and both coefficients use v_a, the speed normal to the span. The loops already use that speed for the polar forces (lr.v_a_dist = |v_eff × y_airf|), which matches the paper's choice of reference.
  • The spanwise velocity was not stored anywhere. update_gamma_candidate! now also writes lr.v_span_dist = dot(v_eff, y_airf), in the same pass that computes alpha and v_a.
  • panel_loads takes c_span (default 0) and adds that force along y_airf. In calc_forces! and calculate_results, the correction adds ΔCd to cd_dist and passes c_span to panel_loads. The per-panel force, the totals and the moments all include it. The flag is also copied into Solver(body_aero, settings) and make_dual_shadow, so linearize keeps it through ForwardDiff.
  • Cleanup in calculate_results, no change in behaviour: lift_induced_va and drag_induced_va only ever appeared summed. The loop now projects loads.force directly and builds the moment the same way calc_forces! does. That is why the file got 24 lines shorter while gaining the feature. test_body_aerodynamics.jl still matches the Python reference results (4870 tests).

Size of the effect, on the 8 m × 1 m INVISCID test wing at 20 m/s and α = 6°:

β side force added drag added
0 0
12° 3.35 N 0.94 N
30° 7.92 N 5.12 N

Even with no sideslip, the chordwise trailing segments induce a small spanwise velocity at the tip panels, so the correction there is up to 2.4 mN per panel. It cancels in the total.

Where I'd push back

Verification

  • Reproduced first: n/a, new feature. Python's zero result comes from reading its code (the axes are orthogonal), not from running it.
  • test/solver/test_viscous_drag_correction.jl: before the change it errored (UndefVarError: spanwise_flow_drag); now 90/90 pass in a fresh juliaserver session. It checks spanwise_flow_drag against the paper at β = −40…35°, and checks each panel's drag and spanwise force for sideslip and straight inflow. It also checks that the drag rises along the flow, that solve and linearize report the corrected forces, that calc_forces! stays zero-alloc with the flag on, and that the flag defaults to off.
  • Other affected files pass in a fresh session: test_solver 34, test_unrefined_dist 40, test_body_aerodynamics 4870, test_results 30, test_flow_curvature 39, test_moment_units 17, test_backend_comparison 15, test_forwarddiff 7, test_verification 22, test_settings 33, test_panel 26
  • Docs build clean (only the existing page-size warnings) · no REUSE in this repo · up to date with main
  • Local CI mirror (agent ci-local, Julia 1.12.7): PASS (11 min, exit 0) · GitHub CI: PASS
  • GitHub CI's first run failed one check: Julia 1.12 - ubuntu at test_forwarddiff.jl:87 (POLAR_MATRICES), relative_error 0.0460. This is test_forwarddiff.jl's POLAR_MATRICES check flakes at ~4%, and the oper #287, not this branch. Re-running the job unchanged made it green. The three test cells that passed print Jacobian norms identical to all 16 digits to main @ 246237a on both lookup tables. Numbers are on test_forwarddiff.jl's POLAR_MATRICES check flakes at ~4%, and the oper #287.
  • Benchmark: n/a. The flag defaults to off, and with it off calc_forces! stays zero-alloc (test_solver.jl, and the new test with it on).
  • Risk: the paper's coefficients assume a flat-plate turbulent boundary layer (Re^(-1/7)); a user who turns this on for a low-Re or laminar section gets that model anyway.

Scope

+185 / −54 across 8 files: 100 lines of new test; calculate_results −24 net after the loop cleanup; spanwise_flow_drag and the c_span term +20; the flag, v_span_dist and its wiring +26 across solver.jl and settings.jl; CHANGELOG, docs index and runtests.jl +9.

Closes #332 · task VortexStepMethod.jl-332

… solver option

is_with_viscous_drag_correction (default false) adds spanwise_flow_drag's drag
increment and spanwise force to every panel in calc_forces! and
calculate_results, from the velocity along y_airf the gamma loop now keeps in
LoopResult.v_span_dist.

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 16, 2026
@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 16, 2026
@codecov

codecov Bot commented Sep 16, 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:ci Agent task state agent:review Agent task state and removed agent:running Agent task state agent:ci Agent task state labels Sep 16, 2026
@1-Bort-1

Copy link
Copy Markdown
Contributor Author

Local full suite: PASS (9 min, Julia 1.12.7, one cell of the matrix)

@1-Bart-1
1-Bart-1 merged commit 1c493cf into main Sep 17, 2026
11 of 12 checks passed
@1-Bart-1
1-Bart-1 deleted the agent/332-add-the-optional-spanwise-flow-viscous-d branch September 17, 2026 07:36
@1-Bort-1 1-Bort-1 added agent:done Agent task state and removed agent:review Agent task state labels Sep 17, 2026
1-Bort-1 added a commit that referenced this pull request Sep 17, 2026
…the-

Main's #352 added is_with_viscous_drag_correction to the deprecated
Solver(body_aero, settings); it now travels through solver_kwargs, so
Solver(settings) carries it too. Its new test builds its solvers from
panel and section counts rather than the deprecated constructor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1-Bort-1 added a commit that referenced this pull request Sep 17, 2026
#352 rewrote calculate_results' panel loop around panel_loads' force and added
spanwise_flow_drag. Keeps that structure with this branch's names: v_rel_dist
for |v_rel x y_airf|, va_panel and inv_va_panel, spanwise_flow_drag(v_rel, ...),
and va_vec for the vector locals of its test.

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.

Add the optional spanwise-flow viscous drag correction (Gaunaa et al. 2024) from the Python solver

2 participants