Summary
Xfoil is a hard dependency, and Xfoil.__init__() costs ~2.45 s of fresh compilation in every Julia process that does using VortexStepMethod. Nothing on the aerodynamic solve path needs it — it is only reached through XFoilSolver. Making it a weak dependency would remove that cost for every user who does not generate polars with XFOIL.
Measurement
Julia 1.12.7, VortexStepMethod v4.3.1, measured via @time_imports using V3Kite (V3Kite depends on VSM), with --startup-file=no:
221.6 ms IntelOpenMP_jll
733.7 ms ModelingToolkitBase
502.9 ms TimeZones
┌ 2452.2 ms Xfoil.__init__() 99.99% compilation time
2454.3 ms Xfoil 99.90% compilation time
92.2 ms VortexStepMethod
Total for that using is ~10.3 s, so Xfoil alone is ~24% of it — the single largest entry, larger than the entire ModelingToolkit/Symbolics stack's individual contributions.
Cause
Xfoil.__init__ calls get_globals() and get_globals_cs() (Xfoil.jl:19-24), which build large NamedTuples wrapping the Fortran globals. Code reachable only from __init__ is not inferred while the module body is precompiled, so it is compiled from scratch on every load and cannot be cached in the pkgimage. This is upstream behaviour and not something VSM can fix directly — but VSM can avoid paying for it.
Why an extension fits here
In v4.3.1 the Xfoil surface is already narrow and already abstracted:
using Xfoil appears once, in src/airfoil_aero/AirfoilAero.jl:8
- all call sites are in
src/airfoil_aero/airfoil_solvers/xfoil_solver.jl (set_coordinates, pane, solve_alpha, cpdump, bldump)
XFoilSolver <: AbstractAirfoilSolver sits alongside neuralfoil_solver.jl behind the same interface, so users on the NeuralFoil path already never touch XFOIL at runtime
Proposed change
Move Xfoil to [weakdeps] and xfoil_solver.jl into a VortexStepMethodXfoilExt, mirroring the existing VortexStepMethodMakieExt. XFoilSolver would become available after using Xfoil; users who generate polars already have a reason to load it explicitly, and everyone else saves ~2.45 s per process.
If keeping XFoilSolver unconditionally exported matters, a stub method that errors with "run using Xfoil first" (with the extension providing the real, more specific method) keeps the name discoverable.
Alternative
An upstream PR to Xfoil.jl adding a PrecompileTools.@compile_workload that calls get_globals()/get_globals_cs() would cache those specializations in Xfoil's own pkgimage and fix this for all downstream users. That is the better global fix, but it is out of your hands and does not help until it lands and is released.
Context
Reported from the OpenSourceAWE stack — using V3Kite takes 12.3 s in a Revise-enabled REPL, and Xfoil plus a separate HDF5 invalidation account for roughly half of it. The HDF5 half has been fixed in V3Kite by the same weakdep treatment.
Summary
Xfoilis a hard dependency, andXfoil.__init__()costs ~2.45 s of fresh compilation in every Julia process that doesusing VortexStepMethod. Nothing on the aerodynamic solve path needs it — it is only reached throughXFoilSolver. Making it a weak dependency would remove that cost for every user who does not generate polars with XFOIL.Measurement
Julia 1.12.7, VortexStepMethod v4.3.1, measured via
@time_imports using V3Kite(V3Kite depends on VSM), with--startup-file=no:Total for that
usingis ~10.3 s, so Xfoil alone is ~24% of it — the single largest entry, larger than the entire ModelingToolkit/Symbolics stack's individual contributions.Cause
Xfoil.__init__callsget_globals()andget_globals_cs()(Xfoil.jl:19-24), which build large NamedTuples wrapping the Fortran globals. Code reachable only from__init__is not inferred while the module body is precompiled, so it is compiled from scratch on every load and cannot be cached in the pkgimage. This is upstream behaviour and not something VSM can fix directly — but VSM can avoid paying for it.Why an extension fits here
In v4.3.1 the Xfoil surface is already narrow and already abstracted:
using Xfoilappears once, insrc/airfoil_aero/AirfoilAero.jl:8src/airfoil_aero/airfoil_solvers/xfoil_solver.jl(set_coordinates,pane,solve_alpha,cpdump,bldump)XFoilSolver <: AbstractAirfoilSolversits alongsideneuralfoil_solver.jlbehind the same interface, so users on the NeuralFoil path already never touch XFOIL at runtimeProposed change
Move
Xfoilto[weakdeps]andxfoil_solver.jlinto aVortexStepMethodXfoilExt, mirroring the existingVortexStepMethodMakieExt.XFoilSolverwould become available afterusing Xfoil; users who generate polars already have a reason to load it explicitly, and everyone else saves ~2.45 s per process.If keeping
XFoilSolverunconditionally exported matters, a stub method that errors with "runusing Xfoilfirst" (with the extension providing the real, more specific method) keeps the name discoverable.Alternative
An upstream PR to Xfoil.jl adding a
PrecompileTools.@compile_workloadthat callsget_globals()/get_globals_cs()would cache those specializations in Xfoil's own pkgimage and fix this for all downstream users. That is the better global fix, but it is out of your hands and does not help until it lands and is released.Context
Reported from the OpenSourceAWE stack —
using V3Kitetakes 12.3 s in a Revise-enabled REPL, and Xfoil plus a separate HDF5 invalidation account for roughly half of it. The HDF5 half has been fixed in V3Kite by the same weakdep treatment.