LCOV - code coverage report
Current view: top level - src/geom - cell_polyhedron.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4475 (55045b) with base a68cc6 Lines: 252 333 75.7 %
Date: 2026-06-03 14:29:06 Functions: 18 28 64.3 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : #include "libmesh/cell_polyhedron.h"
      19             : 
      20             : // Local includes
      21             : #include "libmesh/face_polygon.h"
      22             : #include "libmesh/enum_elem_quality.h"
      23             : #include "libmesh/hashword.h"
      24             : 
      25             : // C++ includes
      26             : #include <array>
      27             : #include <unordered_map>
      28             : #include <unordered_set>
      29             : 
      30             : 
      31             : namespace libMesh
      32             : {
      33             : 
      34             : // ------------------------------------------------------------
      35             : // Polyhedron class static member initializations
      36             : const int Polyhedron::num_children;
      37             : 
      38             : // ------------------------------------------------------------
      39             : // Polyhedron class member functions
      40             : 
      41             : 
      42        6121 : Polyhedron::Polyhedron (const std::vector<std::shared_ptr<Polygon>> & sides,
      43        6121 :                         Elem * p) :
      44         348 :     Cell(/* unused here */ 0, sides.size(), p, nullptr, nullptr),
      45         348 :     _elemlinks_data(sides.size()+2), // neighbors + parent + interior_parent
      46        5773 :     _nodelinks_data(0), // We'll have to resize *this* later too!
      47       12068 :     _sidelinks_data(sides.size())
      48             :   {
      49             :     // Set our sides, and while we're at it figure out our node maps
      50             :     // and side normal directions and edge lookup table, and count our
      51             :     // sides' nodes.  If we have internal nodes too then the subclass
      52             :     // will append those afterward.
      53        6121 :     unsigned int nn = 0;
      54         348 :     std::unordered_map<Node *, unsigned int> local_node_number;
      55             :     std::unordered_set<std::pair<const Node *, const Node *>,
      56         348 :                        libMesh::hash> edges_seen;
      57        6121 :     std::unique_ptr<const Elem> edge;
      58       43131 :     for (unsigned int s : index_range(sides))
      59             :       {
      60        1052 :         libmesh_assert(sides[s].get());
      61       37010 :         auto & side_tuple = _sidelinks_data[s];
      62        2104 :         std::get<0>(side_tuple) = sides[s];
      63             : 
      64        2104 :         Polygon & side = *sides[s]; // not const, for writeable nodes
      65      185618 :         for (auto n : make_range(side.n_nodes()))
      66             :           {
      67      152832 :             Node * node = side.node_ptr(n);
      68      148608 :             if (auto it = local_node_number.find(node);
      69        4224 :                 it != local_node_number.end())
      70             :               {
      71       99072 :                 std::get<2>(side_tuple).push_back(it->second);
      72             :               }
      73             :             else
      74             :               {
      75       49536 :                 std::get<2>(side_tuple).push_back(nn);
      76       49536 :                 local_node_number[node] = nn++;
      77       49536 :                 _nodelinks_data.push_back(node);
      78             :               }
      79             :           }
      80             : 
      81      185618 :         for (unsigned int e : make_range(side.n_edges()))
      82             :           {
      83      148608 :             side.build_edge_ptr(edge, e);
      84      148608 :             auto edge_vertices = std::make_pair(edge->node_ptr(0), edge->node_ptr(1));
      85      148608 :             if (edge_vertices.first > edge_vertices.second)
      86        2136 :               std::swap(edge_vertices.first, edge_vertices.second);
      87             : 
      88        8448 :             if (!edges_seen.count(edge_vertices))
      89             :               {
      90        2112 :                 edges_seen.insert(edge_vertices);
      91       74304 :                 _edge_lookup.emplace_back(s, e);
      92             :               }
      93             :           }
      94             :       }
      95             : 
      96             :     // Plan room for an extra node so we don't invalidate this->_nodes when we add to
      97             :     // nodelinks_data in the derived class
      98        6295 :     _nodelinks_data.reserve(_nodelinks_data.size() + 1);
      99             : 
     100             :     // Do the manual initialization that Elem::Elem and Cell::Cell
     101             :     // couldn't, now that we've resized both our vectors.  No need to
     102             :     // manually set nullptr, though, since std::vector does that.
     103        6121 :     this->_elemlinks = _elemlinks_data.data();
     104        6121 :     this->_nodes = _nodelinks_data.data();
     105        6121 :     this->_elemlinks[0] = p;
     106             : 
     107         174 :     libmesh_assert_equal_to(nn, this->n_nodes());
     108             : 
     109             :     // Figure out the orientation of our sides, now that we've got our
     110             :     // nodes organized enough to find our center.  The algorithm below
     111             :     // only works for convex polyhedra, but that's all we're
     112             :     // supporting for now.
     113         174 :     Point center;
     114       55657 :     for (auto n : make_range(nn))
     115        1408 :       center.add (this->point(n));
     116        6121 :     center /= static_cast<Real>(nn);
     117             : 
     118       43131 :     for (unsigned int s : index_range(sides))
     119             :       {
     120        2104 :         const Polygon & side = *sides[s];
     121       38062 :         const Point x_i = side.point(0);
     122             :         const Point n_i =
     123       35958 :           (side.point(1) - side.point(0)).cross
     124       38062 :           (side.point(0) - side.point(side.n_sides()-1)).unit();
     125             : 
     126        2104 :         bool & inward_normal = std::get<1>(_sidelinks_data[s]);
     127       37010 :         inward_normal = (n_i * (center - x_i) > TOLERANCE);
     128             :       }
     129             : 
     130             :     // We're betting a lot on "our polyhedra are all convex", so let's
     131             :     // check that if we have time.
     132             : #ifdef DEBUG
     133        1226 :     for (unsigned int s : index_range(sides))
     134             :       {
     135        1052 :         const Polygon & side = *sides[s];
     136        1052 :         const Point x_i = side.point(0);
     137        1052 :         const bool inward_normal = std::get<1>(this->_sidelinks_data[s]);
     138             : 
     139             :         const Point n_i =
     140        1052 :           (side.point(1) - side.point(0)).cross
     141        1052 :           (side.point(0) - side.point(side.n_sides()-1)).unit() *
     142        2104 :           (inward_normal ? -1 : 1);
     143             : 
     144        9596 :         for (const Point & node : this->node_ref_range())
     145             :           {
     146        8544 :             const Point d_n = node - x_i;
     147        8544 :             if (d_n * n_i > TOLERANCE * d_n.norm())
     148           0 :               libmesh_not_implemented_msg
     149             :                 ("Cannot create a non-convex polyhedron");
     150             :           }
     151             :       }
     152             : #endif
     153             : 
     154             :     // Is this likely to ever be used?  We may do refinement with
     155             :     // polyhedra but it's probably not going to have a hierarchy...
     156        6121 :     if (p)
     157             :       {
     158           0 :         this->subdomain_id() = p->subdomain_id();
     159           0 :         this->processor_id() = p->processor_id();
     160           0 :         _map_type = p->mapping_type();
     161           0 :         _map_data = p->mapping_data();
     162             : 
     163             : #ifdef LIBMESH_ENABLE_AMR
     164           0 :         this->set_p_level(p->p_level());
     165             : #endif
     166             :       }
     167             : 
     168             :     // Make sure the interior parent isn't undefined
     169        6121 :     this->set_interior_parent(nullptr);
     170       11894 :   }
     171             : 
     172             : 
     173             : 
     174   884547804 : Point Polyhedron::master_point (const unsigned int i) const
     175             : {
     176   957286596 :   return this->point(i);
     177             : }
     178             : 
     179             : 
     180             : 
     181           0 : bool Polyhedron::convex()
     182             : {
     183           0 :   for (unsigned int s : make_range(this->n_sides()))
     184             :     {
     185           0 :       const Polygon & side = *std::get<0>(this->_sidelinks_data[s]);
     186           0 :       const Point x_i = side.point(0);
     187           0 :       const bool inward_normal = std::get<1>(this->_sidelinks_data[s]);
     188             : 
     189             :       const Point n_i =
     190           0 :         (side.point(1) - side.point(0)).cross
     191           0 :         (side.point(0) - side.point(side.n_sides()-1)).unit() *
     192           0 :         (inward_normal ? -1 : 1);
     193             : 
     194           0 :       for (const Point & node : this->node_ref_range())
     195             :         {
     196           0 :           const Point d_n = node - x_i;
     197           0 :           if (d_n * n_i > TOLERANCE * d_n.norm())
     198           0 :             return false;
     199             :         }
     200             :     }
     201           0 :   return true;
     202             : }
     203             : 
     204             : 
     205             : 
     206      442986 : bool Polyhedron::on_reference_element(const Point & p,
     207             :                                       const Real eps) const
     208             : {
     209       57898 :   const unsigned int ns = this->n_sides();
     210             : 
     211             :   // Check that the point is on the same side of all the faces by
     212             :   // testing whether:
     213             :   //
     214             :   // n_i.(p - x_i) <= 0
     215             :   //
     216             :   // for each i, where:
     217             :   //   n_i is the outward normal of face i,
     218             :   //   x_i is a point on face i.
     219             : 
     220     2349102 :   for (auto i : make_range(ns))
     221             :     {
     222     2051436 :       const Polygon & face = *std::get<0>(this->_sidelinks_data[i]);
     223     2051436 :       const bool inward_normal = std::get<1>(this->_sidelinks_data[i]);
     224             : 
     225     2051436 :       const Point x_i = face.point(0);
     226             : 
     227             :       const Point n_i =
     228     1891928 :         (face.point(1) - face.point(0)).cross
     229     2210944 :         (face.point(0) - face.point(face.n_sides()-1)).unit() *
     230     2348284 :         (inward_normal ? -1 : 1);
     231             : 
     232             :   // This only works for polyhedra with flat sides.
     233             : #ifdef DEBUG
     234     1187392 :       for (auto j : make_range(face.n_sides()-1))
     235             :         {
     236      890544 :           const Point x_j = face.point(j+1);
     237      890544 :           const Point d_j = x_j - x_i;
     238      890544 :           if (std::abs(d_j * n_i) > eps * d_j.norm())
     239           0 :             libmesh_not_implemented_msg
     240             :               ("Polyhedra with non-flat sides are not fully supported.");
     241             :         }
     242             : #endif
     243             : 
     244     2051436 :       if (n_i * (p - x_i) > eps)
     245      133210 :         return false;
     246             :     }
     247             : 
     248       68686 :   return true;
     249             : }
     250             : 
     251             : 
     252             : 
     253           0 : dof_id_type Polyhedron::key (const unsigned int s) const
     254             : {
     255           0 :   libmesh_assert_less (s, this->n_sides());
     256             : 
     257           0 :   const Polygon & face = *std::get<0>(this->_sidelinks_data[s]);
     258             : 
     259           0 :   return face.key();
     260             : }
     261             : 
     262             : 
     263             : 
     264       36068 : dof_id_type Polyhedron::low_order_key (const unsigned int s) const
     265             : {
     266        1016 :   libmesh_assert_less (s, this->n_sides());
     267             : 
     268       36068 :   const Polygon & face = *std::get<0>(this->_sidelinks_data[s]);
     269             : 
     270        1016 :   const unsigned int nv = face.n_vertices();
     271       37084 :   std::vector<dof_id_type> vertex_ids(nv);
     272      180908 :   for (unsigned int v : make_range(nv))
     273      153000 :     vertex_ids[v] = face.node_id(v);
     274             : 
     275       37084 :   return Utility::hashword(vertex_ids);
     276             : }
     277             : 
     278             : 
     279             : 
     280           0 : unsigned int Polyhedron::local_side_node(unsigned int side,
     281             :                                          unsigned int side_node) const
     282             : {
     283           0 :   libmesh_assert_less (side, this->n_sides());
     284             : 
     285             :   const std::vector<unsigned int> & node_map =
     286           0 :     std::get<2>(this->_sidelinks_data[side]);
     287           0 :   libmesh_assert_less (side_node, node_map.size());
     288             : 
     289           0 :   return node_map[side_node];
     290             : }
     291             : 
     292             : 
     293             : 
     294        3312 : unsigned int Polyhedron::local_edge_node(unsigned int edge,
     295             :                                          unsigned int edge_node) const
     296             : {
     297         276 :   libmesh_assert_less (edge, this->n_edges());
     298         276 :   libmesh_assert_less (edge, _edge_lookup.size());
     299             : 
     300        3312 :   auto [side, edge_of_side] = _edge_lookup[edge];
     301             : 
     302        3312 :   const Polygon & face = *std::get<0>(this->_sidelinks_data[side]);
     303             : 
     304             :   const std::vector<unsigned int> & node_map =
     305         276 :     std::get<2>(this->_sidelinks_data[side]);
     306             : 
     307        3312 :   return node_map[face.local_edge_node(edge_of_side, edge_node)];
     308             : }
     309             : 
     310             : 
     311             : 
     312           0 : dof_id_type Polyhedron::key () const
     313             : {
     314           0 :   std::vector<dof_id_type> node_ids;
     315           0 :   for (const auto & n : this->node_ref_range())
     316           0 :     node_ids.push_back(n.id());
     317             : 
     318           0 :   return Utility::hashword(node_ids);
     319             : }
     320             : 
     321             : 
     322             : 
     323        2492 : std::unique_ptr<Elem> Polyhedron::side_ptr (const unsigned int i)
     324             : {
     325        2492 :   return const_cast<const Polyhedron *>(this)->side_ptr(i);
     326             : }
     327             : 
     328             : 
     329             : 
     330        3344 : std::unique_ptr<Elem> Polyhedron::side_ptr (const unsigned int i) const
     331             : {
     332         122 :   libmesh_assert_less (i, this->n_sides());
     333             : 
     334        3344 :   Polygon & face = *std::get<0>(this->_sidelinks_data[i]);
     335        3344 :   std::unique_ptr<Elem> face_copy = face.disconnected_clone();
     336       17288 :   for (auto n : face.node_index_range())
     337       14448 :     face_copy->set_node(n, face.node_ptr(n));
     338             : 
     339        3344 :   return face_copy;
     340           0 : }
     341             : 
     342             : 
     343             : 
     344        1270 : void Polyhedron::side_ptr (std::unique_ptr<Elem> & side,
     345             :                            const unsigned int i)
     346             : {
     347          51 :   libmesh_assert_less (i, this->n_sides());
     348             : 
     349             :   // Polyhedra are irregular enough that we're not even going to try
     350             :   // and bother optimizing heap access here.
     351        2438 :   side = this->side_ptr(i);
     352        1270 : }
     353             : 
     354             : 
     355             : 
     356        1222 : std::unique_ptr<Elem> Polyhedron::build_side_ptr (const unsigned int i)
     357             : {
     358        1222 :   auto returnval = this->side_ptr(i);
     359        1222 :   returnval->set_interior_parent(this);
     360        1175 :   returnval->inherit_data_from(*this);
     361        1222 :   return returnval;
     362           0 : }
     363             : 
     364             : 
     365             : 
     366        1270 : void Polyhedron::build_side_ptr (std::unique_ptr<Elem> & side,
     367             :                                  const unsigned int i)
     368             : {
     369        1270 :   this->side_ptr(side, i);
     370        1270 :   side->set_interior_parent(this);
     371        1219 :   side->inherit_data_from(*this);
     372        1270 : }
     373             : 
     374             : 
     375             : 
     376           0 : std::unique_ptr<Elem> Polyhedron::build_edge_ptr (const unsigned int i)
     377             : {
     378           0 :   auto [s, se] = _edge_lookup[i];
     379           0 :   Polygon & face = *std::get<0>(_sidelinks_data[s]);
     380           0 :   return face.build_edge_ptr(se);
     381             : }
     382             : 
     383             : 
     384             : 
     385           0 : void Polyhedron::build_edge_ptr (std::unique_ptr<Elem> & elem,
     386             :                                  const unsigned int i)
     387             : {
     388           0 :   auto [s, se] = _edge_lookup[i];
     389           0 :   Polygon & face = *std::get<0>(_sidelinks_data[s]);
     390           0 :   face.build_edge_ptr(elem, se);
     391           0 : }
     392             : 
     393             : 
     394             : 
     395           0 : bool Polyhedron::is_child_on_side(const unsigned int /*c*/,
     396             :                                   const unsigned int /*s*/) const
     397             : {
     398           0 :   libmesh_not_implemented();
     399             :   return false;
     400             : }
     401             : 
     402             : 
     403             : 
     404           0 : unsigned int Polyhedron::opposite_side(const unsigned int /* side_in */) const
     405             : {
     406             :   // This is too ambiguous in general.
     407           0 :   libmesh_not_implemented();
     408             :   return libMesh::invalid_uint;
     409             : }
     410             : 
     411             : 
     412             : 
     413           0 : unsigned int Polyhedron::opposite_node(const unsigned int /* n */,
     414             :                                        const unsigned int /* s */) const
     415             : {
     416             :   // This is too ambiguous in general.
     417           0 :   libmesh_not_implemented();
     418             :   return libMesh::invalid_uint;
     419             : }
     420             : 
     421             : 
     422             : 
     423         205 : bool Polyhedron::is_flipped() const
     424             : {
     425         205 :   if (this->_triangulation.empty())
     426           0 :     return false;
     427             : 
     428          10 :   auto & tet = this->_triangulation[0];
     429             : 
     430         215 :   const Point v01 = this->point(tet[1]) - this->point(tet[0]);
     431         205 :   const Point v02 = this->point(tet[2]) - this->point(tet[0]);
     432         205 :   const Point v03 = this->point(tet[3]) - this->point(tet[0]);
     433             : 
     434         205 :   return (triple_product(v01, v02, v03) < 0);
     435             : }
     436             : 
     437             : 
     438             : std::vector<unsigned int>
     439         420 : Polyhedron::edges_adjacent_to_node(const unsigned int n) const
     440             : {
     441          35 :   libmesh_assert_less(n, this->n_nodes());
     442             : 
     443             :   // For mid-edge or mid-face nodes, the subclass had better have
     444             :   // overridden this.
     445          35 :   libmesh_assert_less(n, this->n_vertices());
     446             : 
     447          35 :   const unsigned int ne = this->n_edges();
     448          35 :   libmesh_assert_equal_to(ne, _edge_lookup.size());
     449             : 
     450          35 :   std::vector<unsigned int> adjacent_edges;
     451             : 
     452         420 :   unsigned int next_edge = 0;
     453             : 
     454             :   // Look for any adjacent edges on each side.  Make use of the fact
     455             :   // that we number our edges in order as we encounter them from each
     456             :   // side.
     457        2100 :   for (auto t : index_range(_sidelinks_data))
     458             :     {
     459         350 :       const Polygon & face = *std::get<0>(_sidelinks_data[t]);
     460             :       const std::vector<unsigned int> & node_map =
     461         175 :         std::get<2>(_sidelinks_data[t]);
     462             : 
     463        3185 :       while (_edge_lookup[next_edge].first < t)
     464             :         {
     465         840 :           ++next_edge;
     466         840 :           if (next_edge == ne)
     467          35 :             return adjacent_edges;
     468             :         }
     469             : 
     470             :       // If we haven't seen the next edge on this or an earlier side
     471             :       // then we might as well go on to the next.
     472        2100 :       if (_edge_lookup[next_edge].first > t)
     473           0 :         continue;
     474             : 
     475         175 :       const unsigned int fnv = face.n_vertices();
     476         175 :       libmesh_assert_equal_to(fnv, face.n_edges());
     477         175 :       libmesh_assert_equal_to(fnv, face.n_sides());
     478         175 :       libmesh_assert_less_equal(fnv, node_map.size());
     479             : 
     480             :       // Polygon faces have one edge per vertex
     481        8400 :       for (auto v : make_range(fnv))
     482             :         {
     483         630 :           libmesh_assert_equal_to (_edge_lookup[next_edge].first, t);
     484             : 
     485        8190 :           if (_edge_lookup[next_edge].second > v)
     486        1155 :             continue;
     487             : 
     488       11690 :           while (_edge_lookup[next_edge].first == t &&
     489        9240 :                  _edge_lookup[next_edge].second < v)
     490             :             {
     491        4200 :               ++next_edge;
     492        4200 :               if (next_edge == ne)
     493          35 :                 return adjacent_edges;
     494             :             }
     495             : 
     496        5880 :           if (_edge_lookup[next_edge].first > t)
     497          70 :             break;
     498             : 
     499        5040 :           const unsigned int vn = node_map[v];
     500        5040 :           const unsigned int vnp = node_map[(v+1)%fnv];
     501         420 :           libmesh_assert_less(vn, this->n_vertices());
     502         420 :           libmesh_assert_less(vnp, this->n_vertices());
     503        5040 :           if (vn == n || vnp == n)
     504        1260 :             adjacent_edges.push_back(next_edge);
     505             :         }
     506             :     }
     507             : 
     508           0 :   return adjacent_edges;
     509             : }
     510             : 
     511             : 
     512           0 : std::pair<Real, Real> Polyhedron::qual_bounds (const ElemQuality q) const
     513             : {
     514           0 :   std::pair<Real, Real> bounds;
     515             : 
     516           0 :   switch (q)
     517             :     {
     518           0 :     case EDGE_LENGTH_RATIO:
     519           0 :       bounds.first  = 1.;
     520           0 :       bounds.second = 4.;
     521           0 :       break;
     522             : 
     523           0 :     case MIN_ANGLE:
     524           0 :       bounds.first  = 30.;
     525           0 :       bounds.second = 180.;
     526           0 :       break;
     527             : 
     528           0 :     case MAX_ANGLE:
     529           0 :       bounds.first  = 60.;
     530           0 :       bounds.second = 180.;
     531           0 :       break;
     532             : 
     533           0 :     case JACOBIAN:
     534             :     case SCALED_JACOBIAN:
     535           0 :       bounds.first  = 0.5;
     536           0 :       bounds.second = 1.;
     537           0 :       break;
     538             : 
     539           0 :     default:
     540           0 :       libMesh::out << "Warning: Invalid quality measure chosen." << std::endl;
     541           0 :       bounds.first  = -1;
     542           0 :       bounds.second = -1;
     543             :     }
     544             : 
     545           0 :   return bounds;
     546             : }
     547             : 
     548             : 
     549             : 
     550             : std::vector<std::shared_ptr<Polygon>>
     551          15 : Polyhedron::side_clones() const
     552             : {
     553           2 :   const auto ns = this->n_sides();
     554             : 
     555           2 :   libmesh_assert_equal_to(ns, _sidelinks_data.size());
     556             : 
     557          15 :   std::vector<std::shared_ptr<Polygon>> cloned_sides(ns);
     558             : 
     559         105 :   for (auto i : make_range(ns))
     560             :     {
     561          90 :       const Polygon & face = *std::get<0>(this->_sidelinks_data[i]);
     562             : 
     563          90 :       Elem * clone = face.disconnected_clone().release();
     564          12 :       Polygon * polygon_clone = cast_ptr<Polygon *>(clone);
     565          90 :       cloned_sides[i] = std::shared_ptr<Polygon>(polygon_clone);
     566             : 
     567             :       // We can't actually use a *disconnected* clone to reconstruct
     568             :       // links between sides, so we'll temporarily give the clone our
     569             :       // own nodes; user code that typically replaces the usual
     570             :       // nullptr with permanent nodes will then instead place our
     571             :       // nodes with permanent nodes.
     572         528 :       for (auto n : make_range(face.n_nodes()))
     573          96 :         cloned_sides[i]->set_node
     574         408 :           (n, const_cast<Node *>(face.node_ptr(n)));
     575             :     }
     576             : 
     577          17 :   return cloned_sides;
     578           0 : }
     579             : 
     580             : 
     581             : 
     582        2208 : bool Polyhedron::side_has_edge_nodes(unsigned int s,
     583             :                                      unsigned int min_node,
     584             :                                      unsigned int max_node) const
     585             : {
     586        2208 :   const Polygon & face = *std::get<0>(_sidelinks_data[s]);
     587             :   const std::vector<unsigned int> & node_map =
     588         184 :     std::get<2>(this->_sidelinks_data[s]);
     589             : 
     590        9300 :   for (unsigned int e : make_range(face.n_sides()))
     591             :     {
     592             :       std::vector<unsigned int> nodes_on_edge =
     593        7812 :         face.nodes_on_side(e);
     594         651 :       libmesh_assert_equal_to(nodes_on_edge.size(), 2);
     595        7812 :       nodes_on_edge[0] = node_map[nodes_on_edge[0]];
     596        7812 :       nodes_on_edge[1] = node_map[nodes_on_edge[1]];
     597        7901 :       if ((nodes_on_edge[0] == min_node) &&
     598          89 :           (nodes_on_edge[1] == max_node))
     599          60 :         return true;
     600        7536 :       if ((nodes_on_edge[1] == min_node) &&
     601          84 :           (nodes_on_edge[0] == max_node))
     602          60 :         return true;
     603             :     }
     604             : 
     605         248 :   return false;
     606             : }
     607             : 
     608             : 
     609             : 
     610             : std::vector<unsigned int>
     611         576 : Polyhedron::sides_on_edge(const unsigned int e) const
     612             : {
     613         576 :   std::vector<unsigned int> returnval(2);
     614         576 :   auto [s1, s1e] = _edge_lookup[e];
     615         576 :   returnval[0] = s1;
     616             : 
     617         576 :   const Polygon & face1 = *std::get<0>(_sidelinks_data[s1]);
     618             :   const std::vector<unsigned int> & node_map =
     619          48 :     std::get<2>(this->_sidelinks_data[s1]);
     620             : 
     621             :   std::vector<unsigned int> nodes_on_edge =
     622         576 :     face1.nodes_on_side(s1e);
     623          48 :   libmesh_assert_equal_to(nodes_on_edge.size(), 2);
     624         576 :   nodes_on_edge[0] = node_map[nodes_on_edge[0]];
     625         576 :   nodes_on_edge[1] = node_map[nodes_on_edge[1]];
     626             : 
     627         576 :   if (nodes_on_edge[0] > nodes_on_edge[1])
     628          16 :     std::swap(nodes_on_edge[0], nodes_on_edge[1]);
     629             : 
     630        2640 :   for (unsigned int s2 : make_range(this->n_sides()))
     631             :     {
     632        2640 :       if (s2 == s1)
     633         528 :         continue;
     634             : 
     635        2064 :       if (this->side_has_edge_nodes(s2, nodes_on_edge[0],
     636         344 :                                     nodes_on_edge[1]))
     637             :         {
     638         576 :           returnval[1] = s2;
     639         624 :           return returnval;
     640             :         }
     641             :     }
     642             : 
     643           0 :   libmesh_error();
     644             : 
     645             :   return returnval;
     646             : }
     647             : 
     648             : 
     649             : 
     650         288 : bool Polyhedron::is_edge_on_side(const unsigned int e,
     651             :                                  const unsigned int s) const
     652             : {
     653         288 :   auto [s1, s1e] = _edge_lookup[e];
     654             : 
     655             :   // Did we get lucky with our cache?
     656         288 :   if (s1 == s)
     657          12 :     return true;
     658             : 
     659         144 :   const Polygon & face1 = *std::get<0>(_sidelinks_data[s1]);
     660             :   const std::vector<unsigned int> & node_map =
     661          12 :     std::get<2>(this->_sidelinks_data[s1]);
     662             :   std::vector<unsigned int> nodes_on_edge1 =
     663         156 :     face1.nodes_on_side(s1e);
     664          12 :   libmesh_assert_equal_to(nodes_on_edge1.size(), 2);
     665             : 
     666         144 :   nodes_on_edge1[0] = node_map[nodes_on_edge1[0]];
     667         144 :   nodes_on_edge1[1] = node_map[nodes_on_edge1[1]];
     668         144 :   if (nodes_on_edge1[0] > nodes_on_edge1[1])
     669           4 :     std::swap(nodes_on_edge1[0], nodes_on_edge1[1]);
     670             : 
     671         144 :   return this->side_has_edge_nodes(s,
     672          12 :                                    nodes_on_edge1[0],
     673          24 :                                    nodes_on_edge1[1]);
     674             : }
     675             : 
     676             : 
     677             : 
     678   221136927 : std::array<Point, 4> Polyhedron::master_subelement (unsigned int i) const
     679             : {
     680    19024346 :   libmesh_assert_less(i, this->_triangulation.size());
     681             : 
     682   221136927 :   const auto & tet = this->_triangulation[i];
     683             : 
     684   221136927 :   return { this->master_point(tet[0]),
     685   221136927 :            this->master_point(tet[1]),
     686   221136927 :            this->master_point(tet[2]),
     687   221136927 :            this->master_point(tet[3]) };
     688             : }
     689             : 
     690             : 
     691             : 
     692             : std::tuple<unsigned int, Real, Real, Real>
     693    58943010 : Polyhedron::subelement_coordinates (const Point & p, Real tol) const
     694             : {
     695     5071145 :   std::tuple<unsigned int, Real, Real, Real> returnval =
     696             :     {libMesh::invalid_uint, -1, -1, -1};
     697             : 
     698     5071145 :   Real best_bad_coord = -1;
     699             : 
     700   204762016 :   for (auto s : make_range(this->n_subelements()))
     701             :     {
     702             :       const std::array<Point, 4> subtet =
     703   199861240 :         this->master_subelement(s);
     704             : 
     705             :       // Find barycentric coordinates in subelem
     706    17257345 :       const Point v0 = p - subtet[0];
     707             :       // const Point v1 = p - subtet[1];
     708             : 
     709    17257345 :       const Point v01 = subtet[1] - subtet[0];
     710    17257345 :       const Point v02 = subtet[2] - subtet[0];
     711    17257345 :       const Point v03 = subtet[3] - subtet[0];
     712             : 
     713             :       // const Point v12 = subtet[2] - subtet[1];
     714             :       // const Point v13 = subtet[3] - subtet[1];
     715             : 
     716             :       // const Real tp0 = triple_product(v1, v13, v12);
     717   183443545 :       const Real tp1 = triple_product(v0, v02, v03);
     718   183443545 :       const Real tp2 = triple_product(v0, v03, v01);
     719   183443545 :       const Real tp3 = triple_product(v0, v01, v02);
     720             : 
     721   183443545 :       const Real six_vol = triple_product(v01, v02, v03);
     722             : 
     723   199861240 :       const Real xi   = tp1 / six_vol;
     724   199861240 :       const Real eta  = tp2 / six_vol;
     725   199861240 :       const Real zeta = tp3 / six_vol;
     726             : 
     727   199861240 :       if (xi>=0 && eta>=0 && zeta>=0 && xi+eta+zeta<=1)
     728    54042234 :         return { s, xi, eta, zeta };
     729             : 
     730             :       const Real my_best_bad_coord =
     731   145819006 :         std::min(std::min(std::min(xi, eta), zeta), 1-xi-eta-zeta);
     732             : 
     733   145819006 :       if (my_best_bad_coord > best_bad_coord)
     734             :         {
     735     5202878 :           best_bad_coord = my_best_bad_coord;
     736     5202878 :           returnval = { s, xi, eta, zeta };
     737             :         }
     738             :     }
     739             : 
     740     4900776 :   if (best_bad_coord > -tol)
     741      408398 :     return returnval;
     742             : 
     743           0 :   return {libMesh::invalid_uint, -1, -1, -1};
     744             : }
     745             : 
     746             : 
     747             : } // namespace libMesh

Generated by: LCOV version 1.14