Restrict star patches to the adaptively refined region - #5329
Conversation
…tion ASMStarPC now has a single IS-construction mechanism, in firedrake/cython/patchimpl.pyx, used for every case rather than only the colored one: create_star_points() takes the star of each owned seed and create_patch_ises() gathers the dofs those points carry. This reproduces what the Python path built, so a reordering requested with pc_star_mat_ordering_type still applies per star, and order_points() no longer reverses its input since its callers now do. The seeds may be restricted to the entities marked by a DMLabel, named by pc_star_construct_label, and the coloring then colors only those. With pc_star_adaptive the label comes from adapt.mark_refined_entities(), which marks the cells whose parent was genuinely split along with their closure: that is the set of entities whose star meets the refined region, which is what a smoother on an adaptively refined level should relax. PatchPC gains the same option as patch_pc_patch_adaptive, so that PCPatch and ASMStarPC build the same restricted patches and take the same number of iterations. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1e916b4 to
06872ac
Compare
… refined mark_refined_entities() raised on a mesh with no adaptive parent, so patch_pc_patch_adaptive failed on the coarsest level of a hierarchy and on every uniformly refined one. PCMG applies mg_levels_ to level 0 as well, so a single-level hierarchy hit this on its first solve. Such a level has no refined region to single out: every cell of a uniformly refined level was split, and a mesh with no parent is new in its entirety. mark_refined_entities() now returns None there, which both preconditioners already read as relaxing everywhere, so the same option is correct on a uniform level and an adaptive one. The demo solves a cubic Poisson problem over a hierarchy that starts with two uniform refinements of a single square and grows three adaptive levels on top of it, and draws the patch seeds of each level colored by their color. The discs are drawn half the way to the nearest vertex, so they shrink with the mesh and never touch: the first two levels carry a patch on every vertex, and the adaptive levels only over the refined region. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The star patches this branch builds need -pc_patch_construct_label and DMPlexTransformCreateSplitCellLabel(), which are not in petsc/petsc main yet. Clone the tip of the stack !9480 -> !9414 -> !9481 instead, and drop this commit once those merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
06872ac to
5352181
Compare
| Contributed by `Pablo Brubeck <https://www.maths.ox.ac.uk/people/pablo.brubeckmartinez/>`_. | ||
|
|
||
| The demo :doc:`Using patch relaxation for multigrid <poisson_mg_patches.py>` builds a | ||
| vertex star patch around every vertex of every level. That is the right thing to do when |
There was a problem hiding this comment.
Clarify level means mesh refinement level
|
|
||
| An adaptively refined level is different. Only the cells near the marked region were | ||
| split; everywhere else the level is a copy of its parent, already resolved by the coarser | ||
| levels, and relaxing there costs work without improving the error. This demo restricts |
There was a problem hiding this comment.
without substantially improving
(it is effectively doing more relaxation sweeps, which is probably helping, but not much)
| that reaches a whole vertex star, which is what the ``distribution_parameters`` request. :: | ||
|
|
||
| import numpy | ||
| from scipy.spatial import cKDTree |
There was a problem hiding this comment.
I wouldn't call this one of the usual imports. What is it for? Maybe import it later nearer the plotting?
| A :func:`~.MeshHierarchy` records parent child relations however its levels were made, so | ||
| uniform and adaptive levels stack in the same object. We start from a single square cut | ||
| into two triangles and refine it uniformly twice, which gives a 4x4 hierarchy of three | ||
| levels, and the adaptive levels will grow on top of those: :: |
There was a problem hiding this comment.
Break up the sentence: "... three levels. The adaptive levels will grow on top of these"
| mh = MeshHierarchy(base, 2) | ||
| mesh = mh[-1] | ||
|
|
||
| This composition is worth doing. Uniform levels are cheap to build and cheap to apply, and |
There was a problem hiding this comment.
"This composition of uniform and adaptive refinement is often useful"
| "mg_levels_0": { | ||
| "mat_type": "aij", | ||
| "ksp_type": "preonly", | ||
| "pc_type": "lu", |
| params = { | ||
| "mat_type": "aij", | ||
| "snes_adapt_sequence": 3, | ||
| "ksp_type": "cg", |
There was a problem hiding this comment.
add the dm_plex_local_coloring?
| print(f"Solved on {len(hierarchy)} levels, {solution.function_space().dim()} degrees of freedom") | ||
|
|
||
| :class:`~.ASMStarPC` offers the same thing under ``pc_star_adaptive``, building the index | ||
| sets itself rather than going through PCPatch. |
There was a problem hiding this comment.
This doesn't usefully summarise the difference with PCPatch. Maybe say "extracting the submatrices for the vertex stars from the assembled matrix, instead of building them matrix-free?
| Looking at the patches | ||
| ---------------------- | ||
|
|
||
| The restriction and the coloring are both visible from Python. ``mark_refined_entities`` |
There was a problem hiding this comment.
The mark_refined_entities function from firedrake.adapt
| The restriction and the coloring are both visible from Python. ``mark_refined_entities`` | ||
| returns the label that ``patch_pc_patch_adaptive`` passes to PCPatch, and returns `None` | ||
| for a level with no adaptive parent, which is how a uniformly refined level ends up | ||
| relaxing everywhere. ``createColoringLabel`` then colors the marked vertices, under the |
There was a problem hiding this comment.
plex.createColoringLabel instead of createColoringLabel
ASMStarPCbuilt its index sets in Python, taking each seed's star throughgetTransitiveClosureand gathering the dofs point by point. Every entity of the stratum got a patch, whether or not it lay anywhere near a refinement, so a smoother on an adaptively refined level relaxed the whole mesh to fix a corner of it.ASMStarPCnow has a single IS-construction mechanism, infiredrake/cython/patchimpl.pyx, used for every case rather than only the colored one:create_star_points()takes the star of each owned seed andcreate_patch_ises()gathers the dofs those points carry. The seeds may be restricted to the region that was genuinely refined, and the coloring then colors only those, so the patch count follows the size of the refined region instead of the size of the mesh.Changes
create_star_points()andcreate_patch_ises()replacebuild_star_indices, which is gone. They reproduce the dof ordering the Python path produced, so a reordering requested withpc_star_mat_ordering_typestill applies per star.order_points()no longer reverses its input, since both remaining callers pass the star already reversed. A reordering applies to each star on its own, so it survives a coloring that grouped several stars into one patch.adapt.mark_refined_entities()labels the cells whose parent was split into more than one child, plus their closure — exactly the entities whose star meets the refined region. It returnsNonefor a mesh with no adaptive parent, which both preconditioners read as relaxing everywhere.get_colors()inserts itsMatColoringtype withpetsctools.inserted_optionsunder thedm_plex_coloring_prefix, matching how_adapt_marked_cellsdrives the transform type. The previous code set a baremat_coloring_typeglobally and never removed it.demos/patch/adaptive_patches.py.rst, solves a cubic Poisson problem over a hierarchy that starts with two uniform refinements of a single square and grows three adaptive levels on top, and draws the patch seeds of each level colored by their color.Options
pc_star_adaptiveASMStarPCto the entities whose star meets the refined region, building the label frommesh.adaptive_cell_maps.patch_pc_patch_adaptivePatchPC, throughPCPatchSetConstructLabel().pc_star_construct_label/_valueASMStarPCto anyDMLabelalready on the mesh's DMPlex, for a region the adaptive machinery did not produce.pc_star_use_coloringBoth
adaptiveoptions are correct on a uniformly refined level as well as an adaptive one: every cell of a uniform level was split, so the restriction is a no-op there and the smoother relaxes everywhere. Nothing in the solver parameters has to know which kind of level it is looking at. Adding-dm_plex_coloring_localcolors each process's own vertices without communicating, and usually with fewer colors.Patches across a hierarchy
The first two levels below are uniform refinements, so every vertex carries a patch. From the third level the patches sit only where the mesh was refined. Each disc is drawn half the way to the nearest vertex, so discs shrink with the mesh and never touch; within a level no two discs of the same color are adjacent, which is what makes the grouped patch operator block diagonal.
Tests
Added to
tests/firedrake/regression/test_star_pc.py:ASMStarPCandPatchPCtake equal iteration counts on a corner-refined mesh, coloring does not change that count, restriction leaves strictly fewer patches, and the adaptive option on a mesh with no adaptive parent matches the unrestricted one. They run at 1 and 3 processes. The demo is registered intests/firedrake/demos/test_demos_run.py.AI was used to draft this change (Claude Code, Opus 5).