Skip to content

Develop - #163

Merged
ocayon merged 18 commits into
mainfrom
develop
Sep 17, 2026
Merged

ocayon merged 18 commits into
mainfrom
develop

Conversation

@ocayon

@ocayon ocayon commented Sep 17, 2026

Copy link
Copy Markdown
Member

Summary

Aligns the solver with three papers: Gaunaa, Li & Pirrung, From thin airfoil theory to finite wings (TORQUE 2026); Li, Gaunaa, Pirrung & Lønbæk, Relaxation limits and artificial viscosity (TORQUE 2026); Gaunaa, Sørensen & Li, A correction model for the effect of spanwise flow on the viscous force (2024). Each departure was found by reading the papers against the code, and each fix is one commit with the reasoning in its message. Figures with descriptions are in docs/figures/README.md.

What changes for users

  • Force directions come from the quarter-chord flow by default (is_aoa_corrected=True). Magnitudes keep the 3/4-chord angle. False gives the previous behaviour.
  • Attached-trailed vortex force on the chordwise legs, new flag is_with_attached_trailed_vortex_force (default on). Zero net effect on straight wings.
  • Relaxation factor is adaptive by default (relaxation_factor=None), from the Li et al. stability limit; an explicit value is used verbatim. On the V3 kite the base loop converges in 2-4x fewer iterations.
  • New result drag_induced_trefftz, the far-wake induced drag for uniform inflow.
  • Removed: is_smooth_circulation, smoothness_factor, is_artificial_damping, artificial_damping, is_with_simonet_artificial_viscosity, simonet_artificial_viscosity_fva, and the simonet_* loop types (never read, or imported a module that no longer exists).
  • Results move on swept or skewed panels (kite tips); unswept wings are unchanged except through the circulation map and the directions.

Fixes, in commit order

  1. 233a238 Dead stall options removed.
  2. 741e02d Circulation map Γ = ½ |V_inner| c Cl (paper eq. 4) instead of ½ Umag²/Umagw c Cl. 2-6% on the V3.
  3. a6e5cf7 Artificial viscosity also gates on negative stall.
  4. efbf79c Panel frame and chord perpendicular to the quarter-chord line (CP1). The old frame used the rib chord and a leading-edge-step normal; on the V3 tips the chord-to-span skew reached 0.77. Also the issue behind VortexStepMethod.jl PR #273.
  5. 9c43c1a Profile drag along the local inner flow (was the global span axis crossed with lift: up to 108° off at the V3 tips), and the other panels' bound vortices included in the quarter-chord flow (curved bound-vortex effect).
  6. 1300ab7 Gaunaa 2024 viscous correction fed the real spanwise flow. Its β never saw the flow before (exactly zero on a straight wing in sideslip, a geometry-only number on the V3) and its drag increment pointed upstream.
  7. 4e979de Kutta-Joukowski force on the attached-trailed legs (paper sec. 3).
  8. b4043c8 + 60ef3f8 Adaptive relaxation; the paper's bound is exact for LLT and half for VSM (measured against the fixed-point Jacobian).
  9. a404e10 Trefftz-plane induced drag.
  10. b8cec1b Each ring's frozen wake follows its own panel's apparent velocity (matters only with body rates or distributed inflow).
  11. 62c53af Quarter-chord directions by default; the direction step restricted to VSM.

Evidence

Trefftz check (inviscid, same circulation, on-blade / far-wake induced drag):

wing 1/4c + AT 3/4c (old default)
straight AR 4 / 8 / 20 1.00 / 1.00 / 1.00 1.38 / 1.29 / 1.20
elliptic AR 8 1.02 1.28
swept 30° AR 8 0.94 1.23
V3 geometry, 5° / 10° / 15° 0.93 / 0.90 / 0.86 1.28 / 1.34 / 1.40

Trefftz

V3 kite (CAD geometry, CFD+NeuralFoil polars, 50 panels) against RANS and wind tunnel. The new default sits on the RANS drag in the attached range and in sideslip; the old default overshoots RANS but was closer to the wind tunnel.

V3 polars

Other figures: frame and drag-direction errors (02), attached-trailed force (04), relaxation (05), wake direction (06), the perpendicular-leg variant that was tried and rejected (07).

Tests

Unit suite 155 passed, 5 skipped. Verification cases 5 passed; their thesis-code comparisons were widened to the measured deviation of the new formulation (V3: CL +0.015, CD +0.012 at 8°; curved wing: CL +0.02, CD +0.004), the analytical and CFD comparisons are unchanged. New tests: relaxation limit, Trefftz drag, wake direction, negative-stall gate, geometry oracle projected to the new frame.

Known limitations and open questions

  • The V3 on-blade induced drag is still 7-14% below the Trefftz value and a 30° swept wing 6%; the leg direction and the wake direction were both ruled out as the cause.
  • The 3/4-chord directions matched the wind-tunnel drag better despite overestimating the induced drag by 20-40%. The reason is not understood and is the next thing to investigate (Reynolds number of the polars, and post-stall behaviour are candidates).
  • The Li et al. viscosity coefficient is derived for uniform rectangular wings and is applied per panel to kites as an extrapolation.

ocayon and others added 16 commits September 15, 2026 01:20
Add gamma_loop_type="casadi_newton": the circulation fixed point solved as
a root-finding problem, R(gamma) = (I - diag(mu) L) gamma - G_raw(gamma),
with the Jacobian from CasADi automatic differentiation. CasADi holds only
the per-panel section physics (G_raw, mu, alpha as functions of the panel's
relative velocity); NumPy assembles R and J = I - diag(mu) L - sum_k
diag(c_k) AIC_k, so the graph is O(n) and the induction matrices stay
numeric. Same fixed point as base/anderson.

Globalisation: Newton with Armijo backtracking, switching to pseudo-
transient continuation (implicit Euler on the base loop's own flow, SER with
a floor at 0.03) when the line search stalls -- the piecewise-linear polars
make |R|^2 have kink minima that are not roots at the Cl-max corner. The
Jacobian differentiates the corner-averaged polar so it does not chatter
across that corner, and the step control treats the viscosity gate, the
residual's one discontinuity, as such (a crossing at the floor step is
accepted). Stopping rule is the UN-relaxed residual, 1/relaxation_factor
tighter than base's at the same allowed_error. Falls back to base on a
failed attempt (newton_fallback_to_base, default on).

Measured on the AWETrim LEI-V3 (64-state alpha/sideslip/yaw-rate grid, AV
on): 27 panels cold 64/64 converged without fallback in 1.7 s vs base
14.9 s and anderson 49/64 in 7.6 s; 45 panels 61/64 + 3 fallbacks in 6.9 s
vs base 29 s. Quasi-steady trims ~5x faster with identical result.

The CasADi panel function also exposes the polar lookups (cl, cd, cm and
their corner-averaged forms) and a polar_interpolation="bspline" option,
for callers that build forces from the same tables. Optional dependency:
pip install Vortex-Step-Method[casadi].

Co-Authored-By: Claude <noreply@anthropic.com>
… loops

is_smooth_circulation, smoothness_factor, is_artificial_damping,
artificial_damping, is_with_simonet_artificial_viscosity and
simonet_artificial_viscosity_fva were stored on the Solver and never read.
The gamma_loop_type values simonet_stall, non_linear_simonet_stall and
non_linear_simonet_stall_newton_raphson imported VSM.StallSolvers, which no
longer exists, so selecting them raised ImportError. Li, Gaunaa, Pirrung &
Lonbaek (TORQUE 2026) also note the Simonet coefficient must be tuned case
by case, and their artificial viscosity (is_with_artificial_viscosity)
replaces it. An unknown gamma_loop_type now raises a clear ValueError.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…er| c Cl

The fixed-point target was Gamma = 0.5 Umag^2 / Umagw c Cl, i.e. the
Kutta-Joukowski force evaluated with the undisturbed freestream speed
perpendicular to the span. Gaunaa, Li & Pirrung (TORQUE 2026, Eq. 4) equate
the 2D lift to the Kutta-Joukowski force on the bound vortex with the inner
velocity itself, Gamma = 0.5 |V_inner| c Cl, where V_inner is the span-
perpendicular relative velocity at the control point with the section's own
2D bound induction removed. On the V3 kite Umag/Umagw is 0.94-0.98, so the
old map put the circulation 2-6% low. Applied in the base, Anderson, Broyden
and CasADi loops; compute_aerodynamic_quantities no longer returns Umagw and
the relative velocity is exposed through compute_relative_velocity().

The CasADi post-stall test that relies on Newton finding the unregularized
post-stall root now uses 10 panels: with the new map that root is not
reached at 20 panels, while the regularized loop converges there in a few
steps.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The post-stall regularization of Li, Gaunaa, Pirrung & Lonbaek (TORQUE 2026,
Eq. 16) applies wherever the lift slope is negative, but the gate only
looked for the first positive-Cl peak, so a panel past negative stall was
never regularized. A mirror gate (the Cl trough nearest alpha = 0 in the
negative-Cl region) is added to the numpy loops and to the CasADi residual,
whose panel function also drops the now unused freestream input.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…inciple)

The panel frame was built from the section's raw LE-to-TE chord line and
the leading-edge step, so on swept panels the chordwise axis was not
perpendicular to the bound vortex and the normal was not perpendicular to
the chord (on the V3 kite tips the chord-to-span dot product reached 0.77).
CP1 of Gaunaa, Li & Pirrung (TORQUE 2026) defines the inner 2D section
perpendicular to the local quarter-chord line: the chord direction is now
projected perpendicular to the bound-vortex axis, the normal is chord x
span, and the panel chord is the perpendicular one (56% and 19% shorter on
the two V3 tip panels). On unswept panels nothing changes. This also makes
the normal come from the bound-vortex step rather than the leading-edge
step (the issue behind VortexStepMethod.jl PR #273).

The thesis-geometry oracle test now projects its expectations the same way.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… the quarter-chord flow

Two departures from TAT3 of Gaunaa, Li & Pirrung (TORQUE 2026), which takes
the lift and drag directions from the undisturbed relative flow at the
quarter chord:

- The profile drag direction was the global wing span axis crossed with the
  local lift direction. Where a panel's span axis tilts away from the global
  one this misdirects the drag: on the V3 kite by 108, 65, 34 and 22 degrees
  on the four outermost panels, the tip getting an upstream component. The
  drag now acts along the local inner flow direction.
- The quarter-chord flow used for the directions (is_aoa_corrected) came from
  the LLT influence matrix, which drops every bound vortex. That is exact
  only for a straight lifting line; the bound segments of the other panels
  (the curved/swept bound-vortex influence of Li et al. 2020) are now added
  back through assemble_bound_vortex_AIC. On a 30 deg swept inviscid wing the
  on-blade induced drag was a third of the Trefftz-plane value before and is
  within 6% after; on a straight wing both agree to four digits.

The results-oracle test gains an absolute tolerance on the side force, which
is zero only up to round-off now that the drag direction is local.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…w it models

The spanwise-flow friction correction of Gaunaa, Sorensen & Li (2024)
computed its angle beta from cos(alpha) y_airf + sin(alpha) x_airf dotted
with z_airf. That vector lies in the airfoil plane, so beta only ever saw
the frame skew of swept panels, never the flow: on a straight wing in 10
deg sideslip it was exactly zero, on the V3 kite it was a geometry-only
number up to 49 deg at the tips at any sideslip. The drag increment was also
built as z x (z x d) = -d, pointing upstream, and the reference speed and
dynamic pressure were the freestream ones.

The correction now takes the full relative velocity at the control point
(freestream plus induction, passed from the solver): beta is the angle
between it and the span-normal plane, Re_ref and the dynamic pressure use
the span-perpendicular speed (Eq. 4), the drag increment of Eq. 10 acts
along the local inner flow and the spanwise force of Eq. 11 along the span
with the sign of the spanwise flow. The per-panel debug prints are gone.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…gments

Gaunaa, Li & Pirrung (TORQUE 2026, Sec. 3) align the bound part of each
trailed vortex with the local chord up to the trailing edge and apply the
Kutta-Joukowski force to these attached trailed (AT) segments, a
contribution absent from lifting-line codes so far. The chordwise legs
already existed in the induction here (bound point to TE point) but carried
no force.

Neighbouring panels' legs coincide on their shared section boundary with
opposite sense, so boundary j carries gamma_j - gamma_{j-1} in the
bound-to-TE direction; the force is rho * Gamma_net * (V x l) with V the
full relative velocity at the section's 3/4-chord point on the leg (the
paper's evaluation point), from a new numba kernel that evaluates the whole
horseshoe system at arbitrary points and skips a leg the point lies on.
Each boundary force is split over its two panels in F_distribution, added
to the totals and moments, and reported in F_attached_trailed_distribution.

Opt-out with is_with_attached_trailed_vortex_force=False (default True). On
straight wings the forces are spanwise and cancel; with the quarter-chord
directions the on-blade induced drag of a straight inviscid wing then equals
the Trefftz-plane value to four digits. On the V3 kite it adds about 3% lift
and 5% drag.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…lity limit

The fixed-point loops used a fixed relaxation_factor of 0.01 (and 0.005 on
retry), a guess that is far too cautious for coarse high-aspect-ratio wings
and can be unsafe for fine low-aspect-ratio ones. Li, Gaunaa, Pirrung &
Lonbaek (TORQUE 2026, Eqs. 10-11) bound the largest eigenvalue of the
fixed-point Jacobian by 1 + (1/4)(N/AR) max Cl' for a uniform rectangular
wing, giving omega_max = 2 / (1 + N/(4 AR) max Cl'). The per-panel form
1 + (1/4) max_i (c_i/dz_i) Cl'_i is used here so narrow tip panels of a
cosine or kite discretization tighten the limit as they should; Cl'_i is
the steepest positive slope of the panel polar.

relaxation_factor now defaults to None, meaning 0.8 x that limit computed
per solve (Solver.compute_relaxation_factor_limit, stored in
relaxation_factor_used); an explicit value is used verbatim. The paper's
post-stall limit (Eq. 18) is for the explicit viscosity scheme; this solver
applies the viscosity implicitly, for which order-one relaxation stays
stable, so the attached-flow limit is used throughout. On the V3 kite the
factor comes out at 0.06-0.1 and the base loop converges in 3-4x fewer
iterations to the same circulation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s comparisons

docs/Solver.md gains the section on the Gaunaa, Li & Pirrung (TORQUE 2026)
coupling rules as implemented (span-perpendicular frame, quarter-chord
directions, attached-trailed force), the corrected description of the
artificial viscosity and viscous drag correction, and the V3 kite polars
before and after the changes against RANS and wind-tunnel data.

The verification cases compare the solver against the thesis-era code,
which uses Gamma = 0.5 Umag^2/Umagw c Cl, the raw section chord and no
attached-trailed force. Their tolerances are widened to the measured
deviations of the new formulation (V3: CL +0.015, CD +0.012 at 8 deg, half
of the CD from the attached-trailed force; curved wing: CL +0.02, CD
+0.004); the comparisons against the analytical, CFD and Maneia references
are unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The Li et al. (2026) limit is derived for a lifting line evaluated on the
quarter chord. Measured against the true fixed-point Jacobian it is exact
for the LLT model (omega_max / bound = 1.00 on rectangular wings of AR 5 and
16-50 panels) but optimistic for VSM, whose 3/4-chord control point sees
about twice the trailing-vortex induction: the ratio is 0.51-0.66 and tends
to 0.5 with refinement, and at 0.8 x the raw bound a 16-panel VSM case only
converged on the half-factor retry. The bound is now halved for VSM. With
the 0.8 safety factor every rectangular, cosine-spaced and V3 case tried
converges for both models.

Three CasADi tests pin the base loop to relaxation_factor=0.01 explicitly,
because their premise (a ~1000-iteration base reference, a 100x looser base
residual, a post-stall root the relaxed loop does not reach) is about that
fixed factor, not the adaptive default.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Adds BodyAerodynamics.compute_trefftz_plane_induced_drag and the results
entry drag_induced_trefftz for a wing in one uniform inflow: the wake
filaments projected onto the plane normal to the freestream are 2D vortices
with the circulation jump they trail, and the drag is the Kutta-Joukowski
force of half their velocity on the wake trace carrying the bound
circulation, D = 1/2 rho sum Gamma_i (V_T,i x l_i) . e_inf. It does not
depend on where forces are evaluated on the blade, so it is the check the
on-blade induced drag is held against: with quarter-chord directions and
the attached-trailed force an inviscid straight wing agrees to four digits,
an elliptic wing to 2% of L^2/(q pi b^2), and a 30 deg swept wing with
streamwise sections is 6% low. None for per-panel inflow or body rates.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…velocity

The two semi-infinite wake filaments of every ring pointed along one
direction for the whole wing: the area-weighted mean of the prescribed
freestream, taken before the body-rate velocity is added. Under yaw or roll
rates the relative inflow direction varies along the span, so the wake did
not follow the local flow anywhere. Each ring now uses its panel's own
apparent velocity (freestream plus rotational term, or the distributed
inflow) for direction and for the viscous-core speed, in the numba AIC
kernel, the pure-Python reference, the attached-trailed force evaluation and
the plotting wake. In a uniform inflow every panel gets the same direction,
so translating-flight results are unchanged to round-off. A single direction
is still accepted and broadcast. The wake direction is a second-order effect
for a translating wing (a per-panel local-flow wake moves the V3 lift by
under 1%), so this matters for turning flight and stability derivatives.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Seven figures with a README, generated by docs/figures/make_pr_figures.py:
V3 polars before/after against RANS and wind tunnel, the panel frame and
drag-direction and viscous-beta corrections, the Trefftz-plane check of the
on-blade induced drag for both direction choices, the attached-trailed force
on the V3, the adaptive relaxation factor and the measured stability limit,
the per-ring wake direction under a yaw rate, and the investigated and
rejected perpendicular chordwise legs. The pre-change polars are kept as
JSON since the current code cannot reproduce them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…cted=True)

The lift and drag directions now come from the undisturbed flow at the
quarter chord by default (TAT3 of Gaunaa, Li & Pirrung, TORQUE 2026; the
paper's LL-Gaunaa), while the magnitudes keep the 3/4-chord angle (TAT2).
On inviscid wings this reproduces the Trefftz-plane induced drag exactly on
straight wings and within 2% on an elliptic one, where the 3/4-chord
directions overshoot it by 20-40% (docs/figures/03_trefftz_check.png). On
the V3 kite the drag lands on the RANS values in the attached range and in
sideslip. The 3/4-chord directions were kept as default until now because
their overestimated drag happened to match the V3 wind-tunnel drag better,
for reasons not yet understood; is_aoa_corrected=False still gives them.

The direction step is restricted to the VSM model: the LLT loop already
evaluates on the quarter chord without bound-vortex influence (the paper's
LL-1/4), so its loop angle is its direction angle, and adding the other
panels' bound vortices there made the LLT drag inconsistent with its own
circulation (verification case CD 0.153 instead of 0.120 at 8 deg).

On the V3 verification case the new default VSM is close to the thesis-era
VSM, which evaluated directions at the quarter chord too: CL 0.871 vs 0.860,
CD 0.108 vs 0.085 at 8 deg (the remaining CD difference is the attached-
trailed force and the span-perpendicular chord).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 17, 2026 12:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved solver correctness and input-validation issues remain, along with documentation and regression-test gaps.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Aligns the Vortex Step Method solver with updated aerodynamic formulations, adding adaptive/Newton solving, Trefftz drag, force and wake updates, and expanded validation.

Changes:

  • Updates circulation, geometry, force, viscosity, wake, and drag calculations.
  • Adds adaptive relaxation, optional CasADi Newton solving, and attached-trailed forces.
  • Updates tests, documentation, figures, and optional dependencies.
File summaries
File Summary Review notes
tests/verification_cases/TUDELFT_V3_KITE/test_kite.py V3 kite verification updates —
tests/verification_cases/curved_wing/test_curved_wing.py Curved-wing verification updates —
tests/Solver/test_solver.py Relaxation and Trefftz regression tests —
tests/Solver/test_gamma_loop_casadi_newton.py CasADi Newton solver tests —
tests/BodyAerodynamics/test_wing_aero_object_against_create_geometry_general.py Geometry regression tests —
tests/BodyAerodynamics/test_wake_direction.py Wake-direction tests —
tests/BodyAerodynamics/test_calculate_results_against_output_results.py Force/result regression tests —
src/VSM/core/Wake.py Per-panel wake directions —
src/VSM/core/utils.py Aerodynamic influence and force utilities —
src/VSM/core/Solver.py Solver algorithms, relaxation, viscosity, and Newton iteration Moderate (2 votes): accept exact zero pseudo-transient trials. Moderate (1 vote): apply artificial-viscosity regularization in the nonlinear loop or reject the combination. Nit (1 vote): add negative-stall regression coverage.
src/VSM/core/Panel.py Panel frame and chord handling —
src/VSM/core/BodyAerodynamics.py Force, wake, frame, and Trefftz calculations Moderate (1 vote): restrict broadcasting to scalar/one-row inputs and handle empty inputs. Nit (1 vote): add swept/curved attached-trailed-force regression coverage, including flag-off behavior.
pyproject.toml Optional CasADi dependency —
docs/Solver.md Solver behavior documentation Moderate (1 vote): qualify or align the documentation’s “exact Jacobian” claim.
docs/nomenclature.md Solver terminology Nit (3 votes): remove the unsupported simonet_stall choice.
docs/figures/v3_polars_before_fixes.json V3 comparison data —
docs/figures/README.md Figure descriptions —
docs/figures/make_pr_figures.py Validation figure generation —
Review details

Suppressed comments (5)

docs/Solver.md:64

  • This public description calls the CasADi Jacobian exact, but the implementation computes the residual with the exact piecewise-linear cl_of(alpha) and computes the Jacobian from a ±0.5° averaged lookup. Near a polar table corner these are different functions, so Newton is using a surrogate/generalized Jacobian rather than the exact Jacobian of the residual. Please document that qualification here (or make the residual use the same lookup) so users do not rely on the stated exactness.
  - `"casadi_newton"`: Newton on the circulation residual with an exact
    CasADi Jacobian, globalised by pseudo-transient continuation. Same fixed

src/VSM/core/BodyAerodynamics.py:830

  • This broadcast check accepts any shorter repeated array, not just a scalar/one-row legacy input: for example, two identical speeds or two identical direction rows are silently expanded to all panels. Empty inputs also index element 0 before reaching the intended ValueError. Restrict broadcasting to size == 1 / shape[0] == 1 so malformed panel-sized inputs are rejected deterministically.
        if wake_speeds.size != self.n_panels and np.allclose(wake_speeds, wake_speeds[0]):
            wake_speeds = np.full(self.n_panels, float(wake_speeds[0]))
        if wake_units.shape[0] != self.n_panels and np.allclose(wake_units, wake_units[0]):
            wake_units = np.ascontiguousarray(np.tile(wake_units[0], (self.n_panels, 1)))

src/VSM/core/BodyAerodynamics.py:1578

  • The new attached-trailed force is a primary swept/curved-wing behavior, but the added end-to-end check only uses a straight wing where these contributions cancel. The V3/curved verification tests merely widen tolerances, so a sign or boundary-index error in the nonzero swept-wing force can pass; add a regression asserting the force distribution and global load change for a swept/curved case, including the flag-off result.
        if is_with_attached_trailed_vortex_force:
            f_at, p_at = self.compute_attached_trailed_vortex_forces(
                gamma_new, rho, core_radius_fraction, va_array

src/VSM/core/Solver.py:1518

  • gamma_loop_type="non_linear" still solves the raw map gamma - 0.5*|V_inner|*Cl*chord, so is_with_artificial_viscosity=True is silently ignored whenever either Broyden solve succeeds. That returns a different, potentially sawtoothed post-stall fixed point than base, anderson, and casadi_newton; apply _regularize_gamma_target in this residual (or reject this option combination).
            _, Umag_array, cl_array = self.compute_aerodynamic_quantities(gamma)
            gamma_new = 0.5 * Umag_array * cl_array * self.chord_array
            # Residual: difference between the computed and current gamma.

src/VSM/core/Solver.py:814

  • The new negative-stall gate is not covered by a negative-angle regression: the added CasADi tests exercise attached and positive post-stall cases, but no test drives alpha_array < stall_angles_neg. A sign or threshold error in this newly added branch could therefore pass the suite; add a negative-stall case that verifies the gate and regularization are active and agree between the base and Newton paths.
        if viscosity_ctx is None or not (
            np.any(alpha_array > viscosity_ctx["stall_angles"])
            or np.any(alpha_array < viscosity_ctx["stall_angles_neg"])
  • Files reviewed: 18/26 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/VSM/core/Solver.py Outdated
Comment thread docs/nomenclature.md Outdated
ocayon and others added 2 commits September 17, 2026 14:22
Refactor ratio calculation and gate crossing condition for clarity.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@ocayon
ocayon merged commit d735d74 into main Sep 17, 2026
1 check failed
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.

2 participants