Skip to content

Commit fea5434

Browse files
committed
Merge branch 'main' into lin
2 parents 320654b + 26dd4e0 commit fea5434

File tree

5 files changed

+147
-52
lines changed

5 files changed

+147
-52
lines changed

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ makedocs(;
2828
"Private Functions" => "private_functions.md",
2929
"Private Types" => "private_types.md",
3030
"Reference Frames" => "reference_frames.md",
31+
"Tips and tricks" => "tips_and_tricks.md",
3132
"Glossary" => "glossary.md"
3233
],
3334
)

docs/src/tips_and_tricks.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
## Tips and tricks
2+
3+
### What can this model simulate
4+
The following bodies can be simulated:
5+
6+
- conventional bodies, consisting of one or more wings
7+
- leading edge inflatable (LEI) kites
8+
- RAM-air kites
9+
10+
To build the geometry of a RAM-air kite, a 3D .obj file can be used as input. In addition a `.dat` file is needed.
11+
It should have two columns, one for the `x` and one for the `y` coordinate of the 2D polar that is used.
12+
13+
### RAM-air kite model
14+
If running the example `ram_air_kite.jl` fails, try to run the `cleanup.jl` script and then try again. Background: this example caches the calculated polars. Reading cached polars can fail after an update.
15+
16+
### Output formats
17+
Currently, the `solve!()` function returns the results as [VSMSolution](@ref) struct. The function solve() returns a
18+
dictionary with the results. The `solve!()` function is faster, and the `solve()` contains many more entries, therefore
19+
the first function is good for integration in dynamic models and the second one better suited for aerodynamic analysis.
20+
21+
### Performance
22+
Calling `init!(body_aero; init_aero=false)` is very fast. After calling `deform!(wing)`, you have to run `init!(body_aero; init_aero=false)` to apply the deformed wing to the body aerodynamics. This is in turn necessary for the linearization from deformation to aerodynamic coefficients for RAM-air kites.
23+
24+
### Building the documentation locally
25+
You can build the documentation locally after checking out the source code with git, launching Julia and executing:
26+
```
27+
include("scripts/build_docu.jl")
28+
```
29+
A browser window should pop up automatically.

src/solver.jl

Lines changed: 56 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,39 @@
55
Struct for storing the solution of the [solve!](@ref) function. Must contain all info needed by `KiteModels.jl`.
66
77
# Attributes
8-
- gamma_distribution::Union{Nothing, Vector{Float64}}: Vector containing the panel circulations
8+
- `panel_width_array`::Vector{Float64}: Width of the panels [m]
9+
- cl_array::Vector{Float64}: Lift coefficients of the panels [-]
10+
- cd_array::Vector{Float64}: Drag coefficients of the panels [-]
11+
- cm_array::Vector{Float64}: Pitching moment coefficients of the panels [-]
12+
- panel_lift::Vector{Float64}: Lift force of the panels [N]
13+
- panel_drag::Vector{Float64}: Drag force of the panels [N]
14+
- panel_moment::Vector{Float64}: Pitching moment around the spanwise vector of the panels [Nm]
15+
- `f_body_3D`::Matrix{Float64}: Matrix of the aerodynamic forces (x, y, z vectors) [N]
16+
- `m_body_3D`::Matrix{Float64}: Matrix of the aerodynamic moments [Nm]
17+
- `gamma_distribution`::Union{Nothing, Vector{Float64}}: Vector containing the panel circulations.
918
- force::MVec3: Aerodynamic force vector in KB reference frame [N]
1019
- moment::MVec3: Aerodynamic moments [Mx, My, Mz] around the reference point [Nm]
1120
- force_coefficients::MVec3: Aerodynamic force coefficients [CFx, CFy, CFz] [-]
12-
- moment_coefficients::MVec3: Aerodynamic moment coefficients [CMx, CMy, CMz] [-]
13-
- moment_distribution::Vector{Float64}: Pitching moments around the spanwise vector of each panel. [Nm]
14-
- moment_coeff_dist::Vector{Float64}: Pitching moment coefficient around the spanwise vector of each panel. [-]
15-
- solver_status::SolverStatus: enum, see [SolverStatus](@ref)
21+
- `moment_coefficients`::MVec3: Aerodynamic moment coefficients [CMx, CMy, CMz] [-]
22+
- `moment_distribution`::Vector{Float64}: Pitching moments around the spanwise vector of each panel. [Nm]
23+
- `moment_coefficient_distribution`::Vector{Float64}: Pitching moment coefficient around the spanwise vector of each panel. [-]
24+
- `solver_status`::SolverStatus: enum, see [SolverStatus](@ref)
1625
"""
1726
@with_kw mutable struct VSMSolution{P}
1827
### private vectors of solve_base!
19-
x_airf_array::Matrix{Float64} = zeros(P, 3)
20-
y_airf_array::Matrix{Float64} = zeros(P, 3)
21-
z_airf_array::Matrix{Float64} = zeros(P, 3)
22-
va_array::Matrix{Float64} = zeros(P, 3)
23-
chord_array::MVector{P, Float64} = zeros(MVector{P, Float64})
24-
###
25-
panel_width_array::MVector{P, Float64} = zeros(MVector{P, Float64})
26-
cl_array::MVector{P, Float64} = zeros(MVector{P, Float64})
27-
cd_array::MVector{P, Float64} = zeros(MVector{P, Float64})
28-
cm_array::MVector{P, Float64} = zeros(MVector{P, Float64})
29-
panel_lift::Matrix{Float64} = zeros(P,1)
30-
panel_drag::Matrix{Float64} = zeros(P,1)
31-
panel_moment::Matrix{Float64} = zeros(P,1)
28+
_x_airf_array::Matrix{Float64} = zeros(P, 3)
29+
_y_airf_array::Matrix{Float64} = zeros(P, 3)
30+
_z_airf_array::Matrix{Float64} = zeros(P, 3)
31+
_va_array::Matrix{Float64} = zeros(P, 3)
32+
_chord_array::Vector{Float64} = zeros(P)
33+
### end of private vectors
34+
panel_width_array::Vector{Float64} = zeros(P)
35+
cl_array::Vector{Float64} = zeros(P)
36+
cd_array::Vector{Float64} = zeros(P)
37+
cm_array::Vector{Float64} = zeros(P)
38+
panel_lift::Vector{Float64} = zeros(P)
39+
panel_drag::Vector{Float64} = zeros(P)
40+
panel_moment::Vector{Float64} = zeros(P)
3241
f_body_3D::Matrix{Float64} = zeros(3, P)
3342
m_body_3D::Matrix{Float64} = zeros(3, P)
3443
gamma_distribution::Union{Nothing, Vector{Float64}} = nothing
@@ -196,9 +205,9 @@ function solve!(solver::Solver, body_aero::BodyAerodynamics, gamma_distribution=
196205
panel_moment = solver.sol.panel_moment
197206

198207
# Compute using fused broadcasting (no intermediate allocations)
199-
@. lift = cl_array * 0.5 * density * v_a_array^2 * solver.sol.chord_array
200-
@. drag = cd_array * 0.5 * density * v_a_array^2 * solver.sol.chord_array
201-
@. panel_moment = cm_array * 0.5 * density * v_a_array^2 * solver.sol.chord_array
208+
@. lift = cl_array * 0.5 * density * v_a_array^2 * solver.sol._chord_array
209+
@. drag = cd_array * 0.5 * density * v_a_array^2 * solver.sol._chord_array
210+
@. panel_moment = cm_array * 0.5 * density * v_a_array^2 * solver.sol._chord_array
202211

203212
# Calculate alpha corrections based on model type
204213
if aerodynamic_model_type == VSM # 64 bytes
@@ -207,9 +216,9 @@ function solve!(solver::Solver, body_aero::BodyAerodynamics, gamma_distribution=
207216
body_aero,
208217
gamma_new,
209218
solver.core_radius_fraction,
210-
solver.sol.z_airf_array,
211-
solver.sol.x_airf_array,
212-
solver.sol.va_array,
219+
solver.sol._z_airf_array,
220+
solver.sol._x_airf_array,
221+
solver.sol._va_array,
213222
solver.br.va_norm_array,
214223
solver.br.va_unit_array
215224
)
@@ -334,11 +343,11 @@ function solve(solver::Solver, body_aero::BodyAerodynamics, gamma_distribution=n
334343
solver.mu,
335344
solver.lr.alpha_array,
336345
solver.lr.v_a_array,
337-
solver.sol.chord_array,
338-
solver.sol.x_airf_array,
339-
solver.sol.y_airf_array,
340-
solver.sol.z_airf_array,
341-
solver.sol.va_array,
346+
solver.sol._chord_array,
347+
solver.sol._x_airf_array,
348+
solver.sol._y_airf_array,
349+
solver.sol._z_airf_array,
350+
solver.sol._va_array,
342351
solver.br.va_norm_array,
343352
solver.br.va_unit_array,
344353
body_aero.panels,
@@ -365,31 +374,31 @@ function solve_base!(solver::Solver, body_aero::BodyAerodynamics, gamma_distribu
365374
relaxation_factor = solver.relaxation_factor
366375

367376
# Clear arrays
368-
solver.sol.x_airf_array .= 0
369-
solver.sol.y_airf_array .= 0
370-
solver.sol.z_airf_array .= 0
371-
solver.sol.va_array .= 0
372-
solver.sol.chord_array .= 0
377+
solver.sol._x_airf_array .= 0
378+
solver.sol._y_airf_array .= 0
379+
solver.sol._z_airf_array .= 0
380+
solver.sol._va_array .= 0
381+
solver.sol._chord_array .= 0
373382

374383
# Fill arrays from panels
375384
for (i, panel) in enumerate(panels)
376-
solver.sol.x_airf_array[i, :] .= panel.x_airf
377-
solver.sol.y_airf_array[i, :] .= panel.y_airf
378-
solver.sol.z_airf_array[i, :] .= panel.z_airf
379-
solver.sol.va_array[i, :] .= panel.va
380-
solver.sol.chord_array[i] = panel.chord
385+
solver.sol._x_airf_array[i, :] .= panel.x_airf
386+
solver.sol._y_airf_array[i, :] .= panel.y_airf
387+
solver.sol._z_airf_array[i, :] .= panel.z_airf
388+
solver.sol._va_array[i, :] .= panel.va
389+
solver.sol._chord_array[i] = panel.chord
381390
end
382391

383392
# Calculate unit vectors
384-
calc_norm_array!(solver.br.va_norm_array, solver.sol.va_array)
385-
solver.br.va_unit_array .= solver.sol.va_array ./ solver.br.va_norm_array
393+
calc_norm_array!(solver.br.va_norm_array, solver.sol._va_array)
394+
solver.br.va_unit_array .= solver.sol._va_array ./ solver.br.va_norm_array
386395

387396
# Calculate AIC matrices
388397
calculate_AIC_matrices!(body_aero, solver.aerodynamic_model_type, solver.core_radius_fraction, solver.br.va_norm_array,
389398
solver.br.va_unit_array)
390399

391400
# Initialize gamma distribution
392-
gamma_initial = solver.cache_base[1][solver.sol.chord_array]
401+
gamma_initial = solver.cache_base[1][solver.sol._chord_array]
393402
if isnothing(gamma_distribution)
394403
if solver.type_initial_gamma_distribution == ELLIPTIC
395404
calculate_circulation_distribution_elliptical_wing(gamma_initial, body_aero)
@@ -430,11 +439,11 @@ function gamma_loop!(
430439
relaxation_factor::Float64;
431440
log::Bool = true
432441
)
433-
va_array = solver.sol.va_array
434-
chord_array = solver.sol.chord_array
435-
x_airf_array = solver.sol.x_airf_array
436-
y_airf_array = solver.sol.y_airf_array
437-
z_airf_array = solver.sol.z_airf_array
442+
va_array = solver.sol._va_array
443+
chord_array = solver.sol._chord_array
444+
x_airf_array = solver.sol._x_airf_array
445+
y_airf_array = solver.sol._y_airf_array
446+
z_airf_array = solver.sol._z_airf_array
438447
solver.lr.converged = false
439448
n_panels = length(body_aero.panels)
440449
solver.lr.alpha_array .= body_aero.alpha_array

test/bench.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,11 @@ using LinearAlgebra
129129
solver = Solver{P}(
130130
aerodynamic_model_type=model
131131
)
132-
solver.sol.va_array .= va_array
133-
solver.sol.chord_array .= chord_array
134-
solver.sol.x_airf_array .= x_airf_array
135-
solver.sol.y_airf_array .= y_airf_array
136-
solver.sol.z_airf_array .= z_airf_array
132+
solver.sol._va_array .= va_array
133+
solver.sol._chord_array .= chord_array
134+
solver.sol._x_airf_array .= x_airf_array
135+
solver.sol._y_airf_array .= y_airf_array
136+
solver.sol._z_airf_array .= z_airf_array
137137
result = @benchmark gamma_loop!(
138138
$solver,
139139
$body_aero,

test/bench2.jl

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using LinearAlgebra
2+
using VortexStepMethod
3+
using BenchmarkTools
4+
using Test
5+
6+
using Pkg
7+
8+
if !("CSV" keys(Pkg.project().dependencies))
9+
using TestEnv
10+
TestEnv.activate()
11+
end
12+
13+
# Step 1: Define wing parameters
14+
n_panels = 20 # Number of panels
15+
span = 20.0 # Wing span [m]
16+
chord = 1.0 # Chord length [m]
17+
v_a = 20.0 # Magnitude of inflow velocity [m/s]
18+
density = 1.225 # Air density [kg/m³]
19+
alpha_deg = 30.0 # Angle of attack [degrees]
20+
alpha = deg2rad(alpha_deg)
21+
22+
# Step 2: Create wing geometry with linear panel distribution
23+
wing = Wing(n_panels, spanwise_distribution=LINEAR)
24+
25+
# Add wing sections - defining only tip sections with inviscid airfoil model
26+
add_section!(wing,
27+
[0.0, span/2, 0.0], # Left tip LE
28+
[chord, span/2, 0.0], # Left tip TE
29+
INVISCID)
30+
add_section!(wing,
31+
[0.0, -span/2, 0.0], # Right tip LE
32+
[chord, -span/2, 0.0], # Right tip TE
33+
INVISCID)
34+
35+
# Step 3: Initialize aerodynamics
36+
wa = BodyAerodynamics([wing])
37+
38+
# Set inflow conditions
39+
vel_app = [cos(alpha), 0.0, sin(alpha)] .* v_a
40+
set_va!(wa, vel_app)
41+
42+
# Step 4: Initialize solvers for both LLT and VSM methods
43+
P = length(wa.panels)
44+
vsm_solver = Solver{P}(aerodynamic_model_type=VSM)
45+
46+
@testset "Allocation Tests for solve() and solve!()" begin
47+
# Step 5: Solve using both methods
48+
result = @benchmark solve_base!($vsm_solver, $wa, nothing) # 51 allocations
49+
@test result.allocs <= 55
50+
# time Python: 32.0 ms Ryzen 7950x
51+
# time Julia: 0.45 ms Ryzen 7950x
52+
result = @benchmark sol = solve!($vsm_solver, $wa, nothing) # 85 allocations
53+
@test result.allocs <= 89
54+
end
55+
nothing
56+
sol = solve!(vsm_solver, wa, nothing) # 85 allocations

0 commit comments

Comments
 (0)