Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## VortexStepMethod v4.1.1 2026-08-19

### Fixed
- Section twist now rotates the chord fully about the spanwise axis. The axial
Rodrigues term was missing, so a swept or dihedral section's along-span chord
component was scaled by `cos(theta)`: the chord shortened and tilted out of the
twist plane, growing as `theta^2`.

## VortexStepMethod v4.1.0 2026-08-17

### Added
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "VortexStepMethod"
uuid = "ed3cd733-9f0f-46a9-93e0-89b8d4998dd9"
authors = ["1-Bart-1 <bart@vandelint.net>", "Oriol Cayon and contributors"]
version = "4.1.0"
version = "4.1.1"

[workspace]
projects = ["examples", "docs", "test"]
Expand Down
8 changes: 7 additions & 1 deletion src/wing_geometry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,10 @@ end

Rotate each refined section's TE point around its LE point by the given per-section
twist angle, using `wing.non_deformed_sections` as the reference geometry.

The rotation is a full Rodrigues rotation about the spanwise axis. A swept or
dihedral section has a chord component along that axis, and dropping the axial
term would scale it by `cos(theta)`, shortening the chord and tilting it.
"""
function _apply_refined_section_thetas!(wing::Wing{P, T}, section_thetas) where {P, T}
local_y = zeros(MVector{3, T})
Expand Down Expand Up @@ -677,8 +681,10 @@ function _apply_refined_section_thetas!(wing::Wing{P, T}, section_thetas) where

chord .= section.TE_point .- le
normal .= chord × local_y
axial = local_y ⋅ chord
@. wing.refined_sections[i].TE_point = le +
cos(theta) * chord - sin(theta) * normal
cos(theta) * chord - sin(theta) * normal +
(1 - cos(theta)) * axial * local_y
end
return nothing
end
Expand Down
16 changes: 16 additions & 0 deletions test/yaml_geometry/test_yaml_wing_deformation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -271,4 +271,20 @@ using Test
@test wing.refined_section_weight[1] ≈ 1.0
@test wing.refined_section_weight[end] ≈ 0.0
end

@testset "Twist rotates a swept chord rigidly" begin
# A swept or dihedral section's chord has a component along the twist
# axis, which only a full rotation carries through unchanged.
wing = Wing(8; n_unrefined_sections=3)
add_section!(wing, [0.0, 5.0, 1.0], [1.0, 4.6, 1.0], INVISCID)
add_section!(wing, [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], INVISCID)
add_section!(wing, [0.0, -5.0, 1.0], [1.0, -4.6, 1.0], INVISCID)
refine!(wing)
chords = [norm(s.TE_point - s.LE_point) for s in wing.refined_sections]
for theta in (0.05, 0.2, 0.5)
VortexStepMethod.unrefined_deform!(wing, fill(theta, 3), nothing)
twisted = [norm(s.TE_point - s.LE_point) for s in wing.refined_sections]
@test maximum(abs.(twisted .- chords) ./ chords) < 1e-12
end
end
end
Loading