Skip to content

Commit 9c87116

Browse files
authored
Tools for matching topological entities (#1117)
* Add Mesh.p2f, Mesh.p2e, Mesh.p2t * use p2f to match boundaries in from_meshio * optimize also loading of oriented boundaries
1 parent 5ce46d9 commit 9c87116

File tree

5 files changed

+368
-52
lines changed

5 files changed

+368
-52
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,11 @@ with respect to documented and/or tested features.
212212
### Unreleased
213213

214214
- Fixed: Tests now pass with `numpy==2.0rc1`
215-
- Fixed: `MappingAffine` uses lazy evaluation also for element mappings
216-
- Fixed: `MeshTet.init_tensor` uses less memory for large meshes
215+
- Fixed: `MappingAffine` now uses lazy evaluation also for element
216+
mappings, in addition to boundary mappings
217+
- Fixed: `MeshTet.init_tensor` uses significantly less memory for
218+
large meshes
219+
- Fixed: `Mesh.load` uses less memory when loading and matching tags
217220
- Added: `Basis` has new optional `disable_doflocs` kwarg which
218221
can be set to `True` to avoid computing `Basis.doflocs`, to reduce memory
219222
usage

docs/examples/meshes/tagged_gmsh4.msh

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
$MeshFormat
2+
4.1 0 8
3+
$EndMeshFormat
4+
$PhysicalNames
5+
3
6+
1 6 "tagged"
7+
1 7 "test"
8+
2 8 "all"
9+
$EndPhysicalNames
10+
$Entities
11+
5 5 1 0
12+
1 -0.5 -0.5 0 0
13+
2 0.5 -0.5 0 0
14+
3 0.5 -0.3 0 0
15+
4 0 -0.3 0 0
16+
5 0 1.3 0 0
17+
1 0.5 -0.5 0 0.5 -0.3 0 0 2 2 -3
18+
2 0 -0.3 0 0.5 -0.3 0 0 2 3 -4
19+
3 0 -0.3 0 0 1.3 0 2 6 7 2 4 -5
20+
4 -0.5 -0.5 0 0 1.3 0 0 2 5 -1
21+
5 -0.5 -0.5 0 0.5 -0.5 0 0 2 1 -2
22+
1 -0.5 -0.5 0 0.5 1.3 0 1 8 5 4 5 1 2 3
23+
$EndEntities
24+
$Nodes
25+
11 55 1 55
26+
0 1 0 1
27+
10
28+
-0.5 -0.5 0
29+
0 2 0 1
30+
11
31+
0.5 -0.5 0
32+
0 3 0 1
33+
12
34+
0.5 -0.3 0
35+
0 4 0 1
36+
1
37+
0 -0.3 0
38+
0 5 0 1
39+
2
40+
0 1.3 0
41+
1 1 0 3
42+
13
43+
14
44+
15
45+
0.5 -0.4500000000000726 0
46+
0.5 -0.4000000000002213 0
47+
0.5 -0.3500000000002064 0
48+
1 2 0 3
49+
16
50+
17
51+
18
52+
0.3750000000002064 -0.3 0
53+
0.2500000000006652 -0.3 0
54+
0.1250000000003326 -0.3 0
55+
1 3 0 7
56+
3
57+
4
58+
5
59+
6
60+
7
61+
8
62+
9
63+
0 -0.1000000000003512 0
64+
0 0.09999999999904252 0
65+
0 0.2999999999984534 0
66+
0 0.4999999999978643 0
67+
0 0.6999999999982724 0
68+
0 0.8999999999988308 0
69+
0 1.099999999999313 0
70+
1 4 0 7
71+
19
72+
20
73+
21
74+
22
75+
23
76+
24
77+
25
78+
-0.06249999999991335 1.075000000000312 0
79+
-0.1249999999997748 0.8500000000008107 0
80+
-0.1874999999995735 0.6250000000015353 0
81+
-0.2499999999993722 0.40000000000226 0
82+
-0.3124999999996163 0.1750000000013814 0
83+
-0.3749999999996823 -0.04999999999885629 0
84+
-0.4374999999998545 -0.2749999999994761 0
85+
1 5 0 3
86+
26
87+
27
88+
28
89+
-0.2500000000004945 -0.5 0
90+
-1.312838726619248e-12 -0.5 0
91+
0.2499999999993436 -0.5 0
92+
2 1 0 27
93+
29
94+
30
95+
31
96+
32
97+
33
98+
34
99+
35
100+
36
101+
37
102+
38
103+
39
104+
40
105+
41
106+
42
107+
43
108+
44
109+
45
110+
46
111+
47
112+
48
113+
49
114+
50
115+
51
116+
52
117+
53
118+
54
119+
55
120+
0.25 -0.3999999999999999 0
121+
-0.25 -0.3999999999999999 0
122+
-0.1249999999996861 0.4500000000000621 0
123+
-0.1249999999996861 0.05000000000112997 0
124+
0.3750000000003326 -0.3500000000001105 0
125+
0.2500000000003326 -0.3499999999999999 0
126+
0.375 -0.4000000000001105 0
127+
0.125 -0.3499999999999999 0
128+
0.375 -0.4499999999999999 0
129+
0.1249999999993436 -0.4499999999999999 0
130+
0 -0.3999999999999999 0
131+
-0.1250000000006564 -0.4499999999999999 0
132+
-0.125 -0.3499999999999999 0
133+
-0.375 -0.4499999999999999 0
134+
-0.1874999999995292 0.4250000000011609 0
135+
-0.1249999999997305 0.6500000000004362 0
136+
-0.06249999999984306 0.6749999999994464 0
137+
-0.06249999999988741 0.8749999999998205 0
138+
-0.06249999999984306 0.4749999999989631 0
139+
-0.06249999999984306 0.07500000000008623 0
140+
-0.06249999999984306 -0.124999999999435 0
141+
-0.06249999999984306 0.2749999999995523 0
142+
-0.1249999999996861 0.250000000000596 0
143+
-0.1874999999995292 0.2250000000016949 0
144+
-0.3124999999998411 -0.2249999999994281 0
145+
-0.1874999999998431 -0.1749999999994349 0
146+
-0.2499999999996842 1.136840621640544e-12 0
147+
$EndNodes
148+
$Elements
149+
2 88 1 113
150+
1 3 1 8
151+
1 1 3
152+
2 3 4
153+
3 4 5
154+
4 5 6
155+
5 6 7
156+
6 7 8
157+
7 8 9
158+
8 9 2
159+
2 1 2 80
160+
34 12 16 15
161+
35 16 33 15
162+
36 16 17 33
163+
37 15 33 14
164+
38 17 34 33
165+
39 34 35 33
166+
40 34 29 35
167+
41 33 35 14
168+
42 17 18 34
169+
43 18 36 34
170+
44 18 1 36
171+
45 34 36 29
172+
46 14 35 13
173+
47 35 37 13
174+
48 35 29 37
175+
49 13 37 11
176+
50 11 37 28
177+
51 37 38 28
178+
52 37 29 38
179+
53 28 38 27
180+
54 29 39 38
181+
55 39 40 38
182+
56 39 30 40
183+
57 38 40 27
184+
58 29 36 39
185+
59 36 41 39
186+
60 36 1 41
187+
61 39 41 30
188+
62 27 40 26
189+
63 40 42 26
190+
64 40 30 42
191+
65 26 42 10
192+
66 22 43 21
193+
67 43 44 21
194+
68 43 31 44
195+
69 21 44 20
196+
70 31 45 44
197+
71 45 46 44
198+
72 45 8 46
199+
73 44 46 20
200+
74 31 47 45
201+
75 47 7 45
202+
76 47 6 7
203+
77 45 7 8
204+
78 20 46 19
205+
79 46 9 19
206+
80 46 8 9
207+
81 19 9 2
208+
82 1 3 49
209+
83 3 48 49
210+
84 3 4 48
211+
85 49 48 32
212+
86 4 50 48
213+
87 50 51 48
214+
88 50 31 51
215+
89 48 51 32
216+
90 4 5 50
217+
91 5 47 50
218+
92 5 6 47
219+
93 50 47 31
220+
94 32 51 52
221+
95 51 43 52
222+
96 51 31 43
223+
97 52 43 22
224+
98 10 42 25
225+
99 42 53 25
226+
100 42 30 53
227+
101 25 53 24
228+
102 30 54 53
229+
103 54 55 53
230+
104 54 32 55
231+
105 53 55 24
232+
106 30 41 54
233+
107 41 49 54
234+
108 41 1 49
235+
109 54 49 32
236+
110 24 55 23
237+
111 55 52 23
238+
112 55 32 52
239+
113 23 52 22
240+
$EndElements

skfem/io/meshio.py

Lines changed: 42 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Import/export any formats supported by meshio."""
22

3+
import logging
34
from pathlib import Path
45

56
import meshio
@@ -10,6 +11,9 @@
1011
from skfem.generic_utils import OrientedBoundary
1112

1213

14+
logger = logging.getLogger(__name__)
15+
16+
1317
MESH_TYPE_MAPPING = {
1418
'tetra': MeshTet1,
1519
'tetra10': MeshTet2,
@@ -54,6 +58,9 @@ def from_meshio(m,
5458
ignore_orientation=False,
5559
ignore_interior_facets=False):
5660

61+
if ignore_interior_facets:
62+
logger.warning("kwarg ignore_interior_facets is unused.")
63+
5764
cells = m.cells_dict
5865
meshio_type = None
5966

@@ -121,56 +128,42 @@ def from_meshio(m,
121128

122129
# parse boundaries from cell_sets
123130
if m.cell_sets and bnd_type in m.cells_dict:
124-
oriented_facets = {
125-
k: [tuple(f) for f in m.cells_dict[bnd_type][v[bnd_type]]]
126-
for k, v in m.cell_sets_dict.items()
127-
if bnd_type in v and k.split(":")[0] != "gmsh"
128-
}
129-
sorted_facets = {k: [tuple(np.sort(f)) for f in v]
130-
for k, v in oriented_facets.items()}
131-
for k, v in oriented_facets.items():
132-
if ignore_orientation or ignore_interior_facets:
133-
a = np.array(sorted_facets[k])
134-
if ignore_interior_facets:
135-
b = mtmp.facets[:, mtmp.boundary_facets()].T
136-
else:
137-
b = mtmp.facets.T
138-
boundaries[k] = np.nonzero((a == b[:, None])
139-
.all(axis=2)
140-
.any(axis=1))[0]
141-
else:
142-
indices = []
143-
oris = []
144-
for i, f in enumerate(map(tuple, mtmp.facets.T)):
145-
if f in sorted_facets[k]:
146-
indices.append(i)
147-
ix = sorted_facets[k].index(f)
148-
facet = v[ix]
149-
t1, t2 = mtmp.f2t[:, i]
150-
if t2 == -1:
151-
# orientation on boundary is 0
152-
oris.append(0)
153-
continue
154-
if len(f) == 2:
155-
# rotate tangent to find normal
156-
tangent = (mtmp.p[:, facet[1]]
157-
- mtmp.p[:, facet[0]])
158-
normal = np.array([-tangent[1], tangent[0]])
159-
elif len(f) == 3:
160-
# cross product to find normal
161-
tangent1 = (mtmp.p[:, facet[1]]
162-
- mtmp.p[:, facet[0]])
163-
tangent2 = (mtmp.p[:, facet[2]]
164-
- mtmp.p[:, facet[0]])
165-
normal = -np.cross(tangent1, tangent2)
131+
p2f = mtmp.p2f
132+
for k, v in m.cell_sets_dict.items():
133+
if bnd_type in v and k.split(":")[0] != "gmsh":
134+
facets = m.cells_dict[bnd_type][v[bnd_type]].T
135+
sorted_facets = np.sort(facets, axis=0)
136+
ind = p2f[:, sorted_facets[0]]
137+
for itr in range(sorted_facets.shape[0] - 1):
138+
ind = ind.multiply(p2f[:, sorted_facets[itr + 1]])
139+
boundaries[k] = np.nonzero(ind)[0]
140+
141+
if not ignore_orientation:
142+
try:
143+
ori = np.zeros_like(boundaries[k], dtype=np.float64)
144+
t1, _ = mtmp.f2t[:, boundaries[k]]
145+
if facets.shape[0] == 2:
146+
tangents = (mtmp.p[:, facets[1]]
147+
- mtmp.p[:, facets[0]])
148+
normals = np.array([-tangents[1], tangents[0]])
149+
elif facets.shape[0] == 3:
150+
tangents1 = (mtmp.p[:, facets[1]]
151+
- mtmp.p[:, facets[0]])
152+
tangents2 = (mtmp.p[:, facets[2]]
153+
- mtmp.p[:, facets[0]])
154+
normals = -np.cross(tangents1.T, tangents2.T).T
166155
else:
167156
raise NotImplementedError
168-
# find another vector using node outside of boundary
169-
third = np.setdiff1d(mtmp.t[:, t1],
170-
np.array(f))[0]
171-
outplane = mtmp.p[:, f[0]] - mtmp.p[:, third]
172-
oris.append(1 if np.dot(normal, outplane) > 0 else 0)
173-
boundaries[k] = OrientedBoundary(indices, oris)
157+
for itr in range(mtmp.t.shape[0]):
158+
ori += np.sum(normals
159+
* (mtmp.p[:, facets[0]]
160+
- mtmp.p[:, mtmp.t[itr, t1]]),
161+
axis=0)
162+
ori = 1 * (ori > 0)
163+
boundaries[k] = OrientedBoundary(boundaries[k],
164+
ori)
165+
except Exception:
166+
logger.warning("Failure to orient a boundary.")
174167

175168
# MSH 2.2 tag parsing
176169
if len(boundaries) == 0 and m.cell_data and m.field_data:
@@ -212,7 +205,7 @@ def find_tagname(tag):
212205
boundaries[find_tagname(tag)] = index[tagindex, 1]
213206

214207
except Exception:
215-
pass
208+
logger.warning("Failure to parse tags from meshio.")
216209

217210
# attempt parsing skfem tags
218211
if m.cell_data:

0 commit comments

Comments
 (0)