Skip to content

Commit 0a209f4

Browse files
committed
Implement lin_to_quad for wedges and to_serendipity
1 parent 7c8e932 commit 0a209f4

File tree

8 files changed

+2171
-13
lines changed

8 files changed

+2171
-13
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "biomesh"
7-
version = "0.5.1"
7+
version = "0.6.0"
88
authors = [
99
{ name="The biomesh Authors" },
1010
]

src/biomesh/adapt.py

Lines changed: 138 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
"""Small utilities for mesh adaption (e.g. converting linear elements to
77
quadratic elements)"""
88

9+
from typing import Callable, Optional
10+
911
import meshio
1012
import numpy as np
1113

@@ -54,6 +56,23 @@
5456
[1, 3],
5557
[2, 3],
5658
],
59+
"wedge": [
60+
[0],
61+
[1],
62+
[2],
63+
[3],
64+
[4],
65+
[5],
66+
[0, 1],
67+
[1, 2],
68+
[2, 0],
69+
[3, 4],
70+
[4, 5],
71+
[5, 3],
72+
[0, 3],
73+
[1, 4],
74+
[2, 5],
75+
],
5776
"triangle": [
5877
[0],
5978
[1],
@@ -68,26 +87,73 @@
6887
_lin_to_quad_cell_type = {
6988
"hexahedron": "hexahedron27",
7089
"tetra": "tetra10",
90+
"wedge": "wedge15",
7191
"triangle": "triangle6",
7292
"quad": "quad9",
7393
"line": "line3",
7494
}
7595

96+
_hex8_to_hex20 = [
97+
[0],
98+
[1],
99+
[2],
100+
[3],
101+
[4],
102+
[5],
103+
[6],
104+
[7],
105+
[0, 1],
106+
[1, 2],
107+
[2, 3],
108+
[3, 0],
109+
[4, 5],
110+
[5, 6],
111+
[6, 7],
112+
[7, 4],
113+
[0, 4],
114+
[1, 5],
115+
[2, 6],
116+
[3, 7],
117+
]
76118

77-
def lin_to_quad(mesh: meshio.Mesh) -> meshio.Mesh:
78-
"""Convert linear elements to quadratic elements in a mesh.
79119

80-
This function returns a new mesh in which all linear elements (triangles, quadrilaterals, tetrahedra, and hexahedra)
81-
are converted into their corresponding quadratic elements.
120+
def adapt_mesh(
121+
mesh: meshio.Mesh, adapting_scheme: Callable[[str], Optional[tuple[str, list]]]
122+
) -> meshio.Mesh:
123+
"""Adapt a mesh to a higher-order (quadratic) representation using a
124+
provided adapting scheme.
82125
83-
Args:
84-
mesh:
85-
The input mesh containing linear elements.
126+
This function takes a mesh and, for each cell block, determines whether it should be adapted
127+
to a new cell type. If so, it generates new mid-edge nodes and updates
128+
the mesh accordingly. The adapting scheme determines the mapping from the original cell type
129+
to the new cell type and specifies how to construct new nodes.
86130
87-
Returns:
88-
The modified mesh with quadratic elements.
89-
"""
131+
Parameters
132+
----------
133+
mesh : meshio.Mesh
134+
The input mesh to be adapted.
135+
adapting_scheme : Callable[[str], Optional[tuple[str, list]]]
136+
A function that takes a cell type string and returns either:
137+
- None, if the cell type should not be adapted,
138+
- or a tuple of (new_cell_type, node_mapping), where:
139+
new_cell_type : str
140+
The name of the quadratic cell type (e.g., "triangle6").
141+
node_mapping : list
142+
A list specifying how to map old nodes and create new mid-edge nodes.
143+
Each entry is either a tuple with one index (existing node) or multiple
144+
indices (to create a new node as the mean of those nodes).
90145
146+
Returns
147+
-------
148+
meshio.Mesh
149+
The adapted mesh with quadratic cells and updated points, point data, and cell data.
150+
151+
Notes
152+
-----
153+
- Cell blocks that are already quadratic or are not to be adapted are left unchanged.
154+
- New mid-edge nodes are created only once per unique edge and reused across cells.
155+
- Point data is interpolated for new nodes using the mean of the corresponding data.
156+
"""
91157
new_points = [coord for coord in mesh.points]
92158
new_point_data = {key: [d for d in data] for key, data in mesh.point_data.items()}
93159
new_cell_blocks = []
@@ -110,8 +176,17 @@ def lin_to_quad(mesh: meshio.Mesh) -> meshio.Mesh:
110176
continue
111177

112178
# determine the mapping of the nodes to the new quadratic celltype
113-
node_mapping = _lin_to_quad_nodes[cell_type]
114-
new_cell_type = _lin_to_quad_cell_type[cell_type]
179+
scheme = adapting_scheme(cell_type)
180+
181+
if not scheme:
182+
# don't adapt this block
183+
# this cell block is already quadratic, no need to convert
184+
new_cell_blocks.append(cellblock)
185+
for key, data in mesh.cell_data.items():
186+
new_cell_data[key].append(data[i])
187+
continue
188+
189+
new_cell_type, node_mapping = scheme
115190

116191
for cell in cellblock.data:
117192
cell_nodes = []
@@ -135,6 +210,7 @@ def lin_to_quad(mesh: meshio.Mesh) -> meshio.Mesh:
135210
np.mean(
136211
[mesh.point_data[name][cell[n]] for n in node],
137212
axis=0,
213+
dtype=mesh.point_data[name].dtype,
138214
)
139215
)
140216

@@ -159,3 +235,53 @@ def lin_to_quad(mesh: meshio.Mesh) -> meshio.Mesh:
159235
key: [np.array(d) for d in data] for key, data in new_cell_data.items()
160236
},
161237
)
238+
239+
240+
def lin_to_quad(mesh: meshio.Mesh) -> meshio.Mesh:
241+
"""Convert linear elements to quadratic elements in a mesh.
242+
243+
This function returns a new mesh in which all linear elements (triangles, quadrilaterals, tetrahedra, and hexahedra)
244+
are converted into their corresponding quadratic elements.
245+
246+
Args:
247+
mesh:
248+
The input mesh containing linear elements.
249+
250+
Returns:
251+
The modified mesh with quadratic elements.
252+
"""
253+
254+
def lin_to_quad(cell_type: str) -> Optional[tuple[str, list[list[int]]]]:
255+
"""Returns the node mapping for quadratic elements."""
256+
if cell_type in ["vertex", "hexahedron27", "tetra10", "triangle6", "quad9"]:
257+
return None
258+
259+
return _lin_to_quad_cell_type[cell_type], _lin_to_quad_nodes[cell_type]
260+
261+
return adapt_mesh(mesh, lin_to_quad)
262+
263+
264+
def to_serendipity(mesh: meshio.Mesh) -> meshio.Mesh:
265+
"""Converts elements to serendipity elements in a mesh.
266+
267+
Args:
268+
mesh:
269+
The input mesh containing linear elements.
270+
271+
Returns:
272+
The modified mesh with serendipity elements.
273+
"""
274+
275+
def _to_serendipity(cell_type: str) -> Optional[tuple[str, list[list[int]]]]:
276+
"""Returns the node mapping for serendipity elements."""
277+
if cell_type in ["hexahedron20"]:
278+
return None
279+
280+
if cell_type not in ["hexahedron"]:
281+
raise ValueError(
282+
f"Cell type {cell_type} not supported for serendipity conversion."
283+
)
284+
285+
return "hexahedron20", _hex8_to_hex20
286+
287+
return adapt_mesh(mesh, _to_serendipity)

0 commit comments

Comments
 (0)