Skip to content
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Added

- `Solver(settings)` and `Solver(n_panels, n_unrefined_sections)` build a solver without
a `BodyAerodynamics`; keyword arguments override the settings.
- Spanwise-flow viscous drag correction (Gaunaa et al. 2024,
doi:10.1088/1742-6596/2767/2/022068): each section gets a drag increment and a force
along its span from the flow across it, in `solve!`, `solve` and `linearize`. Opt-in
Expand All @@ -17,9 +19,15 @@
- The Makie `plot!` methods for a `Panel` or a `BodyAerodynamics` return a
`Vector{Makie.AbstractPlot}` instead of a `Vector{Any}`; for a `BodyAerodynamics`
drawn as flat panels it is one flat list rather than a list per panel.
- `Solver(body_aero; kwargs...)` and `Solver(body_aero, settings)` are deprecated and warn
on use; build the solver with `Solver(settings)` or
`Solver(n_panels, n_unrefined_sections)` instead.

### Fixed

- `solve!` and `solve` throw a `DimensionMismatch` naming both sizes for a `body_aero` whose
panel or unrefined-section count differs from the solver's, where they failed on a
broadcast partway through or silently left section results at zero.
- The `VSMSolution` docstring gives `lift_dist`, `drag_dist` and `panel_moment_dist` in
the per-unit-span units they hold, [N/m] and [Nm/m], instead of [N] and [Nm].
- Inside its vortex core, `velocity_3D_trailing_vortex!` induces an azimuthal velocity
Expand Down
2 changes: 1 addition & 1 deletion data/TUDELFT_V3_KITE/vsm_settings_coarse.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ condition:
# Define wing geometry files and discretization parameters
wings:
- name: V3_Kite # Wing identifier for output labeling
geometry_file: data/TUDELFT_V3_KITE/aero_geometry.yaml
geometry_file: data/TUDELFT_V3_KITE/aero_geometry_coarse_discretisation.yaml
n_panels: 54 # Total number of panels along wingspan
spanwise_panel_distribution: SPLIT_PROVIDED # Panel spacing algorithm
spanwise_direction: [0.0, 1.0, 0.0] # Unit vector defining wingspan direction
Expand Down
4 changes: 2 additions & 2 deletions docs/src/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ julia> set_va!(body_aero, va_vec, [0, 0, 0.1])
#### Step 5: Initialize solvers for both LLT and VSM methods

```julia
julia> llt_solver = Solver(body_aero; aerodynamic_model_type=LLT)
julia> vsm_solver = Solver(body_aero; aerodynamic_model_type=VSM)
julia> llt_solver = Solver(wing.n_panels, wing.n_unrefined_sections; aerodynamic_model_type=LLT)
julia> vsm_solver = Solver(wing.n_panels, wing.n_unrefined_sections; aerodynamic_model_type=VSM)
```

#### Step 6: Solve using both methods
Expand Down
3 changes: 3 additions & 0 deletions docs/src/private_functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ settings_range

### Solver, forces and circulation
```@docs
n_unrefined_sections
solver_kwargs
check_dimensions
calculate_AIC_matrices!
gamma_loop!
build_spanwise_laplacian!
Expand Down
2 changes: 1 addition & 1 deletion docs/src/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ settings = VSMSettings("my/vsm_settings.yaml"; data_prefix=false) # as written

wing = Wing(settings)
body_aero = BodyAerodynamics([wing])
solver = Solver(body_aero, settings)
solver = Solver(settings)
set_va!(body_aero, settings)
```

Expand Down
4 changes: 2 additions & 2 deletions examples/V3_kite.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ wing = Wing(settings)
refine!(wing)
body_aero = BodyAerodynamics([wing])
VortexStepMethod.reinit!(body_aero)
solver = Solver(body_aero, settings)
solver = Solver(settings)
solver.reference_point .= REFERENCE_POINT

if DEFORM
Expand Down Expand Up @@ -75,7 +75,7 @@ if NEURALFOIL
refine!(wing_nf)
body_nf = BodyAerodynamics([wing_nf])
VortexStepMethod.reinit!(body_nf)
solver_nf = Solver(body_nf, settings_nf)
solver_nf = Solver(settings_nf)
solver_nf.reference_point .= REFERENCE_POINT

# Reading the generated directory instead of the OBJ shows the airfoils the polar
Expand Down
8 changes: 6 additions & 2 deletions examples/V3_neuralfoil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,18 @@ wing_cfd = Wing(settings_cfd)
refine!(wing_cfd)
body_cfd = BodyAerodynamics([wing_cfd])
VortexStepMethod.reinit!(body_cfd)
solver_cfd = Solver(body_cfd, settings_cfd)
solver_cfd = Solver(settings_cfd)

println("Creating wing with NeuralFoil polars...")
wing_nf = Wing(nf_yaml; n_panels=50, spanwise_distribution=LINEAR)
refine!(wing_nf)
body_nf = BodyAerodynamics([wing_nf])
VortexStepMethod.reinit!(body_nf)
solver_nf = Solver(body_nf, settings_cfd)
settings_nf = VSMSettings("TUDELFT_V3_KITE/vsm_settings.yaml")
settings_nf.wings[1].geometry_file = nf_yaml
settings_nf.solver_settings.relaxation_factor = RELAXATION
settings_nf.solver_settings.artificial_damping = ARTIFICIAL_DAMPING
solver_nf = Solver(settings_nf)

# Compare CFD-polar and NeuralFoil-polar wings against published references
# (Poland 2025 RANS CFD and wind tunnel). `plot_polars` sweeps each solver over the
Expand Down
6 changes: 3 additions & 3 deletions examples/bench.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ va_vec = [cos(alpha), 0.0, sin(alpha)] .* va
set_va!(body_aero, va_vec)

# Step 4: Initialize solvers for both LLT and VSM methods
llt_solver = Solver(body_aero; aerodynamic_model_type=LLT)
vsm_solver = Solver(body_aero; aerodynamic_model_type=VSM)
llt_solver = Solver(wing.n_panels, wing.n_unrefined_sections; aerodynamic_model_type=LLT)
vsm_solver = Solver(wing.n_panels, wing.n_unrefined_sections; aerodynamic_model_type=VSM)

# Step 5: Solve using both methods
results_vsm = solve(vsm_solver, body_aero, nothing)
Expand All @@ -66,7 +66,7 @@ body_aero = BodyAerodynamics([wing])

# Create solvers
vsm_solver = Solver(
body_aero;
wing.n_panels, wing.n_unrefined_sections;
aerodynamic_model_type=VSM,
is_with_artificial_damping=false,
solver_type=LOOP,
Expand Down
55 changes: 10 additions & 45 deletions examples/billowing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,12 @@ literature_paths = [
"windtunnel_alpha_sweep_beta_00_0_Poland_2025_Rey_5e5.csv"),
]

# Load solver settings (coarse: 54 panels, matches 10-section geometry)
settings_data = VortexStepMethod.YAML.load_file(
joinpath(v3_dir, "vsm_settings_coarse.yaml"))
condition_cfg = settings_data["condition"]
solver_cfg = settings_data["solver_settings"]
wing_cfg = settings_data["wings"][1]
n_panels = wing_cfg["n_panels"]
# Coarse settings: 54 panels on the 10-section geometry
settings = VSMSettings(joinpath(v3_dir, "vsm_settings_coarse.yaml"); data_prefix=false)
settings.wings[1].geometry_file = joinpath(project_dir, settings.wings[1].geometry_file)
n_panels = settings.wings[1].n_panels

BILLOWING_PCT = get(wing_cfg, "billowing_percentage", 0.0)
BILLOWING_PCT = settings.wings[1].billowing_percentage

labels = [
"VSM flat",
Expand All @@ -49,9 +46,7 @@ labels = [
"WindTunnel Re=5e5",
]

# Load coarse geometry (10 structural rib sections)
geom_data = VortexStepMethod.YAML.load_file(
joinpath(v3_dir, "aero_geometry_coarse_discretisation.yaml"))
geom_data = VortexStepMethod.YAML.load_file(settings.wings[1].geometry_file)
section_headers = geom_data["wing_sections"]["headers"]
section_rows = geom_data["wing_sections"]["data"]

Expand Down Expand Up @@ -84,43 +79,13 @@ body_aero_bill = BodyAerodynamics([wing_bill])
VortexStepMethod.reinit!(body_aero_bill)

# --- Build solvers ---
function make_solver(body_aero)
Solver(body_aero;
solver_type=(solver_cfg["solver_type"] == "NONLIN" ?
NONLIN : LOOP),
aerodynamic_model_type=getproperty(
VortexStepMethod,
Symbol(solver_cfg["aerodynamic_model_type"])),
density=solver_cfg["density"],
max_iterations=solver_cfg["max_iterations"],
rtol=solver_cfg["rtol"],
tol_reference_error=solver_cfg["tol_reference_error"],
relaxation_factor=solver_cfg["relaxation_factor"],
is_with_artificial_damping=solver_cfg["artificial_damping"],
artificial_damping=(
k2=solver_cfg["k2"], k4=solver_cfg["k4"]),
type_initial_gamma_distribution=getproperty(
VortexStepMethod,
Symbol(solver_cfg["type_initial_gamma_distribution"])),
use_gamma_prev=get(solver_cfg, "use_gamma_prev",
get(solver_cfg, "use_gamme_prev", true)),
core_radius_fraction=solver_cfg["core_radius_fraction"],
mu=solver_cfg["mu"],
is_only_f_and_gamma_output=get(
solver_cfg, "calc_only_f_and_gamma", false),
correct_aoa=get(solver_cfg, "correct_aoa", false),
reference_point=get(solver_cfg, "reference_point",
[0.422646, 0.0, 9.3667]),
)
end

solver_flat = make_solver(body_aero_flat)
solver_bill = make_solver(body_aero_bill)
solver_flat = Solver(settings; reference_point=[0.422646, 0.0, 9.3667])
solver_bill = Solver(settings; reference_point=[0.422646, 0.0, 9.3667])

# --- Set flight conditions ---
wind_speed = condition_cfg["wind_speed"]
wind_speed = settings.condition.wind_speed
angle_of_attack_deg = 10.0
sideslip_deg = condition_cfg["beta"]
sideslip_deg = settings.condition.beta

α0 = deg2rad(angle_of_attack_deg)
β0 = deg2rad(sideslip_deg)
Expand Down
2 changes: 1 addition & 1 deletion examples/linearize_check.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ yaml = obj_to_yaml(
wing = Wing(yaml; n_panels=16)
body_aero = BodyAerodynamics([wing])

solver = Solver(body_aero;
solver = Solver(wing.n_panels, wing.n_unrefined_sections;
aerodynamic_model_type=VSM,
is_with_artificial_damping=false,
rtol=1e-7,
Expand Down
3 changes: 2 additions & 1 deletion examples/obj_to_yaml_kite.jl
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ refine!(wing)
body_aero = BodyAerodynamics([wing])
VortexStepMethod.reinit!(body_aero)

solver = Solver(body_aero; aerodynamic_model_type=VSM, rtol=1e-5, solver_type=LOOP)
solver = Solver(wing.n_panels, wing.n_unrefined_sections;
aerodynamic_model_type=VSM, rtol=1e-5, solver_type=LOOP)
set_va!(body_aero, [cos(deg2rad(8)) * va, 0.0, sin(deg2rad(8)) * va])
results = VortexStepMethod.solve(solver, body_aero; log=true)

Expand Down
2 changes: 1 addition & 1 deletion examples/pyramid_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ vsm_settings = VSMSettings("pyramid_model/vsm_settings.yaml")
wing = Wing(vsm_settings)
refine!(wing)
body_aero = BodyAerodynamics([wing])
solver = Solver(body_aero, vsm_settings)
solver = Solver(vsm_settings)

# Set flight conditions from settings
set_va!(body_aero, vsm_settings)
Expand Down
6 changes: 4 additions & 2 deletions examples/ram_air_kite.jl
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,15 @@ fig_audit = plot_slices_3d(joinpath("data", "ram_air_kite", "polars_xfoil");
delta=1.0, obj_path=obj_path)
GLMakie.save("ram_air_slices_audit.png", fig_audit)
body_xfoil = BodyAerodynamics([wing_xfoil])
solver_xfoil = Solver(body_xfoil; aerodynamic_model_type=VSM, rtol=1e-5, solver_type=LOOP,
solver_xfoil = Solver(wing_xfoil.n_panels, wing_xfoil.n_unrefined_sections;
aerodynamic_model_type=VSM, rtol=1e-5, solver_type=LOOP,
relaxation_factor=RELAXATION, is_with_artificial_damping=ARTIFICIAL_DAMPING)

println("Creating NeuralFoil wing...")
wing_nf = matrix_wing(NF_SOLVER, "polars_neuralfoil")
body_nf = BodyAerodynamics([wing_nf])
solver_nf = Solver(body_nf; aerodynamic_model_type=VSM, rtol=1e-5, solver_type=LOOP,
solver_nf = Solver(wing_nf.n_panels, wing_nf.n_unrefined_sections;
aerodynamic_model_type=VSM, rtol=1e-5, solver_type=LOOP,
relaxation_factor=RELAXATION, is_with_artificial_damping=ARTIFICIAL_DAMPING)

# Compare using plot_polars
Expand Down
4 changes: 2 additions & 2 deletions examples/rectangular_wing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ va_vec = [cos(alpha), 0.0, sin(alpha)] .* va
set_va!(body_aero, va_vec, [0, 0, 0.1])

# Step 4: Initialize solvers for both LLT and VSM methods
llt_solver = Solver(body_aero; aerodynamic_model_type=LLT)
vsm_solver = Solver(body_aero; aerodynamic_model_type=VSM)
llt_solver = Solver(wing.n_panels, wing.n_unrefined_sections; aerodynamic_model_type=LLT)
vsm_solver = Solver(wing.n_panels, wing.n_unrefined_sections; aerodynamic_model_type=VSM)

# Step 5: Solve using both methods
results_llt = solve(llt_solver, body_aero)
Expand Down
4 changes: 2 additions & 2 deletions examples/stall_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ refine!(CAD_wing)
body_aero = BodyAerodynamics([CAD_wing])

# Create solvers
vsm_solver = Solver(body_aero;
vsm_solver = Solver(CAD_wing.n_panels, CAD_wing.n_unrefined_sections;
aerodynamic_model_type=VSM,
is_with_artificial_damping=false
)
VSM_with_stall_correction = Solver(body_aero;
VSM_with_stall_correction = Solver(CAD_wing.n_panels, CAD_wing.n_unrefined_sections;
aerodynamic_model_type=VSM,
is_with_artificial_damping=true
)
Expand Down
2 changes: 1 addition & 1 deletion mwes/mwe_warntype.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ refine!(wing)
body_aero = BodyAerodynamics([wing])
va_vec = [cos(alpha), 0.0, sin(alpha)] .* 20.0
set_va!(body_aero, va_vec)
solver = Solver(body_aero)
solver = Solver(wing.n_panels, wing.n_unrefined_sections)

va_dist = ones(n_panels)
va_unit_dist = ones(n_panels, 3)
Expand Down
Loading
Loading