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 intoSep 17, 2026
Conversation
… 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>
5 tasks
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Contributor
Author
|
Local full suite: PASS (9 min, Julia 1.12.7, one cell of the matrix) |
1-Bart-1
approved these changes
Sep 17, 2026
1-Bart-1
deleted the
agent/332-add-the-optional-spanwise-flow-viscous-d
branch
September 17, 2026 07:36
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>
This was referenced Sep 17, 2026
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.
TL;DR
is_with_viscous_drag_correction(defaultfalse, inSolverSettingsandSolver) 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, sosolve!,solveandlinearizeall 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
ocayonrepo),viscous_drag_correctiongets β fromdir_induced_va = cos(α)·y_airf + sin(α)·x_airf. That vector lies in the airfoil plane, and Python'sz_airfis the span axis, sov_par = Umag·dot(dir_induced_va, z_airf)is always 0. So β = 0, and both ΔCd and C_par are 0 for every panel. TheUmagit gets is|v_eff × z_airf|, which contains no spanwise part either. Its drag directionz × (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)inpanel_aerodynamics.jlcomputes 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 usev_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.update_gamma_candidate!now also writeslr.v_span_dist = dot(v_eff, y_airf), in the same pass that computesalphaandv_a.panel_loadstakesc_span(default 0) and adds that force alongy_airf. Incalc_forces!andcalculate_results, the correction adds ΔCd tocd_distand passesc_spantopanel_loads. The per-panel force, the totals and the moments all include it. The flag is also copied intoSolver(body_aero, settings)andmake_dual_shadow, solinearizekeeps it through ForwardDiff.calculate_results, no change in behaviour:lift_induced_vaanddrag_induced_vaonly ever appeared summed. The loop now projectsloads.forcedirectly and builds the moment the same waycalc_forces!does. That is why the file got 24 lines shorter while gaining the feature.test_body_aerodynamics.jlstill 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°:
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
src/solver.jl. Build a Solver from VSMSettings or from panel and section counts, deprecate the body_aero constructors, and check the sizes in solve! #340's head predates Rename the*_arraylocals to_dist, matching the per-panel fields they hold #341, which this change builds on, and stacking copied Rename the*_arraylocals to_dist, matching the per-panel fields they hold #341's commits into this branch. The only real overlap is one line: the new flag inSolver(body_aero, settings). Whichever PR merges second moves that line into Build a Solver from VSMSettings or from panel and section counts, deprecate the body_aero constructors, and check the sizes in solve! #340'ssolver_kwargs.panel_loadswith symbolic arguments. The newc_span = 0term should simplify away there, but I haven't run SymbolicAWEModels against this branch.viscous_drag|gaunaa|spanwise.?flow|crossflow|cross_flow|v_span: the only match is the Li/Gaunaa artificial viscosity, nothing to reuse.Verification
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 checksspanwise_flow_dragagainst 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, thatsolveandlinearizereport the corrected forces, thatcalc_forces!stays zero-alloc with the flag on, and that the flag defaults to off.test_solver34,test_unrefined_dist40,test_body_aerodynamics4870,test_results30,test_flow_curvature39,test_moment_units17,test_backend_comparison15,test_forwarddiff7,test_verification22,test_settings33,test_panel26agent ci-local, Julia 1.12.7): PASS (11 min, exit 0) · GitHub CI: PASSJulia 1.12 - ubuntuattest_forwarddiff.jl:87(POLAR_MATRICES),relative_error0.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 tomain@ 246237a on both lookup tables. Numbers are on test_forwarddiff.jl's POLAR_MATRICES check flakes at ~4%, and the oper #287.calc_forces!stays zero-alloc (test_solver.jl, and the new test with it on).Scope
+185 / −54 across 8 files: 100 lines of new test;
calculate_results−24 net after the loop cleanup;spanwise_flow_dragand thec_spanterm +20; the flag,v_span_distand its wiring +26 acrosssolver.jlandsettings.jl; CHANGELOG, docs index andruntests.jl+9.Closes #332 · task
VortexStepMethod.jl-332