Conversation
…ion (#747) PR #493 fixed #457 by replacing `derive_by_array` + `permutedims` with explicit-index loops, and converted `SNES_Vector` and `SNES_Stokes_SaddlePt`. `SNES_Scalar` was missed, and still carried the same transpose: F1 = [L0 + 7 L1, 3 L0 + 2 L1] k = [[1,7],[3,2]] ImmutableMatrix(derive_by_array(F1, L)) = [[1,3],[7,2]] <- k TRANSPOSED `derive_by_array` is dx-FIRST, so it yields [dg][df] = dF1[dg]/dL[df] where PETSc's g3 wants [df][dg]. Invisible whenever the flux tensor is symmetric — which covers every shipped scalar model, since `DiffusionModel` takes a scalar kappa and `AnisotropicDiffusionModel` builds a diagonal one — and wrong the moment it is not. **This is not academic.** `SNES_Darcy` derives from `SNES_Scalar`, and a hydrodynamic dispersion tensor `D = a_T|v| I + (a_L - a_T) v(x)v / |v|` is exactly the case that reaches it. Only G3 changes. G1 and G2 flatten to unambiguous sequences (one free index each), and the shapes are held identical to the previous form — G0 (1,1), G1 (cdim,1), G2 (1,cdim), G3 (cdim,cdim) — so the JIT sees the same flat layout and only the index order moves. The loops also avoid the second hazard in that idiom. Handed a flux built from a matrix-valued symbol, `derive_by_array` returns an array whose `.shape` disagrees with its backing store (claims (2,2), holds 2 entries); nothing checks, and it surfaces frames later as a bare `IndexError` from sympy internals naming nothing useful. Verified a no-op where the code was already right: scalar and diagonal kappa give identical shapes and `g3[i][j] == dF1[i]/dL[j]` in both. 35 passed, 1 xpassed (a pre-existing fragile AdvDiff test whose marker states either outcome is acceptable) across Poisson, Darcy, Transient Darcy, Richards, AdvDiffusion and the units Poisson. Does NOT close #747: a full (dim, dim) tensor is still unreachable through any shipped model, so the regression test that would pin this cannot be written yet. That needs an API decision — see the issue. Underworld development team with AI support from Claude Code
|
Correction to this PR's framing, after maintainer review. Two claims in the The transpose is not observable through the public API
So this PR is not fixing a wrong answer that anyone can currently produce. The regression test I proposed would have tested an unreachable stateAsserting the index order needs a non-symmetric What IS worth testing is reachable todaydarcy.constitutive_model.Parameters.permeability = sympy.Matrix([[2, 3], [3, 5]])
# accepted unvalidated -> bare IndexError three frames down in sympy internals
Why the explicit loops still earn their placeThe flux law is general. A non-symmetric Underworld development team with AI support from Claude Code |
PR #493 fixed #457 by replacing
derive_by_array+permutedimswithexplicit-index loops, and converted
SNES_VectorandSNES_Stokes_SaddlePt.SNES_Scalarwas missed and still carried the same transpose:derive_by_arrayis dx-first, so it yields[dg][df]where PETSc'sg3wants[df][dg]. Invisible whenever the flux tensor is symmetric — which covers everyshipped scalar model, since
DiffusionModeltakes a scalar κ andAnisotropicDiffusionModelbuilds a diagonal one — and wrong the moment it isnot.
Not academic.
SNES_Darcyderives fromSNES_Scalar, and a hydrodynamicdispersion tensor
is exactly the case that reaches it. It is symmetric but not diagonal unless
the flow is axis-aligned.
Scope
Only
G3changes.G1andG2flatten to unambiguous sequences (one freeindex each), and shapes are held identical to the previous form —
G0 (1,1),G1 (cdim,1),G2 (1,cdim),G3 (cdim,cdim)— so the JIT sees the same flatlayout and only the index order moves.
The loops also avoid the second hazard in that idiom: handed a flux built from a
matrix-valued symbol,
derive_by_arrayreturns an array whose.shapedisagrees with its backing store (claims
(2,2), holds 2 entries). Nothingchecks it, and it surfaces frames later as a bare
IndexErrorfrom sympyinternals naming nothing useful.
Verified as a no-op where the code was already right
g3[i][j] == dF1[i]/dL[j](1,1) (2,1) (1,2) (2,2)— unchanged(1,1) (2,1) (1,2) (2,2)— unchanged35 passed, 1 xpassed across Poisson, Poisson-natural-BC, Poisson-spherical,
Poisson-constants, Darcy, Darcy sign regression, Transient Darcy, Richards,
AdvDiffusion Cartesian, AdvDiffusion annulus, and Poisson-with-units. The xpass
is a pre-existing fragile AdvDiff test whose own marker states either outcome is
acceptable.
This does NOT close #747
A full
(dim, dim)tensor is still unreachable through any shipped model, sothe regression test that would pin this cannot be written yet — and a
symmetric tensor would pass whether or not the bug were present, so the test
must use a non-symmetric one.
Closing #747 needs an API decision on where a full tensor belongs:
DarcyFlowModel.permeability(smallest change, but permeability anddispersivity are different physics sharing a slot),
AnisotropicDiffusionModelwidened from diagonal to full (honest to its name, already works on the Darcy
solver), or a new model. Left for the maintainers.
Underworld development team with AI support from Claude Code