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