Skip to content

Commit 46adf3d

Browse files
authored
Merge pull request #366 from OpenSourceAWE/agent/357-elliptic-initial-circulation-for-a-body-
Start ELLIPTIC circulation on each wing's own ellipse, so multi-wing bodies solve
2 parents ce77044 + 83b5ef6 commit 46adf3d

5 files changed

Lines changed: 70 additions & 37 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@
6868

6969
### Fixed
7070

71+
- `ELLIPTIC` initial circulation works on a body with more than one wing, where it threw
72+
an `ArgumentError`: each wing gets an ellipse over its own span, along its own
73+
`spanwise_direction` and centred on its own mid-span, also for a single wing off y = 0.
7174
- `get_lower_upper`, and with it the flap hinge in `deform_section`, takes the lower and
7275
upper surface heights where the contour crosses `x = crease_frac`. It took the nearest
7376
points below and above `y = 0`, which on a cambered section put the hinge near the

docs/src/private_functions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ calculate_relative_alpha_and_relative_velocity
3636
update_effective_angle_of_attack!
3737
calculate_stall_angle_list
3838
wing_span_flip
39+
spanwise_extent
3940
calculate_circulation_distribution_elliptical_wing
4041
_compute_reference_velocity_from_distribution
4142
smooth_distribution!

src/body_aerodynamics.jl

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ Main structure for calculating aerodynamic properties of bodies. Use the constru
2828
influence coefficients, used only for the corrected angle of attack
2929
- `projected_area::Float64` = 1.0: The area projected onto the xy-plane of the kite body reference frame [m²]
3030
- `c_ref::Float64` = 1.0: Reference chord length (max panel chord) [m]
31-
- `y::MVector{P, Float64}` = MVector{P,Float64}(zeros(P))
3231
- `cache::Vector{PreallocationTools.LazyBufferCache{typeof(identity), typeof(identity)}}` = [LazyBufferCache() for _ in 1:15]
3332
"""
3433
@with_kw mutable struct BodyAerodynamics{P, W<:AbstractWing, T, PN<:Panel{T}}
@@ -50,7 +49,6 @@ Main structure for calculating aerodynamic properties of bodies. Use the constru
5049
AIC_aero_center::Array{T, 3} = zeros(T, P, P, 3)
5150
projected_area::T = one(T)
5251
c_ref::T = one(T)
53-
y::MVector{P, T} = zeros(MVector{P, T})
5452
cache::Vector{PreallocationTools.LazyBufferCache{typeof(identity), typeof(identity)}} = [LazyBufferCache() for _ in 1:15]
5553
end
5654

@@ -494,30 +492,26 @@ Returns: nothing
494492
end
495493

496494
"""
497-
calculate_circulation_distribution_elliptical_wing(body_aero::BodyAerodynamics, gamma_0=1.0)
495+
calculate_circulation_distribution_elliptical_wing(gamma_i, body_aero::BodyAerodynamics,
496+
gamma_0=1.0)
498497
499-
Calculate circulation distribution for an elliptical wing.
500-
501-
Returns: nothing
498+
Write into `gamma_i` an elliptic circulation of peak `gamma_0` over each wing, its control
499+
points measured along that wing's `spanwise_direction` from the wing's mid-span.
502500
"""
503-
function calculate_circulation_distribution_elliptical_wing(gamma_i, body_aero::BodyAerodynamics, gamma_0=1.0)
504-
length(body_aero.wings) == 1 || throw(ArgumentError("Multiple wings not yet implemented"))
505-
506-
wing_span = body_aero.wings[1].span
507-
@debug "Wing span: $wing_span"
508-
509-
# Calculate y-coordinates of control points
510-
y = body_aero.y
511-
for (i, panel) in pairs(body_aero.panels)
512-
y[i] = panel.control_point[2]
501+
function calculate_circulation_distribution_elliptical_wing(gamma_i,
502+
body_aero::BodyAerodynamics, gamma_0=1.0)
503+
panel_offset = 0
504+
for wing in body_aero.wings
505+
lo, hi = spanwise_extent(wing)
506+
axis = normalize(wing.spanwise_direction)
507+
for i in panel_offset .+ (1:wing.n_panels)
508+
span_position = dot(body_aero.panels[i].control_point, axis) - (lo + hi) / 2
509+
# Clamped: a control point can lie outside the span of the unrefined sections
510+
gamma_i[i] = gamma_0 * sqrt(max(0.0, 1 - (2span_position / (hi - lo))^2))
511+
end
512+
panel_offset += wing.n_panels
513513
end
514-
515-
# Calculate elliptical distribution (clamp to avoid sqrt of negative
516-
# when control points lie outside the nominal span envelope)
517-
gamma_i .= gamma_0 * sqrt.(max.(0.0, 1 .- (2 .* y ./ wing_span).^2))
518-
519-
@debug "Calculated circulation distribution: $gamma_i"
520-
nothing
514+
return nothing
521515
end
522516

523517
"""

src/wing_geometry.jl

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1685,24 +1685,25 @@ function refine_mesh_with_billowing!(wing; reuse_aero_data::Bool=false)
16851685
end
16861686

16871687
"""
1688-
calculate_span(wing::AbstractWing)
1688+
spanwise_extent(wing::AbstractWing)
16891689
1690-
Calculate wing span along spanwise direction.
1690+
Lowest and highest projection of the unrefined sections' LE and TE points on `wing`'s
1691+
`spanwise_direction`, as `(lo, hi)` [m].
1692+
"""
1693+
function spanwise_extent(wing::AbstractWing)
1694+
axis = normalize(wing.spanwise_direction)
1695+
return extrema(dot(point, axis) for section in wing.unrefined_sections
1696+
for point in (section.LE_point, section.TE_point))
1697+
end
16911698

1692-
Returns:
1693-
Float64: Wing span
1699+
"""
1700+
calculate_span(wing::AbstractWing)
1701+
1702+
Wing span along `spanwise_direction` [m].
16941703
"""
16951704
function calculate_span(wing::AbstractWing)
1696-
# Normalize spanwise direction
1697-
vector_axis = wing.spanwise_direction ./ norm(wing.spanwise_direction)
1698-
1699-
# Get all points
1700-
all_points = reduce(vcat, [[section.LE_point, section.TE_point]
1701-
for section in wing.unrefined_sections])
1702-
1703-
# Project points and calculate span
1704-
projections = [dot(point, vector_axis) for point in all_points]
1705-
return maximum(projections) - minimum(projections)
1705+
lo, hi = spanwise_extent(wing)
1706+
return hi - lo
17061707
end
17071708

17081709
# Project point onto plane

test/body_aerodynamics/test_body_aerodynamics.jl

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,40 @@ end
616616
@test sol.force[3] > 2single.force[3]
617617
end
618618

619+
@testset "ELLIPTIC starts each wing on its own ellipse" begin
620+
control_y = [panel.control_point[2] for panel in single_aero.panels]
621+
ellipse = sqrt.(1 .- (2control_y ./ span) .^ 2)
622+
fin = Wing(n_panels; spanwise_direction=[0.0, 0.0, 1.0])
623+
for z in section_y .+ 5.0
624+
add_section!(fin, [0.0, 0.0, z], [1.0, 0.0, z], INVISCID)
625+
end
626+
refine!(fin)
627+
body_aero = BodyAerodynamics([wing_pair(section_y, n_panels, span + 1.0); fin])
628+
gamma = zeros(3n_panels)
629+
VortexStepMethod.calculate_circulation_distribution_elliptical_wing(gamma,
630+
body_aero)
631+
632+
@test gamma repeat(ellipse, 3)
633+
634+
lone_wing_gamma = zeros(n_panels)
635+
VortexStepMethod.calculate_circulation_distribution_elliptical_wing(
636+
lone_wing_gamma, BodyAerodynamics([inviscid_wing(section_y .+ 5.0; n_panels)]))
637+
@test lone_wing_gamma ellipse
638+
end
639+
640+
@testset "ELLIPTIC and ZEROS converge to the same gamma" begin
641+
wings = wing_pair(section_y, n_panels, span + 1.0)
642+
_, from_zeros = solve_wings(wings)
643+
gamma_zeros = copy(from_zeros.gamma_distribution)
644+
body_aero = BodyAerodynamics(wings; va_vec=[10.0, 0.0, 1.0])
645+
solver = Solver(2n_panels, 2length(section_y);
646+
type_initial_gamma_distribution=ELLIPTIC)
647+
sol = solve!(solver, body_aero)
648+
649+
@test sol.solver_status == FEASIBLE
650+
@test sol.gamma_distribution gamma_zeros rtol=1e-4
651+
end
652+
619653
n_wing_sections = length(section_y)
620654
first_sections = 1:n_wing_sections
621655
second_sections = n_wing_sections+1:2n_wing_sections

0 commit comments

Comments
 (0)