@@ -841,7 +841,6 @@ function refine!(wing::AbstractWing; recompute_mapping=true, sort_sections=true)
841841 # Only sort sections if requested (skip for REFINE wings with fixed structural order)
842842 # TODO : only works if can be sorted by global y position
843843 if sort_sections
844- # Check if already sorted (descending by LE y)
845844 sorted = true
846845 for i in 1 : (length (wing. unrefined_sections) - 1 )
847846 if _section_sort_key (wing. unrefined_sections[i]) <
@@ -962,7 +961,6 @@ function compute_refined_panel_mapping!(wing::AbstractWing)
962961 for pi in 1 : n_panels
963962 r1 = wing. refined_sections[pi ]
964963 r2 = wing. refined_sections[pi + 1 ]
965- # refined panel center (scalar)
966964 rc1 = (r1. LE_point[1 ] + r1. TE_point[1 ] +
967965 r2. LE_point[1 ] + r2. TE_point[1 ]) * 0.25
968966 rc2 = (r1. LE_point[2 ] + r1. TE_point[2 ] +
994992"""
995993 calculate_new_aero_data(sections, section_index, left_weight, right_weight)
996994
997- Interpolate aerodynamic input between two sections (zero-allocation variant
998- that reads directly from a `Vector{Section}`).
995+ Interpolate aerodynamic input between two adjacent sections (zero-copy variant).
999996"""
1000997function calculate_new_aero_data (
1001998 sections:: Vector{Section} ,
@@ -1013,11 +1010,8 @@ function calculate_new_aero_data(
10131010end
10141011
10151012"""
1016- calculate_new_aero_data(aero_model,
1017- aero_data,
1018- section_index::Int,
1019- left_weight::Float64,
1020- right_weight::Float64)
1013+ calculate_new_aero_data(aero_model, aero_data, section_index,
1014+ left_weight, right_weight)
10211015
10221016Interpolate aerodynamic input between two sections.
10231017"""
@@ -1104,21 +1098,22 @@ end
11041098
11051099"""
11061100 refine_mesh_for_linear_cosine_distribution!(wing, idx, dist,
1107- n_sections, sections; reuse_aero_data)
1101+ n_sections, sections; endpoints, reuse_aero_data)
11081102
1109- Zero-allocation variant that reads directly from a `Vector{Section}`.
1103+ Refine wing mesh using linear or cosine spacing. Reads LE/TE
1104+ directly from a `Vector{Section}` (zero matrix allocations).
11101105"""
11111106function refine_mesh_for_linear_cosine_distribution! (
11121107 wing:: AbstractWing ,
11131108 idx,
11141109 spanwise_distribution:: PanelDistribution ,
11151110 n_sections:: Int ,
11161111 sections:: Vector{Section} ;
1112+ endpoints:: Bool = true ,
11171113 reuse_aero_data:: Bool = false )
11181114
11191115 n_input = length (sections)
11201116
1121- # Inline LE/TE accessor
11221117 @inline _le (s, j) = @inbounds s. LE_point[j]
11231118 @inline _te (s, j) = @inbounds s. TE_point[j]
11241119
@@ -1145,10 +1140,10 @@ function refine_mesh_for_linear_cosine_distribution!(
11451140 target = if spanwise_distribution == LINEAR
11461141 qc_total * (i - 1 ) / (n_sections - 1 )
11471142 else
1148- qc_total * (1 - cos (π * (i - 1 ) / (n_sections - 1 ))) / 2
1143+ qc_total * (1 - cos (π * (i - 1 ) /
1144+ (n_sections - 1 ))) / 2
11491145 end
11501146
1151- # Linear scan to find segment (n_input is small)
11521147 cum = 0.0
11531148 si = 1
11541149 for k in 1 : (n_input - 1 )
@@ -1170,7 +1165,6 @@ function refine_mesh_for_linear_cosine_distribution!(
11701165 end
11711166 s_l = sections[si]; s_r = sections[si + 1 ]
11721167
1173- # Recompute segment length for this pair
11741168 seg_d = 0.0
11751169 for j in 1 : 3
11761170 qc_j = (_le (s_r, j) + 0.25 * (_te (s_r, j) -
@@ -1214,142 +1208,11 @@ function refine_mesh_for_linear_cosine_distribution!(
12141208 end
12151209
12161210 new_data = reuse_aero_data ? nothing :
1217- calculate_new_aero_data (
1218- sections, si, wl, wr)
1211+ calculate_new_aero_data (sections, si, wl, wr)
12191212
1220- reinit! (wing. refined_sections[idx],
1221- new_le, new_te, s_l. aero_model, new_data)
1222- idx += 1
1223- end
1224-
1225- return idx
1226- end
1227-
1228- """
1229- refine_mesh_for_linear_cosine_distribution!(
1230- wing::AbstractWing,
1231- idx::Int,
1232- spanwise_distribution::PanelDistribution,
1233- n_sections::Int,
1234- LE::Matrix{Float64},
1235- TE::Matrix{Float64},
1236- aero_model,
1237- aero_data)
1238-
1239- Refine wing mesh using linear or cosine spacing.
1240-
1241- # Arguments
1242- - `wing`: Wing object
1243- - `idx`: Section start index
1244- - `spanwise_distribution`: [PanelDistribution](@ref)
1245- - `n_sections`: Number of sections to generate
1246- - `LE`: Matrix of leading edge points
1247- - `TE`: Matrix of trailing edge points
1248- - `aero_model`: Vector of aerodynamic models for each section
1249- - `aero_data`: Vector of aerodynamic data for each section
1250-
1251- # Keyword arguments
1252- - endpoints=true
1253-
1254- Returns:
1255- idx: Last section index
1256- """
1257- function refine_mesh_for_linear_cosine_distribution! (
1258- wing:: AbstractWing ,
1259- idx,
1260- spanwise_distribution:: PanelDistribution ,
1261- n_sections:: Int ,
1262- LE,
1263- TE,
1264- aero_model,
1265- aero_data;
1266- endpoints= true ,
1267- reuse_aero_data:: Bool = false )
1268-
1269- n_input = size (LE, 1 )
1270-
1271- # 1. Compute quarter chord points and cumulative lengths
1272- # using scalar arithmetic to avoid matrix allocations
1273- qc_cum = Vector {Float64} (undef, n_input)
1274- qc_cum[1 ] = 0.0
1275- for i in 1 : (n_input - 1 )
1276- d = 0.0
1277- for j in 1 : 3
1278- qc_j = (LE[i+ 1 , j] + 0.25 * (TE[i+ 1 , j] - LE[i+ 1 , j])) -
1279- (LE[i, j] + 0.25 * (TE[i, j] - LE[i, j]))
1280- d += qc_j * qc_j
1281- end
1282- qc_cum[i+ 1 ] = qc_cum[i] + sqrt (d)
1283- end
1284- qc_total = qc_cum[end ]
1285-
1286- # 2. Iterate over target positions
1287- new_le = MVec3 (0.0 , 0.0 , 0.0 )
1288- new_te = MVec3 (0.0 , 0.0 , 0.0 )
1289-
1290- for i in 1 : n_sections
1291- # Target length along quarter-chord
1292- target = if spanwise_distribution == LINEAR
1293- qc_total * (i - 1 ) / (n_sections - 1 )
1294- else # COSINE
1295- qc_total * (1 - cos (π * (i - 1 ) / (n_sections - 1 ))) / 2
1296- end
1297-
1298- # Find segment via binary search
1299- si = searchsortedlast (qc_cum, target)
1300- si = clamp (si, 1 , n_input - 1 )
1301-
1302- # Interpolation weight
1303- seg_len = qc_cum[si+ 1 ] - qc_cum[si]
1304- t = seg_len > 1e-30 ?
1305- (target - qc_cum[si]) / seg_len : 0.0
1306- wl = 1 - t; wr = t
1307-
1308- # Interpolate chord direction and length
1309- lc_len = 0.0 ; rc_len = 0.0
1310- for j in 1 : 3
1311- @inbounds lc_len += (TE[si, j] - LE[si, j])^ 2
1312- @inbounds rc_len += (TE[si+ 1 , j] - LE[si+ 1 , j])^ 2
1313- end
1314- lc_len = sqrt (lc_len); rc_len = sqrt (rc_len)
1315-
1316- # Interpolated chord direction (normalised)
1317- dir = MVec3 (0.0 , 0.0 , 0.0 )
1318- for j in 1 : 3
1319- @inbounds dir[j] = (
1320- wl * (TE[si, j] - LE[si, j]) /
1321- max (lc_len, 1e-12 ) +
1322- wr * (TE[si+ 1 , j] - LE[si+ 1 , j]) /
1323- max (rc_len, 1e-12 ))
1324- end
1325- dir_len = sqrt (dir[1 ]^ 2 + dir[2 ]^ 2 + dir[3 ]^ 2 )
1326- avg_len = wl * lc_len + wr * rc_len
1327-
1328- # Quarter chord point
1329- for j in 1 : 3
1330- @inbounds begin
1331- qc_j = LE[si, j] +
1332- 0.25 * (TE[si, j] - LE[si, j]) +
1333- t * ((LE[si+ 1 , j] +
1334- 0.25 * (TE[si+ 1 , j] - LE[si+ 1 , j])) -
1335- (LE[si, j] +
1336- 0.25 * (TE[si, j] - LE[si, j])))
1337- chord_j = dir[j] / max (dir_len, 1e-12 ) *
1338- avg_len
1339- new_le[j] = qc_j - 0.25 * chord_j
1340- new_te[j] = qc_j + 0.75 * chord_j
1341- end
1342- end
1343-
1344- # Interpolate aero properties
1345- new_data = reuse_aero_data ? nothing :
1346- calculate_new_aero_data (
1347- aero_model, aero_data, si, wl, wr)
1348-
1349- # Store section
13501213 if endpoints || (i != 1 && i != n_sections)
13511214 reinit! (wing. refined_sections[idx],
1352- new_le, new_te, aero_model[ 1 ] , new_data)
1215+ new_le, new_te, s_l . aero_model, new_data)
13531216 idx += 1
13541217 end
13551218 end
@@ -1396,11 +1259,11 @@ function refine_mesh_by_splitting_provided_sections!(
13961259 n_section_pairs = n_sections_provided - 1
13971260 new_sections_per_pair, remaining = divrem (n_new_sections, n_section_pairs)
13981261
1399- # Pre-allocate pair buffers to avoid per-pair allocations
1400- LE_pair = zeros (Float64, 2 , 3 )
1401- TE_pair = zeros (Float64, 2 , 3 )
14021262 sections = wing. unrefined_sections
14031263
1264+ # Pre-allocate a 2-element section buffer for pair refinement
1265+ section_pair = Section[Section (), Section ()]
1266+
14041267 # Process each section pair
14051268 idx = 1
14061269 for li in 1 : n_section_pairs
@@ -1420,30 +1283,20 @@ function refine_mesh_by_splitting_provided_sections!(
14201283 (li <= remaining ? 1 : 0 )
14211284
14221285 if num_new > 0
1423- s_l = sections[li]; s_r = sections[li + 1 ]
1424- # Fill pair buffers in-place
1425- for j in 1 : 3
1426- LE_pair[1 , j] = s_l. LE_point[j]
1427- LE_pair[2 , j] = s_r. LE_point[j]
1428- TE_pair[1 , j] = s_l. TE_point[j]
1429- TE_pair[2 , j] = s_r. TE_point[j]
1430- end
1431- aero_model_pair = (s_l. aero_model,
1432- s_r. aero_model)
1433- aero_data_pair = (s_l. aero_data,
1434- s_r. aero_data)
1286+ # Copy pair into reusable buffer
1287+ reinit! (section_pair[1 ], sections[li])
1288+ reinit! (section_pair[2 ], sections[li + 1 ])
14351289
1436- # Generate sections for this pair
14371290 start_idx = idx
14381291 idx = refine_mesh_for_linear_cosine_distribution! (
14391292 wing, idx, LINEAR,
14401293 num_new + 2 , # +2 for endpoints
1441- LE_pair, TE_pair,
1442- aero_model_pair, aero_data_pair;
1294+ section_pair;
14431295 endpoints= false , reuse_aero_data)
14441296
14451297 # Apply billowing by rotating chords around LE
14461298 if billowing_percentage > 0 && idx > start_idx
1299+ s_l = sections[li]; s_r = sections[li + 1 ]
14471300 le_l = s_l. LE_point; le_r = s_r. LE_point
14481301 diff_vec = le_l - le_r
14491302 span_len = norm (diff_vec)
@@ -1487,7 +1340,6 @@ Returns an `SVector{3}` (stack-allocated, zero heap allocs).
14871340@inline function rotated_te (le, te, y_hat, θ)
14881341 c1 = te[1 ] - le[1 ]; c2 = te[2 ] - le[2 ]; c3 = te[3 ] - le[3 ]
14891342 ct = cos (θ); st = sin (θ)
1490- # cross(y_hat, chord)
14911343 cx1 = y_hat[2 ] * c3 - y_hat[3 ] * c2
14921344 cx2 = y_hat[3 ] * c1 - y_hat[1 ] * c3
14931345 cx3 = y_hat[1 ] * c2 - y_hat[2 ] * c1
0 commit comments