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 
245 Point
246 C0Polyhedron::side_vertex_average_normal(const unsigned int s) const
247 {
248  libmesh_assert_less (s, this->n_sides());
249  libmesh_assert_equal_to(this->mapping_type(), LAGRANGE_MAP);
250  const auto poly_side_ptr = this->side_ptr(s);
251  const auto n_side_edges = poly_side_ptr->n_sides();
252 
253  // At the side vertex average, things simplify a bit
254  // We get the side "plane" normal at all vertices, then average them
255  Point normal;
256  Point current_edge = poly_side_ptr->point(1) - poly_side_ptr->point(0);
257  for (auto i : make_range(n_side_edges))
258  {
259  const Point next_edge = poly_side_ptr->point((i + 2) % n_side_edges) -
260  poly_side_ptr->point((i + 1) % n_side_edges);
261  const Point normal_at_vertex = current_edge.cross(next_edge);
262  normal += normal_at_vertex;
263  // Note: the sides are planar, we don't need to test them all
264  if (normal.norm_sq() > TOLERANCE)
265  break;
266  current_edge = next_edge;
267  }
268  bool outward_normal = std::get<1>(_sidelinks_data[s]);
269  return (outward_normal ? 1. : -1.) * normal.unit();
270 }
271 
272 
273 
275 {
276  this->_triangulation.clear();
277 
278  // We should already have a triangulation for each side. We'll turn
279  // that into a triangulation of the entire polyhedral surface
280  // (stored as a mesh, so we can use `find_neighbors()`, then turn
281  // that into a tetrahedralization by shrinking the volume one tet at
282  // a time, which should work fine for convex polyhedra.
283 
284  Parallel::Communicator comm_self; // the default communicator is 1 rank
285  ReplicatedMesh surface(comm_self);
286 
287  // poly_node_to_local_id[poly_node] is the local id of
288  // poly_node in the Polyhedron, which is also the global id of the
289  // corresponding node in the surface mesh.
290  std::unordered_map<Node *, unsigned int> poly_node_to_id;
291 
292  for (unsigned int s : make_range(this->n_sides()))
293  {
294  const auto & [side, inward_normal, node_map] = this->_sidelinks_data[s];
295 
296  for (auto t : make_range(side->n_subtriangles()))
297  {
298  Elem * tri = surface.add_elem(Elem::build(TRI3));
299 
300  const std::array<int, 3> subtri = side->subtriangle(t);
301 
302  for (int i : make_range(3))
303  {
304  const int side_id = subtri[i];
305  const Node * poly_node = side->node_ptr(side_id);
306 
307  libmesh_assert_less(side_id, node_map.size());
308  const unsigned int local_id = node_map[side_id];
309 
310  Node * surf_node = surface.query_node_ptr(local_id);
311  if (surf_node)
312  libmesh_assert_equal_to(*(const Point*)poly_node,
313  *(const Point*)surf_node);
314  else
315  surf_node = surface.add_point(*poly_node, local_id);
316 
317  // Get a consistent orientation for surface triangles,
318  // facing their zeta directions outward
319  const int tri_node = inward_normal ? i : 2-i;
320  tri->set_node(tri_node, surf_node);
321  }
322  }
323  }
324 
325  surface.allow_renumbering(false);
326  surface.prepare_for_use();
327 
328  // We should have a watertight surface with consistent triangle
329  // orientations. That's expensive to check.
330 #ifdef DEBUG
331  auto verify_surface = [& surface] ()
332  {
333  for (const Elem * elem : surface.element_ptr_range())
334  {
335  for (auto s : make_range(3))
336  {
337  const Elem * neigh = elem->neighbor_ptr(s);
338  libmesh_assert(neigh);
339  libmesh_assert_equal_to(neigh,
340  surface.elem_ptr(neigh->id()));
341  const unsigned int ns = neigh->which_neighbor_am_i(elem);
342  libmesh_assert_less(ns, 3);
343  libmesh_assert_equal_to(elem->node_ptr(s),
344  neigh->node_ptr((ns+1)%3));
345  libmesh_assert_equal_to(elem->node_ptr((s+1)%3),
346  neigh->node_ptr(ns));
347  }
348  }
349  };
350 
351  verify_surface();
352 #endif
353 
354  // We'll have to edit this as we change the surface elements, but we
355  // have a method to initialize it easily.
356  std::vector<std::vector<dof_id_type>> nodes_to_elem_vec_map;
357  MeshTools::build_nodes_to_elem_map(surface, nodes_to_elem_vec_map);
358 
359  // We'll be inserting and deleting entries heavily, so we'll use
360  // sets rather than vectors. We want to get the same results in
361  // parallel, so we'll use element ids rather than Elem pointers
362  std::vector<std::set<dof_id_type>> nodes_to_elem_map;
363  for (auto i : index_range(nodes_to_elem_vec_map))
364  nodes_to_elem_map.emplace_back
365  (nodes_to_elem_vec_map[i].begin(),
366  nodes_to_elem_vec_map[i].end());
367 
368  // Now start meshing the volume enclosed by surface, one tet at a
369  // time, with a greedy heuristic: find the vertex node with the most
370  // acutely convex (solid) angle and strip it out as tetrahedra.
371 
372  // We'll want a vector of surrounding nodes for multiple uses,
373  // sometimes with a similarly-sorted vector of surrounding elements
374  // to go with it.
375  auto surroundings_of =
376  [&nodes_to_elem_map, & surface]
377  (const Node & node,
378  std::vector<Elem *> * surrounding_elems)
379  {
380  const std::set<dof_id_type> & elems_by_node =
381  nodes_to_elem_map[node.id()];
382 
383  const unsigned int n_surrounding = elems_by_node.size();
384  libmesh_assert_greater_equal(n_surrounding, 3);
385 
386  if (surrounding_elems)
387  {
388  libmesh_assert(surrounding_elems->empty());
389  surrounding_elems->resize(n_surrounding);
390  }
391 
392  std::vector<Node *> surrounding_nodes(n_surrounding);
393 
394  Elem * elem = surface.elem_ptr(*elems_by_node.begin());
395  for (auto i : make_range(n_surrounding))
396  {
397  const unsigned int n = elem->get_node_index(&node);
398  libmesh_assert_not_equal_to(n, invalid_uint);
399  Node * next_node = elem->node_ptr((n+1)%3);
400  surrounding_nodes[i] = next_node;
401  if (surrounding_elems)
402  (*surrounding_elems)[i] = elem;
403  elem = elem->neighbor_ptr((n+2)%3);
404  libmesh_assert(elem);
405  libmesh_assert_equal_to(elem, surface.elem_ptr(elem->id()));
406 
407  // We should have a manifold here, but verifying that is
408  // expensive
409 #ifdef DEBUG
410  libmesh_assert_equal_to
411  (std::count(surrounding_nodes.begin(),
412  surrounding_nodes.end(), next_node),
413  1);
414 #endif
415  }
416 
417  // We should have finished a loop
418  libmesh_assert_equal_to
419  (elem, surface.elem_ptr(*elems_by_node.begin()));
420 
421  return surrounding_nodes;
422  };
423 
424  auto geometry_at = [&surroundings_of](const Node & node)
425  {
426  const std::vector<Node *> surrounding_nodes =
427  surroundings_of(node, nullptr);
428 
429  // Now sum up solid angles from tetrahedra created from the
430  // trivial triangulation of the surrounding nodes loop
431  Real total_solid_angle = 0;
432  const int n_surrounding =
433  cast_int<int>(surrounding_nodes.size());
434 
435  for (auto n : make_range(n_surrounding-2))
436  {
437  const Point
438  v01 = static_cast<Point>(*surrounding_nodes[n]) - node,
439  v02 = static_cast<Point>(*surrounding_nodes[n+1]) - node,
440  v03 = static_cast<Point>(*surrounding_nodes[n+2]) - node;
441 
442  total_solid_angle += solid_angle(v01, v02, v03);
443  }
444 
445  return std::make_pair(n_surrounding, total_solid_angle);
446  };
447 
448  // We'll keep track of solid angles and node valences so we don't
449  // waste time recomputing them when they haven't changed. We need
450  // to be able to search efficiently for the smallest angles of each
451  // valence, but also search efficiently for a particular node to
452  // remove and reinsert it when its connectivity changes.
453  //
454  // Since C++11 multimap has guaranteed that pairs with matching keys
455  // are kept in insertion order, so we can use Node * for values even
456  // in parallel.
457  typedef std::multimap<std::pair<int, Real>, Node*> node_map_type;
458  node_map_type nodes_by_geometry;
459  std::map<Node *, node_map_type::iterator> node_index;
460 
461  for (auto node : surface.node_ptr_range())
462  node_index[node] =
463  nodes_by_geometry.emplace(geometry_at(*node), node);
464 
465  // In 3D, this will require nested loops: an outer loop to remove
466  // each vertex, and an inner loop to remove multiple tetrahedra in
467  // cases where the vertex has more than 3 neighboring triangles.
468 
469  // We'll be done when there are only three "unremoved" nodes left,
470  // so they don't actually enclose any volume.
471  for (auto i : make_range(nodes_by_geometry.size()-3))
472  {
473  auto geometry_it = nodes_by_geometry.begin();
474  auto geometry_key = geometry_it->first;
475  auto [valence, angle] = geometry_key;
476  Node * node = geometry_it->second;
477  libmesh_ignore(i);
478 
479  // If our lowest-valence nodes are all points of non-convexity,
480  // skip to a higher valence.
481  while (angle > 2*pi-TOLERANCE)
482  {
483  geometry_it =
484  nodes_by_geometry.upper_bound
485  (std::make_pair(valence, Real(100)));
486  libmesh_assert(geometry_it != nodes_by_geometry.end());
487 
488  std::tie(geometry_key, node) = *geometry_it;
489  std::tie(valence, angle) = geometry_key;
490  }
491 
492  std::vector<Elem *> surrounding_elems;
493  std::vector<Node *> surrounding_nodes =
494  surroundings_of(*node, &surrounding_elems);
495 
496  const std::size_t n_surrounding = surrounding_nodes.size();
497 
498  // As we separate surrounding nodes from our center node, we'll
499  // be marking them as nullptr; we still need to be able to find
500  // predecessor and successor nodes in order afterward.
501  auto find_valid_nodes_around =
502  [n_surrounding, & surrounding_nodes]
503  (unsigned int j)
504  {
505  unsigned int jnext = (j+1)%n_surrounding;
506  while (!surrounding_nodes[jnext])
507  jnext = (jnext+1)%n_surrounding;
508 
509  unsigned int jprev = (j+n_surrounding-1)%n_surrounding;
510  while (!surrounding_nodes[jprev])
511  jprev = (jprev+n_surrounding-1)%n_surrounding;
512 
513  return std::make_pair(jprev, jnext);
514  };
515 
516  // We may have too many surrounding nodes to handle with
517  // just one tet. In that case we'll keep a cache of the
518  // element qualities that we'd get by making a tet with the
519  // edge from the center node to each surrounding node, so we
520  // can build the best tets first.
521  //
522  // In the case where we just have 3 nodes, we'll just pretend
523  // they all have the same positive quality, so we can still
524  // search this vector.
525  std::vector<Real> local_tet_quality(n_surrounding, 1);
526 
527  // From our center node with N surrounding nodes we can make N-2
528  // tetrahedra. The first N-3 each replace two surface tets with
529  // two new surface tets.
530  //
531  // My first idea was to greedily pick nodes with the smallest
532  // local (solid) angles to get the best quality. This works in
533  // 2D, but such a node can give a pancake tet in 3D.
534  //
535  // My second idea was to greedily pick nodes with the highest
536  // prospective tet quality. This works for the first tets, but
537  // can leave a pancake tet behind.
538  //
539  // My third idea is to try to fix the lowest quality tets first,
540  // by picking cases where they have higher quality neighbors,
541  // and creating those neighbors so as to change them.
542 
543  auto find_new_tet_nodes =
544  [& local_tet_quality, & find_valid_nodes_around]
545  ()
546  {
547  unsigned int jbest = 0;
548  auto [jminus, jplus] = find_valid_nodes_around(jbest);
549  Real qneighbest = std::min(local_tet_quality[jminus],
550  local_tet_quality[jplus]);
551  for (auto j : make_range(std::size_t(1),
552  local_tet_quality.size()))
553  {
554  // We don't want to build a bad tet
555  if (local_tet_quality[j] <= 0)
556  continue;
557 
558  std::tie(jminus, jplus) = find_valid_nodes_around(j);
559  Real qneighj = std::min(local_tet_quality[jminus],
560  local_tet_quality[jplus]);
561 
562  // We don't want to build a tet that can't fix a neighbor
563  // if we can build one that can.
564  if (qneighbest <= 0 &&
565  qneighj > 0)
566  continue;
567 
568  // We want to try for the best possible fix.
569  if ((local_tet_quality[j] - qneighj) >
570  (local_tet_quality[jbest] - qneighj))
571  {
572  jbest = j;
573  qneighbest = qneighj;
574  }
575  }
576 
577  libmesh_error_msg_if
578  (local_tet_quality[jbest] <= 0,
579  "Cannot build non-singular non-inverted tet");
580 
581  std::tie(jminus, jplus) = find_valid_nodes_around(jbest);
582 
583  return std::make_tuple(jbest, jminus, jplus);
584  };
585 
586  if (n_surrounding > 3)
587  {
588  // We'll be searching local_tet_quality even after tet
589  // extraction disconnects us from some nodes; when we do we
590  // don't want to get one.
591  constexpr Real far_node = -1e6;
592 
593  // Vectors from the center node to each of its surrounding
594  // nodes are helpful for calculating prospective tet
595  // quality.
596  std::vector<Point> v0s(n_surrounding);
597  for (auto j : make_range(n_surrounding))
598  v0s[j] = *(Point *)surrounding_nodes[j] - *node;
599 
600  // Find the tet quality we'd potentially get from each
601  // possible choice of tet
602  auto local_tet_quality_of =
603  [& surrounding_nodes, & v0s, & find_valid_nodes_around]
604  (unsigned int j)
605  {
606  auto [jminus, jplus] = find_valid_nodes_around(j);
607 
608  // Anything proportional to the ratio of volume to
609  // total-edge-length-cubed should peak for perfect tets
610  // but hit 0 for pancakes and slivers.
611 
612  const Real total_len =
613  v0s[j].norm() + v0s[jminus].norm() + v0s[jplus].norm() +
614  (*(Point *)surrounding_nodes[jplus] -
615  *(Point *)surrounding_nodes[j]).norm() +
616  (*(Point *)surrounding_nodes[j] -
617  *(Point *)surrounding_nodes[jminus]).norm() +
618  (*(Point *)surrounding_nodes[jminus] -
619  *(Point *)surrounding_nodes[jplus]).norm();
620 
621  // Orientation here is tricky. Think of the triple
622  // product as (v1 cross v2) dot v3, with right hand rule.
623  const Real six_vol =
624  triple_product(v0s[jminus], v0s[jplus], v0s[j]);
625 
626  return six_vol / (total_len * total_len * total_len);
627  };
628 
629  for (auto j : make_range(n_surrounding))
630  local_tet_quality[j] = local_tet_quality_of(j);
631 
632  // If we have N surrounding nodes, we can make N tets and
633  // that'll bring us back to the 3-surrounding-node case to
634  // finish.
635  for (auto t : make_range(n_surrounding-3))
636  {
637  libmesh_ignore(t);
638 
639  auto [jbest, jminus, jplus] = find_new_tet_nodes();
640 
641  // Turn these four nodes into a tet
642  Node * nbest = surrounding_nodes[jbest],
643  * nminus = surrounding_nodes[jminus],
644  * nplus = surrounding_nodes[jplus];
645  this->add_tet(nminus->id(), nbest->id(), nplus->id(),
646  node->id());
647 
648  // Replace the old two triangles around these nodes with the
649  // proper two new triangles.
650  Elem * oldtri1 = surrounding_elems[jminus],
651  * oldtri2 = surrounding_elems[jbest],
652  * newtri1 = surface.add_elem(Elem::build(TRI3)),
653  * newtri2 = surface.add_elem(Elem::build(TRI3));
654 
655  const unsigned int c1 = oldtri1->get_node_index(node),
656  c2 = oldtri2->get_node_index(node);
657 
658  newtri1->set_node(0, node);
659  newtri1->set_node(1, nminus);
660  newtri1->set_node(2, nplus);
661 
662  surrounding_elems[jminus] = newtri1;
663 
664  Elem * neigh10 = oldtri1->neighbor_ptr(c1);
665  Elem * neigh12 = oldtri2->neighbor_ptr((c2+2)%3);
666  newtri1->set_neighbor(0, neigh10);
667  neigh10->set_neighbor(neigh10->which_neighbor_am_i(oldtri1), newtri1);
668  newtri1->set_neighbor(1, newtri2);
669  newtri1->set_neighbor(2, neigh12);
670  neigh12->set_neighbor(neigh12->which_neighbor_am_i(oldtri2), newtri1);
671 
672  newtri2->set_node(0, nplus);
673  newtri2->set_node(1, nminus);
674  newtri2->set_node(2, nbest);
675 
676  Elem * neigh21 = oldtri1->neighbor_ptr((c1+1)%3);
677  Elem * neigh22 = oldtri2->neighbor_ptr((c2+1)%3);
678  newtri2->set_neighbor(0, newtri1);
679  newtri2->set_neighbor(1, neigh21);
680  neigh21->set_neighbor(neigh21->which_neighbor_am_i(oldtri1), newtri2);
681  newtri2->set_neighbor(2, neigh22);
682  neigh22->set_neighbor(neigh22->which_neighbor_am_i(oldtri2), newtri2);
683 
684  for (unsigned int p : make_range(3))
685  {
686  nodes_to_elem_map[oldtri1->node_id(p)].erase(oldtri1->id());
687  nodes_to_elem_map[oldtri2->node_id(p)].erase(oldtri2->id());
688  nodes_to_elem_map[newtri1->node_id(p)].insert(newtri1->id());
689  nodes_to_elem_map[newtri2->node_id(p)].insert(newtri2->id());
690  }
691 
692  // We've finished replacing the old triangles.
693  surface.delete_elem(oldtri1);
694  surface.delete_elem(oldtri2);
695 
696  // The solid angle for the far node should now stay
697  // unchanged until we're out of this inner loop; let's
698  // recalculate it here, and then we'll be done with it.
699  Node * & nbestref = surrounding_nodes[jbest];
700  nodes_by_geometry.erase(node_index[nbestref]);
701  node_index[nbestref] =
702  nodes_by_geometry.emplace(geometry_at(*nbestref), nbestref);
703 
704  // The far node is no longer sharing an edge with our center
705  // node. Make sure we don't use it again with the center
706  // node.
707  local_tet_quality[jbest] = far_node;
708  nbestref = nullptr;
709 
710  // The potential tet qualities using the side nodes have
711  // changed now that they're directly connected to each
712  // other.
713  local_tet_quality[jminus] =
714  local_tet_quality_of(jminus);
715 
716  local_tet_quality[jplus] =
717  local_tet_quality_of(jplus);
718  }
719  }
720 
721  // Now we should have just 3 surrounding nodes, with which to
722  // make one tetrahedron. Put them in a counterclockwise
723  // (looking from outside) orientation, not the "best, clockwise,
724  // counterclockwise" we get from the lambda.
725  auto [j2, j1, j3] = find_new_tet_nodes();
726 
727  // Turn these last four nodes into a tet
728  Node * n1 = surrounding_nodes[j1],
729  * n2 = surrounding_nodes[j2],
730  * n3 = surrounding_nodes[j3];
731  this->add_tet(n1->id(), n2->id(), n3->id(), node->id());
732 
733  // Replace the three surface triangles of that tet with the new
734  // fourth triangle.
735  Elem * oldtri1 = surrounding_elems[j1],
736  * oldtri2 = surrounding_elems[j2],
737  * oldtri3 = surrounding_elems[j3],
738  * newtri = surface.add_elem(Elem::build(TRI3));
739 
740  const unsigned int c1 = oldtri1->get_node_index(node),
741  c2 = oldtri2->get_node_index(node),
742  c3 = oldtri3->get_node_index(node);
743 
744  newtri->set_node(0, n1);
745  newtri->set_node(1, n2);
746  newtri->set_node(2, n3);
747  Elem * neigh0 = oldtri1->neighbor_ptr((c1+1)%3);
748  newtri->set_neighbor(0, neigh0);
749  neigh0->set_neighbor(neigh0->which_neighbor_am_i(oldtri1), newtri);
750  Elem * neigh1 = oldtri2->neighbor_ptr((c2+1)%3);
751  newtri->set_neighbor(1, neigh1);
752  neigh1->set_neighbor(neigh1->which_neighbor_am_i(oldtri2), newtri);
753  Elem * neigh2 = oldtri3->neighbor_ptr((c3+1)%3);
754  newtri->set_neighbor(2, neigh2);
755  neigh2->set_neighbor(neigh2->which_neighbor_am_i(oldtri3), newtri);
756 
757  for (unsigned int p : make_range(3))
758  {
759  nodes_to_elem_map[oldtri1->node_id(p)].erase(oldtri1->id());
760  nodes_to_elem_map[oldtri2->node_id(p)].erase(oldtri2->id());
761  nodes_to_elem_map[oldtri3->node_id(p)].erase(oldtri3->id());
762  nodes_to_elem_map[newtri->node_id(p)].insert(newtri->id());
763  }
764 
765  // We've finished replacing the old triangles.
766  surface.delete_elem(oldtri1);
767  surface.delete_elem(oldtri2);
768  surface.delete_elem(oldtri3);
769 
770  // We should have used up all our surrounding nodes now, and we
771  // shouldn't have messed up our surface in the process, and our
772  // center node should no longer be part of the surface.
773 #ifdef DEBUG
774  surrounding_nodes[j1] = nullptr;
775  surrounding_nodes[j2] = nullptr;
776  surrounding_nodes[j3] = nullptr;
777 
778  for (auto ltq : surrounding_nodes)
779  libmesh_assert(!ltq);
780 
781  if (surface.n_elem() > 3)
782  verify_surface();
783 
784  for (const Elem * elem : surface.element_ptr_range())
785  for (auto p : make_range(3))
786  libmesh_assert_not_equal_to
787  (elem->node_ptr(p), node);
788 #endif
789 
790  // We've used up our center node, so it's not something we can
791  // eliminate again.
792  nodes_by_geometry.erase(geometry_it);
793  }
794 
795  // At this point our surface should just have two triangles left.
796  libmesh_assert_equal_to(surface.n_elem(), 2);
797 }
798 
799 
801  int n2,
802  int n3,
803  int n4)
804 {
805 #ifndef NDEBUG
806  const auto nn = this->n_nodes();
807  libmesh_assert_less(n1, nn);
808  libmesh_assert_less(n2, nn);
809  libmesh_assert_less(n3, nn);
810  libmesh_assert_less(n4, nn);
811 
812  const Point v12 = this->point(n2) - this->point(n1);
813  const Point v13 = this->point(n3) - this->point(n1);
814  const Point v14 = this->point(n4) - this->point(n1);
815  const Real six_vol = triple_product(v12, v13, v14);
816  libmesh_assert_greater(six_vol, Real(0));
817 #endif
818 
819  this->_triangulation.push_back({n1, n2, n3, n4});
820 }
821 
822 
823 } // namespace libMesh
virtual Point true_centroid() const
Definition: elem.C:592
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:2559
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:2546
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: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:1029
TypeVector< T > unit() const
Definition: type_vector.h:1104
void libmesh_ignore(const Args &...)
auto norm_sq() const -> decltype(std::norm(T()))
Definition: type_vector.h:926
virtual Real volume() const override
An optimized method for calculating the area of a C0Polyhedron.
ElemMappingType mapping_type() const
Definition: elem.h:3121
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:2920
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
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
TypeVector< typename CompareTypes< T, T2 >::supertype > cross(const TypeVector< T2 > &v) const
Definition: type_vector.h:884
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:2619
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:2599
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:2508
virtual Real volume() const
Definition: elem.C:3433
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
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:2454
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