Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
f078c77
Make sure 0 groups works
1-Bart-1 Oct 25, 2025
dba02d9
Unify Wing and RamAirWing
1-Bart-1 Oct 26, 2025
1581170
Restore test
1-Bart-1 Oct 26, 2025
df97a21
Not passing bench
1-Bart-1 Oct 29, 2025
e28c0ce
Working grouping
1-Bart-1 Oct 29, 2025
c27d157
Working tests except aqua
1-Bart-1 Oct 29, 2025
3535643
Working tests with aqua
1-Bart-1 Oct 29, 2025
2b03015
Don't deform tuple
1-Bart-1 Nov 1, 2025
4ca52ac
Option to not use data prefix
1-Bart-1 Nov 6, 2025
128f720
Add obj set
1-Bart-1 Nov 7, 2025
74d7cb1
Merge branch 'wing_groups' into wing_groups_unify_wing
1-Bart-1 Nov 7, 2025
7a7e203
Old tests not failing
1-Bart-1 Nov 7, 2025
2ef5c3e
Passing tests
1-Bart-1 Nov 7, 2025
dad4e72
Add cl cd cm group array
1-Bart-1 Nov 7, 2025
1438b55
Added tests and settings
1-Bart-1 Nov 7, 2025
80fc357
Improved obj file reading
1-Bart-1 Nov 8, 2025
b3ef2b0
Fixed deform
1-Bart-1 Nov 8, 2025
8e28721
Test on 1.10 and 1.11
1-Bart-1 Nov 8, 2025
a6a8337
Disable mapping kwarg
1-Bart-1 Nov 8, 2025
3502599
Add kwarg to sort sections
1-Bart-1 Nov 8, 2025
f55597f
Add groups
1-Bart-1 Nov 8, 2025
872bdfd
Correct width
1-Bart-1 Nov 9, 2025
a1df712
Add makie plotting
1-Bart-1 Nov 10, 2025
084e7bb
Don't use corrected alpha
1-Bart-1 Nov 16, 2025
112fced
Updated settings
1-Bart-1 Nov 16, 2025
2031982
Use plot and plot! with obs
1-Bart-1 Nov 16, 2025
e23eb99
Use unrefined sections
1-Bart-1 Nov 21, 2025
988b1c9
Use glmakie
1-Bart-1 Dec 4, 2025
ab1a282
Use unrefined segments and no groups
1-Bart-1 Dec 4, 2025
e500260
Working ram example
1-Bart-1 Dec 6, 2025
a76454c
Add combined plot
1-Bart-1 Dec 6, 2025
55d2d0d
Dont use old vsm
1-Bart-1 Dec 6, 2025
3f0cf3d
Actually remove super confusing unrefined panels, simplify to use unr…
1-Bart-1 Dec 6, 2025
c536ba9
Adjust for using the unrefined segment approach
1-Bart-1 Dec 6, 2025
860f38d
Dist for per panel, unrefined_dist for per unrefined section
1-Bart-1 Dec 6, 2025
a971574
Update the deform functions to make more sense
1-Bart-1 Dec 6, 2025
4a6a371
Update tests for recent changes
1-Bart-1 Dec 6, 2025
6a7aca5
Rename to more logical name
1-Bart-1 Dec 6, 2025
f130157
Add claude md
1-Bart-1 Dec 6, 2025
ddfc633
Rename
1-Bart-1 Dec 6, 2025
4c16cde
Simpler example, angle at wingtips
1-Bart-1 Dec 6, 2025
cd1cc5b
Add unreleased news
1-Bart-1 Dec 6, 2025
135a713
Remove last instances of groups
1-Bart-1 Dec 6, 2025
c9febb9
Removed groups
1-Bart-1 Dec 6, 2025
b4a90f8
Add tests for smoothing and non smoothing deform
1-Bart-1 Dec 6, 2025
cbbc311
Add everything plotting function
1-Bart-1 Dec 7, 2025
66ac77d
Manual refine
1-Bart-1 Dec 7, 2025
ec46e3b
Improved makie plotting
1-Bart-1 Dec 7, 2025
9e535da
Deformable yaml kite
1-Bart-1 Dec 7, 2025
56f219e
Fix for updated separate refine
1-Bart-1 Dec 7, 2025
d3bc096
Move Obj to refinement method
1-Bart-1 Dec 7, 2025
662c3eb
Add obj wing refinement test
1-Bart-1 Dec 7, 2025
56687a4
Auto refine yaml wing
1-Bart-1 Dec 7, 2025
25fb6f0
Width is sum not average
1-Bart-1 Dec 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
matrix:
version:
- '1.10'
- '1'
- '1.11'
os:
- ubuntu-latest
build_is_production_build:
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Manifest.toml
Manifest*.toml
.vscode/settings.json
venv
results/TUDELFT_V3_LEI_KITE/polars/$C_L$ vs $C_D$.pdf
Expand All @@ -9,3 +9,4 @@ results/TUDELFT_V3_LEI_KITE/polars/tutorial_testing_stall_model_n_panels_54_dist
!test/data/*.bin
Manifest-v1.11.toml
Manifest-v1.10.toml
CLAUDE.md
24 changes: 24 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
## VortexStepMethod [Unreleased]
### Changed
- Unified `Wing` and `RamAirWing` into single `Wing` type (`RamAirWing` now alias for `ObjWing`)
- Renamed `ram_geometry.jl` to `obj_geometry.jl`
- Wing geometry uses unrefined sections with automatic panel-to-section mapping
- Consistent naming: variables ending in `_dist` are per-panel, `_unrefined_dist` per unrefined section
- `VSMSolution` field names: `panel_width_array` → `width_dist`, `alpha_array` → `alpha_dist`, etc.
- Enhanced Makie extension with `plot_combined_analysis` for combined plotting

### Added
- `n_unrefined_sections` field in `Wing` for tracking pre-refinement sections
- `refined_panel_mapping` for automatic panel-to-section association
- Unrefined distribution fields in `VSMSolution`: `cl_unrefined_dist`, `cd_unrefined_dist`, `cm_unrefined_dist`, `alpha_unrefined_dist`, `moment_unrefined_dist`
- `PanelDistribution.NONE` for wings already refined
- Kwarg `sort_sections` for section ordering
- YAML wing deformation tests
- Unrefined distribution tests

### Removed
- Panel grouping (replaced with unrefined section mapping)
- `PanelGroupingMethod` enum (deprecated, grouping automatic via mapping)
- `n_groups` and `grouping_method` from settings files and structs
- `n_groups` field from `WingSettings` and `SolverSettings`

## VortexStepMethod v2.3.0 2025-10-16
### Added
- A Makie plotting extension.
Expand Down
20 changes: 0 additions & 20 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,11 @@ VortexStepMethodControlPlotsExt = "ControlPlots"
VortexStepMethodMakieExt = "Makie"

[compat]
Aqua = "0.8"
BenchmarkTools = "1"
CSV = "0.10"
Colors = "0.13"
ControlPlots = "0.2.5"
DataFrames = "1.7"
DefaultApplication = "1"
DelimitedFiles = "1"
DifferentiationInterface = "0.7.4"
Documenter = "1.8"
FiniteDiff = "2.27.0"
Interpolations = "0.15, 0.16"
LaTeXStrings = "1"
Expand All @@ -60,28 +55,13 @@ Parameters = "0.12"
Pkg = "1"
PreallocationTools = "0.4.31"
PrecompileTools = "1.2.1"
Random = "1.10.0"
RecursiveArrayTools = "3 - 3.36.0"
SciMLBase = "2.77.0"
Serialization = "1"
StaticArrays = "1"
Statistics = "1"
StructMapping = "0.2.3"
Test = "1"
Timers = "0.1"
Xfoil = "1.1.0"
YAML = "0.4.13"
julia = "1.10, 1.11"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
ControlPlots = "23c2ee80-7a9e-4350-b264-8e670f12517c"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test", "DataFrames", "CSV", "Documenter", "BenchmarkTools", "ControlPlots", "Aqua", "Random"]
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ if you haven't already. On Linux, make sure that Python3 and Matplotlib are inst
```
sudo apt install python3-matplotlib
```
Furthermore, the packages `TestEnv` and `ControlPlots` must be installed globally:
Furthermore, the package `ControlPlots` must be installed globally:
```
julia -e 'using Pkg; Pkg.add("TestEnv"); Pkg.add("ControlPlots")'
julia -e 'using Pkg; Pkg.add("ControlPlots")'
```

Before installing this software it is suggested to create a new project, for example like this:
Expand Down
2 changes: 1 addition & 1 deletion bin/install
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fi

export JULIA_PKG_SERVER_REGISTRY_PREFERENCE=eager

julia -e 'using Pkg; Pkg.add("TestEnv"); Pkg.add("ControlPlots")'
julia -e 'using Pkg; Pkg.add("ControlPlots")'

julia --project -e 'include("bin/install.jl")'

Expand Down
2 changes: 0 additions & 2 deletions data/TUDELFT_V3_KITE/vsm_settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
# NEWTON: Newton-Raphson nonlinear solver
#
# USAGE NOTES:
# - n_panels should be divisible by n_groups for proper load balancing
# - Higher n_panels improves accuracy but increases computation time
# - Lower relaxation_factor if convergence issues occur

Expand All @@ -46,7 +45,6 @@ wings:
- name: V3_Kite # Wing identifier for output labeling
geometry_file: data/TUDELFT_V3_KITE/aero_geometry.yaml
n_panels: 36 # Total number of panels along wingspan
n_groups: 1 # Number of panel groups (must divide n_panels)
spanwise_panel_distribution: LINEAR # Panel spacing algorithm
spanwise_direction: [0.0, 1.0, 0.0] # Unit vector defining wingspan direction
remove_nan: true # Remove NaN values from polar data
Expand Down
2 changes: 0 additions & 2 deletions data/pyramid_model/vsm_settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
# NEWTON: Newton-Raphson nonlinear solver
#
# USAGE NOTES:
# - n_panels should be divisible by n_groups for proper load balancing
# - Higher n_panels improves accuracy but increases computation time
# - Lower relaxation_factor if convergence issues occur

Expand All @@ -46,7 +45,6 @@ wings:
- name: V3_Kite # Wing identifier for output labeling
geometry_file: data/pyramid_model/wing_geometry.yaml
n_panels: 2 # Total number of panels along wingspan
n_groups: 1 # Number of panel groups (must divide n_panels)
spanwise_panel_distribution: LINEAR # Panel spacing algorithm
spanwise_direction: [0.0, 1.0, 0.0] # Unit vector defining wingspan direction
remove_nan: true # Remove NaN values from polar data
Expand Down
5 changes: 1 addition & 4 deletions data/ram_air_kite/vsm_settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,14 @@ PanelDistribution:
InitialGammaDistribution:
ELLIPTIC: Elliptic distribution
ZEROS: Constant distribution

wings:
- name: main_wing
n_panels: 40
n_groups: 40
spanwise_panel_distribution: LINEAR
spanwise_panel_distribution: NONE
spanwise_direction: [0.0, 1.0, 0.0]
remove_nan: true
solver_settings:
n_panels: 40
n_groups: 40
aerodynamic_model_type: VSM
density: 1.225 # air density [kg/m³]
max_iterations: 1500
Expand Down
2 changes: 0 additions & 2 deletions data/ram_air_kite/vsm_settings_dual.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,11 @@ InitialGammaDistribution:
wings:
- name: main_wing
n_panels: 40
n_groups: 40
spanwise_panel_distribution: LINEAR
spanwise_direction: [0.0, 1.0, 0.0]
remove_nan: true
- name: tail
n_panels: 20
n_groups: 20
spanwise_panel_distribution: COSINE
spanwise_direction: [0.0, 1.1, 0.0]
remove_nan: false
Expand Down
6 changes: 0 additions & 6 deletions docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
using Pkg
if ("TestEnv" ∈ keys(Pkg.project().dependencies))
if ! ("Documents" ∈ keys(Pkg.project().dependencies))
using TestEnv; TestEnv.activate()
end
end
using ControlPlots
using VortexStepMethod
using Documenter
Expand Down
1 change: 1 addition & 0 deletions docs/src/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
| AIC | Aerodynamic Influence Coefficient (AIC). The AIC matrix represents the relationship between the induced velocities or pressures on aerodynamic surfaces and the circulation strength or modal deformations of the lifting surfaces.|
| inviscid | A fluid flow in which viscosity is considered negligible or zero. This means that there is no internal friction between the fluid layers, and the effects of viscosity on the flow are assumed to be insignificant. |
| Panel | Flat surface element in 3D that approximate the contour of the aerodynamic body being studied.|
| Panel Group | A collection of panels whose aerodynamic forces and moments are summed together. Groups can be defined using EQUAL_SIZE (sequential grouping) or REFINE (based on original unrefined structure) methods.|
| Section |A wing section, also known as an airfoil or aerofoil, is the cross-sectional shape of an aircraft wing.|
| Span | Distance from one wing tip to the other wing tip. |
| Polar | The polar typically plots the coefficient of lift (CL) against the coefficient of drag (CD), with the angle of attack as a parameter along the curve. |
Expand Down
4 changes: 2 additions & 2 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ if you haven't already. On Linux, make sure that Python3 and Matplotlib are inst
```
sudo apt install python3-matplotlib
```
Furthermore, the packages `TestEnv` and `ControlPlots` must be installed globally:
Furthermore, the package `ControlPlots` must be installed globally:
```
julia -e 'using Pkg; Pkg.add("TestEnv"); Pkg.add("ControlPlots")'
julia -e 'using Pkg; Pkg.add("ControlPlots")'
```

Before installing this software it is suggested to create a new project, for example like this:
Expand Down
2 changes: 1 addition & 1 deletion docs/src/private_functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ CurrentModule = VortexStepMethod
calculate_AIC_matrices!
update_panel_properties!
calculate_inertia_tensor
group_deform!
unrefined_deform!
```
23 changes: 23 additions & 0 deletions docs/src/tips_and_tricks.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,29 @@ The following bodies can be simulated:
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.
It should have two columns, one for the `x` and one for the `y` coordinate of the 2D polar that is used.

## Unrefined Section Distribution
When creating a wing, panel forces and moments are automatically computed for each unrefined section. The unrefined sections correspond to the original geometry sections you define, while panels represent the refined mesh used for aerodynamic calculations.

### How It Works
The solver automatically tracks which panels belong to which original unrefined section. After refinement (e.g., splitting each section into multiple panels), the aerodynamic forces and moments are aggregated back to the unrefined section level.

```julia
# Create wing with 4 sections, refined to 40 panels
wing = Wing(40)
add_section!(wing, [0, 5, 0], [1, 5, 0], INVISCID) # Section 1
add_section!(wing, [0, 2.5, 0], [1, 2.5, 0], INVISCID) # Section 2
add_section!(wing, [0, 0, 0], [1, 0, 0], INVISCID) # Section 3
add_section!(wing, [0, -5, 0], [1, -5, 0], INVISCID) # Section 4
```

The 40 panels are distributed across the 3 unrefined panels (sections 1-2, 2-3, 3-4). Forces and moments are computed per panel during the solve, then automatically aggregated to the 3 unrefined panels for output.

This approach is useful for:
- LEI kites where you want loads per rib
- Wings with discrete control surfaces
- Cases where physical structure doesn't align with uniform panel distribution
- Dynamic simulations where you have fewer structural segments than panels needed for accurate VSM aerodynamics. For example, a 6-segment structural model can be combined with 40-panel aerodynamics, with loads automatically mapped back to the 6 structural segments.

## RAM-air kite model
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.

Expand Down
11 changes: 6 additions & 5 deletions docs/src/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Model
WingType
AeroModel
PanelDistribution
PanelGroupingMethod
InitialGammaDistribution
SolverStatus
```
Expand All @@ -24,17 +25,17 @@ AeroData
```

## Wing Geometry, Panel and Aerodynamics
A body is constructed of one or more abstract wings. An abstract wing can be a Wing or a RamAirWing.
A Wing/ RamAirWing has one or more sections.
A body is constructed of one or more abstract wings. All wings are of type Wing.
A Wing has one or more sections and can be created from YAML files or OBJ geometry.
```@docs
Section
Section(LE_point::PosVector, TE_point::PosVector, aero_model)
Wing
Wing(n_panels::Int; spanwise_distribution::PanelDistribution=LINEAR,
spanwise_direction::PosVector=MVec3([0.0, 1.0, 0.0]))
RamAirWing
RamAirWing(obj_path, dat_path; alpha=0.0, crease_frac=0.75, wind_vel=10., mass=1.0,
n_panels=54, n_sections=n_panels+1, spanwise_distribution=UNCHANGED,
ObjWing
ObjWing(obj_path, dat_path; alpha=0.0, crease_frac=0.75, wind_vel=10., mass=1.0,
n_panels=54, n_sections=n_panels+1, spanwise_distribution=UNCHANGED,
spanwise_direction=[0.0, 1.0, 0.0])
BodyAerodynamics
```
Expand Down
11 changes: 11 additions & 0 deletions examples/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
ControlPlots = "23c2ee80-7a9e-4350-b264-8e670f12517c"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
VortexStepMethod = "ed3cd733-9f0f-46a9-93e0-89b8d4998dd9"

[sources]
VortexStepMethod = {path = ".."}
71 changes: 29 additions & 42 deletions examples/V3_kite.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
using LinearAlgebra
using VortexStepMethod
using ControlPlots
using GLMakie

PLOT = true
USE_TEX = false
DEFORM = false

project_dir = dirname(dirname(pathof(VortexStepMethod))) # Go up one level from src to project root#
literature_paths = [
Expand All @@ -11,8 +15,8 @@ literature_paths = [
]
labels= [
"Julia VSM 2D CFD PCHIP",
"CFD RANS Re=5e5",
"CFD RANS Re=10e5 (With Struts)",
"CFD RANS Re=5e5",
"CFD RANS Re=10e5 (With Struts)",
"Python VSM 2D CFD PCHIP Re=5e5",
"Wind Tunnel Re=5e5 (With Struts)"
]
Expand All @@ -22,7 +26,20 @@ settings = VSMSettings("TUDELFT_V3_KITE/vsm_settings.yaml")

# Create wing, body_aero, and solver objects using settings
wing = Wing(settings)
refine!(wing)
body_aero = BodyAerodynamics([wing])
VortexStepMethod.reinit!(body_aero)

if DEFORM
VortexStepMethod.unrefined_deform!(
wing,
deg2rad.(range(-10, 10, length=wing.n_unrefined_sections)),
deg2rad.(range(0, 0, length=wing.n_unrefined_sections));
smooth=true
)
VortexStepMethod.reinit!(body_aero; init_aero=false)
end

solver = Solver(body_aero, settings)

# Set flight conditions from settings
Expand All @@ -38,53 +55,23 @@ yaw_rate = settings.condition.yaw_rate
PLOT = true
USE_TEX = false

# Plotting polars
PLOT && plot_polars(
[solver],
[body_aero],
labels,
# Solve and plot combined analysis
results = VortexStepMethod.solve(solver, body_aero; log=true)
PLOT && plot_combined_analysis(
solver,
body_aero,
results;
solver_label="VSM",
literature_path_list=literature_paths,
angle_range=range(-5, 25, length=30),
angle_type="angle_of_attack",
angle_of_attack=angle_of_attack_deg,
side_slip=sideslip_deg,
v_a=wind_speed,
title="$(wing.n_panels)_panels_$(wing.spanwise_distribution)_from_yaml_settings",
data_type=".pdf",
is_save=false,
is_show=true,
use_tex=USE_TEX
)


# Plotting geometry
results = VortexStepMethod.solve(solver, body_aero; log=true)
PLOT && plot_geometry(
body_aero,
"";
data_type=".svg",
save_path="",
is_save=false,
is_show=true,
view_elevation=15,
view_azimuth=-120,
use_tex=USE_TEX
)


# Plotting spanwise distributions
body_y_coordinates = [panel.aero_center[2] for panel in body_aero.panels]

PLOT && plot_distribution(
[body_y_coordinates],
[results],
["VSM"];
title="CAD_spanwise_distributions_alpha_$(round(angle_of_attack_deg, digits=1))_delta_$(round(sideslip_deg, digits=1))_yaw_$(round(yaw_rate, digits=1))_v_a_$(round(wind_speed, digits=1))",
data_type=".pdf",
is_save=false,
title="TU Delft V3 Kite",
is_show=true,
use_tex=USE_TEX
)


nothing
nothing
Loading
Loading