Found while adding the linear-exact local RBF interpolator (feature/linear-rbf), which builds the same kind of system locally.
1. No LinAlgError / conditioning path
utilities/custom_mg.py::rbf_prolongation:
M = np.block([[Acc, Pc], [Pc.T, np.zeros((dim + 1, dim + 1))]])
Praw = np.linalg.solve(M, B.T).T[:, :nc]
M is the polyharmonic + affine-tail saddle-point (KKT) matrix — symmetric but indefinite, not SPD. np.linalg.solve is LU with partial pivoting and raises LinAlgError outright on a singular M, which a duplicated or near-duplicated coarse point produces. There is no condition check, no smooth ridge applied by default (smooth=0.0, and no caller passes anything else), and no fallback.
The degeneracy handling is one line, and it addresses a different failure:
rs[np.abs(rs) < 1e-12] = 1.0 # keeps the Shepard row-normalise finite
By contrast the sibling barycentric_prolongation has an explicit out-of-hull fallback to the nearest coarse DOF.
For comparison, the local version added in feature/linear-rbf (utilities/rbf_stencil.py) pre-screens the affine block's rank by SVD, validates the two reproduction identities on the computed weights, and falls back per-point — so a bad stencil degrades instead of raising.
2. The test does not test the builder
tests/test_1015_custom_mg_prolongation.py::test_custom_mg_rbf_drives_solver asserts only:
assert pc.getType() == "mg"
assert snes.getConvergedReason() > 0
The solution-agreement check (rel < 1e-4 against nested FMG) and the iteration-count check in that file apply to the barycentric builder only. So rbf_prolongation has no accuracy assertion at all — a prolongation that merely converges to the right answer slowly, or one that quietly lost linear reproduction, would pass.
Note on scope
Not urgent for the MG path itself: since the orphan-repair fix (#424) the dense RBF builder no longer fires on real adapt children, and the barycentric builder is both sparse and linear-exact. This is recorded so the sharp edge is known if the RBF builder is ever selected deliberately.
Found while adding the linear-exact local RBF interpolator (
feature/linear-rbf), which builds the same kind of system locally.1. No
LinAlgError/ conditioning pathutilities/custom_mg.py::rbf_prolongation:Mis the polyharmonic + affine-tail saddle-point (KKT) matrix — symmetric but indefinite, not SPD.np.linalg.solveis LU with partial pivoting and raisesLinAlgErroroutright on a singularM, which a duplicated or near-duplicated coarse point produces. There is no condition check, nosmoothridge applied by default (smooth=0.0, and no caller passes anything else), and no fallback.The degeneracy handling is one line, and it addresses a different failure:
By contrast the sibling
barycentric_prolongationhas an explicit out-of-hull fallback to the nearest coarse DOF.For comparison, the local version added in
feature/linear-rbf(utilities/rbf_stencil.py) pre-screens the affine block's rank by SVD, validates the two reproduction identities on the computed weights, and falls back per-point — so a bad stencil degrades instead of raising.2. The test does not test the builder
tests/test_1015_custom_mg_prolongation.py::test_custom_mg_rbf_drives_solverasserts only:The solution-agreement check (
rel < 1e-4against nested FMG) and the iteration-count check in that file apply to the barycentric builder only. Sorbf_prolongationhas no accuracy assertion at all — a prolongation that merely converges to the right answer slowly, or one that quietly lost linear reproduction, would pass.Note on scope
Not urgent for the MG path itself: since the orphan-repair fix (#424) the dense RBF builder no longer fires on real adapt children, and the barycentric builder is both sparse and linear-exact. This is recorded so the sharp edge is known if the RBF builder is ever selected deliberately.