You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Tracking issue for the compact flux-form ∇·(κ∇u) leaf and its rollout across the execution stack.
Why
The algebraic spelling divergence(g) * scaling(κ) * gradient(g) chains two collocated centered first differences, giving a wide 2Δ stencil. The equation at cell I samples the flux κ∇u only at I±e, never at I, so no equation ever couples κ at two adjacent cells — a κ-inversion decouples exactly into the even and odd (i+j)-parity sublattices, fit to disjoint halves of the noisy data and tied together only by the regularizer. #48 has the measurements.
The cure is the finite-volume form: face fluxes q_{i+½} = κ_{i+½}(u_{i+1} − u_i)/Δ, differenced across the cell, with κ averaged to faces. Every equation then couples adjacent cells, so the checkerboard mode is not in the null space.
The leaf is worth having for three more reasons beyond accuracy:
it is exactly symmetric for real κ, which the composed form is not — test/algebra.jl can only assert that the declared transpose matches the dense transpose, never that the operator equals its own transpose;
it declares operator_diagonal, which the composition structurally cannot (operator_diagonal(::Composed) requires both factors diagonal), so it is the only spelling of variable-coefficient diffusion that a Jacobi/Chebyshev multigrid smoother can touch;
it is square and scalar→scalar, so it can be distributed where the composition cannot: its rank-changing factors are non-square and would each need their own partition spec.
Stages
Each stage is a reviewable PR. Later stages must not regress the invariants earlier ones establish.
Stage 3 is the one carrying real open design questions and should get its own design pass before implementation. Stage 2 turned out to need no communication at all — see #57.
Invariants that carry across every stage
Declared adjoints, never assumed. The leaf is symmetric face by face for real κ, so apply_adjoint! short-circuits to the forward action on an all-physical grid — but a grid carrying Interface faces must use the mechanical transpose gather, so cotangents reach the ghosts that halo_update_adjoint! folds. _selfadjoint_grid already makes the claim false on a non-uniform forest; keep it that way.
AD stays rule-free on the κ path. The coefficient path must remain plain taped array code. A custom rule that fires while carrying operator parameters silently zeroes coefficient-field gradients, and κ-inversion is the whole point of this operator. test/enzyme_rules.jl asserts rule_hits() is unchanged across a Diffusion gradient; keep that assertion alive as the leaf gains paths.
Coefficients are not solutions. κ is extended into ghosts by an even mirror and a periodic wrap, never by apply_bc! — the Dirichlet antisymmetric mirror would negate a material coefficient at a wall. Same hazard _average_to_coarse documents for coarsening.
Constant-κ parity. With κ ≡ c the leaf must agree with c * laplacian(g) to floating-point tolerance, so the finite-difference and finite-volume forms cannot drift apart. Not bitwise: the flux form multiplies by κ per face before summing, and the flux difference rounds differently from the three-point second difference.
Investigate a Symbolics.jl-first operator architecture #53 (Symbolics-first architecture) uses this operator as its worked example that symbolic and discrete manipulation do not commute. Its lowering target gains a second spelling; nothing blocks.
Tracking issue for the compact flux-form
∇·(κ∇u)leaf and its rollout across the execution stack.Why
The algebraic spelling
divergence(g) * scaling(κ) * gradient(g)chains two collocated centered first differences, giving a wide 2Δ stencil. The equation at cellIsamples the fluxκ∇uonly atI±e, never atI, so no equation ever couples κ at two adjacent cells — a κ-inversion decouples exactly into the even and odd(i+j)-parity sublattices, fit to disjoint halves of the noisy data and tied together only by the regularizer. #48 has the measurements.The cure is the finite-volume form: face fluxes
q_{i+½} = κ_{i+½}(u_{i+1} − u_i)/Δ, differenced across the cell, with κ averaged to faces. Every equation then couples adjacent cells, so the checkerboard mode is not in the null space.The leaf is worth having for three more reasons beyond accuracy:
test/algebra.jlcan only assert that the declared transpose matches the dense transpose, never that the operator equals its own transpose;operator_diagonal, which the composition structurally cannot (operator_diagonal(::Composed)requires both factors diagonal), so it is the only spelling of variable-coefficient diffusion that a Jacobi/Chebyshev multigrid smoother can touch;Stages
Each stage is a reviewable PR. Later stages must not regress the invariants earlier ones establish.
CartesianGridoperator_diagonal, multigrid rediscretization, AD_distributable,_slab_op, coefficient ghosts across the partition cutBlockForestreference pathStage 3 is the one carrying real open design questions and should get its own design pass before implementation. Stage 2 turned out to need no communication at all — see #57.
Invariants that carry across every stage
apply_adjoint!short-circuits to the forward action on an all-physical grid — but a grid carryingInterfacefaces must use the mechanical transpose gather, so cotangents reach the ghosts thathalo_update_adjoint!folds._selfadjoint_gridalready makes the claim false on a non-uniform forest; keep it that way.test/enzyme_rules.jlassertsrule_hits()is unchanged across aDiffusiongradient; keep that assertion alive as the leaf gains paths.prepare/construction time and must not appear in the per-apply cost. That is a feat(fv): add conservative finite-volume operators without sacrificing matrix-free AMR #54 success criterion and it interacts with meta(amr): forest performance — the coarse–fine exchange, not regridding, is the AMR tax #49 / perf(algebra): constant-fold affine time-step operators (α·I + β·Laplacian) into one stencil pass #52.apply_bc!— the Dirichlet antisymmetric mirror would negate a material coefficient at a wall. Same hazard_average_to_coarsedocuments for coarsening.κ ≡ cthe leaf must agree withc * laplacian(g)to floating-point tolerance, so the finite-difference and finite-volume forms cannot drift apart. Not bitwise: the flux form multiplies by κ per face before summing, and the flux difference rounds differently from the three-point second difference.Relationship to other issues
FluxDivergenceparameterized by a numerical flux, and conservative advection. This tracker is narrower — it carries one scalar diffusion operator through the whole stack, which is feat(fv): add conservative finite-volume operators without sacrificing matrix-free AMR #54 step 1 plus the execution work its steps 3–5 describe. Decisions made here (the face-flux descriptor seam, the coefficient-transfer policy) are the input feat(fv): add conservative finite-volume operators without sacrificing matrix-free AMR #54 needs before generalizing.operator_diagonal(::Laplacian)bug found while deriving this leaf's diagonal. Independent, but the fix is the same weight table.🤖 Filed by Claude, who has now read this stencil more times than is strictly healthy — Kyle is not narrating his own backlog.