LCOV - code coverage report
Current view: top level - src/mesh - triangulator_interface.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4500 (a28635) with base 421597 Lines: 160 232 69.0 %
Date: 2026-07-21 14:09:40 Functions: 10 19 52.6 %
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             : 
      19             : #include "libmesh/libmesh_config.h"
      20             : 
      21             : // Avoid anything here that might break precise IEEE754 compatibility,
      22             : // to give us more reproduceable results.
      23             : //
      24             : // We can't disable excess x87 precision from pragmas, but hopefully
      25             : // anyone optimizing will be using SSE instead anyway.
      26             : #if defined(__clang__)
      27             : #  pragma float_control(precise, on)
      28             : #  pragma clang fp contract(off) reassociate(off)
      29             : #elif defined(__NVCOMPILER)
      30             : // We can't get -Kieee from pragmas, but so far FMA contractions are
      31             : // the only thing we've caught breaking us, and nvc++ inherits a
      32             : // pragma for those from LLVM.
      33             : #  pragma clang fp contract(off) reassociate(off)
      34             : #elif defined(__GNUC__)
      35             : // GCC goes last, because other compilers define __GNUC__, because
      36             : // they're liars.
      37             : #  pragma GCC optimize("-fno-unsafe-math-optimizations")
      38             : #  pragma GCC optimize("-ffp-contract=off")
      39             : #endif
      40             : 
      41             : // libmesh includes
      42             : #include "libmesh/mesh_triangle_interface.h"
      43             : #include "libmesh/unstructured_mesh.h"
      44             : #include "libmesh/face_tri3.h"
      45             : #include "libmesh/face_tri6.h"
      46             : #include "libmesh/mesh_generation.h"
      47             : #include "libmesh/mesh_smoother_laplace.h"
      48             : #include "libmesh/boundary_info.h"
      49             : #include "libmesh/mesh_triangle_holes.h"
      50             : #include "libmesh/mesh_triangle_wrapper.h"
      51             : #include "libmesh/enum_elem_type.h"
      52             : #include "libmesh/enum_order.h"
      53             : #include "libmesh/enum_to_string.h"
      54             : #include "libmesh/utility.h"
      55             : 
      56             : #include "libmesh/meshfree_interpolation.h"
      57             : 
      58             : // C/C++ includes
      59             : #include <sstream>
      60             : 
      61             : 
      62             : namespace libMesh
      63             : {
      64             : //
      65             : // Function definitions for the AutoAreaFunction class
      66             : //
      67             : 
      68             : // Constructor
      69           0 : AutoAreaFunction::AutoAreaFunction (const Parallel::Communicator &comm,
      70             :                                     const unsigned int num_nearest_pts,
      71             :                                     const unsigned int power,
      72             :                                     const Real background_value,
      73           0 :                                     const Real  background_eff_dist):
      74           0 :   _comm(comm),
      75           0 :   _num_nearest_pts(num_nearest_pts),
      76           0 :   _power(power),
      77           0 :   _background_value(background_value),
      78           0 :   _background_eff_dist(background_eff_dist),
      79           0 :   _auto_area_mfi(std::make_unique<InverseDistanceInterpolation<3>>(_comm, _num_nearest_pts, _power, _background_value, _background_eff_dist))
      80             : {
      81           0 :   this->_initialized = false;
      82           0 :   this->_is_time_dependent = false;
      83           0 : }
      84             : 
      85             : // Destructor
      86           0 : AutoAreaFunction::~AutoAreaFunction () = default;
      87             : 
      88           0 : void AutoAreaFunction::init_mfi (const std::vector<Point> & input_pts,
      89             :                                  const std::vector<Real> & input_vals)
      90             : {
      91           0 :   std::vector<std::string> field_vars{"f"};
      92           0 :   _auto_area_mfi->set_field_variables(field_vars);
      93           0 :   _auto_area_mfi->get_source_points() = input_pts;
      94             : #ifdef LIBMESH_USE_COMPLEX_NUMBERS
      95             :   std::vector<Number> input_complex_vals;
      96           0 :   for (const auto & input_val : input_vals)
      97           0 :     input_complex_vals.push_back(Complex (input_val, 0.0));
      98           0 :   _auto_area_mfi->get_source_vals() = input_complex_vals;
      99             : #else
     100           0 :   _auto_area_mfi->get_source_vals() = input_vals;
     101             : #endif
     102           0 :   _auto_area_mfi->prepare_for_use();
     103           0 :   this->_initialized = true;
     104           0 : }
     105             : 
     106           0 : Real AutoAreaFunction::operator() (const Point & p,
     107             :                                    const Real /*time*/)
     108             : {
     109           0 :   libmesh_assert(this->_initialized);
     110             : 
     111           0 :   std::vector<Point> target_pts;
     112           0 :   std::vector<Number> target_vals;
     113             : 
     114           0 :   target_pts.push_back(p);
     115           0 :   target_vals.resize(1);
     116             : 
     117           0 :   _auto_area_mfi->interpolate_field_data(_auto_area_mfi->field_variables(), target_pts, target_vals);
     118             : 
     119           0 :   return libmesh_real(target_vals.front());
     120             : }
     121             : 
     122             : //
     123             : // Function definitions for the TriangulatorInterface class
     124             : //
     125             : 
     126             : // Constructor
     127        1977 : TriangulatorInterface::TriangulatorInterface(UnstructuredMesh & mesh)
     128        1809 :   : _mesh(mesh),
     129        1809 :     _holes(nullptr),
     130        1809 :     _markers(nullptr),
     131        1809 :     _regions(nullptr),
     132        1809 :     _elem_type(TRI3),
     133        1809 :     _desired_area(0.1),
     134        1809 :     _minimum_angle(20.0),
     135        1809 :     _triangulation_type(GENERATE_CONVEX_HULL),
     136        1809 :     _insert_extra_points(false),
     137        1809 :     _smooth_after_generating(true),
     138        1809 :     _quiet(true),
     139        2145 :     _auto_area_function(nullptr)
     140        1977 : {}
     141             : 
     142             : 
     143         221 : void TriangulatorInterface::set_interpolate_boundary_points (int n_points)
     144             : {
     145             :   // Maybe we'll reserve a meaning for negatives later?
     146          10 :   libmesh_assert(n_points >= 0);
     147             : 
     148         221 :   _interpolate_boundary_points = n_points;
     149             : 
     150             :   // backwards compatibility - someone (including us) might want to
     151             :   // query this via the old API.
     152         221 :   _insert_extra_points = n_points;
     153         221 : }
     154             : 
     155             : 
     156             : 
     157        2137 : int TriangulatorInterface::get_interpolate_boundary_points () const
     158             : {
     159             :   // backwards compatibility - someone might have turned this off via
     160             :   // the old API
     161        2137 :   if (!_insert_extra_points)
     162         288 :     return 0;
     163             : 
     164         221 :   return _interpolate_boundary_points;
     165             : }
     166             : 
     167             : 
     168             : 
     169        2350 : void TriangulatorInterface::elems_to_segments()
     170             : {
     171             :   // Don't try to override manually specified segments
     172        2350 :   if (!this->segments.empty())
     173           4 :     return;
     174             : 
     175             :   // If we have edges, they should form the polyline with the ordering
     176             :   // we want.  Let's turn them into segments for later use, because
     177             :   // we're going to delete the original elements to replace with our
     178             :   // triangulation.
     179        2275 :   if (_mesh.n_elem())
     180             :     {
     181             :       // Mapping from points to node ids, to back those out from
     182             :       // MeshedHole results later
     183          48 :       std::map<Point, dof_id_type> point_id_map;
     184             : 
     185        6119 :       for (Node * node : _mesh.node_ptr_range())
     186             :         {
     187             :           // We're not going to support overlapping nodes on the boundary
     188        4841 :           libmesh_error_msg_if
     189             :             (point_id_map.count(*node),
     190             :              "TriangulatorInterface does not support overlapping nodes found at "
     191             :              << static_cast<Point&>(*node));
     192             : 
     193        4841 :           point_id_map.emplace(*node, node->id());
     194         603 :         }
     195             : 
     196             :       // We don't support directly generating Tri6, so for
     197             :       // compatibility with future stitching we need to be working
     198             :       // with first-order elements.  Let's get rid of any non-vertex
     199             :       // nodes we just added.
     200        7556 :       for (Elem * elem : _mesh.element_ptr_range())
     201        4486 :         for (auto n : make_range(elem->n_vertices(), elem->n_nodes()))
     202         687 :           point_id_map.erase(elem->point(n));
     203             : 
     204             :       // We'll steal the ordering calculation from
     205             :       // the MeshedHole code
     206        1320 :       const TriangulatorInterface::MeshedHole mh { _mesh, this->_bdy_ids };
     207             : 
     208             :       // If we've specified only a subset of the mesh as our outer
     209             :       // boundary, then we may have nodes that don't actually fall
     210             :       // inside that boundary.  Triangulator code doesn't like Steiner
     211             :       // points that aren't inside the triangulation domain, so we
     212             :       // need to get rid of them.
     213             :       //
     214             :       // Also, if we're using Edge3 elements to define our outer
     215             :       // boundary, we're only dealing with their 2 end nodes and we'll
     216             :       // need to get rid of their central nodes.
     217          36 :       std::unordered_set<Node *> nodes_to_delete;
     218             : 
     219        5618 :       for (Elem * elem : _mesh.element_ptr_range())
     220        3705 :         for (auto n : make_range(elem->n_vertices(), elem->n_nodes()))
     221        1667 :           nodes_to_delete.insert(elem->node_ptr(n));
     222             : 
     223         438 :       if (!this->_bdy_ids.empty())
     224             :         {
     225        4048 :           for (auto & node : _mesh.node_ptr_range())
     226        1953 :             if (!mh.contains(*node))
     227         204 :               nodes_to_delete.insert(node);
     228             :         }
     229             : 
     230             :       // And now we're done with elements.  Delete them lest they have
     231             :       // dangling pointers to nodes we'll be deleting.
     232         438 :       _mesh.clear_elems();
     233             : 
     234             :       // Make segments from boundary nodes; also make sure we don't
     235             :       // delete them.
     236         438 :       const std::size_t np = mh.n_points();
     237        2190 :       for (auto i : make_range(np))
     238             :         {
     239        1752 :           const Point pt = mh.point(i);
     240        1752 :           const dof_id_type id0 = libmesh_map_find(point_id_map, pt);
     241        1752 :           nodes_to_delete.erase(_mesh.node_ptr(id0));
     242        1752 :           const Point next_pt = mh.point((np+i+1)%np);
     243        1752 :           const dof_id_type id1 = libmesh_map_find(point_id_map, next_pt);
     244        1752 :           this->segments.emplace_back(id0, id1);
     245        2620 :           for (auto m : make_range(mh.n_midpoints()))
     246             :           {
     247         868 :             this->segment_midpoints.emplace_back(mh.midpoint(m, i));
     248         868 :             this->segment_midpoints_keys.emplace_back(pt);
     249             :           }
     250             :         }
     251             : 
     252        2391 :       for (Node * node : nodes_to_delete)
     253        1953 :         _mesh.delete_node(node);
     254             : 
     255         438 :       if (this->_verify_hole_boundaries && _holes)
     256           0 :         this->verify_holes(mh);
     257         402 :     }
     258             : }
     259             : 
     260             : 
     261             : 
     262        2137 : void TriangulatorInterface::nodes_to_segments(dof_id_type max_node_id)
     263             : {
     264             :   // Don't try to override manually specified segments, or try to add
     265             :   // segments if we're doing a convex hull
     266        2137 :   if (!this->segments.empty() || _triangulation_type != PSLG)
     267         252 :     return;
     268             : 
     269        1210 :   for (auto node_it = _mesh.nodes_begin(),
     270        1210 :        node_end = _mesh.nodes_end();
     271        5828 :        node_it != node_end;)
     272             :     {
     273        4664 :       Node * node = *node_it;
     274             : 
     275             :       // If we're out of boundary nodes, the rest are going to be
     276             :       // Steiner points or hole points
     277        4664 :       if (node->id() >= max_node_id)
     278           0 :         break;
     279             : 
     280        4476 :       ++node_it;
     281             : 
     282        9140 :       Node * next_node = (node_it == node_end) ?
     283        1306 :         *_mesh.nodes_begin() : *node_it;
     284             : 
     285        4664 :       this->segments.emplace_back(node->id(), next_node->id());
     286             :     }
     287             : 
     288        1164 :   if (this->_verify_hole_boundaries && _holes)
     289             :     {
     290          48 :       std::vector<Point> outer_pts;
     291        3262 :       for (auto segment : this->segments)
     292        2610 :         outer_pts.push_back(_mesh.point(segment.first));
     293             : 
     294         701 :       ArbitraryHole ah(outer_pts);
     295         652 :       this->verify_holes(ah);
     296         603 :     }
     297             : }
     298             : 
     299             : 
     300             : 
     301        2137 : void TriangulatorInterface::insert_any_extra_boundary_points()
     302             : {
     303             :   // If the initial PSLG is really simple, e.g. an L-shaped domain or
     304             :   // a square/rectangle, the resulting triangulation may be very
     305             :   // "structured" looking.  Sometimes this is a problem if your
     306             :   // intention is to work with an "unstructured" looking grid.  We can
     307             :   // attempt to work around this limitation by inserting midpoints
     308             :   // into the original PSLG.  Inserting additional points into a
     309             :   // set of points meant to be a convex hull usually makes less sense.
     310             : 
     311        2137 :   const int n_interpolated = this->get_interpolate_boundary_points();
     312        2137 :   if ((_triangulation_type==PSLG) && n_interpolated)
     313             :     {
     314             :       // If we were lucky enough to start with contiguous node ids,
     315             :       // let's keep them that way.
     316         221 :       dof_id_type nn = _mesh.max_node_id();
     317             : 
     318             :       std::vector<std::pair<unsigned int, unsigned int>> old_segments =
     319         231 :         std::move(this->segments);
     320             : 
     321             :       // We expect to have converted any elems and/or nodes into
     322             :       // segments by now.
     323          10 :       libmesh_assert(!old_segments.empty());
     324             : 
     325          10 :       this->segments.clear();
     326             : 
     327             :       // Insert a new point on each segment at evenly spaced locations
     328             :       // between existing boundary points.
     329             :       // np=index into new points vector
     330             :       // n =index into original points vector
     331        1105 :       for (auto old_segment : old_segments)
     332             :         {
     333         884 :           Node * begin_node = _mesh.node_ptr(old_segment.first);
     334         884 :           Node * end_node = _mesh.node_ptr(old_segment.second);
     335         884 :           dof_id_type current_id = begin_node->id();
     336        2920 :           for (auto i : make_range(n_interpolated))
     337             :             {
     338             :               // new points are equispaced along the original segments
     339             :               const Point new_point =
     340        2036 :                 ((n_interpolated-i) * *(Point *)(begin_node) +
     341        2036 :                  (i+1) * *(Point *)(end_node)) /
     342        2116 :                 (n_interpolated + 1);
     343        2036 :               Node * next_node = _mesh.add_point(new_point, nn++);
     344        1876 :               this->segments.emplace_back(current_id,
     345        2036 :                                           next_node->id());
     346        2036 :               current_id = next_node->id();
     347             :             }
     348         804 :           this->segments.emplace_back(current_id,
     349         884 :                                       end_node->id());
     350             :         }
     351             :     }
     352        2137 : }
     353             : 
     354             : 
     355        1641 : void TriangulatorInterface::increase_triangle_order()
     356             : {
     357        1641 :   switch (_elem_type)
     358             :     {
     359          42 :     case TRI3:
     360             :     // Nothing to do if we're not requested to increase order
     361        1491 :       return;
     362         150 :     case TRI6:
     363         150 :       _mesh.all_second_order();
     364           8 :       break;
     365           0 :     case TRI7:
     366           0 :       _mesh.all_complete_order();
     367           0 :       break;
     368           0 :     default:
     369           0 :       libmesh_not_implemented();
     370             :     }
     371             : 
     372             :   // If we have any midpoint location data, we'll want to look it up
     373             :   // by point.  all_midpoints[{p, m}] will be the mth midpoint
     374             :   // location following after point p (when traversing a triangle
     375             :   // counter-clockwise)
     376           8 :   std::map<std::pair<Point, unsigned int>, Point> all_midpoints;
     377             :   unsigned int n_midpoints =
     378         166 :     this->segment_midpoints.size() / this->segments.size();
     379           8 :   libmesh_assert_equal_to(this->segments.size() * n_midpoints,
     380             :                           this->segment_midpoints.size());
     381         225 :   for (auto m : make_range(n_midpoints))
     382         375 :     for (auto i : make_range(this->segments.size()))
     383             :       {
     384         300 :         const Point & p = segment_midpoints_keys[i*n_midpoints+m];
     385         300 :         all_midpoints[{p,m}] =
     386          32 :           this->segment_midpoints[i*n_midpoints+m];
     387             :       }
     388             : 
     389         150 :   if (_holes)
     390         150 :     for (const Hole * hole : *_holes)
     391             :       {
     392          75 :         if (!hole->n_midpoints())
     393           0 :           continue;
     394          75 :         if (!n_midpoints)
     395          75 :           n_midpoints = hole->n_midpoints();
     396           0 :         else if (hole->n_midpoints() != n_midpoints)
     397           0 :           libmesh_not_implemented_msg
     398             :             ("Differing boundary midpoint counts " <<
     399             :              hole->n_midpoints() << " and " << n_midpoints);
     400             : 
     401             :         // Our inner holes are expected to have points in
     402             :         // counter-clockwise order, which is backwards from how we
     403             :         // want to traverse them when iterating in counter-clockwise
     404             :         // order over a triangle, so we'll need to reverse our maps
     405             :         // carefully here.
     406          75 :         const auto n_hole_points = hole->n_points();
     407           4 :         libmesh_assert(n_hole_points);
     408         150 :         for (auto m : make_range(n_midpoints))
     409             :           {
     410         600 :             for (auto i : make_range(n_hole_points-1))
     411             :               {
     412         525 :                 const Point & p = hole->point(i+1);
     413         525 :                 all_midpoints[{p,m}] = hole->midpoint(n_midpoints-m-1, i);
     414             :               }
     415          75 :             const Point & p = hole->point(0);
     416          75 :             all_midpoints[{p,m}] = hole->midpoint(n_midpoints-m-1, n_hole_points-1);
     417             :           }
     418             :       }
     419             : 
     420             :   // The n_midpoints > 1 case is for future proofing, but in the
     421             :   // present we have EDGE4 and no TRI10 yet.
     422         150 :   if (n_midpoints > 1)
     423           0 :     libmesh_not_implemented_msg
     424             :       ("Cannot construct triangles with more than 1 midpoint per edge");
     425             : 
     426         150 :   if (!n_midpoints)
     427           0 :     return;
     428             : 
     429        2336 :   for (Elem * elem : _mesh.element_ptr_range())
     430             :     {
     431             :       // This should only be called right after we've finished
     432             :       // converting a triangulation to higher order
     433          56 :       libmesh_assert_equal_to(elem->n_vertices(), 3);
     434          56 :       libmesh_assert_not_equal_to(elem->default_order(), FIRST);
     435             : 
     436        4200 :       for (auto n : make_range(3))
     437             :         {
     438             :           // Only hole/outer boundary segments need adjusted midpoints
     439        3318 :           if (elem->neighbor_ptr(n))
     440        1846 :             continue;
     441             : 
     442         128 :           const Point & p = elem->point(n);
     443             : 
     444        1200 :           if (const auto it = all_midpoints.find({p,0});
     445          64 :               it != all_midpoints.end())
     446         948 :             elem->point(n+3) = it->second;
     447             :         }
     448         134 :     }
     449             : }
     450             : 
     451             : 
     452         652 : void TriangulatorInterface::verify_holes(const Hole & outer_bdy)
     453             : {
     454        1874 :   for (const Hole * hole : *_holes)
     455             :     {
     456        7562 :       for (const Hole * hole2 : *_holes)
     457             :         {
     458        6340 :           if (hole == hole2)
     459        1179 :             continue;
     460             : 
     461       25872 :           for (auto i : make_range(hole2->n_points()))
     462       20754 :             if (hole->contains(hole2->point(i)))
     463           0 :               libmesh_error_msg
     464             :                 ("Found point " << hole2->point(i) <<
     465             :                  " on one hole boundary and another's interior");
     466             :         }
     467             : 
     468        6851 :       for (auto i : make_range(hole->n_points()))
     469        5629 :         if (!outer_bdy.contains(hole->point(i)))
     470           0 :           libmesh_error_msg
     471             :             ("Found point " << hole->point(i) <<
     472             :              " on hole boundary but outside outer boundary");
     473             :     }
     474         652 : }
     475             : 
     476             : 
     477         504 : unsigned int TriangulatorInterface::total_hole_points()
     478             : {
     479             :   // If the holes vector is non-nullptr (and non-empty) we need to determine
     480             :   // the number of additional points which the holes will add to the
     481             :   // triangulation.
     482             :   // Note that the number of points is always equal to the number of segments
     483             :   // that form the holes.
     484         252 :   unsigned int n_hole_points = 0;
     485             : 
     486         504 :   if (_holes)
     487          40 :     for (const auto & hole : *_holes)
     488             :     {
     489          24 :       n_hole_points += hole->n_points();
     490             :       // A hole at least has one enclosure.
     491             :       // Points on enclosures are ordered so that we can add segments implicitly.
     492             :       // Elements in segment_indices() indicates the starting points of all enclosures.
     493             :       // The last element in segment_indices() is the number of total points.
     494          12 :       libmesh_assert_greater(hole->segment_indices().size(), 1);
     495          12 :       libmesh_assert_equal_to(hole->segment_indices().back(), hole->n_points());
     496             :     }
     497             : 
     498         504 :   return n_hole_points;
     499             : }
     500             : 
     501           0 : void TriangulatorInterface::set_auto_area_function(const Parallel::Communicator &comm,
     502             :                                                    const unsigned int num_nearest_pts,
     503             :                                                    const unsigned int power,
     504             :                                                    const Real background_value,
     505             :                                                    const Real  background_eff_dist)
     506             : {
     507           0 :    _auto_area_function = std::make_unique<AutoAreaFunction>(comm, num_nearest_pts, power, background_value, background_eff_dist);
     508           0 : }
     509             : 
     510           0 : FunctionBase<Real> * TriangulatorInterface::get_auto_area_function()
     511             : {
     512           0 :   if (!_auto_area_function->initialized())
     513             :   {
     514             :     // Points and target element sizes for the interpolation
     515           0 :     std::vector<Point> function_points;
     516           0 :     std::vector<Real> function_sizes;
     517           0 :     calculate_auto_desired_area_samples(function_points, function_sizes);
     518           0 :     _auto_area_function->init_mfi(function_points, function_sizes);
     519             :   }
     520           0 :   return _auto_area_function.get();
     521             : }
     522             : 
     523           0 : void TriangulatorInterface::calculate_auto_desired_area_samples(std::vector<Point> & function_points,
     524             :                                                                 std::vector<Real> & function_sizes,
     525             :                                                                 const Real & area_factor)
     526             : {
     527             :   // Get the hole mesh of the outer boundary
     528             :   // Holes should already be attached if applicable when this function is called
     529           0 :   const TriangulatorInterface::MeshedHole bdry_mh { _mesh, this->_bdy_ids };
     530             :   // Collect all the centroid points of the outer boundary segments
     531             :   // and the corresponding element sizes
     532           0 :   for (unsigned int i = 0; i < bdry_mh.n_points(); i++)
     533             :   {
     534           0 :     function_points.push_back((bdry_mh.point(i) + bdry_mh.point((i + 1) % bdry_mh.n_points())) /
     535           0 :                               Real(2.0));
     536           0 :     function_sizes.push_back(
     537           0 :         (bdry_mh.point(i) - bdry_mh.point((i + 1) % bdry_mh.n_points())).norm());
     538             :   }
     539             :   // If holes are present, do the same for the hole boundaries
     540           0 :   if(_holes)
     541           0 :     for (const Hole * hole : *_holes)
     542             :     {
     543           0 :       for (unsigned int i = 0; i < hole->n_points(); i++)
     544             :       {
     545           0 :         function_points.push_back(
     546           0 :             (hole->point(i) + hole->point((i + 1) % hole->n_points())) / Real(2.0));
     547           0 :         function_sizes.push_back(
     548           0 :             (hole->point(i) - hole->point((i + 1) % hole->n_points())).norm());
     549             :       }
     550             :     }
     551             : 
     552           0 :   std::for_each(
     553           0 :       function_sizes.begin(), function_sizes.end(), [&area_factor](Real & a) { a = a * a * area_factor * std::sqrt(3.0) / 4.0; });
     554             : 
     555           0 : }
     556             : } // namespace libMesh
     557             : 

Generated by: LCOV version 1.14