Skip to content

Add Li/Gaunaa spanwise artificial viscosity for post-stall stabilization#247

Merged
1-Bart-1 merged 4 commits into
mainfrom
feature/artificial-viscosity
Jul 13, 2026
Merged

Add Li/Gaunaa spanwise artificial viscosity for post-stall stabilization#247
1-Bart-1 merged 4 commits into
mainfrom
feature/artificial-viscosity

Conversation

@1-Bart-1

Copy link
Copy Markdown
Member

Ports the opt-in implicit artificial-viscosity regularization (Li, Gaunaa, Pirrung & Lønbæk, TORQUE 2026) from the Python Vortex-Step-Method into the Julia LOOP solver.

Why

Post-stall (negative lift-slope) circulation distributions develop non-physical sawtooth oscillations in the fixed-point gamma_loop! and never converge. The implicit scheme (I - diag(mu) L) gamma = F(gamma) has the same steady solution as the explicit fixed point but stays stable at relaxation factors of order one, whereas the explicit stable step shrinks like N^-2 in post-stall.

What

  • Solver: new opt-in fields is_with_artificial_viscosity (default false) and artificial_viscosity_factor (default 0.035), threaded through the VSMSettings and ForwardDiff dual-copy constructors.
  • build_spanwise_laplacian!: discrete spanwise Laplacian with the second-order tip closures (Eq. 15) enforcing gamma -> 0 at the tips.
  • local_lift_slope!: per-panel dCl/dalpha via central difference of calculate_cl.
  • gamma_loop! (LOOP): builds the Laplacian and planform area once, then when any panel is post-stall, solves (I - diag(mu) L) gamma = F(gamma) with mu_i = max(0, -k S Cl'_i / dz_i^2) before relaxation.
  • SolverSettings: the two fields exposed for YAML config.

Design note (difference from Python)

The Python version gates the expensive linear solve on per-panel stall angles read from raw polar arrays. In Julia the polar is an interpolation object, so the solve is instead gated on any(mu .> 0) — behaviourally identical (it fires exactly in post-stall) without exposing internal polar storage.

The feature is fully opt-in and a no-op in attached flow, so existing results are unchanged.

Tests

Ports both Python tests:

  • Laplacian tip closures (interior stencil + Eq. 15 closures).
  • Post-stall stabilization on the paper's canonical AR=10 rectangular wing (plain iteration diverges; implicit scheme converges to a smooth distribution).

Verified locally: solver tests 14/14, ForwardDiff tests 7/7, and an integration smoke test confirming attached flow is bit-identical to baseline while a high-AoA case converges.

🤖 Generated with Claude Code

@1-Bart-1 1-Bart-1 force-pushed the feature/artificial-viscosity branch from 26e1f9e to c75da31 Compare June 27, 2026 16:29
1-Bart-1 and others added 2 commits July 13, 2026 11:12
Port the opt-in implicit artificial-viscosity regularization (Li, Gaunaa,
Pirrung & Lønbæk, TORQUE 2026) from the Python Vortex-Step-Method to the
LOOP solver. Post-stall (negative lift-slope) circulation distributions
otherwise develop non-physical sawtooth oscillations and never converge;
the implicit scheme (I - diag(mu) L) gamma = F(gamma) stays stable at
relaxation factors of order one.

Adds the discrete spanwise Laplacian with second-order tip closures
(Eq. 15), per-panel lift-slope evaluation, and gating so attached flow is
a no-op. The expensive linear solve is gated on any(mu > 0) rather than on
precomputed stall angles, since the Julia polar is an interpolation object.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@1-Bart-1 1-Bart-1 force-pushed the feature/artificial-viscosity branch from 088e739 to fbaf1f5 Compare July 13, 2026 10:32
@1-Bart-1

Copy link
Copy Markdown
Member Author

Rebased onto current main (resolved the one docs/src/private_functions.md list conflict). Validated on Julia 1.12: Spanwise Laplacian tip closures 5/5, post-stall stabilization 3/3, ForwardDiff 7/7, solver constructor 3/3 — all green against main's split-calc_forces!/split-cd-cm refactors.

@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

1-Bart-1 and others added 2 commits July 13, 2026 13:55
The previous post-stall test reimplemented the whole scheme inline and never
touched the solver, so the actual artificial-viscosity path in gamma_loop! had
0% patch coverage.

- Extract the viscosity step from gamma_loop! into apply_artificial_viscosity!,
  so it is directly testable and no longer duplicated inline.
- Solve in place with ldiv!(gamma, lu!(M), rhs) instead of M \ rhs, cutting the
  post-stall allocation from ~3.7 kB to the LU pivot vector; the attached hot
  path stays zero-alloc.
- Replace the reimplementing test with a unit test of apply_artificial_viscosity!
  on a real post-stall wing (asserts fire/no-op, smoothing, and zero allocation
  on the attached path) and a solve! integration test (attached no-op is
  bit-identical to viscosity off; high-AoA solve stays finite).

The firing test regenerates the NeuralFoil polar over alpha_range -5:40 deg
because cl clamps flat past the tabulated range, so the default 15 deg polar
never reaches negative lift slope and the feature never activates.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…scosity

# Conflicts:
#	ext/VortexStepMethodControlPlotsExt.jl
@1-Bart-1 1-Bart-1 merged commit 1179bf7 into main Jul 13, 2026
8 checks passed
@1-Bart-1 1-Bart-1 deleted the feature/artificial-viscosity branch July 13, 2026 12:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant