LCOV - code coverage report
Current view: top level - src/geom - face_tri.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4538 (c3bcaa) with base 0a0a9d Lines: 69 142 48.6 %
Date: 2026-09-01 22:57:06 Functions: 13 15 86.7 %
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             : // Local includes
      19             : #include "libmesh/face_tri.h"
      20             : #include "libmesh/edge_edge2.h"
      21             : #include "libmesh/face_tri3.h"
      22             : #include "libmesh/enum_elem_quality.h"
      23             : 
      24             : // C++ includes
      25             : #include <array>
      26             : 
      27             : namespace libMesh
      28             : {
      29             : 
      30             : 
      31             : // ------------------------------------------------------------
      32             : // Tri class static member initializations
      33             : const int Tri::num_sides;
      34             : const int Tri::num_children;
      35             : 
      36             : // Note: we can omit initialization of the third entry of each row because
      37             : // static variables are automatically zero-initialized.
      38             : const Real Tri::_master_points[7][3] =
      39             :   {
      40             :     {0, 0},
      41             :     {1, 0},
      42             :     {0, 1},
      43             :     {0.5, 0},
      44             :     {0.5, 0.5},
      45             :     {0, 0.5},
      46             :     {Real(1)/3, Real(1)/3}
      47             :   };
      48             : 
      49             : const unsigned int Tri::adjacent_sides_map[/*num_vertices*/3][/*n_adjacent_sides*/2] =
      50             :   {
      51             :     {0, 2},  // Sides adjacent to node 0
      52             :     {0, 1},  // Sides adjacent to node 1
      53             :     {1, 2}   // Sides adjacent to node 2
      54             :   };
      55             : 
      56             : 
      57             : 
      58             : // ------------------------------------------------------------
      59             : // Tri class member functions
      60           0 : dof_id_type Tri::key (const unsigned int s) const
      61             : {
      62           0 :   libmesh_assert_less (s, this->n_sides());
      63             : 
      64           0 :   return this->compute_key(this->node_id(Tri3::side_nodes_map[s][0]),
      65           0 :                            this->node_id(Tri3::side_nodes_map[s][1]));
      66             : }
      67             : 
      68             : 
      69             : 
      70    33552771 : dof_id_type Tri::low_order_key (const unsigned int s) const
      71             : {
      72      846084 :   libmesh_assert_less (s, this->n_sides());
      73             : 
      74    34398477 :   return this->compute_key(this->node_id(Tri3::side_nodes_map[s][0]),
      75    34398855 :                            this->node_id(Tri3::side_nodes_map[s][1]));
      76             : }
      77             : 
      78             : 
      79             : 
      80     4689204 : unsigned int Tri::local_side_node(unsigned int side,
      81             :                                   unsigned int side_node) const
      82             : {
      83      390844 :   libmesh_assert_less (side, this->n_sides());
      84      390844 :   libmesh_assert_less (side_node, Tri3::nodes_per_side);
      85             : 
      86     4689204 :   return Tri3::side_nodes_map[side][side_node];
      87             : }
      88             : 
      89             : 
      90             : 
      91   111038504 : unsigned int Tri::local_edge_node(unsigned int edge,
      92             :                                   unsigned int edge_node) const
      93             : {
      94   111038504 :   return local_side_node(edge, edge_node);
      95             : }
      96             : 
      97             : 
      98             : 
      99          48 : dof_id_type Tri::key () const
     100             : {
     101          52 :   return this->compute_key(this->node_id(0),
     102             :                            this->node_id(1),
     103          48 :                            this->node_id(2));
     104             : }
     105             : 
     106             : 
     107             : 
     108      213395 : std::unique_ptr<Elem> Tri::side_ptr (const unsigned int i)
     109             : {
     110        6300 :   libmesh_assert_less (i, this->n_sides());
     111             : 
     112      213395 :   std::unique_ptr<Elem> edge = std::make_unique<Edge2>();
     113             : 
     114      640185 :   for (auto n : edge->node_index_range())
     115      439326 :     edge->set_node(n, this->node_ptr(Tri3::side_nodes_map[i][n]));
     116             : 
     117      213395 :   return edge;
     118           0 : }
     119             : 
     120             : 
     121             : 
     122    31617703 : void Tri::side_ptr (std::unique_ptr<Elem> & side,
     123             :                     const unsigned int i)
     124             : {
     125    31617703 :   this->simple_side_ptr<Tri,Tri3>(side, i, EDGE2);
     126    31617703 : }
     127             : 
     128             : 
     129             : 
     130    11175298 : bool Tri::is_child_on_side(const unsigned int c,
     131             :                            const unsigned int s) const
     132             : {
     133     3230222 :   libmesh_assert_less (c, this->n_children());
     134     3230222 :   libmesh_assert_less (s, this->n_sides());
     135             : 
     136    11175298 :   return (c == s || c == (s+1)%3);
     137             : }
     138             : 
     139             : 
     140       46054 : bool Tri::is_flipped() const
     141             : {
     142             :   return (
     143             : #if LIBMESH_DIM > 2
     144             :           // Don't bother outside the XY plane
     145       87854 :           !this->point(0)(2) && !this->point(1)(2) &&
     146      133908 :           !this->point(2)(2) &&
     147             : #endif
     148       46310 :           ((this->point(1)(0)-this->point(0)(0))*
     149       46054 :            (this->point(2)(1)-this->point(0)(1)) <
     150       46310 :            (this->point(2)(0)-this->point(0)(0))*
     151       87854 :            (this->point(1)(1)-this->point(0)(1))));
     152             : }
     153             : 
     154             : 
     155             : std::vector<unsigned int>
     156        9120 : Tri::edges_adjacent_to_node(const unsigned int n) const
     157             : {
     158         760 :   libmesh_assert_less(n, this->n_nodes());
     159             : 
     160             :   // For vertices, we use the Tri::adjacent_sides_map, otherwise each
     161             :   // of the mid-edge nodes is adjacent only to the edge it is on, and the
     162             :   // center node is not adjacent to any edge.
     163        9120 :   if (this->is_vertex(n))
     164        6240 :     return {std::begin(adjacent_sides_map[n]), std::end(adjacent_sides_map[n])};
     165        3360 :   else if (this->is_edge(n))
     166        2880 :     return {n - this->n_vertices()};
     167             : 
     168          40 :   libmesh_assert(this->is_face(n));
     169         440 :   return {};
     170             : }
     171             : 
     172             : 
     173        2133 : Real Tri::quality (const ElemQuality q) const
     174             : {
     175        2133 :   switch (q)
     176             :     {
     177         213 :     case ASPECT_RATIO:
     178             :       {
     179             :         // Aspect Ratio definition from Ansys Theory Manual.
     180             :         // Reference: Ansys, Inc. Theory Reference, Ansys Release 9.0, 2004 (Chapter: 13.7.3)
     181             : 
     182             :         // Compute midpoint positions along each edge
     183             :         Point m[3] = {
     184          12 :           Real(0.5) * (this->point(0) + this->point(1)),  // side opposite vertex 2
     185           6 :           Real(0.5) * (this->point(1) + this->point(2)),  // side opposite vertex 0
     186          18 :           Real(0.5) * (this->point(2) + this->point(0))}; // side opposite vertex 1
     187             : 
     188             :         // opposite[i] is the side index which is "opposite" vertex i
     189             :         static const unsigned int opposite[3] = {1, 2, 0};
     190             : 
     191             :         // other[i] is the side index which is _not_ i and _not_ opposite[i]
     192             :         static const unsigned int other[3] = {2, 0, 1};
     193             : 
     194             :         // Input is vertex index, i = 0, 1, 2
     195         639 :         auto vertex_aspect_ratio = [&](unsigned int i) -> Real
     196             :         {
     197             :           // Compute vectors:
     198             :           // v0: (vertex v, opposite midpoint)
     199             :           // v1: (midpoint[i], last midpoint)
     200         657 :           Point v0 = m[opposite[i]] - this->point(i);
     201         639 :           Point v1 = m[other[i]] - m[i];
     202             : 
     203             :           // Compute the length of the midlines
     204         639 :           Real v0_norm = v0.norm();
     205         639 :           Real v1_norm = v1.norm();
     206             : 
     207             :           // Instead of dividing by zero in the next step, just return
     208             :           // 0.  The optimal aspect ratio is 1.0, and "high" aspect
     209             :           // ratios are bad, but an aspect ratio of 0 should also be
     210             :           // considered bad.
     211         639 :           if (v0_norm == 0. || v1_norm == 0.)
     212           0 :             return 0.;
     213             : 
     214             :           // Compute sine of the angle between v0, v1.
     215         639 :           Real sin_theta = cross_norm(v0, v1) / v0_norm / v1_norm;
     216         639 :           Real v0s = v0_norm*sin_theta;
     217         639 :           Real v1s = v1_norm*sin_theta;
     218             : 
     219             :           // Determine the min, max of each midline length and its
     220             :           // projection.
     221          18 :           auto [min0, max0] = std::minmax(v0_norm, v1s);
     222          18 :           auto [min1, max1] = std::minmax(v0s, v1_norm);
     223             : 
     224             :           // Return the max of the two quotients
     225         777 :           return std::max(max0/min0, max1/min1);
     226         207 :         };
     227             : 
     228         420 :         return std::max(std::max(vertex_aspect_ratio(0), vertex_aspect_ratio(1)), vertex_aspect_ratio(2)) / std::sqrt(3);
     229             :       }
     230             : 
     231             :       /**
     232             :        * Source: Netgen, meshtool.cpp, TriangleQualityInst
     233             :        */
     234           0 :     case DISTORTION:
     235             :     case STRETCH:
     236             :       {
     237           0 :         const Point & p1 = this->point(0);
     238           0 :         const Point & p2 = this->point(1);
     239           0 :         const Point & p3 = this->point(2);
     240             : 
     241           0 :         Point v1 = p2 - p1;
     242           0 :         Point v2 = p3 - p1;
     243           0 :         Point v3 = p3 - p2;
     244           0 :         const Real l1 = v1.norm();
     245           0 :         const Real l2 = v2.norm();
     246           0 :         const Real l3 = v3.norm();
     247             : 
     248             :         // if one length is 0, quality is quite bad!
     249           0 :         if ((l1 <=0.) || (l2 <= 0.) || (l3 <= 0.))
     250           0 :           return 0.;
     251             : 
     252           0 :         const Real s1 = std::sin(std::acos(v1*v2/l1/l2)/2.);
     253           0 :         v1 *= -1;
     254           0 :         const Real s2 = std::sin(std::acos(v1*v3/l1/l3)/2.);
     255           0 :         const Real s3 = std::sin(std::acos(v2*v3/l2/l3)/2.);
     256             : 
     257           0 :         return 8. * s1 * s2 * s3;
     258             : 
     259             :       }
     260             : 
     261             :       // From: P. Knupp, "Algebraic mesh quality metrics for
     262             :       // unstructured initial meshes," Finite Elements in Analysis
     263             :       // and Design 39, 2003, p. 217-241, Section 3.2.
     264           0 :     case SHAPE:
     265             :       {
     266             :         // Unlike Quads, the Tri SHAPE metric is independent of the
     267             :         // node at which it is computed, we choose to compute it for
     268             :         // node 0.
     269             : 
     270             :         // The nodal Jacobian matrix A is a 3x2 matrix, hence we
     271             :         // represent it by a std:array with 6 entries.
     272             :         Point
     273           0 :           d01 = point(1) - point(0),
     274           0 :           d02 = point(2) - point(0);
     275             : 
     276             :         std::array<Real, 6> A =
     277           0 :           {{d01(0), d02(0),
     278           0 :             d01(1), d02(1),
     279           0 :             d01(2), d02(2)}};
     280             : 
     281             :         // Compute metric tensor entries, T = A^T * A.
     282             :         // This is a symmetric 2x2 matrix so we only
     283             :         // compute one of the off-diagonal entries.
     284             :         // As in the paper, we define lambda_ij := T_ij.
     285             :         Real
     286           0 :           lambda11 = A[0]*A[0] + A[2]*A[2] + A[4]*A[4],
     287           0 :           lambda12 = A[0]*A[1] + A[2]*A[3] + A[4]*A[5],
     288           0 :           lambda22 = A[1]*A[1] + A[3]*A[3] + A[5]*A[5];
     289             : 
     290             :         // Compute the denominator of the metric. If it is exactly
     291             :         // zero then return 0 (lowest quality) for this metric.
     292           0 :         Real den = lambda11 + lambda22 - lambda12;
     293           0 :         if (den == 0.0)
     294           0 :           return 0.;
     295             : 
     296             :         // Compute the nodal area
     297           0 :         Real alpha = std::sqrt(lambda11 * lambda22 - lambda12 * lambda12);
     298             : 
     299             :         // Finally, compute and return the metric.
     300           0 :         return std::sqrt(3) * alpha / den;
     301             :       }
     302             : 
     303        1920 :     default:
     304        1920 :       return Elem::quality(q);
     305             :     }
     306             : 
     307             :   // We won't get here.
     308             :   return Elem::quality(q);
     309             : }
     310             : 
     311             : 
     312             : 
     313             : 
     314             : 
     315             : 
     316           0 : std::pair<Real, Real> Tri::qual_bounds (const ElemQuality q) const
     317             : {
     318           0 :   std::pair<Real, Real> bounds;
     319             : 
     320           0 :   switch (q)
     321             :     {
     322             :       // A recent copy of the cubit manual [0] does not list bounds
     323             :       // for EDGE_LENGTH_RATIO or ASPECT_RATIO quality metrics, so we
     324             :       // have arbitrarily adopted the same values used for Quads here.
     325             :       // I'm open to suggestions of other appropriate values.
     326             :       //
     327             :       // [0]: https://cubit.sandia.gov/files/cubit/16.08/help_manual/WebHelp/mesh_generation/mesh_quality_assessment/triangular_metrics.htm
     328           0 :     case EDGE_LENGTH_RATIO:
     329             :     case ASPECT_RATIO:
     330           0 :       bounds.first  = 1.;
     331           0 :       bounds.second = 4.;
     332           0 :       break;
     333             : 
     334           0 :     case MAX_ANGLE:
     335           0 :       bounds.first  = 60.;
     336           0 :       bounds.second = 90.;
     337           0 :       break;
     338             : 
     339           0 :     case MIN_ANGLE:
     340           0 :       bounds.first  = 30.;
     341           0 :       bounds.second = 60.;
     342           0 :       break;
     343             : 
     344           0 :     case CONDITION:
     345           0 :       bounds.first  = 1.;
     346           0 :       bounds.second = 1.3;
     347           0 :       break;
     348             : 
     349           0 :     case JACOBIAN:
     350             :     case SCALED_JACOBIAN:
     351           0 :       bounds.first  = 0.5;
     352           0 :       bounds.second = 1.155;
     353           0 :       break;
     354             : 
     355           0 :     case SIZE:
     356             :     case SHAPE:
     357           0 :       bounds.first  = 0.25;
     358           0 :       bounds.second = 1.;
     359           0 :       break;
     360             : 
     361           0 :     case DISTORTION:
     362           0 :       bounds.first  = 0.6;
     363           0 :       bounds.second = 1.;
     364           0 :       break;
     365             : 
     366           0 :     default:
     367           0 :       libMesh::out << "Warning: Invalid quality measure chosen." << std::endl;
     368           0 :       bounds.first  = -1;
     369           0 :       bounds.second = -1;
     370             :     }
     371             : 
     372           0 :   return bounds;
     373             : }
     374             : 
     375             : 
     376    14153942 : bool Tri::on_reference_element(const Point & p,
     377             :                                const Real eps) const
     378             : {
     379     2941960 :   const Real & xi = p(0);
     380     2941960 :   const Real & eta = p(1);
     381    27566558 :   return ((xi  >= 0.-eps) &&
     382    17033758 :           (eta >= 0.-eps) &&
     383    15776148 :           ((xi + eta) <= 1.+eps));
     384             : }
     385             : 
     386             : 
     387             : } // namespace libMesh

Generated by: LCOV version 1.14