libMesh
cell_c0polyhedron.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 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 
27 // C++ headers
28 #include <numeric> // std::iota
29 
30 namespace libMesh
31 {
32 
33 // ------------------------------------------------------------
34 // C0Polyhedron class member functions
35 
37  (const std::vector<std::shared_ptr<Polygon>> & sides, Elem * p) :
38  Polyhedron(sides, p)
39 {
40  this->retriangulate();
41 }
42 
43 
44 
45 C0Polyhedron::~C0Polyhedron() = default;
46 
47 
48 
49 std::unique_ptr<Elem> C0Polyhedron::disconnected_clone() const
50 {
51  auto sides = this->side_clones();
52 
53  std::unique_ptr<Elem> returnval =
54  std::make_unique<C0Polyhedron>(sides);
55 
56  returnval->set_id() = this->id();
57 #ifdef LIBMESH_ENABLE_UNIQUE_ID
58  if (this->valid_unique_id())
59  returnval->set_unique_id(this->unique_id());
60 #endif
61 
62  const auto n_elem_ints = this->n_extra_integers();
63  returnval->add_extra_integers(n_elem_ints);
64  for (unsigned int i = 0; i != n_elem_ints; ++i)
65  returnval->set_extra_integer(i, this->get_extra_integer(i));
66 
67  returnval->inherit_data_from(*this);
68 
69  return returnval;
70 }
71 
72 
73 
74 bool C0Polyhedron::is_vertex(const unsigned int libmesh_dbg_var(i)) const
75 {
76  libmesh_assert (i < this->n_nodes());
77 
78  return true;
79 }
80 
81 
82 
83 bool C0Polyhedron::is_edge(const unsigned int libmesh_dbg_var(i)) const
84 {
85  libmesh_assert_less (i, this->n_nodes());
86 
87  return false;
88 }
89 
90 
91 
92 bool C0Polyhedron::is_face(const unsigned int libmesh_dbg_var(i)) const
93 {
94  libmesh_assert_less (i, this->n_nodes());
95 
96  return false;
97 }
98 
99 
100 
101 bool C0Polyhedron::is_node_on_side(const unsigned int n,
102  const unsigned int s) const
103 {
104  libmesh_assert_less (n, this->n_nodes());
105  libmesh_assert_less (s, this->n_sides());
106 
107  const std::vector<unsigned int> & node_map =
108  std::get<2>(_sidelinks_data[s]);
109 
110  const auto it = std::find(node_map.begin(), node_map.end(), n);
111 
112  return (it != node_map.end());
113 }
114 
115 std::vector<unsigned int>
116 C0Polyhedron::nodes_on_side(const unsigned int s) const
117 {
118  libmesh_assert_less(s, this->n_sides());
119  return std::get<2>(_sidelinks_data[s]);
120 }
121 
122 std::vector<unsigned int>
123 C0Polyhedron::nodes_on_edge(const unsigned int e) const
124 {
125  auto [s, se] = _edge_lookup[e];
126  const Polygon & face = *std::get<0>(_sidelinks_data[s]);
127  const std::vector<unsigned int> & node_map =
128  std::get<2>(_sidelinks_data[s]);
129  std::vector<unsigned int> nodes_on_edge =
130  face.nodes_on_side(se);
131  for (auto i : index_range(nodes_on_edge))
132  nodes_on_edge[i] = node_map[nodes_on_edge[i]];
133  return nodes_on_edge;
134 }
135 
136 
137 
138 bool C0Polyhedron::is_node_on_edge(const unsigned int n,
139  const unsigned int e) const
140 {
141  libmesh_assert_less(e, _edge_lookup.size());
142  auto [s, se] = _edge_lookup[e];
143 
144  const Polygon & face = *std::get<0>(_sidelinks_data[s]);
145  const std::vector<unsigned int> & node_map =
146  std::get<2>(_sidelinks_data[s]);
147  std::vector<unsigned int> nodes_on_edge =
148  face.nodes_on_side(se);
149  for (auto noe : nodes_on_edge)
150  if (node_map[noe] == n)
151  return true;
152 
153  return false;
154 }
155 
156 
157 
158 void C0Polyhedron::connectivity(const unsigned int /*sf*/,
159  const IOPackage /*iop*/,
160  std::vector<dof_id_type> & /*conn*/) const
161 {
162  libmesh_not_implemented();
163 }
164 
165 
166 
168 {
169  // This specialization is good for Lagrange mappings only
170  if (this->mapping_type() != LAGRANGE_MAP)
171  return this->Elem::volume();
172 
173  // We use a triangulation to calculate here.
174 
175  Real six_vol = 0;
176  for (const auto & subtet : this->_triangulation)
177  {
178  const Point p0 = this->point(subtet[0]);
179  const Point p1 = this->point(subtet[1]);
180  const Point p2 = this->point(subtet[2]);
181  const Point p3 = this->point(subtet[3]);
182 
183  const Point v01 = p1 - p0;
184  const Point v02 = p2 - p0;
185  const Point v03 = p3 - p0;
186 
187  six_vol += triple_product(v01, v02, v03);
188  }
189 
190  return six_vol * (1./6.);
191 }
192 
193 
194 
196 {
197  // This specialization is good for Lagrange mappings only
198  if (this->mapping_type() != LAGRANGE_MAP)
199  return this->Elem::true_centroid();
200 
201  Real six_vol = 0;
202  Point six_vol_weighted_centroid;
203  for (const auto & subtet : this->_triangulation)
204  {
205  const Point p0 = this->point(subtet[0]);
206  const Point p1 = this->point(subtet[1]);
207  const Point p2 = this->point(subtet[2]);
208  const Point p3 = this->point(subtet[3]);
209 
210  const Point v01 = p1 - p0;
211  const Point v02 = p2 - p0;
212  const Point v03 = p3 - p0;
213 
214  const Real six_tet_vol = triple_product(v01, v02, v03);
215 
216  const Point tet_centroid = (p0 + p1 + p2 + p3) * 0.25;
217 
218  six_vol += six_tet_vol;
219 
220  six_vol_weighted_centroid += six_tet_vol * tet_centroid;
221  }
222 
223  return six_vol_weighted_centroid / six_vol;
224 }
225 
226 
227 
228 std::pair<unsigned short int, unsigned short int>
229 C0Polyhedron::second_order_child_vertex (const unsigned int /*n*/) const
230 {
231  libmesh_not_implemented();
232  return std::pair<unsigned short int, unsigned short int> (0,0);
233 }
234 
235 
236 
237 ElemType C0Polyhedron::side_type (const unsigned int libmesh_dbg_var(s)) const
238 {
239  libmesh_assert_less (s, this->n_sides());
240  return C0POLYGON;
241 }
242 
243 
244 
246 {
247  this->_triangulation.clear();
248 
249  // We should already have a triangulation for each side. We'll turn
250  // that into a triangulation of the entire polyhedral surface
251  // (stored as a mesh, so we can use `find_neighbors()`, then turn
252  // that into a tetrahedralization by shrinking the volume one tet at
253  // a time, which should work fine for convex polyhedra.
254 
255  Parallel::Communicator comm_self; // the default communicator is 1 rank
256  ReplicatedMesh surface(comm_self);
257 
258  // poly_node_to_local_id[poly_node] is the local id of
259  // poly_node in the Polyhedron, which is also the global id of the
260  // corresponding node in the surface mesh.
261  std::unordered_map<Node *, unsigned int> poly_node_to_id;
262 
263  for (unsigned int s : make_range(this->n_sides()))
264  {
265  const auto & [side, inward_normal, node_map] = this->_sidelinks_data[s];
266 
267  for (auto t : make_range(side->n_subtriangles()))
268  {
269  Elem * tri = surface.add_elem(Elem::build(TRI3));
270 
271  const std::array<int, 3> subtri = side->subtriangle(t);
272 
273  for (int i : make_range(3))
274  {
275  const int side_id = subtri[i];
276  const Node * poly_node = side->node_ptr(side_id);
277 
278  libmesh_assert_less(side_id, node_map.size());
279  const unsigned int local_id = node_map[side_id];
280 
281  Node * surf_node = surface.query_node_ptr(local_id);
282  if (surf_node)
283  libmesh_assert_equal_to(*(const Point*)poly_node,
284  *(const Point*)surf_node);
285  else
286  surf_node = surface.add_point(*poly_node, local_id);
287 
288  // Get a consistent orientation for surface triangles,
289  // facing their zeta directions outward
290  const int tri_node = inward_normal ? i : 2-i;
291  tri->set_node(tri_node, surf_node);
292  }
293  }
294  }
295 
296  surface.allow_renumbering(false);
297  surface.prepare_for_use();
298 
299  // We should have a watertight surface with consistent triangle
300  // orientations. That's expensive to check.
301 #ifdef DEBUG
302  auto verify_surface = [& surface] ()
303  {
304  for (const Elem * elem : surface.element_ptr_range())
305  {
306  for (auto s : make_range(3))
307  {
308  const Elem * neigh = elem->neighbor_ptr(s);
309  libmesh_assert(neigh);
310  libmesh_assert_equal_to(neigh,
311  surface.elem_ptr(neigh->id()));
312  const unsigned int ns = neigh->which_neighbor_am_i(elem);
313  libmesh_assert_less(ns, 3);
314  libmesh_assert_equal_to(elem->node_ptr(s),
315  neigh->node_ptr((ns+1)%3));
316  libmesh_assert_equal_to(elem->node_ptr((s+1)%3),
317  neigh->node_ptr(ns));
318  }
319  }
320  };
321 
322  verify_surface();
323 #endif
324 
325  // We'll have to edit this as we change the surface elements, but we
326  // have a method to initialize it easily.
327  std::vector<std::vector<dof_id_type>> nodes_to_elem_vec_map;
328  MeshTools::build_nodes_to_elem_map(surface, nodes_to_elem_vec_map);
329 
330  // We'll be inserting and deleting entries heavily, so we'll use
331  // sets rather than vectors. We want to get the same results in
332  // parallel, so we'll use element ids rather than Elem pointers
333  std::vector<std::set<dof_id_type>> nodes_to_elem_map;
334  for (auto i : index_range(nodes_to_elem_vec_map))
335  nodes_to_elem_map.emplace_back
336  (nodes_to_elem_vec_map[i].begin(),
337  nodes_to_elem_vec_map[i].end());
338 
339  // Now start meshing the volume enclosed by surface, one tet at a
340  // time, with a greedy heuristic: find the vertex node with the most
341  // acutely convex (solid) angle and strip it out as tetrahedra.
342 
343  // We'll want a vector of surrounding nodes for multiple uses,
344  // sometimes with a similarly-sorted vector of surrounding elements
345  // to go with it.
346  auto surroundings_of =
347  [&nodes_to_elem_map, & surface]
348  (const Node & node,
349  std::vector<Elem *> * surrounding_elems)
350  {
351  const std::set<dof_id_type> & elems_by_node =
352  nodes_to_elem_map[node.id()];
353 
354  const unsigned int n_surrounding = elems_by_node.size();
355  libmesh_assert_greater_equal(n_surrounding, 3);
356 
357  if (surrounding_elems)
358  {
359  libmesh_assert(surrounding_elems->empty());
360  surrounding_elems->resize(n_surrounding);
361  }
362 
363  std::vector<Node *> surrounding_nodes(n_surrounding);
364 
365  Elem * elem = surface.elem_ptr(*elems_by_node.begin());
366  for (auto i : make_range(n_surrounding))
367  {
368  const unsigned int n = elem->get_node_index(&node);
369  libmesh_assert_not_equal_to(n, invalid_uint);
370  Node * next_node = elem->node_ptr((n+1)%3);
371  surrounding_nodes[i] = next_node;
372  if (surrounding_elems)
373  (*surrounding_elems)[i] = elem;
374  elem = elem->neighbor_ptr((n+2)%3);
375  libmesh_assert(elem);
376  libmesh_assert_equal_to(elem, surface.elem_ptr(elem->id()));
377 
378  // We should have a manifold here, but verifying that is
379  // expensive
380 #ifdef DEBUG
381  libmesh_assert_equal_to
382  (std::count(surrounding_nodes.begin(),
383  surrounding_nodes.end(), next_node),
384  1);
385 #endif
386  }
387 
388  // We should have finished a loop
389  libmesh_assert_equal_to
390  (elem, surface.elem_ptr(*elems_by_node.begin()));
391 
392  return surrounding_nodes;
393  };
394 
395  auto geometry_at = [&surroundings_of](const Node & node)
396  {
397  const std::vector<Node *> surrounding_nodes =
398  surroundings_of(node, nullptr);
399 
400  // Now sum up solid angles from tetrahedra created from the
401  // trivial triangulation of the surrounding nodes loop
402  Real total_solid_angle = 0;
403  const int n_surrounding =
404  cast_int<int>(surrounding_nodes.size());
405 
406  for (auto n : make_range(n_surrounding-2))
407  {
408  const Point
409  v01 = static_cast<Point>(*surrounding_nodes[n]) - node,
410  v02 = static_cast<Point>(*surrounding_nodes[n+1]) - node,
411  v03 = static_cast<Point>(*surrounding_nodes[n+2]) - node;
412 
413  total_solid_angle += solid_angle(v01, v02, v03);
414  }
415 
416  return std::make_pair(n_surrounding, total_solid_angle);
417  };
418 
419  // We'll keep track of solid angles and node valences so we don't
420  // waste time recomputing them when they haven't changed. We need
421  // to be able to search efficiently for the smallest angles of each
422  // valence, but also search efficiently for a particular node to
423  // remove and reinsert it when its connectivity changes.
424  //
425  // Since C++11 multimap has guaranteed that pairs with matching keys
426  // are kept in insertion order, so we can use Node * for values even
427  // in parallel.
428  typedef std::multimap<std::pair<int, Real>, Node*> node_map_type;
429  node_map_type nodes_by_geometry;
430  std::map<Node *, node_map_type::iterator> node_index;
431 
432  for (auto node : surface.node_ptr_range())
433  node_index[node] =
434  nodes_by_geometry.emplace(geometry_at(*node), node);
435 
436  // In 3D, this will require nested loops: an outer loop to remove
437  // each vertex, and an inner loop to remove multiple tetrahedra in
438  // cases where the vertex has more than 3 neighboring triangles.
439 
440  // We'll be done when there are only three "unremoved" nodes left,
441  // so they don't actually enclose any volume.
442  for (auto i : make_range(nodes_by_geometry.size()-3))
443  {
444  auto geometry_it = nodes_by_geometry.begin();
445  auto geometry_key = geometry_it->first;
446  auto [valence, angle] = geometry_key;
447  Node * node = geometry_it->second;
448  libmesh_ignore(i);
449 
450  // If our lowest-valence nodes are all points of non-convexity,
451  // skip to a higher valence.
452  while (angle > 2*pi-TOLERANCE)
453  {
454  geometry_it =
455  nodes_by_geometry.upper_bound
456  (std::make_pair(valence, Real(100)));
457  libmesh_assert(geometry_it != nodes_by_geometry.end());
458 
459  std::tie(geometry_key, node) = *geometry_it;
460  std::tie(valence, angle) = geometry_key;
461  }
462 
463  std::vector<Elem *> surrounding_elems;
464  std::vector<Node *> surrounding_nodes =
465  surroundings_of(*node, &surrounding_elems);
466 
467  const std::size_t n_surrounding = surrounding_nodes.size();
468 
469  // As we separate surrounding nodes from our center node, we'll
470  // be marking them as nullptr; we still need to be able to find
471  // predecessor and successor nodes in order afterward.
472  auto find_valid_nodes_around =
473  [n_surrounding, & surrounding_nodes]
474  (unsigned int j)
475  {
476  unsigned int jnext = (j+1)%n_surrounding;
477  while (!surrounding_nodes[jnext])
478  jnext = (jnext+1)%n_surrounding;
479 
480  unsigned int jprev = (j+n_surrounding-1)%n_surrounding;
481  while (!surrounding_nodes[jprev])
482  jprev = (jprev+n_surrounding-1)%n_surrounding;
483 
484  return std::make_pair(jprev, jnext);
485  };
486 
487  // We may have too many surrounding nodes to handle with
488  // just one tet. In that case we'll keep a cache of the
489  // element qualities that we'd get by making a tet with the
490  // edge from the center node to each surrounding node, so we
491  // can build the best tets first.
492  //
493  // In the case where we just have 3 nodes, we'll just pretend
494  // they all have the same positive quality, so we can still
495  // search this vector.
496  std::vector<Real> local_tet_quality(n_surrounding, 1);
497 
498  // From our center node with N surrounding nodes we can make N-2
499  // tetrahedra. The first N-3 each replace two surface tets with
500  // two new surface tets.
501  //
502  // My first idea was to greedily pick nodes with the smallest
503  // local (solid) angles to get the best quality. This works in
504  // 2D, but such a node can give a pancake tet in 3D.
505  //
506  // My second idea was to greedily pick nodes with the highest
507  // prospective tet quality. This works for the first tets, but
508  // can leave a pancake tet behind.
509  //
510  // My third idea is to try to fix the lowest quality tets first,
511  // by picking cases where they have higher quality neighbors,
512  // and creating those neighbors so as to change them.
513 
514  auto find_new_tet_nodes =
515  [& local_tet_quality, & find_valid_nodes_around]
516  ()
517  {
518  unsigned int jbest = 0;
519  auto [jminus, jplus] = find_valid_nodes_around(jbest);
520  Real qneighbest = std::min(local_tet_quality[jminus],
521  local_tet_quality[jplus]);
522  for (auto j : make_range(std::size_t(1),
523  local_tet_quality.size()))
524  {
525  // We don't want to build a bad tet
526  if (local_tet_quality[j] <= 0)
527  continue;
528 
529  std::tie(jminus, jplus) = find_valid_nodes_around(j);
530  Real qneighj = std::min(local_tet_quality[jminus],
531  local_tet_quality[jplus]);
532 
533  // We don't want to build a tet that can't fix a neighbor
534  // if we can build one that can.
535  if (qneighbest <= 0 &&
536  qneighj > 0)
537  continue;
538 
539  // We want to try for the best possible fix.
540  if ((local_tet_quality[j] - qneighj) >
541  (local_tet_quality[jbest] - qneighj))
542  {
543  jbest = j;
544  qneighbest = qneighj;
545  }
546  }
547 
548  libmesh_error_msg_if
549  (local_tet_quality[jbest] <= 0,
550  "Cannot build non-singular non-inverted tet");
551 
552  std::tie(jminus, jplus) = find_valid_nodes_around(jbest);
553 
554  return std::make_tuple(jbest, jminus, jplus);
555  };
556 
557  if (n_surrounding > 3)
558  {
559  // We'll be searching local_tet_quality even after tet
560  // extraction disconnects us from some nodes; when we do we
561  // don't want to get one.
562  constexpr Real far_node = -1e6;
563 
564  // Vectors from the center node to each of its surrounding
565  // nodes are helpful for calculating prospective tet
566  // quality.
567  std::vector<Point> v0s(n_surrounding);
568  for (auto j : make_range(n_surrounding))
569  v0s[j] = *(Point *)surrounding_nodes[j] - *node;
570 
571  // Find the tet quality we'd potentially get from each
572  // possible choice of tet
573  auto local_tet_quality_of =
574  [& surrounding_nodes, & v0s, & find_valid_nodes_around]
575  (unsigned int j)
576  {
577  auto [jminus, jplus] = find_valid_nodes_around(j);
578 
579  // Anything proportional to the ratio of volume to
580  // total-edge-length-cubed should peak for perfect tets
581  // but hit 0 for pancakes and slivers.
582 
583  const Real total_len =
584  v0s[j].norm() + v0s[jminus].norm() + v0s[jplus].norm() +
585  (*(Point *)surrounding_nodes[jplus] -
586  *(Point *)surrounding_nodes[j]).norm() +
587  (*(Point *)surrounding_nodes[j] -
588  *(Point *)surrounding_nodes[jminus]).norm() +
589  (*(Point *)surrounding_nodes[jminus] -
590  *(Point *)surrounding_nodes[jplus]).norm();
591 
592  // Orientation here is tricky. Think of the triple
593  // product as (v1 cross v2) dot v3, with right hand rule.
594  const Real six_vol =
595  triple_product(v0s[jminus], v0s[jplus], v0s[j]);
596 
597  return six_vol / (total_len * total_len * total_len);
598  };
599 
600  for (auto j : make_range(n_surrounding))
601  local_tet_quality[j] = local_tet_quality_of(j);
602 
603  // If we have N surrounding nodes, we can make N tets and
604  // that'll bring us back to the 3-surrounding-node case to
605  // finish.
606  for (auto t : make_range(n_surrounding-3))
607  {
608  libmesh_ignore(t);
609 
610  auto [jbest, jminus, jplus] = find_new_tet_nodes();
611 
612  // Turn these four nodes into a tet
613  Node * nbest = surrounding_nodes[jbest],
614  * nminus = surrounding_nodes[jminus],
615  * nplus = surrounding_nodes[jplus];
616  this->add_tet(nminus->id(), nbest->id(), nplus->id(),
617  node->id());
618 
619  // Replace the old two triangles around these nodes with the
620  // proper two new triangles.
621  Elem * oldtri1 = surrounding_elems[jminus],
622  * oldtri2 = surrounding_elems[jbest],
623  * newtri1 = surface.add_elem(Elem::build(TRI3)),
624  * newtri2 = surface.add_elem(Elem::build(TRI3));
625 
626  const unsigned int c1 = oldtri1->get_node_index(node),
627  c2 = oldtri2->get_node_index(node);
628 
629  newtri1->set_node(0, node);
630  newtri1->set_node(1, nminus);
631  newtri1->set_node(2, nplus);
632 
633  surrounding_elems[jminus] = newtri1;
634 
635  Elem * neigh10 = oldtri1->neighbor_ptr(c1);
636  Elem * neigh12 = oldtri2->neighbor_ptr((c2+2)%3);
637  newtri1->set_neighbor(0, neigh10);
638  neigh10->set_neighbor(neigh10->which_neighbor_am_i(oldtri1), newtri1);
639  newtri1->set_neighbor(1, newtri2);
640  newtri1->set_neighbor(2, neigh12);
641  neigh12->set_neighbor(neigh12->which_neighbor_am_i(oldtri2), newtri1);
642 
643  newtri2->set_node(0, nplus);
644  newtri2->set_node(1, nminus);
645  newtri2->set_node(2, nbest);
646 
647  Elem * neigh21 = oldtri1->neighbor_ptr((c1+1)%3);
648  Elem * neigh22 = oldtri2->neighbor_ptr((c2+1)%3);
649  newtri2->set_neighbor(0, newtri1);
650  newtri2->set_neighbor(1, neigh21);
651  neigh21->set_neighbor(neigh21->which_neighbor_am_i(oldtri1), newtri2);
652  newtri2->set_neighbor(2, neigh22);
653  neigh22->set_neighbor(neigh22->which_neighbor_am_i(oldtri2), newtri2);
654 
655  for (unsigned int p : make_range(3))
656  {
657  nodes_to_elem_map[oldtri1->node_id(p)].erase(oldtri1->id());
658  nodes_to_elem_map[oldtri2->node_id(p)].erase(oldtri2->id());
659  nodes_to_elem_map[newtri1->node_id(p)].insert(newtri1->id());
660  nodes_to_elem_map[newtri2->node_id(p)].insert(newtri2->id());
661  }
662 
663  // We've finished replacing the old triangles.
664  surface.delete_elem(oldtri1);
665  surface.delete_elem(oldtri2);
666 
667  // The solid angle for the far node should now stay
668  // unchanged until we're out of this inner loop; let's
669  // recalculate it here, and then we'll be done with it.
670  Node * & nbestref = surrounding_nodes[jbest];
671  nodes_by_geometry.erase(node_index[nbestref]);
672  node_index[nbestref] =
673  nodes_by_geometry.emplace(geometry_at(*nbestref), nbestref);
674 
675  // The far node is no longer sharing an edge with our center
676  // node. Make sure we don't use it again with the center
677  // node.
678  local_tet_quality[jbest] = far_node;
679  nbestref = nullptr;
680 
681  // The potential tet qualities using the side nodes have
682  // changed now that they're directly connected to each
683  // other.
684  local_tet_quality[jminus] =
685  local_tet_quality_of(jminus);
686 
687  local_tet_quality[jplus] =
688  local_tet_quality_of(jplus);
689  }
690  }
691 
692  // Now we should have just 3 surrounding nodes, with which to
693  // make one tetrahedron. Put them in a counterclockwise
694  // (looking from outside) orientation, not the "best, clockwise,
695  // counterclockwise" we get from the lambda.
696  auto [j2, j1, j3] = find_new_tet_nodes();
697 
698  // Turn these last four nodes into a tet
699  Node * n1 = surrounding_nodes[j1],
700  * n2 = surrounding_nodes[j2],
701  * n3 = surrounding_nodes[j3];
702  this->add_tet(n1->id(), n2->id(), n3->id(), node->id());
703 
704  // Replace the three surface triangles of that tet with the new
705  // fourth triangle.
706  Elem * oldtri1 = surrounding_elems[j1],
707  * oldtri2 = surrounding_elems[j2],
708  * oldtri3 = surrounding_elems[j3],
709  * newtri = surface.add_elem(Elem::build(TRI3));
710 
711  const unsigned int c1 = oldtri1->get_node_index(node),
712  c2 = oldtri2->get_node_index(node),
713  c3 = oldtri3->get_node_index(node);
714 
715  newtri->set_node(0, n1);
716  newtri->set_node(1, n2);
717  newtri->set_node(2, n3);
718  Elem * neigh0 = oldtri1->neighbor_ptr((c1+1)%3);
719  newtri->set_neighbor(0, neigh0);
720  neigh0->set_neighbor(neigh0->which_neighbor_am_i(oldtri1), newtri);
721  Elem * neigh1 = oldtri2->neighbor_ptr((c2+1)%3);
722  newtri->set_neighbor(1, neigh1);
723  neigh1->set_neighbor(neigh1->which_neighbor_am_i(oldtri2), newtri);
724  Elem * neigh2 = oldtri3->neighbor_ptr((c3+1)%3);
725  newtri->set_neighbor(2, neigh2);
726  neigh2->set_neighbor(neigh2->which_neighbor_am_i(oldtri3), newtri);
727 
728  for (unsigned int p : make_range(3))
729  {
730  nodes_to_elem_map[oldtri1->node_id(p)].erase(oldtri1->id());
731  nodes_to_elem_map[oldtri2->node_id(p)].erase(oldtri2->id());
732  nodes_to_elem_map[oldtri3->node_id(p)].erase(oldtri3->id());
733  nodes_to_elem_map[newtri->node_id(p)].insert(newtri->id());
734  }
735 
736  // We've finished replacing the old triangles.
737  surface.delete_elem(oldtri1);
738  surface.delete_elem(oldtri2);
739  surface.delete_elem(oldtri3);
740 
741  // We should have used up all our surrounding nodes now, and we
742  // shouldn't have messed up our surface in the process, and our
743  // center node should no longer be part of the surface.
744 #ifdef DEBUG
745  surrounding_nodes[j1] = nullptr;
746  surrounding_nodes[j2] = nullptr;
747  surrounding_nodes[j3] = nullptr;
748 
749  for (auto ltq : surrounding_nodes)
750  libmesh_assert(!ltq);
751 
752  if (surface.n_elem() > 3)
753  verify_surface();
754 
755  for (const Elem * elem : surface.element_ptr_range())
756  for (auto p : make_range(3))
757  libmesh_assert_not_equal_to
758  (elem->node_ptr(p), node);
759 #endif
760 
761  // We've used up our center node, so it's not something we can
762  // eliminate again.
763  nodes_by_geometry.erase(geometry_it);
764  }
765 
766  // At this point our surface should just have two triangles left.
767  libmesh_assert_equal_to(surface.n_elem(), 2);
768 }
769 
770 
772  int n2,
773  int n3,
774  int n4)
775 {
776 #ifndef NDEBUG
777  const auto nn = this->n_nodes();
778  libmesh_assert_less(n1, nn);
779  libmesh_assert_less(n2, nn);
780  libmesh_assert_less(n3, nn);
781  libmesh_assert_less(n4, nn);
782 
783  const Point v12 = this->point(n2) - this->point(n1);
784  const Point v13 = this->point(n3) - this->point(n1);
785  const Point v14 = this->point(n4) - this->point(n1);
786  const Real six_vol = triple_product(v12, v13, v14);
787  libmesh_assert_greater(six_vol, Real(0));
788 #endif
789 
790  this->_triangulation.push_back({n1, n2, n3, n4});
791 }
792 
793 
794 } // namespace libMesh
virtual Point true_centroid() const
Definition: elem.C:594
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:1051
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:2558
A Node is like a Point, but with more information.
Definition: node.h:52
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:310
unsigned int get_node_index(const Node *node_ptr) const
Definition: elem.h:2545
virtual const Node * query_node_ptr(const dof_id_type i) const override final
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:1196
static constexpr Real TOLERANCE
std::vector< std::tuple< std::shared_ptr< Polygon >, bool, std::vector< unsigned int > > > _sidelinks_data
Data for links to sides.
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
Prepare a newly ecreated (or read) mesh for use.
Definition: mesh_base.C:759
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:844
virtual void retriangulate() override final
Create a triangulation (tetrahedralization) based on the current sides&#39; triangulations.
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:449
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.
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:1029
void libmesh_ignore(const Args &...)
virtual Real volume() const override
An optimized method for calculating the area of a C0Polyhedron.
ElemMappingType mapping_type() const
Definition: elem.h:3120
dof_id_type id() const
Definition: dof_object.h:828
unsigned int which_neighbor_am_i(const Elem *e) const
This function tells you which neighbor e is.
Definition: elem.h:2919
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition: elem.C:444
virtual bool is_vertex(const unsigned int i) const override
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:893
auto norm(const T &a) -> decltype(std::abs(a))
Definition: tensor_tools.h:74
void set_neighbor(const unsigned int i, Elem *n)
Assigns n as the neighbor.
Definition: elem.h:2618
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
C0Polyhedron(const std::vector< std::shared_ptr< Polygon >> &sides, Elem *p=nullptr)
Constructor.
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2598
std::unique_ptr< Elem > disconnected_clone() const override
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const Node * node_ptr(const unsigned int i) const
Definition: elem.h:2507
virtual Real volume() const
Definition: elem.C:3429
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:140
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
unsigned int n_extra_integers() const
Returns how many extra integers are associated to the DofObject.
Definition: dof_object.h:1170
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:2453
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:117
virtual std::vector< unsigned int > nodes_on_side(const unsigned int) const =0
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:299