Skip to content

Geometric multigrid is unreachable for every single-field solver, even when explicitly requested (the #276 lockout) #478

Description

@lmoresi

preconditioner="auto" / "fmg" is documented as giving geometric multigrid whenever the mesh carries a refinement hierarchy. For the Stokes velocity block it does. For every single-field solver — Poisson, Darcy, AdvDiffusionSLCN, Projection, SNES_Vector — it never does, at any refinement depth, even when asked for explicitly.

Measured, off the live PC after setup

solver mesh preconditioner= live PC
Stokes no hierarchy auto gamg
Stokes refinement=1 auto mg(2)
Stokes refinement=2 auto mg(3)
Poisson no hierarchy auto gamg
Poisson refinement=1 auto gamg
Poisson refinement=1 fmg (explicit) gamg + warning
Poisson refinement=2 fmg (explicit) gamg + warning

Repro: ~/+Simulations/rotated_pmat_audit/fmg_gate_audit.py. It reads the live PC rather than the options database, because the gate leaves no trace in the options — the bundle is simply never written.

Cause

SolverBaseClass._apply_preconditioner_options:

if want_fmg and prefix == "":     # prefix "" == single-field
    want_fmg = False

This is the #276 lockout, and it is not a bug: PETSc's DMCreateInjection fails at solve time with err62 (Could not locate matching functional for injection) on curved shells and some flat high-degree cases, so rather than let users hit that, the native route is closed for single-field solvers. #276 was closed on that basis, with custom_mg.set_custom_fmg() as the documented way around it.

Why it is worth reopening as a routing question

The workaround is real but it is opt-in, per-solver, and requires the user to build a coarse-mesh tail by hand — for a mesh that already carries one as mesh.dm_hierarchy. So the situation is: the hierarchy exists, geometric MG would work on it, the machinery to use it exists, and the user gets algebraic multigrid unless they know to go and wire it up themselves. On a scalar Poisson over a refined box, custom-P geometric MG converges in 1–3 iterations against GAMG's 13 (measured in custom_mg's own module docstring).

It is also silent in the direction that matters. preconditioner="auto" gives no warning at all — only the explicit "fmg" request warns. Anyone who set up a refined mesh expecting geometric multigrid on their advection–diffusion or projection solve has been running GAMG.

Proposed fix

Route single-field solvers to custom-P instead of falling back to GAMG, using the machinery that already exists:

  • mesh.dm_hierarchy is exactly a coarse tail — the same shape set_custom_fmg takes;
  • barycentric/RBF transfers with Galerkin RAP coarse operators need no injection anywhere, which is precisely what dodges err62;
  • custom_mg.build_transfers (added in One owner for the geometric-MG option bundle; rotated free-slip picks up a mesh-owned hierarchy (#468, #467) #471) is already the single "which hierarchy does this solver get?" rule, with the opportunistic barycentric→RBF fallback and degrade-to-GAMG on failure, so a build that does not work still cannot crash a solve.

Concretely, in _apply_preconditioner_options, for prefix == "" with n_levels > 1: register a CustomMGHierarchy built from the DMPlex levels rather than setting want_fmg = False. The existing solve-time hook (auto_inject_custom_mg) then installs it with no further change, and the warning can go.

Worth checking as part of it: this looks like it subsumes #425 (use the nested hierarchy for MG transfers on adapt children) — same source of levels, same transfer question.

Scope

This is a behavioural change to a default across every scalar and vector solver, so it wants its own PR and its own validation: the scalar suites, the adaptivity suites, and np2/np4. It is not a rider on #471.

Relationship to #471

#471 does not fix this, deliberately — the gate is a routing decision (which route a solver may take), not an option value, so it was kept out of the shared option-bundle owner. What #471 does change is the quality of the escape hatch: set_custom_fmg() on a single-field solver previously installed a drifted bundle (richardson at an iteration count nobody set), and now installs the same bundle as every other route. So single-field geometric MG got better where it was already reachable; it did not become reachable.

Found while auditing what #471 does and does not close, prompted by a note from a parallel session that geometric FMG is gated off.

Underworld development team with AI support from Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions