API reference

This page provides a detailed reference for all public functions exported by SymbolicAWEModels.jl.

High-level simulation functions

These functions provide convenient wrappers for running common simulation scenarios.

SymbolicAWEModels.sim!Function
sim!(sam, set_values; dt, total_time, vsm_interval,
     prn, lin_model, y_op)

Run a generic simulation with a matrix of control inputs. Optionally, also simulate a provided linear model.

Arguments

  • sam::SymbolicAWEModel: Initialized AWE model.
  • set_values::Matrix{Float64}: Control torque offsets [Nm] per step. Rows = steps, columns = winches.

Keywords

  • dt: Time step [s]. Default 1/sam.set.sample_freq.
  • total_time: Simulation duration [s]. Default 10.0.
  • vsm_interval: Steps between VSM updates. Default 3.
  • prn: Print performance summary. Default true.
  • lin_model: Optional StateSpace for linear comparison.
  • y_op: Operating point output vector. Required when lin_model is provided.

Returns

  • (SysLog, Nothing) or (SysLog, SysLog) when lin_model is provided.
source
SymbolicAWEModels.sim_reposition!Function
sim_reposition!(sam; dt, total_time, reposition_interval_s, target_elevation_deg,
                target_azimuth_deg, prn)

Run a simulation that periodically resets the kite's elevation and azimuth.

This function simulates the AWE model and, at a specified time interval, calls reposition! to reposition the kite to a target elevation and azimuth. It logs the entire simulation and returns a SysLog.

Arguments

  • sam::SymbolicAWEModel: Initialized AWE model.

Keywords

  • dt::Float64: Time step [s]. Defaults to 1/sam.set.sample_freq.
  • total_time::Float64: Total simulation duration [s]. Defaults to 20.0.
  • reposition_interval_s::Float64: The interval in seconds at which to reset the pose. Defaults to 5.0.
  • target_elevation::Float64: The target elevation in rad for repositioning. Defaults to deg2rad(45.0).
  • target_azimuth::Float64: The target azimuth in rad for repositioning. Defaults to 0.0.
  • target_heading::Float64: The target heading in rad for repositioning. Defaults to 0.0.
  • prn::Bool: If true, prints status messages during the simulation. Defaults to true.

Returns

  • SysLog: A log of the complete simulation.
source

Low-level simulation and analysis

These functions provide direct control over the simulation and tools for model analysis.

KiteUtils.init!Function
init!(sam::SymbolicAWEModel; ...)

Load or build the symbolic model, create the ODEProblem (and optionally the LinearizationProblem / control functions), serialize new builds to disk, and return a freshly initialized ODEIntegrator.

Keyword Arguments

  • solver, adaptive: ODE solver and time-stepping mode. solver=nothing picks a default from sam.set.solver.
  • autodiff: automatic-differentiation choice for the default solver's Jacobian. Defaults to default_autodiff of the backend. Ignored when solver is passed explicitly.
  • linsolve: factorization the default solver uses. Defaults to default_linsolve of the backend. Ignored when solver is passed explicitly.
  • prn: print progress messages.
  • remake: force a full rebuild, ignoring any cached compiled model. nothing rebuilds when a custom winch/aero component is present (see has_custom_component) and reuses the cache otherwise.
  • reload: force reloading the serialized model from disk.
  • outputs: vector of output variables (used by linearization / control funcs).
  • create_prob, create_lin_prob, create_control_func: which artefacts to build.
  • lin_vsm: linearize the VSM aerodynamics after init.
  • remake_vsm: rebuild the VSM wing/aero from settings (after editing aero_geometry.yaml etc.).
  • reset_vel, ignore_l0: forwarded to reinit!(sys_struct, set).
  • reinit_sys: run reinit!(sys_struct, set) to refresh positions, lengths, and transforms. false preserves manual adjustments to the SystemStructure.
  • reset_integrator: discard the existing integrator and build a fresh one, so no stale BDF history taints the next run.
  • vsm_min_wind=0.5: minimum |va| [m/s] for the initial VSM solve. Below this the solve is skipped and the wing's aero outputs are zeroed.
  • sparse: give the solver a Jacobian sparsity pattern, so the finite-difference Jacobian is coloured and its factorization sparse. The MonolithBackend takes MTK's structural pattern, the KernelBackend derives its own from the wiring (state_sparsity). Part of the serialized model's name, so sparse and dense builds are cached separately. nothing takes the backend's default_sparse.
  • analytic_jacobian=nothing: give the solver a Jacobian instead of letting it differentiate the right-hand side numerically. nothing takes the backend's default_analytic_jacobian. Part of the serialized model's name, as sparse is.
source
KiteUtils.next_step!Function
next_step!(s::SymbolicAWEModel, integrator::ODEIntegrator; set_values, dt, vsm_interval)

Take a simulation step, using the provided integrator.

source
next_step!(s::SymbolicAWEModel; set_values, dt,
           vsm_interval, vsm_min_wind, vsm_warn_on_fail)

Advance the simulation by one time step, optionally updating control inputs and re-linearizing the VSM model, then update the SystemStructure from the ODE integrator. Errors on an unstable solver retcode.

Keyword Arguments

  • set_values=nothing: Control input values. If nothing, current values are used.
  • dt=1/s.set.sample_freq: Time step size [s].
  • vsm_interval=1: Steps between VSM re-linearization. 0 disables re-linearization.
  • vsm_min_wind=0.5: Minimum apparent wind [m/s] for a VSM solve. Below this the solve is skipped and the wing's aero outputs are zeroed.
  • vsm_warn_on_fail=false: Warn instead of erroring when a VSM solve fails, keeping the circulation, the angles of attack and the frozen forces of the last converged solve. The next scheduled update solves again.
source
SymbolicAWEModels.find_steady_state!Function
find_steady_state!(s::SymbolicAWEModel, integ=s.integrator; t=1.0, dt=1/s.set.sample_freq)

Run the simulation for a short period to allow the system to settle.

During this period, the winches are braked and the wing's elevation and azimuth angles are fixed, but it is free to move radially (in distance). This allows the dynamic components of the bridle and tethers to settle into a stable, steady-state equilibrium before starting a maneuver or analysis.

Arguments

  • s::SymbolicAWEModel: The model to be stabilized.
  • integ: The integrator to use. Defaults to s.integrator.

Keywords

  • t::Float64=1.0: The duration [s] for which to run the settling simulation.
  • dt::Float64: The time step [s] for the settling simulation.
source
SymbolicAWEModels.linearize!Function
linearize!(s::SymbolicAWEModel; set_values=s.get_set_values(s.integrator)) -> LinType

Compute the full state-space linearization of the model around the current operating point, from the LinearizationProblem generated by ModelingToolkit.jl.

Arguments

  • s::SymbolicAWEModel: The model to linearize.

Keywords

  • set_values: The control input vector u around which to linearize.

Returns

  • LinType: A NamedTuple (A, B, C, D) containing the state-space matrices.
source
SymbolicAWEModels.position_slotsFunction
position_slots(sys_struct) -> NamedTuple

Index layout of a SysState's X/Y/Z position arrays for this model: structural points, then VSM panel_corners, then wings origins, then standalone rigid bodies origins. Each field is the UnitRange of slots for that group (empty if none); total is the position count. Orientation frames (orients) are laid out wings-first, then bodies — i.e. wing w uses frame w, rigid body b uses frame n_wings + b.

slots = position_slots(sam.sys_struct)
sam.sys_struct.bodies[2]  # logged at X/Y/Z slot slots.bodies[2]
source
SymbolicAWEModels.check_live_polarFunction
check_live_polar(mode::AeroPressure, wing; panel_idx=nothing) -> NamedTuple

XFoil against the live polar one panel is flying, at the state the model is in now: the panel's own deformed shape, its converged angle of attack and its Reynolds. panel_idx defaults to the panel NeuralFoil is least confident about, which is the one worth asking about. See AirfoilAero.compare_live_polar for what comes back.

The Live polar off the trained shape range warning says the network is extrapolating. This says whether the extrapolation is any good, which the confidence cannot: it scores the input shape, not the answer. One viscous solve, so it is cheap enough to call from the REPL mid-run and far too slow to call every step.

source

YAML loading

SymbolicAWEModels.load_sys_struct_from_yamlFunction
load_sys_struct_from_yaml(yaml_path; system_name, set, ...)

Build a SystemStructure from a component-based structural YAML file. See source for full documentation of expected blocks. wind_mode=PerPointWind() builds a structure whose points carry their own settable wind. prn=false silences the transform, wing-frame and aero-setup messages loading a structure prints.

source

System configuration

SymbolicAWEModels.set_world_frame_dampingFunction
set_world_frame_damping(sys::SystemStructure, damping[, point_idxs])
set_world_frame_damping(components::AbstractVector, damping[, idxs])

Set the world-frame damping coefficient, on sys.points by default or on any component vector given directly — pass sys.bodies to damp the rigid bodies of a beam wing.

World-frame damping applies a velocity-dependent drag in the global reference frame: $\mathbf{a}_{damp} = -c_{damp} \odot \mathbf{v}$, where $c_{damp}$ is the per-mass damping vector [1/s] and $\odot$ is element-wise multiplication. A scalar damping applies to all three axes.

source
SymbolicAWEModels.set_body_frame_dampingFunction
set_body_frame_damping(sys::SystemStructure, damping[, point_idxs])
set_body_frame_damping(components::AbstractVector, damping[, idxs])

Set the body-frame damping coefficient, on sys.points by default or on any component vector given directly — pass sys.bodies to damp the rigid bodies of a beam wing.

The velocity is resolved on the body axes before damping, so a per-axis coefficient damps selectively: [0, 0, 20] on a wing body resists motion normal to the wing without slowing flight along it. Coefficients are per-mass [1/s]; a scalar applies to all three axes.

source
SymbolicAWEModels.set_angular_dampingFunction
set_angular_damping(bodies::AbstractVector, damping[, idxs])

Set the angular damping coefficient of rigid bodies: the spin is resolved on the body's own axes and damped per axis, dω/dt -= c .* ω, so [0, 20, 0] resists rotation about the body y axis alone. Coefficients are [1/s]; a scalar applies to all three axes.

Unlike the joint Rayleigh damping this resists the body's absolute spin, so it brakes rigid rotation of whatever the body belongs to as well as deformation.

source

Wind

Where a model takes its wind from is a WindMode on its SystemStructure. The default ProfileWind scales the ground wind set.wind_vec by the height profile set.profile_law. PerPointWind instead gives every point its own wind vector, settable between steps.

sys_struct = load_sys_struct_from_yaml(path; set, wind_mode=PerPointWind())
sam = SymbolicAWEModel(set, sys_struct)
init!(sam)
for _ in 1:steps
    for point in sam.sys_struct.points    # a gust field, refreshed every step
        point.wind_vec .= my_wind_field(point.pos_w, sam.integrator.t)
    end
    sam.sys_struct.wings[1].wind_vec .= my_wind_field(...)
    next_step!(sam)
end

Each point's own drag and apparent wind read its wind_vec directly, and a segment's tether drag the mean of its two endpoints — the same averaging its drag already applies to their velocities. A wing reads its own wing.wind_vec, which is the wind a rigid wing's aerodynamics fly in; the per-point winds reach the VSM panels through the per-point inflow the panels already interpolate. init! seeds every point and wing with set.wind_vec, so a model that is never written to flies in a uniform wind.

SymbolicAWEModels.ProfileWindType
ProfileWind()

The default wind mode: the ground wind set.wind_vec scaled by the height profile set.profile_law of AtmosphericModels, evaluated at each consumer's own height.

source
SymbolicAWEModels.PerPointWindType
PerPointWind()

Wind mode in which every point carries its own wind vector: point.wind_vec becomes a parameter, settable between steps, instead of an output of the height profile. A segment takes the mean of its two endpoints' winds and a wing reads its own wing.wind_vec; set.profile_law is unused. reinit! seeds every point and wing with set.wind_vec, so a model that is never written to flies in a uniform wind.

source

Winch components

The winch motor model is a pluggable AbstractWinchModel struct. Pass one to Winch(...; model=...) to override the default torque-driven motor (TorqueWinch). The builder is selected by dispatch on the model type, winch_component; custom components must respect the connector contract enforced by validate_winch_component.

SymbolicAWEModels.winch_componentFunction
winch_component(model::AbstractWinchModel, sys_struct, winch_idx; name, params)

Build the winch motor subsystem for sys_struct.winches[winch_idx], selected by dispatch on the winch's model. The component is pure-algebraic at its connector boundary (see validate_winch_component) but may declare arbitrary internal D(x) ~ … states. Common drum parameters are read live via the flat params view (params.winches[winch_idx].gear_ratio etc.); a model's own extra fields go through params.winches[winch_idx].model.your_field. Add a method on a custom AbstractWinchModel subtype to plug in your own dynamics.

source
SymbolicAWEModels.validate_winch_componentFunction
validate_winch_component(subsys, winch)

Check that subsys (built by winch_component) satisfies the connector contract.

Required connector variables:

  • vel (input, drum-perimeter velocity [m/s])
  • len (input, mean of connected tether lengths [m])
  • force (input, summed tether tension magnitude [N])
  • set_value (input, abstract setpoint; component fixes meaning)
  • brake (input, brake in [0, 1])
  • acc (output, drum-perimeter acceleration [m/s²])
  • friction (output, friction torque [N·m])

Forbidden:

  • Equations whose LHS is D(vel) or D(len) (those derivatives belong to the outer SymbolicAWEModels system). Internal D(x) ~ … for any other variable is allowed.
source

State accessor functions

Use these functions to retrieve state information and calculated values from a model instance.

SymbolicAWEModels.segment_stretch_statsFunction
segment_stretch_stats(sys::SystemStructure)

Maximum and mean relative stretch (len - l0) / l0 over the segments in tension (len > l0), plus the index of the most stretched one.

For pulley segments, the combined length of both legs is used against the pulley's sum_l0, since the pulley constraint distributes length between them.

Arguments

  • sys::SystemStructure: System structure with current segment states

Returns

  • (max_stretch, mean_stretch, max_idx): Tuple of maximum stretch, mean stretch, and index of the segment with maximum stretch (or first pulley segment index)
source

Visualization functions

SymbolicAWEModels provides plotting functionality through a package extension that loads once a Makie backend and MakieControlPlots are both available.

3D system visualization

Plot the 3D structure of the system with interactive features:

import GLMakie
using MakieControlPlots
plot(sys::SystemStructure; kwargs...)

Keyword arguments:

  • vector_scale::Float64=1.0: Length scale of the force/orientation arrows
  • force_color::Bool=false: Colour segments by tension instead of segment_color
  • segment_color=RGBf(0.25, 0.25, 0.25): Default colour for segments
  • relmargin::Float64=0.2: Margin around the system, as a fraction of its extent
  • body_frame: Track a wing body frame with the camera (defaults on with aero_mapping)
  • zoom, pan_horizontal, pan_vertical, tilt_horizontal, tilt_vertical: Camera placement
  • plot_aero::Bool=true: Draw the aerodynamic geometry of each wing
  • extra_points, extra_groups, mesh: Extra geometry to overlay

Remaining keywords are forwarded to plot!, including:

  • show_points, show_segments, show_orient, show_beams: Layer visibility
  • transparency::Bool=true: Order-independent transparency; false is much faster
  • aero_mapping::Bool=false: Overlay the AeroPressure station→point map
  • linewidth, point_size, beam_color, airfoil_color, …: Styling

Interactive features:

  • Hover over segments to highlight them
  • Click on a segment to zoom in
  • Click in empty space to zoom out
  • Rotate, pan, and zoom with mouse

Time-series visualization

Plot simulation results as multi-panel time-series:

plot(sys::SystemStructure, log::SysLog; kwargs...)

Keyword argumentsplot_default::Bool=true switches the default panel set on or off as a group; the panels below default to plot_default:

  • plot_reelout: Reel-out velocities of the steering winches
  • plot_aero_force: z-component of the aerodynamic force
  • plot_aoa: Angle of attack
  • plot_heading: Heading and course angles
  • plot_winch_force: Winch forces

Opt-in panels (all default false): plot_twist, plot_turn_rates, plot_turn_radius, plot_aero_moment, plot_tether_moment, plot_tether, plot_tether_actual, plot_v_app, plot_elevation, plot_azimuth, plot_distance, plot_yaw_rate, plot_cone_angle, plot_old_heading, plot_kiteutils_course, plot_set_values.

Appearance: suffix::String=" - " * sys.name, size::Tuple=(1200, 800), label_fontsize::Int=16, ticklabelsize::Int=12, legendsize::Int=10, and the per-panel limits aoa_ylims, gk_ylims, turn_radius_ylims.

Passing a Vector{SysLog} instead of a single log overlays several runs on the same panels for comparison.

Extension loading

The plot functions become available once both a Makie backend and MakieControlPlots are loaded — using GLMakie on its own is not enough. plot extends MakieControlPlots.plot, the generic of the figure-returning plot commands; the scene-mutating plot! extends Makie.plot!. A Makie backend exports a plot of its own, so load the backend with import GLMakie rather than using GLMakie to keep the name unambiguous.

Inflated-tube rigidity laws

Rigidities for the TimoshenkoJoints of a beam wing whose leading edge and struts are pressurised fabric tubes. tube_bending_law and tube_torsion_law come from the empirical Breukels correlations; comer_levy_bending_law is the analytical alternative that stays valid past collapse but needs the fabric membrane stiffness E·t, which breukels_membrane_stiffness can supply.

SymbolicAWEModels.tube_linear_rigiditiesFunction
tube_linear_rigidities(radius, pressure) -> (EA, GA, EI0, GJ0)

Small-deformation rigidities of an inflated tube from the Breukels laws: GJ0 = c1·c2, shear modulus from GJ0 and the section (J = πr⁴/2, A = πr²), EI0 from the initial slope of the tip-force curve with the shear share of the deflection removed, and EA = EI0·A/I.

source
SymbolicAWEModels.tube_bending_lawFunction
tube_bending_law(radius, pressure; n_samples=120) -> TubeRigidityLaw

Breukels bending rigidity law EI(κ) of an inflated tube (radius [m], pressure [bar]): moment-curvature samples from the tip-force curve (curvature κ = 3·(δ - δ_shear) of the 1 m cantilever), knee seeded at half the collapse moment, and the post-knee exponent from a least-squares fit of M_c - M = (M_c - M_knee)·(κ_knee/κ)^a on the sampled tail. The knee is a fitting anchor, not a tangency — the Breukels curve never runs at EI0 — so the law is C0 there, not C1.

source
SymbolicAWEModels.tube_torsion_lawFunction
tube_torsion_law(radius, pressure) -> TubeRigidityLaw

Breukels torsional rigidity law GJ(κ) of an inflated tube (radius [m], pressure [bar]). Errors when the correlation leaves its fitted range and returns a non-positive c1 or c2; c2 does so for a large radius above 1 bar, where log(pressure) changes sign.

source
SymbolicAWEModels.breukels_tip_forceFunction
breukels_tip_force(deflection, radius, pressure) -> Float64

Tip force [N] of a 1 m inflated-tube cantilever at normalized tip deflection, per the Breukels correlation (radius [m], pressure [bar]).

source
SymbolicAWEModels.breukels_membrane_stiffnessFunction
breukels_membrane_stiffness(radius, pressure_bar) -> Float64

Membrane stiffness E·t [N/m] implied by the Breukels linear bending rigidity, E·t = EI0_breukels / (π·r³) (inverting EI0 = E·t·π·r³). This sources the Comer-Levy fabric input from the same empirical law the model already uses, so the linear regime is unchanged and only the pressure-driven collapse branch is added. radius [m], pressure_bar [bar]. Trustworthy only in Breukels' fitted range (radius ≲ 0.15 m).

source
SymbolicAWEModels.comer_levy_bending_stiffnessFunction
comer_levy_bending_stiffness(radius, membrane_stiffness) -> Float64

Pre-wrinkling bending stiffness EI = E·t·π·r³ [N·m²] (Fichter) of a thin-walled inflated tube of radius [m] and membrane stiffness membrane_stiffness (E·t, [N/m]). Pressure enters the wrinkling/collapse limits, not this small-deflection slope.

source
SymbolicAWEModels.comer_levy_wrinkling_momentFunction
comer_levy_wrinkling_moment(radius, pressure) -> Float64

Wrinkling-onset moment M_w = p·π·r³/2 [N·m] (radius [m], pressure [Pa]): the compressed fibre's pressure pre-stress is cancelled and the fabric first goes slack (Comer-Levy slack angle θ₀ = 0).

source
SymbolicAWEModels.comer_levy_bending_lawFunction
comer_levy_bending_law(radius, pressure, membrane_stiffness; n_samples=60)
    -> TubeRigidityLaw

Curvature-softening bending rigidity EI(κ) of an inflated tube from the analytical Comer-Levy wrinkled section (radius [m], pressure [Pa], membrane_stiffness E·t [N/m]): linear EI0 = E·t·π·r³ below the wrinkling knee, then the smooth power-law approach to the collapse moment M_c = p·π·r³ fitted from the closed-form section curve. The returned TubeRigidityLaw :bending callable is the drop-in replacement for tube_bending_law and, unlike it, stays valid past collapse (moment saturates at M_c, rigidity → 0). C1 across the knee, where the section itself has dM/dκ → EI0.

source
SymbolicAWEModels.membrane_linear_rigiditiesFunction
membrane_linear_rigidities(radius, membrane_stiffness) -> (EA, GA, EI0)

Thin-walled inflated-tube axial, shear and bending rigidities from the membrane stiffness membrane_stiffness (E·t [N/m]) and radius [m], all with one consistent provenance: EA = E·t·2πr, GA = G·t·2πr with G·t = E·t / (2(1 + ν)) (TUBE_POISSON_RATIO), and EI0 = E·t·π·r³. The Comer-Levy counterpart of tube_linear_rigidities (Breukels); torsion GJ is not covered here and stays on tube_torsion_law.

source
SymbolicAWEModels.frame_quaternionFunction
frame_quaternion(x_dir, ref) -> Vector{Float64}

Quaternion of the right-handed frame with along x_dir and along the component of ref orthogonal to (ref must not be parallel to x_dir).

source
SymbolicAWEModels.frame_quaternion_xyFunction
frame_quaternion_xy(x_dir, y_ref) -> Vector{Float64}

Quaternion of the right-handed frame with along x_dir (kept exact) and ŷ along the component of y_ref orthogonal to , so ẑ = x̂ × ŷ. Use when the primary axis is x — e.g. a node body whose is the strut/chord direction and ŷ the in-plane span, matching flap_axis=[0,1,0]. y_ref must not be parallel to x_dir.

source

Unsteady aerodynamics

SymbolicAWEModels.apply_apparent_mass!Function
apply_apparent_mass!(sys_struct, wing, rho)

Give the wing's nodes the air they entrain, scaled by the wing's UnsteadyAero apparent_mass. Each panel's panel_apparent_mass is spread over the nodes by the weights that already carry its force (aero_scatter_entries), so the air lands where the lift does. It resists acceleration without weighing anything, which is why it is a field of its own and not extra_mass. Always clears the carriers first, so turning the correction off and rebuilding leaves no stale inertia behind.

The air goes to whatever actually integrates the node's translation (apparent_mass_carriers): the node itself when it is a free particle, and the body or pair of beam bodies that place it when it is not — which is every node of a beam wing, whose mass and motion live in those bodies.

source

Utility and helper functions

SymbolicAWEModels.init_moduleFunction
init_module(; force=false, add_pkg=true)

Initialize the module in the current working directory.

This function performs the following actions:

  • Copies all files from the module's data directory to the current working directory's data folder (pwd()/data). Existing files in the destination are NOT overwritten unless force=true.
  • Copies all example scripts from the module to the current working directory's examples folder (pwd()/examples). The folder is created if it does not exist. Existing files are NOT overwritten unless force=true.
  • Installs all required packages if they are not already installed. This occurs only if add_pkg=true (default). The packages are automatically determined from examples/Project.toml.

Keyword Arguments

  • force::Bool=false: If true, existing files in the destination directories will be overwritten. If false (default), existing files will be preserved.
  • add_pkg::Bool=true: If true (default), installs required packages if they are not already present. If false, package installation is skipped.
source