LCOV - code coverage report
Current view: top level - src/geom - face_c0polygon.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 131 184 71.2 %
Date: 2025-08-19 19:27:09 Functions: 16 21 76.2 %
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/face_c0polygon.h"
      20             : 
      21             : #include "libmesh/edge_edge2.h"
      22             : #include "libmesh/enum_order.h"
      23             : #include "libmesh/tensor_value.h"
      24             : 
      25             : // C++ headers
      26             : #include <numeric> // std::iota
      27             : 
      28             : namespace libMesh
      29             : {
      30             : 
      31             : 
      32       29293 : C0Polygon::C0Polygon (const unsigned int num_sides, Elem * p) :
      33       29293 :   Polygon(num_sides, num_sides, p)
      34             : {
      35             :   // A default triangulation is better than nothing
      36       94284 :   for (int i : make_range(num_sides-2))
      37       64991 :     this->_triangulation.push_back({0, i+1, i+2});
      38       29293 : }
      39             : 
      40             : 
      41           0 : unsigned int C0Polygon::opposite_node(const unsigned int node_in,
      42             :                                       const unsigned int side_in) const
      43             : {
      44           0 :   const auto ns = this->n_sides();
      45           0 :   if (ns % 2)
      46           0 :     libmesh_error();
      47             : 
      48           0 :   libmesh_assert_less (node_in, ns);
      49           0 :   libmesh_assert_less (node_in, this->n_nodes());
      50           0 :   libmesh_assert_less (side_in, this->n_sides());
      51           0 :   libmesh_assert(this->is_node_on_side(node_in, side_in));
      52             : 
      53           0 :   if (node_in == side_in)
      54           0 :     return (node_in + ns/2 + 1) % ns;
      55             : 
      56           0 :   libmesh_assert(node_in == side_in + 1);
      57           0 :   return (node_in + ns/2 - 1) % ns;
      58             : }
      59             : 
      60             : 
      61             : 
      62             : // ------------------------------------------------------------
      63             : // C0Polygon class member functions
      64             : 
      65        8813 : bool C0Polygon::is_vertex(const unsigned int libmesh_dbg_var(i)) const
      66             : {
      67         836 :   libmesh_assert (i < this->n_nodes());
      68             : 
      69        8813 :   return true;
      70             : }
      71             : 
      72        3053 : bool C0Polygon::is_edge(const unsigned int libmesh_dbg_var(i)) const
      73             : {
      74          86 :   libmesh_assert (i < this->n_nodes());
      75             : 
      76        3053 :   return false;
      77             : }
      78             : 
      79        1349 : bool C0Polygon::is_face(const unsigned int libmesh_dbg_var(i)) const
      80             : {
      81          38 :   libmesh_assert (i < this->n_nodes());
      82             : 
      83        1349 :   return false;
      84             : }
      85             : 
      86        5756 : bool C0Polygon::is_node_on_side(const unsigned int n,
      87             :                                 const unsigned int s) const
      88             : {
      89         182 :   const auto ns = this->n_sides();
      90         182 :   libmesh_assert_less (s, ns);
      91         182 :   libmesh_assert_less (n, this->n_nodes());
      92             : 
      93        5847 :   return ((n % ns) == s) ||
      94        3060 :          ((n < ns) && ((s+1)%ns) == n);
      95             : }
      96             : 
      97             : std::vector<unsigned int>
      98       12778 : C0Polygon::nodes_on_side(const unsigned int s) const
      99             : {
     100         916 :   const auto ns = this->n_sides();
     101         916 :   libmesh_assert(!(this->n_nodes() % ns));
     102         916 :   libmesh_assert_less(s, ns);
     103             : 
     104       12778 :   std::vector<unsigned int> returnval(2);
     105       12778 :   returnval[0] = s;
     106       12778 :   returnval[1] = (s+1)%ns;
     107             : 
     108       13694 :   return returnval;
     109             : }
     110             : 
     111             : std::vector<unsigned int>
     112        1589 : C0Polygon::nodes_on_edge(const unsigned int e) const
     113             : {
     114        1589 :   return this->nodes_on_side(e);
     115             : }
     116             : 
     117       22964 : bool C0Polygon::has_affine_map() const
     118             : {
     119        1898 :   const unsigned int ns = this->n_sides();
     120             : 
     121             :   // Get a good tolerance to use
     122        1898 :   Real perimeter_l1 = 0;
     123      137713 :   for (auto s : make_range(ns))
     124      229498 :     perimeter_l1 += (this->point((s+1)%ns) - this->point(s)).l1_norm();
     125       22964 :   const Real tol = perimeter_l1 * affine_tol;
     126             : 
     127             :   // Find the map we expect to have
     128        3796 :   const Point veci = this->point(1) - this->point(0);
     129        1898 :   const Point vec12 = this->point(2) - this->point(1);
     130             : 
     131             :   // Exterior angle
     132       22964 :   const Real theta = 2 * libMesh::pi / ns;
     133       22964 :   const Real costheta = cos(theta);
     134       22964 :   const Real sintheta = sin(theta);
     135             : 
     136        3796 :   RealTensor map;
     137       22964 :   map(0, 0) = veci(0);
     138       22964 :   map(1, 0) = veci(1);
     139       22964 :   map(2, 0) = veci(2);
     140       22964 :   map(0, 1) = (vec12(0) - veci(0)*costheta)/sintheta;
     141       22964 :   map(1, 1) = (vec12(1) - veci(1)*costheta)/sintheta;
     142       22964 :   map(2, 1) = (vec12(2) - veci(2)*costheta)/sintheta;
     143             : 
     144        1898 :   libmesh_assert_less((map * this->master_point(1) -
     145             :                        (this->point(1) - this->point(0))).l1_norm(),
     146             :                       tol);
     147        1898 :   libmesh_assert_less((map * this->master_point(2) -
     148             :                        (this->point(2) - this->point(0))).l1_norm(),
     149             :                       tol);
     150             : 
     151       67960 :   for (auto i : make_range(3u, ns))
     152       87159 :     if ((map * this->master_point(i) -
     153       11295 :                      (this->point(i) - this->point(0))).l1_norm() >
     154             :         tol)
     155          31 :       return false;
     156             : 
     157        1867 :   return true;
     158             : }
     159             : 
     160             : 
     161             : 
     162      356750 : Order C0Polygon::default_order() const
     163             : {
     164      356750 :   return FIRST;
     165             : }
     166             : 
     167             : 
     168             : 
     169        4053 : std::unique_ptr<Elem> C0Polygon::build_side_ptr (const unsigned int i)
     170             : {
     171         127 :   const auto ns = this->n_sides();
     172         127 :   libmesh_assert_less (i, ns);
     173             : 
     174        4053 :   std::unique_ptr<Elem> sidep = std::make_unique<Edge2>();
     175        4180 :   sidep->set_node(0, this->node_ptr(i));
     176        4180 :   sidep->set_node(1, this->node_ptr((i+1)%ns));
     177             : 
     178        4053 :   sidep->set_interior_parent(this);
     179        3926 :   sidep->inherit_data_from(*this);
     180             : 
     181        4180 :   return sidep;
     182           0 : }
     183             : 
     184             : 
     185             : 
     186       92556 : void C0Polygon::build_side_ptr (std::unique_ptr<Elem> & side,
     187             :                                 const unsigned int i)
     188             : {
     189        2655 :   const auto ns = this->n_sides();
     190        2655 :   libmesh_assert_less (i, ns);
     191             : 
     192       92556 :   if (!side.get() || side->type() != EDGE2)
     193             :     {
     194        7634 :       side = this->build_side_ptr(i);
     195             :     }
     196             :   else
     197             :     {
     198       86140 :       side->inherit_data_from(*this);
     199             : 
     200       91226 :       side->set_node(0, this->node_ptr(i));
     201       91226 :       side->set_node(1, this->node_ptr((i+1)%ns));
     202             :     }
     203       92556 : }
     204             : 
     205             : 
     206             : 
     207           0 : void C0Polygon::connectivity(const unsigned int /*sf*/,
     208             :                              const IOPackage /*iop*/,
     209             :                              std::vector<dof_id_type> & /*conn*/) const
     210             : {
     211           0 :   libmesh_not_implemented();
     212             : }
     213             : 
     214             : 
     215             : 
     216         284 : Real C0Polygon::volume () const
     217             : {
     218             :   // This specialization is good for Lagrange mappings only
     219         284 :   if (this->mapping_type() != LAGRANGE_MAP)
     220           0 :     return this->Elem::volume();
     221             : 
     222             :   // We use a triangulation to calculate here; assert that it's as
     223             :   // consistent as possible.
     224           8 :   libmesh_assert_equal_to (this->_triangulation.size(),
     225             :                            this->n_nodes() - 2);
     226             : 
     227           8 :   Real double_area = 0;
     228        1065 :   for (const auto & triangle : this->_triangulation)
     229             :     {
     230         781 :       Point v01 = this->point(triangle[1]) -
     231         803 :                   this->point(triangle[0]);
     232         781 :       Point v02 = this->point(triangle[2]) -
     233          44 :                   this->point(triangle[0]);
     234         781 :       double_area += std::sqrt(cross_norm_sq(v01, v02));
     235             :     }
     236             : 
     237         284 :   return double_area/2;
     238             : }
     239             : 
     240             : 
     241             : 
     242          72 : Point C0Polygon::true_centroid () const
     243             : {
     244             :   // This specialization is good for Lagrange mappings only
     245          72 :   if (this->mapping_type() != LAGRANGE_MAP)
     246           0 :     return this->Elem::true_centroid();
     247             : 
     248             :   // We use a triangulation to calculate here; assert that it's as
     249             :   // consistent as possible.
     250           6 :   libmesh_assert_equal_to (this->_triangulation.size(),
     251             :                            this->n_nodes() - 2);
     252             : 
     253           6 :   Real double_area = 0;
     254           6 :   Point double_area_weighted_centroid;
     255         288 :   for (const auto & triangle : this->_triangulation)
     256             :     {
     257         216 :       Point v01 = this->point(triangle[1]) -
     258         234 :                   this->point(triangle[0]);
     259         216 :       Point v02 = this->point(triangle[2]) -
     260          36 :                   this->point(triangle[0]);
     261             : 
     262         216 :       const Real  double_tri_area = std::sqrt(cross_norm_sq(v01, v02));
     263         216 :       const Point tri_centroid = (this->point(triangle[0]) +
     264         234 :                                   this->point(triangle[1]) +
     265         234 :                                   this->point(triangle[2]))/3;
     266             : 
     267         216 :       double_area += double_tri_area;
     268             : 
     269          18 :       double_area_weighted_centroid += double_tri_area * tri_centroid;
     270             :     }
     271             : 
     272           6 :   return double_area_weighted_centroid / double_area;
     273             : }
     274             : 
     275             : 
     276             : 
     277             : std::pair<unsigned short int, unsigned short int>
     278           0 : C0Polygon::second_order_child_vertex (const unsigned int /*n*/) const
     279             : {
     280           0 :   libmesh_not_implemented();
     281             :   return std::pair<unsigned short int, unsigned short int> (0, 0);
     282             : }
     283             : 
     284             : 
     285         600 : void C0Polygon::permute(unsigned int perm_num)
     286             : {
     287          77 :   const auto ns = this->n_sides();
     288          77 :   libmesh_assert_less (perm_num, ns);
     289             : 
     290             :   // This is mostly for unit testing so I'll just make it O(N).
     291        2880 :   for (unsigned int p : make_range(perm_num))
     292             :     {
     293         298 :       libmesh_ignore(p);
     294             : 
     295         596 :       Node * tempnode = this->node_ptr(0);
     296         596 :       Elem * tempneigh = this->neighbor_ptr(0);
     297       11400 :       for (unsigned int s : make_range(ns-1))
     298             :         {
     299       10312 :           this->set_node(s, this->node_ptr((s+1)%ns));
     300        2384 :           this->set_neighbor(s, this->neighbor_ptr(s+1));
     301             :         }
     302        2280 :       this->set_node(ns-1, tempnode);
     303         596 :       this->set_neighbor(ns-1, tempneigh);
     304             : 
     305             :       // Keep the same triangulation, just with permuted node order,
     306             :       // so we can really expect this to act like the *same* polygon
     307        9120 :       for (auto & triangle : this->_triangulation)
     308       27360 :         for (auto i : make_range(3))
     309       20520 :           triangle[i] = (triangle[i]+1)%ns;
     310             :     }
     311         600 : }
     312             : 
     313             : 
     314         580 : void C0Polygon::flip(BoundaryInfo * boundary_info)
     315             : {
     316          17 :   libmesh_assert(boundary_info);
     317             : 
     318          17 :   const auto ns = this->n_sides();
     319             : 
     320             :   // Reorder nodes in such a way as to keep side ns-1 the same
     321        1882 :   for (auto s : make_range(0u, ns/2))
     322             :     {
     323        1302 :       swap2nodes(s, ns-1-s);
     324        1264 :       swap2neighbors(s, ns-2-s);
     325        1302 :       swap2boundarysides(s, ns-2-s, boundary_info);
     326        1302 :       swap2boundaryedges(s, ns-2-s, boundary_info);
     327             :     }
     328         580 : }
     329             : 
     330             : 
     331             : 
     332         180 : ElemType C0Polygon::side_type (const unsigned int libmesh_dbg_var(s)) const
     333             : {
     334          15 :   libmesh_assert_less (s, this->n_sides());
     335         180 :   return EDGE2;
     336             : }
     337             : 
     338             : 
     339             : 
     340           0 : void C0Polygon::retriangulate()
     341             : {
     342           0 :   this->_triangulation.clear();
     343             : 
     344             :   // Start with the full polygon
     345           0 :   std::vector<int> remaining_nodes(this->n_nodes());
     346           0 :   std::iota(remaining_nodes.begin(), remaining_nodes.end(), 0);
     347             : 
     348           0 :   const auto ns = this->n_sides();
     349             : 
     350           0 :   const Point vavg = this->vertex_average();
     351             : 
     352             :   // Find out what plane we're in, if we're in 3D
     353             : #if LIBMESH_DIM > 2
     354           0 :   Point plane_normal;
     355           0 :   for (auto i : make_range(ns))
     356             :     {
     357           0 :       const Point vi     = this->point(i) - vavg;
     358           0 :       const Point viplus = this->point((i+1)%ns) - vavg;
     359           0 :       plane_normal += vi.cross(viplus);
     360             :     }
     361           0 :   plane_normal = plane_normal.unit();
     362             : #endif
     363             : 
     364             :   // Greedy heuristic: find the node with the most acutely convex
     365             :   // angle and strip it out as a triangle.
     366           0 :   while (remaining_nodes.size() > 2)
     367             :     {
     368           0 :       Real min_cos_angle = 1;
     369           0 :       int best_vertex = -1;
     370           0 :       for (auto n : index_range(remaining_nodes))
     371             :         {
     372           0 :           const Point & pn = this->point(remaining_nodes[n]);
     373           0 :           const Point & pnext = this->point(remaining_nodes[(n+1)%ns]);
     374           0 :           const Point & pprev = this->point(remaining_nodes[(n+ns-1)%ns]);
     375           0 :           const Point vprev = (pn - pprev).unit();
     376           0 :           const Point vnext = (pnext - pn).unit();
     377             : 
     378           0 :           const Real sign_check = (vprev.cross(vnext)) * plane_normal;
     379           0 :           if (sign_check <= 0)
     380           0 :             continue;
     381             : 
     382           0 :           const Real cos_angle = vprev * vnext;
     383           0 :           if (cos_angle < min_cos_angle)
     384             :             {
     385           0 :               min_cos_angle = cos_angle;
     386           0 :               best_vertex = n;
     387             :             }
     388             :         }
     389             : 
     390           0 :       libmesh_assert(best_vertex >= 0);
     391             : 
     392           0 :       this->_triangulation.push_back({remaining_nodes[(best_vertex+ns-1)%ns],
     393           0 :                                       remaining_nodes[best_vertex],
     394           0 :                                       remaining_nodes[(best_vertex+1)%ns]});
     395           0 :       remaining_nodes.erase(remaining_nodes.begin()+best_vertex);
     396             :     }
     397           0 : }
     398             : 
     399             : 
     400             : 
     401             : } // namespace libMesh

Generated by: LCOV version 1.14