LCOV - code coverage report
Current view: top level - src/geom - cell_inf_hex.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 106 155 68.4 %
Date: 2025-08-19 19:27:09 Functions: 12 15 80.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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/libmesh_config.h"
      20             : 
      21             : #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
      22             : 
      23             : 
      24             : // C++ includes
      25             : #include <algorithm> // for std::min, std::max
      26             : 
      27             : // Local includes cont'd
      28             : #include "libmesh/cell_inf_hex.h"
      29             : #include "libmesh/cell_inf_hex8.h"
      30             : #include "libmesh/face_quad4.h"
      31             : #include "libmesh/face_inf_quad4.h"
      32             : #include "libmesh/fe_type.h"
      33             : #include "libmesh/fe_interface.h"
      34             : #include "libmesh/inf_fe_map.h"
      35             : #include "libmesh/enum_elem_quality.h"
      36             : 
      37             : namespace libMesh
      38             : {
      39             : 
      40             : 
      41             : 
      42             : // ------------------------------------------------------------
      43             : // InfHex class static member initializations
      44             : const int InfHex::num_sides;
      45             : const int InfHex::num_edges;
      46             : const int InfHex::num_children;
      47             : 
      48             : const Real InfHex::_master_points[18][3] =
      49             :   {
      50             :     {-1, -1, 0},
      51             :     {1, -1, 0},
      52             :     {1, 1, 0},
      53             :     {-1, 1, 0},
      54             :     {-1, -1, 1},
      55             :     {1, -1, 1},
      56             :     {1, 1, 1},
      57             :     {-1, 1, 1},
      58             :     {0, -1, 0},
      59             :     {1, 0, 0},
      60             :     {0, 1, 0},
      61             :     {-1, 0, 0},
      62             :     {0, -1, 1},
      63             :     {1, 0, 1},
      64             :     {0, 1, 1},
      65             :     {-1, 0, 1},
      66             :     {0, 0, 0},
      67             :     {0, 0, 1}
      68             :   };
      69             : 
      70             : const unsigned int InfHex::edge_sides_map[8][2] =
      71             :   {
      72             :     {0, 1}, // Edge 0
      73             :     {0, 2}, // Edge 1
      74             :     {0, 3}, // Edge 2
      75             :     {0, 4}, // Edge 3
      76             :     {1, 4}, // Edge 4
      77             :     {1, 2}, // Edge 5
      78             :     {2, 3}, // Edge 6
      79             :     {3, 4}  // Edge 7
      80             :   };
      81             : 
      82             : const unsigned int InfHex::adjacent_edges_map[/*num_vertices*/8][/*max_adjacent_edges*/3] =
      83             :   {
      84             :     {0,  3,  4}, // Edges adjacent to node 0
      85             :     {0,  1,  5}, // Edges adjacent to node 1
      86             :     {1,  2,  6}, // Edges adjacent to node 2
      87             :     {2,  3,  7}, // Edges adjacent to node 3
      88             :     {4, 99, 99}, // Edges adjacent to node 4
      89             :     {5, 99, 99}, // Edges adjacent to node 5
      90             :     {6, 99, 99}, // Edges adjacent to node 6
      91             :     {7, 99, 99}  // Edges adjacent to node 7
      92             :   };
      93             : 
      94             : // ------------------------------------------------------------
      95             : // InfHex class member functions
      96           0 : dof_id_type InfHex::key (const unsigned int s) const
      97             : {
      98           0 :   libmesh_assert_less (s, this->n_sides());
      99             : 
     100             :   // The order of the node ids does not matter, they are sorted by the
     101             :   // compute_key() function.
     102           0 :   return this->compute_key(this->node_id(InfHex8::side_nodes_map[s][0]),
     103           0 :                            this->node_id(InfHex8::side_nodes_map[s][1]),
     104           0 :                            this->node_id(InfHex8::side_nodes_map[s][2]),
     105           0 :                            this->node_id(InfHex8::side_nodes_map[s][3]));
     106             : }
     107             : 
     108             : 
     109             : 
     110       30845 : dof_id_type InfHex::low_order_key (const unsigned int s) const
     111             : {
     112       12250 :   libmesh_assert_less (s, this->n_sides());
     113             : 
     114             :   // The order of the node ids does not matter, they are sorted by the
     115             :   // compute_key() function.
     116       43095 :   return this->compute_key(this->node_id(InfHex8::side_nodes_map[s][0]),
     117       30845 :                            this->node_id(InfHex8::side_nodes_map[s][1]),
     118       30845 :                            this->node_id(InfHex8::side_nodes_map[s][2]),
     119       43095 :                            this->node_id(InfHex8::side_nodes_map[s][3]));
     120             : }
     121             : 
     122             : 
     123             : 
     124           0 : unsigned int InfHex::local_side_node(unsigned int side,
     125             :                                      unsigned int side_node) const
     126             : {
     127           0 :   libmesh_assert_less (side, this->n_sides());
     128           0 :   libmesh_assert_less (side_node, InfHex8::nodes_per_side);
     129             : 
     130           0 :   return InfHex8::side_nodes_map[side][side_node];
     131             : }
     132             : 
     133             : 
     134             : 
     135         480 : unsigned int InfHex::local_edge_node(unsigned int edge,
     136             :                                      unsigned int edge_node) const
     137             : {
     138         160 :   libmesh_assert_less (edge, this->n_edges());
     139         160 :   libmesh_assert_less (edge_node, InfHex8::nodes_per_edge);
     140             : 
     141         480 :   return InfHex8::edge_nodes_map[edge][edge_node];
     142             : }
     143             : 
     144             : 
     145             : 
     146       16296 : std::unique_ptr<Elem> InfHex::side_ptr (const unsigned int i)
     147             : {
     148        6484 :   libmesh_assert_less (i, this->n_sides());
     149             : 
     150             :   // Return value
     151       16296 :   std::unique_ptr<Elem> face;
     152             : 
     153             :   // Think of a unit cube: (-1,1) x (-1,1) x (-1,1),
     154             :   // with (in general) the normals pointing outwards
     155       16296 :   switch (i)
     156             :     {
     157             :       // the face at z = -1
     158             :       // the base, where the infinite element couples to conventional
     159             :       // elements
     160        5439 :     case 0:
     161             :       {
     162             :         // Oops, here we are, claiming the normal of the face
     163             :         // elements point outwards -- and this is the exception:
     164             :         // For the side built from the base face,
     165             :         // the normal is pointing _into_ the element!
     166             :         // Why is that? - In agreement with build_side_ptr(),
     167             :         // which in turn _has_ to build the face in this
     168             :         // way as to enable the cool way \p InfFE re-uses \p FE.
     169        5439 :         face = std::make_unique<Quad4>();
     170        5439 :         break;
     171             :       }
     172             : 
     173             :       // These faces connect to other infinite elements.
     174       10857 :     case 1: // the face at y = -1
     175             :     case 2: // the face at x = 1
     176             :     case 3: // the face at y = 1
     177             :     case 4: // the face at x = -1
     178             :       {
     179       10857 :         face = std::make_unique<InfQuad4>();
     180       10857 :         break;
     181             :       }
     182             : 
     183           0 :     default:
     184           0 :       libmesh_error_msg("Invalid side i = " << i);
     185             :     }
     186             : 
     187             :   // Set the nodes
     188       81480 :   for (auto n : face->node_index_range())
     189       91120 :     face->set_node(n, this->node_ptr(InfHex8::side_nodes_map[i][n]));
     190             : 
     191       16296 :   return face;
     192           0 : }
     193             : 
     194             : 
     195             : 
     196       28689 : void InfHex::side_ptr (std::unique_ptr<Elem> & side,
     197             :                        const unsigned int i)
     198             : {
     199       11399 :   libmesh_assert_less (i, this->n_sides());
     200             : 
     201             :   // Think of a unit cube: (-1,1) x (-1,1) x (1,1)
     202       28689 :   switch (i)
     203             :     {
     204             :       // the base face
     205        2203 :     case 0:
     206             :       {
     207        5545 :         if (!side.get() || side->type() != QUAD4)
     208             :           {
     209        6532 :             side = this->side_ptr(i);
     210        5424 :             return;
     211             :           }
     212          45 :         break;
     213             :       }
     214             : 
     215             :       // connecting to another infinite element
     216        9196 :     case 1:
     217             :     case 2:
     218             :     case 3:
     219             :     case 4:
     220             :       {
     221       23144 :         if (!side.get() || side->type() != INFQUAD4)
     222             :           {
     223       13002 :             side = this->side_ptr(i);
     224       10797 :             return;
     225             :           }
     226        4900 :         break;
     227             :       }
     228             : 
     229           0 :     default:
     230           0 :       libmesh_error_msg("Invalid side i = " << i);
     231             :     }
     232             : 
     233       17413 :   side->subdomain_id() = this->subdomain_id();
     234             : 
     235             :   // Set the nodes
     236       62340 :   for (auto n : side->node_index_range())
     237       69652 :     side->set_node(n, this->node_ptr(InfHex8::side_nodes_map[i][n]));
     238             : }
     239             : 
     240             : 
     241             : 
     242       14268 : bool InfHex::is_child_on_side(const unsigned int c,
     243             :                               const unsigned int s) const
     244             : {
     245       13176 :   libmesh_assert_less (c, this->n_children());
     246       13176 :   libmesh_assert_less (s, this->n_sides());
     247             : 
     248       14268 :   return (s == 0 || c+1 == s || c == s%4);
     249             : }
     250             : 
     251             : 
     252             : 
     253       98172 : bool InfHex::is_edge_on_side (const unsigned int e,
     254             :                               const unsigned int s) const
     255             : {
     256       98076 :   libmesh_assert_less (e, this->n_edges());
     257       98076 :   libmesh_assert_less (s, this->n_sides());
     258             : 
     259       98172 :   return (edge_sides_map[e][0] == s || edge_sides_map[e][1] == s);
     260             : }
     261             : 
     262             : 
     263             : 
     264         288 : std::vector<unsigned int> InfHex::sides_on_edge(const unsigned int e) const
     265             : {
     266          96 :   libmesh_assert_less(e, this->n_edges());
     267             : 
     268         288 :   return {edge_sides_map[e][0], edge_sides_map[e][1]};
     269             : }
     270             : 
     271             : 
     272             : bool
     273          24 : InfHex::is_flipped() const
     274             : {
     275          15 :   return (triple_product(this->point(1)-this->point(0),
     276          15 :                          this->point(3)-this->point(0),
     277          42 :                          this->point(4)-this->point(0)) < 0);
     278             : }
     279             : 
     280             : std::vector<unsigned int>
     281         630 : InfHex::edges_adjacent_to_node(const unsigned int n) const
     282             : {
     283         210 :   libmesh_assert_less(n, this->n_nodes());
     284             : 
     285             :   // For vertices, we use the InfHex::adjacent_edges_map with
     286             :   // appropriate "trimming" based on whether the vertices are "at
     287             :   // infinity" or not.  Otherwise each of the mid-edge nodes is
     288             :   // adjacent only to the edge it is on, and the remaining nodes are
     289             :   // not adjacent to any edge.
     290         630 :   if (this->is_vertex(n))
     291             :     {
     292         360 :       auto trim = (n < 4) ? 0 : 2;
     293         360 :       return {std::begin(adjacent_edges_map[n]), std::end(adjacent_edges_map[n]) - trim};
     294             :     }
     295         270 :   else if (this->is_edge(n))
     296         120 :     return {n - this->n_vertices()};
     297             : 
     298          50 :   libmesh_assert(this->is_face(n) || this->is_internal(n));
     299         100 :   return {};
     300             : }
     301             : 
     302          63 : Real InfHex::quality (const ElemQuality q) const
     303             : {
     304          63 :   switch (q)
     305             :     {
     306             : 
     307             :       /**
     308             :        * Compute the min/max diagonal ratio.
     309             :        * Source: CUBIT User's Manual.
     310             :        *
     311             :        * For infinite elements, we just only compute
     312             :        * the diagonal in the face...
     313             :        * Don't know whether this makes sense,
     314             :        * but should be a feasible way.
     315             :        */
     316           0 :     case DIAGONAL:
     317             :       {
     318             :         // Diagonal between node 0 and node 2
     319           0 :         const Real d02 = this->length(0,2);
     320             : 
     321             :         // Diagonal between node 1 and node 3
     322           0 :         const Real d13 = this->length(1,3);
     323             : 
     324             :         // Find the biggest and smallest diagonals
     325           0 :         const Real min = std::min(d02, d13);
     326           0 :         const Real max = std::max(d02, d13);
     327             : 
     328           0 :         libmesh_assert_not_equal_to (max, 0.0);
     329             : 
     330           0 :         return min / max;
     331             : 
     332             :         break;
     333             :       }
     334             : 
     335             :       /**
     336             :        * Minimum ratio of lengths derived from opposite edges.
     337             :        * Source: CUBIT User's Manual.
     338             :        *
     339             :        * For IFEMs, do this only for the base face...
     340             :        * Does this make sense?
     341             :        */
     342           0 :     case TAPER:
     343             :       {
     344             : 
     345             :         /**
     346             :          * Compute the side lengths.
     347             :          */
     348           0 :         const Real d01 = this->length(0,1);
     349           0 :         const Real d12 = this->length(1,2);
     350           0 :         const Real d23 = this->length(2,3);
     351           0 :         const Real d03 = this->length(0,3);
     352             : 
     353           0 :         std::vector<Real> edge_ratios(2);
     354             : 
     355             :         // Bottom
     356           0 :         edge_ratios[0] = std::min(d01, d23) / std::max(d01, d23);
     357           0 :         edge_ratios[1] = std::min(d03, d12) / std::max(d03, d12);
     358             : 
     359           0 :         return *(std::min_element(edge_ratios.begin(), edge_ratios.end())) ;
     360             : 
     361             :         break;
     362             :       }
     363             : 
     364             : 
     365             :       /**
     366             :        * Minimum edge length divided by max diagonal length.
     367             :        * Source: CUBIT User's Manual.
     368             :        *
     369             :        * And again, we mess around a bit, for the IFEMs...
     370             :        * Do this only for the base.
     371             :        */
     372           0 :     case STRETCH:
     373             :       {
     374             :         /**
     375             :          * Should this be a sqrt2, when we do this for the base only?
     376             :          */
     377           0 :         const Real sqrt3 = 1.73205080756888;
     378             : 
     379             :         /**
     380             :          * Compute the maximum diagonal in the base.
     381             :          */
     382           0 :         const Real d02 = this->length(0,2);
     383           0 :         const Real d13 = this->length(1,3);
     384           0 :         const Real max_diag = std::max(d02, d13);
     385             : 
     386           0 :         libmesh_assert_not_equal_to ( max_diag, 0.0 );
     387             : 
     388             :         /**
     389             :          * Compute the minimum edge length in the base.
     390             :          */
     391           0 :         std::vector<Real> edges(4);
     392           0 :         edges[0]  = this->length(0,1);
     393           0 :         edges[1]  = this->length(1,2);
     394           0 :         edges[2]  = this->length(2,3);
     395           0 :         edges[3]  = this->length(0,3);
     396             : 
     397           0 :         const Real min_edge = *(std::min_element(edges.begin(), edges.end()));
     398           0 :         return sqrt3 * min_edge / max_diag ;
     399             :       }
     400             : 
     401             : 
     402             :       /**
     403             :        * I don't know what to do for this metric.
     404             :        * Maybe the base class knows...
     405             :        */
     406          63 :     default:
     407          63 :       return Elem::quality(q);
     408             :     }
     409             : }
     410             : 
     411             : 
     412             : 
     413           0 : std::pair<Real, Real> InfHex::qual_bounds (const ElemQuality) const
     414             : {
     415           0 :   libmesh_not_implemented();
     416             : 
     417             :   // We'll never get here
     418             :   return {0., 0.};
     419             : }
     420             : 
     421             : 
     422             : 
     423             : 
     424             : 
     425             : const unsigned short int InfHex::_second_order_adjacent_vertices[8][2] =
     426             :   {
     427             :     { 0,  1}, // vertices adjacent to node 8
     428             :     { 1,  2}, // vertices adjacent to node 9
     429             :     { 2,  3}, // vertices adjacent to node 10
     430             :     { 0,  3}, // vertices adjacent to node 11
     431             : 
     432             :     { 4,  5}, // vertices adjacent to node 12
     433             :     { 5,  6}, // vertices adjacent to node 13
     434             :     { 6,  7}, // vertices adjacent to node 14
     435             :     { 4,  7}  // vertices adjacent to node 15
     436             :   };
     437             : 
     438             : 
     439             : 
     440             : const unsigned short int InfHex::_second_order_vertex_child_number[18] =
     441             :   {
     442             :     99,99,99,99,99,99,99,99, // Vertices
     443             :     0,1,2,0,                 // Edges
     444             :     0,1,2,0,0,               // Faces
     445             :     0                        // Interior
     446             :   };
     447             : 
     448             : 
     449             : 
     450             : const unsigned short int InfHex::_second_order_vertex_child_index[18] =
     451             :   {
     452             :     99,99,99,99,99,99,99,99, // Vertices
     453             :     1,2,3,3,                 // Edges
     454             :     5,6,7,7,2,               // Faces
     455             :     6                        // Interior
     456             :   };
     457             : 
     458             : 
     459        3875 : bool InfHex::contains_point (const Point & p, Real tol) const
     460             : {
     461             :   // For infinite elements with linear base interpolation:
     462             :   // make use of the fact that infinite elements do not
     463             :   // live inside the envelope.  Use a fast scheme to
     464             :   // check whether point \p p is inside or outside
     465             :   // our relevant part of the envelope.  Note that
     466             :   // this is not exclusive: only when the distance is less,
     467             :   // we are safe.  Otherwise, we cannot say anything. The
     468             :   // envelope may be non-spherical, the physical point may lie
     469             :   // inside the envelope, outside the envelope, or even inside
     470             :   // this infinite element.  Therefore if this fails,
     471             :   // fall back to the inverse_map()
     472        3875 :   const Point my_origin (this->origin());
     473             : 
     474             :   // determine the minimal distance of the base from the origin
     475             :   // Use norm_sq() instead of norm(), it is faster
     476          12 :   Point pt0_o(this->point(0) - my_origin);
     477           6 :   Point pt1_o(this->point(1) - my_origin);
     478           6 :   Point pt2_o(this->point(2) - my_origin);
     479           6 :   Point pt3_o(this->point(3) - my_origin);
     480        3875 :   const Real tmp_min_distance_sq = std::min(pt0_o.norm_sq(),
     481        3875 :                                             std::min(pt1_o.norm_sq(),
     482        7732 :                                                      std::min(pt2_o.norm_sq(),
     483        7756 :                                                               pt3_o.norm_sq())));
     484             : 
     485             :   // For a coarse grid, it is important to account for the fact
     486             :   // that the sides are not spherical, thus the closest point
     487             :   // can be closer than all edges.
     488             :   // This is an estimator using Pythagoras:
     489             :   const Real min_distance_sq = tmp_min_distance_sq
     490        7738 :                               - .5*std::max((point(0)-point(2)).norm_sq(),
     491        6202 :                                             (point(1)-point(3)).norm_sq());
     492             : 
     493             :   // work with 1% allowable deviation.  We can still fall
     494             :   // back to the InfFE::inverse_map()
     495        3875 :   const Real conservative_p_dist_sq = 1.01 * (Point(p - my_origin).norm_sq());
     496             : 
     497             : 
     498             : 
     499        3875 :   if (conservative_p_dist_sq < min_distance_sq)
     500             :     {
     501             :       // the physical point is definitely not contained in the element
     502           6 :       return false;
     503             :     }
     504             : 
     505             :   // this captures the case that the point is not (almost) in the direction of the element.:
     506             :   // first, project the problem onto the unit sphere:
     507           0 :   Point p_o(p - my_origin);
     508        3810 :   pt0_o /= pt0_o.norm();
     509        3810 :   pt1_o /= pt1_o.norm();
     510        3810 :   pt2_o /= pt2_o.norm();
     511        3810 :   pt3_o /= pt3_o.norm();
     512        3810 :   p_o /= p_o.norm();
     513             : 
     514             : 
     515             :   // now, check if it is in the projected face; using that the diagonal contains
     516             :   // the largest distance between points in it
     517        7620 :   Real max_h = std::max((pt0_o - pt2_o).norm_sq(),
     518        5606 :                         (pt1_o - pt3_o).norm_sq())*1.01;
     519             : 
     520        2644 :   if ((p_o - pt0_o).norm_sq() > max_h ||
     521        2024 :       (p_o - pt1_o).norm_sq() > max_h ||
     522        5171 :       (p_o - pt2_o).norm_sq() > max_h ||
     523           0 :       (p_o - pt3_o).norm_sq() > max_h )
     524             :     {
     525             :       // the physical point is definitely not contained in the element
     526        2820 :       return false;
     527             :     }
     528             : 
     529             :   // Declare a basic FEType.  Will use default in the base,
     530             :   // and something else (not important) in radial direction.
     531         990 :   FEType fe_type(default_order());
     532             : 
     533         990 :   const Point mapped_point = InfFEMap::inverse_map(dim(), this, p,
     534           0 :                                                    tol, false);
     535             : 
     536         990 :   return this->on_reference_element(mapped_point, tol);
     537             : }
     538             : 
     539             : 
     540             : 
     541        1009 : bool InfHex::on_reference_element(const Point & p,
     542             :                                   const Real eps) const
     543             : {
     544           6 :   const Real & xi = p(0);
     545           6 :   const Real & eta = p(1);
     546           6 :   const Real & zeta = p(2);
     547             : 
     548             :   // The reference infhex is a [-1,1]^3.
     549        1969 :   return ((xi   >= -1.-eps) &&
     550         960 :           (xi   <=  1.+eps) &&
     551         929 :           (eta  >= -1.-eps) &&
     552         888 :           (eta  <=  1.+eps) &&
     553        1903 :           (zeta >= -1.-eps) &&
     554        1015 :           (zeta <=  1.+eps));
     555             : }
     556             : 
     557             : 
     558             : 
     559             : } // namespace libMesh
     560             : 
     561             : 
     562             : 
     563             : 
     564             : #endif // ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS

Generated by: LCOV version 1.14