Private API
This page documents the internal functions and types of SymbolicAWEModels.jl. These are not part of the public API and may change without notice. They are listed here for developers and for those interested in the model's internal workings.
Core types and constructors
SymbolicAWEModels.SerializedModel — Type
@with_kw mutable struct SerializedModel{...}A type-stable container for the compiled and serialized components of a SymbolicAWEModel.
This struct holds the products of the ModelingToolkit.jl compilation process, now organized into nested attribute structs (ProbWithAttributes, etc.). This simplifies the structure and improves serialization robustness.
set_hash::Vector{UInt8}sys_struct_hash::Vector{UInt8}full_sys::Union{Nothing, ModelingToolkitBase.System}: Unsimplified system of the mtk model Default: nothingdefaults::AbstractVector: Default: Pair{Num, Any}[]guesses::AbstractVector: Default: Pair{Num, Any}[]inputs::Union{Symbolics.Arr, Vector{Symbolics.Num}}: Symbolic representation of the control inputs. Default: Num[]outputs::Union{Symbolics.Arr, Vector{Symbolics.Num}}: Outputs of the linearization and control function. Default: Num[]prob::Union{Nothing, SymbolicAWEModels.ProbWithAttributes}: Container for the ODE problem and its getters/setters. Default: nothinglin_prob::Union{Nothing, SymbolicAWEModels.LinProbWithAttributes}: Container for the linearization problem and its components. Default: nothingcontrol_functions::Union{Nothing, SymbolicAWEModels.ControlFuncWithAttributes}: Container for the control functions. Default: nothing
SymbolicAWEModels.SimFloat — Type
const SimFloat = Float64This type is used for all real variables, used in the Simulation. Possible alternatives: Float32, Double64, Dual Other types than Float64 or Float32 do require support of Julia types by the solver.
SymbolicAWEModels.KVec3 — Type
const KVec3 = MVector{3, SimFloat}
Basic 3-dimensional vector, stack allocated, mutable.
SymbolicAWEModels.SVec3 — Type
const SVec3 = SVector{3, SimFloat}
Basic 3-dimensional vector, stack allocated, immutable.
VortexStepMethod.Wing — Type
Wing(name, vsm_aero, vsm_wing, vsm_solver, groups, R_b_to_c, pos_cad; transform=1)Constructs a VSMWing object (backward compatibility constructor).
This is a convenience constructor that creates a VSMWing for backward compatibility with existing code. New code should use VSMWing(...) directly.
Arguments
name::Union{Int, Symbol}: Name/identifier for the wing.vsm_aero,vsm_wing,vsm_solver: Vortex Step Method components.groups::Vector: References to groups attached to this wing (names or indices).R_b_to_c::Matrix{SimFloat}: Rotation matrix from body frame to CAD frame.pos_cad::KVec3: Position of wing center of mass in CAD frame.
Keyword Arguments
transform::Union{Int, Symbol}=1: Reference to the transform.y_damping::SimFloat=150.0: Damping coefficient for lateral motion.
Returns
VSMWing: A new VSM wing object.
SymbolicAWEModels.create_vsm_wing — Function
create_vsm_wing(set::Settings, vsm_set::VortexStepMethod.VSMSettings; prn=true, sort_sections=true)Create a Wing geometry object from the settings provided.
This function checks for .obj and .dat files in the model directory. If found, it uses VortexStepMethod.ObjWing(obj_path, dat_path) to load the wing. Otherwise, it falls back to loading from aero_geometry.yaml.
Reads geometry from the Settings object and initializes the Wing object from VortexStepMethod.jl.
State management and model simplification
SymbolicAWEModels.copy! — Function
copy!(sys1::SystemStructure, sys2::SystemStructure)Copy the dynamic state from one SystemStructure (sys1) to another (sys2).
This function is designed to transfer the state (positions, velocities, etc.) between two system models, which can have different levels of fidelity. For example, it can copy the state from a detailed multi-segment tether model (sys1) to a simplified single-segment model (sys2).
The function handles several cases:
- If
sys1andsys2have the same structure, it performs a direct copy of all point states. - If
sys2is a simplified (1-segment per tether) version ofsys1, it copies the positions and velocities of the tether endpoints. - It also copies the state of wings, groups, winches, and pulleys where applicable.
SymbolicAWEModels.reinit! — Function
reinit!(transforms::AbstractVector{Transform}, sys_struct::SystemStructure;
update_vel=true)Apply transforms to all components in a SystemStructure.
Expects pos_w to already be set (via copy_cad_to_world! and optionally apply_tether_init_stretched_lens! from reinit!(sys_struct, set; ...)). Applies: translate (from pos_w) → azimuth/elevation → heading.
reinit!(sys_struct::SystemStructure, set::Settings; kwargs...)Re-initialize a SystemStructure from a Settings object.
This function resets various component states (e.g., winch lengths, group twists, pulley positions) to their initial values as defined in the Settings object. It is typically called before starting a new simulation run.
Pulley lengths are initialized proportionally based on current segment lengths: pulley.len = segment1.len / (segment1.len+segment2.len) * pulley.sum_len
Keyword Arguments
ignore_l0::Bool=false: If true, recalculate segment rest lengths from current positionsremake_vsm::Bool=false: If true, recreate VSM wing, aerodynamics, and solver from settings. This is useful after modifyingaero_geometry.yamlor other VSM-related configuration files. For PARTICLEDYNAMICS wings, also rebuilds the `pointtovsmpoint` mapping.apply_transforms::Bool=true: If false, skip applying spatial transforms (translate, rotate, heading) during reinitialization.apply_tether_lengths::Bool=true: If false, skip scaling point positions to matchtether.init_stretched_len.prn::Bool=true: If true, print info messages (e.g. when several root tethers are placed to their mean stretched length).
reinit!(sam, prob, solver; kwargs...) -> (ODEIntegrator, Bool)Reset the ODE integrator from new initial conditions without rebuilding the symbolic model. See init! for adaptive, reset_integrator, lin_vsm, and vsm_min_wind.
SymbolicAWEModels.reposition! — Function
reposition!(transforms::AbstractVector{Transform},
sys_struct::SystemStructure)Update the system's spatial orientation based on its current position, preserving velocities.
Unlike reinit!, uses current world positions (pos_w) as the starting point (no reset from CAD coordinates, no tether length scaling). Heading uses the tangential sphere frame, consistent with reinit!.
SymbolicAWEModels.update_sys_struct! — Function
update_sys_struct!(s::SymbolicAWEModel, sys_struct::SystemStructure, integ=s.integrator)Updates the high-level SystemStructure from the low-level integrator state vector.
This function reads the raw state vector from the ODE integrator and uses the generated getter functions to populate the human-readable fields in the SystemStructure. This synchronization step is crucial for making the simulation results accessible.
SymbolicAWEModels.get_set_hash — Function
get_set_hash(set::Settings; fields)Calculates a SHA1 hash for structural fields in the Settings object. This is used to check if a cached compiled model is still valid.
Structural Fields (affect symbolic equations):
:segments: Number of tether segments (affects state vector size):model: Kite model name (affects geometry):foil_file: Airfoil data file (affects VSM setup):physical_model: Model type (ram, simpleram, 4attach_ram):quasi_static: Whether points are quasi-static (affects equations):winch_model: Winch dynamics model (affects winch equations)
Runtime Fields (don't affect compilation, excluded from hash):
:profile_law: Wind profile law (evaluated at runtime via symbolic function):wind_vec,:elevation: Initial conditions- Other runtime parameters
SymbolicAWEModels.get_sys_struct_hash — Function
get_sys_struct_hash(sys_struct::SystemStructure)Calculates a SHA1 hash for the topology and structure of a SystemStructure. This is used to check if a cached compiled model is still valid.
Includes all structural properties that affect the symbolic equations:
- Point connectivity and types (STATIC, DYNAMIC, QUASI_STATIC, WING)
- Segment connectivity
- Group structure and types (FIXED, TWIST)
- Pulley constraints and types
- Tether topology
- Winch configuration
- Wing topology, connectivity, aerodynamic model type (RIGIDDYNAMICS vs PARTICLEDYNAMICS), and aero mode
- Transform hierarchy
Excludes runtime-configurable properties like masses, lengths, stiffnesses.
Physics and geometry helpers
SymbolicAWEModels.calc_angle_of_attack — Function
calc_angle_of_attack(va_wing_b)Calculate the angle of attack [rad] from the apparent wind vector va_wing_b in the body frame.
SymbolicAWEModels.calc_heading — Function
calc_heading(sys::SystemStructure)Calculate heading angles for all wings using the tangential sphere frame method. Returns a vector of heading angles, one per wing.
calc_heading(R_b_to_w, wing_pos)Calculate heading angle using the tangential sphere frame.
Projects the body x-axis onto the tangent plane of the tether sphere at wing_pos. Heading is measured from the elevation direction (xt, away from zenith) toward the azimuthal direction (yt). Heading = 0 when the kite nose points toward the ground station.
SymbolicAWEModels.calc_R_t_to_w — Function
calc_R_t_to_w(wing_pos)Calculate the rotation matrix from the local tether frame (_t) to the world frame (_w).
The tether frame is a local spherical coordinate system:
- z-axis: Aligned with the tether (radial direction).
- y-axis: Azimuthal direction, parallel to the XY plane.
- x-axis: Elevation direction, tangent to the sphere (
y × z).
SymbolicAWEModels.calc_R_v_to_w — Function
calc_R_v_to_w(wing_pos, e_x)Calculate the rotation matrix from the view frame (_v) to the world frame (_w).
The view frame is defined with its z-axis pointing from the origin to the wing, and its x-axis aligned with the wing's x-axis projected onto the view plane.
SymbolicAWEModels.cad_to_body_frame — Function
cad_to_body_frame(wing::Wing, pos)Transform a position from the CAD frame to the wing's body frame.
SymbolicAWEModels.calc_pos — Function
calc_pos(wing::Wing, gamma, frac)Calculate a position on the kite based on spanwise (gamma) and chordwise (frac) parameters.
SymbolicAWEModels.calc_winch_force — Function
calc_winch_force(sys, winch_vel, winch_acc, set_values)Calculate the tensile force on each winch tether from its motion and motor torque.
Arguments
sys::SystemStructure: System structure with winch params.winch_vel: Winch velocities [m/s] per winch.winch_acc: Winch accelerations [m/s²] per winch.set_values: Motor torque inputs [Nm] per winch.
Returns
- Vector of winch forces [N].
SymbolicAWEModels.quaternion_to_rotation_matrix — Function
quaternion_to_rotation_matrix(q)Convert a quaternion q (scalar-first format [w, x, y, z]) to a 3x3 rotation matrix.
SymbolicAWEModels.rotation_matrix_to_quaternion — Function
rotation_matrix_to_quaternion(R)Convert a 3x3 rotation matrix R to a quaternion (scalar-first format [w, x, y, z]). This implementation is based on the method that avoids division by zero.
SymbolicAWEModels.rotate_v_around_k — Function
rotate_v_around_k(v, k, θ)Rotate vector v around axis k by angle θ using Rodrigues' rotation formula.
SymbolicAWEModels.smooth_norm — Function
smooth_norm(v, eps=1e-12)Differentiable norm: sqrt(sum(abs2, v) + eps^2).
SymbolicAWEModels.smooth_normalize — Function
smooth_normalize(vec)Differentiable normalization: vec / smooth_norm(vec).
SymbolicAWEModels.apply_heading — Function
apply_heading(vec, R_t_to_w, curr_R_t_to_w, heading)Apply a heading rotation to a vector.
SymbolicAWEModels.get_rot_pos — Function
get_rot_pos(transform::Transform, wings, points)Get the world position of the rotating object (wing or point).
SymbolicAWEModels.get_base_pos — Function
get_base_pos(transform, transforms, wings, points)Get (base_pos, curr_base_pos) for a transform.
For chained transforms (base_transform): returns the parent's current world position and CAD position, so T = base_pos - curr_base_pos shifts child points by the same displacement the parent transform applied.
For direct transforms (base_pos + base_point): returns the user-specified position and the base point's current position.
SymbolicAWEModels.calc_aoa — Function
calc_aoa(s::SymbolicAWEModel)Calculates the mean angle of attack [rad] over the wingspan from the VSM solver.
Equations and system management
SymbolicAWEModels.create_sys! — Function
create_sys!(s::SymbolicAWEModel, system::SystemStructure; prn=true)Create the full ModelingToolkit.ODESystem for the AWE model.
This is the main top-level function that orchestrates the generation of the entire set of differential-algebraic equations (DAEs). It calls specialized sub-functions to build the equations for each part of the system (forces, wing dynamics, scalar kinematics, linearized aerodynamics) and assembles them into a single System.
Arguments
s::SymbolicAWEModel: The main model object to be populated with the system.system::SystemStructure: The physical structure definition.prn::Bool=true: If true, print progress information during system creation.
Returns
set_values: The symbolic variable representing the control inputs (winch torques).
SymbolicAWEModels.scalar_eqs! — Function
scalar_eqs!(s, eqs, psys; kwargs...)Generate equations for derived scalar kinematic quantities useful for control and analysis.
This includes elevation, azimuth, heading, course, angle of attack, and their time derivatives, as well as apparent wind calculations.
Arguments
s::SymbolicAWEModel: The main model object.eqs,psys: Accumulating vectors and symbolic parameter.kwargs...: Symbolic variables for the system's state.
Returns
eqs: The updated list of system equations.
SymbolicAWEModels.wing_eqs! — Function
wing_eqs!(s, eqs, psys, defaults; kwargs...)Generate the differential equations for the wing's rigid body dynamics.
For RIGID_DYNAMICS wings:
- ODE state:
com_w,com_vel,Q_p_to_w,ω_p(principal frame) - Euler rotation equations in principal frame (diagonal I)
- Newton's 2nd law for COM translation
- Body frame output (
R_b_to_w,wing_pos,ω_b) computed algebraically viaR_b_to_w=R_p_to_w*R_b_to_p(constant)
For PARTICLE_DYNAMICS wings:
- No rigid body dynamics (handled by DYNAMIC points)
R_b_to_wfrom structural ref points- Principal frame variables set to zero/aliases
SymbolicAWEModels.vsm_eqs! — Function
vsm_eqs!(s, eqs, guesses, psys; kwargs...)Generate aerodynamic equations for all wings.
Aero mode is resolved at build time — each wing's aero_mode determines which equations are generated:
AERO_LINEARIZED: wind-axis coefficient equations with Jacobian-based linearization around the operating pointAERO_DIRECT: registered functions returning stored forcesAERO_NONE: zeros
For PARTICLEDYNAMICS wings, per-point forces come from `getpointaeroforce(which also respectsAERO_NONE`).
SymbolicAWEModels.point_eqs! — Function
point_eqs!(s, eqs, defaults, guesses, points, segments, groups, wings, psys;
R_b_to_w, wing_vel, wind_vec_gnd, twist_angle,
pos, vel, acc, point_force, point_mass, spring_force_vec, drag_force, l0,
spring_sum_force, point_drag_force, total_drag,
disturb_force, tether_r, chord_b, fixed_pos, normal, pos_b,
fix_point_sphere, fix_static, body_frame_damping, world_frame_damping,
va_point_b, va_point_w, wind_at_point, height,
aero_force_point_b,
group_y_airf, tether_wing_force, tether_wing_moment)Generate equations for all point types (STATIC, DYNAMIC, QUASI_STATIC, WING).
Arguments
s::SymbolicAWEModel: The main model object (for atmospheric model).eqs,defaults,guesses: Accumulating vectors for the MTK system.points,segments,groups,wings: System components.psys: Symbolic parameter representing the system structure.R_b_to_w: Symbolic rotation matrix (body to world).wing_vel: Symbolic wing center of mass velocity.wind_vec_gnd: Symbolic ground-level wind vector.twist_angle: Symbolic group twist angle.pos,vel,acc: Pre-declared point state variables.point_force,point_mass: Pre-declared point force and mass variables.spring_force_vec,drag_force,l0: Pre-declared segment force variables.spring_sum_force: Pre-declared accumulated spring/drag forces variable.- Other variables: Various point-specific symbolic variables.
tether_wing_force,tether_wing_moment: Mutable arrays to accumulate forces/moments.
Returns
- Tuple
(eqs, defaults, guesses)with updated equation vectors. Note:tether_wing_forceandtether_wing_momentare modified in-place.
SymbolicAWEModels.segment_eqs! — Function
segment_eqs!(s, eqs, guesses, points, segments, pulleys, tethers, wings, psys;
pos, vel, wind_vec_gnd, spring_force_vec, drag_force, l0,
pulley_len, tether_len)Generate equations for segment spring-damper forces and aerodynamic drag.
Arguments
s::SymbolicAWEModel: The main model object (for atmospheric model).eqs,guesses: Accumulating vectors for the MTK system.points,segments,pulleys,tethers,wings: System components.psys: Symbolic parameter representing the system structure.pos,vel: Symbolic point state variables.wind_vec_gnd: Symbolic ground-level wind vector.spring_force_vec,drag_force,l0: Pre-declared segment force variables.pulley_len,tether_len: Symbolic state variables for pulley and tether lengths.
Returns
- Tuple
(eqs, guesses, len, spring_force)with updated equation vectors and the segment length and spring force variables for use by other components.
SymbolicAWEModels.update_vsm! — Function
update_vsm!(s::SymbolicAWEModel, integ=s.integrator)Update the aerodynamic model from the Vortex Step Method.
For RIGID_DYNAMICS wings: Computes wind-axis coefficients (CL, CD, CS, CM, cm) at the current operating point, plus a ForwardDiff Jacobian over the input vector [α, β, ω₁, ω₂, ω₃, θ_group₁…]. Stores the dense Jacobian d(coeffs)/d(inputs) in wing.aero_jac.
For PARTICLE_DYNAMICS wings: Full nonlinear VSM solve with per-point force distribution.
SymbolicAWEModels.jacobian — Function
jacobian(f::Function, x::AbstractVector, ϵ::AbstractVector) -> MatrixNumerically compute the Jacobian of a vector-valued function f at point x.
This function uses a simple forward finite difference method to approximate the partial derivatives of f with respect to each component of x.
Arguments
f::Function: The function to differentiate (y = f(x)).x::AbstractVector: The point at which to evaluate the Jacobian.ϵ::AbstractVector: A vector of perturbation sizes for each component ofx.
Returns
Matrix: The Jacobian matrixJ, whereJ[i, j] = ∂f[i] / ∂x[j].
SymbolicAWEModels.load_serialized_model! — Function
load_serialized_model!(sam, model_path; remake=false, reload=false)Load a serialized model from disk if it is valid.
A model is considered valid if its settings and system structure hashes match the current ones in the SymbolicAWEModel object (sam).
Arguments
sam::SymbolicAWEModel: The main model object.model_path::String: The path to the serialized model file.remake::Bool: If true, forces the model to be considered invalid, triggering a rebuild.reload::Bool: If true, forces reloading from disk even if the model is already in memory.
Returns
trueif a valid model was successfully loaded intosam.serialized_model,falseotherwise.
SymbolicAWEModels.maybe_create_lin_prob! — Function
maybe_create_lin_prob!(sam, outputs; ...)Create and cache the LinearizationProblem if it does not exist or if the outputs have changed.
Arguments
sam::SymbolicAWEModel: The main model object.outputs: A vector of output variables for the linearization.create_lin_prob::Bool: Flag to enable/disable creation.outputs_changed::Bool: Flag indicating if the output vector has changed.prn::Bool: Flag to enable/disable printing of progress messages.
Returns
trueif a new problem was created,falseotherwise.
SymbolicAWEModels.maybe_create_control_functions! — Function
maybe_create_control_functions!(sam, outputs; ...)Create and cache the control functions if they do not exist or if the outputs have changed.
Arguments
sam::SymbolicAWEModel: The main model object.outputs: A vector of output variables for the control functions.create_control_func::Bool: Flag to enable/disable creation.outputs_changed::Bool: Flag indicating if the output vector has changed.prn::Bool: Flag to enable/disable printing of progress messages.
Returns
trueif new functions were created,falseotherwise.
SymbolicAWEModels.maybe_create_prob! — Function
maybe_create_prob!(sam; create_prob=true, prn=true)Create and cache the ODEProblem if it does not already exist.
This function compiles the full system, creates the ODEProblem, and generates the necessary getter/setter functions.
Arguments
sam::SymbolicAWEModel: The main model object.create_prob::Bool: A flag to enable or disable the creation of the problem.prn::Bool: A flag to enable or disable printing of progress messages.
Returns
trueif a new problem was created,falseotherwise.
SymbolicAWEModels.generate_control_funcs — Function
generate_control_funcs(model, inputs, outputs)Generate in-place and out-of-place control functions from a ModelingToolkit system.
This function wraps ModelingToolkit.generate_control_function and ModelingToolkit.build_explicit_observed_function to create the necessary functions for simulation and analysis.
Arguments
model: The fullODESystem.inputs: A vector of input variables.outputs: A vector of output variables.
Returns
- A
NamedTuplecontaining the generated functions (f_oop,f_ip,h_oop,h_ip), system dimensions (nu,nx,ny), and symbolic variables (dvs,psym,io_sys).
SymbolicAWEModels.generate_lin_getters — Function
generate_lin_getters(sys)Generate setter functions for the parameters of a linearized system.
Arguments
sys: The linearized ModelingToolkit system.
Returns
- A
NamedTuplecontaining setter functions for the winch set-points (set_set_values), and the system structure parameters (set_sys).
SymbolicAWEModels.generate_prob_getters — Function
generate_prob_getters(sys_struct, sys)Generate getter and setter functions for the state variables of the full system model.
These functions provide a convenient way to access and modify the state and parameters of the compiled ODESystem (sys).
Arguments
sys_struct::SystemStructure: The structure defining the system topology.sys::ODESystem: The compiled ModelingToolkit system.
Returns
- A
NamedTuplecontaining various getter and setter functions for different parts of the system state.
SymbolicAWEModels.LinProbWithAttributes — Type
@with_kw struct LinProbWithAttributes{SetLinSetValues, SetLinSys, SetLinSet, LinOut}A container for the general-purpose linearization problem and the resulting full linearized model (A,B,C,D matrices).
prob::Any: Linearization problem of the mtk model.set_set_values::Anyset_sys::Any
SymbolicAWEModels.ProbWithAttributes — Type
@with_kw struct ProbWithAttributes{...}A container for the main Ordinary Differential Equation (ODE) problem and its associated getter and setter functions for the full, nonlinear physical state.
SymbolicAWEModels.ControlFuncWithAttributes — Type
@with_kw struct ControlFuncWithAttributes{FIP, FOOP, HIP, HOOP, DVS, PSYM}A container for callable control functions and their symbolic representations, generated from the full system model.
f_ip::Any: In-place dynamics function f(dx, x, u, p, t).f_oop::Any: Out-of-place dynamics function dx = f(x, u, p, t).h_ip::Any: In-place observation function h(y, x, u, p, t).h_oop::Any: Out-of-place observation function y = h(x, u, p, t).nu::Int64: Number of inputs (u).nx::Int64: Number of states (x).ny::Int64: Number of outputs (y).dvs::Any: The symbolic state vector.psym::Any: The symbolic parameter vector.io_sys::ModelingToolkitBase.System: The generated input-output system.
Utility and internal functions
SymbolicAWEModels.get_model_name — Function
get_model_name(set::Settings, sys_struct::SystemStructure; precompile=false)Constructs a unique filename for the serialized model based on its configuration. The filename includes the SymbolicAWEModels version, Julia version, physical model, wing type, dynamics type, and component counts to ensure that the correct cached model is loaded.
SymbolicAWEModels.calc_height — Function
calc_height(s::SymbolicAWEModel)Returns the height (z-position) [m] of the wing.
SymbolicAWEModels.set_depower_steering! — Function
set_depower_steering!(s::SymbolicAWEModel, depower, steering)Sets the kite's depower and steering by adjusting the tether length set-points.
SymbolicAWEModels.min_chord_len — Function
min_chord_len(s::SymbolicAWEModel)Calculates the minimum chord length of the wing at the tip.
SymbolicAWEModels.pos — Function
pos(s::SymbolicAWEModel)Returns a vector of the position vectors [m] for each point in the system.
SymbolicAWEModels.spring_forces — Function
spring_forces(s::SymbolicAWEModel)Returns the spring force [N] for each tether segment.
SymbolicAWEModels.calc_spring_props — Function
calc_spring_props(sam, tether_sam; prn=false) -> (Vector, Vector, Matrix, Float64)Calculate the equivalent stiffness and damping, and return the step response data.
This function orchestrates the process by performing a step response test on the tether_sam model and then analyzing the resulting tether length data.
Arguments
sam::SymbolicAWEModel: The reference model, used for its physical properties.tether_sam::SymbolicAWEModel: A copy of the model to perform the step test on.
Keywords
prn::Bool=false: If true, enables printing of intermediate results.
Returns
Tuple{Vector{Float64}, Vector{Float64}, Matrix{Float64}, Float64}: A tuple containing:unit_stiffness[N]unit_damping[Ns]tether_lens(the step response data)dt(the simulation time step)
calc_spring_props(sam, tether_lens, F_step; p=5, prn=false) -> (Vector, Vector)Calculate spring constant k and damping coefficient c from a step response.
This function analyzes the time series of tether lengths (tether_lens) resulting from a step force (F_step) to estimate the parameters of an equivalent second-order mass-spring-damper system.
Arguments
sam::SymbolicAWEModel: The model from which to take physical parameters (mass).tether_lens::Matrix{Float64}: A matrix of tether length time series data.F_step::Float64: The magnitude of the applied step force.
Keywords
p::Int=5: The percentage band used to determine the settling time.prn::Bool=false: If true, enables printing of detailed calculations.
Returns
Tuple{Vector{Float64}, Vector{Float64}}: A tuple containing two vectors:k_values(spring constants [N/m])c_values(damping coefficients [Ns/m])
SymbolicAWEModels.in_percent_band — Function
in_percent_band(x, steady, delta_x, i, p) -> BoolHelper function to check if a time series has settled within a percentage band.
It checks if all values of the time series x from index i to the end are within a tolerance band defined by p percent of the total change delta_x.
SymbolicAWEModels.step — Function
step(sam, steps, F_step, F_0; abs_tol, consecutive_steps_needed, prn) -> MatrixApply a step force to a model and simulate its dynamic response.
This function records the length of each tether over a specified number of simulation steps. It includes an early exit condition if the system's state settles.
Arguments
sam::SymbolicAWEModel: The model to be simulated.steps::Int: The total number of simulation steps.F_step::Float64: The magnitude of the step force to apply.F_0::Vector{KVec3}: The initial force vector for each tether attachment point.
Keywords
abs_tol::Float64=1e-6: Absolute tolerance for the settling check.consecutive_steps_needed::Int=10: Number of consecutive steps required to be within tolerance to be considered settled.prn::Bool=false: If true, enables printing of status messages.
Returns
Matrix{Float64}: A matrix where each row corresponds to a tether and each column to a time step, containing the tether lengths.
SymbolicAWEModels.create_model_archive — Function
create_model_archive(source_dir, archive_path)Finds all model*.bin files in the source_dir, copies them to a temporary directory, and compresses that directory into a .tar.gz archive at the specified archive_path.
SymbolicAWEModels.filecmp — Function
filecmp(path1::AbstractString, path2::AbstractString) -> BoolCompare two files byte-by-byte to check if they are identical.
SymbolicAWEModels.extract_model_archive — Function
extract_model_archive(archive_path, dest_dir)Safely decompress a .tar.gz file by first extracting to a temporary directory and then copying the contents to the final destination.
Arguments
archive_path::String: The path to the.tar.gzfile to be extracted.dest_dir::String: The path to the target directory.
SymbolicAWEModels.copy_bin — Function
copy_bin()Copy all example scripts to the folder "bin" (it will be created if it doesn't exist).
SymbolicAWEModels.copy_examples — Function
copy_examples()Copy all example scripts to the folder "examples" (it will be created if it doesn't exist).
SymbolicAWEModels.copy_data — Function
copy_data()Copy all data scripts to the folder "data" (it will be created if it doesn't exist).
SymbolicAWEModels.copy_dir — Function
copy_dir(src_dir, dst_dir)Copies all files from src_dir to dst_dir. Overwrites existing files if force=true. Creates dst_dir if it does not exist.
SymbolicAWEModels.get_example_packages — Function
get_example_packages()Get the list of packages from examples/Project.toml, excluding SymbolicAWEModels itself. This ensures init_module installs the correct dependencies for running examples.
SymbolicAWEModels.make_lin_sys_state — Function
make_lin_sys_state(y::AbstractVector, sam::SymbolicAWEModel, t::Real)Construct a SysState for logging linear state-space simulation output y (ordered as sam.outputs).
Base overloads (internal use)
SymbolicAWEModels._SAM_FIELDS — Constant
_SAM_FIELDSTuple of field names that are direct fields of SymbolicAWEModel (as opposed to fields delegated to the nested serialized_model). Used by getproperty and setproperty! to dispatch field access correctly.
Base.getindex — Function
Access item by numeric index.
Access item by symbolic name.
Base.getindex(x::ModelingToolkit.Symbolics.Arr, idxs::Vector{Int64})Extend Base.getindex to allow indexing a symbolic array with a vector of integer indices, which is not natively supported by ModelingToolkit.
Base.getproperty — Function
Base.getproperty(pa::ProbWithAttributes, sym::Symbol)Overloads getproperty to provide convenient access to the simplified system (sys) contained within the ODE problem's function definition.
Base.setproperty! — Function
Base.setproperty!(sam::SymbolicAWEModel, sym::Symbol, val)Overloads setproperty! to allow direct setting of fields within the nested serialized_model. This allows you to change properties of the compiled model as if they were fields of the SymbolicAWEModel itself.
YAML loader internals
SymbolicAWEModels.get_field_or_nothing — Function
get_field_or_nothing(::Type{T}, row::NamedTuple,
field::Symbol) where TConvert field to type T if present, otherwise return nothing.
Examples
get_field_or_nothing(Int64, row, :idx) # -> Int64 or nothing
get_field_or_nothing(Tuple{Int64,Int64}, row, :pair)
# -> (Int64, Int64) or nothingSymbolicAWEModels.convert_to_type — Function
convert_to_type(::Type{T}, value) where TConvert value to type T. Handles special cases like Tuples.
SymbolicAWEModels.resolve_references — Function
resolve_references(row::NamedTuple, property_tables::Dict{String, Dict{String, NamedTuple}})Resolve string references in a row by looking them up in property tables. If a field value is a String, check if it exists as a key in any property table. If found, merge those properties into the current row (current row takes precedence).
SymbolicAWEModels.calculate_derived_properties! — Function
calculate_derived_properties!(props::Dict{Symbol, Any})Calculate derived properties like unit_stiffness and unit_damping from material properties. Modifies props in-place.
SymbolicAWEModels._extract_args — Function
_extract_args(row, args_spec, mappings)Extract positional constructor arguments from a YAML row.
For each name in args_spec, this helper first checks for a mapping in mappings, then falls back to row[arg_name]. Throws an error if a required argument is missing.
SymbolicAWEModels.call_yaml_constructor — Function
call_yaml_constructor(Constructor, row::NamedTuple,
args_spec, kwargs_spec; mappings=Dict())Generic YAML-to-constructor caller. Extracts positional args and kwargs from YAML row and calls constructor.
Arguments
Constructor: Constructor function to callrow::NamedTuple: Parsed YAML rowargs_spec::Vector{Symbol}: Names for positional argskwargs_spec::Vector{Symbol}: Names for kwargs
Keyword Arguments
mappings::Dict{Symbol, Function}: Mapping functions that take the row and return the arg value
Example
row = (idx=1, x=0.0, y=0.0, z=0.0, type="STATIC")
point = call_yaml_constructor(Point, row,
[:idx, :pos_cad, :type], # positional args
[:extra_mass, :wing_idx]; # kwargs
mappings=Dict(
:pos_cad => r -> [Float64(r.x),
Float64(r.y), Float64(r.z)],
:type => r -> parse_dynamics_type(
String(r.type))
))SymbolicAWEModels.parse_tether_init — Function
parse_tether_init(row, tether_name) -> (stretched_len, force)Read init_stretched_length and init_tether_force from a tether YAML row (each nothing if absent). Errors if the deprecated init_unstretched_length field is present — the unstretched rest length is now derived from the placed stretched length and init_tether_force.
SystemStructure internals
SymbolicAWEModels.segment_cad_length — Function
segment_cad_length(segment::Segment, points)Compute segment length from endpoint pos_cad positions.
SymbolicAWEModels.segment_world_length — Function
segment_world_length(segment::Segment, points)Compute segment length from endpoint pos_w positions.
SymbolicAWEModels.tether_ordered_point_idxs — Function
tether_ordered_point_idxs(tether, segments)Point indices along the tether, ordered from start_point_idx through each segment's far endpoint to the end point.
SymbolicAWEModels.tether_anchor_free — Function
tether_anchor_free(tether, boundary)Return (anchor_idx, free_idx) for a root tether: the endpoint in boundary (STATIC/winch points) is the anchor, the other is free. Returns (nothing, nothing) if neither endpoint is on a boundary; errors if both are.
SymbolicAWEModels.rigid_point_siblings — Function
rigid_point_siblings(points, wings)Map each WING-type point index of a RIGID_DYNAMICS wing to the set of all such points sharing that wing. These points move as one rigid body without inter-point segments, so the set captures their connectivity for downstream traversal.
SymbolicAWEModels.tether_downstream_idxs — Function
tether_downstream_idxs(tether, segments, boundary, from_idx,
anchor_idx, rigid_siblings)Breadth-first set of point indices reachable from from_idx (the tether's free end) through segments outside this tether and through rigid_siblings, stopping at boundary points. These are the points that must translate with the free end when the tether is repositioned. Errors if traversal reaches anchor_idx (a loop back to the anchor).
SymbolicAWEModels.group_tethers_by_overlap — Function
group_tethers_by_overlap(specified, reach)Cluster the specified tethers with a union-find over reach (point indices each tether touches): tethers whose reaches intersect share structure and land in the same cluster. Returns a vector of tether vectors, one per cluster.
SymbolicAWEModels.tether_unit_stiffness — Function
tether_unit_stiffness(tether, segments)Return the common per-unit-length stiffness [N] of the tether's segments. Errors if the segments are not uniform, since the spring inversion in apply_tether_init_forces! assumes a single stiffness.
SymbolicAWEModels.apply_cluster_init_stretched_len! — Function
apply_cluster_init_stretched_len!(cluster, points, segments,
downstream, boundary; prn=true)Reposition one cluster of root tethers so each sits at its init_stretched_len standoff. Each tether contributes the displacement that would move its free end onto the target length along the anchor→free direction; the free end and everything downstream of it are translated by the mean of those displacements, then interior points are redistributed proportionally along each tether. For a multi-tether cluster, logs an @info when prn.
SymbolicAWEModels.apply_tether_init_stretched_lens! — Function
apply_tether_init_stretched_lens!(sys_struct::SystemStructure; prn=true)Scale pos_w so each tether with an explicit init_stretched_len sits at that standoff. Call after copy_cad_to_world!. Rest length is derived separately by apply_tether_init_forces!.
Only tethers with one endpoint on a boundary (STATIC or winch point) are placed; that endpoint is the fixed anchor (start or end). Scaling runs from the anchor toward the free end, translating everything downstream of it. A tether with neither endpoint anchored is an error. Roots feeding one structure form a cluster, placed by their mean displacement (length and direction).
Errors if a downstream segment connects back to the anchor.
SymbolicAWEModels.apply_tether_init_forces! — Function
apply_tether_init_forces!(sys_struct::SystemStructure)Derive every tether's unstretched length len from its current (placed) stretched length so the initial spring force equals init_tether_force (default 0): len = stretched · (1 − force / unit_stiffness) (zero-velocity, tension branch of the segment spring law). Force 0 gives len = stretched (zero tension).
Must be called after segment world lengths are current. Errors if force < 0 (compression unsupported), if force ≥ unit_stiffness (no positive rest length achieves it), or if a tether's segments have non-uniform unit_stiffness.
SymbolicAWEModels.assign_indices_and_resolve! — Function
assign_indices_and_resolve!(components, name_dicts)Assign indices to all components based on their position in the vectors, and resolve all references to indices.
SymbolicAWEModels.resolve_ref — Function
resolve_ref(ref::NameRef, name_dict::Dict{Symbol, Int64}, component_type::String) -> Int64Resolve a reference (name or index) to an index using the name dictionary. If ref is an integer, returns it directly. If ref is a symbol, looks up in dictionary.
SymbolicAWEModels.resolve_ref_spec — Function
resolve_ref_spec(spec, name_dict, component_type) -> Union{Int64, Vector{Int64}, Nothing}Resolve a reference point specification (single ref or vector of refs) to indices.
SymbolicAWEModels.validate_sys_struct — Function
validate_sys_struct(sys_struct::SystemStructure)Validate a SystemStructure for common configuration errors.
This function checks for issues that can cause initialization failures or numerical problems during simulation. It emits warnings for suspicious configurations and throws assertions for definite errors.
Validations Performed
Point Validations
- NaN extra_mass (error)
- Negative extra_mass (warning)
- Non-positive total_mass for DYNAMIC points (error) - checked before NaN position
- NaN position (error) - often caused by zero mass
Wing Validations
- Non-positive mass (error) - checked before NaN position
- Zero or near-zero principal inertia components on RIGID_DYNAMICS wings (error/warning)
- NaN inertia values (error)
- Empty group list for RIGID_DYNAMICS wings (warning)
- NaN position (error) - often caused by zero mass/inertia
Winch Validations
- Zero or negative inertia_total (error)
- Very small inertia_total (warning)
- NaN inertia_total (error)
- Non-positive drum_radius (error)
- Non-positive gear_ratio (error)
Segment Validations
- Unusual diameter outside (0, 1) m range (warning)
- Non-positive rest length l0 (error)
- Zero or negative stiffness (warning)
- Negative damping (warning)
Pulley Validations
- Zero total length constraint (error)
Group Validations
- Inconsistent moment_frac across groups (error)
SymbolicAWEModels.build_name_dict — Function
build_name_dict(items::Vector) -> Dict{Symbol, Int64}Build a name→index dictionary from a vector of items with optional name fields. Items with name=nothing are skipped. Integer names are converted to Symbols.
SymbolicAWEModels.identify_wing_segments — Function
identify_wing_segments(wing_points; groups=nothing, wing_group_idxs=nothing)Identify wing segments (LE/TE pairs) from WING-type points.
When groups and wing_group_idxs are provided, uses group point_idxs to determine LE (point_idxs[1]) and TE (point_idxs[end]) for each section. Falls back to a consecutive-pair heuristic (sorted by point index) when groups are unavailable.
In both paths an x-coordinate check swaps LE/TE if needed (LE has smaller pos_cad[1]).
Arguments
wing_points::AbstractVector{Point}: WING-type points for a wing.
Keyword Arguments
groups::Union{Nothing, AbstractVector{Group}}: All groups in the system (indexed bywing_group_idxs).wing_group_idxs::Union{Nothing, AbstractVector{<:Integer}}: Indices intogroupsbelonging to this wing.
Returns
Vector{Tuple{Int64, Int64}}: (lepointidx, tepointidx) pairs.
SymbolicAWEModels.match_aero_sections_to_structure! — Function
match_aero_sections_to_structure!(wing, points; groups)Reconcile a wing's aerodynamic sections with its structural geometry.
RIGIDDYNAMICS wings own their aero panel geometry (mesh- or YAML-defined) and keep it; only the group→section mapping (`wing.wingsegments) is recorded. PARTICLE_DYNAMICS wings deform with their structural points, so each unrefined section is rebuilt onto its structural LE/TE pair: a 1:1 copy when counts match, otherwiseusepriorpolarand existingrefined_sections` are required to preserve polars.
Keyword Arguments
groups::AbstractVector{Group}: Groups used for LE/TE identification viaidentify_wing_segments.
SymbolicAWEModels.compute_spatial_group_mapping! — Function
compute_spatial_group_mapping!(the_wing, groups, points)Partition the wing's unrefined VSM sections among its groups by spatial proximity: each unrefined section is assigned to the single closest group (by distance between section centre and group centre, both in body frame).
When n_groups == n_unrefined this is the same 1:1 mapping as before. When n_groups < n_unrefined a group may own several adjacent sections; its single twist DOF then drives all of them as a rigid unit. The case n_groups > n_unrefined is rejected — a twist DOF without a section to drive would be undefined.
SymbolicAWEModels.copy_cad_to_world! — Function
copy_cad_to_world!(points, wings; update_vel=true)Copy CAD geometry to world frame for ALL points and wings. Sets pos_w = pos_cad (and Q_b_to_w to initial CAD orientation for wings). Must be called before reinit!(transforms, ...).
SymbolicAWEModels.adjust_vsm_panels_to_origin! — Function
adjust_vsm_panels_to_origin!(vsm_wing, origin_offset)Adjust VSM panel positions when body frame origin changes.
When RIGIDDYNAMICS wings are loaded from YAML, the panel positions in aerogeometry.yaml are specified in an absolute body frame. However, the body frame origin is adjusted to the mean position of all WING points. This function updates all panel positions to be relative to the new origin by subtracting the offset.
Arguments
vsm_wing: VortexStepMethod.Wing with sections to adjustorigin_offset: Vector [x, y, z] to subtract from panel positions
SymbolicAWEModels.apply_aero_z_offset! — Function
apply_aero_z_offset!(vsm_wing, aero_z_offset)Apply z-axis offset to VSM panel positions in body frame.
For RIGID_DYNAMICS wings, this shifts the aerodynamic center of pressure in the positive z-direction (body frame) to adjust the moment arm. This is applied AFTER the COM adjustment.
Arguments
vsm_wing: VortexStepMethod.Wing with sections to adjustaero_z_offset: Distance to shift panels in +z direction [m]
SymbolicAWEModels.calc_particle_dynamics_wing_frame — Function
calc_particle_dynamics_wing_frame(points, z_ref_points, y_ref_points, origin)Calculate Rbto_w rotation matrix and origin position from structural point positions.
Algorithm
- Weighted ref point positions
- Z-axis (normal): zp1 → zp2
- X-axis (chord): Y_temp × Z
- Y-axis (span): Z × X (orthogonal, right-handed)
- Origin from weighted
originref points
SymbolicAWEModels.calc_inertia_y_rotation — Function
calc_inertia_y_rotation(I_tensor)Find the Y-axis rotation that diagonalizes the XZ block of the inertia tensor (zeros out I[1,3] and I[3,1]).
Returns (I_diag, Ry) where I_diag = Ry * I_tensor * Ry' and Ry is a rotation about the Y axis by angle θ = atan(2·I₁₃, I₁₁ − I₃₃) / 2.
SymbolicAWEModels.rotate_vsm_sections! — Function
rotate_vsm_sections!(vsm_wing, R)Rotate all VSM section LE/TE points by rotation matrix R.
Used during initialization to transform sections from CAD frame to body frame. After the first step, update_vsm!() updates positions from pos_b (already in body frame).
SymbolicAWEModels.expand_auto_tethers! — Function
expand_auto_tethers!(points, segments, tethers, set)For Route 2 tethers (auto-generation), create intermediate DYNAMIC points and segments. Must be called before assign_indices_and_resolve!.
Detects Route 2 tethers by checking start_point_ref !== nothing and segment_refs names not yet present in segments.
SymbolicAWEModels.WeightedRefPoints — Type
WeightedRefPointsWeighted combination of reference points for wing frame definition. Supports single points, equal-weight averaging, and arbitrary weight combinations.
Fields
refs: Unresolved names/indices (filled at construction)ids: Resolved point indices (filled byresolve!)weights: Normalized weights (sum to 1.0)
SymbolicAWEModels.resolve! — Function
resolve!(ref_pt::WeightedRefPoints, name_dict, type)Resolve symbolic refs to integer indices, filling ref_pt.ids. No-op if refs is empty (already resolved).
SymbolicAWEModels._validate_weights! — Function
Warn and normalize if weights don't sum to 1.
SymbolicAWEModels.SegmentType — Type
SegmentType `POWER_LINE` `STEERING_LINE` `BRIDLE`NamedCollection internals
SymbolicAWEModels.names — Function
names(nc::NamedCollection)Return a vector of all symbolic names in order of their indices. Names are nothing for unnamed items.
SymbolicAWEModels.get_idx — Function
get_idx(nc::NamedCollection, name::Symbol)Get the numeric index for a given symbolic name.
SymbolicAWEModels.get_name — Function
get_name(nc::NamedCollection, idx::Integer)Get the symbolic name for a given numeric index, or nothing if unnamed.
Base.values — Method
Get all values (same as iterating).
Base.haskey — Method
Check if a symbolic name exists in the collection.
Base.setindex! — Method
Set item by numeric index.
Base.setindex! — Method
Set item by symbolic name.
Equation builders
SymbolicAWEModels.tether_eqs! — Function
tether_eqs!(eqs, tethers; len, spring_force)Generate equations for tether stretched length and average spring force.
Arguments
eqs: Accumulating equation vector.tethers: Collection of Tether objects.len: Symbolic segment length variable.spring_force: Symbolic segment spring force variable.
Returns
- Updated
eqsvector with tether equations.
SymbolicAWEModels.pulley_eqs! — Function
pulley_eqs!(eqs, defaults, guesses, pulleys, segments, psys;
spring_force, pulley_len, pulley_vel)Generate equations for pulley dynamics (rope distribution over pulleys).
Arguments
eqs,defaults,guesses: Accumulating vectors for the MTK system.pulleys: Collection of Pulley objects.segments: Collection of Segment objects (for mass calculation).psys: Symbolic parameter representing the system structure.spring_force: Symbolic segment spring force variable.pulley_len,pulley_vel: Symbolic pulley state variables.
Returns
- Tuple
(eqs, defaults, guesses)with updated equation vectors.
SymbolicAWEModels.winch_eqs! — Function
winch_eqs!(eqs, defaults, winches, tethers, points,
psys;
point_force, set_values,
tether_len, winch_vel)Generate equations for winch motor dynamics and per-tether length state.
Each tether gets a differential equation for tether_len:
- With winch:
D(tether_len) = winch_vel(shared velocity from the winch motor). - Without winch:
D(tether_len) = 0(constant).
Each winch gets a differential equation for winch_vel:
D(winch_vel) = winch_acc(from motor dynamics), gated bybrakeandspeed_controlled.
Returns
- Tuple
(eqs, defaults)with updated equation vectors.
SymbolicAWEModels.group_eqs! — Function
group_eqs!(eqs, defaults, guesses, groups, wings, psys;
R_b_to_w, fix_wing, twist_angle, twist_ω, group_aero_moment,
point_force, tether_wing_moment, group_y_airf, group_chord, group_le_pos)Generate equations for deformable wing group twist dynamics.
Arguments
eqs,defaults,guesses: Accumulating vectors for the MTK system.groups: Collection of Group objects (deformable wing sections).wings: Collection of Wing objects.psys: Symbolic parameter representing the system structure.R_b_to_w: Symbolic rotation matrix (body to world).fix_wing: Symbolic boolean for fixing wing dynamics.twist_angle,twist_ω: Symbolic twist state variables.group_aero_moment: Symbolic aerodynamic moment on groups.point_force: Symbolic point force variable.tether_wing_moment: Accumulated tether moments on wings (for validation).group_y_airf,group_chord,group_le_pos: Symbolic group geometry variables.
Returns
- Tuple
(eqs, defaults, guesses)with updated equation vectors.
SymbolicAWEModels.plate_eqs! — Function
plate_eqs!(eqs, wing, psys, R_b_to_w, va_point_b,
aero_force_b, aero_moment_b,
aero_force_point_b, pos, com_w, height)Generate symbolic flat-plate aerodynamic equations for a PlateWing.
For each PlateSurface:
- Read twist angle directly from surface
- Rotate xairf/zairf around y_airf by twist
- AoA = atan(vnormal, vtangential)
- CL/CD from registered interpolation lookup
- Lift perpendicular to flow in airfoil plane, drag along flow
Forces are applied per-point (PARTICLEDYNAMICS) or summed to wing (RIGIDDYNAMICS).
Plate aerodynamics internals
SymbolicAWEModels.plate_alpha — Function
plate_alpha(wing::PlateWing, surf::PlateSurface)Compute current AoA [deg] from body-frame apparent wind and twist. Requires va_b to be up to date.
SymbolicAWEModels._load_plate_wing — Function
_load_plate_wing(row, idx, data, set, wt, am,
yaml_to_ref, yaml_parse_ref_points)Load a PlateWing from YAML wing row + surfaces block. CL/CD interpolations are created from Settings polar data.
VSM and aerodynamics internals
SymbolicAWEModels.build_point_to_vsm_point_mapping — Function
build_point_to_vsm_point_mapping(wing_points::AbstractVector{Point}, wing::VSMWing)Build 1:1 mapping from structural WING points to VSM wing section points (LE/TE) using closest-point distance.
For each VSM section point (LE/TE), finds the closest structural point. Distances are computed in body frame: structural pos_cad is transformed via wing.R_b_to_c' and wing.pos_cad before being compared against section LE/TE points (which already live in body frame after match_aero_sections_to_structure!).
Constraint
Requires: length(wing_points) == 2 * length(wing.vsm_wing.unrefined_sections)
SymbolicAWEModels.update_vsm_wing_from_structure! — Function
update_vsm_wing_from_structure!(wing::VSMWing, points::AbstractVector{Point})Update VSM section points (LE/TE) directly from structural point positions using 1:1 mapping.
This creates two-way coupling: structural deformation → VSM sections → aero forces.
Algorithm
Uses direct 1:1 correspondence between structural points and VSM section points:
- For each structural WING point:
- Calculate current position in body frame: posb = Rbtow' * (pos_w - origin)
- Find corresponding VSM section point (LE or TE) via wing.pointtovsm_point
- Set VSM section point directly: section.LEpoint = posb (or TE_point)
Notes
- Section points are stored in body frame coordinates
wing.R_b_to_wandwing.pos_ware updated each timestep from structural geometry (symbolic equations)- To get world coordinates:
world_pos = wing.R_b_to_w * section.LE_point + wing.pos_w
Arguments
wing::VSMWing: Wing with PARTICLE_DYNAMICS typepoints::AbstractVector{Point}: All structural points (will filter for WING type)
SymbolicAWEModels.distribute_panel_forces_to_points! — Function
distribute_panel_forces_to_points!(wing::VSMWing, points::AbstractVector{Point})Distribute VSM forces to structural points using refined panel forces.
After VSM solve, each refined panel force/moment is split into corner-node forces (moment-preserving about the chosen reference) and then aggregated to the structural LE/TE points of the parent section (1:1 mapping).
Algorithm
- Initialize all WING point aero_forces to zero
- Build inverse mapping from section → LE/TE structural point indices
- For each refined panel of this wing:
- Get panel force/moment from solver solution (body frame)
- Map panel to its parent section using
refined_panel_mapping - Split to LE/TE forces with
compute_aerostruc_loads - Accumulate forces at the corresponding structural points
Arguments
wing::VSMWing: Wing with PARTICLE_DYNAMICS type and solved VSM statepoints::AbstractVector{Point}: All structural points (will filter for WING type)
SymbolicAWEModels._update_rigid_dynamics_wing! — Function
_update_rigid_dynamics_wing!(wing, am, groups)Compute baseline wind-axis coefficients and the ForwardDiff Jacobian d(coeffs)/d(inputs) for one wing.
Writes wing.aero_y / aero_x / aero_jac, updates groups[gidx].aero_moment, and (in AERODIRECT mode) writes `wing.aeroforceb/wing.aeromoment_b`.
SymbolicAWEModels._apply_direct_forces! — Function
Apply direct forces from wind-axis coefficients.
SymbolicAWEModels._safe_vsm_solve! — Function
NaN/Inf-guarded solve!. Checks both Dual value and partials. On non-finite or non-converged result, zero gamma and return false.
Heading and geometry
SymbolicAWEModels.solve_heading_rotation — Function
solve_heading_rotation(R_b_to_w, target_heading, wing_pos)Calculate the rotation angle around the radial axis needed to achieve target_heading.
With the tangential sphere heading, rotating around the radial axis by θ simply shifts the heading by θ, so the solution is target_heading - current_heading.
SymbolicAWEModels.get_ref_position_from_points — Function
get_ref_position_from_points(points, ref_pt; field=:pos_w)Weighted position from structural points. field selects which point coordinate to read (:pos_w or :pos_cad).
SymbolicAWEModels.sym_calc_R_t_to_w — Function
sym_calc_R_t_to_w(wing_pos)Symbolic version of calc_R_t_to_w that uses explicit element access to avoid slice scalarization issues.
SymbolicAWEModels.wrap_to_pi — Function
wrap_to_pi(angle)Wrap angle to [-π, π] range.
Transform internals
SymbolicAWEModels._apply_azimuth_elevation! — Function
_apply_azimuth_elevation!(transform, wings, points, base_pos; update_vel=false)Apply the azimuth/elevation rotation of a single transform to all components in it. Returns (curr_R_t_to_w, R_t_to_w) for use in the heading step.
SymbolicAWEModels._apply_heading! — Function
_apply_heading!(transform, wings, points,
curr_R_t_to_w, R_t_to_w, base_pos)Apply heading rotation to all components in a single transform. Rotates around the radial axis through base_pos (not the origin). Uses wing.R_b_to_w for the no-ref-points orientation source. After copy_cad_to_world!, this equals wing.R_b_to_c (for reinit!), or the current world orientation (for reposition!).
SymbolicAWEModels._finalize_transforms! — Function
_finalize_transforms!(wings, points)Finalize transforms: update PARTICLE_DYNAMICS wing frames from structural point positions, then compute principal frame ODE state.
Other internals
SymbolicAWEModels.init_principal_frame! — Function
init_principal_frame!(wings, points)Compute principal frame ODE state from body frame. Must be called after body frame (pos_w, R_b_to_w, vel_w, ω_b) is fully initialized.
Sets: com_w, Q_p_to_w, com_vel, ω_p (derived from body frame), and pos_b for RIGID_DYNAMICS wing points (body frame, relative to COM).
SymbolicAWEModels.init_body_frame_from_ref_points! — Function
init_body_frame_from_ref_points!(wing, points; prn=true)Initialize wing body frame (Rbtoc, poscad) from z/y reference points. Shared by VSMWing PARTICLE_DYNAMICS and PlateWing.
SymbolicAWEModels.update_segment_forces! — Function
update_segment_forces!(sys_struct::SystemStructure)Calculate and update spring forces for all segments in-place.
This function computes the spring-damper forces for each segment using the same formulas as in generate_system.jl. It updates the len and force fields of each segment in the SystemStructure.
The spring-damper force follows Hooke's law with damping:
\[F = k(l - l_0) - c\dot{l}\]
where:
k = unit_stiffness / l(tension) ork = compression_frac * unit_stiffness / l(compression)lis current length,l_0is unstretched lengthc = unit_damping / lis damping coefficient\dot{l} = (v₁ - v₂) ⋅ ûis extension rate
Arguments
sys_struct::SystemStructure: The system structure containing points and segments.
Returns
nothing: Modifies segment.len and segment.force in-place.
Example
update_segment_forces!(sam.sys_struct)
for segment in sam.sys_struct.segments
println("Segment $(segment.idx): force = $(segment.force) N")
endSymbolicAWEModels.get_rot_pos_cad — Function
get_rot_pos_cad(transform::Transform, wings, points)Get the CAD-frame position of the rotating object (wing or point). Used by get_base_pos to compute the translation offset for chained transforms.
KiteUtils.Logger — Method
KiteUtils.Logger(sam::SymbolicAWEModel, steps::Int)Constructs a Logger from a SymbolicAWEModel with the correct number of points.
This convenience constructor automatically calculates the total number of points including VSM panel corners (4 corners per panel) and creates a Logger with the appropriate size.
Arguments
sam::SymbolicAWEModel: The AWE model to create a logger for.steps::Int: The number of time steps to allocate for logging.
Returns
Logger: A new logger with size for all points including panel corners.
Example
logger = Logger(sam, 1000) # Instead of Logger(length(sam.sys_struct.points), 1000)