Geometric-MG bundle owns its outer-KSP pairing: fgmres, applied to the live KSP - #515
Conversation
…o the live KSP (#514) Since #471 the bundle's default smoother is gmres/4. A Krylov smoother makes the preconditioner vary between applications, and a plain-gmres outer's recurrence assumes it fixed: measured on a 3-D adapt child (PR #510 CI, then locally), the KSP reported CONVERGED_RTOL at 3 iterations with the true residual stalled at 1.3e-6 - a hundred times the gate. The same failure was found and fixed locally twice before, on the Stokes velocity sub-KSP and on the free-surface solver, which is exactly the piecemeal drift #471 exists to end; this puts the pairing in the one owner. Three parts. ksp_type=fgmres joins the geometric bundle for BOTH smoother variants - richardson does not need flexibility, but the stale-key derivation requires every variant to own the same keys, and fgmres judges convergence on the true residual, which is never wrong. The three solver-default writers of ksp_type go through _push_managed_option, so the bundle may upgrade the DEFAULT but never a user's explicit choice (the ownership latch). And the top-level install path applies the resolved type to the LIVE KSP: the database write alone is provably inert there, because the KSP consumed its options long before injection - the new live test caught exactly that in its first run. The fieldsplit velocity sub-KSP needs no live application (its setFromOptions runs at the parent's PCSetUp, after the write) and the rotated path already writes fgmres unconditionally on its per-solve prefix. On hierarchies where one FMG application converges the cycle, the two pairings are indistinguishable (measured: 3.3e-8 either way on the 2-D fixture); the drift needs a hierarchy the smoother varies on, which is the 3-D one-level-per-doubling shape arriving with PR #510 - its test_0842 is the discriminating gate, and passes on the composed tree. Closes #514. Underworld development team with AI support from Claude Code
lmoresi
left a comment
There was a problem hiding this comment.
Adversarial review (authoring session).
Where we attacked it:
- The inert-fix trap — caught by our own test's first run. The obvious fix (put
ksp_typein the bundle) does nothing on the standard path: the top-level KSP consumes its options before injection, so the DB write never takes. The live-object application in_configure_pcmg(ksp=...)is the actual fix; the live test asserts onksp.getType(), not the database, for exactly this reason. - Ownership latch semantics. A user's
petsc_options["ksp_type"] = "cg"survives: the latch sees an unrecorded key and the bundle backs off; the live application then applies the user's value (a no-op setType). The three converted default writers are internal writers of a now-bundle key, which #471's own doctrine says must be recorded — this was a pre-existing violation of that rule, not a new pattern. - Negative control on the new live test. On the 2-D fixture the broken and fixed pairings are indistinguishable (3.3e-8 both, one FMG application — measured before setting the gate), so the error assert is a sanity bound only and says so; the discriminating gate is
test_0842on the 3-D subsampled hierarchy, which goes 1.0e-6 → 2.7e-10 with this fix and is part of PR #510's CI. - Blast radius.
fast/richardson variant: stationary smoother, fgmres merely judges on the true residual — sound, small extra memory. Fieldsplit velocity sub-KSP: bundle value equals current practice (fgmres, per the code's own comment). Rotated path: writes fgmres unconditionally already, untouched. GAMG fallback:ksp_typeis in its stale list, so falling back clears our key and returns the default — verified by the bundle unit test.
Recommend merge; #510's re-run against post-merge development is the integration proof.
Underworld development team with AI support from Claude Code
There was a problem hiding this comment.
Pull request overview
This PR fixes a PETSc Krylov-method pairing defect in Underworld3’s geometric multigrid (FMG) option bundle: when the MG smoother itself is Krylov-based, the preconditioner becomes non-stationary between applications, so a non-flexible outer GMRES can report convergence while the true residual stalls. The change makes the geometric-MG bundle responsible for enforcing a flexible outer KSP type, and ensures that resolved defaults are applied to the live KSP object (not just written into the options DB).
Changes:
- Add
ksp_type=fgmresto the geometric multigrid option bundle (for both smoother variants) and ensure GAMG treats that key as stale to avoid prefix leakage. - Route solver-default
ksp_typewrites through_push_managed_optionso bundle ownership can upgrade defaults without overriding user-owned settings. - Apply the resolved
ksp_typeto the live top-level KSP during custom-P FMG injection, and add tests that assert both bundle invariants and live-object behavior on an adapt-child scalar solve.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
tests/test_1021_mg_option_bundle.py |
Adds regression tests for the geometric-MG “Krylov smoother ⇒ flexible outer” pairing and verifies the live KSP type on an adapt-child scalar solve. |
src/underworld3/utilities/multigrid_options.py |
Extends the geometric MG settings bundle to include ksp_type=fgmres so the bundle owns the outer/inner pairing rule. |
src/underworld3/utilities/custom_mg.py |
Threads the live top-level ksp into _configure_pcmg() and sets the resolved ksp_type directly on the live object after bundle application. |
src/underworld3/cython/petsc_generic_snes_solvers.pyx |
Switches solver-default ksp_type initialization to _push_managed_option() (and similarly for other bundle-owned defaults) to preserve bundle/user ownership semantics. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…_Boundary removal (#503) Two reconciliations beyond the textual merge: - custom_mg.py: #515 puts ksp_type=fgmres in the geometric bundle and applies the resolved type to the live KSP from _configure_pcmg — the one-owner form of the same fix our _ensure_flexible_outer carried for the scalar custom-P route. #515's mechanism covers that route (the solver-default ksp_type=gmres is now a managed option the bundle may upgrade, and _install_transfers passes the live KSP), so our helper is removed outright; no residue is needed. The explicit-gamg opt-out gate in build_transfers survives unchanged. - Null_Boundary fixtures: #503 stopped manufacturing the every-vertex sentinel label. test_0844's vertex-blanket negative control now builds its own blanket label explicitly (the hazard is any vertex-blanket label, not that one spelling — and the fixture is now self-contained); stale comments in test_0848, test_0842 and reconnect.py's _interface_edges docstring are updated to match, with the vertex exclusion kept load-bearing for caller enums and old checkpoints. Underworld development team with AI support from Claude Code
Fixes #514. First car of the landing train recorded in the planning-hub liaison ledger: this → #510 → #511.
The defect
Since #471 the geometric-MG bundle's default smoother is
gmres/4. A Krylov smoother makes the preconditioner vary between applications, and a plain-gmres outer's recurrence assumes it fixed. Measured on a 3-D adapt child (PR #510's CI, reproduced locally): the KSP reported CONVERGED_RTOL at 3 iterations while the true residual stalled at 1.3e-6 — one hundred times the gate. The same failure was previously found and fixed locally, twice: the Stokes velocity sub-KSP (the bundle's own comment records the reason) and the free-surface solver ("false-converges … TRUE residual blew up",systems/solvers.py). Piecemeal fixes of one pairing rule are the drift #471 exists to end.The fix, three parts
ksp_type = fgmresjoins the geometric bundle, both smoother variants — richardson does not need flexibility, but every variant must own the same key set (the stale-key derivation requires it), and fgmres judges convergence on the true residual, which is never wrong.ksp_typenow go through_push_managed_option, so the bundle may upgrade the default but never a user's explicit choice (petsc_options["ksp_type"] = ...still wins, per the One owner for the geometric-MG option bundle; rotated free-slip picks up a mesh-owned hierarchy (#468, #467) #471 ownership latch).setFromOptionsruns at the parent'sPCSetUp, after the write); the rotated path already writes fgmres unconditionally on its per-solve prefix.Verification
pc=mg+ksp=fgmreson the live objects.test_1021(20),test_0842(4), rotated paralleltest_1064at np=2 (9), Poisson/Stokes ND smoke (9) — all pass.test_0842passes at its 1e-8 gate (was 1.0e-6 broken; 2.7e-10 fixed), 40 tests green across 0842/1021/0753.test_0842is the discriminating gate.Underworld development team with AI support from Claude Code