Skip to content
Open
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
2 changes: 1 addition & 1 deletion bin/run_julia
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ done
if [[ $# -gt 0 ]]; then
JULIA_ARGS=("$@")
else
JULIA_ARGS=(-i -e 'using VortexStepMethod; function menu(); include("examples/menu.jl"); end; function menu_cp(); include("examples_cp/menu_cp.jl"); end')
JULIA_ARGS=()
fi

julia --project "${JULIA_ARGS[@]}"
5 changes: 3 additions & 2 deletions ext/VortexStepMethodControlPlotsExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -793,8 +793,9 @@ function VortexStepMethod.plot_polar_data(body_aero::BodyAerodynamics,
ax = fig.add_subplot(1, 3, idx, projection="3d")

# Create interpolation matrix
interp_matrix = zeros(length(alphas), length(delta_tes))
interp_matrix .= [interp(alpha, delta_te) for alpha in alphas, delta_te in delta_tes]
interp_matrix = zeros(length(delta_tes), length(alphas))
interp_matrix .= [interp(alpha, delta_te)
for delta_te in delta_tes, alpha in alphas]
X = collect(delta_tes) .+ zeros(length(alphas))'
Y = collect(alphas)' .+ zeros(length(delta_tes))

Expand Down
2 changes: 1 addition & 1 deletion ext/VortexStepMethodMakieExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ function VortexStepMethod.plot_polar_data(body_aero::BodyAerodynamics,

# Create interpolation matrix
interp_matrix = [interp(alpha, delta_te)
for alpha in alphas, delta_te in delta_tes]
for delta_te in delta_tes, alpha in alphas]

# Create wireframe
wireframe!(ax, delta_tes, alphas, interp_matrix;
Expand Down
2 changes: 1 addition & 1 deletion src/VortexStepMethod.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ using Xfoil
export SolverSettings, VSMSettings, WingSettings
export ObjWing, Section, Wing, refine!, reinit!
export BodyAerodynamics
export Solver, VSMSolution, linearize, solve, solve!, solve_base!
export Solver, VSMSolution, linearize, solve, solve!, solve_base!, calc_forces!
export calculate_results
export add_section!, set_va!
export calculate_projected_area, calculate_span
Expand Down
29 changes: 15 additions & 14 deletions src/body_aerodynamics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -315,28 +315,29 @@ end
)
size(va_input) == (n_panels, 3) ||
throw(ArgumentError("'va' must be shape (3,) or ($(n_panels), 3); got $(size(va_input))"))

T = promote_type(eltype(va_input),
isnothing(panel_areas) ? Float64 : eltype(panel_areas))
areas = if isnothing(panel_areas)
ones(T, n_panels)
else
if !isnothing(panel_areas)
length(panel_areas) == n_panels ||
throw(ArgumentError("panel_areas must be shape ($(n_panels),), got length $(length(panel_areas))"))
T.(panel_areas)
end

total_area = sum(areas)
total_area > 0.0 || throw(ArgumentError("Total panel area must be positive."))

T = promote_type(eltype(va_input),
isnothing(panel_areas) ? Float64 : eltype(panel_areas))
total_area = zero(T)
weighted_speed_sq = zero(T)
direction = zeros(MVector{3, T})
@inbounds for i in 1:n_panels
@views va_i = va_input[i, :]
speed_i = norm(va_i)
weighted_speed_sq += areas[i] * speed_i^2
direction .+= areas[i] .* va_i
area_i = isnothing(panel_areas) ? one(T) : T(panel_areas[i])
va1 = va_input[i, 1]
va2 = va_input[i, 2]
va3 = va_input[i, 3]
speed_i = sqrt(va1^2 + va2^2 + va3^2)
total_area += area_i
weighted_speed_sq += area_i * speed_i^2
direction[1] += area_i * va1
direction[2] += area_i * va2
direction[3] += area_i * va3
end
total_area > 0.0 || throw(ArgumentError("Total panel area must be positive."))

reference_speed = sqrt(weighted_speed_sq / total_area)
direction_norm = norm(direction)
Expand Down
28 changes: 22 additions & 6 deletions src/solver.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Struct for storing the solution of the [solve!](@ref) function. Must contain all
_chord_dist::Vector{T} = zeros(T, P)
### end of private vectors
width_dist::Vector{T} = zeros(T, P)
panel_area_dist::Vector{T} = zeros(T, P)
alpha_dist::Vector{T} = zeros(T, P)
alpha_geometric_dist::Vector{T} = zeros(T, P)
cl_dist::Vector{T} = zeros(T, P)
Expand Down Expand Up @@ -74,6 +75,7 @@ Struct for storing the solution of the [solve!](@ref) function. Must contain all
va_unrefined_dist::Vector{MVector{3, T}} = [zeros(MVector{3, T}) for _ in 1:U]
chord_unrefined_dist::MVector{U, T} = zeros(MVector{U, T})
width_unrefined_dist::MVector{U, T} = zeros(MVector{U, T})
unrefined_count_dist::Vector{Int} = zeros(Int, U)
solver_status::SolverStatus = FAILURE
end

Expand Down Expand Up @@ -237,6 +239,20 @@ function solve!(solver::Solver{P, U, T}, body_aero::BodyAerodynamics, gamma_dist
solver.sol.gamma_distribution = gamma_new
end

return calc_forces!(solver, body_aero; reference_point, moment_frac)
end

"""
calc_forces!(solver::Solver, body_aero::BodyAerodynamics;
reference_point=solver.reference_point, moment_frac=0.1)

Assemble aerodynamic forces and moments from the circulation already converged
by [`solve_base!`](@ref) and stored in `solver`. Split out of [`solve!`](@ref).
"""
function calc_forces!(solver::Solver{P, U, T}, body_aero::BodyAerodynamics;
reference_point=solver.reference_point, moment_frac=0.1) where {P, U, T}
gamma_new = solver.lr.gamma_new

# Initialize arrays
cl_dist = solver.sol.cl_dist
cd_dist = solver.sol.cd_dist
Expand Down Expand Up @@ -322,7 +338,7 @@ function solve!(solver::Solver{P, U, T}, body_aero::BodyAerodynamics, gamma_dist

# Initialize result arrays
area_all_panels = zero(T)
panel_areas = zeros(T, length(panels))
panel_areas = solver.sol.panel_area_dist

# Get wing properties
spanwise_direction = body_aero.wings[1].spanwise_direction
Expand Down Expand Up @@ -428,6 +444,7 @@ function solve!(solver::Solver{P, U, T}, body_aero::BodyAerodynamics, gamma_dist
va_unrefined_dist = solver.sol.va_unrefined_dist
chord_unrefined_dist = solver.sol.chord_unrefined_dist
width_unrefined_dist = solver.sol.width_unrefined_dist
unrefined_count_dist = solver.sol.unrefined_count_dist

# Zero all unrefined arrays
moment_unrefined_dist .= 0.0
Expand All @@ -444,13 +461,12 @@ function solve!(solver::Solver{P, U, T}, body_aero::BodyAerodynamics, gamma_dist
end
chord_unrefined_dist .= 0.0
width_unrefined_dist .= 0.0
fill!(unrefined_count_dist, 0)

panel_idx = 1
unrefined_idx = 1
for wing in body_aero.wings
if wing.n_unrefined_sections > 0
# Accumulate values from refined panels to unrefined sections
unrefined_section_counts = zeros(Int, wing.n_unrefined_sections)
for local_panel_idx in 1:wing.n_panels
panel = body_aero.panels[panel_idx]
original_section_idx = wing.refined_panel_mapping[local_panel_idx]
Expand All @@ -472,16 +488,16 @@ function solve!(solver::Solver{P, U, T}, body_aero::BodyAerodynamics, gamma_dist
chord_unrefined_dist[target_unrefined_idx] += panel.chord
width_unrefined_dist[target_unrefined_idx] += panel.width

unrefined_section_counts[original_section_idx] += 1
unrefined_count_dist[target_unrefined_idx] += 1
panel_idx += 1
end

# Average coefficients and geometry. width and
# moment_coeff_unrefined_dist stay summed (extensive).
for i in 1:wing.n_unrefined_sections
target_unrefined_idx = unrefined_idx + i - 1
if unrefined_section_counts[i] > 0
count = unrefined_section_counts[i]
if unrefined_count_dist[target_unrefined_idx] > 0
count = unrefined_count_dist[target_unrefined_idx]
moment_unrefined_dist[target_unrefined_idx] /= count
cl_unrefined_dist[target_unrefined_idx] /= count
cd_unrefined_dist[target_unrefined_idx] /= count
Expand Down
10 changes: 10 additions & 0 deletions test/plotting/test_plotting.jl
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,16 @@ end
@test fig !== nothing
end

fig_rect = plot_polar_data(body_aero;
alphas=collect(deg2rad.(-5:1.0:15)),
delta_tes=collect(deg2rad.(-3:1.0:5)),
is_show=false)
if backend == "Makie"
@test fig_rect isa Figure
else
@test fig_rect !== nothing
end

# Tests for both backends
body_aero_empty = create_body_aero()
empty!(body_aero_empty.panels)
Expand Down
23 changes: 23 additions & 0 deletions test/solver/test_solver.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,26 @@ end
rm(settings_file; force=true)
end
end

calc_forces_allocs(solver, body_aero) =
(calc_forces!(solver, body_aero); @allocated calc_forces!(solver, body_aero))

@testset "calc_forces! is zero-alloc" begin
settings_file = create_temp_wing_settings(
"solver", "solver_test_wing.yaml";
alpha=5.0, beta=0.0, wind_speed=10.0,
)
try
settings = VSMSettings(settings_file)
wing = Wing(settings)
refine!(wing)
body_aero = BodyAerodynamics([wing])
solver = Solver(body_aero, settings)
set_va!(body_aero, [10.0, 0.0, 0.0])
solve!(solver, body_aero)

@test calc_forces_allocs(solver, body_aero) == 0
finally
rm(settings_file; force=true)
end
end
Loading