Skip to content

Commit 66ac77d

Browse files
committed
Manual refine
1 parent cbbc311 commit 66ac77d

6 files changed

Lines changed: 126 additions & 85 deletions

File tree

src/VortexStepMethod.jl

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ using Xfoil
2727

2828
# Export public interface
2929
export VSMSettings, WingSettings, SolverSettings
30-
export Wing, Section, ObjWing, reinit!
30+
export Wing, Section, ObjWing, reinit!, refine!
3131
export BodyAerodynamics
3232
export Solver, solve, solve_base!, solve!, VSMSolution, linearize
3333
export calculate_results
@@ -36,7 +36,7 @@ export calculate_span, calculate_projected_area
3636
export MVec3
3737
export Model, VSM, LLT
3838
export AeroModel, LEI_AIRFOIL_BREUKELS, POLAR_VECTORS, POLAR_MATRICES, INVISCID
39-
export PanelDistribution, LINEAR, COSINE, COSINE_VAN_GARREL, SPLIT_PROVIDED, UNCHANGED, NONE
39+
export PanelDistribution, LINEAR, COSINE, COSINE_VAN_GARREL, SPLIT_PROVIDED, UNCHANGED
4040
export InitialGammaDistribution, ELLIPTIC, ZEROS
4141
export SolverStatus, FEASIBLE, INFEASIBLE, FAILURE
4242
export SolverType, LOOP, NONLIN
@@ -130,16 +130,14 @@ Enumeration of the implemented panel distributions.
130130
- COSINE # Cosine distribution
131131
- `COSINE_VAN_GARREL` # van Garrel cosine distribution
132132
- `SPLIT_PROVIDED` # Split provided sections
133-
- `UNCHANGED` # Keep original sections without interpolation
134-
- `NONE` # No refinement - sections already refined
133+
- `UNCHANGED` # 1:1 copy of unrefined to refined sections (no interpolation)
135134
"""
136135
@enum PanelDistribution begin
137136
LINEAR # Linear distribution
138137
COSINE # Cosine distribution
139138
COSINE_VAN_GARREL # van Garrel cosine distribution
140139
SPLIT_PROVIDED # Split provided sections
141-
UNCHANGED # Keep original sections without interpolation
142-
NONE # No refinement - sections already refined
140+
UNCHANGED # 1:1 copy of unrefined to refined sections
143141
end
144142

145143
"""

src/body_aerodynamics.jl

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,27 @@ function BodyAerodynamics(
7171
va=[15.0, 0.0, 0.0],
7272
omega=zeros(MVec3)
7373
) where T <: AbstractWing
74+
# Validate all wings are refined
75+
for (i, wing) in enumerate(wings)
76+
if isempty(wing.refined_sections) ||
77+
length(wing.refined_sections) != wing.n_panels + 1
78+
throw(ArgumentError(
79+
"Wing $i has not been refined. " *
80+
"Call refine!(wing) before creating BodyAerodynamics.\n\n" *
81+
"Expected workflow:\n" *
82+
" wing = Wing(...)\n" *
83+
" refine!(wing)\n" *
84+
" body_aero = BodyAerodynamics([wing])"
85+
))
86+
end
87+
88+
if isempty(wing.non_deformed_sections)
89+
@warn "Wing $i has no non_deformed_sections. " *
90+
"Deformation (unrefined_deform!) will not work. " *
91+
"This should have been created by refine!." maxlog=1
92+
end
93+
end
94+
7495
# Initialize panels
7596
panels = Panel[]
7697
n_unrefined_total = 0
@@ -79,17 +100,6 @@ function BodyAerodynamics(
79100
section.LE_point .-= kite_body_origin
80101
section.TE_point .-= kite_body_origin
81102
end
82-
if wing.spanwise_distribution == NONE
83-
# NONE distribution: refined_sections already populated in constructor
84-
!(wing.n_panels == length(wing.refined_sections) - 1) &&
85-
throw(ArgumentError("(wing.n_panels = $(wing.n_panels)) != (length(wing.refined_sections) - 1 = $(length(wing.unrefined_sections) - 1))"))
86-
elseif wing.spanwise_distribution == UNCHANGED
87-
wing.refined_sections = wing.unrefined_sections
88-
!(wing.n_panels == length(wing.unrefined_sections) - 1) &&
89-
throw(ArgumentError("(wing.n_panels = $(wing.n_panels)) != (length(wing.unrefined_sections) - 1 = $(length(wing.unrefined_sections) - 1))"))
90-
else
91-
wing.refined_sections = Section[Section() for _ in 1:wing.n_panels+1]
92-
end
93103

94104
# Create panels
95105
for _ in 1:wing.n_panels
@@ -127,31 +137,22 @@ Initialize a BodyAerodynamics struct in-place by setting up panels and coefficie
127137
- `body_aero::BodyAerodynamics`: The structure to initialize
128138
129139
# Keyword Arguments
130-
- `init_aero::Bool`: Wether to initialize the aero data or not
140+
- `init_aero::Bool`: Whether to initialize the aero data or not
131141
- `va=[15.0, 0.0, 0.0]`: Apparent wind vector
132142
- `omega=zeros(3)`: Turn rate in kite body frame x y and z
133-
- `refine_mesh=true`: Whether to refine wing meshes. Set to `false` after
134-
`deform!()` to preserve deformed geometry.
135-
- `recompute_mapping=true`: Whether to recompute the refined panel mapping.
136-
Set to `false` to skip mapping computation when it hasn't changed.
137-
- `sort_sections=true`: Whether to sort sections by spanwise position.
138-
Set to `false` for REFINE wings where section order is determined by structural connectivity.
139143
140144
# Returns
141145
nothing
142146
"""
143147
function reinit!(body_aero::BodyAerodynamics;
144148
init_aero=true,
145149
va=[15.0, 0.0, 0.0],
146-
omega=zeros(MVec3),
147-
refine_mesh=true,
148-
recompute_mapping=true,
149-
sort_sections=true
150+
omega=zeros(MVec3)
150151
)
151152
idx = 1
152153
vec = @MVector zeros(3)
153154
for wing in body_aero.wings
154-
reinit!(wing; refine_mesh, recompute_mapping, sort_sections)
155+
reinit!(wing)
155156
panel_props = wing.panel_props
156157

157158
# Create panels

src/obj_geometry.jl

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -428,15 +428,9 @@ function ObjWing(
428428
interp_steps=n_panels+1
429429
)
430430

431-
# Force NONE distribution for ObjWing
432-
if spanwise_distribution != NONE
433-
@warn "ObjWing only supports spanwise_distribution=NONE. Overriding to NONE." maxlog=1
434-
spanwise_distribution = NONE
435-
end
436-
437431
# Set default: evenly spaced unrefined sections including both tips
438432
if isnothing(n_unrefined_sections)
439-
# Default to having same number of unrefined sections as refined (no refinement)
433+
# Default to having same number of unrefined sections as refined (no interpolation needed)
440434
n_unrefined_sections = n_panels + 1
441435
end
442436

@@ -493,24 +487,34 @@ function ObjWing(
493487
push!(sections, Section(LE_point, TE_point, POLAR_MATRICES, aero_data))
494488
end
495489

490+
# Create refined sections (evenly spaced including both tips)
496491
refined_sections = Section[]
497492
for gamma in range(-gamma_tip, gamma_tip, n_panels+1)
498493
LE_point = [le_interp[i](gamma) for i in 1:3]
499494
TE_point = [te_interp[i](gamma) for i in 1:3]
500495
push!(refined_sections, Section(LE_point, TE_point, POLAR_MATRICES, aero_data))
501496
end
502497

498+
# Create non_deformed_sections as copy of refined_sections for deformation support
499+
non_deformed_sections = [Section() for _ in 1:n_panels+1]
500+
for i in 1:n_panels+1
501+
reinit!(non_deformed_sections[i], refined_sections[i])
502+
end
503+
503504
panel_props = PanelProperties{n_panels}()
504505
cache = [PreallocationTools.LazyBufferCache()]
505506

506507
wing = Wing(n_panels, Int16(n_unrefined_sections), spanwise_distribution, panel_props, MVec3(spanwise_direction),
507508
sections, refined_sections, remove_nan,
508509
Int16[],
509-
Section[], zeros(n_panels), zeros(n_panels),
510+
non_deformed_sections, zeros(n_panels), zeros(n_panels),
510511
mass, gamma_tip, inertia_tensor, T_cad_body, R_cad_body, radius,
511512
le_interp, te_interp, area_interp, cache)
512513

513-
# Refine mesh and update panel properties
514+
# Compute panel mapping for deformation support
515+
VortexStepMethod.compute_refined_panel_mapping!(wing)
516+
517+
# Update panel properties
514518
reinit!(wing)
515519

516520
wing

src/precompile.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
INVISCID)
2727

2828
# Step 3: Initialize aerodynamics (simplified)
29+
refine!(wing)
2930
body_aero = BodyAerodynamics([wing])
3031

3132
nothing

src/wing_geometry.jl

Lines changed: 80 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -297,25 +297,20 @@ function Wing(n_panels::Int;
297297
end
298298

299299
"""
300-
reinit!(wing::AbstractWing; refine_mesh=true, recompute_mapping=true, sort_sections=true)
300+
reinit!(wing::AbstractWing)
301301
302-
Reinitialize wing geometry and panel properties.
302+
Reinitialize wing panel properties based on current refined_sections geometry.
303303
304-
# Keyword Arguments
305-
- `refine_mesh::Bool=true`: Whether to refine the mesh. Set to `false` after
306-
`deform!()` to preserve deformed geometry while updating panel properties.
307-
- `recompute_mapping::Bool=true`: Whether to recompute the refined panel mapping.
308-
Set to `false` to skip mapping computation when it hasn't changed.
309-
- `sort_sections::Bool=true`: Whether to sort sections by spanwise position.
310-
Set to `false` for REFINE wings where section order is determined by structural connectivity.
311-
"""
312-
function reinit!(wing::AbstractWing; refine_mesh=true, recompute_mapping=true, sort_sections=true)
313-
# Refine mesh unless explicitly disabled (e.g., to preserve deformation)
314-
if refine_mesh
315-
refine_aerodynamic_mesh!(wing; recompute_mapping, sort_sections)
316-
end
304+
This function only updates panel properties (chord, area, etc.) from the existing
305+
refined_sections. It does NOT refine the mesh - call refine_aerodynamic_mesh!(wing)
306+
first if needed.
317307
318-
# Calculate panel properties
308+
# Note
309+
After deformation via `unrefined_deform!()` or `deform!()`, call `reinit!` to update
310+
panel properties while preserving the deformed geometry.
311+
"""
312+
function reinit!(wing::AbstractWing)
313+
# Calculate panel properties from refined sections
319314
update_panel_properties!(
320315
wing.panel_props,
321316
wing.refined_sections,
@@ -620,54 +615,99 @@ end
620615
621616
Create non_deformed_sections to match refined_sections.
622617
This enables deformation support for all wings (YAML and OBJ).
623-
Should be called after refined_sections are populated for the FIRST time only.
618+
Should be called after refined_sections are populated.
624619
Once populated, non_deformed_sections serves as the undeformed reference geometry.
625620
"""
626621
function update_non_deformed_sections!(wing::AbstractWing)
627622
n_sections = wing.n_panels + 1
628623

629-
# Only populate non_deformed_sections if it's empty (initial setup)
630-
# Once populated, it serves as the undeformed reference and should not be overwritten
624+
# Populate or update non_deformed_sections
631625
if isempty(wing.non_deformed_sections)
626+
@show length(wing.refined_sections) n_sections
627+
# Initial setup
632628
wing.non_deformed_sections = [Section() for _ in 1:n_sections]
633629
for i in 1:n_sections
634630
reinit!(wing.non_deformed_sections[i], wing.refined_sections[i])
635631
end
632+
elseif length(wing.non_deformed_sections) != n_sections
633+
# Size mismatch - error
634+
throw(ArgumentError(
635+
"non_deformed_sections has incorrect size. " *
636+
"Expected $(n_sections) sections (n_panels+1), got $(length(wing.non_deformed_sections)). " *
637+
"This indicates an inconsistent wing state."
638+
))
639+
else
640+
# Correct size - update all sections
641+
for i in 1:n_sections
642+
reinit!(wing.non_deformed_sections[i], wing.refined_sections[i])
643+
end
636644
end
637645
return nothing
638646
end
639647

640648
"""
641-
refine_aerodynamic_mesh!(wing::AbstractWing; recompute_mapping=true, sort_sections=true)
642-
643-
Refine the aerodynamic mesh of the wing based on spanwise panel distribution.
649+
refine!(wing::AbstractWing; recompute_mapping=true, sort_sections=true)
650+
651+
Refine the wing aerodynamic mesh from unrefined sections to refined sections.
652+
653+
This function interpolates the wing geometry from a coarse set of unrefined sections
654+
to a fine mesh of refined sections (n_panels+1 sections) based on the wing's
655+
spanwise_distribution setting. It also populates non_deformed_sections which
656+
enables deformation support via `unrefined_deform!`.
657+
658+
# Required Workflow
659+
Must be called after wing construction and before creating `BodyAerodynamics`:
660+
```julia
661+
wing = Wing("wing.yaml"; n_panels=40) # or ObjWing(...) or manual Wing
662+
refine!(wing) # Refine mesh
663+
body_aero = BodyAerodynamics([wing]) # Create aerodynamics
664+
```
665+
666+
# Distribution Methods
667+
- `LINEAR`: Linear interpolation between sections
668+
- `COSINE`: Cosine spacing (more panels near tips)
669+
- `COSINE_VAN_GARREL`: van Garrel cosine distribution
670+
- `SPLIT_PROVIDED`: Split each unrefined section into sub-panels
671+
- `UNCHANGED`: 1:1 copy when n_unrefined_sections == n_panels+1
644672
645673
# Keyword Arguments
646-
- `recompute_mapping::Bool=true`: Whether to recompute the refined panel mapping.
647-
Set to `false` to skip mapping computation when it hasn't changed.
648-
- `sort_sections::Bool=true`: Whether to sort sections by spanwise position (y-coordinate).
649-
Set to `false` for REFINE wings where section order is determined by structural connectivity.
674+
- `recompute_mapping::Bool=true`: Recompute the mapping from refined panels to unrefined sections
675+
- `sort_sections::Bool=true`: Sort sections by spanwise position (disable for structural ordering)
650676
651-
Returns:
652-
Vector{Section}: List of refined sections
677+
# Effects
678+
1. Populates `wing.refined_sections` (n_panels+1 sections)
679+
2. Populates `wing.non_deformed_sections` (copy of refined_sections for deformation reference)
680+
3. Computes `wing.refined_panel_mapping` (panel → unrefined section mapping)
681+
4. Resizes `wing.theta_dist` and `wing.delta_dist` to n_panels
682+
683+
# Example
684+
```julia
685+
# YAML wing
686+
wing = Wing("wing.yaml"; n_panels=40)
687+
refine!(wing)
688+
body_aero = BodyAerodynamics([wing])
689+
690+
# After refinement, deformation is supported
691+
unrefined_deform!(wing, theta_angles, delta_angles)
692+
```
653693
"""
654-
function refine_aerodynamic_mesh!(wing::AbstractWing; recompute_mapping=true, sort_sections=true)
694+
function refine!(wing::AbstractWing; recompute_mapping=true, sort_sections=true)
695+
# Validate unrefined_sections exist
696+
if isempty(wing.unrefined_sections)
697+
throw(ArgumentError(
698+
"Cannot refine mesh: wing has no unrefined_sections. " *
699+
"Add sections using add_section! or check wing construction."
700+
))
701+
end
702+
655703
# Only sort sections if requested (skip for REFINE wings with fixed structural order)
656704
sort_sections && sort!(wing.unrefined_sections, by=s -> s.LE_point[2], rev=true)
657705
n_sections = wing.n_panels + 1
658706

659-
# Handle NONE distribution - sections already refined, just compute mapping
660-
if wing.spanwise_distribution == NONE
661-
if length(wing.refined_sections) != n_sections
662-
throw(ArgumentError("NONE distribution requires refined_sections to be pre-populated"))
663-
end
664-
recompute_mapping && compute_refined_panel_mapping!(wing)
665-
update_non_deformed_sections!(wing)
666-
return nothing
667-
end
668-
669707
if length(wing.refined_sections) == 0
670-
if wing.spanwise_distribution == UNCHANGED || length(wing.unrefined_sections) == n_sections
708+
@show wing.spanwise_distribution
709+
if wing.spanwise_distribution == UNCHANGED ||
710+
length(wing.unrefined_sections) == n_sections
671711
wing.refined_sections = wing.unrefined_sections
672712
update_non_deformed_sections!(wing)
673713
return nothing

src/yaml_geometry.jl

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ function Wing(
246246
# Get coordinates directly from struct fields
247247
le_coord = [section.LE_x, section.LE_y, section.LE_z]
248248
te_coord = [section.TE_x, section.TE_y, section.TE_z]
249-
249+
250250
# Load polar data and create section
251251
csv_file_path = get(airfoil_csv_map, section.airfoil_id, "")
252252
if !isempty(csv_file_path) && !isabspath(csv_file_path)
@@ -256,15 +256,12 @@ function Wing(
256256
csv_file_path = joinpath(dirname(geometry_file), csv_file_path)
257257
end
258258
aero_data, aero_model = load_polar_data(csv_file_path)
259-
259+
260260
prn && println("Section airfoil_id $(section.airfoil_id): Using $aero_model model")
261-
261+
262262
add_section!(wing, le_coord, te_coord, aero_model, aero_data)
263263
end
264-
265-
# Initialize the wing after adding all sections
266-
reinit!(wing)
267-
264+
268265
return wing
269266
end
270267

0 commit comments

Comments
 (0)