LCOV - code coverage report
Current view: top level - src/geom - edge_edge4.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 57 127 44.9 %
Date: 2025-08-19 19:27:09 Functions: 7 14 50.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             : 
      19             : 
      20             : // Local includes
      21             : #include "libmesh/edge_edge4.h"
      22             : #include "libmesh/enum_io_package.h"
      23             : #include "libmesh/enum_order.h"
      24             : 
      25             : namespace libMesh
      26             : {
      27             : 
      28             : // ------------------------------------------------------------
      29             : // Edge4 class static member initializations
      30             : const int Edge4::num_nodes;
      31             : 
      32             : #ifdef LIBMESH_ENABLE_AMR
      33             : 
      34             : const Real Edge4::_embedding_matrix[Edge4::num_children][Edge4::num_nodes][Edge4::num_nodes] =
      35             :   {
      36             :     // embedding matrix for child 0
      37             : 
      38             :     {
      39             :       // 0       1       2        3        // Shape function index
      40             :       {1.0,      0.0,    0.0,     0.0},    // left,  xi = -1
      41             :       {-0.0625, -0.0625, 0.5625,  0.5625}, // right, xi = 0
      42             :       {0.3125,   0.0625, 0.9375, -0.3125}, // middle left,  xi = -2/3
      43             :       {0.0,      0.0,    1.0,     0.0}     // middle right, xi = -1/3
      44             :     },
      45             : 
      46             :     // embedding matrix for child 1
      47             :     {
      48             :       // 0       1        2        3        // Shape function index
      49             :       {-0.0625, -0.0625,  0.5625,  0.5625}, // left,  xi = 0
      50             :       {0.0,      1.0,     0.0,     0.0},    // right, xi = 1
      51             :       {0.0,      0.0,     0.0,     1.0},    // middle left,  xi = 1/3
      52             :       {0.0625,   0.3125, -0.3125,  0.9375}  // middle right, xi = 2/3
      53             :     }
      54             :   };
      55             : 
      56             : #endif
      57             : 
      58          96 : bool Edge4::is_vertex(const unsigned int i) const
      59             : {
      60          96 :   return (i==0) || (i==1);
      61             : }
      62             : 
      63           0 : bool Edge4::is_edge(const unsigned int i) const
      64             : {
      65           0 :   return (i==2) || (i==3);
      66             : }
      67             : 
      68           0 : bool Edge4::is_face(const unsigned int ) const
      69             : {
      70           0 :   return false;
      71             : }
      72             : 
      73         616 : bool Edge4::is_node_on_side(const unsigned int n,
      74             :                             const unsigned int s) const
      75             : {
      76          20 :   libmesh_assert_less (s, 2);
      77          20 :   libmesh_assert_less (n, Edge4::num_nodes);
      78         616 :   return (s == n);
      79             : }
      80             : 
      81           0 : bool Edge4::is_node_on_edge(const unsigned int,
      82             :                             const unsigned int libmesh_dbg_var(e)) const
      83             : {
      84           0 :   libmesh_assert_equal_to (e, 0);
      85           0 :   return true;
      86             : }
      87             : 
      88             : 
      89             : 
      90        1018 : bool Edge4::has_affine_map() const
      91             : {
      92          80 :   Point v = this->point(1) - this->point(0);
      93        1058 :   if (!v.relative_fuzzy_equals
      94        1018 :       ((this->point(2) - this->point(0))*3, affine_tol))
      95           0 :     return false;
      96        1058 :   if (!v.relative_fuzzy_equals
      97        1018 :       ((this->point(3) - this->point(0))*1.5, affine_tol))
      98           0 :     return false;
      99          40 :   return true;
     100             : }
     101             : 
     102             : 
     103             : 
     104         355 : bool Edge4::has_invertible_map(Real tol) const
     105             : {
     106             :   // At the moment this only makes sense for Lagrange elements
     107          10 :   libmesh_assert_equal_to(this->mapping_type(), LAGRANGE_MAP);
     108             : 
     109             :   // dx/dxi = a*xi^2 + b*xi + c,
     110             :   // where a, b, and c are vector quantities that depend on the
     111             :   // nodal positions as follows:
     112          20 :   const Point & x0 = this->point(0);
     113          10 :   const Point & x1 = this->point(1);
     114          10 :   const Point & x2 = this->point(2);
     115          10 :   const Point & x3 = this->point(3);
     116             : 
     117          10 :   Point a = Real(27)/16 * (x1 - x0 + 3*(x2 - x3));
     118          10 :   Point b = Real(18)/16 * (x0 + x1 - x2 - x3);
     119          10 :   Point c = Real(1)/16  * (x0 - x1 + 27*(x3 - x2));
     120             : 
     121             :   // Normalized midpoint value of Jacobian. If c==0, then dx/dxi(0) =
     122             :   // 0 and the Jacobian is either zero on the whole element, or
     123             :   // changes sign within the element. Either way, the element is not
     124             :   // invertible. Note: use <= tol, so that if someone passes 0 for tol
     125             :   // it still works.
     126         345 :   Real c_norm = c.norm();
     127         355 :   if (c_norm <= tol)
     128           0 :     return false;
     129             : 
     130             :   // Coefficients of the quadratic scalar function
     131             :   // j(xi) := dot(c.unit(), dx/dxi)
     132             :   //        = alpha*xi^2 + beta*xi + gamma
     133         355 :   Real alpha = (a * c) / c_norm;
     134         355 :   Real beta = (b * c) / c_norm;
     135          10 :   Real gamma = c_norm;
     136             : 
     137             :   // If alpha and beta are both (approximately) zero, then the
     138             :   // Jacobian is actually constant but it's not zero (as we already
     139             :   // checked) so the element is invertible!
     140         355 :   if ((std::abs(alpha) <= tol) && (std::abs(beta) <= tol))
     141           2 :     return true;
     142             : 
     143             :   // If alpha is approximately zero, but beta is not, then j(xi) is
     144             :   // actually linear and the Jacobian changes sign at the point xi =
     145             :   // -gamma/beta, so we need to check whether that's in the
     146             :   // element.
     147         284 :   if (std::abs(alpha) <= tol)
     148             :     {
     149             :       // Debugging:
     150             :       // libMesh::out << "alpha = " << alpha << ", std::abs(alpha) <= tol" << std::endl;
     151             : 
     152           0 :       Real xi_0 = -gamma / beta;
     153           0 :       return ((xi_0 < -1.) || (xi_0 > 1.));
     154             :     }
     155             : 
     156             :   // If alpha is not (approximately) zero, then j(xi) is quadratic
     157             :   // and we need to solve for the roots.
     158         284 :   Real sqrt_term = beta*beta - 4*alpha*gamma;
     159             : 
     160             :   // Debugging:
     161             :   // libMesh::out << "sqrt_term = " << sqrt_term << std::endl;
     162             : 
     163             :   // If the term under the square root is negative, then the roots are
     164             :   // imaginary and the element is invertible.
     165         284 :   if (sqrt_term < 0)
     166           2 :     return true;
     167             : 
     168         213 :   sqrt_term = std::sqrt(sqrt_term);
     169             :   Real
     170         213 :     xi_1 = 0.5 * (-beta + sqrt_term) / alpha,
     171         213 :     xi_2 = 0.5 * (-beta - sqrt_term) / alpha;
     172             : 
     173             :   // libMesh::out << "xi_1 = " << xi_1 << std::endl;
     174             :   // libMesh::out << "xi_2 = " << xi_2 << std::endl;
     175             : 
     176             :   // If a root is outside the reference element, it is "OK".
     177             :   bool
     178         213 :     xi_1_ok = ((xi_1 < -1.) || (xi_1 > 1.)),
     179         213 :     xi_2_ok = ((xi_2 < -1.) || (xi_2 > 1.));
     180             : 
     181             :   // If both roots are OK, the element is invertible.
     182         213 :   return xi_1_ok && xi_2_ok;
     183             : }
     184             : 
     185             : 
     186             : 
     187       25893 : Order Edge4::default_order() const
     188             : {
     189       25893 :   return THIRD;
     190             : }
     191             : 
     192             : 
     193             : 
     194           0 : void Edge4::connectivity(const unsigned int sc,
     195             :                          const IOPackage iop,
     196             :                          std::vector<dof_id_type> & conn) const
     197             : {
     198           0 :   libmesh_assert_less_equal (sc, 2);
     199           0 :   libmesh_assert_less (sc, this->n_sub_elem());
     200           0 :   libmesh_assert_not_equal_to (iop, INVALID_IO_PACKAGE);
     201             : 
     202             :   // Create storage
     203           0 :   conn.resize(2);
     204             : 
     205           0 :   switch(iop)
     206             :     {
     207           0 :     case TECPLOT:
     208             :       {
     209           0 :         switch (sc)
     210             :           {
     211           0 :           case 0:
     212           0 :             conn[0] = this->node_id(0)+1;
     213           0 :             conn[1] = this->node_id(2)+1;
     214           0 :             return;
     215             : 
     216           0 :           case 1:
     217           0 :             conn[0] = this->node_id(2)+1;
     218           0 :             conn[1] = this->node_id(3)+1;
     219           0 :             return;
     220             : 
     221           0 :           case 2:
     222           0 :             conn[0] = this->node_id(3)+1;
     223           0 :             conn[1] = this->node_id(1)+1;
     224           0 :             return;
     225             : 
     226           0 :           default:
     227           0 :             libmesh_error_msg("Invalid sc = " << sc);
     228             :           }
     229             : 
     230             :       }
     231             : 
     232           0 :     case VTK:
     233             :       {
     234             : 
     235           0 :         switch (sc)
     236             :           {
     237           0 :           case 0:
     238           0 :             conn[0] = this->node_id(0);
     239           0 :             conn[1] = this->node_id(2);
     240           0 :             return;
     241             : 
     242           0 :           case 1:
     243           0 :             conn[0] = this->node_id(2);
     244           0 :             conn[1] = this->node_id(3);
     245           0 :             return;
     246             : 
     247           0 :           case 2:
     248           0 :             conn[0] = this->node_id(3);
     249           0 :             conn[1] = this->node_id(1);
     250           0 :             return;
     251             : 
     252           0 :           default:
     253           0 :             libmesh_error_msg("Invalid sc = " << sc);
     254             :           }
     255             :       }
     256             : 
     257           0 :     default:
     258           0 :       libmesh_error_msg("Unsupported IO package " << iop);
     259             :     }
     260             : }
     261             : 
     262             : 
     263             : 
     264        8147 : BoundingBox Edge4::loose_bounding_box () const
     265             : {
     266             :   // This might be a curved line through 2-space or 3-space, in which
     267             :   // case the full bounding box can be larger than the bounding box of
     268             :   // just the nodes.
     269             :   //
     270             :   // FIXME - I haven't yet proven the formula below to be correct for
     271             :   // cubics - RHS
     272         299 :   Point pmin, pmax;
     273             : 
     274       32588 :   for (unsigned d=0; d<LIBMESH_DIM; ++d)
     275             :     {
     276       26235 :       Real center = (this->point(2)(d) + this->point(3)(d))/2;
     277       25338 :       Real hd = std::max(std::abs(center - this->point(0)(d)),
     278       26235 :                          std::abs(center - this->point(1)(d)));
     279             : 
     280       24441 :       pmin(d) = center - hd;
     281       24441 :       pmax(d) = center + hd;
     282             :     }
     283             : 
     284        8446 :   return BoundingBox(pmin, pmax);
     285             : }
     286             : 
     287             : 
     288             : 
     289           0 : dof_id_type Edge4::key () const
     290             : {
     291           0 :   return this->compute_key(this->node_id(0),
     292             :                            this->node_id(1),
     293             :                            this->node_id(2),
     294           0 :                            this->node_id(3));
     295             : }
     296             : 
     297             : 
     298             : 
     299           0 : Real Edge4::volume () const
     300             : {
     301             :   // Make copies of our points.  It makes the subsequent calculations a bit
     302             :   // shorter and avoids dereferencing the same pointer multiple times.
     303             :   Point
     304           0 :     x0 = point(0),
     305           0 :     x1 = point(1),
     306           0 :     x2 = point(2),
     307           0 :     x3 = point(3);
     308             : 
     309             :   // Construct constant data vectors.
     310             :   // \vec{x}_{\xi}  = \vec{a1}*xi**2 + \vec{b1}*xi + \vec{c1}
     311             :   // This is copy-pasted directly from the output of a Python script.
     312             :   Point
     313           0 :     a1 = -27*x0/16 + 27*x1/16 + 81*x2/16 - 81*x3/16,
     314           0 :     b1 = 9*x0/8 + 9*x1/8 - 9*x2/8 - 9*x3/8,
     315           0 :     c1 = x0/16 - x1/16 - 27*x2/16 + 27*x3/16;
     316             : 
     317             :   // 4 point quadrature, 7th-order accurate
     318           0 :   const unsigned int N = 4;
     319           0 :   const Real q[N] = {-std::sqrt(525 + 70*std::sqrt(30.)) / 35,
     320             :                      -std::sqrt(525 - 70*std::sqrt(30.)) / 35,
     321             :                      std::sqrt(525 - 70*std::sqrt(30.)) / 35,
     322             :                      std::sqrt(525 + 70*std::sqrt(30.)) / 35};
     323           0 :   const Real w[N] = {(18 - std::sqrt(30.)) / 36,
     324             :                      (18 + std::sqrt(30.)) / 36,
     325             :                      (18 + std::sqrt(30.)) / 36,
     326             :                      (18 - std::sqrt(30.)) / 36};
     327             : 
     328           0 :   Real vol=0.;
     329           0 :   for (unsigned int i=0; i<N; ++i)
     330           0 :     vol += w[i] * (q[i]*q[i]*a1 + q[i]*b1 + c1).norm();
     331             : 
     332           0 :   return vol;
     333             : }
     334             : 
     335             : 
     336          72 : void Edge4::flip(BoundaryInfo * boundary_info)
     337             : {
     338           6 :   libmesh_assert(boundary_info);
     339             : 
     340          72 :   swap2nodes(0,1);
     341          72 :   swap2nodes(2,3);
     342           6 :   swap2neighbors(0,1);
     343          72 :   swap2boundarysides(0,1,boundary_info);
     344          72 : }
     345             : 
     346             : 
     347             : } // namespace libMesh

Generated by: LCOV version 1.14