Functions for creating the geometry
VortexStepMethod.add_section! — Function
add_section!(wing::Wing, LE_point::PosVector, TE_point::PosVector,
aero_model, aero_data::AeroData=nothing)Add a new section to the wing.
Arguments:
VortexStepMethod.refine! — Function
refine!(wing::AbstractWing; recompute_mapping=true, sort_sections=true)Refine the wing aerodynamic mesh from unrefined sections to refined sections.
This function interpolates the wing geometry from a coarse set of unrefined sections to a fine mesh of refined sections (npanels+1 sections) based on the wing's spanwisedistribution setting. It also populates nondeformedsections which enables deformation support via unrefined_deform!.
Required Workflow
Must be called after wing construction and before creating BodyAerodynamics:
wing = Wing("wing.yaml"; n_panels=40) # or ObjWing(...) or manual Wing
refine!(wing) # Refine mesh
body_aero = BodyAerodynamics([wing]) # Create aerodynamicsDistribution Methods
LINEAR: Linear interpolation between sectionsCOSINE: Cosine spacing (more panels near tips)SPLIT_PROVIDED: Split each unrefined section into sub-panelsUNCHANGED: 1:1 copy when nunrefinedsections == n_panels+1
Keyword Arguments
recompute_mapping::Bool=true: Recompute the mapping from refined panels to unrefined sectionssort_sections::Bool=true: Sort sections by spanwise position using globalLE_point[2](Y-axis). Disable for structural ordering.
Effects
- Populates
wing.refined_sections(n_panels+1 sections) - Populates
wing.non_deformed_sections(copy of refined_sections for deformation reference) - Computes
wing.refined_panel_mapping(panel → unrefined section mapping) - Resizes
wing.theta_distandwing.delta_distto n_panels
Example
# YAML wing
wing = Wing("wing.yaml"; n_panels=40)
refine!(wing)
body_aero = BodyAerodynamics([wing])
# After refinement, deformation is supported
unrefined_deform!(wing, theta_angles, delta_angles)VortexStepMethod.calculate_span — Function
calculate_span(wing::AbstractWing)Calculate wing span along spanwise direction.
Returns: Float64: Wing span
VortexStepMethod.calculate_projected_area — Function
calculate_projected_area(wing::AbstractWing, z_plane_vector=[0.0, 0.0, 1.0])Calculate projected wing area onto plane defined by normal vector.
Returns: Float64: Projected area
VortexStepMethod.load_polar_data — Function
load_polar_data(csv_file_path::String) -> Tuple{Union{Nothing, Tuple{Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{Float64}}}, Symbol}Load aerodynamic polar data from a CSV file using only readlines.
The CSV file must contain a header row with columns for alpha, cl, cd, and cm (case-insensitive, order arbitrary). Each subsequent row should contain numeric values for these columns.
Arguments
csv_file_path::String: Path to the CSV file containing polar data.
Returns
- A tuple
(aero_data, model_type)where:aero_data: A tuple of vectors(alpha, cl, cd, cm)if the file is valid, ornothingif invalid or missing.alpha: Angle of attack in degrees (converted to radians internally).cl: Lift coefficient.cd: Drag coefficient.cm: Moment coefficient.
model_type:POLAR_VECTORSif data is loaded, orINVISCIDif not.
Behavior
- If the file is missing, empty, or invalid, a warning is issued and
(nothing, INVISCID)is returned.
Example
# Create a YAML-based wing from configuration file
wing = Wing(
"path/to/wing_config.yaml";
n_panels=40,
n_groups=4
)Setting the inflow conditions and solving
VortexStepMethod.set_va! — Function
set_va!(body_aero::BodyAerodynamics, va::VelVector, omega=zeros(MVec3))Set velocity array and update wake filaments.
Arguments
- body_aero::BodyAerodynamics: The BodyAerodynamics struct to modify
va::VelVector: Velocity vector of the apparent wind speed [m/s]omega::VelVector: Turn rate vector around x y and z axis [rad/s]
set_va!(body_aero::BodyAerodynamics, settings::VSMSettings)Set velocity array from VSM settings configuration.
This convenience method extracts flight conditions from VSMSettings and constructs the velocity vector in the body reference frame based on:
- Wind speed from settings.condition.wind_speed
- Angle of attack from settings.condition.alpha (converted from degrees)
- Sideslip angle from settings.condition.beta (converted from degrees)
The velocity vector is constructed as:
- Xb (forward): windspeed * cos(α) * cos(β)
- Yb (right): windspeed * sin(β)
- Zb (down): windspeed * sin(α) * cos(β)
Arguments
body_aero::BodyAerodynamics: The aerodynamic body to modifysettings::VSMSettings: Settings object containing flight conditions
Example
settings = VSMSettings("path/to/settings.yaml")
body_aero = BodyAerodynamics([wing])
set_va!(body_aero, settings)CommonSolve.solve — Function
solve(solver::Solver, body_aero::BodyAerodynamics, gamma_distribution=nothing;
log=false, reference_point=solver.reference_point)Main solving routine for the aerodynamic model. Reference point is in the kite body (KB) frame. See also: solve!
Arguments:
- solver::Solver: The solver to use, could be a VSM or LLT solver. See: Solver
- body_aero::BodyAerodynamics: The aerodynamic body. See: BodyAerodynamics
- gamma_distribution: Initial circulation vector or nothing; Length: Number of segments. [m²/s]
Keyword Arguments:
- log=false: If true, print the number of iterations and other info.
- referencepoint=solver.referencepoint
Returns
A dictionary with the results.
CommonSolve.solve! — Function
solve!(solver::Solver, body_aero::BodyAerodynamics, gamma_distribution=solver.sol.gamma_distribution;
log=false, reference_point=solver.reference_point, moment_frac=0.1)Main solving routine for the aerodynamic model. Reference point is in the kite body (KB) frame. This version is modifying the solver.sol struct and is faster than the solve function which returns a dictionary.
Arguments:
- solver::Solver: The solver to use, could be a VSM or LLT solver. See: Solver
- body_aero::BodyAerodynamics: The aerodynamic body. See: BodyAerodynamics
- gamma_distribution: Initial circulation vector or nothing; Length: Number of segments. [m²/s]
Keyword Arguments:
- log=false: If true, print the number of iterations and other info.
- referencepoint=solver.referencepoint
- moment_frac=0.1: X-coordinate of normalized panel around which the moment distribution should be calculated.
Returns
The solution of type VSMSolution
VortexStepMethod.reinit! — Method
reinit!(body_aero::BodyAerodynamics; init_aero, va, omega, refine_mesh, recompute_mapping, sort_sections)Initialize a BodyAerodynamics struct in-place by setting up panels and coefficients.
Arguments
body_aero::BodyAerodynamics: The structure to initialize
Keyword Arguments
init_aero::Bool: Whether to initialize the aero data or notva=[15.0, 0.0, 0.0]: Apparent wind vectoromega=zeros(3): Turn rate in kite body frame x y and z
Returns
nothing
VortexStepMethod.linearize — Function
linearize(solver, body_aero, y; theta_idxs=1:4, delta_idxs=nothing,
va_idxs=nothing, omega_idxs=nothing, aero_coeffs=false,
backend=AutoForwardDiff(), kwargs...)Jacobian of aerodynamic outputs w.r.t. control and kinematic inputs at y. Each *_idxs selects which entries of y map to twist angles (one per unrefined section), trailing-edge deflections (one per unrefined section), apparent wind (vx, vy, vz), and angular rate (ωx, ωy, ωz) respectively.
backend accepts any DifferentiationInterface backend; AutoForwardDiff() (the default) requires solver_type=LOOP. fd_absstep/fd_relstep are forwarded only when the backend is AutoFiniteDiff.
Returns (jac, results, converged) where results is (F, M, moment_unrefined_dist...) — or the corresponding coefficients when aero_coeffs=true — and converged is false (with a warning) if any internal solve missed the solver's tolerances.
VortexStepMethod.calculate_results — Function
calculate_results(body_aero::BodyAerodynamics, gamma_new,
density,
core_radius_fraction, mu,
alpha_dist, v_a_dist,
chord_array, x_airf_array,
z_airf_array,
va_array, va_norm_array,
va_unit_array, panels::Vector{<:Panel},
is_only_f_and_gamma_output::Bool)Calculate final aerodynamic results. Reference point is in the kite body (KB) frame.
Returns: Dict: Results including forces, coefficients and distributions
Main Plotting Functions
The plotting functions are implemented as package extensions. They are available when GLMakie (or ControlPlots) is loaded before VortexStepMethod. The examples use GLMakie.
VortexStepMethod.plot_geometry — Function
plot_geometry(body_aero::BodyAerodynamics, title; kwargs...)Plot wing geometry from different viewpoints and optionally save/show plots. Routes to the active plotting backend (Makie or ControlPlots).
Arguments
body_aero: theBodyAerodynamicsto plottitle: plot title
Keyword arguments
data_type: file extension for saving (default depends on backend)save_path: path for saving the graphic (default:nothing)is_save: whether to save the graphic (default:false)is_show: whether to display the graphic (default:false)view_elevation: initial view elevation angle in degrees (default:15)view_azimuth: initial view azimuth angle in degrees (default:-120)use_tex: use externalpdflatexfor rendering (default:false; ignored by Makie)
plot_geometry(body_aero::BodyAerodynamics, title, ::MakieBackend;
data_type=nothing, save_path=nothing,
is_save=false, is_show=false,
view_elevation=15, view_azimuth=-120, use_tex=false)Makie backend implementation of plot_geometry.
Arguments:
body_aero: the BodyAerodynamics to plottitle: plot title
Keyword arguments:
data_type: File extension (default:nothing; delegated tosave_plotbackend-aware default)save_path: Path for saving (default: nothing)is_save: Whether to save (default: false)is_show: Whether to display (default: false)view_elevation: View elevation angle in degrees (default: 15)view_azimuth: View azimuth angle in degrees (default: -120)use_tex: Ignored for Makie (default: false)
VortexStepMethod.plot_distribution — Function
plot_distribution(y_coordinates_list, results_list, label_list; kwargs...)Plot spanwise distributions of aerodynamic properties. Routes to the active plotting backend (Makie or ControlPlots).
Arguments
y_coordinates_list: list of spanwise coordinate arraysresults_list: list of result dictionaries fromsolve!label_list: list of labels for each result
Keyword arguments
title: plot title (default:"spanwise_distribution")data_type: file extension for saving (default depends on backend)save_path: path to save plots (default:nothing)is_save: whether to save (default:false)is_show: whether to display (default:true)use_tex: use externalpdflatexfor rendering (default:false; ignored by Makie)
plot_distribution(y_coordinates_list, results_list, label_list, ::MakieBackend;
title="spanwise_distribution", data_type=nothing,
save_path=nothing, is_save=false, is_show=true, use_tex=false)Makie backend implementation of plot_distribution.
Arguments
y_coordinates_list: List of spanwise coordinatesresults_list: List of result dictionarieslabel_list: List of labels for different results
Keyword arguments
title: Plot title (default: "spanwise_distribution")data_type: File extension (default:nothing; delegated tosave_plotbackend-aware default)save_path: Path to save plots (default: nothing)is_save: Whether to save (default: false)is_show: Whether to display (default: true)use_tex: Ignored for Makie (default: false)
VortexStepMethod.plot_polars — Function
plot_polars(solver_list, body_aero_list, label_list; kwargs...)Plot polar data comparing different solvers and configurations. Routes to the active plotting backend (Makie or ControlPlots).
Arguments
solver_list: list of aerodynamic solversbody_aero_list: list ofBodyAerodynamicsobjectslabel_list: list of labels for each configuration
Keyword arguments
literature_path_list: optional paths to literature data CSV files (default:String[])angle_range: range of angles to analyze in degrees (default:range(0, 20, 2))angle_type:"angle_of_attack"or"side_slip"(default:"angle_of_attack")angle_of_attack: AoA for the polar sweep (default:0.0) [°]side_slip: side slip angle (default:0.0) [°]v_a: apparent wind speed magnitude (default:10.0) [m/s]title: plot title (default:"polar")data_type: file extension for saving (default depends on backend)save_path: path to save plots (default:nothing)is_save: whether to save (default:true)is_show: whether to display (default:true)use_tex: use externalpdflatexfor rendering (default:false; ignored by Makie)cl_over_cd: plot CL/CD vs angle instead of CL vs CD (default:true)
plot_polars(solver_list, body_aero_list, label_list, ::MakieBackend;
literature_path_list=String[],
angle_range=range(0, 20, 2), angle_type="angle_of_attack",
angle_of_attack=0.0, side_slip=0.0, v_a=10.0,
title="polar", data_type=nothing, save_path=nothing,
is_save=true, is_show=true, use_tex=false)Makie backend implementation of plot_polars.
Arguments
solver_list: List of aerodynamic solversbody_aero_list: List of wing aerodynamics objectslabel_list: List of labels for each configuration
Keyword arguments
literature_path_list: Optional paths to literature data filesangle_range: Range of angles in degreesangle_type: "angleofattack" or "sideslip" (default: angleof_attack)angle_of_attack: AoA [°] (default: 0.0)side_slip: Side slip angle [°] (default: 0.0)v_a: Wind speed [m/s] (default: 10.0)title: Plot titledata_type: File extension (default:nothing; delegated tosave_plotbackend-aware default)save_path: Path to save (default: nothing)is_save: Whether to save (default: true)is_show: Whether to display (default: true)use_tex: Ignored for Makie (default: false)cl_over_cd: Plot CL/CD vs angle instead of CL vs CD (default: true)
VortexStepMethod.plot_polar_data — Function
plot_polar_data(body_aero::BodyAerodynamics; kwargs...)Plot polar data (Cl, Cd, Cm) as 3-D surfaces against angle of attack and trailing edge deflection. Routes to the active plotting backend (Makie or ControlPlots).
Arguments
body_aero: theBodyAerodynamicsto plot (must usePOLAR_MATRICESaero model)
Keyword arguments
alphas: AoA values in radians (default:deg2rad.(-5:0.3:25))delta_tes: trailing edge deflection angles in radians (default:deg2rad.(-5:0.3:25))is_show: whether to display (default:true)use_tex: use externalpdflatexfor rendering (default:false; ignored by Makie)
plot_polar_data(body_aero::BodyAerodynamics, ::MakieBackend;
alphas=collect(deg2rad.(-5:0.3:25)),
delta_tes=collect(deg2rad.(-5:0.3:25)),
is_show=true, use_tex=false)Makie backend implementation of plot_polar_data.
Arguments
body_aero: Wing aerodynamics struct
Keyword arguments
alphas: Range of AoA values in radians (default:deg2rad.(-5:0.3:25))delta_tes: Range of trailing edge angles in radians (default:deg2rad.(-5:0.3:25))is_show: Whether to display (default: true)use_tex: Ignored for Makie (default: false)
VortexStepMethod.plot_combined_analysis — Function
plot_combined_analysis(solver, body_aero, results; kwargs...)Create a combined analysis by calling plot_geometry, plot_distribution, and plot_polars in sequence. Routes to the active plotting backend (Makie or ControlPlots).
Arguments
solver: solver or vector of solversbody_aero:BodyAerodynamicsobject or vector thereofresults: results dictionary (or vector) fromsolve!
Keyword arguments
solver_label: label string for the solver (backward-compatible alias forlabels)labels: optional label string or vectorangle_range: range of angles for polar plots (default:range(0, 20, length=20))angle_type:"angle_of_attack"or"side_slip"(default:"angle_of_attack")angle_of_attack: AoA in degrees (default:0.0)side_slip: side slip angle in degrees (default:0.0)v_a: wind speed in m/s (default:10.0)title: overall figure title (default:"Combined Analysis")view_elevation: geometry view elevation in degrees (default:15)view_azimuth: geometry view azimuth in degrees (default:-120)is_show: whether to display (default:true)use_tex: use externalpdflatexfor rendering (default:false; ignored by Makie)literature_path_list: paths to literature CSV files (default:String[])data_type: file extension for saving (default depends on backend)save_path: directory to save files (default:nothing)is_save: whether to save (default:false)cl_over_cd: plot CL/CD vs angle (default:true)
plot_combined_analysis(solver, body_aero, results, ::MakieBackend;
solver_label="VSM",
angle_range=range(0,20,length=20),
angle_type="angle_of_attack",
angle_of_attack=0.0, side_slip=0.0, v_a=10.0,
title="Combined Analysis",
view_elevation=15, view_azimuth=-120,
is_show=true, use_tex=false,
literature_path_list=String[],
data_type=".png", save_path=nothing, is_save=false)Makie backend implementation of plot_combined_analysis.
Arguments
solver: Aerodynamic solverbody_aero: BodyAerodynamics objectresults: Solution dictionary from solve()
Keyword arguments
labels: Optional label string or label vector. If a vector with lengthlength(solver)+length(literature_path_list)is provided, the tail is used for literature labels; otherwise literature labels default to file basenames.solver_label: Backward-compatible alias forlabels.angle_range: Range of angles for polars (default: range(0,20,length=20))angle_type: "angleofattack" or "sideslip" (default: "angleof_attack")angle_of_attack: AoA in degrees (default: 0.0)side_slip: Side slip in degrees (default: 0.0)v_a: Wind speed in m/s (default: 10.0)title: Overall figure title (default: "Combined Analysis")view_elevation: Geometry view elevation in degrees (default: 15)view_azimuth: Geometry view azimuth in degrees (default: -120)is_show: Display figure (default: true)use_tex: Ignored for Makie (default: false)literature_path_list: Paths to literature CSV files (default: String[])data_type: File extension (default: ".png", also supports ".jpeg")save_path: Directory path to save files (default: nothing)is_save: Save plots to files (default: false)cl_over_cd: Plot CL/CD vs angle instead of CL vs CD (default: true)angle_of_attack_for_spanwise_distribution: AoA for spanwise plots (default: 5.0)
Helper Functions
VortexStepMethod.save_plot — Function
save_plot(fig, save_path, title; data_type=nothing)Save a Makie figure to a file.
Arguments
fig: Makie Figure objectsave_path: Path to save the plottitle: Title of the plot
Keyword arguments
data_type: File extension. Ifnothing, defaults to".pdf"when the active Makie backend is CairoMakie and".png"otherwise.
VortexStepMethod.show_plot — Function
show_plot(fig; dpi=130)Display a Makie figure.
Arguments
fig: Makie Figure object
Keyword arguments
dpi: Dots per inch for the figure (default: 130) - currently unused in Makie