Repository navigation
Keep calc_forces!' per-panel loads out of line so Julia 1.12 gets linearize's ForwardDiff Jacobian right on AVX-512 Zen - #378
Conversation
…il's The ram-air wing's tip sections are ~70%-thick slices (#361) on which NeuralFoil returns Cd=1 at every alpha, and those outputs differ with the runner's CPU, so the LOOP solve lands on one of two different solutions depending on the runner. Tables that are affine in (alpha, delta) make the bilinear interpolation exact, keep every panel attached, and are the same bits on every machine. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1-Bort-1
left a comment
There was a problem hiding this comment.
Independent review (advisory)
Verdict: APPROVE WITH COMMENTS · 2 inline, 0 off the diff
Good
- The change goes after the cause: affine tables make bilinear interpolation exact, so no grid knot can be a kink, and the tables no longer depend on the CPU; the three-dispatch CI result in the card, with norm_fwd bit-identical across 15 cells, backs this
- The solver settings, operating point, input indices, rtol and the
< 1e-4assertion are unchanged (the unchanged context lines confirm it), so no test was weakened - The knot-offset comment block is gone: it described a mechanism the card shows was wrong (the runners land on two solutions 11° apart, not near a knot)
- No existing helper duplicates this one:
rgfinds onlyram_air_matrix_wingintest/test_data_utils.jl(NeuralFoil) and a local two-sectionflat_wingclosure intest_backend_comparison.jl - The card names the
ram_*→matrix_*rename and scopes it to the testset's locals;src/is untouched and the diff stays at one idea
Not good
test/solver/test_forwarddiff.jl:8— This is a two-line comment block explaining why the tables are affine, which is the kind of why §3/§5 route to the PR card. At most keep one line, e.g. 'affine tables: bilinear interpolation is exact'.test/solver/test_forwarddiff.jl:79— The Windows ForwardDiff norm of 2.0576, which matched neither converged solution while norm_fd was bit-identical to ubuntu, is left unexplained, and this swap is what stops it being exercised. Under §5 that needs its own issue linked from the PR, or a possible Dual-path bug goes silent with the flake.- The card's 'Where I would push back' items (test_results.jl has the same NeuralFoil exposure,
fail-fast: falsein CI.yml) are noted but not linked to an issue; #361 covers the first only by implication - The check now runs only on smooth attached flow with affine tables, so it no longer tests ForwardDiff through a real table's curvature or near stall; the card names this as the risk, but no other test picks that coverage up
affine_matrix_wing(n_panels)has a single caller that passes 8; the argument is harmless but a literal inside the helper would say the same thing
claude, rubric CLEAN_CODE.md. A different lab from the implementer
on purpose: a reviewer sharing its blind spots would not flag its mistakes.
|
|
||
| relative_error(jac, reference) = maximum(abs.(jac .- reference)) / maximum(abs, reference) | ||
|
|
||
| # Four-section arc whose POLAR_MATRICES tables are affine in (alpha, delta), so the |
There was a problem hiding this comment.
MINOR: This is a two-line comment block explaining why the tables are affine, which is the kind of why §3/§5 route to the PR card. At most keep one line, e.g. 'affine tables: bilinear interpolation is exact'.
| ) | ||
| ram_body = BodyAerodynamics([ram_wing]) | ||
| ram_solver = Solver(ram_wing.n_panels, ram_wing.n_unrefined_sections; | ||
| matrix_wing = affine_matrix_wing(8) |
There was a problem hiding this comment.
MINOR: The Windows ForwardDiff norm of 2.0576, which matched neither converged solution while norm_fd was bit-identical to ubuntu, is left unexplained, and this swap is what stops it being exercised. Under §5 that needs its own issue linked from the PR, or a possible Dual-path bug goes silent with the flake.
There was a problem hiding this comment.
Opened #379 for it, with the lost curved-table coverage; linked from the PR.
…ide it Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Local full suite: FAIL (0 min, Julia 1.13.0, one cell of the matrix) |
|
This is not the solution right and should be closed. |
…ompiles for Dual{10} on Zen 4/5
panel_body_loads (@noinline) now returns panel_loads plus the moment about the
reference point, for calc_forces! and for the body_aerodynamics results loop,
which carried the same moment line. The POLAR_MATRICES testset also checks that
the Jacobian does not depend on the ForwardDiff chunk size.
Refs #360, #379
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…bounds=yes Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
TL;DR
On Julia 1.12 with
--check-bounds=yes(asPkg.testruns), LLVM 18's SLP vectorizer miscompilescalc_forces!'s per-panel force assembly for 10-partial Duals on AMD Zen 4/5 CPUs with AVX-512 (JuliaLang/julia#62368).linearize's default ForwardDiff Jacobian came out about 5% off there, and whether CI drew such a runner was the whole #360 flake. The loads and body moment now come from a@noinlinepanel_body_loads, which keeps the Jacobian exact there, and a new check makes sure the Jacobian does not depend on the ForwardDiff chunk size.This replaces #378, cut down as asked on #360: only the workaround and the chunk-size test. The POLAR_MATRICES check stays on main's ram-air wing; the affine tables are gone.
What was wrong
Diagnostic CI ran
repro379.jl(in #379) againstmainon 16 Julia 1.12.7 runners per round. It computeslinearize's Jacobian with chunk 10 and with chunk 5, which must agree to rounding:--check-bounds=yes--check-bounds=yes, SLP off+avx512fJULIA_LLVM_ARGS=-vectorize-slp=falsemakes it exact, which places it in Julia 1.12 and LLVM 18 vectorization defect JuliaLang/julia#62368. That issue has the LLVM fix identified (llvm/llvm-project@5d7cf504), still waiting on a backport to Julia 1.12's LLVM.--check-bounds=yes, are not affected. The finite-difference Jacobian was right everywhere.Dual{10}solve with two stitchedDual{5}solves put the first wrong value atsol.f_body_3Dincalc_forces!. Recompiling copies of it with single statements removed: leaving out either themoment_dist[i]line or them_body_3Dstore fixes it. The same loop in a function of its own, or in a standalone script, compiles correctly, so there is no package-free reproducer.What changed
panel_body_loads(axes, dirs, q_dyn, cl, cd, cm, c_span, arm)(src/panel_aerodynamics.jl,@noinline) returnspanel_loadsplusbody_moment, the moment about the reference point.calc_forces!andcalculate_resultsboth call it;calculate_resultscarried the same moment line, so the formula has one source now. Results do not move:norm_fwdwas bit-identical with and without the change.test/solver/test_forwarddiff.jl: a nested testset on the POLAR_MATRICES case,AutoForwardDiff(chunksize=5)matches the default chunk to 1e-12. That is the class this bug belongs to, checked with no finite-difference noise.Where I would push back
@noinlineworks around the bug and does not fix it. Other code with the same shape could hit the same SLP bug, and the chunk-size testset catches that only on an AVX-512 Zen runner, and only for this case. Julia 1.12's LLVM SLP vectorizer miscompiles calc_forces! for Dual{10} on AVX-512 Zen 4/5 under --check-bounds=yes (JuliaLang/julia#62368) #379 tracks removing it once Julia 1.12 ships the LLVM backport.Verification
0.04894348185682458 < 0.0001. Control runs onmain(35730023185, 35736962099):repro379.jlrel. 0.042 / 0.050 on every AVX-512 Zen runner.repro379.jlrel. 0 under--check-bounds=yes. That run carried the affine test; thesrc/change is the same one here.main(e62fc15): the 11 solver and body-aerodynamics test files, 5243/5243 pass locally (juliaserver, Julia 1.13.0). There is no local red: this box's i5-13500 has no AVX-512.agent ci-local, Julia 1.13, fullPkg.test): PASS in 6 min · GitHub CI: PASS (all five cells, docs, setup test)main--check-bounds=yeswould show.Scope
+34 / −10 across 6 files.
src/is +21 / −10: the new 13-line function, and its two callers trading their inline moment line for the call. The test gains the 8-line chunk-size testset; the docs page and the changelog take one entry each.🤖 Generated with Claude Code
Closes #360 · task
VortexStepMethod.jl-360