LCOV - code coverage report
Current view: top level - src/geom - cell_inf_prism.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4232 (290bfc) with base 82cc40 Lines: 90 131 68.7 %
Date: 2025-08-27 15:46:53 Functions: 10 13 76.9 %
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             : #include "libmesh/libmesh_config.h"
      19             : 
      20             : #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
      21             : 
      22             : // Local includes
      23             : #include "libmesh/cell_inf_prism.h"
      24             : #include "libmesh/cell_inf_prism6.h"
      25             : #include "libmesh/face_tri3.h"
      26             : #include "libmesh/face_inf_quad4.h"
      27             : #include "libmesh/fe_type.h"
      28             : #include "libmesh/fe_interface.h"
      29             : #include "libmesh/inf_fe_map.h"
      30             : #include "libmesh/enum_order.h"
      31             : 
      32             : namespace libMesh
      33             : {
      34             : 
      35             : 
      36             : // ------------------------------------------------------------
      37             : // InfPrism class static member initializations
      38             : const int InfPrism::num_sides;
      39             : const int InfPrism::num_edges;
      40             : const int InfPrism::num_children;
      41             : 
      42             : const Real InfPrism::_master_points[12][3] =
      43             :   {
      44             :     {0, 0, 0},
      45             :     {1, 0, 0},
      46             :     {0, 1, 0},
      47             :     {0, 0, 1},
      48             :     {1, 0, 1},
      49             :     {0, 1, 1},
      50             :     {.5, 0, 0},
      51             :     {.5, .5, 0},
      52             :     {0, .5, 0},
      53             :     {.5, 0, 1},
      54             :     {.5, .5, 1},
      55             :     {0, .5, 1}
      56             :   };
      57             : 
      58             : const unsigned int InfPrism::edge_sides_map[6][2] =
      59             :   {
      60             :     {0, 1}, // Edge 0
      61             :     {0, 2}, // Edge 1
      62             :     {0, 3}, // Edge 2
      63             :     {1, 3}, // Edge 3
      64             :     {1, 2}, // Edge 4
      65             :     {2, 3}  // Edge 5
      66             :   };
      67             : 
      68             : const unsigned int InfPrism::adjacent_edges_map[/*num_vertices*/6][/*max_adjacent_edges*/3] =
      69             : {
      70             :   {0,  2,  3}, // Edges adjacent to node 0
      71             :   {0,  1,  4}, // Edges adjacent to node 1
      72             :   {1,  2,  5}, // Edges adjacent to node 2
      73             :   {3, 99, 99}, // Edges adjacent to node 3
      74             :   {4, 99, 99}, // Edges adjacent to node 4
      75             :   {5, 99, 99}, // Edges adjacent to node 5
      76             : };
      77             : 
      78             : // ------------------------------------------------------------
      79             : // InfPrism class member functions
      80           0 : dof_id_type InfPrism::key (const unsigned int s) const
      81             : {
      82           0 :   libmesh_assert_less (s, this->n_sides());
      83             : 
      84           0 :   switch (s)
      85             :     {
      86           0 :     case 0: // the triangular face at z=-1, base face
      87           0 :       return this->compute_key (this->node_id(InfPrism6::side_nodes_map[s][0]),
      88           0 :                                 this->node_id(InfPrism6::side_nodes_map[s][1]),
      89           0 :                                 this->node_id(InfPrism6::side_nodes_map[s][2]));
      90             : 
      91           0 :     case 1: // the quad face at y=0
      92             :     case 2: // the other quad face
      93             :     case 3: // the quad face at x=0
      94           0 :       return this->compute_key (this->node_id(InfPrism6::side_nodes_map[s][0]),
      95           0 :                                 this->node_id(InfPrism6::side_nodes_map[s][1]),
      96           0 :                                 this->node_id(InfPrism6::side_nodes_map[s][2]),
      97           0 :                                 this->node_id(InfPrism6::side_nodes_map[s][3]));
      98             : 
      99           0 :     default:
     100           0 :       libmesh_error_msg("Invalid side s = " << s);
     101             :     }
     102             : }
     103             : 
     104             : 
     105             : 
     106       15000 : dof_id_type InfPrism::low_order_key (const unsigned int s) const
     107             : {
     108        6000 :   libmesh_assert_less (s, this->n_sides());
     109             : 
     110       15000 :   switch (s)
     111             :     {
     112        3750 :     case 0: // the triangular face at z=-1, base face
     113        5250 :       return this->compute_key (this->node_id(InfPrism6::side_nodes_map[s][0]),
     114        3750 :                                 this->node_id(InfPrism6::side_nodes_map[s][1]),
     115        5250 :                                 this->node_id(InfPrism6::side_nodes_map[s][2]));
     116             : 
     117       11250 :     case 1: // the quad face at y=0
     118             :     case 2: // the other quad face
     119             :     case 3: // the quad face at x=0
     120       15750 :       return this->compute_key (this->node_id(InfPrism6::side_nodes_map[s][0]),
     121       11250 :                                 this->node_id(InfPrism6::side_nodes_map[s][1]),
     122       11250 :                                 this->node_id(InfPrism6::side_nodes_map[s][2]),
     123       15750 :                                 this->node_id(InfPrism6::side_nodes_map[s][3]));
     124             : 
     125           0 :     default:
     126           0 :       libmesh_error_msg("Invalid side s = " << s);
     127             :     }
     128             : }
     129             : 
     130             : 
     131             : 
     132           0 : unsigned int InfPrism::local_side_node(unsigned int side,
     133             :                                        unsigned int side_node) const
     134             : {
     135           0 :   libmesh_assert_less (side, this->n_sides());
     136             : 
     137             :   // Never more than 4 nodes per side.
     138           0 :   libmesh_assert_less(side_node, InfPrism6::nodes_per_side);
     139             : 
     140             :   // Some sides have 3 nodes.
     141           0 :   libmesh_assert(side != 0 || side_node < 3);
     142             : 
     143           0 :   return InfPrism6::side_nodes_map[side][side_node];
     144             : }
     145             : 
     146             : 
     147             : 
     148         360 : unsigned int InfPrism::local_edge_node(unsigned int edge,
     149             :                                        unsigned int edge_node) const
     150             : {
     151         120 :   libmesh_assert_less(edge, this->n_edges());
     152         120 :   libmesh_assert_less(edge_node, InfPrism6::nodes_per_edge);
     153             : 
     154         360 :   return InfPrism6::edge_nodes_map[edge][edge_node];
     155             : }
     156             : 
     157             : 
     158             : 
     159       10137 : std::unique_ptr<Elem> InfPrism::side_ptr (const unsigned int i)
     160             : {
     161        4054 :   libmesh_assert_less (i, this->n_sides());
     162             : 
     163       10137 :   std::unique_ptr<Elem> face;
     164             : 
     165       10137 :   switch (i)
     166             :     {
     167        3341 :     case 0: // the triangular face at z=-1, base face
     168             :       {
     169        3341 :         face = std::make_unique<Tri3>();
     170        3341 :         break;
     171             :       }
     172             : 
     173        6796 :     case 1: // the quad face at y=0
     174             :     case 2: // the other quad face
     175             :     case 3: // the quad face at x=0
     176             :       {
     177        6796 :         face = std::make_unique<InfQuad4>();
     178        6796 :         break;
     179             :       }
     180             : 
     181           0 :     default:
     182           0 :       libmesh_error_msg("Invalid side i = " << i);
     183             :     }
     184             : 
     185             :   // Set the nodes
     186       47344 :   for (auto n : face->node_index_range())
     187       52087 :     face->set_node(n, this->node_ptr(InfPrism6::side_nodes_map[i][n]));
     188             : 
     189       10137 :   return face;
     190           0 : }
     191             : 
     192             : 
     193             : 
     194       13856 : void InfPrism::side_ptr (std::unique_ptr<Elem> & side,
     195             :                          const unsigned int i)
     196             : {
     197        5540 :   libmesh_assert_less (i, this->n_sides());
     198             : 
     199       13856 :   switch (i)
     200             :     {
     201             :       // the base face
     202        1337 :     case 0:
     203             :       {
     204        3344 :         if (!side.get() || side->type() != TRI3)
     205             :           {
     206        3992 :             side = this->side_ptr(i);
     207        3326 :             return;
     208             :           }
     209           7 :         break;
     210             :       }
     211             : 
     212             :       // connecting to another infinite element
     213        4203 :     case 1:
     214             :     case 2:
     215             :     case 3:
     216             :     case 4:
     217             :       {
     218       10512 :         if (!side.get() || side->type() != INFQUAD4)
     219             :           {
     220        8120 :             side = this->side_ptr(i);
     221        6766 :             return;
     222             :           }
     223        1497 :         break;
     224             :       }
     225             : 
     226           0 :     default:
     227           0 :       libmesh_error_msg("Invalid side i = " << i);
     228             :     }
     229             : 
     230        5268 :   side->subdomain_id() = this->subdomain_id();
     231             : 
     232             :   // Set the nodes
     233       18802 :   for (auto n : side->node_index_range())
     234       21047 :     side->set_node(n, this->node_ptr(InfPrism6::side_nodes_map[i][n]));
     235             : }
     236             : 
     237             : 
     238             : 
     239        1436 : bool InfPrism::is_child_on_side(const unsigned int c,
     240             :                                 const unsigned int s) const
     241             : {
     242         852 :   libmesh_assert_less (c, this->n_children());
     243         852 :   libmesh_assert_less (s, this->n_sides());
     244             : 
     245        1436 :   return (s == 0 || c+1 == s || c == s%3);
     246             : }
     247             : 
     248             : 
     249             : 
     250        2592 : bool InfPrism::is_edge_on_side (const unsigned int e,
     251             :                                 const unsigned int s) const
     252             : {
     253        2544 :   libmesh_assert_less (e, this->n_edges());
     254        2544 :   libmesh_assert_less (s, this->n_sides());
     255             : 
     256        2592 :   return (edge_sides_map[e][0] == s || edge_sides_map[e][1] == s);
     257             : }
     258             : 
     259          14 : bool InfPrism::contains_point (const Point & p, Real tol) const
     260             : {
     261             :   // For infinite elements with linear base interpolation:
     262             :   // make use of the fact that infinite elements do not
     263             :   // live inside the envelope.  Use a fast scheme to
     264             :   // check whether point \p p is inside or outside
     265             :   // our relevant part of the envelope.  Note that
     266             :   // this is not exclusive: only when the distance is less,
     267             :   // we are safe.  Otherwise, we cannot say anything. The
     268             :   // envelope may be non-spherical, the physical point may lie
     269             :   // inside the envelope, outside the envelope, or even inside
     270             :   // this infinite element.  Therefore if this fails,
     271             :   // fall back to the inverse_map()
     272          14 :   const Point my_origin (this->origin());
     273             : 
     274             :   // determine the minimal distance of the base from the origin
     275             :   // use norm_sq(), it is faster than norm() and produces
     276             :   // the same behavior. Shift my_origin to center first:
     277          12 :   Point pt0_o(this->point(0) - my_origin);
     278           6 :   Point pt1_o(this->point(1) - my_origin);
     279           6 :   Point pt2_o(this->point(2) - my_origin);
     280          14 :   const Real tmp_min_distance_sq = std::min(pt0_o.norm_sq(),
     281          10 :                                             std::min(pt1_o.norm_sq(),
     282          28 :                                                      pt2_o.norm_sq()));
     283             : 
     284             :   // For a coarse grid, it is important to account for the fact
     285             :   // that the sides are not spherical, thus the closest point
     286             :   // can be closer than all edges.
     287             :   // This is an estimator using Pythagoras:
     288             :   const Real min_distance_sq = tmp_min_distance_sq
     289          14 :                               - .5*std::max((point(0)-point(1)).norm_sq(),
     290          16 :                                              std::max((point(0)-point(2)).norm_sq(),
     291          34 :                                                       (point(1)-point(2)).norm_sq()));
     292             : 
     293             :   // work with 1% allowable deviation.  We can still fall
     294             :   // back to the InfFE::inverse_map()
     295          14 :   const Real conservative_p_dist_sq = 1.01 * (Point(p - my_origin).norm_sq());
     296             : 
     297          14 :   if (conservative_p_dist_sq < min_distance_sq)
     298             :     {
     299             :       // the physical point is definitely not contained in the element:
     300           6 :       return false;
     301             :     }
     302             : 
     303             :   // this captures the case that the point is not (almost) in the direction of the element.:
     304             :   // first, project the problem onto the unit sphere:
     305           0 :   Point p_o(p - my_origin);
     306           1 :   pt0_o /= pt0_o.norm();
     307           1 :   pt1_o /= pt1_o.norm();
     308           1 :   pt2_o /= pt2_o.norm();
     309           1 :   p_o /= p_o.norm();
     310             : 
     311             :   // now, check if it is in the projected face; by comparing the distance of
     312             :   // any point in the element to \p p with the largest distance between this point
     313             :   // to any other point in the element.
     314           1 :   if ((p_o - pt0_o).norm_sq() > std::max((pt0_o - pt1_o).norm_sq(), (pt0_o - pt2_o).norm_sq()) ||
     315           1 :       (p_o - pt1_o).norm_sq() > std::max((pt1_o - pt2_o).norm_sq(), (pt1_o - pt0_o).norm_sq()) ||
     316           0 :       (p_o - pt2_o).norm_sq() > std::max((pt2_o - pt0_o).norm_sq(), (pt2_o - pt1_o).norm_sq()) )
     317             :     {
     318             :       // the physical point is definitely not contained in the element
     319           1 :       return false;
     320             :     }
     321             : 
     322             : 
     323             :   // It seems that \p p is at least close to this element.
     324             :   //
     325             :   // Declare a basic FEType.  Will use default in the base,
     326             :   // and something else (not important) in radial direction.
     327           0 :   FEType fe_type(default_order());
     328             : 
     329           0 :   const Point mapped_point = InfFEMap::inverse_map(dim(), this, p,
     330           0 :                                                    tol, false);
     331             : 
     332           0 :   return this->on_reference_element(mapped_point, tol);
     333             : }
     334             : 
     335             : std::vector<unsigned>
     336         144 : InfPrism::sides_on_edge(const unsigned int e) const
     337             : {
     338          48 :   libmesh_assert_less(e, n_edges());
     339         192 :   return {std::begin(edge_sides_map[e]), std::end(edge_sides_map[e])};
     340             : }
     341             : 
     342             : 
     343             : bool
     344          16 : InfPrism::is_flipped() const
     345             : {
     346          10 :   return (triple_product(this->point(1)-this->point(0),
     347          10 :                          this->point(2)-this->point(0),
     348          28 :                          this->point(3)-this->point(0)) < 0);
     349             : }
     350             : 
     351             : std::vector<unsigned int>
     352         270 : InfPrism::edges_adjacent_to_node(const unsigned int n) const
     353             : {
     354          90 :   libmesh_assert_less(n, this->n_nodes());
     355             : 
     356             :   // For vertices, we use the InfPrism::adjacent_edges_map with
     357             :   // appropriate "trimming" based on whether the vertices are "at
     358             :   // infinity" or not.  Otherwise each of the mid-edge nodes is
     359             :   // adjacent only to the edge it is on, and the remaining nodes are
     360             :   // not adjacent to any edge.
     361         270 :   if (this->is_vertex(n))
     362             :     {
     363         180 :       auto trim = (n < 3) ? 0 : 2;
     364         180 :       return {std::begin(adjacent_edges_map[n]), std::end(adjacent_edges_map[n]) - trim};
     365             :     }
     366          90 :   else if (this->is_edge(n))
     367          45 :     return {n - this->n_vertices()};
     368             : 
     369          15 :   libmesh_assert(this->is_face(n) || this->is_internal(n));
     370          30 :   return {};
     371             : }
     372             : 
     373             : 
     374             : 
     375           0 : bool InfPrism::on_reference_element(const Point & p,
     376             :                                     const Real eps) const
     377             : {
     378           0 :   const Real & xi = p(0);
     379           0 :   const Real & eta = p(1);
     380           0 :   const Real & zeta = p(2);
     381             : 
     382             :   // inside the reference triangle with zeta in [-1,1]
     383           0 :   return ((xi   >=  0.-eps) &&
     384           0 :           (eta  >=  0.-eps) &&
     385           0 :           (zeta >= -1.-eps) &&
     386           0 :           (zeta <=  1.+eps) &&
     387           0 :           ((xi + eta) <= 1.+eps));
     388             : }
     389             : 
     390             : 
     391             : 
     392             : 
     393             : } // namespace libMesh
     394             : 
     395             : 
     396             : 
     397             : #endif // ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS

Generated by: LCOV version 1.14