libMesh
cell_c0polyhedron.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 // Local includes
19 #include "libmesh/cell_c0polyhedron.h"
20 
21 #include "libmesh/enum_order.h"
22 #include "libmesh/face_polygon.h"
23 #include "libmesh/mesh_tools.h"
24 #include "libmesh/replicated_mesh.h"
25 #include "libmesh/tensor_value.h"
26 #include "libmesh/node.h"
27 
28 // C++ headers
29 #include <numeric> // std::iota
30 
31 namespace libMesh
32 {
33 
34 // ------------------------------------------------------------
35 // C0Polyhedron class member functions
36 
38  (const std::vector<std::shared_ptr<Polygon>> & sides, std::unique_ptr<Node> & mid_elem_node, Elem * p) :
39  Polyhedron(sides, p),
40  _has_mid_elem_node(false)
41 {
42  this->retriangulate();
43 
44  // Grab the last node
45  if (_has_mid_elem_node)
46  mid_elem_node.reset(this->_nodelinks_data.back());
47 }
48 
49 
50 
51 C0Polyhedron::~C0Polyhedron() = default;
52 
53 
54 
55 std::unique_ptr<Elem> C0Polyhedron::disconnected_clone() const
56 {
57  auto sides = this->side_clones();
58 
59  std::unique_ptr<Node> mid_elem_node;
60  std::unique_ptr<Elem> returnval = std::make_unique<C0Polyhedron>(sides, mid_elem_node);
61  // Swap out the new mid elem node with the previous one
62  if (mid_elem_node)
63  {
64  libmesh_assert(returnval->n_nodes() == this->n_nodes());
65  returnval->set_node(returnval->n_nodes() - 1, this->_nodelinks_data.back());
66  }
67 
68  returnval->set_id() = this->id();
69 #ifdef LIBMESH_ENABLE_UNIQUE_ID
70  if (this->valid_unique_id())
71  returnval->set_unique_id(this->unique_id());
72 #endif
73 
74  const auto n_elem_ints = this->n_extra_integers();
75  returnval->add_extra_integers(n_elem_ints);
76  for (unsigned int i = 0; i != n_elem_ints; ++i)
77  returnval->set_extra_integer(i, this->get_extra_integer(i));
78 
79  returnval->inherit_data_from(*this);
80 
81  return returnval;
82 }
83 
84 
85 
86 unsigned int
88 {
89  return this->_nodelinks_data.size() - _has_mid_elem_node;
90 }
91 
92 
93 
94 bool C0Polyhedron::is_vertex(const unsigned int i) const
95 {
96  libmesh_assert_less (i, this->n_nodes());
97 
98  if (i < this->n_vertices())
99  return true;
100  else
101  return false;
102 }
103 
104 
105 
106 bool C0Polyhedron::is_edge(const unsigned int libmesh_dbg_var(i)) const
107 {
108  libmesh_assert_less (i, this->n_nodes());
109 
110  return false;
111 }
112 
113 
114 
115 bool C0Polyhedron::is_face(const unsigned int libmesh_dbg_var(i)) const
116 {
117  libmesh_assert_less (i, this->n_nodes());
118 
119  return false;
120 }
121 
122 
123 
124 bool C0Polyhedron::is_node_on_side(const unsigned int n,
125  const unsigned int s) const
126 {
127  libmesh_assert_less (n, this->n_nodes());
128  libmesh_assert_less (s, this->n_sides());
129 
130  const std::vector<unsigned int> & node_map =
131  std::get<2>(_sidelinks_data[s]);
132 
133  const auto it = std::find(node_map.begin(), node_map.end(), n);
134 
135  return (it != node_map.end());
136 }
137 
138 std::vector<unsigned int>
139 C0Polyhedron::nodes_on_side(const unsigned int s) const
140 {
141  libmesh_assert_less(s, this->n_sides());
142  return std::get<2>(_sidelinks_data[s]);
143 }
144 
145 std::vector<unsigned int>
146 C0Polyhedron::nodes_on_edge(const unsigned int e) const
147 {
148  auto [s, se] = _edge_lookup[e];
149  const Polygon & face = *std::get<0>(_sidelinks_data[s]);
150  const std::vector<unsigned int> & node_map =
151  std::get<2>(_sidelinks_data[s]);
152  std::vector<unsigned int> nodes_on_edge =
153  face.nodes_on_side(se);
154  for (auto i : index_range(nodes_on_edge))
155  nodes_on_edge[i] = node_map[nodes_on_edge[i]];
156  return nodes_on_edge;
157 }
158 
159 
160 
161 bool C0Polyhedron::is_node_on_edge(const unsigned int n,
162  const unsigned int e) const
163 {
164  libmesh_assert_less(e, _edge_lookup.size());
165  auto [s, se] = _edge_lookup[e];
166 
167  const Polygon & face = *std::get<0>(_sidelinks_data[s]);
168  const std::vector<unsigned int> & node_map =
169  std::get<2>(_sidelinks_data[s]);
170  std::vector<unsigned int> nodes_on_edge =
171  face.nodes_on_side(se);
172  for (auto noe : nodes_on_edge)
173  if (node_map[noe] == n)
174  return true;
175 
176  return false;
177 }
178 
179 
180 
181 std::vector<unsigned int>
182 C0Polyhedron::edges_adjacent_to_node(const unsigned int n) const
183 {
184  if (n == n_nodes() - 1)
185  return {};
187 }
188 
189 
190 
191 void C0Polyhedron::connectivity(const unsigned int /*sf*/,
192  const IOPackage /*iop*/,
193  std::vector<dof_id_type> & /*conn*/) const
194 {
195  libmesh_not_implemented();
196 }
197 
198 
199 
201 {
202  // This specialization is ... probably good for general non-Lagrange
203  // mappings, since we require our polyhedral faces to be planar, but
204  // I'd rather be slow than wrong.
205  if (this->mapping_type() != LAGRANGE_MAP)
206  return this->Elem::volume();
207 
208  // We use a triangulation to calculate here.
209 
210  Real six_vol = 0;
211  for (const auto & subtet : this->_triangulation)
212  {
213  const Point p0 = this->point(subtet[0]);
214  const Point p1 = this->point(subtet[1]);
215  const Point p2 = this->point(subtet[2]);
216  const Point p3 = this->point(subtet[3]);
217 
218  const Point v01 = p1 - p0;
219  const Point v02 = p2 - p0;
220  const Point v03 = p3 - p0;
221 
222  six_vol += triple_product(v01, v02, v03);
223  }
224 
225  return six_vol * (1./6.);
226 }
227 
228 
229 
231 {
232  // This specialization is good for Lagrange mappings only
233  if (this->mapping_type() != LAGRANGE_MAP)
234  return this->Elem::true_centroid();
235 
236  Real six_vol = 0;
237  Point six_vol_weighted_centroid;
238  for (const auto & subtet : this->_triangulation)
239  {
240  const Point p0 = this->point(subtet[0]);
241  const Point p1 = this->point(subtet[1]);
242  const Point p2 = this->point(subtet[2]);
243  const Point p3 = this->point(subtet[3]);
244 
245  const Point v01 = p1 - p0;
246  const Point v02 = p2 - p0;
247  const Point v03 = p3 - p0;
248 
249  const Real six_tet_vol = triple_product(v01, v02, v03);
250 
251  const Point tet_centroid = (p0 + p1 + p2 + p3) * 0.25;
252 
253  six_vol += six_tet_vol;
254 
255  six_vol_weighted_centroid += six_tet_vol * tet_centroid;
256  }
257 
258  return six_vol_weighted_centroid / six_vol;
259 }
260 
261 
262 
263 std::pair<unsigned short int, unsigned short int>
264 C0Polyhedron::second_order_child_vertex (const unsigned int /*n*/) const
265 {
266  libmesh_not_implemented();
267  return std::pair<unsigned short int, unsigned short int> (0,0);
268 }
269 
270 
271 
272 ElemType C0Polyhedron::side_type (const unsigned int libmesh_dbg_var(s)) const
273 {
274  libmesh_assert_less (s, this->n_sides());
275  return C0POLYGON;
276 }
277 
278 
279 
280 Point
281 C0Polyhedron::side_vertex_average_normal(const unsigned int s) const
282 {
283  libmesh_assert_less (s, this->n_sides());
284  libmesh_assert_equal_to(this->mapping_type(), LAGRANGE_MAP);
285  const auto poly_side_ptr = this->side_ptr(s);
286  const auto n_side_edges = poly_side_ptr->n_sides();
287 
288  // At the side vertex average, things simplify a bit
289  // We get the side "plane" normal at all vertices, then average them
290  Point normal;
291  Point current_edge = poly_side_ptr->point(1) - poly_side_ptr->point(0);
292  for (auto i : make_range(n_side_edges))
293  {
294  const Point next_edge = poly_side_ptr->point((i + 2) % n_side_edges) -
295  poly_side_ptr->point((i + 1) % n_side_edges);
296  const Point normal_at_vertex = current_edge.cross(next_edge);
297  normal += normal_at_vertex;
298  // Note: the sides are planar, we don't need to test them all
299  if (normal.norm_sq() > TOLERANCE)
300  break;
301  current_edge = next_edge;
302  }
303  bool outward_normal = std::get<1>(_sidelinks_data[s]);
304  return (outward_normal ? 1. : -1.) * normal.unit();
305 }
306 
307 
308 std::array<int, 4>
310 {
311  std::array<int, 4> sides = {invalid_int, invalid_int, invalid_int, invalid_int};
312  libmesh_assert(tri_i < this->n_subelements());
313  const auto & tet_nodes = _triangulation[tri_i];
314  for (const auto poly_side : make_range(n_sides()))
315  {
316  const auto sd_nodes = nodes_on_side(poly_side);
317  bool zero_in = std::find(sd_nodes.begin(), sd_nodes.end(), tet_nodes[0]) != sd_nodes.end();
318  bool one_in = std::find(sd_nodes.begin(), sd_nodes.end(), tet_nodes[1]) != sd_nodes.end();
319  bool two_in = std::find(sd_nodes.begin(), sd_nodes.end(), tet_nodes[2]) != sd_nodes.end();
320  bool three_in = std::find(sd_nodes.begin(), sd_nodes.end(), tet_nodes[3]) != sd_nodes.end();
321 
322  // Take advantage of the fact that the poly side can only contain one tet side
323  // Note: we could optimize by replacing the booleans with the searches above
324  if (zero_in)
325  {
326  if (one_in)
327  {
328  if (two_in)
329  sides[0] = poly_side;
330  else if (three_in)
331  sides[1] = poly_side;
332  }
333  else if (two_in && three_in)
334  sides[3] = poly_side;
335  }
336  else if (three_in && two_in && one_in)
337  sides[2] = poly_side;
338  }
339  return sides;
340 }
341 
342 
344 {
345  this->_triangulation.clear();
346 
347  // We should already have a triangulation for each side. We'll turn
348  // that into a triangulation of the entire polyhedral surface
349  // (stored as a mesh, so we can use `find_neighbors()`, then turn
350  // that into a tetrahedralization by shrinking the volume one tet at
351  // a time, which should work fine for convex polyhedra.
352 
353  Parallel::Communicator comm_self; // the default communicator is 1 rank
354  ReplicatedMesh surface(comm_self);
355 
356  // poly_node_to_local_id[poly_node] is the local id of
357  // poly_node in the Polyhedron, which is also the global id of the
358  // corresponding node in the surface mesh.
359  std::unordered_map<Node *, unsigned int> poly_node_to_id;
360 
361  for (unsigned int s : make_range(this->n_sides()))
362  {
363  const auto & [side, inward_normal, node_map] = this->_sidelinks_data[s];
364 
365  for (auto t : make_range(side->n_subtriangles()))
366  {
367  Elem * tri = surface.add_elem(Elem::build(TRI3));
368 
369  const std::array<int, 3> subtri = side->subtriangle(t);
370 
371  for (int i : make_range(3))
372  {
373  const int side_id = subtri[i];
374  const Node * poly_node = side->node_ptr(side_id);
375 
376  libmesh_assert_less(side_id, node_map.size());
377  const unsigned int local_id = node_map[side_id];
378 
379  Node * surf_node = surface.query_node_ptr(local_id);
380  if (surf_node)
381  libmesh_assert_equal_to(*(const Point*)poly_node,
382  *(const Point*)surf_node);
383  else
384  surf_node = surface.add_point(*poly_node, local_id);
385 
386  // Get a consistent orientation for surface triangles,
387  // facing their zeta directions outward
388  const int tri_node = inward_normal ? i : 2-i;
389  tri->set_node(tri_node, surf_node);
390  }
391  }
392  }
393 
394  surface.allow_renumbering(false);
395  surface.prepare_for_use();
396 
397  // We should have a watertight surface with consistent triangle
398  // orientations. That's expensive to check.
399 #ifdef DEBUG
400  auto verify_surface = [& surface] ()
401  {
402  for (const Elem * elem : surface.element_ptr_range())
403  {
404  for (auto s : make_range(3))
405  {
406  const Elem * neigh = elem->neighbor_ptr(s);
407  libmesh_assert(neigh);
408  libmesh_assert_equal_to(neigh,
409  surface.elem_ptr(neigh->id()));
410  const unsigned int ns = neigh->which_neighbor_am_i(elem);
411  libmesh_assert_less(ns, 3);
412  libmesh_assert_equal_to(elem->node_ptr(s),
413  neigh->node_ptr((ns+1)%3));
414  libmesh_assert_equal_to(elem->node_ptr((s+1)%3),
415  neigh->node_ptr(ns));
416  }
417  }
418  };
419 
420  verify_surface();
421 #endif
422 
423  // First heuristic: try with no interior point
424  // This might not succeed, not every surface triangulation gives a tetrahedralization
425  // with no additional interior point
426  // But if it succeeds, it uses less tetrahedra to cut the polyhedron
427 #ifdef LIBMESH_ENABLE_EXCEPTIONS
428  try
429  {
430  // We'll have to edit this as we change the surface elements, but we
431  // have a method to initialize it easily.
432  std::vector<std::vector<dof_id_type>> nodes_to_elem_vec_map;
433  MeshTools::build_nodes_to_elem_map(surface, nodes_to_elem_vec_map);
434 
435  // We'll be inserting and deleting entries heavily, so we'll use
436  // sets rather than vectors. We want to get the same results in
437  // parallel, so we'll use element ids rather than Elem pointers
438  std::vector<std::set<dof_id_type>> nodes_to_elem_map;
439  for (auto i : index_range(nodes_to_elem_vec_map))
440  nodes_to_elem_map.emplace_back
441  (nodes_to_elem_vec_map[i].begin(),
442  nodes_to_elem_vec_map[i].end());
443 
444  // Now start meshing the volume enclosed by surface, one tet at a
445  // time, with a greedy heuristic: find the vertex node with the most
446  // acutely convex (solid) angle and strip it out as tetrahedra.
447 
448  // We'll want a vector of surrounding nodes for multiple uses,
449  // sometimes with a similarly-sorted vector of surrounding elements
450  // to go with it.
451  auto surroundings_of =
452  [&nodes_to_elem_map, & surface]
453  (const Node & node,
454  std::vector<Elem *> * surrounding_elems)
455  {
456  const std::set<dof_id_type> & elems_by_node =
457  nodes_to_elem_map[node.id()];
458 
459  const unsigned int n_surrounding = elems_by_node.size();
460  libmesh_assert_greater_equal(n_surrounding, 3);
461 
462  if (surrounding_elems)
463  {
464  libmesh_assert(surrounding_elems->empty());
465  surrounding_elems->resize(n_surrounding);
466  }
467 
468  std::vector<Node *> surrounding_nodes(n_surrounding);
469 
470  Elem * elem = surface.elem_ptr(*elems_by_node.begin());
471  for (auto i : make_range(n_surrounding))
472  {
473  const unsigned int n = elem->get_node_index(&node);
474  libmesh_assert_not_equal_to(n, invalid_uint);
475  Node * next_node = elem->node_ptr((n+1)%3);
476  surrounding_nodes[i] = next_node;
477  if (surrounding_elems)
478  (*surrounding_elems)[i] = elem;
479  elem = elem->neighbor_ptr((n+2)%3);
480  libmesh_assert(elem);
481  libmesh_assert_equal_to(elem, surface.elem_ptr(elem->id()));
482 
483  // We should have a manifold here, but verifying that is
484  // expensive
485 #ifdef DEBUG
486  libmesh_assert_equal_to
487  (std::count(surrounding_nodes.begin(),
488  surrounding_nodes.end(), next_node),
489  1);
490 #endif
491  }
492 
493  // We should have finished a loop
494  libmesh_assert_equal_to
495  (elem, surface.elem_ptr(*elems_by_node.begin()));
496 
497  return surrounding_nodes;
498  };
499 
500  auto geometry_at = [&surroundings_of](const Node & node)
501  {
502  const std::vector<Node *> surrounding_nodes =
503  surroundings_of(node, nullptr);
504 
505  // Now sum up solid angles from tetrahedra created from the
506  // trivial triangulation of the surrounding nodes loop
507  Real total_solid_angle = 0;
508  const int n_surrounding =
509  cast_int<int>(surrounding_nodes.size());
510 
511  for (auto n : make_range(n_surrounding-2))
512  {
513  const Point
514  v01 = static_cast<Point>(*surrounding_nodes[n]) - node,
515  v02 = static_cast<Point>(*surrounding_nodes[n+1]) - node,
516  v03 = static_cast<Point>(*surrounding_nodes[n+2]) - node;
517 
518  total_solid_angle += solid_angle(v01, v02, v03);
519  }
520 
521  return std::make_pair(n_surrounding, total_solid_angle);
522  };
523 
524  // We'll keep track of solid angles and node valences so we don't
525  // waste time recomputing them when they haven't changed. We need
526  // to be able to search efficiently for the smallest angles of each
527  // valence, but also search efficiently for a particular node to
528  // remove and reinsert it when its connectivity changes.
529  //
530  // Since C++11 multimap has guaranteed that pairs with matching keys
531  // are kept in insertion order, so we can use Node * for values even
532  // in parallel.
533  typedef std::multimap<std::pair<int, Real>, Node*> node_map_type;
534  node_map_type nodes_by_geometry;
535  std::map<Node *, node_map_type::iterator> node_index;
536 
537  for (auto node : surface.node_ptr_range())
538  node_index[node] =
539  nodes_by_geometry.emplace(geometry_at(*node), node);
540 
541  // In 3D, this will require nested loops: an outer loop to remove
542  // each vertex, and an inner loop to remove multiple tetrahedra in
543  // cases where the vertex has more than 3 neighboring triangles.
544 
545  // We'll be done when there are only three "unremoved" nodes left,
546  // so they don't actually enclose any volume.
547  for (auto i : make_range(nodes_by_geometry.size()-3))
548  {
549  auto geometry_it = nodes_by_geometry.begin();
550  auto geometry_key = geometry_it->first;
551  auto [valence, angle] = geometry_key;
552  Node * node = geometry_it->second;
553  libmesh_ignore(i);
554 
555  // If our lowest-valence nodes are all points of non-convexity,
556  // skip to a higher valence.
557  while (angle > 2*pi-TOLERANCE)
558  {
559  geometry_it =
560  nodes_by_geometry.upper_bound
561  (std::make_pair(valence, Real(100)));
562  libmesh_assert(geometry_it != nodes_by_geometry.end());
563 
564  std::tie(geometry_key, node) = *geometry_it;
565  std::tie(valence, angle) = geometry_key;
566  }
567 
568  std::vector<Elem *> surrounding_elems;
569  std::vector<Node *> surrounding_nodes =
570  surroundings_of(*node, &surrounding_elems);
571 
572  const std::size_t n_surrounding = surrounding_nodes.size();
573 
574  // As we separate surrounding nodes from our center node, we'll
575  // be marking them as nullptr; we still need to be able to find
576  // predecessor and successor nodes in order afterward.
577  auto find_valid_nodes_around =
578  [n_surrounding, & surrounding_nodes]
579  (unsigned int j)
580  {
581  unsigned int jnext = (j+1)%n_surrounding;
582  while (!surrounding_nodes[jnext])
583  jnext = (jnext+1)%n_surrounding;
584 
585  unsigned int jprev = (j+n_surrounding-1)%n_surrounding;
586  while (!surrounding_nodes[jprev])
587  jprev = (jprev+n_surrounding-1)%n_surrounding;
588 
589  return std::make_pair(jprev, jnext);
590  };
591 
592  // We may have too many surrounding nodes to handle with
593  // just one tet. In that case we'll keep a cache of the
594  // element qualities that we'd get by making a tet with the
595  // edge from the center node to each surrounding node, so we
596  // can build the best tets first.
597  //
598  // In the case where we just have 3 nodes, we'll just pretend
599  // they all have the same positive quality, so we can still
600  // search this vector.
601  std::vector<Real> local_tet_quality(n_surrounding, 1);
602 
603  // From our center node with N surrounding nodes we can make N-2
604  // tetrahedra. The first N-3 each replace two surface tets with
605  // two new surface tets.
606  //
607  // My first idea was to greedily pick nodes with the smallest
608  // local (solid) angles to get the best quality. This works in
609  // 2D, but such a node can give a pancake tet in 3D.
610  //
611  // My second idea was to greedily pick nodes with the highest
612  // prospective tet quality. This works for the first tets, but
613  // can leave a pancake tet behind.
614  //
615  // My third idea is to try to fix the lowest quality tets first,
616  // by picking cases where they have higher quality neighbors,
617  // and creating those neighbors so as to change them.
618 
619  auto find_new_tet_nodes =
620  [& local_tet_quality, & find_valid_nodes_around]
621  ()
622  {
623  unsigned int jbest = 0;
624  auto [jminus, jplus] = find_valid_nodes_around(jbest);
625  Real qneighbest = std::min(local_tet_quality[jminus],
626  local_tet_quality[jplus]);
627  for (auto j : make_range(std::size_t(1),
628  local_tet_quality.size()))
629  {
630  // We don't want to build a bad tet
631  if (local_tet_quality[j] <= 0)
632  continue;
633 
634  std::tie(jminus, jplus) = find_valid_nodes_around(j);
635  Real qneighj = std::min(local_tet_quality[jminus],
636  local_tet_quality[jplus]);
637 
638  // We don't want to build a tet that can't fix a neighbor
639  // if we can build one that can.
640  if (qneighbest <= 0 &&
641  qneighj > 0)
642  continue;
643 
644  // We want to try for the best possible fix.
645  if ((local_tet_quality[j] - qneighj) >
646  (local_tet_quality[jbest] - qneighj))
647  {
648  jbest = j;
649  qneighbest = qneighj;
650  }
651  }
652 
653  libmesh_error_msg_if
654  (local_tet_quality[jbest] <= 0,
655  "Cannot build non-singular non-inverted tet");
656 
657  std::tie(jminus, jplus) = find_valid_nodes_around(jbest);
658 
659  return std::make_tuple(jbest, jminus, jplus);
660  };
661 
662  if (n_surrounding > 3)
663  {
664  // We'll be searching local_tet_quality even after tet
665  // extraction disconnects us from some nodes; when we do we
666  // don't want to get one.
667  constexpr Real far_node = -1e6;
668 
669  // Vectors from the center node to each of its surrounding
670  // nodes are helpful for calculating prospective tet
671  // quality.
672  std::vector<Point> v0s(n_surrounding);
673  for (auto j : make_range(n_surrounding))
674  v0s[j] = *(Point *)surrounding_nodes[j] - *node;
675 
676  // Find the tet quality we'd potentially get from each
677  // possible choice of tet
678  auto local_tet_quality_of =
679  [& surrounding_nodes, & v0s, & find_valid_nodes_around]
680  (unsigned int j)
681  {
682  auto [jminus, jplus] = find_valid_nodes_around(j);
683 
684  // Anything proportional to the ratio of volume to
685  // total-edge-length-cubed should peak for perfect tets
686  // but hit 0 for pancakes and slivers.
687 
688  const Real total_len =
689  v0s[j].norm() + v0s[jminus].norm() + v0s[jplus].norm() +
690  (*(Point *)surrounding_nodes[jplus] -
691  *(Point *)surrounding_nodes[j]).norm() +
692  (*(Point *)surrounding_nodes[j] -
693  *(Point *)surrounding_nodes[jminus]).norm() +
694  (*(Point *)surrounding_nodes[jminus] -
695  *(Point *)surrounding_nodes[jplus]).norm();
696 
697  // Orientation here is tricky. Think of the triple
698  // product as (v1 cross v2) dot v3, with right hand rule.
699  const Real six_vol =
700  triple_product(v0s[jminus], v0s[jplus], v0s[j]);
701 
702  return six_vol / (total_len * total_len * total_len);
703  };
704 
705  for (auto j : make_range(n_surrounding))
706  local_tet_quality[j] = local_tet_quality_of(j);
707 
708  // If we have N surrounding nodes, we can make N tets and
709  // that'll bring us back to the 3-surrounding-node case to
710  // finish.
711  for (auto t : make_range(n_surrounding-3))
712  {
713  libmesh_ignore(t);
714 
715  auto [jbest, jminus, jplus] = find_new_tet_nodes();
716 
717  // Turn these four nodes into a tet
718  Node * nbest = surrounding_nodes[jbest],
719  * nminus = surrounding_nodes[jminus],
720  * nplus = surrounding_nodes[jplus];
721  this->add_tet(nminus->id(), nbest->id(), nplus->id(),
722  node->id());
723 
724  // Replace the old two triangles around these nodes with the
725  // proper two new triangles.
726  Elem * oldtri1 = surrounding_elems[jminus],
727  * oldtri2 = surrounding_elems[jbest],
728  * newtri1 = surface.add_elem(Elem::build(TRI3)),
729  * newtri2 = surface.add_elem(Elem::build(TRI3));
730 
731  const unsigned int c1 = oldtri1->get_node_index(node),
732  c2 = oldtri2->get_node_index(node);
733 
734  newtri1->set_node(0, node);
735  newtri1->set_node(1, nminus);
736  newtri1->set_node(2, nplus);
737 
738  surrounding_elems[jminus] = newtri1;
739 
740  Elem * neigh10 = oldtri1->neighbor_ptr(c1);
741  Elem * neigh12 = oldtri2->neighbor_ptr((c2+2)%3);
742  newtri1->set_neighbor(0, neigh10);
743  neigh10->set_neighbor(neigh10->which_neighbor_am_i(oldtri1), newtri1);
744  newtri1->set_neighbor(1, newtri2);
745  newtri1->set_neighbor(2, neigh12);
746  neigh12->set_neighbor(neigh12->which_neighbor_am_i(oldtri2), newtri1);
747 
748  newtri2->set_node(0, nplus);
749  newtri2->set_node(1, nminus);
750  newtri2->set_node(2, nbest);
751 
752  Elem * neigh21 = oldtri1->neighbor_ptr((c1+1)%3);
753  Elem * neigh22 = oldtri2->neighbor_ptr((c2+1)%3);
754  newtri2->set_neighbor(0, newtri1);
755  newtri2->set_neighbor(1, neigh21);
756  neigh21->set_neighbor(neigh21->which_neighbor_am_i(oldtri1), newtri2);
757  newtri2->set_neighbor(2, neigh22);
758  neigh22->set_neighbor(neigh22->which_neighbor_am_i(oldtri2), newtri2);
759 
760  for (unsigned int p : make_range(3))
761  {
762  nodes_to_elem_map[oldtri1->node_id(p)].erase(oldtri1->id());
763  nodes_to_elem_map[oldtri2->node_id(p)].erase(oldtri2->id());
764  nodes_to_elem_map[newtri1->node_id(p)].insert(newtri1->id());
765  nodes_to_elem_map[newtri2->node_id(p)].insert(newtri2->id());
766  }
767 
768  // We've finished replacing the old triangles.
769  surface.delete_elem(oldtri1);
770  surface.delete_elem(oldtri2);
771 
772  // The solid angle for the far node should now stay
773  // unchanged until we're out of this inner loop; let's
774  // recalculate it here, and then we'll be done with it.
775  Node * & nbestref = surrounding_nodes[jbest];
776  nodes_by_geometry.erase(node_index[nbestref]);
777  node_index[nbestref] =
778  nodes_by_geometry.emplace(geometry_at(*nbestref), nbestref);
779 
780  // The far node is no longer sharing an edge with our center
781  // node. Make sure we don't use it again with the center
782  // node.
783  local_tet_quality[jbest] = far_node;
784  nbestref = nullptr;
785 
786  // The potential tet qualities using the side nodes have
787  // changed now that they're directly connected to each
788  // other.
789  local_tet_quality[jminus] =
790  local_tet_quality_of(jminus);
791 
792  local_tet_quality[jplus] =
793  local_tet_quality_of(jplus);
794  }
795  }
796 
797  // Now we should have just 3 surrounding nodes, with which to
798  // make one tetrahedron. Put them in a counterclockwise
799  // (looking from outside) orientation, not the "best, clockwise,
800  // counterclockwise" we get from the lambda.
801  auto [j2, j1, j3] = find_new_tet_nodes();
802 
803  // Turn these last four nodes into a tet
804  Node * n1 = surrounding_nodes[j1],
805  * n2 = surrounding_nodes[j2],
806  * n3 = surrounding_nodes[j3];
807  this->add_tet(n1->id(), n2->id(), n3->id(), node->id());
808 
809  // Replace the three surface triangles of that tet with the new
810  // fourth triangle.
811  Elem * oldtri1 = surrounding_elems[j1],
812  * oldtri2 = surrounding_elems[j2],
813  * oldtri3 = surrounding_elems[j3],
814  * newtri = surface.add_elem(Elem::build(TRI3));
815 
816  const unsigned int c1 = oldtri1->get_node_index(node),
817  c2 = oldtri2->get_node_index(node),
818  c3 = oldtri3->get_node_index(node);
819 
820  newtri->set_node(0, n1);
821  newtri->set_node(1, n2);
822  newtri->set_node(2, n3);
823  Elem * neigh0 = oldtri1->neighbor_ptr((c1+1)%3);
824  newtri->set_neighbor(0, neigh0);
825  neigh0->set_neighbor(neigh0->which_neighbor_am_i(oldtri1), newtri);
826  Elem * neigh1 = oldtri2->neighbor_ptr((c2+1)%3);
827  newtri->set_neighbor(1, neigh1);
828  neigh1->set_neighbor(neigh1->which_neighbor_am_i(oldtri2), newtri);
829  Elem * neigh2 = oldtri3->neighbor_ptr((c3+1)%3);
830  newtri->set_neighbor(2, neigh2);
831  neigh2->set_neighbor(neigh2->which_neighbor_am_i(oldtri3), newtri);
832 
833  for (unsigned int p : make_range(3))
834  {
835  nodes_to_elem_map[oldtri1->node_id(p)].erase(oldtri1->id());
836  nodes_to_elem_map[oldtri2->node_id(p)].erase(oldtri2->id());
837  nodes_to_elem_map[oldtri3->node_id(p)].erase(oldtri3->id());
838  nodes_to_elem_map[newtri->node_id(p)].insert(newtri->id());
839  }
840 
841  // We've finished replacing the old triangles.
842  surface.delete_elem(oldtri1);
843  surface.delete_elem(oldtri2);
844  surface.delete_elem(oldtri3);
845 
846  // We should have used up all our surrounding nodes now, and we
847  // shouldn't have messed up our surface in the process, and our
848  // center node should no longer be part of the surface.
849 #ifdef DEBUG
850  surrounding_nodes[j1] = nullptr;
851  surrounding_nodes[j2] = nullptr;
852  surrounding_nodes[j3] = nullptr;
853 
854  for (auto ltq : surrounding_nodes)
855  libmesh_assert(!ltq);
856 
857  if (surface.n_elem() > 3)
858  verify_surface();
859 
860  for (const Elem * elem : surface.element_ptr_range())
861  for (auto p : make_range(3))
862  libmesh_assert_not_equal_to
863  (elem->node_ptr(p), node);
864 #endif
865 
866  // We've used up our center node, so it's not something we can
867  // eliminate again.
868  nodes_by_geometry.erase(geometry_it);
869  }
870  // At this point our surface should just have two triangles left.
871  libmesh_assert_equal_to(surface.n_elem(), 2);
872  _has_mid_elem_node = false;
873  }
874  // Failed without an interior point.
875  // Use a single vertex-average interior point and tetrahedralize around it
876  catch ( libMesh::LogicError & )
877 #endif
878  {
879  // Clear the triangulation we started building
880  this->_triangulation.clear();
881 
882  // Get the vertex-average, no need to triangulate for this
883  const auto v_avg = this->vertex_average();
884  if (!_has_mid_elem_node)
885  {
886  // Create the mid element node. Add it to nodelinks
887  // We'll attach a unique ptr to it later
888  Node * mid_elem_node = new Node(v_avg);
889  this->_nodelinks_data.push_back(mid_elem_node);
890  _has_mid_elem_node = true;
891  }
892  else
893  {
894  // We are triangulating for a second time, we have already added this mid-element node
895  libmesh_assert(n_vertices() == n_nodes() - 1);
896  }
897 
898  // Build the tetrahedralization with each of the triangles on each side
899  for (unsigned int s : make_range(this->n_sides()))
900  {
901  const auto & [side, inward_normal, node_map] = this->_sidelinks_data[s];
902 
903  for (auto t : make_range(side->n_subtriangles()))
904  {
905  // Get all the nodes
906  const auto & n1 = node_map[side->subtriangle(t)[0]];
907  const auto & n2 = node_map[side->subtriangle(t)[1]];
908  const auto & n3 = node_map[side->subtriangle(t)[2]];
909 
910  // The mid node is the last node in the _nodes array
911  if (!inward_normal)
912  this->add_tet((int)n1, (int)n2, (int)n3, this->n_nodes() - 1);
913  else
914  this->add_tet((int)n1, (int)n3, (int)n2, this->n_nodes() - 1);
915  }
916  }
917  }
918 }
919 
920 
922  int n2,
923  int n3,
924  int n4)
925 {
926 #ifndef NDEBUG
927  const auto nn = this->n_nodes();
928  libmesh_assert_less(n1, nn);
929  libmesh_assert_less(n2, nn);
930  libmesh_assert_less(n3, nn);
931  libmesh_assert_less(n4, nn);
932 #endif
933 
934  const Point v12 = this->point(n2) - this->point(n1);
935  const Point v13 = this->point(n3) - this->point(n1);
936  const Point v14 = this->point(n4) - this->point(n1);
937  const Real six_vol = triple_product(v12, v13, v14);
938  // We need to error on this in optimized modes to fall back onto the
939  // tetrahedralization with a mid node
940  libmesh_error_msg_if(six_vol <= 0, "Creating flat tet");
941 
942  this->_triangulation.push_back({n1, n2, n3, n4});
943 }
944 
945 
946 } // namespace libMesh
virtual Point true_centroid() const
Definition: elem.C:575
virtual unsigned int n_nodes() const override
ElemType
Defines an enum for geometric element types.
virtual bool is_node_on_edge(const unsigned int n, const unsigned int e) const override
T solid_angle(const TypeVector< T > &v01, const TypeVector< T > &v02, const TypeVector< T > &v03)
Definition: type_vector.h:1054
The ReplicatedMesh class is derived from the MeshBase class, and is used to store identical copies of...
virtual Node *& set_node(const unsigned int i)
Definition: elem.h:2564
A Node is like a Point, but with more information.
Definition: node.h:52
virtual unsigned int n_vertices() const override final
ElemType side_type(const unsigned int s) const override final
virtual dof_id_type n_elem() const override final
The Polyhedron is an element in 3D with an arbitrary number of polygonal faces.
const unsigned int invalid_uint
A number which is used quite often to represent an invalid or uninitialized value for an unsigned int...
Definition: libmesh.h:303
unsigned int get_node_index(const Node *node_ptr) const
Definition: elem.h:2551
virtual const Node * query_node_ptr(const dof_id_type i) const override final
std::vector< Node * > _nodelinks_data
Data for links to nodes.
void allow_renumbering(bool allow)
If false is passed in then this mesh will no longer be renumbered when being prepared for use...
Definition: mesh_base.h:1345
static constexpr Real TOLERANCE
std::vector< std::tuple< std::shared_ptr< Polygon >, bool, std::vector< unsigned int > > > _sidelinks_data
Data for links to sides.
virtual std::vector< unsigned int > edges_adjacent_to_node(const unsigned int n) const override
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
Prepare a newly created (or read) mesh for use.
Definition: mesh_base.C:824
IOPackage
libMesh interfaces with several different software packages for the purposes of creating, reading, and writing mesh files.
const boundary_id_type side_id
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
unique_id_type unique_id() const
Definition: dof_object.h:835
virtual void retriangulate() override final
Create a triangulation (tetrahedralization) based on the current sides&#39; triangulations.
C0Polyhedron(const std::vector< std::shared_ptr< Polygon >> &sides, std::unique_ptr< Node > &mid_elem_node, Elem *p=nullptr)
Constructor.
void build_nodes_to_elem_map(const MeshBase &mesh, std::vector< std::vector< dof_id_type >> &nodes_to_elem_map)
After calling this function the input vector nodes_to_elem_map will contain the node to element conne...
Definition: mesh_tools.C:456
The libMesh namespace provides an interface to certain functionality in the library.
virtual unsigned int n_sides() const override final
virtual void delete_elem(Elem *e) override final
Removes element e from the mesh.
std::vector< std::pair< unsigned int, unsigned int > > _edge_lookup
One entry for each polyhedron edge, a pair indicating the side number and the edge-of-side number whi...
virtual Point true_centroid() const override
An optimized method for calculating the centroid of a C0Polyhedron.
Point side_vertex_average_normal(const unsigned int s) const override final
virtual const Elem * elem_ptr(const dof_id_type i) const override final
T triple_product(const TypeVector< T > &a, const TypeVector< T > &b, const TypeVector< T > &c)
Definition: type_vector.h:1032
TypeVector< T > unit() const
Definition: type_vector.h:1141
void libmesh_ignore(const Args &...)
auto norm_sq() const
Definition: type_vector.h:928
virtual Real volume() const override
An optimized method for calculating the area of a C0Polyhedron.
const int invalid_int
A number which is used quite often to represent an invalid or uninitialized value for an integer...
Definition: libmesh.h:309
ElemMappingType mapping_type() const
Definition: elem.h:3134
dof_id_type id() const
Definition: dof_object.h:819
unsigned int which_neighbor_am_i(const Elem *e) const
This function tells you which neighbor e is.
Definition: elem.h:2933
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition: elem.C:442
virtual bool is_vertex(const unsigned int i) const override
unsigned int n_subelements() const
bool _has_mid_elem_node
Whether we have a mid element node.
The Polygon is an element in 2D with an arbitrary (but fixed) number of sides.
Definition: face_polygon.h:39
virtual bool is_node_on_side(const unsigned int n, const unsigned int s) const override
libmesh_assert(ctx)
virtual std::vector< unsigned int > nodes_on_side(const unsigned int s) const override
bool valid_unique_id() const
Definition: dof_object.h:869
TypeVector< typename CompareTypes< T, T2 >::supertype > cross(const TypeVector< T2 > &v) const
Definition: type_vector.h:885
void set_neighbor(const unsigned int i, Elem *n)
Assigns n as the neighbor.
Definition: elem.h:2632
virtual Elem * add_elem(Elem *e) override final
Add elem e to the end of the element array.
std::vector< std::shared_ptr< Polygon > > side_clones() const
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2612
std::unique_ptr< Elem > disconnected_clone() const override
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
auto norm(const T &a)
Definition: tensor_tools.h:74
A class to represent the internal "this should never happen" errors, to be thrown by "libmesh_error()...
const Node * node_ptr(const unsigned int i) const
Definition: elem.h:2513
virtual Real volume() const
Definition: elem.C:3462
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:176
virtual std::vector< unsigned int > edges_adjacent_to_node(const unsigned int n) const override
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id) override final
functions for adding /deleting nodes elements.
virtual bool is_face(const unsigned int i) const override
virtual std::array< int, 4 > subelement_sides_to_poly_sides(unsigned int i) const
unsigned int n_extra_integers() const
Returns how many extra integers are associated to the DofObject.
Definition: dof_object.h:1146
virtual std::unique_ptr< Elem > side_ptr(const unsigned int i) override final
void add_tet(int n1, int n2, int n3, int n4)
Add to our triangulation (tetrahedralization).
virtual bool is_edge(const unsigned int i) const override
virtual void connectivity(const unsigned int sf, const IOPackage iop, std::vector< dof_id_type > &conn) const override
std::vector< std::array< int, 4 > > _triangulation
Data for a triangulation (tetrahedralization) of the polyhedron.
virtual std::vector< unsigned int > nodes_on_edge(const unsigned int e) const override
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
const Point & point(const unsigned int i) const
Definition: elem.h:2459
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:153
virtual std::vector< unsigned int > nodes_on_side(const unsigned int) const =0
Point vertex_average() const
Definition: elem.C:669
virtual std::pair< unsigned short int, unsigned short int > second_order_child_vertex(const unsigned int n) const override
Element refinement is not implemented for polyhedra.
const Real pi
.
Definition: libmesh.h:292