Skip to content

UWTN 2026-014: Setting Up Full Multigrid - #21

Merged
lmoresi merged 15 commits into
mainfrom
note/setting-up-full-multigrid
Aug 17, 2026
Merged

lmoresi merged 15 commits into
mainfrom
note/setting-up-full-multigrid

Conversation

@lmoresi

@lmoresi lmoresi commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

The G1 note from WRITING-PLAN.md, and the pair to UWTN 2026-011. Ready to merge.

What Underworld's geometric multigrid is worth against the algebraic multigrid it falls back to, measured rather than asserted, and how to get a mesh that can use it.

What it says

  • Getting a hierarchy. refinement at mesh construction, what dm_hierarchy holds, and why the same resolution reached through a fine cellSize gives you nothing to coarsen onto. The preconditioner property, and reading preconditioner_settings / pc_fallbacks rather than inferring the configuration from timings.
  • Curved boundaries. Why refinement alone leaves a polygon, and the snapping callback that fixes it.
  • What the coarse mesh leaves behind, and that relax() loosens the imprint without disturbing the refinement relation — the hand-off to UWTN 2026-011.
  • The measurements. Against viscosity contrast and against problem size, in flops per unknown and in seconds, both preconditioners at PETSc's defaults.

The finding

On SolKz, whose viscosity varies smoothly across the box, the geometric hierarchy is worth about a factor of two in arithmetic and nothing in wall clock — 1.8 against 1.9 at 747 007 unknowns. GAMG does more arithmetic in denser, more regular operators and runs at 3.4–4.6 Gflop/s against FMG's 1.6–2.4, and the two effects cancel. Both converge on O(N).

Put the same total contrast into a band a twentieth of the box thick and that changes. FMG is almost indifferent; GAMG's cost climbs with the contrast, and at 10⁶ it does not converge — 20 000 multigrid cycles per velocity solve without reaching tolerance. That is the case for keeping a hierarchy when you can, and it is the figure the note ends on.

What was checked before claiming it

An earlier draft of this note claimed an order of magnitude on the strength of measurements taken against a GAMG that was misconfigured by us. That is fixed, and the fixes are underworld3#584 (pc_mg_type=additive where PETSc defaults to multiplicative; the velocity operator reaching GAMG with block size 1 on a vector field) and #576 (the Stokes outer Krylov inheriting gmres while its sub-blocks are Krylov solves). Correcting the outer alone moved the published contrast table at 10⁶ from 3.8 to 1.8 for FMG and 9.1 to 4.0 for GAMG.

So the baseline here is the fairest one available:

  • Both preconditioners run at PETSc's defaults, with GAMG told the velocity field has two components per node.
  • Stripping GAMG back to stock PETSc makes it worse on the band, not better — 38.7× FMG against 16.8× — so the finding is not an artefact of our remaining settings. pc_gamg_threshold=0.05 improves it to 11.9× and still does not close the gap.
  • The band is resolved: it spans about six cells here, and refining four and sixteen times further moves GAMG's disadvantage on the band relative to the smooth problem at the same size 5.5 → 7.9 → 8.0. It is not an under-resolution artefact.
  • Nothing reported is truncated. The default velocity iteration cap silently cuts these solves off (underworld3#585), which cost me one wrong conclusion before it was caught; every number here comes from a run that converged with no count on the cap.

Notes

  • Both example scripts and both figures ship in examples/, stamped at underworld3 0addec15. The measurement script sets the corrected options explicitly, so the numbers do not depend on whether #584 has landed — verified identical either way. When #584 merges the script can drop them and the stamp moves; the numbers will not change.
  • On publication, UWTN 2026-011 needs a v2 carrying the reciprocal link — it refers to this note in prose because it did not exist yet. WRITING-PLAN.md records that against this entry.
  • ID 2026-014: 2026-012 belongs to gthyagi's note on UWTN 2026-012: Benchmarking Stokes Flow in Annulus and Spherical-Shell Geometries #11 and 2026-013 to the news post.

pixi run build clean, pixi run validate clean, 2243 asset references all present, PDF builds.

Underworld development team with AI support from Claude Code

The G1 note from the writing plan, and the pair to UWTN 2026-011. Four things
that decide whether multigrid helps on the meshes we actually build, none of
which are solver options:

- turning it on, and what "auto" will and will not override;
- a level is a coarsening ratio, not an engine pass -- with the box-fault
  timings, 93.7s at nine levels against 23.3s at five, same mesh and same
  answer;
- how transfers are built when the levels were not made from each other, why
  the geometric fallback works despite agreeing with the real cells only 17% of
  the time in 3D, and the pullback that keeps the nested construction
  conditioned;
- the outer Krylov has to be flexible, with the notch table: gmres diverging at
  983 velocity iterations a step against fgmres at 58, and the iteration cap
  inert in every combination.

Closes by saying what the hierarchy is worth, which is the claim UWTN 2026-011
defers to this note for.

ID 2026-014: 2026-012 is gthyagi's note on PR #11 and 2026-013 the news post,
neither of which the allocator can see from this branch.

Underworld development team with AI support from Claude Code
@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Preview

Or the whole site.

Built from 6a5b02a. Shows notes at draft and review, which the published site withholds. Not indexed, no comments, and not the citable version. Confirmed serving this commit before this was posted.

lmoresi added 13 commits August 14, 2026 16:47
The first draft opened on non-nested hierarchies and transfer construction,
which assumes a reader who already knows what FMG is in this context. Nothing
has introduced it.

It now runs: what multigrid does, the two ways of building coarse levels and
what each needs, how to build a mesh that carries a hierarchy, why curved
boundaries need the snap callback the annulus already has, what the coarse
mesh's triangulation leaves in the fine one and how relax() loosens it, and
finally when the choice between GAMG and FMG is worth making.

The comparison is measured rather than asserted: same box, same discretisation,
twice refined. At constant viscosity both converge and the difference is
seconds. Across a 1e4 viscous layer GAMG does not converge the velocity block
at all -- capped at 200 and again at 2000, DIVERGED_ITS, 78 seconds to fail --
against three iterations and three seconds for FMG. The note says the GAMG
configuration is the untuned default and that supplying the near-null-space
would improve it.

The material the first draft led with -- non-nested transfers, the Delaunay
fallback, the flexible outer Krylov -- is not here. It belongs after this.

Example script shipped with the note.

Underworld development team with AI support from Claude Code
…ripts

The annulus is the clearer subject than a box: its base is coarse relative to
the curvature, so the coarse cells' edges survive two refinements as visible
seams. Median quality 0.940 to 0.972 and the tenth percentile 0.839 to 0.927,
against 0.976 to 0.994 for the box, where the base mesh is already good.

The caption records that the worst single cell gets worse, 0.827 to 0.763,
because the mover minimises a global energy and trades a few cells for many;
and that every node starting on a bounding circle is still exactly on it, which
is the boundary snapping and the interior-only motion both holding.

Both figures now ship their generating scripts in examples/, per house policy,
so they are archived with the deposit: relax_annulus_figure.py for the picture
and fmg-vs-gamg.py for the comparison table. Both run from the repository root
with their parameters at the top.

Underworld development team with AI support from Claude Code
Both tables now carry the same column, relative effort per unknown, normalised
to the cheapest FMG run. Being a ratio it does not depend on the machine, so
the numbers mean the same thing wherever the note is read, and the two tables
can be compared with each other rather than only resembling each other.

The problem is now stated: SolKz on the unit box, viscosity e^{2Bz}, forcing
(0, sin(m pi z) cos(n pi x)) with n=3 m=2, free slip, Taylor-Hood P2-P1. It has
a closed form, and the note says plainly that nothing here uses it -- these are
timings.

Added the scaling result: FMG's velocity iteration count is EXACTLY 48 at every
problem size across a 64-fold growth, so everything above linear in its timing
is the cost of an iteration rather than more iterations. Solve time goes as
N^1.16 against N^1.69 for GAMG, and neither is N log N -- that would predict
growth factors of 4.67, 4.58, 4.51 against the 5.21, 4.92, 4.83 measured.

Two caveats stated rather than buried: the solver tolerance is held fixed as
the mesh refines, which answers what it costs to solve the system rather than
what it costs to reach the accuracy the mesh supports; and GAMG is running
without the rigid-body near-null space, which Underworld does not supply for
the velocity block.

Also records that SolKz is a gentle test -- a localised viscous layer stops
GAMG converging entirely above about 1e2, where the smooth profile does not.

Underworld development team with AI support from Claude Code
…utput

The shipped example now emits exactly the two tables in the note, both
normalised to effort per unknown relative to the cheapest FMG run, plus the
fitted exponents.

Re-running it moved the numbers by two to nine per cent: 1.12 to 1.14, 3.45 to
3.55, 13.04 to 14.16. These are wall-clock measurements and do not reproduce to
the digit. The note is updated to the run the shipped script produced, and now
says the ratios move by a few per cent between runs and should be read as the
shape of the difference rather than as constants. The fitted exponents moved
from N^1.16 and N^1.69 to N^1.15 and N^1.70, which is the same answer.

Underworld development team with AI support from Claude Code
…near

The script now repeats each configuration three times and reports the median.
Worst run-to-run spread was 5%, so the tables are quoted to one decimal place;
two implied a precision the measurement does not have.

Reporting the superlinear trend rather than rounding it away: an ideal
multigrid solve is O(N) and neither of these is. FMG at N^1.16 stays
predictable as a model grows, which is what makes it usable at scale; GAMG at
N^1.70 makes the cost of the next refinement hard to plan for.

Also says the solver holds up: FMG converged on every problem here, constant
viscosity through 1e6 contrast and three thousand unknowns through nearly two
hundred thousand, with no tuning beyond choosing it, and GAMG converged
everywhere too -- more slowly and with a worse trend, but not fragile.

Underworld development team with AI support from Claude Code
The 48 was never 48 cycles on the velocity block. Decomposed, it is SIXTEEN
invocations of three cycles each: the Schur factorisation calls the velocity
solve sixteen times per Stokes solve, and the monitor was summing across all of
them. Sixteen is the same for both preconditioners at every resolution, so it
cancels from any comparison and was pure noise in the number.

What is left is the number worth quoting. FMG converges the velocity block in
THREE cycles whatever the resolution -- the mesh grows sixteenfold and the work
to solve does not move -- against GAMG's 89-126, then 243-347, then 723-1129.

The summed counts are out of both tables and out of the script's tables with
them; the script now reports the decomposition the note quotes, and prints the
invocation count so the cancelling factor is visible rather than assumed.

Still says what the counts do not measure: a geometric cycle and an algebraic
one are different amounts of work, so three against a hundred is not a
hundredfold difference in effort. The timings answer that; the counts show the
shape.

Underworld development team with AI support from Claude Code
The script disagreed with the note by 35% at the largest problem, reporting a
doubled velocity-solve count that no configuration reproduced when measured
alone. The cause was the iteration cap. GAMG at 186,879 unknowns needs up to
4,136 cycles per velocity solve; the cap was 2,000, so it was cut off mid-solve
and the outer solve compensated by calling the velocity block twice as often.
The extra work was the truncation, not the method. Cap raised to 20,000, which
nothing here comes near.

The note's numbers were the correct ones throughout -- 178.3, sixteen velocity
solves, GAMG's flop exponent settling at 1.85 rather than climbing to 2.06.
Only the timings and rates move, by the few per cent they always do.

Also measures each configuration in a fresh process. That was aimed at the
wrong cause -- there was no contamination between runs -- but it is the right
design anyway: it is how every number in the note was actually obtained, and it
means a sweep cannot be perturbed by what ran before it.

Both scripts now record the underworld3 commit they were run against, in their
docstrings and in the note's software_version. uw.__version__ reports 0.0.0 for
every build, so the commit is the only thing that identifies what these numbers
came from.

Underworld development team with AI support from Claude Code
Underworld's GAMG settings deviate from PETSc's in two ways that cripple it on
a vector problem: pc_mg_type=additive where PETSc defaults to multiplicative,
and block size 1 on a two-component velocity field, so it aggregates scalars
rather than nodes. Together they cost a factor of seven in cycles and 3.7 in
arithmetic. Filed as underworldcode/underworld3#579; the script now corrects
both before measuring, and says so.

Correcting them overturns the conclusion the note was drawing.

GAMG is a properly scaling multigrid method. Its cycle count per velocity solve
is 26-32, 34-47, 33-49 across a sixteenfold growth -- flat, like FMG's three --
and its flop count goes as N^1.25, N^1.04, N^1.11, converging on linear just as
FMG's does. The earlier claim that its exponent settles near 1.85 and rises was
an artefact of the broken configuration.

The geometric hierarchy is worth about a factor of TWO in arithmetic, held
consistently through six orders of viscosity contrast and a 250-fold growth in
problem size. Not the order of magnitude it appeared to be.

In wall clock it is worth nothing at the largest size measured -- 2.3 against
2.2 -- because GAMG runs at 5-6 Gflop/s where FMG manages 2-4 and falls at the
largest size as the problem outgrows cache. Doing half the work and getting
none of the time back is the honest summary, and it is why both measures stay
in the note.

Also removed the claim that a localised viscous layer stops GAMG converging.
That was measured against the broken configuration and has not been retested.

Underworld development team with AI support from Claude Code
The conclusion changed late and several passages still carried the earlier one.
Report the comparison at PETSc's defaults and leave it at that: the aside about
how Underworld had configured GAMG is a matter for the issue tracker, not for a
note about how multigrid works.

Also: a capitalised word mid-sentence in the opening; a duplicated clause in the
FMG definition that named the coarsest level twice; a missing sentence break
before stokes.preconditioner_settings; a trailing full stop on one heading and
no other; and a closing paragraph built out of balanced antitheses, replaced
with the mechanism it was gesturing at.

Underworld development team with AI support from Claude Code
Every other article has one. Generated from the model rather than a stock
photograph, which is the house pattern for a method note and leaves nobody to
credit.

The subject of the note is that a mesh built with refinement keeps its coarser
ancestors and that those are what the preconditioner works on, so the banner is
that hierarchy: 116, 464 and 1856 cells, each level subdividing every cell of
the one before. The inner circle is visibly a polygon on the base mesh and
rounds out as the refinement callback snaps each new boundary node back onto
the true radius, which is the point of the section on curved boundaries.

Underworld development team with AI support from Claude Code
The comparison so far used SolKz, whose viscosity varies smoothly across the
whole box -- the distribution algebraic coarsening reads best. Putting the same
total contrast into a band a twentieth of the box thick, and changing nothing
else, separates the two methods: FMG is almost indifferent, GAMG's cost climbs
with the contrast, and at 1e6 it does not converge within 20 000 multigrid
cycles per velocity solve.

Not an under-resolution artefact. The band spans about six cells here, and
resolving it better does not rescue GAMG -- at four and sixteen times the cell
count its disadvantage relative to the smooth problem at the same size goes 5.5,
7.9, 8.0. Comparing band against smooth at equal size is what makes that a
control: the problem size cancels.

Also in this pass:

- The scaling table now runs both preconditioners to 747 007 unknowns. It
  previously compared FMG at that size against GAMG one level down, which is not
  a comparison; the wall-clock reading is 1.8 against 1.9, at the same size.
- Cycle counts are gone. A geometric cycle and an algebraic one are different
  amounts of work, so the two columns never compared and only invited the
  reader to divide them.
- The code sample for selecting a preconditioner shows how to select one.
- SolKz now points at the companion note on testing against exact solutions.
- The passage on flops against wall clock says what it depends on and leaves the
  reader to time their own machine.

Underworld development team with AI support from Claude Code
Alt text stands in for the image when it cannot be seen, so it has to carry what
the image shows. The previous text described the SHAPE of the curves and left
the reader with no way to know what any of them said.

The band measurements appear nowhere else in the note -- the tables cover the
smooth case only -- so the alt text is the sole route to those numbers for a
reader who is not looking at the picture, and it now reads them out.

Underworld development team with AI support from Claude Code
Follows UWTN 2026-011, which carried status and date in the merge commit and
took its DOI at the deposit step afterwards.

Underworld development team with AI support from Claude Code
@lmoresi lmoresi changed the title Draft UWTN 2026-014: Setting Up Full Multigrid UWTN 2026-014: Setting Up Full Multigrid Aug 16, 2026
@lmoresi

lmoresi commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

Adversarial review — UWTN 2026-014

Read against the merged state of the branch. One item blocks, three are wrong as written, one is cosmetic.

1. The cross-reference is a dead link on merge

/testing-a-solver-against-exact-solutions/ is UWTN 2026-015, which is #22 and still open. Merging this note publishes a link to a page that does not exist. scripts/check_links.py will not catch it — it resolves external hosts only, and reported nothing.

Three ways out, in order of preference:

2. "GAMG does slightly more work" understates the table above it

Both solver configurations handle the whole range ... there is almost no change in the amount of work required or time taken to solve. GAMG does slightly more work but does so a little more efficiently.

The table directly above reads 2.2, 3.3, 4.0, 4.0 against 1.0, 1.3, 1.6, 1.8. That is two to two-and-a-half times the work, not "slightly" more, and GAMG's own row rises by 80% across the sweep rather than showing "almost no change". Twenty lines later the note says the hierarchy is "worth roughly a factor of two", which is the same quantity described a second way. A reader who compares the two sentences finds them in conflict.

Suggested: "Both handle the whole range, and neither degrades relative to the other as the contrast rises. GAMG does a little over twice FMG's arithmetic throughout, and gives about half of that back in rate."

3. "a factor of two in throughput" is backwards

So the geometric hierarchy is worth roughly a factor of two in throughput

Throughput is work per unit time, and it is the one measure on which GAMG wins here — 3.4–4.6 Gflop/s against 1.6–2.4, which the next paragraph says. The factor of two is in arithmetic. As written the sentence claims the opposite of the table.

Suggested: "worth roughly a factor of two in arithmetic".

4. The heading promises what the section then withdraws

## When the choice matters sits over the smooth case, whose finding is that the choice does not much matter — two-fold in flops, nothing in wall clock. The section where it does matter is the next one. Consider ## What it costs on a smooth problem and letting the concentrated section keep the "when it matters" role.

5. Minor

relative to the constant-viscosity FMG run with a fixed target iteration tolerance across the board.

The tolerance clause attaches to "run" rather than to the comparison, so it reads as though the baseline had a special tolerance. Splitting it into its own sentence fixes it.

What we checked and found sound

  • Every number traces to examples/fmg-vs-gamg.py output, and both figures ship their scripts.
  • The scaling table now runs both preconditioners to the same largest size; the earlier version compared FMG at 747 007 against GAMG at 186 879.
  • No reported run is truncated. This matters more than it sounds: the default velocity cap silently cuts these solves off (underworld3#585) and produced one wrong conclusion during the work before it was caught.
  • The concentrated-band claim survives the two controls that could have killed it — stripping GAMG back to stock PETSc makes it worse (38.7x against 16.8x), and refining the band from 1.6 to 6.4 cells across moves the penalty 5.5 → 7.9 → 8.0 rather than removing it.
  • The exponents quoted match the per-step values the script prints.

Underworld development team with AI support from Claude Code

…n contradicted

'When the choice matters' sat over the smooth case, whose finding is that it
mostly does not, and the case where it does had a heading of its own further
down. Dropping the second heading puts both under the first, which the section
then answers by the end.

Underworld development team with AI support from Claude Code
@lmoresi
lmoresi merged commit 314d763 into main Aug 17, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant