Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
testPythonMesh.py
Go to the documentation of this file.
1"""Dimension-specific mesh bindings, connectivity, periodicity, and ownership."""
2import gc
3import itertools
4from pathlib import Path
5import sys
6import tempfile
7import weakref
8import numpy as np
9
10sys.path.insert(0, sys.argv[1])
11import pyMoDELib as model
12
13
14def box_mesh(path, dim, split=False):
15 vertices = list(itertools.product((0, 1), repeat=dim))
16 indices = {point: k + 1 for k, point in enumerate(vertices)}
17 cells = []
18 for axes in itertools.permutations(range(dim)):
19 point = [0] * dim
20 cell = [indices[tuple(point)]]
21 for axis in axes:
22 point[axis] = 1
23 cell.append(indices[tuple(point)])
24 cells.append(cell)
25 if split:
26 # Two translated copies share a facet, with distinct region IDs.
27 original_count = len(cells)
28 for cell in list(cells):
29 translated = []
30 for vertex_id in cell:
31 point = list(vertices[vertex_id - 1])
32 point[0] += 1
33 point = tuple(point)
34 if point not in indices:
35 indices[point] = len(vertices) + 1
36 vertices.append(point)
37 translated.append(indices[point])
38 cells.append(translated)
39 lines = ["$MeshFormat", "2.2 0 8", "$EndMeshFormat", "$Nodes", str(len(vertices))]
40 for point, index in indices.items():
41 xyz = list(point) + [0] * (3 - dim)
42 lines.append(f"{index} " + " ".join(map(str, xyz)))
43 lines += ["$EndNodes", "$Elements", str(len(cells))]
44 for index, cell in enumerate(cells, 1):
45 region = 2 if split and index > original_count else 1
46 lines.append(f"{index} { {1: 1, 2: 2, 3: 4}[dim]} 2 1 {region} " + " ".join(map(str, cell)))
47 path.write_text("\n".join(lines + ["$EndElements", ""]))
48
49
50def check(dim, path):
51 Mesh = getattr(model, f"SimplicialMesh{dim}")
52 mesh = Mesh(str(path), periodicFaceIDs={-1})
53 assert Mesh.dim == dim
54 assert Mesh().search(np.zeros(dim)) is None
55 np.testing.assert_allclose(mesh.volume(), 1.)
56 assert mesh.periodicBasis().shape == (dim, dim)
57 topology = mesh.polyhedralMesh()
58 assert isinstance(topology, getattr(model, f"PolyhedralMesh{dim}"))
59 topology.validate()
60 for order in range(dim, -1, -1):
61 simplices = mesh.simplices(order)
62 assert simplices
63 for key, simplex in simplices.items():
64 assert isinstance(simplex, getattr(model, f"Simplex{dim}{order}"))
65 assert simplex.xID == key
66 assert simplex.order == order and simplex.dim == dim
67 assert len(simplex.vertices()) == order + 1
68 if order:
69 assert len(simplex.children()) == order + 1
70 for child in simplex.children():
71 assert child.parents()[key] is simplex
72 bary = np.full(order + 1, 1 / (order + 1))
73 np.testing.assert_allclose(simplex.bary2pos(bary), simplex.center())
74 else:
75 np.testing.assert_allclose(simplex.P0, simplex.center())
76 for cell in topology.cells(order).values():
77 assert isinstance(cell, getattr(model, f"Polyhedron{dim}{order}"))
78 if order:
79 for child in cell.children.values():
80 assert child.parents[cell.key] is cell
81 for source, (partner, shift, permutation) in mesh.periodicSimplexPairs().items():
82 original = mesh.simplices(dim - 1)[source]
83 np.testing.assert_allclose(original.center() + shift, partner.center(), atol=1e-12)
84 for i, vertex in enumerate(original.vertices()):
85 np.testing.assert_allclose(vertex.P0 + shift, partner.vertices()[permutation[i]].P0, atol=1e-12)
86 assert mesh.periodicSimplexPairs()
87 root_simplex = mesh.search(np.full(dim, 0.3))
88 assert root_simplex is not None
89 assert mesh.search(np.full(dim, 2.)) is None
90 np.testing.assert_allclose(root_simplex.bary2pos(root_simplex.pos2bary(np.full(dim, 0.3))), np.full(dim, 0.3))
91 assert root_simplex.region is mesh.region(1)
92 physical = Mesh(str(path))
93 assert physical.periodicBasis().shape == (dim, 0)
94 assert not physical.periodicSimplexPairs()
95 faces = physical.polyhedralMesh().cells(dim - 1)
96 selected = next(iter(faces))
97 partial = physical
98 partial.identifyParallelFaces({selected})
99 assert partial.periodicBasis().shape == (dim, 1)
100 reader = getattr(model, f"SimplexReader{dim}")()
101 reader.read(str(path))
102 assert len(reader.elements()) == len(mesh.simplices())
103 scaled = Mesh(str(path), 2 * np.eye(dim), np.full(dim, 0.25))
104 np.testing.assert_allclose(scaled.xMin(), np.full(dim, -0.5))
105 np.testing.assert_allclose(scaled.xMax(), np.full(dim, 1.5))
106 np.testing.assert_allclose(scaled.volume(), 2.**dim)
107 for method in (mesh.simplices, topology.cells):
108 try:
109 method(dim + 1)
110 except ValueError:
111 pass
112 else:
113 raise AssertionError("Invalid order accepted")
114
115
116def lifetime(dim, path):
117 mesh = getattr(model, f"SimplicialMesh{dim}")(str(path))
118 ref = weakref.ref(mesh)
119 cell = mesh.region(1)
120 del mesh
121 gc.collect()
122 assert ref() is not None
123 assert cell.simplexIDs()
124 retained = cell.simplices()[0].vertices()[0]
125 del cell
126 gc.collect()
127 assert ref() is not None
128 assert retained.parents()
129 del retained
130 gc.collect()
131 assert ref() is None, "Mesh ownership cycle"
132
133
134with tempfile.TemporaryDirectory(prefix="modelib-python-mesh-") as directory:
135 for dim in (3, 2, 1):
136 path = Path(directory) / f"box{dim}.msh"
137 box_mesh(path, dim)
138 check(dim, path)
139 lifetime(dim, path)
140 box_mesh(path, dim, split=True)
141 mesh = getattr(model, f"SimplicialMesh{dim}")(str(path))
142 assert len(mesh.regions()) == 2
143 boundary = mesh.regionBoundaries()[(1, 2)]
144 assert isinstance(boundary, getattr(model, f"MeshRegionBoundary{dim}"))
145 assert len(boundary.faces()) == 1
146 for face in boundary.faces().values():
147 assert not face.isExternal()
148 assert set(face.parents) == {1, 2}
149 assert mesh.region(1).faces()[face.key] is mesh.region(2).faces()[face.key]
150 assert boundary.simplices()
151assert model.SimplicialMesh is model.SimplicialMesh3
152assert model.Polyhedron2 is model.Polyhedron32
153print("PASS: Python mesh dimensions 1–3, all orders, periodicity, connectivity, ownership")
check(dim, path)
box_mesh(path, dim, split=False)
lifetime(dim, path)
int regions
Definition triangle.cpp:696
void vertices