diff --git a/CHANGELOG.md b/CHANGELOG.md index b2c939fe..c06c68a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/Project.toml b/Project.toml index 84d898de..b914de71 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "VortexStepMethod" uuid = "ed3cd733-9f0f-46a9-93e0-89b8d4998dd9" authors = ["1-Bart-1 ", "Oriol Cayon and contributors"] -version = "4.1.0" +version = "4.1.1" [workspace] projects = ["examples", "docs", "test"] diff --git a/src/wing_geometry.jl b/src/wing_geometry.jl index be6d5ebd..6abc004c 100644 --- a/src/wing_geometry.jl +++ b/src/wing_geometry.jl @@ -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}) @@ -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 diff --git a/test/yaml_geometry/test_yaml_wing_deformation.jl b/test/yaml_geometry/test_yaml_wing_deformation.jl index c0d71ff7..124b988b 100644 --- a/test/yaml_geometry/test_yaml_wing_deformation.jl +++ b/test/yaml_geometry/test_yaml_wing_deformation.jl @@ -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