Private Types
Wing Geometry, Panel and Aerodynamics
VortexStepMethod.Panel — Type
@with_kw mutable struct PanelRepresents 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 pointLE_point_1::MVec3=zeros(MVec3): First leading edge pointTE_point_2::MVec3=zeros(MVec3): Second trailing edge pointLE_point_2::MVec3=zeros(MVec3): Second leading edge pointchord::Float64=0: Panel chord lengthva::MVec3=zeros(MVec3): Panel velocitycorner_points::MMatrix{3, 4, Float64}=zeros(MMatrix{3, 4, Float64}: Panel corner pointsaero_model::AeroModel=INVISCID: Aerodynamic model typeAeroModelaero_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 pointbound_point_1::Vector{MVec3}: First bound pointbound_point_2::Vector{MVec3}: Second bound pointx_airf::MVec3=zeros(MVec3): Unit vector tangential to chord liney_airf::MVec3=zeros(MVec3): Unit vector in spanwise directionz_airf::MVec3=zeros(MVec3): Unit vector, cross product of xairf and yairfwidth::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 kinkalpha_ref::Float64=0: centre angle [rad] of the range the polar table coversalpha_window::Float64=0: half width [rad] the table reaches; 0 = unboundedalpha_knots::Vector{Float64}=Float64[]: ascending angles [rad] the panel's own table holds values atlive_shape::Union{Nothing, KulfanParameters}=nothing: the deformed airfoil the polar was generated from
VortexStepMethod.ScanKnots — Type
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.
VortexStepMethod.KulfanParameters — Type
KulfanParametersKulfan 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 surfacelower_weights::Vector{Float64}: weights for lower surfaceleading_edge_weight::Float64: Leading edge modification weightTE_thickness::Float64: Trailing edge thickness
VortexStepMethod.PanelProperties — Type
PanelPropertiesStructure 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 liney_airf::Matrix{Float64}: Vector of unit vectors in spanwise directionz_airf::Matrix{Float64}: Vector of unit vectors pointing up (cross of xairf and yairf)widths::Vector{Float64}: Span width of each panel
VortexStepMethod.Filament — Type
Abstract type for vortex filaments
VortexStepMethod.BoundFilament — Type
BoundFilamentRepresents 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
VortexStepMethod.SemiInfiniteFilament — Type
SemiInfiniteFilamentRepresents a semi-infinite vortex filament.
Fields
- x1::MVec3=zeros(MVec3): Starting point
- direction::MVec3=zeros(MVec3): Direction vector
vel_mag::Float64=zero(Float64): Velocity magnitudefilament_direction::Int64=0 : Direction indicator (-1 or 1)- initialized::Bool=false
Aerodynamic model constants
VortexStepMethod.LEI_AIRFOIL_BREUKELS — Constant
LEI_AIRFOIL_BREUKELSDeprecated 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).
VortexStepMethod.SCAN_KNOT_MAX — Constant
Longest table a linear knot scan still beats a binary search on.
Airfoil aerodynamics (AirfoilAero)
VortexStepMethod.AirfoilAero.KulfanBasis — Type
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.
VortexStepMethod.AirfoilAero.LivePolarSettings — Type
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.
VortexStepMethod.AirfoilAero.LivePolars — Type
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!.
VortexStepMethod.AirfoilAero.NeuralFoilModel — Type
NeuralFoilModelHolds the neural network weights and configuration.
VortexStepMethod.AirfoilAero.NeuralFoilResult — Type
NeuralFoilResultResults from NeuralFoil aerodynamic analysis.
VortexStepMethod.AirfoilAero.NeuralFoilWorkspace — Type
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.
VortexStepMethod.AirfoilAero.ContourPressureScratch — Type
ContourPressureScratch()The buffers a surface-pressure reconstruction reuses, so contour_pressure! allocates nothing per panel or per frame. Each grows to whatever contour and station count it is first handed and stays that size.