Skip to content

Commit f078c77

Browse files
committed
Make sure 0 groups works
1 parent 5d3d09b commit f078c77

File tree

7 files changed

+206
-20
lines changed

7 files changed

+206
-20
lines changed

src/ram_geometry.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ function RamAirWing(
415415
interp_steps=n_sections # TODO: check if interpolations are still needed
416416
)
417417

418-
!(n_panels % n_groups == 0) && throw(ArgumentError("Number of panels should be divisible by number of groups"))
418+
!(n_groups == 0 || n_panels % n_groups == 0) && throw(ArgumentError("Number of panels should be divisible by number of groups"))
419419
!isapprox(spanwise_direction, [0.0, 1.0, 0.0]) && throw(ArgumentError("Spanwise direction has to be [0.0, 1.0, 0.0], not $spanwise_direction"))
420420

421421
# Load or create polars

src/solver.jl

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -292,21 +292,29 @@ function solve!(solver::Solver, body_aero::BodyAerodynamics, gamma_distribution=
292292
moment_coeff_dist[i] = moment_dist[i] / (q_inf * projected_area)
293293
end
294294

295-
group_moment_dist = solver.sol.group_moment_dist
296-
group_moment_coeff_dist = solver.sol.group_moment_coeff_dist
297-
group_moment_dist .= 0.0
298-
group_moment_coeff_dist .= 0.0
299-
panel_idx = 1
300-
group_idx = 1
301-
for wing in body_aero.wings
302-
panels_per_group = wing.n_panels ÷ wing.n_groups
303-
for _ in 1:wing.n_groups
304-
for _ in 1:panels_per_group
305-
group_moment_dist[group_idx] += moment_dist[panel_idx]
306-
group_moment_coeff_dist[group_idx] += moment_coeff_dist[panel_idx]
307-
panel_idx += 1
295+
# Only compute group moments if there are groups
296+
if length(solver.sol.group_moment_dist) > 0
297+
group_moment_dist = solver.sol.group_moment_dist
298+
group_moment_coeff_dist = solver.sol.group_moment_coeff_dist
299+
group_moment_dist .= 0.0
300+
group_moment_coeff_dist .= 0.0
301+
panel_idx = 1
302+
group_idx = 1
303+
for wing in body_aero.wings
304+
if wing.n_groups > 0
305+
panels_per_group = wing.n_panels ÷ wing.n_groups
306+
for _ in 1:wing.n_groups
307+
for _ in 1:panels_per_group
308+
group_moment_dist[group_idx] += moment_dist[panel_idx]
309+
group_moment_coeff_dist[group_idx] += moment_coeff_dist[panel_idx]
310+
panel_idx += 1
311+
end
312+
group_idx += 1
313+
end
314+
else
315+
# Skip panels for wings with n_groups=0
316+
panel_idx += wing.n_panels
308317
end
309-
group_idx += 1
310318
end
311319
end
312320

@@ -689,8 +697,8 @@ jac, results = linearize(
689697
)
690698
```
691699
"""
692-
function linearize(solver::Solver, body_aero::BodyAerodynamics, y::Vector{T};
693-
theta_idxs=1:4,
700+
function linearize(solver::Solver, body_aero::BodyAerodynamics, y::Vector{T};
701+
theta_idxs=1:4,
694702
delta_idxs=nothing,
695703
va_idxs=nothing,
696704
omega_idxs=nothing,
@@ -700,6 +708,19 @@ function linearize(solver::Solver, body_aero::BodyAerodynamics, y::Vector{T};
700708
!(length(body_aero.wings) == 1) && throw(ArgumentError("Linearization only works for a body_aero with one wing"))
701709
wing = body_aero.wings[1]
702710

711+
# Validate that theta_idxs and delta_idxs match the number of groups
712+
if !isnothing(theta_idxs) && wing.n_groups > 0
713+
length(theta_idxs) != wing.n_groups && throw(ArgumentError(
714+
"Length of theta_idxs ($(length(theta_idxs))) must match number of groups ($(wing.n_groups))"))
715+
end
716+
if !isnothing(delta_idxs) && wing.n_groups > 0
717+
length(delta_idxs) != wing.n_groups && throw(ArgumentError(
718+
"Length of delta_idxs ($(length(delta_idxs))) must match number of groups ($(wing.n_groups))"))
719+
end
720+
if wing.n_groups == 0 && (!isnothing(theta_idxs) || !isnothing(delta_idxs))
721+
throw(ArgumentError("Cannot use theta_idxs or delta_idxs when wing has n_groups=0 (no group functionality)"))
722+
end
723+
703724
init_va = body_aero.cache[1][body_aero.va]
704725
init_va .= body_aero.va
705726
if !isnothing(theta_idxs)

src/wing_geometry.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ function Wing(n_panels::Int;
235235
spanwise_distribution::PanelDistribution=LINEAR,
236236
spanwise_direction::PosVector=MVec3([0.0, 1.0, 0.0]),
237237
remove_nan=true)
238-
!(n_panels % n_groups == 0) && throw(ArgumentError("Number of panels should be divisible by number of groups"))
238+
!(n_groups == 0 || n_panels % n_groups == 0) && throw(ArgumentError("Number of panels should be divisible by number of groups"))
239239
panel_props = PanelProperties{n_panels}()
240240
Wing(n_panels, n_groups, spanwise_distribution, panel_props, spanwise_direction, Section[], Section[], remove_nan)
241241
end

src/yaml_geometry.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ function Wing(
190190
remove_nan=true,
191191
prn=false
192192
)
193-
!(n_panels % n_groups == 0) && throw(ArgumentError("Number of panels should be divisible by number of groups"))
193+
!(n_groups == 0 || n_panels % n_groups == 0) && throw(ArgumentError("Number of panels should be divisible by number of groups"))
194194
!isapprox(spanwise_direction, [0.0, 1.0, 0.0]) && throw(ArgumentError("Spanwise direction has to be [0.0, 1.0, 0.0], not $spanwise_direction"))
195195

196196
prn && @info "Reading YAML wing configuration from $geometry_file"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
wing_sections:
2+
headers: [airfoil_id, LE_x, LE_y, LE_z, TE_x, TE_y, TE_z]
3+
data:
4+
- [1, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0]
5+
- [1, 0.0, -1.0, 0.0, 1.0, -1.0, 0.0]
6+
7+
wing_airfoils:
8+
alpha_range: [-10, 10, 1]
9+
reynolds: 1000000
10+
headers: [airfoil_id, type, info_dict]
11+
data:
12+
- [1, polars, {csv_file_path: ""}]

test/solver/test_solver.jl

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,152 @@ using Test
2929
rm(settings_file; force=true)
3030
end
3131
end
32+
33+
@testset "Solver with n_groups=0" begin
34+
# Test that solver works correctly when n_groups=0 (no group functionality)
35+
settings_file = create_temp_wing_settings("solver", "solver_test_wing.yaml";
36+
alpha=5.0, beta=0.0, wind_speed=10.0, n_groups=0)
37+
38+
try
39+
settings = VSMSettings(settings_file)
40+
wing = Wing(settings)
41+
@test wing.n_groups == 0
42+
43+
body_aero = BodyAerodynamics([wing])
44+
solver = Solver(body_aero, settings)
45+
46+
# Verify solver has zero groups
47+
@test length(solver.sol.group_moment_dist) == 0
48+
@test length(solver.sol.group_moment_coeff_dist) == 0
49+
50+
# Test that solve! works without errors
51+
va = [10.0, 0.0, 0.0]
52+
set_va!(body_aero, va)
53+
sol = solve!(solver, body_aero)
54+
55+
@test sol isa VSMSolution
56+
@test sol.solver_status == SUCCESS
57+
58+
# Verify that group moments are empty
59+
@test length(sol.group_moment_dist) == 0
60+
@test length(sol.group_moment_coeff_dist) == 0
61+
62+
# But force and moment should still be computed
63+
@test !all(sol.force .== 0.0)
64+
@test norm(sol.force) > 0
65+
66+
finally
67+
rm(settings_file; force=true)
68+
end
69+
end
70+
71+
@testset "Linearize with n_groups=0" begin
72+
# Test that linearize works correctly when n_groups=0
73+
settings_file = create_temp_wing_settings("solver", "solver_test_wing.yaml";
74+
alpha=5.0, beta=0.0, wind_speed=10.0, n_groups=0, n_panels=4)
75+
76+
try
77+
settings = VSMSettings(settings_file)
78+
wing = Wing(settings)
79+
@test wing.n_groups == 0
80+
81+
body_aero = BodyAerodynamics([wing])
82+
solver = Solver(body_aero, settings)
83+
84+
# Set velocity
85+
va = [10.0, 0.0, 0.0]
86+
set_va!(body_aero, va)
87+
88+
# Test linearize with only velocity (no theta or delta since n_groups=0)
89+
y = va # Only velocity in input vector
90+
jac, results = VortexStepMethod.linearize(
91+
solver,
92+
body_aero,
93+
y;
94+
theta_idxs=nothing,
95+
delta_idxs=nothing,
96+
va_idxs=1:3,
97+
omega_idxs=nothing
98+
)
99+
100+
# Results should only have 6 elements (force + moment, no group moments)
101+
@test length(results) == 6
102+
@test size(jac) == (6, 3) # 6 outputs, 3 inputs (vx, vy, vz)
103+
104+
# Verify forces are non-zero
105+
@test norm(results[1:3]) > 0
106+
107+
# Test that using theta_idxs with n_groups=0 throws an error
108+
@test_throws ArgumentError VortexStepMethod.linearize(
109+
solver,
110+
body_aero,
111+
[0.0, 10.0, 0.0, 0.0]; # Invalid: trying to use theta
112+
theta_idxs=1:1,
113+
va_idxs=2:4
114+
)
115+
116+
# Test that using delta_idxs with n_groups=0 throws an error
117+
@test_throws ArgumentError VortexStepMethod.linearize(
118+
solver,
119+
body_aero,
120+
[0.0, 10.0, 0.0, 0.0]; # Invalid: trying to use delta
121+
delta_idxs=1:1,
122+
va_idxs=2:4
123+
)
124+
125+
finally
126+
rm(settings_file; force=true)
127+
end
128+
end
129+
130+
@testset "Linearize theta_idxs validation" begin
131+
# Test that theta_idxs length must match n_groups
132+
settings_file = create_temp_wing_settings("solver", "solver_test_wing.yaml";
133+
alpha=5.0, beta=0.0, wind_speed=10.0, n_groups=2, n_panels=4)
134+
135+
try
136+
settings = VSMSettings(settings_file)
137+
wing = Wing(settings)
138+
@test wing.n_groups == 2
139+
140+
body_aero = BodyAerodynamics([wing])
141+
solver = Solver(body_aero, settings)
142+
143+
va = [10.0, 0.0, 0.0]
144+
set_va!(body_aero, va)
145+
146+
# Test with correct number of theta angles (2)
147+
y_correct = [0.0, 0.0, 10.0, 0.0, 0.0] # 2 theta + 3 va
148+
jac, results = VortexStepMethod.linearize(
149+
solver,
150+
body_aero,
151+
y_correct;
152+
theta_idxs=1:2,
153+
va_idxs=3:5
154+
)
155+
@test size(jac, 1) == 8 # 6 + 2 group moments
156+
157+
# Test with wrong number of theta angles (should throw error)
158+
y_wrong = [0.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0] # 4 theta + 3 va
159+
@test_throws ArgumentError VortexStepMethod.linearize(
160+
solver,
161+
body_aero,
162+
y_wrong;
163+
theta_idxs=1:4, # Wrong: 4 angles but only 2 groups
164+
va_idxs=5:7
165+
)
166+
167+
# Test with wrong number of delta angles
168+
@test_throws ArgumentError VortexStepMethod.linearize(
169+
solver,
170+
body_aero,
171+
y_wrong;
172+
delta_idxs=1:4, # Wrong: 4 angles but only 2 groups
173+
va_idxs=5:7
174+
)
175+
176+
finally
177+
rm(settings_file; force=true)
178+
end
179+
end
32180
end

test/yaml_geometry/test_wing_constructor.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,12 @@ wing_airfoils:
151151

152152
# Test invalid n_panels/n_groups combination
153153
@test_throws ArgumentError Wing(test_yaml_path; n_panels=5, n_groups=2)
154-
154+
155+
# Test n_groups=0 (no grouping functionality)
156+
wing_no_groups = Wing(test_yaml_path; n_panels=4, n_groups=0)
157+
@test wing_no_groups.n_groups == 0
158+
@test wing_no_groups.n_panels == 4
159+
155160
# Test invalid spanwise direction
156161
@test_throws ArgumentError Wing(test_yaml_path; spanwise_direction=[1.0, 0.0, 0.0])
157162
end

0 commit comments

Comments
 (0)