Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
SurfaceMeshGeometry.h
Go to the documentation of this file.
1/* This file is part of MoDELib, distributed under GNU GPL v2. */
2#ifndef model_SurfaceMeshGeometry_H_
3#define model_SurfaceMeshGeometry_H_
4#include <SimplicialMesh.h>
5#include <Eigen/Dense>
6#include <array>
7#include <cmath>
8#include <map>
9#include <set>
10#include <stdexcept>
11#include <vector>
12
13namespace model
14{
15 // A conforming refinement of the actual boundary simplices, not their convex hull.
16 // Coordinates remain in simulation length units [b]. Never deform the reference mesh.
18 {
19 using Vector = Eigen::Vector3d;
20 using Triangle = std::array<size_t, 3>;
21 std::vector<Vector> referencePoints;
22 std::vector<const Simplex<3, 3> *> parents;
23 std::vector<Triangle> triangles;
24 std::vector<const Simplex<3, 2> *> boundaryFaces;
25 std::vector<Vector> samplePoints;
26 std::vector<Triangle> walls;
27
28 struct Trace
29 {
32 std::set<const Simplex<3, 2> *> faces;
33 };
34
35 static const Simplex<3,3>* samplingParent(const Simplex<3,2>* face)
36 {
37 const Simplex<3,3>* result=nullptr;
38 for(const auto& entry : face->parents())
39 {
40 if(!result || entry.second->region->regionID<result->region->regionID)
41 {
42 result=entry.second.get();
43 }
44 }
45 return result;
46 }
47
48 // Default to the exterior; explicit selection can include internal faces.
49 static SurfaceMeshGeometry build(const SimplicialMesh<3>& mesh,double maxArea)
50 {
51 std::set<size_t> selected;
52 for(const auto& region : mesh.regions())
53 {
54 for(const auto& face : region.second->faces())
55 {
56 if(face.second->isExternal())
57 {
58 selected.insert(face.second->sID);
59 }
60 }
61 }
62 return build(mesh,maxArea,selected);
63 }
64
65 static SurfaceMeshGeometry build(const SimplicialMesh<3> &mesh, double maxArea,
66 const std::set<size_t> &selectedFaceIDs)
67 {
68 if(!std::isfinite(maxArea) || maxArea <= 0.0)
69 {
70 throw std::invalid_argument("Maximum triangle area must be finite and positive");
71 }
72 std::set<const Simplex<3, 2> *> selectedSimplices;
73 for(const auto &region : mesh.regions())
74 {
75 for(const auto &face : region.second->faces())
76 {
77 if(selectedFaceIDs.count(face.second->sID))
78 {
79 selectedSimplices.insert(face.second->simplices().begin(),face.second->simplices().end());
80 }
81 }
82 }
83 std::vector<const Simplex<3, 2> *> faces(selectedSimplices.begin(),selectedSimplices.end());
84 double largestArea=0.0;
85 for(const auto* face : faces)
86 {
87 largestArea=std::max(largestArea,face->vol0);
88 }
89 if(faces.empty())
90 {
91 return {};
92 }
93 // A common subdivision count makes all shared edge samples identical.
94 const double subdivisions = std::max(1.0, std::ceil(std::sqrt(largestArea / maxArea)));
95 if(!std::isfinite(subdivisions) || subdivisions * subdivisions * faces.size() > 500000)
96 {
97 throw std::invalid_argument("Surface would exceed 500000 triangles; increase mesh size");
98 }
99 const size_t n = static_cast<size_t>(subdivisions);
101 using Key = std::vector<std::pair<size_t, size_t>>;
102 std::map<Key, size_t> pointIDs;
103 for(const auto *face : faces)
104 {
105 const auto vertices = face->vertices();
106 std::map<std::pair<size_t, size_t>, size_t> local;
107 for(size_t i = 0; i <= n; ++i)
108 {
109 for(size_t j = 0; j <= n - i; ++j)
110 {
111 const std::array<size_t, 3> weights{n - i - j, i, j};
112 Key key;
113 Vector position = Vector::Zero();
114 for(size_t k = 0; k < 3; ++k)
115 {
116 if(weights[k] != 0)
117 {
118 key.emplace_back(face->xID[k], weights[k]);
119 position += (static_cast<double>(weights[k]) / n) * vertices[k]->P0;
120 }
121 }
122 const auto inserted = pointIDs.emplace(key, result.referencePoints.size());
123 if(inserted.second)
124 {
125 result.referencePoints.push_back(position);
126 result.parents.push_back(samplingParent(face));
127 }
128 local.emplace(std::make_pair(i, j), inserted.first->second);
129 }
130 }
131 const Vector normal = face->outNormal(samplingParent(face)->region->regionID);
132 auto append = [&](size_t a, size_t b, size_t c)
133 {
134 if((result.referencePoints[b] - result.referencePoints[a])
135 .cross(result.referencePoints[c] - result.referencePoints[a])
136 .dot(normal) < 0)
137 {
138 std::swap(b, c);
139 }
140 result.triangles.push_back({a, b, c});
141 result.boundaryFaces.push_back(face);
142 };
143 for(size_t i = 0; i < n; ++i)
144 {
145 for(size_t j = 0; j < n - i; ++j)
146 {
147 append(local.at({i, j}), local.at({i + 1, j}), local.at({i, j + 1}));
148 if(i + j + 1 < n)
149 {
150 append(local.at({i + 1, j}), local.at({i + 1, j + 1}), local.at({i, j + 1}));
151 }
152 }
153 }
154 }
155 result.samplePoints = result.referencePoints;
156 return result;
157 }
158
159 // Cut each refined boundary triangle into convex regions. Coincident trace
160 // vertices deliberately have separate IDs and one-sided field samples.
161 SurfaceMeshGeometry split(const std::vector<Trace> &inputTraces) const
162 {
163 if(inputTraces.empty())
164 {
165 return *this;
166 }
167 double scale = 1.0;
168 for(const auto &p : referencePoints)
169 {
170 scale = std::max(scale, p.norm());
171 }
172 const double tolerance = scale * 1.e-10;
173 std::vector<Trace> traces;
174 for(const auto &trace : inputTraces)
175 {
176 bool merged = false;
177 for(auto &existing : traces)
178 {
179 if(existing.normal.cross(trace.normal).norm() < 1.e-10 &&
180 std::abs(existing.normal.dot(trace.point - existing.point)) < tolerance)
181 {
182 existing.faces.insert(trace.faces.begin(), trace.faces.end());
183 merged = true;
184 break;
185 }
186 }
187 if(!merged)
188 {
189 traces.push_back(trace);
190 }
191 }
193 using Polygon = std::vector<Vector>;
194 using EdgeKey = std::array<long long, 10>;
195 struct Edge
196 {
197 size_t a;
198 size_t b;
199 bool positive;
200 };
201 std::map<EdgeKey, Edge> seams;
202 for(size_t t = 0; t < triangles.size(); ++t)
203 {
204 const auto *face = boundaryFaces[t];
205 const auto &tri = triangles[t];
206 std::vector<size_t> active;
207 for(size_t k = 0; k < traces.size(); ++k)
208 {
209 if(traces[k].faces.count(face))
210 {
211 active.push_back(k);
212 }
213 }
214 std::vector<Polygon> polygons{
216 for(const auto k : active)
217 {
218 const auto &trace = traces[k];
219 std::vector<Polygon> next;
220 for(const auto &polygon : polygons)
221 {
222 double lo = 0.0;
223 double hi = 0.0;
224 for(const auto &p : polygon)
225 {
226 const double d = trace.normal.dot(p - trace.point);
227 lo = std::min(lo, d);
228 hi = std::max(hi, d);
229 }
230 if(lo >= -tolerance || hi <= tolerance)
231 {
232 next.push_back(polygon);
233 continue;
234 }
235 Polygon positive;
236 Polygon negative;
237 for(size_t j = 0; j < polygon.size(); ++j)
238 {
239 const Vector &a = polygon[j];
240 const Vector &b = polygon[(j + 1) % polygon.size()];
241 double da = trace.normal.dot(a - trace.point);
242 double db = trace.normal.dot(b - trace.point);
243 if(std::abs(da) <= tolerance)
244 {
245 da = 0.0;
246 }
247 if(std::abs(db) <= tolerance)
248 {
249 db = 0.0;
250 }
251 if(da >= 0.0)
252 {
253 positive.push_back(a);
254 }
255 if(da <= 0.0)
256 {
257 negative.push_back(a);
258 }
259 if(da * db < 0.0)
260 {
261 const Vector intersection = a + da / (da - db) * (b - a);
262 positive.push_back(intersection);
263 negative.push_back(intersection);
264 }
265 }
266 next.push_back(std::move(positive));
267 next.push_back(std::move(negative));
268 }
269 polygons = std::move(next);
270 if(polygons.size() + result.triangles.size() > 500000)
271 {
272 throw std::runtime_error("Too many slip-trace cuts; increase surface mesh size");
273 }
274 }
275 for(const auto &polygon : polygons)
276 {
277 Vector center = Vector::Zero();
278 for(const auto &p : polygon)
279 {
280 center += p;
281 }
282 center /= polygon.size();
283 const size_t first = result.referencePoints.size();
284 for(const auto &p : polygon)
285 {
286 double fraction = 0.0;
287 for(const auto k : active)
288 {
289 if(std::abs(traces[k].normal.dot(p - traces[k].point)) <= tolerance)
290 {
291 const double distance = std::abs(traces[k].normal.dot(center - p));
292 // Stay outside the relative FLT_EPSILON dead band used
293 // by the dislocation solid-angle evaluation.
294 if(distance > tolerance)
295 {
296 fraction = std::max(fraction, std::min(0.25, 2.e-6 * scale / distance));
297 }
298 }
299 }
300 result.referencePoints.push_back(p);
301 // A convex combination stays in the original boundary simplex
302 // and its FEM parent, including at intersecting slip traces.
303 result.samplePoints.push_back(Vector(p + fraction * (center - p)));
304 result.parents.push_back(samplingParent(face));
305 }
306 for(size_t j = 1; j + 1 < polygon.size(); ++j)
307 {
308 result.triangles.push_back({first, first + j, first + j + 1});
309 result.boundaryFaces.push_back(face);
310 }
311 for(size_t j = 0; j < polygon.size(); ++j)
312 {
313 const size_t next = (j + 1) % polygon.size();
314 const Vector &a = polygon[j];
315 const Vector &b = polygon[next];
316 for(const auto k : active)
317 {
318 const auto &trace = traces[k];
319 const double side = trace.normal.dot(center - trace.point);
320 if(std::abs(side) <= tolerance || std::abs(trace.normal.dot(a - trace.point)) > tolerance ||
321 std::abs(trace.normal.dot(b - trace.point)) > tolerance)
322 {
323 continue;
324 }
325 std::array<long long, 3> qa, qb;
326 for(size_t d = 0; d < 3; ++d)
327 {
328 qa[d] = std::llround(a[d] / tolerance);
329 qb[d] = std::llround(b[d] / tolerance);
330 }
331 if(qb < qa)
332 {
333 std::swap(qa, qb);
334 }
335 const Vector normal = face->outNormal(samplingParent(face)->region->regionID);
336 const EdgeKey key{static_cast<long long>(k),
337 qa[0],
338 qa[1],
339 qa[2],
340 qb[0],
341 qb[1],
342 qb[2],
343 std::llround(normal[0] * 1.e8),
344 std::llround(normal[1] * 1.e8),
345 std::llround(normal[2] * 1.e8)};
346 const Edge edge{first + j, first + next, side > 0.0};
347 const auto inserted = seams.emplace(key, edge);
348 if(!inserted.second)
349 {
350 const auto other = inserted.first->second;
351 if(other.positive != edge.positive)
352 {
353 // Adjacent outward polygons traverse their common edge
354 // oppositely. Reverse those edges on the connecting wall.
355 result.walls.push_back({other.b, other.a, edge.b});
356 result.walls.push_back({other.b, edge.b, edge.a});
357 }
358 seams.erase(inserted.first);
359 }
360 }
361 }
362 if(result.triangles.size() + result.walls.size() > 500000)
363 {
364 throw std::runtime_error("Surface with slip walls exceeds 500000 triangles");
365 }
366 }
367 }
368 return result;
369 }
370
371 std::vector<Vector> displaced(const std::vector<Vector> &displacement, double amplification) const
372 {
373 if(displacement.size() != referencePoints.size() || !std::isfinite(amplification) || amplification < 0)
374 {
375 throw std::invalid_argument("Invalid surface displacement or amplification");
376 }
377 std::vector<Vector> points;
378 points.reserve(referencePoints.size());
379 for(size_t k = 0; k < referencePoints.size(); ++k)
380 {
381 const Vector point = referencePoints[k] + amplification * displacement[k];
382 if(!point.allFinite())
383 {
384 throw std::runtime_error("Nonfinite surface displacement; check the field or amplification");
385 }
386 points.push_back(point);
387 }
388 return points;
389 }
390 };
391} // namespace model
392#endif
ParentContainerType parents() const
final point of the source segment t
c
Definition int_R.m:15
a
Definition int_R.m:14
b
Definition int_R.m:16
const Eigen::MatrixXd Quadrature< dim, qOrder, QuadratureRule >::weights
The static const member data abscissas is initialized only once when Quadrature<1,...
Definition Quadrature.h:239
int regions
Definition triangle.cpp:696
std::set< const Simplex< 3, 2 > * > faces
std::vector< const Simplex< 3, 3 > * > parents
std::vector< Vector > referencePoints
std::vector< Triangle > walls
static SurfaceMeshGeometry build(const SimplicialMesh< 3 > &mesh, double maxArea, const std::set< size_t > &selectedFaceIDs)
std::vector< Vector > samplePoints
static SurfaceMeshGeometry build(const SimplicialMesh< 3 > &mesh, double maxArea)
std::array< size_t, 3 > Triangle
std::vector< const Simplex< 3, 2 > * > boundaryFaces
std::vector< Vector > displaced(const std::vector< Vector > &displacement, double amplification) const
std::vector< Triangle > triangles
static const Simplex< 3, 3 > * samplingParent(const Simplex< 3, 2 > *face)
SurfaceMeshGeometry split(const std::vector< Trace > &inputTraces) const
newotri tri
int j
vertex p[3]
int k
int i
newbad key
result
vertex first
void vertices