Skip to content

The generated ram-air polar fixture is not reproducible, so every test asserting numbers on it draws a fresh geometry each run #291

Description

@1-Bort-1

ram_air_matrix_wing (test/test_data_utils.jl:26) builds its wing by slicing data/ram_air_kite/ram_air_kite_body.obj and sweeping NeuralFoil over it into test/generated/ (gitignored, so CI regenerates it every run). That fixture is not reproducible across machines, and not even across two attempts of the same job.

norm(jac_fwd) from the @info "POLAR_MATRICES linearize jacobian norms" line in test/solver/test_forwarddiff.jl, run 34401975496 on #285 — same commit, same fixture config (n_panels=8, n_sections=4, alpha_range = deg2rad.(-5:5:15), delta_range = deg2rad.(-3:3:3)):

where norm_fwd
attempt 1, Julia 1.12 ubuntu with code coverage 2.1143274312638014
attempt 3, Julia 1.12 macOS aarch64 / 1.12 windows x64 / 1.11 ubuntu coverage 2.114573062599269
attempt 3, Julia 1.11 ubuntu / 1.12 ubuntu / 1.12 ubuntu coverage 2.165929233230906
agent box, Debian x64 2.2058861369294172

A 4.3% spread, and the same job — Julia 1.12 ubuntu with code coverage — gives 2.1143274312638014 on attempt 1 and 2.165929233230906 on attempt 3.

It is the fixture and not the code. The INVISCID sibling in the same file, which builds its wing in Julia and touches no generated file, reads norm_fwd = 11.110746084832735, norm_fd = 11.110746104003324 on this box and the same 17 digits in that failing coverage job. So VSM's solver, geometry pipeline, ForwardDiff and FiniteDiff paths are bit-reproducible, coverage build included; what moves is what obj_to_yaml writes.

Two things are ruled out:

  • Not the %.4f polar rounding (write_polar_matrix_csv, src/airfoil_aero/airfoil_solvers/common.jl:62). Jittering every Cl/Cd/Cm entry by ±1 unit in the last written decimal, 15 draws, moves norm_fwd by ±0.02% — two orders below the observed spread.
  • Not run-to-run on one box. Regenerating three times here with BLAS pinned to one thread gives byte-identical geometry.yaml, polars/*.csv and airfoils/*.dat. Only the full-precision pressure/*_cf.csv differ, by a few ulps, and those do not feed cl/cd/cm.

Scale of it in physical terms: scaling every Cl by 5% moves norm_fwd by 2.7% and the panels' converged alphas by 0.09°, so a 4.3% spread is of order 0.1–0.2° of panel alpha — enough to move a panel across the polar table's 5° alpha grid.

That is what makes #287 a lottery: each run draws a different set of panel alphas, and a draw that lands one within the finite-difference stencil of a table knot fails the AD-vs-FD check. #287 fixes that check's own error budget; it does not make the fixture stop moving, and every other test asserting a number on this geometry is drawing from the same hat.

What I would look at first: whether the difference is in geometry.yaml's section coordinates or in the polar values, by having a run print a hash of each generated file. The coordinates go through yaml_scalar(x::Real) = string(round(Float64(x); digits=3)) (src/airfoil_aero/geometry_yaml.jl:22), 1 mm on a 0.168 m tip chord, which is ~0.3° of tip incidence per rounding unit — so a last-digit flip there is not small. Whether a fixed fixture should be committed, or the generation made deterministic, is the decision this needs.

Found while working #287, which changes only test/solver/test_forwarddiff.jl and leaves this alone.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

agent:doneAgent task stateagent:needs-plan-approvalPost a plan and wait for approval before writing any code

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions