Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
testPolyhedralMesh.cpp
Go to the documentation of this file.
1#include <PolyhedralMesh.h>
2#include <MeshModule.h>
3#include <Eigen/Geometry>
4#include <algorithm>
5#include <iostream>
6#include <stdexcept>
7
8namespace
9{
10 using V = Eigen::Vector3d;
11 void require(bool ok, const char *message)
12 {
13 if (!ok)
14 {
15 throw std::runtime_error(message);
16 }
17 }
18 double sectionArea(const model::BoundingMeshSegments<3> &section, const model::Plane<3> &plane)
19 {
20 double area = 0;
21 for (size_t k = 0; k < section.size(); ++k)
22 {
23 const auto &arc = *section[k];
24 require(arc.P1 == section[(k + 1) % section.size()]->P0, "Section is not exactly connected");
25 require(std::abs((arc.P0 - plane.P).dot(plane.unitNormal)) < 1.e-6, "Section vertex left plane");
26 area += 0.5 * (arc.P0 - plane.P).cross(arc.P1 - plane.P).dot(plane.unitNormal);
27 }
28 require(area >= -1.e-10, "Section orientation is reversed");
29 return area;
30 }
31 // Independent reference: sum planar cuts of the volume tetrahedra.
32 // This geometric ordering is deliberately confined to the test oracle.
33 double referenceArea(const model::Polyhedron<3,3> &region, const model::Plane<3> &plane)
34 {
35 double area = 0;
36 for (const auto *tet : region.simplices())
37 {
38 const auto vertices = tet->vertexPositionMatrix();
39 std::vector<Eigen::Vector2d> points;
40 auto add = [&](const V &p)
41 {
42 const Eigen::Vector2d x = plane.localPosition(p);
43 for (const auto &existing : points)
44 {
45 if ((x - existing).norm() < 1.e-9)
46 {
47 return;
48 }
49 }
50 points.push_back(x);
51 };
52 for (int i = 0; i < 4; ++i)
53 {
54 const double a = (vertices.col(i) - plane.P).dot(plane.unitNormal);
55 if (std::abs(a) < 1.e-10)
56 {
57 add(vertices.col(i));
58 }
59 for (int j = i + 1; j < 4; ++j)
60 {
61 const double b = (vertices.col(j) - plane.P).dot(plane.unitNormal);
62 if (a * b < 0 && std::abs(a) > 1.e-10 && std::abs(b) > 1.e-10)
63 {
64 add(vertices.col(i) + a / (a - b) * (vertices.col(j) - vertices.col(i)));
65 }
66 }
67 }
68 if (points.size() < 3)
69 {
70 continue;
71 }
72 Eigen::Vector2d center = Eigen::Vector2d::Zero();
73 for (const auto &p : points)
74 {
75 center += p / double(points.size());
76 }
77 std::sort(points.begin(), points.end(),
78 [&](const auto &a, const auto &b)
79 {
80 return std::atan2(a.y() - center.y(), a.x() - center.x()) <
81 std::atan2(b.y() - center.y(), b.x() - center.x());
82 });
83 for (size_t k = 0; k < points.size(); ++k)
84 {
85 const auto a = (points[k] - center).eval();
86 const auto b = (points[(k + 1) % points.size()] - center).eval();
87 area += 0.5 * (a.x() * b.y() - a.y() * b.x());
88 }
89 }
90 return area;
91 }
92 void exercise(const std::string &path, const Eigen::Matrix3d &rotation, const V &shift)
93 {
94 std::weak_ptr<model::Polyhedron<3, 0>> weakVertex;
95 std::weak_ptr<model::Polyhedron<3, 2>> weakFace;
96 {
97 model::SimplicialMesh<3> mesh(path, rotation, shift, {-1});
98 const auto &topology = mesh.polyhedralMesh();
99 topology.validate();
100 weakVertex = topology.factory<0>().begin()->second;
101 weakFace = topology.factory<2>().begin()->second;
102 require(topology.factory<3>().size() == mesh.regions().size(), "Missing regions");
103 for (const auto& region : mesh.regions())
104 {
105 require(region.second.get() == &topology.region(region.first), "Parallel region objects remain");
106 require(&region.second->faces() == &region.second->children, "Faces are not the region children");
107 for (const auto* simplex : region.second->simplices())
108 {
109 require(simplex->region == region.second, "Simplex references a different region");
110 }
111 for (const auto& face : region.second->faces())
112 {
113 require(face.second.get() == &topology.face(face.first), "Parallel face objects remain");
114 require(face.second->parents.at(region.first).lock() == region.second, "Face parent is not its region");
115 require(!face.second->simplices().empty(), "Face lost its boundary simplices");
116 }
117 }
118 for (const auto& edge : topology.factory<1>())
119 {
120 require(edge.second->simplices().size() == 1, "Edge does not store its mesh simplex");
121 }
122 for (const auto& vertex : topology.factory<0>())
123 {
124 require(vertex.second->simplices().size() == 1, "Vertex does not store its mesh simplex");
125 }
126 bool sharedJunction = false;
127 for (const auto &edge : topology.factory<1>())
128 {
129 sharedJunction = sharedJunction || edge.second->parents.size() > 2;
130 }
131 require(sharedJunction, "Fixture did not exercise an edge with more than two parent faces");
132 for (const auto &f : topology.factory<2>())
133 {
134 const auto &face = *f.second;
135 require((face.parents.size() == 1) == face.isExternal(), "External classification differs");
136 for (const auto &incidence : face.perimeter)
137 {
138 require(topology.factory<1>().at(incidence.edge->key) == incidence.edge, "Duplicate edge object");
139 for (const auto &vertex : incidence.edge->children)
140 {
141 require(topology.factory<0>().at(vertex.first) == vertex.second, "Duplicate vertex object");
142 require(vertex.second->parents.at(incidence.edge->key).lock() == incidence.edge,
143 "Missing edge parent");
144 }
145 for (const auto &parent : face.parents)
146 {
147 const auto other = incidence.otherFace(parent.first);
148 require(other.otherFace(parent.first).face.lock() == f.second, "Face switch not reciprocal");
149 require(incidence.next().prev().source() == incidence.source(), "Broken perimeter links");
150 }
151 }
152 }
153 for (const auto &r : mesh.regions())
154 {
155 V center = V::Zero();
156 for (const auto *tet : r.second->simplices())
157 {
158 center += tet->center() / double(r.second->simplices().size());
159 }
160 for (const V &localNormal : {V(1, 0.317, -0.219), V(0.731, 1, 0.277)})
161 {
162 const model::Plane<3> plane(center, rotation * localNormal);
163 const model::BoundingMeshSegments<3> section(mesh, r.first, plane);
164 const double expected = referenceArea(*r.second, plane);
165 require(std::abs(sectionArea(section, plane) - expected) < 1.e-7 * std::max(1.0, expected),
166 "Grain contour area differs from tetrahedral cuts");
167 for (const auto &segment : section)
168 {
169 for (const auto *face : segment->faces)
170 {
171 require(r.second->faces().count(face->sID), "Section includes another grain's face");
172 }
173 }
174 const model::Plane<3> opposite(center, -rotation * localNormal);
175 require(std::abs(sectionArea(model::BoundingMeshSegments<3>(mesh, r.first, opposite), opposite) -
176 expected) < 1.e-7 * std::max(1.0, expected),
177 "Reversed normal changed contour area");
178 }
179 const auto &face = *topology.region(r.first).children.begin()->second;
180 const auto &incidence = face.perimeter.front();
181 const V vertex = incidence.source()->position;
182 const model::Plane<3> vertexPlane(vertex, rotation * V(0.319, 0.711, 1.0));
183 const double expected = referenceArea(*r.second, vertexPlane);
184 require(std::abs(sectionArea(model::BoundingMeshSegments<3>(mesh, r.first, vertexPlane), vertexPlane) -
185 expected) < 1.e-7 * std::max(1.0, expected),
186 "Vertex-hit contour changed area");
187 const V normal = incidence.tangent().cross(rotation * V(0.419, 0.833, 0.231));
188 const model::Plane<3> edgePlane(vertex, normal);
189 const auto edgeSection = model::BoundingMeshSegments<3>(mesh, r.first, edgePlane);
190 sectionArea(edgeSection, edgePlane);
191 if (!edgeSection.empty())
192 {
193 bool shared = false;
194 for (const auto &segment : edgeSection)
195 {
196 shared = shared || segment->faces.size() == 2;
197 }
198 require(shared, "Coincident edge lost its two supporting faces");
199 }
200 }
201 }
202 require(weakVertex.expired() && weakFace.expired(), "Polyhedral topology contains an ownership cycle");
203 }
204} // namespace
205int main(int argc, char **argv)
206{
207 try
208 {
209 require(argc == 2, "Expected repository path");
210 const std::string meshes = std::string(argv[1]) + "/Library/Meshes/";
211 const Eigen::Matrix3d rotation = Eigen::AngleAxisd(0.71, V(1, 2, 3).normalized()).toRotationMatrix();
212 for (const std::string name : {"bicrystal_48.msh", "polycrystalCube_10_periodicX.msh",
213 "polycrystalCube_10_periodicXY.msh", "poly30_periodicXYZ.msh"})
214 {
215 exercise(meshes + name, Eigen::Matrix3d::Identity(), V::Zero());
216 exercise(meshes + name, rotation, V(10, -5, 3));
217 }
218 // A coincident face, edge tangency and vertex tangency of the box.
219 model::SimplicialMesh<3> mesh(meshes + "bicrystal_48.msh", Eigen::Matrix3d::Identity(), V::Zero(), {});
220 for (const auto &r : mesh.regions())
221 {
222 V lo = V::Constant(std::numeric_limits<double>::max());
223 V hi = -lo;
224 for (const auto *tet : r.second->simplices())
225 {
226 for (const auto *vertex : tet->vertices())
227 {
228 lo = lo.cwiseMin(vertex->P0);
229 hi = hi.cwiseMax(vertex->P0);
230 }
231 }
232 const model::Plane<3> facePlane(lo, V::UnitZ());
233 const auto section = model::BoundingMeshSegments<3>(mesh, r.first, facePlane);
234 require(std::abs(sectionArea(section, facePlane) - (hi.x() - lo.x()) * (hi.y() - lo.y())) < 1.e-8,
235 "Coincident face area incorrect");
236 require(model::BoundingMeshSegments<3>(mesh, r.first, model::Plane<3>(lo, V::Ones())).empty(),
237 "Vertex tangency is not empty");
238 require(model::BoundingMeshSegments<3>(mesh, r.first, model::Plane<3>(lo, V(1, 1, 0))).empty(),
239 "Edge tangency is not empty");
240 }
241 std::weak_ptr<model::Polyhedron<3,3>> oldRegion = mesh.regions().begin()->second;
242 std::weak_ptr<model::Polyhedron<3, 0>> oldVertex = mesh.polyhedralMesh().factory<0>().begin()->second;
243 mesh.readMesh(meshes + "bicrystal_48.msh", rotation, V(1, 2, 3), {});
244 require(oldRegion.expired(), "Reload retained the previous region objects");
245 require(oldVertex.expired(), "Reload retained the previous topology");
246 mesh.polyhedralMesh().validate();
247 std::cout << "PASS factory sharing, reciprocal incidences, grain contours, degeneracies and ownership\n";
248 }
249 catch (const std::exception &error)
250 {
251 std::cerr << error.what() << '\n';
252 return 1;
253 }
254}
Eigen::Vector3d V
Polyhedron< _dim, _dim > * region(const size_t &id)
function int_R syms a b c u real f
Definition int_R.m:4
a
Definition int_R.m:14
b
Definition int_R.m:16
x
Definition int_R.m:12
auto eval(const std::shared_ptr< T > &t)
float r
Definition plotLoops.py:41
int regions
Definition triangle.cpp:696
const MeshFacesContainerType & faces() const
Definition Polyhedron.h:136
const SimplexContainerType & simplices() const
Definition Polyhedron.h:128
void exercise(const std::filesystem::path &root, int rank, bool clamp)
void require(bool value, const char *message)
int j
int parent
void argv
void b int argc
vertex p[3]
int k
int i
REAL * vertex
Definition triangle.cpp:542
REAL area
void vertices