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:

  • wing::Wing: The Wing to which a section shall be added
  • LE_point::PosVector: PosVector of the point on the side of the leading edge
  • TE_point::PosVector: PosVector of the point on the side of the trailing edge
  • aero_model::AeroModel: AeroModel
  • aero_data::AeroData: See AeroData
source
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 aerodynamics

Distribution Methods

  • LINEAR: Linear interpolation between sections
  • COSINE: Cosine spacing (more panels near tips)
  • SPLIT_PROVIDED: Split each unrefined section into sub-panels
  • UNCHANGED: 1:1 copy when nunrefinedsections == n_panels+1

Keyword Arguments

  • recompute_mapping::Bool=true: Recompute the mapping from refined panels to unrefined sections
  • sort_sections::Bool=true: Sort sections by spanwise position using global LE_point[2] (Y-axis). Disable for structural ordering.

Effects

  1. Populates wing.refined_sections (n_panels+1 sections)
  2. Populates wing.non_deformed_sections (copy of refined_sections for deformation reference)
  3. Computes wing.refined_panel_mapping (panel → unrefined section mapping)
  4. Resizes wing.theta_dist and wing.delta_dist to 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)
source
VortexStepMethod.load_polar_dataFunction
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, or nothing if 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_VECTORS if data is loaded, or INVISCID if 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
)
source

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]
source
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 modify
  • settings::VSMSettings: Settings object containing flight conditions

Example

settings = VSMSettings("path/to/settings.yaml")
body_aero = BodyAerodynamics([wing])
set_va!(body_aero, settings)
source
CommonSolve.solveFunction
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.

source
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

source
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 not
  • va=[15.0, 0.0, 0.0]: Apparent wind vector
  • omega=zeros(3): Turn rate in kite body frame x y and z

Returns

nothing

source
VortexStepMethod.linearizeFunction
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.

source
VortexStepMethod.calculate_resultsFunction
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

source

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_geometryFunction
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

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 external pdflatex for rendering (default: false; ignored by Makie)
source
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 plot
  • title: plot title

Keyword arguments:

  • data_type: File extension (default: nothing; delegated to save_plot backend-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)
source
VortexStepMethod.plot_distributionFunction
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 arrays
  • results_list: list of result dictionaries from solve!
  • 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 external pdflatex for rendering (default: false; ignored by Makie)
source
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 coordinates
  • results_list: List of result dictionaries
  • label_list: List of labels for different results

Keyword arguments

  • title: Plot title (default: "spanwise_distribution")
  • data_type: File extension (default: nothing; delegated to save_plot backend-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)
source
VortexStepMethod.plot_polarsFunction
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 solvers
  • body_aero_list: list of BodyAerodynamics objects
  • label_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 external pdflatex for rendering (default: false; ignored by Makie)
  • cl_over_cd: plot CL/CD vs angle instead of CL vs CD (default: true)
source
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 solvers
  • body_aero_list: List of wing aerodynamics objects
  • label_list: List of labels for each configuration

Keyword arguments

  • literature_path_list: Optional paths to literature data files
  • angle_range: Range of angles in degrees
  • angle_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 title
  • data_type: File extension (default: nothing; delegated to save_plot backend-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)
source
VortexStepMethod.plot_polar_dataFunction
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

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 external pdflatex for rendering (default: false; ignored by Makie)
source
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)
source
VortexStepMethod.plot_combined_analysisFunction
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 solvers
  • body_aero: BodyAerodynamics object or vector thereof
  • results: results dictionary (or vector) from solve!

Keyword arguments

  • solver_label: label string for the solver (backward-compatible alias for labels)
  • labels: optional label string or vector
  • angle_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 external pdflatex for 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)
source
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 solver
  • body_aero: BodyAerodynamics object
  • results: Solution dictionary from solve()

Keyword arguments

  • labels: Optional label string or label vector. If a vector with length length(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 for labels.
  • 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)
source

Helper Functions

VortexStepMethod.save_plotFunction
save_plot(fig, save_path, title; data_type=nothing)

Save a Makie figure to a file.

Arguments

  • fig: Makie Figure object
  • save_path: Path to save the plot
  • title: Title of the plot

Keyword arguments

  • data_type: File extension. If nothing, defaults to ".pdf" when the active Makie backend is CairoMakie and ".png" otherwise.
source
VortexStepMethod.show_plotFunction
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
source