Skip to content

Add neuralfoil and automatic conversion from obj to yaml#249

Open
1-Bart-1 wants to merge 89 commits into
mainfrom
neuralfoil
Open

Add neuralfoil and automatic conversion from obj to yaml#249
1-Bart-1 wants to merge 89 commits into
mainfrom
neuralfoil

Conversation

@1-Bart-1

@1-Bart-1 1-Bart-1 commented Jul 1, 2026

Copy link
Copy Markdown
Member

No description provided.

1-Bart-1 and others added 24 commits February 10, 2026 08:22
Integrates v3.0.0 changes from main:
- Unified Wing type (RamAirWing -> ObjWing)
- use_prior_polar feature for polar data reuse
- Allocation optimizations (scalar ops, LazyBufferCache)
- parse_enum for safe enum parsing
- Reference point validation
- Backend-agnostic test infrastructure (CairoMakie)
- c_ref and q_ref reference quantities

Preserves neuralfoil additions:
- NeuralFoil exports and integration (kulfan, neuralfoil, obj_slice, polar_generation)
- polars_dir parameter for per-section polars in ObjWing
- plot_airfoil_slices in Makie extension
- Printf dependency for neuralfoil formatting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The fitter omitted AeroSandbox's leading-edge-modification (LEM) basis and
reused the first Bernstein weight as the leading_edge_weight. That feeds the
wrong value into NeuralFoil network input #17 and lets the surface weights
absorb LE shaping, so predictions (especially near CL_max) drifted from the
reference the network was trained on.

Rewrite fit_kulfan_parameters as a single least-squares system with the shared
LEM column `x*(1-x)^(n+0.5)` and a trailing-edge thickness term, matching
aerosandbox.get_kulfan_parameters. Add the LEM term to kulfan_to_coordinates so
reconstruction stays consistent. Remove the now-unused per-surface helpers.

Validated against aerosandbox 4.2.9 / neuralfoil 0.3.2 on the ram air kite
foil: Kulfan parameters agree to ~1e-13 and CL/CD/CM to <1e-6 across
alpha = -10..20 deg. Add test/neuralfoil regression tests pinning these
reference values, and an examples/neuralfoil_benchmark.jl matching the upstream
Python speed benchmark.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
calculate_AIC_matrices\! is now zero-alloc (7 allocs) after the main merge's
calc_forces\! work, so the 1.12-specific @test_broken guard unexpectedly passed.
Collapse it to a plain @test and drop the now-unused IS_JULIA_1_12_OR_NEWER const.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
obj_to_yaml slices a 3D wing .obj at n_sections spanwise stations and writes the
package's native geometry inputs: per-section airfoil .dat files, NeuralFoil
polar CSVs, and a geometry.yaml referencing them via wing_sections /
wing_airfoils. After conversion everything flows through the standard
Wing(geometry_file) path.

Each section's LE/TE points are taken from the slice itself (min-x / max-x of
the planar y-slice), so the LE/TE, airfoil shape, and polar are all derived from
one source of truth rather than mixing the kite circle-arc parameterization with
planar slicing.

Extend WingAirfoilInfo with an optional dat_file so the airfoil shape stays
recoverable from the YAML for re-plotting / re-evaluation. ObjWing is left in
place for now (it still builds the full physics wing with mass/inertia).

Verified end-to-end on the ram air kite mesh: obj_to_yaml -> Wing(yaml) ->
BodyAerodynamics -> solve.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Write geometry.yaml in flow style (one-line headers, one line per section/airfoil
row) instead of YAML.write_file's verbose block style. Add read_dat_coordinates
and airfoils_from_yaml helpers; route generate_polar_from_dat through the shared
reader to remove duplication.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
plot_airfoils(geometry_file) plots every airfoil of a YAML wing in a subfigure
grid, reading the .dat files referenced by wing_airfoils (a loaded body does not
retain airfoil coordinates, so the YAML is the source). plot_section_polars(
body_aero, :cl|:cd|:cm) plots one coefficient vs alpha for every POLAR_VECTORS
section — the existing plot_polar_data only handles the single-airfoil
POLAR_MATRICES surface.

Document obj_to_yaml and both plot functions. Verified headless with CairoMakie.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Prepend the standard examples-project activation snippet to every example so
they run against examples/Project.toml regardless of how Julia is launched. Add
obj_to_yaml_kite.jl demonstrating the .obj -> YAML route plus the new airfoil and
per-section polar plots, register it in the menu, and add BenchmarkTools /
Interpolations to the examples project for the NeuralFoil benchmark.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PythonCall/CondaPkg generates .CondaPkg in whichever project initializes it
(root, examples), not just the two paths previously listed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lpha,delta)

Architecture: VSM core now ingests one standard format (geometry YAML + polar
CSVs) and depends on nothing heavy. Generation moves to lib/ sub-packages wired
via [sources] (SciML monorepo pattern):

- core: solver, geometry, yaml/csv readers, Cp types + IO. Sheds Xfoil, NPZ,
  Kulfan, NeuralFoil.
- lib/AirfoilAero (-> core): kulfan, neuralfoil, xfoil/neuralfoil solvers,
  deform, polar + cp generation. Owns Xfoil/NPZ.
- lib/ObjAdapter (-> AirfoilAero): obj mesh reading, slicing, obj_to_yaml, dat
  writing. ObjWing/refine_obj_wing! dropped (convert-then-load via Wing(yaml)).

Cp core (already validated): read_cp_data/cp.csv -> Section.cp_data -> averaged
into Panel.cp_polar in init_aero! -> evaluated per panel at (alpha, delta) in
calc_forces! -> VSMSolution.cp_upper_dist/cp_lower_dist + delta_cp(sol).
All-or-none Cp + uniform n_chord validated. deform_section gains
flip_thickness_neg (kite default true) for negative-flap lower-surface pivot.

Pipeline-generated polars (polars_neuralfoil/, polars_CFD_NF_combined/)
untracked + gitignored; regenerate via AirfoilAero/ObjAdapter.

All three packages precompile and load. Tests/examples not yet repointed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…foilAero

Core AeroModel LEI_AIRFOIL_BREUKELS renamed to POLY (kept as deprecated alias).
Sections now carry (cl_coeffs, cd_coeffs, cm_coeffs) instead of (tube_diameter,
camber); init_aero! and refinement interpolate the coeff vectors, calculate_cl/
cd/cm evaluate the polynomial. The Breukels (tube_diameter, camber) -> coeffs
derivation moves to AirfoilAero.lei_poly_coeffs, so core no longer knows any
airfoil-specific model - only generic polynomial evaluation.

AeroData union: (tube_diameter, camber) NTuple replaced by the 3-vector poly
coeff tuple. Validated: POLY wing (coeffs from lei_poly_coeffs) refines and
solves with finite, sensible cl/cd.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…h .dat

Move the airfoil-general I/O (read_dat_coordinates, write_dat, write_polar_csv,
generate_polar_from_coordinates, generate_polar_from_dat) into AirfoilAero so it
has a clean .dat-in / CSV-out interface. ObjAdapter keeps only obj-specific
generate_neuralfoil_polars, rewired through AirfoilAero. obj_to_yaml now writes
the per-section .dat and generates its polar via AirfoilAero.generate_polar_from_dat,
making .dat the actual ObjAdapter->AirfoilAero handoff.

Validated end-to-end: obj -> slices -> .dat -> AirfoilAero polar CSVs + geometry
YAML -> core Wing(yaml) loads and solves with finite cl.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Foundational step for repointing the test suite to the lib sub-packages. The test
files themselves (ObjWing/create_polars/generate_*/LEI callers) still need
repointing; suite is red until then.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
awegroup/awesIO already specifies the AWE geometry YAML matching our wing_sections/
wing_airfoils layout, with a type taxonomy (polars/breukels_regression/neuralfoil/
masure_regression/inviscid) that maps onto the converter/core split. Plan to adopt
it (add per-section VUP up-vector, type taxonomy, alpha_range/reynolds) rather than
a parallel format.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Alignment plan clarifies that awesIO wing_airfoils "type" is a solver/generation
concept (AirfoilAero), not an airfoil property; core only sees resolved polars/poly/
inviscid. Plan to adopt awesIO wing_sections VUP up-vector + type taxonomy. Draft
(unsubmitted) issue proposes an optional chord-slice Cp format for aero-structural
coupling, which awesIO currently lacks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- neuralfoil: kulfan/neuralfoil symbols now from AirfoilAero.
- polars: create_polars from AirfoilAero (read_aero_matrix stays core).
- wing_geometry: LEI (t,camber) test rewritten to POLY with coeffs from
  AirfoilAero.lei_poly_coeffs; asserts refinement interpolates the coeff vectors.

The 6 ObjWing-based tests are not repointed: ObjWing built an obj+single-foil
XFoil POLAR_MATRICES wing, a converter workflow ObjAdapter does not yet rebuild
(obj_to_yaml does per-section NeuralFoil instead). Needs a decision.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Core YAML loader gains POLAR_MATRICES support: wing_airfoils info_dict may carry
cl_file_path/cd_file_path/cm_file_path; load_matrix_polar_data reads the three
(alpha x delta) matrices via read_aero_matrix. ObjAdapter.obj_to_matrix_yaml
slices the mesh for LE/TE (perpendicular_sections), generates one shared airfoil's
cl/cd/cm matrices via AirfoilAero.create_polars, and writes the geometry YAML -
the convert-then-load replacement for the dropped live ObjWing. Removed tic/toc
(Timers) from create_polars.

Validated: obj+foil -> yaml -> Wing(yaml) loads POLAR_MATRICES and solves.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add ram_air_matrix_wing() test helper (obj_to_matrix_yaml -> Wing(yaml)) and
repoint wake/forwarddiff/results/refinement_validation/plotting to it.
kite_geometry: imports split (mesh utils from ObjAdapter, aero_matrix from core);
the three ObjWing-internal testsets (radius/gamma_tip/UNCHANGED/obj deform!) are
skipped with a redesign TODO since that behavior was intentionally dropped.

Numerics of the converted-wing tests still need a full test-env run to confirm
(assertions may need tolerance updates).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ups green

Verified in the test env: wing_geometry 295/295 (POLY), neuralfoil+polars 27/27
(AirfoilAero), refinement_validation+wake+results+forwarddiff 55/55 (obj+foil ->
POLAR_MATRICES -> Wing converter replacement, incl. ForwardDiff).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1-Bart-1 and others added 4 commits July 1, 2026 16:33
…d polars

- Move neuralfoil/kulfan cross-validation test to lib/AirfoilAero/test/runtests.jl
  with its own fixture (test/data/test_airfoil.dat = the former midspan foil coords,
  keeping the hardcoded aerosandbox/neuralfoil reference values valid). AirfoilAero
  gains [extras]/[targets] so it tests independently (SciML monorepo style).
- Remove the kite midspan foil.dat + generated cl/cd/cm foil polars from data/
  (regenerated from the .obj); gitignore them.
- Remove neuralfoil_benchmark.jl + neuralfoil_polars.jl examples.
- Condense multiline inline comments in the yaml loader.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants