Private Types

Wing Geometry, Panel and Aerodynamics

VortexStepMethod.PanelType
@with_kw mutable struct Panel

Represents a panel in a vortex step method simulation. All points and vectors are in the kite body (KB) frame.

Fields

  • TE_point_1::MVec3=zeros(MVec3): First trailing edge point
  • LE_point_1::MVec3=zeros(MVec3): First leading edge point
  • TE_point_2::MVec3=zeros(MVec3): Second trailing edge point
  • LE_point_2::MVec3=zeros(MVec3): Second leading edge point
  • chord::Float64=0: Panel chord length
  • va::MVec3=zeros(MVec3): Panel velocity
  • corner_points::MMatrix{3, 4, Float64}=zeros(MMatrix{3, 4, Float64}: Panel corner points
  • aero_model::AeroModel=INVISCID: Aerodynamic model type AeroModel
  • aero_center::Vector{Float64}: Panel aerodynamic center
  • cl_coeffs::Vector{Float64}=zeros(Float64, 3)
  • cd_coeffs::Vector{Float64}=zeros(Float64, 3)
  • cm_coeffs::Vector{Float64}=zeros(Float64, 3)
  • cl_interp::CL = nothing: lift interpolation (its type is a struct parameter)
  • cd_interp::CD = nothing: drag interpolation
  • cm_interp::CM = nothing: moment interpolation
  • section_aero::SA = nothing: optional surface aero table, see SectionAero
  • control_point::Vector{MVec3}: Panel control point
  • bound_point_1::Vector{MVec3}: First bound point
  • bound_point_2::Vector{MVec3}: Second bound point
  • x_airf::MVec3=zeros(MVec3): Unit vector tangential to chord line
  • y_airf::MVec3=zeros(MVec3): Unit vector in spanwise direction
  • z_airf::MVec3=zeros(MVec3): Unit vector, cross product of xairf and yairf
  • width::Float64=0: Panel width
  • filaments::Tuple{BoundFilament,BoundFilament,BoundFilament,SemiInfiniteFilament,SemiInfiniteFilament} = ( BoundFilament(), BoundFilament(), BoundFilament(), SemiInfiniteFilament(), SemiInfiniteFilament() ): Panel filaments, see: BoundFilament
  • delta::T=0: flap trailing-edge deflection [rad]
  • crease_frac::T=0: chordwise flap-hinge fraction (0–1); 0 disables the plate kink
  • alpha_ref::Float64=0: centre angle [rad] of the range the polar table covers
  • alpha_window::Float64=0: half width [rad] the table reaches; 0 = unbounded
  • alpha_knots::Vector{Float64}=Float64[]: ascending angles [rad] the panel's own table holds values at
  • live_shape::Union{Nothing, KulfanParameters}=nothing: the deformed airfoil the polar was generated from
source
VortexStepMethod.ScanKnotsType
ScanKnots(data)

Polar angles that a lookup scans rather than bisects. Interpolations finds a gridded knot with searchsortedfirst, which dispatches on the knot vector, so a short table can choose the search that suits it: below SCAN_KNOT_MAX a linear scan over contiguous memory beats a binary search's mispredicted branches, and above it loses badly. Wraps its vector rather than copying it, so the angles stay writable in place.

source
VortexStepMethod.KulfanParametersType
KulfanParameters

Kulfan CST parameters for an airfoil: the weights of the class-shape transformation each surface is built from, the shared leading-edge modification weight and the trailing-edge thickness. Lives here rather than in AirfoilAero because a Panel carries the shape it is currently flying (live_shape), which a plot or a traction pattern reads without knowing how the shape was produced. Fit one with AirfoilAero.fit_kulfan_parameters, deform one with AirfoilAero.deform_kulfan and turn one into coordinates with AirfoilAero.kulfan_to_coordinates.

Mutable, so a live polar source can rewrite one shape every solve instead of building a new one, and the panel pointing at it follows without being told (AirfoilAero.deform_kulfan!). That also makes a panel's live_shape the very object it was sampled from rather than a copy that compares equal to it.

Fields

  • upper_weights::Vector{Float64}: weights for upper surface
  • lower_weights::Vector{Float64}: weights for lower surface
  • leading_edge_weight::Float64: Leading edge modification weight
  • TE_thickness::Float64: Trailing edge thickness
source
VortexStepMethod.PanelPropertiesType
PanelProperties

Structure to hold calculated panel properties.

Fields

  • aero_centers::Matrix{Float64}
  • control_points::Matrix{Float64}
  • bound_points_1::Matrix{Float64}
  • bound_points_2::Matrix{Float64}
  • x_airf::Matrix{Float64}: Vector of unit vectors tangential to chord line
  • y_airf::Matrix{Float64}: Vector of unit vectors in spanwise direction
  • z_airf::Matrix{Float64}: Vector of unit vectors pointing up (cross of xairf and yairf)
  • widths::Vector{Float64}: Span width of each panel
source
VortexStepMethod.BoundFilamentType
BoundFilament

Represents a bound vortex filament defined by two points.

Fields

  • x1::MVec3=zeros(MVec3): First point
  • x2::MVec3=zeros(MVec3): Second point
  • length=zero(Float64): Filament length
  • r0::MVec3=zeros(MVec3): Vector from x1 to x2
  • initialized::Bool = false
source
VortexStepMethod.SemiInfiniteFilamentType
SemiInfiniteFilament

Represents a semi-infinite vortex filament.

Fields

  • x1::MVec3=zeros(MVec3): Starting point
  • direction::MVec3=zeros(MVec3): Direction vector
  • vel_mag::Float64=zero(Float64): Velocity magnitude
  • filament_direction::Int64=0 : Direction indicator (-1 or 1)
  • initialized::Bool=false
source

Aerodynamic model constants

VortexStepMethod.LEI_AIRFOIL_BREUKELSConstant
LEI_AIRFOIL_BREUKELS

Deprecated alias of POLY (see AeroModel). The Breukels (tube_diameter, camber) → coeff derivation now lives in AirfoilAero.lei_poly_coeffs; sections carry the resulting (cl_coeffs, cd_coeffs, cm_coeffs).

source

Airfoil aerodynamics (AirfoilAero)

VortexStepMethod.AirfoilAero.KulfanBasisType
KulfanBasis(; n_stations=60, n_weights=8, ridge=1e-3)

The fixed CST basis a shape deformation is projected onto: chord stations x in [0, 1] and the ridge-regularised inverse projection of the class-times-Bernstein matrix C(x)·B(x) those stations span.

ridge is the Tikhonov weight, relative to the basis' own largest singular value, that keeps the projection from answering a deflection it cannot represent with weights far larger than the airfoil they correct. A plain pseudoinverse has no such bound: a kinked deflection — a strut buckling is one — lands on the basis' weakest directions and comes back amplified a hundredfold and alternating in sign, which is not an airfoil. The ridge trades a small, measured under-response on deflections the basis can hold for a bounded answer on the ones it cannot.

CST is linear in its weights, so a surface displacement is a matvec against this constant matrix — never a refit. Refitting inside a loop is not an option: the Kulfan fit is non-unique, so the same shape fitted twice returns weight vectors differing by more than the deformation signal, which reaches the polars as frame-to-frame jitter.

C(1) = 0, so the parameterisation cannot put the trailing edge off the chord line. Deflections must therefore be measured against the deformed chord, with the chord rotation and stretch taken from the leading- and trailing-edge points; what is left is the representable residual.

source
VortexStepMethod.AirfoilAero.LivePolarSettingsType
LivePolarSettings(; offsets=deg2rad.(-12.0:3.0:12.0), model_size="xlarge",
                  weights_dir=nothing, n_crit=9.0)

How a live polar is sampled. Every solve, each panel's deformed shape is evaluated at its reference angle of attack plus each of offsets, and those values become the panel's polar table directly — there is no fit in between.

Sampling rather than fitting is what lets a panel hold a stall. A polynomial over the same window averages the knee into a slope and past the peak returns a lift slope of the wrong sign, which is a divergence the solve cannot recover from; sampled values are the polar at their own angles and reproduce whatever shape lies between them.

The offsets set both what the polar resolves and how far the solve may move before it reads a flat end (see polar_drift): their spacing is the resolution a stall knee is caught at, their reach is the room the solve has. The default spacing matches the 3° grid the offline polar tables are generated on, over a range wide enough that a tip crossing into stall stays inside it.

source
VortexStepMethod.AirfoilAero.LivePolarsType
LivePolars(base; settings=LivePolarSettings(), n_stations=60)

Live polar source for a wing whose panels each carry one undeformed airfoil in base. Holds the fixed CST basis, the per-panel reference angles, the network and every buffer a refresh needs, so a refresh writes through storage that is already there and allocates nothing at all. A refresh is dominated by the forward pass itself; the deformation is a matvec against the constant basis, about half a microsecond a panel. Drive it with refresh_live_polars!.

source
VortexStepMethod.AirfoilAero.NeuralFoilWorkspaceType
NeuralFoilWorkspace(model, n_cases)

Scratch a symmetry-fused forward pass runs inside, so a caller evaluating a batch of the same width every solve allocates nothing at all. Sized once against model for at most n_cases cases; fused_output! takes any batch up to that width, which is what lets one workspace serve both a sampled polar batch and the narrower pressure batch.

source