Skip to content

Commit 4259068

Browse files
Introduce dolfinx.fem.create_dofmaps (#3842)
* Wrap create_dofmaps in Python layer * Type oblivious * Use python type for reference * Remove * Remove empty * Without checking? * ruff * Try again * Fully qualify * Adapt demo * Fix * Ruff * Simplify * One more * Test case * Update python/dolfinx/fem/dofmap.py Co-authored-by: Garth N. Wells <gnw20@cam.ac.uk> * Remove coordinate elements naming * Improve docstring * Update python/dolfinx/fem/dofmap.py Co-authored-by: Garth N. Wells <gnw20@cam.ac.uk> * rst --------- Co-authored-by: Garth N. Wells <gnw20@cam.ac.uk>
1 parent f138697 commit 4259068

4 files changed

Lines changed: 65 additions & 27 deletions

File tree

python/demo/demo_mixed-topology.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,16 @@
2626
import ufl
2727
from dolfinx.cpp.mesh import GhostMode, create_cell_partitioner, create_mesh
2828
from dolfinx.fem import (
29+
FiniteElement,
2930
FunctionSpace,
3031
assemble_matrix,
3132
assemble_vector,
3233
coordinate_element,
34+
create_dofmaps,
3335
mixed_topology_form,
3436
)
3537
from dolfinx.io.utils import cell_perm_vtk
36-
from dolfinx.mesh import CellType, Mesh
38+
from dolfinx.mesh import CellType, Mesh, Topology
3739

3840
if MPI.COMM_WORLD.size > 1:
3941
print("Not yet running in parallel")
@@ -102,12 +104,20 @@
102104
basix.create_element(basix.ElementFamily.P, basix.CellType.hexahedron, 1),
103105
basix.create_element(basix.ElementFamily.P, basix.CellType.prism, 1),
104106
]
105-
elements_cpp = [_cpp.fem.FiniteElement_float64(e._e, None, True) for e in elements]
107+
dolfinx_elements = [
108+
FiniteElement(_cpp.fem.FiniteElement_float64(e._e, None, True)) for e in elements
109+
]
106110
# NOTE: Both dofmaps have the same IndexMap, but different cell_dofs
107-
dofmaps = _cpp.fem.create_dofmaps(mesh.comm, mesh.topology, elements_cpp)
111+
dofmaps = create_dofmaps(
112+
mesh.comm,
113+
Topology(mesh.topology),
114+
dolfinx_elements,
115+
)
108116

109117
# Create C++ function space
110-
V_cpp = _cpp.fem.FunctionSpace_float64(mesh, elements_cpp, dofmaps)
118+
V_cpp = _cpp.fem.FunctionSpace_float64(
119+
mesh, [e._cpp_object for e in dolfinx_elements], [dofmap._cpp_object for dofmap in dofmaps]
120+
)
111121

112122
# Create forms for each cell type.
113123
# FIXME This hack is required at the moment because UFL does not yet know

python/dolfinx/fem/__init__.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
# SPDX-License-Identifier: LGPL-3.0-or-later
66
"""Tools for assembling and manipulating finite element forms."""
77

8+
import typing
9+
810
import numpy as np
911
import numpy.typing as npt
1012

@@ -18,7 +20,6 @@
1820
from dolfinx.cpp.fem import interpolation_matrix as _interpolation_matrix
1921
from dolfinx.cpp.fem import transpose_dofmap
2022
from dolfinx.cpp.la import SparsityPattern
21-
from dolfinx.cpp.mesh import Topology
2223
from dolfinx.fem.assemble import (
2324
apply_lifting,
2425
assemble_matrix,
@@ -37,7 +38,7 @@
3738
locate_dofs_geometrical,
3839
locate_dofs_topological,
3940
)
40-
from dolfinx.fem.dofmap import DofMap
41+
from dolfinx.fem.dofmap import DofMap, create_dofmaps
4142
from dolfinx.fem.element import CoordinateElement, FiniteElement, coordinate_element, finiteelement
4243
from dolfinx.fem.forms import (
4344
Form,
@@ -59,6 +60,9 @@
5960
from dolfinx.geometry import PointOwnershipData as _PointOwnershipData
6061
from dolfinx.la import MatrixCSR as _MatrixCSR
6162

63+
if typing.TYPE_CHECKING:
64+
import dolfinx.mesh
65+
6266

6367
def create_sparsity_pattern(a: Form):
6468
"""Create a sparsity pattern from a bilinear form.
@@ -174,7 +178,7 @@ def interpolation_matrix(space0: FunctionSpace, space1: FunctionSpace) -> _Matri
174178

175179

176180
def compute_integration_domains(
177-
integral_type: IntegralType, topology: Topology, entities: np.ndarray
181+
integral_type: IntegralType, topology: "dolfinx.mesh.Topology", entities: np.ndarray
178182
):
179183
"""Given an integral type and a set of entities compute integration
180184
entities.
@@ -231,6 +235,7 @@ def compute_integration_domains(
231235
"compile_form",
232236
"compute_integration_domains",
233237
"coordinate_element",
238+
"create_dofmaps",
234239
"create_form",
235240
"create_interpolation_data",
236241
"create_matrix",

python/dolfinx/fem/dofmap.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1-
# Copyright (C) 2018 Michal Habera
1+
# Copyright (C) 2018-2025 Michal Habera and Paul T. Kühner
22
#
33
# This file is part of DOLFINx (https://www.fenicsproject.org)
44
#
55
# SPDX-License-Identifier: LGPL-3.0-or-later
66

7-
from dolfinx import cpp as _cpp
7+
import typing
8+
from collections.abc import Sequence
9+
10+
from mpi4py.MPI import Comm
11+
12+
from basix.finite_element import FiniteElement
13+
from dolfinx.cpp.fem import DofMap as _DofMap
14+
from dolfinx.cpp.fem import create_dofmaps as _create_dofmaps
15+
16+
if typing.TYPE_CHECKING:
17+
import dolfinx.mesh
818

919

1020
class DofMap:
@@ -14,9 +24,9 @@ class DofMap:
1424
dof map based on a FiniteElement on a specific mesh.
1525
"""
1626

17-
_cpp_object: _cpp.fem.DofMap
27+
_cpp_object: _DofMap
1828

19-
def __init__(self, dofmap: _cpp.fem.DofMap):
29+
def __init__(self, dofmap: _DofMap):
2030
self._cpp_object = dofmap
2131

2232
def cell_dofs(self, cell_index: int):
@@ -56,3 +66,22 @@ def index_map_bs(self):
5666
def list(self):
5767
"""Adjacency list with dof indices for each cell."""
5868
return self._cpp_object.map()
69+
70+
71+
def create_dofmaps(
72+
comm: Comm, topology: "dolfinx.mesh.Topology", elements: Sequence[FiniteElement]
73+
) -> list[DofMap]:
74+
"""Create degree-of-freedom maps on a given topology.
75+
76+
Args:
77+
comm: MPI communicator
78+
topology: Mesh topology
79+
elements: Sequence of elements
80+
81+
Returns:
82+
List of degree-of-freedom maps where the ``i``-th map is the map
83+
for ``elements[i]``.
84+
"""
85+
elements_cpp = [e._cpp_object for e in elements]
86+
cpp_dofmaps = _create_dofmaps(comm, topology._cpp_object, elements_cpp)
87+
return [DofMap(cpp_object) for cpp_object in cpp_dofmaps]

python/test/unit/fem/test_mixed_mesh_dofmap.py

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,19 @@
33
import numpy as np
44

55
import basix
6-
import dolfinx.cpp as _cpp
76
from dolfinx.cpp.mesh import Mesh_float64, create_geometry, create_topology
8-
from dolfinx.fem import coordinate_element
9-
from dolfinx.fem.dofmap import DofMap
7+
from dolfinx.fem import coordinate_element, create_dofmaps
8+
from dolfinx.fem.element import finiteelement
109
from dolfinx.log import LogLevel, set_log_level
11-
from dolfinx.mesh import CellType
10+
from dolfinx.mesh import CellType, Topology
1211

1312

1413
def create_element_dofmap(mesh, cell_types, degree):
15-
cpp_elements = []
16-
for cell_type in cell_types:
17-
ufl_e = basix.ufl.element("P", cell_type, degree, dtype=np.float64)
18-
cpp_elements += [_cpp.fem.FiniteElement_float64(ufl_e.basix_element._e, None, False)]
19-
20-
cpp_dofmaps = _cpp.fem.create_dofmaps(mesh.comm, mesh.topology, cpp_elements)
21-
22-
return (cpp_elements, cpp_dofmaps)
14+
elements = [
15+
finiteelement(ct, basix.ufl.element("P", ct, degree), np.float64) for ct in cell_types
16+
]
17+
dofmaps = create_dofmaps(mesh.comm, Topology(mesh.topology), elements)
18+
return (elements, dofmaps)
2319

2420

2521
def test_dofmap_mixed_topology():
@@ -72,8 +68,7 @@ def test_dofmap_mixed_topology():
7268
assert elements[1].basix_element.cell_type.name == "quadrilateral"
7369

7470
assert len(dofmaps) == 2
75-
q0 = DofMap(dofmaps[0])
76-
q1 = DofMap(dofmaps[1])
71+
q0, q1 = dofmaps
7772
assert q0.index_map.size_local == q1.index_map.size_local
7873
# Triangles
7974
print(q0.list)
@@ -121,10 +116,9 @@ def test_dofmap_prism_mesh():
121116
mesh = Mesh_float64(MPI.COMM_WORLD, topology, geom)
122117

123118
elements, dofmaps = create_element_dofmap(mesh, [basix.CellType.prism], 2)
124-
print()
125119
assert len(elements) == 1
126120
assert len(dofmaps) == 1
127-
q = DofMap(dofmaps[0])
121+
q = dofmaps[0]
128122
assert q.index_map.size_local == 18
129123
print(q.list)
130124
facet_dofs = []

0 commit comments

Comments
 (0)