HArD::Core2D
Hybrid Arbitrary Degree::Core 2D - Library to implement 2D schemes with edge and cell polynomials as unknowns
Loading...
Searching...
No Matches
MeshBuilder2D.hpp
Go to the documentation of this file.
1// WARNING - this code assumes that cell vertices are listed anti clockwise and that cells do not intersect themselves
2// If these conditions are not met, behaviour is unknown
3
4#ifndef MESHBUILDER2D_HPP
5#define MESHBUILDER2D_HPP
6
7#include <memory> //std::unique_ptr
8#include <MeshReaderTyp2.hpp>
9#include <Polytope2D.hpp>
10#include <Mesh2D.hpp>
11
12namespace Mesh2D
13{
14 inline double shortest_distance(VectorRd p, VectorRd A, VectorRd B) // computes min distance from a point p to a line segment AB
15 {
16 VectorRd line_vec = B - A;
17 VectorRd pnt_vec = p - A;
18
19 double t = line_vec.dot(pnt_vec) / line_vec.squaredNorm();
20
21 if (t < 0.0)
22 t = 0.0;
23 if (t > 1.0)
24 t = 1.0;
25
26 VectorRd nearest = t * line_vec;
27
28 return (nearest - pnt_vec).norm();
29 }
30
31 inline Simplices<2> simplexify(std::vector<VectorRd> vertices)
32 {
33 assert(vertices.size() >= 3);
34
35 Simplices<2> simplices;
36
37 std::size_t pos = 0; // Start at the first vertex (because why not)
38
39 double tolerance = 0.5;
40 // We start with an initially high tolerance to generate as isotropic simplices as possible.
41 // Each time we complete a full loop of the remaining vertices without generating a new simplex, we reduce tolerance
42 // so to allow for less isotropic simplices. Continue until fully simplexified.
43
44 bool simplex_made = true;
45 std::size_t its_without_new_simplex = 0;
46 while (vertices.size() > 3)
47 {
48 if (!simplex_made)
49 {
50 ++its_without_new_simplex;
51 if (its_without_new_simplex > vertices.size())
52 {
53 tolerance /= 2.0; // reduce the tolerance if we have tried every remaining vertex but still haven't created a simplex
54 its_without_new_simplex = 0;
55 }
56 }
57 else
58 {
59 simplex_made = false;
60 its_without_new_simplex = 0;
61 }
62
63 std::size_t pos_next = (pos + 1) % vertices.size();
64 std::size_t pos_last = (pos_next + 1) % vertices.size();
65
66 VectorRd v1 = vertices[pos];
67 VectorRd v2 = vertices[pos_next];
68 VectorRd v3 = vertices[pos_last];
69
70 // If we know cell is convex and contains no hanging nodes, then all of the following checks are pointless
71 // Perhaps in a future iteration we should add an 'is_convex' flag and if true skip the checks
72 // However, it would require prior knowledge of convexity (i.e. an is_convex line in the data file)
73
74 // Candidate simplex is v1,v2,v3. Need to check validity
75
76 if (signed_area(v1, v2, v3) <= 0) // If negative area, trying to simplexify at non-convex vertex - simplex will be outside cell.
77 {
78 pos = pos_next; // skip to next vertex and continue
79 continue;
80 }
81
82 // Next, we make sure we are not generating a more 'squished' triangle then necessary
83 // In the limiting case, this also ensure the simplex isn't trivial (a line)
84
85 double new_edge_length = (v1 - v3).norm();
86
87 if ((new_edge_length > (v1 - v2).norm()) && (new_edge_length > (v2 - v3).norm()))
88 {
89 if (shortest_distance(v2, v1, v3) < tolerance * new_edge_length)
90 {
91 // If new edge is longest edge, and second vert is very near it, triangle is bad
92 // If new edge is not longest edge, then triangle is best as can be (even if it is bad)
93 pos = pos_next; // skip to next vertex and continue
94 continue;
95 }
96 }
97
98 // Finally, we check if any of the remaining verts are in or on the new triangle
99 // or if any of the remaining verts are unneccessarily close to the new triangle
100
101 bool vert_in_triangle = false;
102 for (std::size_t i = 0; i < vertices.size(); ++i)
103 {
104 if ((i == pos) || (i == pos_next) || (i == pos_last))
105 {
106 // skip all vertices of the triangle
107 continue;
108 }
109 VectorRd vert = vertices[i];
110
111 if (signed_area(vert, v3, v1) >= 0.0)
112 {
113 // vert is to the right of (or in line with) the line v1v3 (new_edge)
114 // might be in or on triangle - check
115 // If not, we continue. We don't care about closeness as it must already be closer to a pre existing edge
116 if ((signed_area(vert, v2, v3) > 0.0) && (signed_area(vert, v1, v2) > 0.0))
117 {
118 vert_in_triangle = true;
119 break;
120 }
121 else
122 {
123 continue;
124 }
125 }
126 else
127 {
128 // vert is to the left of the line v1v3 (new_edge)
129 // If vert is too close, triangle is bad
130 if (shortest_distance(vert, v1, v3) < tolerance * new_edge_length)
131 {
132 vert_in_triangle = true; // not actually in triangle, but too close for our liking
133 break;
134 }
135 }
136 }
137
138 if (vert_in_triangle)
139 {
140 pos = pos_next; // skip to next vertex and continue
141 continue;
142 }
143
144 simplices.push_back(Simplex<2>({v1, v2, v3}));
145 vertices.erase(vertices.begin() + pos_next); // erase vertex located at pos_next
146
147 // Want to move to vertex v3 (used to be located at pos_last). However, vertex at pos_next was deleted, so v3 is now located at
148 // pos_next unless pos_last < pos_next (pos_last = 0). In this case pos_next = vertices.size() - 1 so we set pos = pos_last (= 0)
149
150 pos = pos_next % (vertices.size() - 1);
151 simplex_made = true;
152
153 assert(tolerance > 1E-16); // If tolerance gets too small, code has failed - exit.
154 }
155
156 simplices.push_back(Simplex<2>({vertices[0], vertices[1], vertices[2]})); // final simplex is the remaining vertices
157
158 return simplices;
159 }
160
166 // ----------------------------------------------------------------------------
167 // Class definition
168 // ----------------------------------------------------------------------------
169
172 {
173 public:
174 typedef std::function<std::array<double, 2>(const std::array<double, 2> &)> TransformationType;
175
180
184 MeshBuilder(const std::string mesh_file) : _mesh_file(mesh_file) {}
185
189 std::unique_ptr<Mesh> build_the_mesh(const TransformationType & transformation = [](const std::array<double, 2> & x) { return x; })
190 {
191 MeshReaderTyp2 mesh_reader(_mesh_file);
192
193 std::vector<std::array<double, 2>> vertices;
194 std::vector<std::vector<size_t>> cells;
195
196 mesh_reader.read_mesh(vertices, cells);
197 std::transform(vertices.begin(), vertices.end(), vertices.begin(), transformation);
198
199 if (vertices.size() > 0 && cells.size() > 0)
200 {
201 std::unique_ptr<Mesh> mesh = std::make_unique<Mesh>(); // make a pointer to the mesh so that it outlives the builder
202 std::cout << "[MeshBuilder] ";
203
204 // Create vertices
205 for (auto &v : vertices)
206 {
207 VectorRd vert(v[0], v[1]);
208 Vertex *vertex = new Vertex(mesh->n_vertices(), vert);
209 mesh->add_vertex(vertex);
210 }
211
212 // Create cells
213 double total_area = 0.0;
214 for (auto &c : cells)
215 {
216 std::vector<std::size_t> vertex_ids;
217 std::vector<VectorRd> vertex_coords;
218 for (size_t i = 0; i < c.size(); i++)
219 {
220 vertex_ids.push_back(c[i] - 1); // data file starts at 1, so subtract 1
221 VectorRd coord(vertices[c[i] - 1][0], vertices[c[i] - 1][1]);
222 vertex_coords.push_back(coord);
223 }
224
225 Simplices<2> simplices = simplexify(vertex_coords);
226
227 Cell *cell = new Cell(mesh->n_cells(), simplices);
228 total_area += cell->measure();
229 mesh->add_cell(cell);
230
231 for (std::size_t i = 0; i < vertex_ids.size(); ++i)
232 {
233 std::size_t i_next = (i + 1) % vertex_ids.size();
234
235 bool edge_exists = false;
236
237 std::vector<Vertex *> vlist = mesh->vertex(vertex_ids[i])->get_vertices();
238 for (size_t j = 0; j < vlist.size(); j++)
239 {
240 if (vlist[j]->global_index() == vertex_ids[i_next]) // The edge exists
241 {
242 std::vector<Edge *> elist = mesh->vertex(vertex_ids[i])->get_edges();
243 Edge *edge = elist[j];
244
245 cell->add_edge(edge);
246 edge->add_cell(cell);
247 edge_exists = true;
248 break;
249 }
250 }
251
252 if (!edge_exists)
253 {
254 // edge does not exists - so create it
255
256 Edge *edge = new Edge(mesh->n_edges(), Simplex<1>({vertex_coords[i], vertex_coords[i_next]}));
257
258 mesh->add_edge(edge);
259 cell->add_edge(edge);
260 edge->add_cell(cell);
261
262 Vertex *vertex1 = mesh->vertex(vertex_ids[i]);
263 Vertex *vertex2 = mesh->vertex(vertex_ids[i_next]);
264
265 edge->add_vertex(vertex1);
266 edge->add_vertex(vertex2);
267 vertex1->add_edge(edge);
268 vertex2->add_edge(edge);
269 vertex1->add_vertex(vertex2);
270 vertex2->add_vertex(vertex1);
271 }
272 mesh->vertex(vertex_ids[i])->add_cell(cell);
273 cell->add_vertex(mesh->vertex(vertex_ids[i]));
274 }
275
276 cell->construct_face_normals();
277 }
278
279 // build boundary
280 build_boundary(mesh.get());
281 std::cout << "added " << mesh->n_cells() << " cells; Total area = " << total_area << "\n";
282
283 return mesh;
284 }
285 else
286 {
287 throw "Cannot build mesh. Check input file";
288 }
289 return NULL;
290 }
291
292 private:
293 void build_boundary(Mesh *mesh)
294 {
295 for (auto &face : mesh->get_faces())
296 {
297 if (face->n_cells() == 1) // If face has only one cell, it is a boundary face
298 {
299 face->set_boundary(true); // set face boundary to true
300 face->get_cells()[0]->set_boundary(true); // set cell boundary to true
301
302 for (auto &vert : face->get_vertices())
303 {
304 vert->set_boundary(true); // set vertex boundary to true
305 }
306
307 mesh->add_b_face(face); // add face to list of boundary faces
308 }
309 else
310 {
311 // internal face
312 mesh->add_i_face(face); // add face to list of internal faces
313 }
314 }
315
316 for (auto &cell : mesh->get_cells())
317 {
318 if (cell->is_boundary())
319 {
320 mesh->add_b_cell(cell);
321 }
322 else
323 {
324 mesh->add_i_cell(cell);
325 }
326 }
327
328 for (auto &vert : mesh->get_vertices())
329 {
330 if (vert->is_boundary())
331 {
332 mesh->add_b_vertex(vert);
333 }
334 else
335 {
336 mesh->add_i_vertex(vert);
337 }
338 }
339 }
340 const std::string _mesh_file;
341 };
342
344}
345#endif /* MESHBUILDER2D_HPP */
The MeshBuilder class provides build tools to create a full mesh with all connectivities.
Definition MeshBuilder2D.hpp:172
The MeshReaderTyp2 class provides functions to read a typ2 mesh file.
Definition MeshReaderTyp2.hpp:28
A Vertex is a Polytope with object_dim = 0.
Definition Polytope2D.hpp:54
Compute max and min eigenvalues of all matrices for i
Definition compute_eigs.m:5
Polytope< DIMENSION > Cell
Definition Polytope2D.hpp:151
std::unique_ptr< Mesh > build_the_mesh(const TransformationType &transformation=[](const std::array< double, 2 > &x) { return x;})
Definition MeshBuilder2D.hpp:189
MeshBuilder(const std::string mesh_file)
Definition MeshBuilder2D.hpp:184
Polytope< 1 > Edge
A Face is a Polytope with object_dim = DIMENSION - 1.
Definition Polytope2D.hpp:147
std::function< std::array< double, 2 >(const std::array< double, 2 > &)> TransformationType
Definition MeshBuilder2D.hpp:174
Polytope< 0 > Vertex
An Edge is a Polytope with object_dim = 1.
Definition Polytope2D.hpp:144
MeshBuilder()
Definition MeshBuilder2D.hpp:179
A
Definition mmread.m:102
for j
Definition mmread.m:174
Definition Mesh2D.hpp:7
double shortest_distance(VectorRd p, VectorRd A, VectorRd B)
Definition MeshBuilder2D.hpp:14
double signed_area(VectorRd A, VectorRd B, VectorRd C)
Definition Mesh2D.hpp:8
std::array< VectorRd, object_dim+1 > Simplex
Definition Polytope2D.hpp:23
Simplices< 2 > simplexify(std::vector< VectorRd > vertices)
Definition MeshBuilder2D.hpp:31
std::vector< Simplex< object_dim > > Simplices
Method to find the center mass of an arbitrary simplex in arbitrary space.
Definition Polytope2D.hpp:28
Eigen::Matrix< double, DIMENSION, 1 > VectorRd
Definition Polytope2D.hpp:19