HArD::Core2D
Hybrid Arbitrary Degree::Core 2D - Library to implement 2D schemes with edge and cell polynomials as unknowns
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 
12 namespace 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
std::unique_ptr< Mesh > build_the_mesh(const TransformationType &transformation=[](const std::array< double, 2 > &x) { return x;})
Definition: MeshBuilder2D.hpp:189
Polytope< DIMENSION > Cell
Definition: Polytope2D.hpp:151
MeshBuilder(const std::string mesh_file)
Definition: MeshBuilder2D.hpp:184
std::function< std::array< double, 2 >const std::array< double, 2 > &)> TransformationType
Definition: MeshBuilder2D.hpp:174
Polytope< 1 > Edge
A Face is a Polytope with object_dim = DIMENSION - 1.
Definition: Polytope2D.hpp:147
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
static auto v
Definition: ddrcore-test.hpp:32
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