Skip to content

Commit f44118a

Browse files
authored
Merge pull request #944 from nschloe/better-prunes
some additions for _mesh
2 parents 999f01b + d44b122 commit f44118a

File tree

6 files changed

+58
-29
lines changed

6 files changed

+58
-29
lines changed

meshio/_common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
"tetra286": 286,
8484
}
8585

86-
_geometric_dimension = {
86+
_topological_dimension = {
8787
"line": 1,
8888
"triangle": 2,
8989
"quad": 2,

meshio/_mesh.py

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import numpy
44

5+
from ._common import _topological_dimension
6+
57

68
class CellBlock(collections.namedtuple("CellBlock", ["type", "data"])):
79
def __repr__(self):
@@ -77,30 +79,39 @@ def __repr__(self):
7779

7880
return "\n".join(lines)
7981

80-
def prune(self):
81-
prune_list = ["vertex", "line", "line3"]
82-
if any([c.type in ["tetra", "tetra10"] for c in self.cells]):
83-
prune_list += ["triangle", "triangle6"]
84-
82+
def remove_lower_dimensional_cells(self):
83+
"""Remove all cells of topological dimension lower than the max dimension in the
84+
mesh, i.e., in a mesh that contains tetrahedra, remove triangles, lines, etc.
85+
"""
86+
max_topological_dim = max(_topological_dimension[c.type] for c in self.cells)
8587
new_cells = []
8688
new_cell_data = {}
87-
for block_counter, c in enumerate(self.cells):
88-
if c.type not in prune_list:
89+
new_cell_sets = {}
90+
prune_set = set()
91+
for idx, c in enumerate(self.cells):
92+
if _topological_dimension[c.type] == max_topological_dim:
8993
new_cells.append(c)
94+
9095
for name, data in self.cell_data.items():
9196
if name not in new_cell_data:
9297
new_cell_data[name] = []
93-
new_cell_data[name] += [data[block_counter]]
98+
new_cell_data[name] += [data[idx]]
99+
100+
for name, data in self.cell_sets.items():
101+
if name not in new_cell_sets:
102+
new_cell_sets[name] = []
103+
new_cell_sets[name] += [data[idx]]
104+
else:
105+
prune_set.add(c.type)
94106

95107
self.cells = new_cells
96108
self.cell_data = new_cell_data
109+
self.cell_sets = new_cell_sets
110+
return prune_set
97111

98-
pruned = ", ".join(prune_list)
99-
print(f"Pruned cell types: {pruned}")
100-
101-
# remove_orphaned_nodes.
102-
# find which nodes are not mentioned in the cells and remove them
103-
all_cells_flat = numpy.concatenate([c.data for c in self.cells]).flatten()
112+
def remove_orphaned_nodes(self):
113+
"""Remove nodes which don't belong to any cell."""
114+
all_cells_flat = numpy.concatenate([c.data.flat for c in self.cells])
104115
orphaned_nodes = numpy.setdiff1d(numpy.arange(len(self.points)), all_cells_flat)
105116
self.points = numpy.delete(self.points, orphaned_nodes, axis=0)
106117
# also adapt the point data
@@ -125,6 +136,13 @@ def prune(self):
125136
self.cells[k] = CellBlock(c.type, all_cells_flat[k : k + n].reshape(s))
126137
k += n
127138

139+
def prune_z_0(self, tol=1.0e-13):
140+
"""Remove third (z) component of points if it is 0 everywhere (up to a
141+
tolerance).
142+
"""
143+
if self.points.shape[1] == 3 and numpy.all(numpy.abs(self.points[:, 2]) < tol):
144+
self.points = self.points[:, :2]
145+
128146
def write(self, path_or_buf, file_format=None, **kwargs):
129147
# avoid circular import
130148
from ._helpers import write
@@ -204,14 +222,23 @@ def sets_to_int_data(self):
204222
# If possible, convert cell sets to integer cell data. This is possible if all
205223
# cells appear exactly in one group.
206224
intfun = []
207-
for c in zip(*self.cell_sets.values()):
225+
for k, c in enumerate(zip(*self.cell_sets.values())):
226+
# `c` contains the values of all cell sets for a particular cell block
227+
c = [([] if cc is None else cc) for cc in c]
208228
# check if all numbers appear exactly once in the groups
209229
d = numpy.sort(numpy.concatenate(c))
210-
is_convertible = numpy.all(d[1:] == d[:-1] + 1) and len(d) == d[-1] + 1
211-
if is_convertible:
212-
intfun.append(numpy.zeros(len(d), dtype=int))
230+
if numpy.all(d == numpy.arange(len(d))):
231+
arr = numpy.empty(len(d), dtype=int)
232+
arr[:] = numpy.nan
213233
for k, cc in enumerate(c):
214-
intfun[-1][cc] = k
234+
arr[cc] = k
235+
else:
236+
# We could just append None, but some mesh formats expect _something_
237+
# here. Go for an array of NaNs.
238+
arr = numpy.empty(len(self.cells[k]), dtype=int)
239+
arr[:] = numpy.nan
240+
241+
intfun.append(arr)
215242

216243
data_name = "-".join(self.cell_sets.keys())
217244
self.cell_data = {data_name: intfun}

meshio/gmsh/_gmsh40.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import numpy
99

1010
from .._common import (
11-
_geometric_dimension,
11+
_topological_dimension,
1212
cell_data_from_raw,
1313
num_nodes_per_cell,
1414
raw_from_cell_data,
@@ -344,7 +344,7 @@ def _write_elements(fh, cells, binary):
344344
for cell_type, node_idcs in cells:
345345
# tagEntity(int) dimEntity(int) typeEle(int) numElements(unsigned long)
346346
numpy.array(
347-
[1, _geometric_dimension[cell_type], _meshio_to_gmsh_type[cell_type]],
347+
[1, _topological_dimension[cell_type], _meshio_to_gmsh_type[cell_type]],
348348
dtype=c_int,
349349
).tofile(fh)
350350
numpy.array([node_idcs.shape[0]], dtype=c_ulong).tofile(fh)
@@ -377,7 +377,7 @@ def _write_elements(fh, cells, binary):
377377
fh.write(
378378
"{} {} {} {}\n".format(
379379
1, # tag
380-
_geometric_dimension[cell_type],
380+
_topological_dimension[cell_type],
381381
_meshio_to_gmsh_type[cell_type],
382382
node_idcs.shape[0],
383383
).encode("utf-8")

meshio/gmsh/_gmsh41.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import numpy
99

1010
from .._common import (
11-
_geometric_dimension,
11+
_topological_dimension,
1212
cell_data_from_raw,
1313
num_nodes_per_cell,
1414
raw_from_cell_data,
@@ -349,7 +349,7 @@ def _write_nodes(fh, points, cells, float_fmt, binary):
349349
# TODO Not sure what to do if there are multiple element types present.
350350
if len(cells) != 1:
351351
raise WriteError("Can only deal with one cell type for now")
352-
dim_entity = _geometric_dimension[cells[0][0]]
352+
dim_entity = _topological_dimension[cells[0][0]]
353353
entity_tag = 0
354354

355355
# write all points as one big block
@@ -424,7 +424,7 @@ def _write_elements(fh, cells, binary):
424424
for cell_type, node_idcs in cells:
425425
# entityDim(int) entityTag(int) elementType(int)
426426
# numElementsBlock(size_t)
427-
dim = _geometric_dimension[cell_type]
427+
dim = _topological_dimension[cell_type]
428428
entity_tag = 0
429429
cell_type = _meshio_to_gmsh_type[cell_type]
430430
numpy.array([dim, entity_tag, cell_type], dtype=c_int).tofile(fh)
@@ -458,7 +458,7 @@ def _write_elements(fh, cells, binary):
458458
tag0 = 1
459459
for cell_type, node_idcs in cells:
460460
# entityDim(int) entityTag(int) elementType(int) numElementsBlock(size_t)
461-
dim = _geometric_dimension[cell_type]
461+
dim = _topological_dimension[cell_type]
462462
entity_tag = 0
463463
cell_type = _meshio_to_gmsh_type[cell_type]
464464
n = node_idcs.shape[0]

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = meshio
3-
version = 4.2.2
3+
version = 4.3.0
44
author = Nico Schlömer et al.
55
author_email = [email protected]
66
description = I/O for many mesh formats

test/test_mesh.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ def test_public_attributes():
1616
def test_print_prune():
1717
mesh = copy.deepcopy(helpers.tri_mesh)
1818
print(mesh)
19-
mesh.prune()
19+
mesh.remove_lower_dimensional_cells()
20+
mesh.remove_orphaned_nodes()
21+
mesh.prune_z_0()
2022

2123

2224
def test_cells_dict():

0 commit comments

Comments
 (0)