Skip to content

fix: heat identification_error → area-weighted L2 integral (#85)#93

Draft
andrinr wants to merge 3 commits into
mainfrom
fix/heat-objective-area-weighted-l2
Draft

fix: heat identification_error → area-weighted L2 integral (#85)#93
andrinr wants to merge 3 commits into
mainfrom
fix/heat-objective-area-weighted-l2

Conversation

@andrinr

@andrinr andrinr commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Root cause

identification_error was the raw nodal sum Σ(T-T*)² in every forward, but the adjoint solvers (Firedrake, FEniCS) differentiated the area-weighted integral ∫(T-T*)²dΩ and multiplied by an approximate n_nodes/domain_vol scalar. Per the paper excerpt in the issue, that scalar is only ~5% accurate, so the gradient isn't the exact derivative of the reported objective — and L-BFGS folds the inconsistency into a corrupted Hessian estimate, converging to a suboptimal point.

Fix: make ∫(T-T*)²dΩ the reported objective everywhere

Solver Change Gradient
firedrake-heat forward reports assemble(inner(T-T*,T-T*)*dx) (plumbed through _solve_heat); removed nodal_correction on both rho & source grads exact (consistent mass)
fenics-heat same exact (consistent mass)
jax-fem forward weighted by lumped nodal volumes auto via jax.vjp through the objective
torch-fem-thermal forward weighted by lumped nodal volumes auto via torch.autograd
dealii-heat lumped-volume weighting on the reported scalar (forward-only) n/a

The shared _lumped_node_weights helper splits each axis-aligned hex cell's bounding-box volume equally among its 8 nodes. Unit-tested: total weight = domain volume; interior nodes (shared by 2 cells) weighted 2× corners.

Known limitations (why it's a draft)

  • Consistent vs lumped mass: Firedrake/FEniCS use the consistent mass matrix (*dx); the other three use lumped mass. These agree to O(h²) and converge to the same continuum objective, but aren't bit-identical. Reconciling to one convention is follow-up.
  • deal.II remains forward-only (no adjoint); its reported objective is now area-weighted but it still can't drive the optimization.
  • Reported metric changes for all solvers (integral, not nodal sum) — intended, but it shifts identification_error / final_ic_error numbers in published results.

andrinr and others added 2 commits June 29, 2026 14:28
Addresses #85 (WIP — needs the thermal-mesh benchmark to verify).

identification_error was reported as a raw nodal sum sum((T-T*)²) in every
heat solver's forward, while the adjoint-based solvers (Firedrake, FEniCS)
differentiated the area-weighted integral ∫(T-T*)²dΩ and multiplied the
result by an approximate n_nodes/domain_vol scalar to bridge the two. That
scalar is only ~5% accurate, so the returned gradient was not the exact
derivative of the reported objective; L-BFGS accumulates these inconsistent
gradients into a corrupted curvature estimate and converges to a suboptimal
point.

Make ∫(T-T*)²dΩ the reported objective everywhere, so the gradient is the
exact derivative of what is reported:

- firedrake-heat, fenics-heat (adjoint): the forward now reports the same
  assemble(inner(T-T*,T-T*)*dx) the adjoint already differentiates, and the
  n_nodes/domain_vol correction factor is removed (both rho and source
  gradients). Internally exact.
- jax-fem, torch-fem-thermal (autodiff): the forward objective is weighted by
  lumped nodal volumes derived from the shared hex mesh; since AD runs
  straight through the objective, the gradient is automatically consistent.
- dealii-heat (forward-only): same lumped-volume weighting applied to the
  reported scalar.

The lumped-volume helper splits each axis-aligned hex cell's bounding-box
volume equally among its 8 nodes (verified: total weight = domain volume).
Firedrake/FEniCS use the consistent mass matrix while the other three use
lumped mass; these differ at O(h²) and both converge to the same continuous
objective. Reconciling to one convention (and adding a deal.II adjoint) is
left as follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@PasteurBot

PasteurBot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

📊 View the full benchmark results

The rendered docs preview has every plot for this run (forward accuracy,
gradients, cost, optimization) merged with existing baseline results on main. The summary below reports pass/fail status.


Status diff vs base

Legend · ✅ ok · 🟠 anom · ❌ fail · · missing · 🚫 excluded (permanent — out of score denominator) · ⚪ excluded (work-to-do) · * stale — result predates current benchmark run

No status changes. · score 0.98 → 0.99

Full Mosaic status

Mosaic status

Legend · ✅ ok · 🟠 anom · ❌ fail · · missing · 🚫 excluded (permanent — out of score denominator) · ⚪ excluded (work-to-do) · * stale — result predates current benchmark run

Each solver is run against every experiment in the suite. ok = produced valid results; fail = crashed or returned invalid data; anom = ran successfully but tripped an automated quality check (e.g. poor gradient accuracy, outlier wall-clock time, or diverged optimisation). Thresholds are defined per-problem in the problem config.

problem ok anom fail missing excl (work) excl (perm) stale score
ns-3d-grid 98 0 0 0 0 9 5 🟢 0.98
ns-grid 117 7 0 0 0 16 0 🟢 0.97
structural-mesh 45 0 0 0 0 5 0 🟢 1.00
thermal-mesh 63 0 0 0 0 7 0 🟢 1.00
overall 323 7 0 0 0 37 5 🟢 0.99

Failures & anomalies

  • 🟠 ns-grid · forward/agreement/tgv · PhiFlow — phiflow's double CenteredGrid↔StaggeredGrid resampling gives 4.18% amplitude damping (ratio=0.9582); cosine=0.9999924 (pattern correct); arithmetic-average output conversion fix worsened error 9×; upstream library change required
  • 🟠 ns-grid · forward/agreement/tgv · XLB — automatic k=9 sub-steps reduce Ma 0.88→0.098 (81× Ma² reduction); errors drop from 0.216-0.278 → 0.026-0.031 (11-24× peers); remaining floor is O(dx²) LBM spatial discretization at N=64, not reducible by further sub-stepping (tested k=9..27); valid=True
  • 🟠 ns-grid · forward/baseline · INS.jl — staggered MAC grid double-interpolation: collocated TGV IC -> staggered faces -> collocated output gives sin^2(pi/N) round-trip error at all N; 35-40x above collocated peers
  • 🟠 ns-grid · forward/baseline · jax-cfd — staggered MAC grid double-interpolation: collocated TGV IC -> staggered faces -> collocated output gives sin^2(pi/N) round-trip error at all N; 35-40x above collocated peers
  • 🟠 ns-grid · forward/baseline · XLB — irreducible O(Ma²) LBM compressibility error floor: at fixed dt=0.01, Ma=u·dt/dx grows with N; at N=128 Ma~0.2 giving ~0.007 error floor (230× peers); anomalous at all N
  • 🟠 ns-grid · forward/tgv_nu_sweep · XLB — same root cause as forward/agreement/tgv — automatic k=9 sub-stepping reduces Ma 0.88→0.098 but residual O(dx²) LBM spatial discretization gives 11-24× peer errors at all nu values (0.0001–0.05); 0.0309 at nu=0.05 is 12.0× peer median; not reducible by further sub-stepping (tested k=9..27); valid=True
  • 🟠 ns-grid · cost/temporal_cost · PhiFlow — median time 11.6s is 24× peer median (0.47s); threshold k=20.0
ns-3d-grid — 16 experiment(s)
experiment Exponax INS.jl OpenFOAM PhiFlow PICT Warp-NS XLB
forward/agreement ✅*
forward/baseline ✅*
forward/physical_laws/vs_N ✅*
forward/physical_laws/vs_nu ✅*
forward/physical_laws/vs_steps ✅*
cost/spatial_cost
cost/temporal_cost
cost/vjp_cost/by_N 🚫
cost/vjp_cost/by_steps 🚫
gradient/fd_check 🚫
gradient/horizon_sweep_limits 🚫
gradient/jacobian_svd 🚫
gradient/jacobian_svd_nu01 🚫
gradient/jacobian_svd_steps20 🚫
gradient/jacobian_svd_steps40 🚫
optimization/recovery_constant_ic_bfgs_proj 🚫
ns-grid — 20 experiment(s)
experiment INS.jl jax-cfd OpenFOAM PhiFlow PICT Warp-NS XLB
forward/agreement/multimode
forward/agreement/tgv 🟠 🟠
forward/baseline 🟠 🟠 🟠
forward/cylinder 🚫 🚫 🚫
forward/physical_laws/vs_N
forward/physical_laws/vs_nu
forward/physical_laws/vs_steps
forward/tgv_nu_sweep 🟠
cost/spatial_cost
cost/temporal_cost 🟠
cost/vjp_cost/by_N 🚫
cost/vjp_cost/by_steps 🚫
gradient/fd_check 🚫
gradient/horizon_sweep 🚫
gradient/jacobian_svd 🚫
gradient/jacobian_svd_nu01 🚫
gradient/jacobian_svd_steps20 🚫
gradient/jacobian_svd_steps40 🚫
gradient/param_sweep 🚫
optimization/drag_opt 🚫 🚫 🚫 🚫
structural-mesh — 10 experiment(s)
experiment deal.II FEniCS Firedrake JAX-FEM TopOpt.jl
forward/agreement
forward/baseline
forward/physical_laws
cost/spatial_cost
cost/temporal_cost
cost/vjp_cost/by_N 🚫
cost/vjp_cost/by_steps 🚫
gradient/fd_check 🚫
gradient/param_sweep 🚫
optimization/topopt 🚫
thermal-mesh — 14 experiment(s)
experiment deal.II FEniCS Firedrake JAX-FEM torch-fem
forward/agreement
forward/baseline
forward/physical_laws
forward/source_baseline
forward/source_linearity
cost/spatial_cost
cost/temporal_cost
cost/vjp_cost/by_N 🚫
cost/vjp_cost/by_steps 🚫
gradient/fd_check 🚫
gradient/param_sweep 🚫
gradient/source_fd_check 🚫
gradient/source_width_sweep 🚫
optimization/conductivity_recovery_bfgs 🚫

@andrinr andrinr added the benchmark:solver Benchmark only the modified solver label Jun 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

benchmark:solver Benchmark only the modified solver

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants