Write the panel aerodynamics once, as traceable functions - #265
Merged
Conversation
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
1-Bart-1
force-pushed
the
feat/shared-panel-kernel
branch
from
August 24, 2026 13:28
5ce22f4 to
c7906ac
Compare
SymbolicAWEModels re-expressed this package's per-panel force symbolically so it could carry it in an ODE right-hand side: the airfoil axes, the chord blend, the angle of attack, the lift and drag directions, the force and the pitching moment, all transcribed by hand into ModelingToolkit equations. Two spellings of one derivation, and the recent billow-axis fix is exactly the kind of edit that silently changes only one of them. Pull that algebra into src/panel_aerodynamics.jl as pure functions of the section geometry and the flow: panel_axes, panel_inflow, panel_force_directions, panel_loads, and the small helpers around them. They are generic in the number type, free of in-place buffers, and branch-free apart from two ifelse guards, so the same definitions serve a Float64 solver, a ForwardDiff.Dual linearization and a symbolic trace. update_panel_properties!, init_pos!, calc_forces! and calculate_results now call them instead of spelling the algebra out three times, which is one copy fewer inside this package too. Panel geometry is unchanged bit for bit. Forces, moments and coefficients agree to within 1 ulp, the products having been reassociated: on a POLAR_VECTORS wing 47 of 55 reported quantities are bitwise identical and the largest relative difference is 2.1e-16. calc_forces! stays zero-allocation, and the geometry pass got slightly cheaper (update_panel_properties! 64 -> 0 bytes, reinit! 4208 -> 4144) because the chord no longer subtracts MVectors. section_pitch_rate gains a three-argument form taking the trailing minus leading edge apparent wind directly, which is the shape a symbolic consumer has; the four-argument form is unchanged. effective_alpha and panel_inflow's deficiency argument carry an unsteady lag into the angle the polars are read at. Nothing in the solver has unsteady state to feed them, but they belong beside the rest of the panel physics rather than in whoever grows it first. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1-Bart-1
force-pushed
the
feat/shared-panel-kernel
branch
from
August 25, 2026 09:41
c7906ac to
8cfb9ee
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
SymbolicAWEModelsre-expressed this package's per-panel force symbolically so it could carry it in an ODE right-hand side — the airfoil axes, the chord blend, the angle of attack, the lift and drag directions, the force and the pitching moment, all transcribed by hand into ModelingToolkit equations, plus twins offlow_curvature_cmandsection_pitch_rate. Two spellings of one derivation, kept in step by hand. The recent billow-axis fix (#264) is exactly the kind of edit that changes one and not the other.There were three copies inside this package too:
update_panel_properties!built the axes,calc_forces!built the force directions, andcalculate_resultsbuilt them again.What
src/panel_aerodynamics.jlholds that algebra once, as pure functions of the section geometry and the flow:plus
panel_span_vector,panel_chord,panel_chord_weight,dynamic_pressure,panel_moment,panel_couple_force,effective_alpha,smooth_norm, and the relocatedflow_curvature_cm/section_pitch_rate.They are generic in the number type, free of in-place buffers, and branch-free apart from two
ifelseguards, so one definition serves aFloat64solver, aForwardDiff.Duallinearization, and a symbolic trace.update_panel_properties!,init_pos!,calc_forces!andcalculate_resultsall call them.The companion SymbolicAWEModels PR (OpenSourceAWE/SymbolicAWEModels.jl#284) deletes its transcription and calls these instead.
Numerical equivalence
Measured against
mainon a 12-panelPOLAR_VECTORSwing over three inflow directions — 55 stored distributions plus the panel geometry, compared element by element:Panel geometry (
x_airf/y_airf/z_airf/chord/width/aero_center/control_point) is bitwise identical, which is the check thatpanel_axesand its offset chord blend reproduceupdate_panel_properties!exactly.The eight that differ are
lift_dist,drag_dist,panel_moment_dist,moment_dist,moment_coeff_dist,f_body_3D,m_body_3Dandmoment. The cause is reassociation, not a behaviour change:moment_distwas(f·ẑ·arm + m)·widthand is now(f·width)·ẑ·arm + m·width, and the dynamic pressure is now factored out of the lift/drag products instead of being multiplied through. Out-of-polar-range inflow producesNaNin exactly the same places as before.Allocations
calc_forces!solve_base!/solve!calculate_results(viasolve)update_panel_properties!reinit!(body_aero)The two improvements come from the chord no longer subtracting
MVectors.Notes
section_pitch_rategains a three-argument form taking the trailing minus leading edge apparent wind directly, which is the shape a symbolic consumer has. The four-argument form is unchanged.effective_alphaandpanel_inflow'sdeficiencyargument carry an unsteady lag into the angle the polars are read at. Nothing in the solver has unsteady state to feed them; they are here so a symbolic consumer that does reads the same definition rather than growing its own.smooth_norm,1e-12m) instead of branch-guarded.flow_curvature_cmandsection_pitch_ratekeep anifelseguard: a zerov_relor zero chord must still report zero, and dropping that guard made a zero-chord section return-1e12— caught bytest_flow_curvature.update_panel_properties!previously indexed out of bounds for a single-panel wing;panel_chord_weightreturns0.5there instead.🤖 Generated with Claude Code