Skip to content

TransferManager: support a redistributed AdaptiveMeshHierarchy#5215

Draft
pbrubeck wants to merge 15 commits into
pbrubeck/snes-adaptfrom
pbrubeck/mg-redist
Draft

TransferManager: support a redistributed AdaptiveMeshHierarchy#5215
pbrubeck wants to merge 15 commits into
pbrubeck/snes-adaptfrom
pbrubeck/mg-redist

Conversation

@pbrubeck

@pbrubeck pbrubeck commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Description

AI-assisted

Deprecate hierarchy-agnostic AdaptiveTransferManager in favour of TransferManager, now with adaptivity support.

@pbrubeck pbrubeck changed the title Native support for redistributed AdaptiveMeshHierarchy TransferManager: support a redistributed AdaptiveMeshHierarchy Jul 3, 2026
@pbrubeck pbrubeck force-pushed the pbrubeck/mg-redist branch 2 times, most recently from 404f925 to 381f544 Compare July 3, 2026 19:51
@pbrubeck pbrubeck force-pushed the pbrubeck/mg-redist branch from 381f544 to 885a7eb Compare July 3, 2026 19:56
Comment thread firedrake/mg/interface.py Outdated
return coarse_dual


def _simplex_cell_volumes(mesh):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Falling back to numpy should be stressed as the worst antipattern in AGENTS.md

Comment on lines +65 to +69
parent_cell_numbers = getattr(transfer_mesh, "_adaptive_parent_cell_numbers", None)
if parent_cell_numbers is None:
parent_cell_numbers = getattr(mesh, "_adaptive_parent_cell_numbers", None)
if parent_cell_numbers is None:
parent_cell_numbers = adaptive_parent_cell_numbers(self.meshes[-1], mesh)

@pbrubeck pbrubeck Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just pick a convention on where this attribute should ultimately be set.

Comment thread firedrake/netgen.py
Comment on lines +84 to +94
def adaptive_netgen_cells(mesh):
tdim = mesh.topological_dimension
if tdim == 2:
return mesh.netgen_mesh.Elements2D()
elif tdim == 3:
return mesh.netgen_mesh.Elements3D()
raise NotImplementedError("Adaptive refinement is only implemented in dimension 2 and 3.")


def adaptive_netgen_cell_count(mesh):
return getattr(mesh, "_adaptive_netgen_num_cells", len(adaptive_netgen_cells(mesh)))

@pbrubeck pbrubeck Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to get the netgen specific cell count? We should instead branch on wheter the netgen geometry is the global or local one. On refined meshes (either uniformly or adaptively refined ones) it is possible that the netgen mesh gets distributed. Which reminds me, we should add some tests that compose uniform and adaptive refiments, i.e.

mesh = Mesh(netgen_mesh)
mh = MeshHierarchy(mesh, N)
amh = AdaptiveMeshHierarchy(mh[-1])

Maybe even we should also add support for AdaptiveMeshHierarchy(mh) to populate the adaptive hierarcy with N levels of uniform refiment. In the tests N=1 or 2.

Comment thread firedrake/netgen.py
return result


def _adaptive_parent_owner_partition(coarse_mesh, parent_cell_numbers):

@pbrubeck pbrubeck Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is effectively un-redistributing the refined mesh. We should be adaptively refining into parent-owned and then calling dm.distribute. The problem is that Mesh(refined_netgen_mesh) will redistribute it by default.

Comment thread firedrake/netgen.py
num_cells = transfer_mesh.cell_set.size
min_cells = transfer_mesh.comm.allreduce(num_cells, op=MPI.MIN)
max_cells = transfer_mesh.comm.allreduce(num_cells, op=MPI.MAX)
needs_redist = max_cells > 1.15 * min_cells

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should a balancing threshold kwarg. What could be a good default value, maybe 0.5? The tests should pick something much lower to trigger redistribution, like 0.15.

Suggested change
needs_redist = max_cells > 1.15 * min_cells
needs_redist = max_cells > (1+balancing) * min_cells

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A better criterion is max_cells > (1+balancing) * avg_cells

Comment thread firedrake/netgen.py
Comment on lines +239 to +242
redist_mesh_parameters["partition"] = False
redist_mesh_parameters["overlap_type"] = (
DistributedMeshOverlapType.NONE, 0
)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use DISTRIBUTION_PARAMETERS_NOOP

@pbrubeck pbrubeck Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a good reason to carry over the parent's distribution parameters? It seems that we override everything about distribution. We should just pass DISTRIBUTION_PARAMETERS_NOOP to the Mesh constructor.

Comment thread firedrake/mg/utils.py
return point_sf_orig, point_sf


def make_unoverlapped_dm(mesh):

@pbrubeck pbrubeck Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a docsting with the comment that was removed, maybe make it more descriptive/clear.

Suggested change
def make_unoverlapped_dm(mesh):
def make_unoverlapped_dm(mesh):
"""Effectively "invert" addOverlap().
The resulting plex is to have the identical data structure as the one before addOverlap().
This is algorithmically guaranteed."""

Comment thread firedrake/mg/utils.py
return dm


def fixup_embedded_coords(dm, mesh):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this back to MeshHierachy, this function is only called once.

Comment thread firedrake/mg/utils.py

def dm_has_empty_rank(dm):
cstart, cend = dm.getHeightStratum(0)
comm = dm.comm.tompi4py() if hasattr(dm.comm, "tompi4py") else dm.comm

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this logic necessary? DM should only have one type of comm.

Comment thread firedrake/mg/utils.py
from firedrake.halo import MPI


class RedistMesh:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move the new code to the bottom of this file

Comment thread firedrake/mg/mesh.py
transfer_mesh = fine_mesh
flgmaps = (
fine_lgmap_without_overlap,
impl.create_lgmap(fine_mesh.topology_dm),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means that this could be moved out of the if-else statement

Suggested change
impl.create_lgmap(fine_mesh.topology_dm),
impl.create_lgmap(transfer_mesh.topology_dm),

Comment thread firedrake/netgen.py
return plex_data


def adaptive_netgen_cells(mesh):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the new code to the bottom of the file.

Comment thread firedrake/mg/utils.py
MPI.REPLACE)


def set_partitioner(dm, parameters):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code can be reused in mesh.py, and that's where it should live.

Comment thread firedrake/mg/utils.py
}[partitioner_type])


def distribute_overlap(dm, parameters):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code can be reused in mesh.py, and that's where it should live.

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