diff --git a/firedrake/assemble.py b/firedrake/assemble.py index 10c1ba2b43..e5adb33a62 100644 --- a/firedrake/assemble.py +++ b/firedrake/assemble.py @@ -981,10 +981,6 @@ def wrapper(self, *args, **kwargs): def __init__(self, form, bcs=None, form_compiler_parameters=None): super().__init__(form, bcs=bcs, form_compiler_parameters=form_compiler_parameters) - # Ensure mesh is 'initialised' as we could have got here without building a - # function space (e.g. if integrating a constant). - for mesh in form.ufl_domains(): - mesh.init() if any(c.dat.dtype != ScalarType for c in form.coefficients()): raise ValueError("Cannot assemble a form containing coefficients where the " "dtype is not the PETSc scalar type.") diff --git a/firedrake/checkpointing.py b/firedrake/checkpointing.py index a9fba2dd00..6a63b1aaf1 100644 --- a/firedrake/checkpointing.py +++ b/firedrake/checkpointing.py @@ -566,7 +566,6 @@ def save_mesh(self, mesh, distribution_name=None, permutation_name=None): :kwarg distribution_name: the name under which distribution is saved; if `None`, auto-generated name will be used. :kwarg permutation_name: the name under which permutation is saved; if `None`, auto-generated name will be used. """ - mesh.init() # Handle extruded mesh tmesh = mesh.topology if mesh.extruded: @@ -669,7 +668,6 @@ def save_mesh(self, mesh, distribution_name=None, permutation_name=None): @PETSc.Log.EventDecorator("SaveMeshTopology") def _save_mesh_topology(self, tmesh): # -- Save DMPlex -- - tmesh.init() topology_dm = tmesh.topology_dm tmesh_name = topology_dm.getName() distribution_name = tmesh._distribution_name @@ -1048,7 +1046,6 @@ def load_mesh(self, name=DEFAULT_MESH_NAME, reorder=None, distribution_parameter # -- Load mesh topology -- base_tmesh_name = self.get_attr(path, PREFIX_EXTRUDED + "_base_mesh") base_tmesh = self._load_mesh_topology(base_tmesh_name, reorder, distribution_parameters) - base_tmesh.init() periodic = self.get_attr(path, PREFIX_EXTRUDED + "_periodic") if self.has_attr(path, PREFIX_EXTRUDED + "_periodic") else False variable_layers = self.get_attr(path, PREFIX_EXTRUDED + "_variable_layers") if variable_layers: @@ -1106,9 +1103,6 @@ def load_mesh(self, name=DEFAULT_MESH_NAME, reorder=None, distribution_parameter # tmesh.topology_dm has already been redistributed. path = self._path_to_mesh(tmesh_name, name) # Load firedrake coordinates directly. - # When implementing checkpointing for MeshHierarchy in the future, - # we will need to postpone calling tmesh.init(). - tmesh.init() coord_element = self._load_ufl_element(path, PREFIX + "_coordinate_element") coord_name = self.get_attr(path, PREFIX + "_coordinates") coordinates = self._load_function_topology(tmesh, coord_element, coord_name) @@ -1194,7 +1188,13 @@ def _load_mesh_topology(self, tmesh_name, reorder, distribution_parameters): plex.distributionSetName(distribution_name) sfXB = plex.topologyLoad(self.viewer) plex.distributionSetName(None) + plex.labelsLoad(self.viewer, sfXB) self.viewer.popFormat() + # These labels are distribution dependent. + # We should be able to save/load labels selectively. + plex.removeLabel("pyop2_core") + plex.removeLabel("pyop2_owned") + plex.removeLabel("pyop2_ghost") if load_distribution_permutation: chart_size = np.empty(1, dtype=utils.IntType) chart_sizes_iset = PETSc.IS().createGeneral(chart_size, comm=self._comm) @@ -1220,21 +1220,10 @@ def _load_mesh_topology(self, tmesh_name, reorder, distribution_parameters): distribution_parameters=distribution_parameters, sfXB=sfXB, perm_is=perm_is, distribution_name=distribution_name, permutation_name=permutation_name, comm=self.comm) - self.viewer.pushFormat(format=format) - # tmesh.topology_dm has already been redistributed. - sfXCtemp = tmesh.sfXB.compose(tmesh.sfBC) if tmesh.sfBC is not None else tmesh.sfXB - plex.labelsLoad(self.viewer, sfXCtemp) - self.viewer.popFormat() - # These labels are distribution dependent. - # We should be able to save/load labels selectively. - plex.removeLabel("pyop2_core") - plex.removeLabel("pyop2_owned") - plex.removeLabel("pyop2_ghost") return tmesh @PETSc.Log.EventDecorator("LoadFunctionSpace") def _load_function_space(self, mesh, name): - mesh.init() mesh_key = self._generate_mesh_key_from_names(mesh.name, mesh.topology._distribution_name, mesh.topology._permutation_name) @@ -1271,7 +1260,6 @@ def _load_function_space(self, mesh, name): @PETSc.Log.EventDecorator("LoadFunctionSpaceTopology") def _load_function_space_topology(self, tmesh, element): - tmesh.init() if element.family() == "Real": return impl.RealFunctionSpace(tmesh, element, "unused_name") tmesh_key = self._generate_mesh_key_from_names(tmesh.name, diff --git a/firedrake/cython/dmcommon.pyx b/firedrake/cython/dmcommon.pyx index c3ee78600b..db194b04b7 100644 --- a/firedrake/cython/dmcommon.pyx +++ b/firedrake/cython/dmcommon.pyx @@ -3818,7 +3818,8 @@ def create_halo_exchange_sf(PETSc.DM dm): def submesh_create(PETSc.DM dm, PetscInt subdim, label_name, - PetscInt label_value): + PetscInt label_value, + PetscBool ignore_label_halo): """Create submesh. Parameters @@ -3831,6 +3832,8 @@ def submesh_create(PETSc.DM dm, Name of the label label_value : int Value in the label + ignore_label_halo : bool + If labeled points in the halo are ignored. """ cdef: @@ -3860,7 +3863,7 @@ def submesh_create(PETSc.DM dm, CHKERR(ISRestoreIndices(stratum_is, &stratum_indices)) CHKERR(ISDestroy(&stratum_is)) # Make submesh using temp_label. - CHKERR(DMPlexFilter(dm.dm, temp_label.dmlabel, label_value, PETSC_FALSE, PETSC_TRUE, &ownership_transfer_sf.sf, &subdm.dm)) + CHKERR(DMPlexFilter(dm.dm, temp_label.dmlabel, label_value, ignore_label_halo, PETSC_TRUE, &ownership_transfer_sf.sf, &subdm.dm)) # Destroy temp_label. dm.removeLabel(temp_label_name) subdm.removeLabel(temp_label_name) @@ -4112,3 +4115,36 @@ def submesh_create_cell_closure( CHKERR(PetscFree(subpoint_indices_inv)) CHKERR(ISRestoreIndices(subpoint_is.iset, &subpoint_indices)) return subcell_closure + + +@cython.boundscheck(False) +@cython.wraparound(False) +def get_dm_cell_types(PETSc.DM dm): + """Return all cell types in the mesh. + + Parameters + ---------- + dm : PETSc.DM + The parent dm. + + Returns + ------- + tuple + Tuple of all cell types in the mesh. + + """ + cdef: + PetscInt cStart, cEnd, c + np.ndarray found, found_all + PetscDMPolytopeType celltype + + cStart, cEnd = dm.getHeightStratum(0) + found = np.zeros((DM_NUM_POLYTOPES, ), dtype=IntType) + found_all = np.zeros((DM_NUM_POLYTOPES, ), dtype=IntType) + for c in range(cStart, cEnd): + CHKERR(DMPlexGetCellType(dm.dm, c, &celltype)) + found[celltype] = 1 + dm.comm.tompi4py().Allreduce(found, found_all, op=MPI.MAX) + return tuple( + polytope_type_enum for polytope_type_enum, found in enumerate(found_all) if found + ) diff --git a/firedrake/cython/petschdr.pxi b/firedrake/cython/petschdr.pxi index 750a3c3714..a94de11e56 100644 --- a/firedrake/cython/petschdr.pxi +++ b/firedrake/cython/petschdr.pxi @@ -29,6 +29,27 @@ cdef extern from "petscsys.h" nogil: int PetscFree2(void*,void*) int PetscSortIntWithArray(PetscInt,PetscInt[],PetscInt[]) +cdef extern from "petscdmtypes.h" nogil: + ctypedef enum PetscDMPolytopeType "DMPolytopeType": + DM_POLYTOPE_POINT + DM_POLYTOPE_SEGMENT + DM_POLYTOPE_POINT_PRISM_TENSOR + DM_POLYTOPE_TRIANGLE + DM_POLYTOPE_QUADRILATERAL + DM_POLYTOPE_SEG_PRISM_TENSOR + DM_POLYTOPE_TETRAHEDRON + DM_POLYTOPE_HEXAHEDRON + DM_POLYTOPE_TRI_PRISM + DM_POLYTOPE_TRI_PRISM_TENSOR + DM_POLYTOPE_QUAD_PRISM_TENSOR + DM_POLYTOPE_PYRAMID + DM_POLYTOPE_FV_GHOST + DM_POLYTOPE_INTERIOR_GHOST + DM_POLYTOPE_UNKNOWN + DM_POLYTOPE_UNKNOWN_CELL + DM_POLYTOPE_UNKNOWN_FACE + DM_NUM_POLYTOPES + cdef extern from "petscdmplex.h" nogil: int DMPlexGetHeightStratum(PETSc.PetscDM,PetscInt,PetscInt*,PetscInt*) int DMPlexGetDepthStratum(PETSc.PetscDM,PetscInt,PetscInt*,PetscInt*) @@ -56,6 +77,9 @@ cdef extern from "petscdmplex.h" nogil: int DMPlexGetSubpointMap(PETSc.PetscDM,PETSc.PetscDMLabel*) int DMPlexSetSubpointMap(PETSc.PetscDM,PETSc.PetscDMLabel) + int DMPlexSetCellType(PETSc.PetscDM,PetscInt,PetscDMPolytopeType) + int DMPlexGetCellType(PETSc.PetscDM,PetscInt,PetscDMPolytopeType*) + cdef extern from "petscdmlabel.h" nogil: struct _n_DMLabel ctypedef _n_DMLabel* DMLabel "DMLabel" diff --git a/firedrake/functionspaceimpl.py b/firedrake/functionspaceimpl.py index 00f517c088..988a7b0585 100644 --- a/firedrake/functionspaceimpl.py +++ b/firedrake/functionspaceimpl.py @@ -359,7 +359,6 @@ def collapse(self): @classmethod def make_function_space(cls, mesh, element, name=None): r"""Factory method for :class:`.WithGeometryBase`.""" - mesh.init() topology = mesh.topology # Create a new abstract (Mixed/Real)FunctionSpace, these are neither primal nor dual. if type(element) is finat.ufl.MixedElement: diff --git a/firedrake/mesh.py b/firedrake/mesh.py index de786079b5..07956b7e39 100644 --- a/firedrake/mesh.py +++ b/firedrake/mesh.py @@ -528,7 +528,7 @@ class AbstractMeshTopology(object, metaclass=abc.ABCMeta): """A representation of an abstract mesh topology without a concrete PETSc DM implementation""" - def __init__(self, topology_dm, name, reorder, sfXB, perm_is, distribution_name, permutation_name, comm): + def __init__(self, topology_dm, name, reorder, sfXB, perm_is, distribution_name, permutation_name, comm, submesh_parent=None): """Initialise a mesh topology. Parameters @@ -555,6 +555,8 @@ def __init__(self, topology_dm, name, reorder, sfXB, perm_is, distribution_name, Name of the entity permutation (reordering); if `None`, automatically generated. comm : mpi4py.MPI.Comm Communicator. + submesh_parent: AbstractMeshTopology + Submesh parent. """ utils._init() @@ -566,62 +568,56 @@ def __init__(self, topology_dm, name, reorder, sfXB, perm_is, distribution_name, r"The PETSc SF that pushes the input (naive) plex to current (good) plex." self.sfXB = sfXB r"The PETSc SF that pushes the global point number slab [0, NX) to input (naive) plex." - + self.submesh_parent = submesh_parent # User comm self.user_comm = comm # Internal comm self._comm = internal_comm(self.user_comm, self) - dmcommon.label_facets(self.topology_dm) self._distribute() self._grown_halos = False - - def callback(self): - """Finish initialisation.""" - del self._callback - if self.comm.size > 1: - self._add_overlap() - if self.sfXB is not None: - self.sfXC = sfXB.compose(self.sfBC) if self.sfBC else self.sfXB - dmcommon.label_facets(self.topology_dm) - dmcommon.complete_facet_labels(self.topology_dm) - # TODO: Allow users to set distribution name if they want to save - # conceptually the same mesh but with different distributions, - # e.g., those generated by different partitioners. - # This currently does not make sense since those mesh instances - # of different distributions in general have different global - # point numbers (so they must be saved under different mesh names - # even though they are conceptually the same). - # The name set here almost uniquely identifies a distribution, but - # there is no gurantee that it really does or it continues to do so - # there are lots of parameters that can change distributions. - # Thus, when using CheckpointFile, it is recommended that the user set - # distribution_name explicitly. - # Mark OP2 entities and derive the resulting Plex renumbering - with PETSc.Log.Event("Mesh: numbering"): - self._mark_entity_classes() - self._entity_classes = dmcommon.get_entity_classes(self.topology_dm).astype(int) - if perm_is: - self._dm_renumbering = perm_is - else: - self._dm_renumbering = self._renumber_entities(reorder) - self._did_reordering = bool(reorder) - # Derive a cell numbering from the Plex renumbering - tdim = dmcommon.get_topological_dimension(self.topology_dm) - entity_dofs = np.zeros(tdim+1, dtype=IntType) - entity_dofs[-1] = 1 - self._cell_numbering, _ = self.create_section(entity_dofs) - if tdim == 0: - self._vertex_numbering = self._cell_numbering - else: - entity_dofs[:] = 0 - entity_dofs[0] = 1 - self._vertex_numbering, _ = self.create_section(entity_dofs) - entity_dofs[:] = 0 - entity_dofs[-2] = 1 - facet_numbering, _ = self.create_section(entity_dofs) - self._facet_ordering = dmcommon.get_facet_ordering(self.topology_dm, facet_numbering) - self._callback = callback + if self.comm.size > 1: + self._add_overlap() + if self.sfXB is not None: + self.sfXC = sfXB.compose(self.sfBC) if self.sfBC else self.sfXB + dmcommon.label_facets(self.topology_dm) + dmcommon.complete_facet_labels(self.topology_dm) + # TODO: Allow users to set distribution name if they want to save + # conceptually the same mesh but with different distributions, + # e.g., those generated by different partitioners. + # This currently does not make sense since those mesh instances + # of different distributions in general have different global + # point numbers (so they must be saved under different mesh names + # even though they are conceptually the same). + # The name set here almost uniquely identifies a distribution, but + # there is no gurantee that it really does or it continues to do so + # there are lots of parameters that can change distributions. + # Thus, when using CheckpointFile, it is recommended that the user set + # distribution_name explicitly. + # Mark OP2 entities and derive the resulting Plex renumbering + with PETSc.Log.Event("Mesh: numbering"): + self._mark_entity_classes() + self._entity_classes = dmcommon.get_entity_classes(self.topology_dm).astype(int) + if perm_is: + self._dm_renumbering = perm_is + else: + self._dm_renumbering = self._renumber_entities(reorder) + self._did_reordering = bool(reorder) + # Derive a cell numbering from the Plex renumbering + tdim = dmcommon.get_topological_dimension(self.topology_dm) + entity_dofs = np.zeros(tdim+1, dtype=IntType) + entity_dofs[-1] = 1 + self._cell_numbering, _ = self.create_section(entity_dofs) + if tdim == 0: + self._vertex_numbering = self._cell_numbering + else: + entity_dofs[:] = 0 + entity_dofs[0] = 1 + self._vertex_numbering, _ = self.create_section(entity_dofs) + entity_dofs[:] = 0 + entity_dofs[-2] = 1 + facet_numbering, _ = self.create_section(entity_dofs) + self._facet_ordering = dmcommon.get_facet_ordering(self.topology_dm, facet_numbering) self.name = name # Set/Generate names to be used when checkpointing. self._distribution_name = distribution_name or _generate_default_mesh_topology_distribution_name(self.topology_dm.comm.size, self._distribution_parameters) @@ -635,8 +631,6 @@ def callback(self): # To set, do e.g. # target_mesh._parallel_compatible = {weakref.ref(source_mesh)} self._parallel_compatible = None - # submesh - self.submesh_parent = None layers = None """No layers on unstructured mesh""" @@ -672,12 +666,6 @@ def mpi_comm(self): """The MPI communicator this mesh is built on (an mpi4py object).""" return self.comm - @PETSc.Log.EventDecorator("CreateMesh") - def init(self): - """Finish the initialisation of the mesh.""" - if hasattr(self, '_callback'): - self._callback(self) - @property def topology(self): """The underlying mesh topology object.""" @@ -723,6 +711,12 @@ def ufl_mesh(self): """ return self._ufl_mesh + @property + @abc.abstractmethod + def dm_cell_types(self): + """All DM.PolytopeTypes of cells in the mesh.""" + pass + @property @abc.abstractmethod def cell_closure(self): @@ -1093,7 +1087,19 @@ class MeshTopology(AbstractMeshTopology): """A representation of mesh topology implemented on a PETSc DMPlex.""" @PETSc.Log.EventDecorator("CreateMesh") - def __init__(self, plex, name, reorder, distribution_parameters, sfXB=None, perm_is=None, distribution_name=None, permutation_name=None, comm=COMM_WORLD): + def __init__( + self, + plex, + name, + reorder, + distribution_parameters, + sfXB=None, + perm_is=None, + distribution_name=None, + permutation_name=None, + submesh_parent=None, + comm=COMM_WORLD, + ): """Initialise a mesh topology. Parameters @@ -1120,10 +1126,14 @@ def __init__(self, plex, name, reorder, distribution_parameters, sfXB=None, perm Name of the parallel distribution; if `None`, automatically generated. permutation_name : str Name of the entity permutation (reordering); if `None`, automatically generated. + submesh_parent: MeshTopology + Submesh parent. comm : mpi4py.MPI.Comm Communicator. """ + if distribution_parameters is None: + distribution_parameters = {} self._distribution_parameters = {} distribute = distribution_parameters.get("partition") if distribute is None: @@ -1137,7 +1147,7 @@ def __init__(self, plex, name, reorder, distribution_parameters, sfXB=None, perm # Disable auto distribution and reordering before setFromOptions is called. plex.distributeSetDefault(False) plex.reorderSetDefault(PETSc.DMPlex.ReorderDefaultFlag.FALSE) - super().__init__(plex, name, reorder, sfXB, perm_is, distribution_name, permutation_name, comm) + super().__init__(plex, name, reorder, sfXB, perm_is, distribution_name, permutation_name, comm, submesh_parent=submesh_parent) def _distribute(self): # Distribute/redistribute the dm to all ranks @@ -1228,6 +1238,11 @@ def _renumber_entities(self, reorder): reordering = None return dmcommon.plex_renumbering(self.topology_dm, self._entity_classes, reordering) + @property + def dm_cell_types(self): + """All DM.PolytopeTypes of cells in the mesh.""" + return dmcommon.get_dm_cell_types(self.topology_dm) + @utils.cached_property def cell_closure(self): """2D array of ordered cell closures @@ -1690,7 +1705,6 @@ def __init__(self, mesh, layers, periodic=False, name=None): if layers.shape and periodic: raise ValueError("Must provide constant layer for periodic extrusion") - mesh.init() self._base_mesh = mesh self.user_comm = mesh.comm self._comm = internal_comm(mesh._comm, self) @@ -1739,6 +1753,11 @@ def _ufl_mesh(self): cell = self._ufl_cell return ufl.Mesh(finat.ufl.VectorElement("Lagrange", cell, 1, dim=cell.topological_dimension())) + @property + def dm_cell_types(self): + """All DM.PolytopeTypes of cells in the mesh.""" + raise NotImplementedError("'dm_cell_types' is not implemented for ExtrudedMeshTopology") + @utils.cached_property def cell_closure(self): """2D array of ordered cell closures @@ -1929,8 +1948,8 @@ def __init__(self, swarm, parentmesh, name, reorder, input_ordering_swarm=None, "partitioner_type": None, "overlap_type": (DistributedMeshOverlapType.NONE, 0)} self.input_ordering_swarm = input_ordering_swarm - super().__init__(swarm, name, reorder, None, perm_is, distribution_name, permutation_name, parentmesh.comm) self._parent_mesh = parentmesh + super().__init__(swarm, name, reorder, None, perm_is, distribution_name, permutation_name, parentmesh.comm) def _distribute(self): pass @@ -1977,6 +1996,11 @@ def _renumber_entities(self, reorder): else: return dmcommon.plex_renumbering(self.topology_dm, self._entity_classes, None) + @property + def dm_cell_types(self): + """All DM.PolytopeTypes of cells in the mesh.""" + return (PETSc.DM.PolytopeType.POINT,) + @utils.cached_property # TODO: Recalculate if mesh moves def cell_closure(self): """2D array of ordered cell closures @@ -2272,14 +2296,6 @@ def _ufl_signature_data_(self, *args, **kwargs): return (type(self), self.extruded, self.variable_layers, super()._ufl_signature_data_(*args, **kwargs)) - def init(self): - """Finish the initialisation of the mesh. Most of the time - this is carried out automatically, however, in some cases (for - example accessing a property of the mesh directly after - constructing it) you need to call this manually.""" - if hasattr(self, '_callback'): - self._callback(self) - def _init_topology(self, topology): """Initialise the topology. @@ -2294,20 +2310,13 @@ def _init_topology(self, topology): import firedrake.function as function self._topology = topology - - def callback(self): - """Finish initialisation.""" - del self._callback - # Finish the initialisation of mesh topology - self.topology.init() - coordinates_fs = functionspace.FunctionSpace(self.topology, self.ufl_coordinate_element()) - coordinates_data = dmcommon.reordered_coords(topology.topology_dm, coordinates_fs.dm.getDefaultSection(), - (self.num_vertices(), self.geometric_dimension())) - coordinates = function.CoordinatelessFunction(coordinates_fs, - val=coordinates_data, - name=_generate_default_mesh_coordinates_name(self.name)) - self.__init__(coordinates) - self._callback = callback + coordinates_fs = functionspace.FunctionSpace(self.topology, self.ufl_coordinate_element()) + coordinates_data = dmcommon.reordered_coords(topology.topology_dm, coordinates_fs.dm.getDefaultSection(), + (self.num_vertices(), self.geometric_dimension())) + coordinates = function.CoordinatelessFunction(coordinates_fs, + val=coordinates_data, + name=_generate_default_mesh_coordinates_name(self.name)) + self.__init__(coordinates) @property def topology(self): @@ -2366,7 +2375,6 @@ def _coordinates_function(self): if hasattr(self.ufl_cargo(), "_coordinates_function"): return self.ufl_cargo()._coordinates_function else: - self.init() coordinates_fs = self._coordinates.function_space() V = functionspaceimpl.WithGeometry.create(coordinates_fs, self) f = function.Function(V, val=self._coordinates) @@ -2732,7 +2740,6 @@ def input_ordering(self): _input_ordering = make_vom_from_vom_topology(self.topology.input_ordering, self.name + "_input_ordering") if _input_ordering: _input_ordering._parent_mesh = self - _input_ordering.init() return _input_ordering def cell_orientations(self): @@ -3091,16 +3098,19 @@ def Mesh(meshfile, **kwargs): % (meshfile, ext[1:])) plex.setName(_generate_default_mesh_topology_name(name)) # Create mesh topology + submesh_parent = kwargs.get("submesh_parent", None) topology = MeshTopology(plex, name=plex.getName(), reorder=reorder, distribution_parameters=distribution_parameters, distribution_name=kwargs.get("distribution_name"), permutation_name=kwargs.get("permutation_name"), + submesh_parent=submesh_parent.topology if submesh_parent else None, comm=user_comm) if netgen and isinstance(meshfile, netgen.libngpy._meshing.Mesh): netgen_firedrake_mesh.createFromTopology(topology, name=name, comm=user_comm) mesh = netgen_firedrake_mesh.firedrakeMesh else: mesh = make_mesh_from_mesh_topology(topology, name) + mesh.submesh_parent = submesh_parent mesh._tolerance = tolerance return mesh @@ -3175,7 +3185,6 @@ def ExtrudedMesh(mesh, layers, layer_height=None, extrusion_type='uniform', peri if name is not None and name == mesh.name: raise ValueError("Extruded mesh and base mesh can not have the same name") name = name if name is not None else mesh.name + "_extruded" - mesh.init() layers = np.asarray(layers, dtype=IntType) if layers.shape: if periodic: @@ -3344,7 +3353,6 @@ def VertexOnlyMesh(mesh, vertexcoords, reorder=None, missing_points_behaviour='e tolerance = mesh.tolerance else: mesh.tolerance = tolerance - mesh.init() vertexcoords = np.asarray(vertexcoords, dtype=RealType) if reorder is None: reorder = parameters["reorder_meshes"] @@ -3394,7 +3402,6 @@ def VertexOnlyMesh(mesh, vertexcoords, reorder=None, missing_points_behaviour='e ) vmesh_out = make_vom_from_vom_topology(topology, name, tolerance) vmesh_out._parent_mesh = mesh - vmesh_out.init() return vmesh_out @@ -4665,7 +4672,6 @@ def Submesh(mesh, subdim, subdomain_id, label_name=None, name=None): raise NotImplementedError("Can not create a submesh of an ``ExtrudedMesh``") elif isinstance(mesh.topology, VertexOnlyMeshTopology): raise NotImplementedError("Can not create a submesh of a ``VertexOnlyMesh``") - mesh.topology.init() plex = mesh.topology_dm dim = plex.getDimension() if subdim not in [dim, dim - 1]: @@ -4676,13 +4682,17 @@ def Submesh(mesh, subdim, subdomain_id, label_name=None, name=None): elif subdim == dim - 1: label_name = dmcommon.FACE_SETS_LABEL name = name or _generate_default_submesh_name(mesh.name) - subplex = dmcommon.submesh_create(plex, subdim, label_name, subdomain_id) + subplex = dmcommon.submesh_create(plex, subdim, label_name, subdomain_id, False) subplex.setName(_generate_default_mesh_topology_name(name)) if subplex.getDimension() != subdim: raise RuntimeError(f"Found subplex dim ({subplex.getDimension()}) != expected ({subdim})") - submesh = Mesh(subplex, name=name, distribution_parameters={"partition": False, - "overlap_type": (DistributedMeshOverlapType.NONE, 0)}) - submesh.topology.submesh_parent = mesh.topology - submesh.init() - submesh.submesh_parent = mesh + submesh = Mesh( + subplex, + submesh_parent=mesh, + name=name, + distribution_parameters={ + "partition": False, + "overlap_type": (DistributedMeshOverlapType.NONE, 0), + }, + ) return submesh diff --git a/firedrake/mg/mesh.py b/firedrake/mg/mesh.py index 446e042b35..5924d76c7a 100644 --- a/firedrake/mg/mesh.py +++ b/firedrake/mg/mesh.py @@ -5,6 +5,7 @@ from pyop2.datatypes import IntType import firedrake +import firedrake.cython.dmcommon as dmcommon from firedrake.utils import cached_property from firedrake.cython import mgimpl as impl from .utils import set_level @@ -130,37 +131,27 @@ def MeshHierarchy(mesh, refinement_levels, else: raise RuntimeError("Cannot create a NetgenHierarchy from a mesh that has not been generated by\ Netgen.") - - cdm = mesh.topology_dm + # Effectively "invert" addOverlap(). + # -- The resulting plex is to have the identical data structure as the one before addOverlap(). + # This is algorithmically guaranteed. + dm_cell_type, = mesh.dm_cell_types + tdim = mesh.topology_dm.getDimension() + cdm = dmcommon.submesh_create(mesh.topology_dm, tdim, "celltype", dm_cell_type, True) + cdm.removeLabel("pyop2_core") + cdm.removeLabel("pyop2_owned") + cdm.removeLabel("pyop2_ghost") cdm.setRefinementUniform(True) - dms = [] - if mesh.comm.size > 1 and mesh._grown_halos: - raise RuntimeError("Cannot refine parallel overlapped meshes " - "(make sure the MeshHierarchy is built immediately after the Mesh)") - parameters = {} - if distribution_parameters is not None: - parameters.update(distribution_parameters) - else: - parameters.update(mesh._distribution_parameters) - - parameters["partition"] = False - distribution_parameters = parameters - + dms = [cdm] if callbacks is not None: before, after = callbacks else: before = after = lambda dm, i: None - for i in range(refinement_levels*refinements_per_level): if i % refinements_per_level == 0: before(cdm, i) rdm = cdm.refine() if i % refinements_per_level == 0: after(rdm, i) - rdm.removeLabel("pyop2_core") - rdm.removeLabel("pyop2_owned") - rdm.removeLabel("pyop2_ghost") - dms.append(rdm) cdm = rdm # Fix up coords if refining embedded circle or sphere @@ -172,20 +163,30 @@ def MeshHierarchy(mesh, refinement_levels, coords = cdm.getCoordinatesLocal().array.reshape(-1, mesh.geometric_dimension()) scale = mesh._radius / np.linalg.norm(coords, axis=1).reshape(-1, 1) coords *= scale - - meshes = [mesh] + [mesh_builder(dm, dim=mesh.geometric_dimension(), - distribution_parameters=distribution_parameters, - reorder=reorder, comm=mesh.comm) - for dm in dms] - - lgmaps = [] + lgmaps_without_overlap = [impl.create_lgmap(dm) for dm in dms] + parameters = {} + if distribution_parameters is not None: + parameters.update(distribution_parameters) + else: + parameters.update(mesh._distribution_parameters) + parameters["partition"] = False + meshes = [mesh] + [ + mesh_builder( + dm, + dim=mesh.geometric_dimension(), + distribution_parameters=parameters, + reorder=reorder, + comm=mesh.comm, + ) + for dm in dms[1:] + ] + lgmaps_with_overlap = [] for i, m in enumerate(meshes): - no = impl.create_lgmap(m.topology_dm) - m.init() - o = impl.create_lgmap(m.topology_dm) + lgmaps_with_overlap.append(impl.create_lgmap(m.topology_dm)) m.topology_dm.setRefineLevel(i) - lgmaps.append((no, o)) - + lgmaps = [ + (no, o) for no, o in zip(lgmaps_without_overlap, lgmaps_with_overlap) + ] coarse_to_fine_cells = [] fine_to_coarse_cells = [None] for (coarse, fine), (clgmaps, flgmaps) in zip(zip(meshes[:-1], meshes[1:]), @@ -286,7 +287,6 @@ def SemiCoarsenedExtrudedHierarchy(base_mesh, height, nref=1, base_layer=-1, ref """ if not isinstance(base_mesh, firedrake.mesh.MeshGeometry): raise ValueError(f"Can only extruded a mesh, not a {type(base_mesh)}") - base_mesh.init() if base_mesh.cell_set._extruded: raise ValueError("Base mesh must not be extruded") if layers is None: diff --git a/firedrake/ufl_expr.py b/firedrake/ufl_expr.py index 259de72b4c..bbc8a0d933 100644 --- a/firedrake/ufl_expr.py +++ b/firedrake/ufl_expr.py @@ -348,7 +348,6 @@ def CellSize(mesh): :arg mesh: the mesh for which to calculate the cell size. """ - mesh.init() return ufl.CellDiameter(mesh) @@ -358,7 +357,6 @@ def FacetNormal(mesh): :arg mesh: the mesh over which the normal should be represented. """ - mesh.init() return ufl.FacetNormal(mesh) diff --git a/firedrake/utility_meshes.py b/firedrake/utility_meshes.py index 99d687449e..6540611752 100644 --- a/firedrake/utility_meshes.py +++ b/firedrake/utility_meshes.py @@ -33,6 +33,7 @@ from firedrake import mesh from firedrake import function from firedrake import functionspace +from firedrake.parameters import parameters from firedrake.petsc import PETSc from pyadjoint.tape import no_annotations @@ -375,9 +376,14 @@ def OneElementThickMesh( plex = mesh.plex_from_cell_list( 2, cells, coords, comm, mesh._generate_default_mesh_topology_name(name) ) - mesh1 = mesh.Mesh(plex, distribution_parameters=distribution_parameters, comm=comm) - mesh1.topology.init() - cell_numbering = mesh1._cell_numbering + tmesh1 = mesh.MeshTopology( + plex, + plex.getName(), + reorder=parameters["reorder_meshes"], + distribution_parameters=distribution_parameters, + comm=comm, + ) + cell_numbering = tmesh1._cell_numbering cell_range = plex.getHeightStratum(0) cell_closure = np.zeros((cell_range[1], 9), dtype=IntType) @@ -474,10 +480,8 @@ def OneElementThickMesh( cell_closure[row][0:4] = [v1, v1, v2, v2] - mesh1.topology.cell_closure = np.array(cell_closure, dtype=IntType) - - mesh1.init() - + tmesh1.cell_closure = np.array(cell_closure, dtype=IntType) + mesh1 = mesh.make_mesh_from_mesh_topology(tmesh1, "temp") fe_dg = FiniteElement("DQ", mesh1.ufl_cell(), 1, variant="equispaced") Vc = VectorFunctionSpace(mesh1, fe_dg) fc = Function( diff --git a/tests/firedrake/multigrid/test_basics.py b/tests/firedrake/multigrid/test_basics.py index ec80c1ee4f..d4161ebca1 100644 --- a/tests/firedrake/multigrid/test_basics.py +++ b/tests/firedrake/multigrid/test_basics.py @@ -60,13 +60,3 @@ def test_refine_square_ncell_parallel(): # Should be fewer than 4 times the number of coarse cells due to # halo shrinking. assert mh[1].num_cells() < 4 * mh[0].num_cells() - - -@pytest.mark.parallel(nprocs=2) -def test_refining_overlapped_mesh_fails_parallel(): - m = UnitSquareMesh(4, 4) - - m.init() - - with pytest.raises(RuntimeError): - MeshHierarchy(m, 1) diff --git a/tests/firedrake/regression/test_constant.py b/tests/firedrake/regression/test_constant.py index f95e0a3e2d..019e982356 100644 --- a/tests/firedrake/regression/test_constant.py +++ b/tests/firedrake/regression/test_constant.py @@ -200,7 +200,6 @@ def test_fresh_constant_hashes_different(): def test_constants_are_renumbered_in_form_signature(): mesh = UnitSquareMesh(1, 1) - mesh.init() c = Constant(1) d = Constant(1) diff --git a/tests/firedrake/regression/test_exodus_mesh.py b/tests/firedrake/regression/test_exodus_mesh.py index aa761c98fe..bb2cac177b 100644 --- a/tests/firedrake/regression/test_exodus_mesh.py +++ b/tests/firedrake/regression/test_exodus_mesh.py @@ -27,5 +27,4 @@ def test_sidesets(exodus_mesh): if exodus_mesh is None: pytest.skip("PETSc not configured with exodusII") else: - exodus_mesh.init() assert (exodus_mesh.exterior_facets.unique_markers == [200, 201]).all() diff --git a/tests/firedrake/regression/test_facets.py b/tests/firedrake/regression/test_facets.py index b50eb95b5f..ed0936527c 100644 --- a/tests/firedrake/regression/test_facets.py +++ b/tests/firedrake/regression/test_facets.py @@ -187,6 +187,5 @@ def test_facet_map_no_reshape(): def test_mesh_with_no_facet_markers(): mesh = UnitTriangleMesh() - mesh.init() with pytest.raises(LookupError): mesh.exterior_facets.subset((10,)) diff --git a/tests/firedrake/regression/test_mesh_generation.py b/tests/firedrake/regression/test_mesh_generation.py index b7cffa6777..4cb7cc109a 100644 --- a/tests/firedrake/regression/test_mesh_generation.py +++ b/tests/firedrake/regression/test_mesh_generation.py @@ -247,7 +247,6 @@ def test_periodic_unit_cube_parallel(): def assert_num_exterior_facets_equals_zero(m): # Need to initialise the mesh so that exterior facets have been # built. - m.init() assert m.exterior_facets.set.total_size == 0 @@ -421,7 +420,6 @@ def test_bendy_cube_unit_parallel(degree): def test_mesh_reordering_defaults_on(): assert parameters["reorder_meshes"] m = UnitSquareMesh(1, 1) - m.init() assert m._did_reordering @@ -449,7 +447,6 @@ def test_mesh_validation_parallel(): [False, True]) def test_force_reordering_works(reorder): m = UnitSquareMesh(1, 1, reorder=reorder) - m.init() assert m._did_reordering == reorder @@ -461,7 +458,6 @@ def test_changing_default_reorder_works(reorder): try: parameters["reorder_meshes"] = reorder m = UnitSquareMesh(1, 1) - m.init() assert m._did_reordering == reorder finally: @@ -472,7 +468,6 @@ def test_changing_default_reorder_works(reorder): [("default", 6)]) def test_boxmesh_kind(kind, num_cells): m = BoxMesh(1, 1, 1, 1, 1, 1, diagonal=kind) - m.init() assert m.num_cells() == num_cells diff --git a/tests/firedrake/regression/test_mesh_overlaps.py b/tests/firedrake/regression/test_mesh_overlaps.py index 2466d1a328..ac766b4fad 100644 --- a/tests/firedrake/regression/test_mesh_overlaps.py +++ b/tests/firedrake/regression/test_mesh_overlaps.py @@ -62,7 +62,6 @@ def mesh(request, overlap): else: mesh = UnitSquareMesh(2, 2, reorder=False, distribution_parameters=params) - mesh.init() return mesh @@ -93,9 +92,6 @@ def test_override_distribution_parameters(overlap): params = {"overlap_type": (DistributedMeshOverlapType.NONE, 0)} fine_mesh = MeshHierarchy(mesh, 1, distribution_parameters=params)[-1] - mesh.init() - fine_mesh.init() - if overlap[0] == DistributedMeshOverlapType.NONE: assert mesh.num_cells() == 1 else: diff --git a/tests/firedrake/regression/test_point_eval_fs.py b/tests/firedrake/regression/test_point_eval_fs.py index 91a751a826..6c4a9e837f 100644 --- a/tests/firedrake/regression/test_point_eval_fs.py +++ b/tests/firedrake/regression/test_point_eval_fs.py @@ -217,7 +217,6 @@ def test_point_reset_works(): def test_changing_coordinates_invalidates_spatial_index(): mesh = UnitSquareMesh(2, 2) - mesh.init() saved_spatial_index = mesh.spatial_index mesh.coordinates.assign(mesh.coordinates * 2) diff --git a/tests/firedrake/supermesh/test_nonnested_project.py b/tests/firedrake/supermesh/test_nonnested_project.py index 9439e649f3..798228908d 100644 --- a/tests/firedrake/supermesh/test_nonnested_project.py +++ b/tests/firedrake/supermesh/test_nonnested_project.py @@ -18,8 +18,6 @@ def hierarchy(): mesh2 = RectangleMesh(5, 5, 1, 1, diagonal="right", distribution_parameters=distribution_parameters) - mesh.init() - mesh2.init() coarse_to_fine = numpy.tile(numpy.arange(mesh2.num_cells(), dtype=IntType), (mesh.num_cells(), 1)) diff --git a/tests/firedrake/vertexonly/test_swarm.py b/tests/firedrake/vertexonly/test_swarm.py index 555351cd01..96c4602511 100644 --- a/tests/firedrake/vertexonly/test_swarm.py +++ b/tests/firedrake/vertexonly/test_swarm.py @@ -16,7 +16,6 @@ def cell_midpoints(m): `midpoints` are the midpoints for the entire mesh even if the mesh is distributed and `local_midpoints` are the midpoints of only the rank-local non-ghost cells.""" - m.init() V = VectorFunctionSpace(m, "DG", 0) f = Function(V).interpolate(SpatialCoordinate(m)) # since mesh may be distributed, the number of cells on the MPI rank @@ -46,7 +45,6 @@ def cell_ownership(m): m.locate_cell(point). """ - m.init() # Interpolating Constant(parent_mesh.comm.rank) into P0DG cleverly creates # a Function whose dat contains rank ownership information in an ordering # that is accessible using Firedrake's cell numbering. This is because, on @@ -158,7 +156,6 @@ def test_pic_swarm_in_mesh(parentmesh, redundant, exclude_halos): # Setup - parentmesh.init() inputpointcoords, inputlocalpointcoords = cell_midpoints(parentmesh) inputcoordindices = np.arange(len(inputpointcoords)) inputlocalpointcoordranks = point_ownership(parentmesh, inputpointcoords, inputlocalpointcoords) diff --git a/tests/firedrake/vertexonly/test_vertex_only_mesh_generation.py b/tests/firedrake/vertexonly/test_vertex_only_mesh_generation.py index 18906ec924..9970b41a1b 100644 --- a/tests/firedrake/vertexonly/test_vertex_only_mesh_generation.py +++ b/tests/firedrake/vertexonly/test_vertex_only_mesh_generation.py @@ -16,7 +16,6 @@ def cell_midpoints(m): `midpoints` are the midpoints for the entire mesh even if the mesh is distributed and `local_midpoints` are the midpoints of only the rank-local non-ghost cells.""" - m.init() V = VectorFunctionSpace(m, "DG", 0) f = Function(V).interpolate(SpatialCoordinate(m)) # since mesh may be distributed, the number of cells on the MPI rank @@ -121,7 +120,6 @@ def verify_vertexonly_mesh(m, vm, inputvertexcoords, name): assert vm.geometric_dimension() == gdim assert vm.topological_dimension() == 0 # Can initialise - vm.init() # has correct name assert vm.name == name # Find in-bounds and non-halo-region input coordinates