Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create topology Python wrapper #3406

Merged
merged 34 commits into from
Sep 18, 2024
Merged
Changes from 17 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
65c9c65
Create topology Python wrapper
jorgensd Sep 15, 2024
153a0b0
Add error handling for missing connectivity
jorgensd Sep 15, 2024
0fcf462
Add docstring to exterior_facet_indices
jorgensd Sep 15, 2024
e14f671
More wrapping
jorgensd Sep 15, 2024
d231d36
Fix wrapper of cell_name
jorgensd Sep 15, 2024
5997f30
Update meshtags calling
jorgensd Sep 15, 2024
f3c2590
Formatting
jorgensd Sep 15, 2024
effa49d
Another wrapper update
jorgensd Sep 15, 2024
c982f13
Fix wrapper and remove unused dictionary/mapping
jorgensd Sep 15, 2024
bc76e5d
Fix conditional
jorgensd Sep 15, 2024
75a50fa
Fix compute integration domain
jorgensd Sep 15, 2024
97fb8d4
Update import in test
jorgensd Sep 15, 2024
4cd7227
Revert change in mixed test that uses C++ mesh directly
jorgensd Sep 15, 2024
cbae680
Update transfer facet meshtags
jorgensd Sep 15, 2024
a12ca47
Fix mixed test
jorgensd Sep 15, 2024
77ad688
Merge branch 'main' into dokken/topology-interface
jorgensd Sep 16, 2024
8d642d6
Fix typo pointed out by Remi
jorgensd Sep 16, 2024
352bc53
Apply suggestions from code review
jorgensd Sep 16, 2024
a973959
Remove cell_name binding + fix docs
jorgensd Sep 16, 2024
d128321
Apply suggestions from code review
jorgensd Sep 16, 2024
26bb2ba
Fix submesh code
jorgensd Sep 16, 2024
568ce22
Merge branch 'main' into dokken/topology-interface
jorgensd Sep 16, 2024
3d5195e
Add documentation to facet permutations
jorgensd Sep 16, 2024
d6b4a33
Merge branch 'main' into dokken/topology-interface
jorgensd Sep 16, 2024
4a9ab9b
Add helper messages + specify bits of each integer
jorgensd Sep 16, 2024
fc3f3c9
Merge branch 'main' into dokken/topology-interface
jorgensd Sep 17, 2024
f432173
Merge branch 'main' into dokken/topology-interface
jhale Sep 17, 2024
63ee875
Merge branch 'main' into dokken/topology-interface
jorgensd Sep 17, 2024
52e844f
Apply suggestions from code review
jorgensd Sep 17, 2024
ca0b8cb
Merge branch 'main' into dokken/topology-interface
jorgensd Sep 18, 2024
323d597
Ruff formatting
jorgensd Sep 18, 2024
d9e7286
Remove mixed mesh oddity comment
jorgensd Sep 18, 2024
c6f959a
Merge branch 'main' into dokken/topology-interface
jorgensd Sep 18, 2024
1c47e3e
Small doc formatting updates
garth-wells Sep 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion python/dolfinx/fem/__init__.py
Original file line number Diff line number Diff line change
@@ -155,7 +155,7 @@ def compute_integration_domains(
Returns:
List of integration entities
"""
return _compute_integration_domains(integral_type, topology, entities, dim)
return _compute_integration_domains(integral_type, topology._cpp_object, entities, dim)


__all__ = [
2 changes: 1 addition & 1 deletion python/dolfinx/fem/function.py
Original file line number Diff line number Diff line change
@@ -645,7 +645,7 @@ def functionspace(

cpp_element = _create_dolfinx_element(mesh.comm, mesh.topology.cell_type, ufl_e, dtype)

cpp_dofmap = _cpp.fem.create_dofmap(mesh.comm, mesh.topology, cpp_element)
cpp_dofmap = _cpp.fem.create_dofmap(mesh.comm, mesh.topology._cpp_object, cpp_element)

assert np.issubdtype(
mesh.geometry.x.dtype, cpp_element.dtype
194 changes: 172 additions & 22 deletions python/dolfinx/mesh.py
Original file line number Diff line number Diff line change
@@ -26,7 +26,6 @@
build_dual_graph,
cell_dim,
create_cell_partitioner,
exterior_facet_indices,
to_string,
to_type,
)
@@ -67,10 +66,140 @@
]


class Topology:
"""Topology for a :class:`dolfinx.mesh.Mesh`"""

_cpp_object: _cpp.mesh.Topology

def __init__(self, topology: _cpp.mesh.Topology):
"""Initialize a topology from a C++ topology.
Args: The C++ topology object
Note:
topology objects should not usually be created using this
initializer directly.
"""
self._cpp_object = topology

def cell_name(self) -> str:
"""String representation of the cell-type of the topology"""
return self._cpp_object.cell_name()

def connectivity(self, d0: int, d1: int) -> _cpp.graph.AdjacencyList_int32:
"""Return connectivity from entities of dimension ``d0`` to entities of dimension ``d1``.

Note:
Assumes only one entity type per dimension.

Args:
d0: Dimension of entity one is mapping from
d1: Dimension of entity one is mapping to
"""
if (conn := self._cpp_object.connectivity(d0, d1)) is not None:
return conn
else:
raise RuntimeError(
f"Connectiivty between dimension {d0} and {d1} has not been computed."
)

@property
def comm(self):
return self._cpp_object.comm

def create_connectivity(self, d0: int, d1: int):
"""Create connectivity between given pair of dimensions, ``d0`` and ``d1``.

Args:
d0: Dimension of entity one is mapping from
d1: Dimension of entity one is mapping to
"""
self._cpp_object.create_connectivity(d0, d1)

def create_entities(self, dim: int) -> int:
"""Create entities of given topological dimension.

Args:
dim: Topological dimension

Returns:
Number of newly created entities, returns -1 if entities already existed
"""
return self._cpp_object.create_entities(dim)

def create_entity_permutations(self):
"""Compute entity permutations and reflections."""
self._cpp_object.create_entity_permutations()

@property
def dim(self) -> int:
"""Return the topological dimension of the mesh."""
return self._cpp_object.dim

@property
def entity_types(self) -> list[list[CellType]]:
"""Get the entity types in the topology for all topological dimensions."""
return self._cpp_object.entity_types

def get_cell_permutation_info(self) -> npt.NDArray[np.uint32]:
"""Returns the permutation information"""
return self._cpp_object.get_cell_permutation_info()

def get_facet_permutations(self) -> npt.NDArray[np.uint8]:
"""Get the permutation number to apply to a facet."""
return self._cpp_object.get_facet_permutations()

def index_map(self, dim: int) -> _cpp.common.IndexMap:
"""Get the IndexMap that described the parallel distribution of the mesh entities.

Args:
dim: Topological dimension

Returns:
Index map for the entities of dimension `dim`.
"""
if (imap := self._cpp_object.index_map(dim)) is not None:
return imap
else:
raise RuntimeError(f"Entities of dimension {dim} has not been computed")

def interprocess_facets(self) -> npt.NDArray[np.int32]:
"""List of inter-process facets, if facet topology has been computed."""
return self._cpp_object.interprocess_facets()

@property
def original_cell_index(self) -> npt.NDArray[np.int64]:
"""Get the original cell index"""
return self._cpp_object.original_cell_index

def set_connectivity(self, graph: _cpp.graph.AdjacencyList_int32, d0: int, d1: int):
"""Set connectivity for given pair of topological dimensions.

Args:
graph: Connectivity graph
d0: Topological dimension mapping from
d1: Topological dimension mapping to
"""
self._cpp_object.set_connectivity(graph, d0, d1)

def set_index_map(self, dim: int, index_map: _cpp.common.IndexMap):
"""Set the IndexMap for dimension ``dim``.

Args:
dim: Topological dimension of entity
index_map: IndexMap
"""
return self._cpp_object.set_index_map(dim, index_map)

@property
def cell_type(self) -> CellType:
"""Get the cell type of the topology"""
return self._cpp_object.cell_type


class Mesh:
"""A mesh."""

_mesh: typing.Union[_cpp.mesh.Mesh_float32, _cpp.mesh.Mesh_float64]
_topology: Topology
_ufl_domain: typing.Optional[ufl.Mesh]

def __init__(self, mesh, domain: typing.Optional[ufl.Mesh]):
@@ -85,6 +214,7 @@ def __init__(self, mesh, domain: typing.Optional[ufl.Mesh]):
initializer directly.
"""
self._cpp_object = mesh
self._topology = Topology(self._cpp_object.topology)
self._ufl_domain = domain
if self._ufl_domain is not None:
self._ufl_domain._ufl_cargo = self._cpp_object # type: ignore
@@ -138,9 +268,9 @@ def h(self, dim: int, entities: npt.NDArray[np.int32]) -> npt.NDArray[np.float64
return _cpp.mesh.h(self._cpp_object, dim, entities)

@property
def topology(self):
def topology(self) -> Topology:
"Mesh topology."
return self._cpp_object.topology
return self._topology

@property
def geometry(self):
@@ -214,8 +344,19 @@ def find(self, value) -> npt.NDArray[np.int32]:
return self._cpp_object.find(value)


def compute_incident_entities(topology, entities: npt.NDArray[np.int32], d0: int, d1: int):
return _cpp.mesh.compute_incident_entities(topology, entities, d0, d1)
def compute_incident_entities(
topology: Topology, entities: npt.NDArray[np.int32], d0: int, d1: int
) -> npt.NDArray[np.int32]:
"""Compute all entities of ``d1`` connected to ``entities`` of dimension ``d0``.

Args:
topology: The topology
entities: List of entities fo dimension ``d0``.
d0: Topological dimension
d1: Topological dimension to map to

"""
return _cpp.mesh.compute_incident_entities(topology._cpp_object, entities, d0, d1)


def compute_midpoints(mesh: Mesh, dim: int, entities: npt.NDArray[np.int32]):
@@ -266,19 +407,6 @@ def locate_entities_boundary(mesh: Mesh, dim: int, marker: typing.Callable) -> n
return _cpp.mesh.locate_entities_boundary(mesh._cpp_object, dim, marker)


_uflcell_to_dolfinxcell = {
"interval": CellType.interval,
"interval2D": CellType.interval,
"interval3D": CellType.interval,
"triangle": CellType.triangle,
"triangle3D": CellType.triangle,
"quadrilateral": CellType.quadrilateral,
"quadrilateral3D": CellType.quadrilateral,
"tetrahedron": CellType.tetrahedron,
"hexahedron": CellType.hexahedron,
}


def transfer_meshtag(
meshtag: MeshTags,
mesh1: Mesh,
@@ -300,12 +428,14 @@ def transfer_meshtag(
Mesh tags on the refined mesh.
"""
if meshtag.dim == meshtag.topology.dim:
mt = _cpp.refinement.transfer_cell_meshtag(meshtag._cpp_object, mesh1.topology, parent_cell)
mt = _cpp.refinement.transfer_cell_meshtag(
meshtag._cpp_object, mesh1.topology._cpp_object, parent_cell
)
return MeshTags(mt)
elif meshtag.dim == meshtag.topology.dim - 1:
assert parent_facet is not None
mt = _cpp.refinement.transfer_facet_meshtag(
meshtag._cpp_object, mesh1.topology, parent_cell, parent_facet
meshtag._cpp_object, mesh1.topology._cpp_object, parent_cell, parent_facet
)
return MeshTags(mt)
else:
@@ -546,7 +676,9 @@ def meshtags(
else:
raise NotImplementedError(f"Type {values.dtype} not supported.")

return MeshTags(ftype(mesh.topology, dim, np.asarray(entities, dtype=np.int32), values))
return MeshTags(
ftype(mesh.topology._cpp_object, dim, np.asarray(entities, dtype=np.int32), values)
)


def meshtags_from_entities(
@@ -577,7 +709,7 @@ def meshtags_from_entities(
elif isinstance(values, float):
values = np.full(entities.num_nodes, values, dtype=np.double)
values = np.asarray(values)
return MeshTags(_cpp.mesh.create_meshtags(mesh.topology, dim, entities, values))
return MeshTags(_cpp.mesh.create_meshtags(mesh.topology._cpp_object, dim, entities, values))


def create_interval(
@@ -819,3 +951,21 @@ def entities_to_geometry(
The geometric DOFs associated with the closure of the entities in `entities`.
"""
return _cpp.mesh.entities_to_geometry(mesh._cpp_object, dim, entities, permute)


def exterior_facet_indices(topology: Topology) -> npt.NDArray[np.int32]:
"""Compute the indices of all exterior facets that are owned by the caller.

An exterior facet (co-dimension 1) is one that is connected globally to
only one cell of co-dimension 0).

Note:
This is a collective operation that should be called on all processes.

Args:
topology: The topology

Returns:
Sorted list of owned facet indices that are exterior facets of the mesh.
"""
return _cpp.mesh.exterior_facet_indices(topology._cpp_object)
2 changes: 1 addition & 1 deletion python/test/unit/fem/test_assemble_submesh.py
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@

import ufl
from dolfinx import default_scalar_type, fem, la
from dolfinx.cpp.fem import compute_integration_domains
from dolfinx.fem import compute_integration_domains
from dolfinx.mesh import (
GhostMode,
compute_incident_entities,
2 changes: 1 addition & 1 deletion python/test/unit/mesh/test_mixed_topology.py
Original file line number Diff line number Diff line change
@@ -242,7 +242,7 @@ def test_create_entities():

quad_v = mesh.topology.connectivity((2, qi), (0, 0))
tri_v = mesh.topology.connectivity((2, ti), (0, 0))
ims = mesh.topology.index_maps(2)
ims = mesh.topology._cpp_object.index_maps(2)
assert len(ims) == 2
assert ims[qi].size_global == 32
assert len(quad_v.links(0)) == 4