Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@

### Fixed

- `ELLIPTIC` initial circulation works on a body with more than one wing, where it threw
Comment thread
1-Bart-1 marked this conversation as resolved.
an `ArgumentError`: each wing gets an ellipse over its own span, along its own
`spanwise_direction` and centred on its own mid-span, also for a single wing off y = 0.
- `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.
Expand Down
1 change: 1 addition & 0 deletions docs/src/private_functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ calculate_relative_alpha_and_relative_velocity
update_effective_angle_of_attack!
calculate_stall_angle_list
wing_span_flip
spanwise_extent
calculate_circulation_distribution_elliptical_wing
_compute_reference_velocity_from_distribution
smooth_circulation!
Expand Down
40 changes: 17 additions & 23 deletions src/body_aerodynamics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ Main structure for calculating aerodynamic properties of bodies. Use the constru
influence coefficients, used only for the corrected angle of attack
- `projected_area::Float64` = 1.0: The area projected onto the xy-plane of the kite body reference frame [m²]
- `c_ref::Float64` = 1.0: Reference chord length (max panel chord) [m]
- `y::MVector{P, Float64}` = MVector{P,Float64}(zeros(P))
- `cache::Vector{PreallocationTools.LazyBufferCache{typeof(identity), typeof(identity)}}` = [LazyBufferCache() for _ in 1:15]
"""
@with_kw mutable struct BodyAerodynamics{P, W<:AbstractWing, T, PN<:Panel{T}}
Expand All @@ -49,7 +48,6 @@ Main structure for calculating aerodynamic properties of bodies. Use the constru
AIC_aero_center::Array{T, 3} = zeros(T, P, P, 3)
projected_area::T = one(T)
c_ref::T = one(T)
y::MVector{P, T} = zeros(MVector{P, T})
cache::Vector{PreallocationTools.LazyBufferCache{typeof(identity), typeof(identity)}} = [LazyBufferCache() for _ in 1:15]
end

Expand Down Expand Up @@ -496,30 +494,26 @@ Returns: nothing
end

"""
calculate_circulation_distribution_elliptical_wing(body_aero::BodyAerodynamics, gamma_0=1.0)
calculate_circulation_distribution_elliptical_wing(gamma_i, body_aero::BodyAerodynamics,
gamma_0=1.0)

Calculate circulation distribution for an elliptical wing.

Returns: nothing
Write into `gamma_i` an elliptic circulation of peak `gamma_0` over each wing, its control
points measured along that wing's `spanwise_direction` from the wing's mid-span.
"""
function calculate_circulation_distribution_elliptical_wing(gamma_i, body_aero::BodyAerodynamics, gamma_0=1.0)
length(body_aero.wings) == 1 || throw(ArgumentError("Multiple wings not yet implemented"))

wing_span = body_aero.wings[1].span
@debug "Wing span: $wing_span"

# Calculate y-coordinates of control points
y = body_aero.y
for (i, panel) in pairs(body_aero.panels)
y[i] = panel.control_point[2]
function calculate_circulation_distribution_elliptical_wing(gamma_i,
body_aero::BodyAerodynamics, gamma_0=1.0)
panel_offset = 0
for wing in body_aero.wings
lo, hi = spanwise_extent(wing)
axis = normalize(wing.spanwise_direction)
for i in panel_offset .+ (1:wing.n_panels)
span_position = dot(body_aero.panels[i].control_point, axis) - (lo + hi) / 2
# Clamped: a control point can lie outside the span of the unrefined sections
gamma_i[i] = gamma_0 * sqrt(max(0.0, 1 - (2span_position / (hi - lo))^2))
end
panel_offset += wing.n_panels
end

# Calculate elliptical distribution (clamp to avoid sqrt of negative
# when control points lie outside the nominal span envelope)
gamma_i .= gamma_0 * sqrt.(max.(0.0, 1 .- (2 .* y ./ wing_span).^2))

@debug "Calculated circulation distribution: $gamma_i"
nothing
return nothing
end

"""
Expand Down
29 changes: 15 additions & 14 deletions src/wing_geometry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1685,24 +1685,25 @@ function refine_mesh_with_billowing!(wing; reuse_aero_data::Bool=false)
end

"""
calculate_span(wing::AbstractWing)
spanwise_extent(wing::AbstractWing)

Calculate wing span along spanwise direction.
Lowest and highest projection of the unrefined sections' LE and TE points on `wing`'s
`spanwise_direction`, as `(lo, hi)` [m].
"""
function spanwise_extent(wing::AbstractWing)
axis = normalize(wing.spanwise_direction)
return extrema(dot(point, axis) for section in wing.unrefined_sections
for point in (section.LE_point, section.TE_point))
end

Returns:
Float64: Wing span
"""
calculate_span(wing::AbstractWing)

Wing span along `spanwise_direction` [m].
"""
function calculate_span(wing::AbstractWing)
# Normalize spanwise direction
vector_axis = wing.spanwise_direction ./ norm(wing.spanwise_direction)

# Get all points
all_points = reduce(vcat, [[section.LE_point, section.TE_point]
for section in wing.unrefined_sections])

# Project points and calculate span
projections = [dot(point, vector_axis) for point in all_points]
return maximum(projections) - minimum(projections)
lo, hi = spanwise_extent(wing)
return hi - lo
end

# Project point onto plane
Expand Down
29 changes: 29 additions & 0 deletions test/body_aerodynamics/test_body_aerodynamics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,35 @@ end
@test sol.force[3] > 2single.force[3]
end

@testset "ELLIPTIC starts each wing on its own ellipse" begin
control_y = [panel.control_point[2] for panel in single_aero.panels]
ellipse = sqrt.(1 .- (2control_y ./ span) .^ 2)
fin = Wing(n_panels; spanwise_direction=[0.0, 0.0, 1.0])
for z in section_y .+ 5.0
add_section!(fin, [0.0, 0.0, z], [1.0, 0.0, z], INVISCID)
end
refine!(fin)
body_aero = BodyAerodynamics([wing_pair(section_y, n_panels, span + 1.0); fin])
gamma = zeros(3n_panels)
VortexStepMethod.calculate_circulation_distribution_elliptical_wing(gamma,
body_aero)

@test gamma ≈ repeat(ellipse, 3)
end

@testset "ELLIPTIC and ZEROS converge to the same gamma" begin
wings = wing_pair(section_y, n_panels, span + 1.0)
_, from_zeros = solve_wings(wings)
gamma_zeros = copy(from_zeros.gamma_distribution)
body_aero = BodyAerodynamics(wings; va=[10.0, 0.0, 1.0])
solver = Solver(2n_panels, 2length(section_y);
type_initial_gamma_distribution=ELLIPTIC)
sol = solve!(solver, body_aero)

@test sol.solver_status == FEASIBLE
@test sol.gamma_distribution ≈ gamma_zeros rtol=1e-4
end

n_wing_sections = length(section_y)
first_sections = 1:n_wing_sections
second_sections = n_wing_sections+1:2n_wing_sections
Expand Down
Loading