LCOV - code coverage report
Current view: top level - src/mesh - inf_elem_builder.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4475 (55045b) with base a68cc6 Lines: 152 275 55.3 %
Date: 2026-06-03 14:29:06 Functions: 3 3 100.0 %
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             : #include "libmesh/libmesh_config.h"
      19             : 
      20             : #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
      21             : 
      22             : // C++ includes
      23             : 
      24             : // Local includes
      25             : #include "libmesh/inf_elem_builder.h"
      26             : #include "libmesh/libmesh_logging.h"
      27             : #include "libmesh/mesh_tools.h"
      28             : #include "libmesh/face_inf_quad4.h"
      29             : #include "libmesh/face_inf_quad6.h"
      30             : #include "libmesh/cell_inf_prism6.h"
      31             : #include "libmesh/cell_inf_prism12.h"
      32             : #include "libmesh/cell_inf_hex8.h"
      33             : #include "libmesh/cell_inf_hex16.h"
      34             : #include "libmesh/cell_inf_hex18.h"
      35             : #include "libmesh/mesh_base.h"
      36             : #include "libmesh/remote_elem.h"
      37             : 
      38             : #include "timpi/parallel_implementation.h"
      39             : 
      40             : namespace libMesh
      41             : {
      42             : 
      43          76 : const Point InfElemBuilder::build_inf_elem(bool be_verbose)
      44             : {
      45             :   // determine origin automatically,
      46             :   // works only if the mesh has no symmetry planes.
      47          76 :   const BoundingBox b_box = MeshTools::create_bounding_box(_mesh);
      48          30 :   Point origin = (b_box.first + b_box.second) / 2;
      49             : 
      50          30 :   if (be_verbose && _mesh.processor_id() == 0)
      51             :     {
      52             : #ifdef DEBUG
      53           2 :       libMesh::out << " Determined origin for Infinite Elements:"
      54           2 :                    << std::endl
      55           2 :                    << "  ";
      56           2 :       origin.write_unformatted(libMesh::out);
      57           2 :       libMesh::out << std::endl;
      58             : #endif
      59             :     }
      60             : 
      61             :   // Call the protected implementation function with the
      62             :   // automatically determined origin.
      63          76 :   this->build_inf_elem(origin, false, false, false, be_verbose);
      64             : 
      65             :   // when finished with building the Ifems,
      66             :   // it remains to prepare the mesh for use:
      67             :   // find neighbors (again), partition (if needed)...
      68          76 :   this->_mesh.prepare_for_use ();
      69             : 
      70         106 :   return origin;
      71             : }
      72             : 
      73             : 
      74             : 
      75             : 
      76             : 
      77             : 
      78             : 
      79             : 
      80             : 
      81             : 
      82             : 
      83             : 
      84          10 : const Point InfElemBuilder::build_inf_elem (const InfElemOriginValue & origin_x,
      85             :                                             const InfElemOriginValue & origin_y,
      86             :                                             const InfElemOriginValue & origin_z,
      87             :                                             const bool x_sym,
      88             :                                             const bool y_sym,
      89             :                                             const bool z_sym,
      90             :                                             const bool be_verbose,
      91             :                                             std::vector<const Node *> * inner_boundary_nodes)
      92             : {
      93           8 :   LOG_SCOPE("build_inf_elem()", "InfElemBuilder");
      94             : 
      95             :   // first determine the origin of the
      96             :   // infinite elements.  For this, the
      97             :   // origin defaults to the given values,
      98             :   // and may be overridden when the user
      99             :   // provided values
     100          10 :   Point origin(origin_x.second, origin_y.second, origin_z.second);
     101             : 
     102             :   // when only _one_ of the origin coordinates is _not_
     103             :   // given, we have to determine it on our own
     104          10 :   if ( !origin_x.first || !origin_y.first || !origin_z.first)
     105             :     {
     106             :       // determine origin
     107           0 :       const BoundingBox b_box = MeshTools::create_bounding_box(_mesh);
     108           0 :       const Point auto_origin = (b_box.first+b_box.second)/2;
     109             : 
     110             :       // override default values, if necessary
     111           0 :       if (!origin_x.first)
     112           0 :         origin(0) = auto_origin(0);
     113             : #if LIBMESH_DIM > 1
     114           0 :       if (!origin_y.first)
     115           0 :         origin(1) = auto_origin(1);
     116             : #endif
     117             : #if LIBMESH_DIM > 2
     118           0 :       if (!origin_z.first)
     119           0 :         origin(2) = auto_origin(2);
     120             : #endif
     121             : 
     122           0 :       if (be_verbose)
     123             :         {
     124           0 :           libMesh::out << " Origin for Infinite Elements:" << std::endl;
     125             : 
     126           0 :           if (!origin_x.first)
     127           0 :             libMesh::out << "  determined x-coordinate" << std::endl;
     128           0 :           if (!origin_y.first)
     129           0 :             libMesh::out << "  determined y-coordinate" << std::endl;
     130           0 :           if (!origin_z.first)
     131           0 :             libMesh::out << "  determined z-coordinate" << std::endl;
     132             : 
     133           0 :           libMesh::out << "  coordinates: ";
     134           0 :           origin.write_unformatted(libMesh::out);
     135           0 :           libMesh::out << std::endl;
     136           0 :         }
     137           0 :     }
     138             : 
     139          10 :   else if (be_verbose)
     140             : 
     141             :     {
     142           4 :       libMesh::out << " Origin for Infinite Elements:" << std::endl;
     143           4 :       libMesh::out << "  coordinates: ";
     144          10 :       origin.write_unformatted(libMesh::out);
     145           4 :       libMesh::out << std::endl;
     146             :     }
     147             : 
     148             : 
     149             : 
     150             :   // Now that we have the origin, check if the user provided an \p
     151             :   // inner_boundary_nodes.  If so, we pass a std::set to the actual
     152             :   // implementation of the build_inf_elem(), so that we can convert
     153             :   // this to the Node * vector
     154          10 :   if (inner_boundary_nodes != nullptr)
     155             :     {
     156             :       // note that the std::set that we will get
     157             :       // from build_inf_elem() uses the index of
     158             :       // the element in this->_elements vector,
     159             :       // and the second entry is the side index
     160             :       // for this element.  Therefore, we do _not_
     161             :       // need to renumber nodes and elements
     162             :       // prior to building the infinite elements.
     163             :       //
     164             :       // However, note that this method here uses
     165             :       // node id's... Do we need to renumber?
     166             : 
     167             : 
     168             :       // Form the list of faces of elements which finally
     169             :       // will tell us which nodes should receive boundary
     170             :       // conditions (to form the std::vector<const Node *>)
     171             :       std::set<std::pair<dof_id_type,
     172           0 :                          unsigned int>> inner_faces;
     173             : 
     174             : 
     175             :       // build infinite elements
     176           0 :       this->build_inf_elem(origin,
     177             :                            x_sym, y_sym, z_sym,
     178             :                            be_verbose,
     179             :                            &inner_faces);
     180             : 
     181           0 :       if (be_verbose)
     182             :         {
     183           0 :           this->_mesh.print_info();
     184           0 :           libMesh::out << "Data pre-processing:" << std::endl
     185           0 :                        << " convert the <int,int> list to a Node * list..."
     186           0 :                        << std::endl;
     187             :         }
     188             : 
     189             :       // First use a std::vector<dof_id_type> that holds
     190             :       // the global node numbers.  Then sort this vector,
     191             :       // so that it can be made unique (no multiple occurrence
     192             :       // of a node), and then finally insert the Node * in
     193             :       // the vector inner_boundary_nodes.
     194             :       //
     195             :       // Reserve memory for the vector<> with
     196             :       // 4 times the size of the number of elements in the
     197             :       // std::set. This is a good bet for Quad4 face elements.
     198             :       // For higher-order elements, this probably _has_ to lead
     199             :       // to additional allocations...
     200             :       // Practice has to show how this affects performance.
     201           0 :       std::vector<dof_id_type> inner_boundary_node_numbers;
     202           0 :       inner_boundary_node_numbers.reserve(4*inner_faces.size());
     203             : 
     204             :       // Now transform the set of pairs to a list of (possibly
     205             :       // duplicate) global node numbers.
     206           0 :       for (const auto & p : inner_faces)
     207             :         {
     208             :           // build a full-ordered side element to get _all_ the base nodes
     209           0 :           std::unique_ptr<Elem> side(this->_mesh.elem_ref(p.first).build_side_ptr(p.second));
     210             : 
     211             :           // insert all the node numbers in inner_boundary_node_numbers
     212           0 :           for (const Node & node : side->node_ref_range())
     213           0 :             inner_boundary_node_numbers.push_back(node.id());
     214           0 :         }
     215             : 
     216             : 
     217             :       // inner_boundary_node_numbers now still holds multiple entries of
     218             :       // node numbers.  So first sort, then unique the vector.
     219             :       // Note that \p std::unique only puts the new ones in
     220             :       // front, while to leftovers are not deleted.  Instead,
     221             :       // it returns a pointer to the end of the unique range.
     222             :       //TODO:[BSK] int_ibn_size_before is not the same type as unique_size!
     223             : #ifndef NDEBUG
     224           0 :       const std::size_t ibn_size_before = inner_boundary_node_numbers.size();
     225             : #endif
     226           0 :       std::sort (inner_boundary_node_numbers.begin(), inner_boundary_node_numbers.end());
     227             :       auto unique_end =
     228           0 :         std::unique (inner_boundary_node_numbers.begin(), inner_boundary_node_numbers.end());
     229             : 
     230           0 :       std::size_t unique_size = std::distance(inner_boundary_node_numbers.begin(), unique_end);
     231           0 :       libmesh_assert_less_equal (unique_size, ibn_size_before);
     232             : 
     233             :       // Finally, create const Node * in the inner_boundary_nodes
     234             :       // vector.  Reserve, not resize (otherwise, the push_back
     235             :       // would append the interesting nodes, while nullptr-nodes
     236             :       // live in the resize'd area...
     237           0 :       inner_boundary_nodes->reserve (unique_size);
     238           0 :       inner_boundary_nodes->clear();
     239             : 
     240           0 :       for (const auto & dof : as_range(inner_boundary_node_numbers.begin(), unique_end))
     241             :         {
     242           0 :           const Node * node = this->_mesh.node_ptr(dof);
     243           0 :           inner_boundary_nodes->push_back(node);
     244             :         }
     245             : 
     246           0 :       if (be_verbose)
     247           0 :         libMesh::out << "  finished identifying " << unique_size
     248           0 :                      << " target nodes." << std::endl;
     249             :     }
     250             : 
     251             :   else
     252             : 
     253             :     {
     254             :       // There are no inner boundary nodes, so simply build the infinite elements
     255          10 :       this->build_inf_elem(origin, x_sym, y_sym, z_sym, be_verbose);
     256             :     }
     257             : 
     258             :   // when finished with building the Ifems,
     259             :   // it remains to prepare the mesh for use:
     260             :   // find neighbors again, partition (if needed)...
     261          10 :   this->_mesh.prepare_for_use ();
     262             : 
     263          14 :   return origin;
     264             : }
     265             : 
     266             : 
     267             : 
     268             : 
     269             : 
     270             : 
     271             : 
     272             : 
     273             : 
     274             : // The actual implementation of building elements.
     275          86 : void InfElemBuilder::build_inf_elem(const Point & origin,
     276             :                                     const bool x_sym,
     277             :                                     const bool y_sym,
     278             :                                     const bool z_sym,
     279             :                                     const bool be_verbose,
     280             :                                     std::set<std::pair<dof_id_type,
     281             :                                     unsigned int>> * inner_faces)
     282             : {
     283          86 :   if (be_verbose)
     284             :     {
     285             : #ifdef DEBUG
     286           8 :       libMesh::out << " Building Infinite Elements:" << std::endl;
     287           8 :       libMesh::out << "  updating element neighbor tables..." << std::endl;
     288             : #else
     289             :       libMesh::out << " Verbose mode disabled in non-debug mode." << std::endl;
     290             : #endif
     291             :     }
     292             : 
     293             : 
     294             :   // update element neighbors
     295          86 :   this->_mesh.find_neighbors();
     296             : 
     297          68 :   LOG_SCOPE("build_inf_elem()", "InfElemBuilder");
     298             : 
     299             :   // A set for storing element number, side number pairs.
     300             :   // pair.first == element number, pair.second == side number
     301          68 :   std::set<std::pair<dof_id_type,unsigned int>> faces;
     302          68 :   std::set<std::pair<dof_id_type,unsigned int>> ofaces;
     303             : 
     304             :   // A set for storing node numbers on the outer faces.
     305          68 :   std::set<dof_id_type> onodes;
     306             : 
     307             :   // The distance to the farthest point in the mesh from the origin
     308          86 :   Real max_r=0.;
     309             : 
     310             :   // The index of the farthest point in the mesh from the origin
     311          86 :   int max_r_node = -1;
     312             : 
     313             : #ifdef DEBUG
     314          34 :   if (be_verbose)
     315             :     {
     316           8 :       libMesh::out << "  collecting boundary sides";
     317           8 :       if (x_sym || y_sym || z_sym)
     318           0 :         libMesh::out << ", skipping sides in symmetry planes..." << std::endl;
     319             :       else
     320           8 :         libMesh::out << "..." << std::endl;
     321             :     }
     322             : #endif
     323             : 
     324             :   // Iterate through all elements and sides, collect indices of all active
     325             :   // boundary sides in the faces set. Skip sides which lie in symmetry planes.
     326             :   // Later, sides of the inner boundary will be sorted out.
     327        8600 :   for (const auto & elem : _mesh.active_element_ptr_range())
     328       44147 :     for (auto s : elem->side_index_range())
     329       47984 :       if (elem->neighbor_ptr(s) == nullptr)
     330             :         {
     331             :           // note that it is safe to use the Elem::side() method,
     332             :           // which gives a non-full-ordered element
     333        4934 :           std::unique_ptr<Elem> side(elem->build_side_ptr(s));
     334             : 
     335             :           // bool flags for symmetry detection
     336        1408 :           bool sym_side=false;
     337        1408 :           bool on_x_sym=true;
     338        1408 :           bool on_y_sym=true;
     339        1408 :           bool on_z_sym=true;
     340             : 
     341             : 
     342             :           // Loop over the nodes to check whether they are on the symmetry planes,
     343             :           // and therefore sufficient to use a non-full-ordered side element
     344       25570 :           for (const Node & node : side->node_ref_range())
     345             :             {
     346       17616 :               const dof_id_type node_id = node.id();
     347             :               const Point dist_from_origin =
     348       22044 :                 this->_mesh.point(node_id) - origin;
     349             : 
     350       22044 :               if (x_sym)
     351           0 :                 if (std::abs(dist_from_origin(0)) > 1.e-3)
     352           0 :                   on_x_sym=false;
     353             : 
     354       22044 :               if (y_sym)
     355           0 :                 if (std::abs(dist_from_origin(1)) > 1.e-3)
     356           0 :                   on_y_sym=false;
     357             : 
     358       22044 :               if (z_sym)
     359           0 :                 if (std::abs(dist_from_origin(2)) > 1.e-3)
     360           0 :                   on_z_sym=false;
     361             : 
     362             :               //       if (x_sym)
     363             :               // if (std::abs(dist_from_origin(0)) > 1.e-6)
     364             :               //   on_x_sym=false;
     365             : 
     366             :               //       if (y_sym)
     367             :               // if (std::abs(dist_from_origin(1)) > 1.e-6)
     368             :               //   on_y_sym=false;
     369             : 
     370             :               //       if (z_sym)
     371             :               // if (std::abs(dist_from_origin(2)) > 1.e-6)
     372             :               //   on_z_sym=false;
     373             : 
     374             :               //find the node most distant from origin
     375             : 
     376       13236 :               Real r = dist_from_origin.norm();
     377       22044 :               if (r > max_r)
     378             :                 {
     379         157 :                   max_r = r;
     380         157 :                   max_r_node=node_id;
     381             :                 }
     382             : 
     383             :             }
     384             : 
     385        3526 :           sym_side = (x_sym && on_x_sym) || (y_sym && on_y_sym) || (z_sym && on_z_sym);
     386             : 
     387        1408 :           if (!sym_side)
     388        3526 :             faces.emplace(elem->id(), s);
     389             : 
     390         728 :         } // neighbor(s) == nullptr
     391             : 
     392             :   // On a distributed mesh it might be some other processor who sees
     393             :   // the farthest node.
     394          86 :   if (!this->_mesh.is_serial())
     395             :     {
     396             :       unsigned int rank;
     397           0 :       this->_mesh.comm().maxloc(max_r, rank);
     398           0 :       this->_mesh.comm().broadcast(max_r_node, rank);
     399             :     }
     400             : 
     401             :   //  If a boundary side has one node on the outer boundary,
     402             :   //  all points of this side are on the outer boundary.
     403             :   //  Start with the node most distant from origin, which has
     404             :   //  to be on the outer boundary, then recursively find all
     405             :   //  sides and nodes connected to it. Found sides are moved
     406             :   //  from faces to ofaces, nodes are collected in onodes.
     407             :   //  Here, the search is done iteratively, because, depending on
     408             :   //  the mesh, a very high level of recursion might be necessary.
     409          86 :   if (max_r_node >= 0)
     410             :     // include the possibility of the 1st element being most far away.
     411             :     // Only the case of no outer boundary is to be excluded.
     412          86 :     onodes.insert(max_r_node);
     413             : 
     414             :   // If we're not on a serial mesh, we'll need to synchronize that
     415             :   // onodes list too.
     416             :   bool did_parallel_update;
     417             : 
     418          86 :   do
     419             :   {
     420          86 :     did_parallel_update = false;
     421             : 
     422          34 :     auto face_it = faces.begin();
     423          34 :     auto face_end = faces.end();
     424          34 :     unsigned int facesfound=0;
     425        4712 :     while (face_it != face_end) {
     426        4626 :       std::pair<dof_id_type, unsigned int> p = *face_it;
     427             : 
     428             :       // This has to be a full-ordered side element,
     429             :       // since we need the correct n_nodes,
     430        6474 :       std::unique_ptr<Elem> side(this->_mesh.elem_ref(p.first).build_side_ptr(p.second));
     431             : 
     432        1848 :       bool found=false;
     433       13127 :       for (const Node & node : side->node_ref_range())
     434       12027 :         if (onodes.count(node.id()))
     435             :           {
     436        1408 :             found=true;
     437        1408 :             break;
     438             :           }
     439             : 
     440             :       // If a new oface is found, include its nodes in onodes
     441        4626 :       if (found)
     442             :         {
     443       25570 :           for (const Node & node : side->node_ref_range())
     444       22044 :             onodes.insert(node.id());
     445             : 
     446        2118 :           ofaces.insert(p);
     447        2118 :           face_it = faces.erase(face_it); // increment is done here
     448             : 
     449        3526 :           facesfound++;
     450             :         }
     451             : 
     452             :       else
     453         440 :         ++face_it; // increment is done here
     454             : 
     455             :       // If at least one new oface was found in this cycle,
     456             :       // do another search cycle.
     457        4626 :       if (facesfound>0 && face_it == faces.end())
     458             :         {
     459          44 :           facesfound = 0;
     460          44 :           face_it    = faces.begin();
     461             :         }
     462         930 :     }
     463             : 
     464          86 :     if (!this->_mesh.is_serial())
     465             :       {
     466           0 :         auto my_onodes_size = onodes.size();
     467           0 :         this->_mesh.comm().set_union(onodes);
     468           0 :         did_parallel_update = (onodes.size() > my_onodes_size);
     469           0 :         this->_mesh.comm().max(did_parallel_update);
     470             :       }
     471             :   }
     472             :   while (did_parallel_update);
     473             : 
     474             : 
     475             : #ifdef DEBUG
     476          34 :   if (be_verbose)
     477           8 :     libMesh::out << "  found "
     478           8 :                  << faces.size()
     479           8 :                  << " inner and "
     480          16 :                  << ofaces.size()
     481           8 :                  << " outer boundary faces"
     482           8 :                  << std::endl;
     483             : #endif
     484             : 
     485             :   // When the user provided a non-null pointer to
     486             :   // inner_faces, that implies he wants to have
     487             :   // this std::set.  For now, simply copy the data.
     488          86 :   if (inner_faces != nullptr)
     489           0 :     *inner_faces = faces;
     490             : 
     491             :   // free memory, clear our local variable, no need
     492             :   // for it any more.
     493          34 :   faces.clear();
     494             : 
     495             : 
     496             :   // outer_nodes maps onodes to their duplicates
     497          68 :   std::map<dof_id_type, Node *> outer_nodes;
     498             : 
     499             :   // We may need to pick our own object ids in parallel
     500          86 :   dof_id_type old_max_node_id = _mesh.max_node_id();
     501          86 :   dof_id_type old_max_elem_id = _mesh.max_elem_id();
     502             : 
     503             :   // Likewise with our unique_ids
     504             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
     505          86 :   unique_id_type old_max_unique_id = _mesh.parallel_max_unique_id();
     506             : #endif
     507             : 
     508             :   // for each boundary node, add an outer_node with
     509             :   // double distance from origin.
     510        8184 :   for (const auto & n : onodes)
     511             :     {
     512        8098 :       if (!this->_mesh.query_node_ptr(n))
     513             :         {
     514           0 :           libmesh_assert(!_mesh.is_serial());
     515           0 :           continue;
     516             :         }
     517             : 
     518        8098 :       Point p = (Point(this->_mesh.point(n)) * 2) - origin;
     519        8098 :       if (_mesh.is_serial())
     520             :         {
     521             :           // Add with a default id in serial
     522        8098 :           outer_nodes[n]=this->_mesh.add_point(p);
     523             :         }
     524             :       else
     525             :         {
     526             :           // Pick a unique id in parallel
     527           0 :           Node & bnode = _mesh.node_ref(n);
     528           0 :           dof_id_type new_id = bnode.id() + old_max_node_id;
     529           0 :           std::unique_ptr<Node> new_node = Node::build(p, new_id);
     530           0 :           new_node->processor_id() = bnode.processor_id();
     531             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
     532           0 :           new_node->set_unique_id(old_max_unique_id + bnode.id());
     533             : #endif
     534             : 
     535           0 :           outer_nodes[n] =
     536           0 :             this->_mesh.add_node(std::move(new_node));
     537           0 :         }
     538             :     }
     539             : 
     540             : 
     541             : #ifdef DEBUG
     542             :   // for verbose, remember n_elem
     543          34 :   dof_id_type n_conventional_elem = this->_mesh.n_elem();
     544             : #endif
     545             : 
     546             : 
     547             :   // build Elems based on boundary side type
     548        3612 :   for (auto & p : ofaces)
     549             :     {
     550        3526 :       Elem & belem = this->_mesh.elem_ref(p.first);
     551             : 
     552             :       // build a full-ordered side element to get the base nodes
     553        3526 :       std::unique_ptr<Elem> side(belem.build_side_ptr(p.second));
     554             : 
     555             :       // create cell depending on side type, assign nodes,
     556             :       // use braces to force scope.
     557        1408 :       bool is_higher_order_elem = false;
     558             : 
     559         710 :       std::unique_ptr<Elem> el;
     560        3526 :       switch(side->type())
     561             :         {
     562             :           // 3D infinite elements
     563             :           // TRIs
     564           0 :         case TRI3:
     565           0 :           el = Elem::build(INFPRISM6);
     566           0 :           break;
     567             : 
     568        1820 :         case TRI6:
     569        2184 :           el = Elem::build(INFPRISM12);
     570         728 :           is_higher_order_elem = true;
     571        1820 :           break;
     572             : 
     573             :           // QUADs
     574         846 :         case QUAD4:
     575        1020 :           el = Elem::build(INFHEX8);
     576         846 :           break;
     577             : 
     578           0 :         case QUAD8:
     579           0 :           el = Elem::build(INFHEX16);
     580           0 :           is_higher_order_elem = true;
     581           0 :           break;
     582             : 
     583         860 :         case QUAD9:
     584        1032 :           el = Elem::build(INFHEX18);
     585             : 
     586             :           // the method of assigning nodes (which follows below)
     587             :           // omits in the case of QUAD9 the bubble node; therefore
     588             :           // we assign these first by hand here.
     589        1548 :           el->set_node(16, side->node_ptr(8));
     590        1548 :           el->set_node(17, outer_nodes[side->node_id(8)]);
     591         344 :           is_higher_order_elem=true;
     592         860 :           break;
     593             : 
     594             :           // 2D infinite elements
     595           0 :         case EDGE2:
     596           0 :           el = Elem::build(INFQUAD4);
     597           0 :           break;
     598             : 
     599           0 :         case EDGE3:
     600           0 :           el = Elem::build(INFQUAD6);
     601           0 :           el->set_node(4, side->node_ptr(2));
     602           0 :           break;
     603             : 
     604             :           // 1D infinite elements not supported
     605           0 :         default:
     606           0 :           libMesh::out << "InfElemBuilder::build_inf_elem(Point, bool, bool, bool, bool): "
     607           0 :                        << "invalid face element "
     608           0 :                        << std::endl;
     609           0 :           continue;
     610             :         }
     611             : 
     612        3526 :       const unsigned int n_base_vertices = side->n_vertices();
     613             : 
     614             :       // On a distributed mesh, manually assign unique ids to the new
     615             :       // element, and make sure any RemoteElem neighbor links are set.
     616        3526 :       if (!_mesh.is_serial())
     617             :         {
     618           0 :           el->processor_id() = belem.processor_id();
     619             : 
     620             :           // We'd better not have elements with more than 6 sides
     621           0 :           const unsigned int max_sides = 6;
     622           0 :           libmesh_assert_less_equal(el->n_sides(), max_sides);
     623           0 :           el->set_id (belem.id() * max_sides + p.second + old_max_elem_id);
     624             : 
     625             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
     626           0 :           el->set_unique_id(old_max_unique_id + old_max_node_id +
     627           0 :                             belem.id() * max_sides + p.second);
     628             : #endif
     629             : 
     630             :           // If we have a remote neighbor on a boundary element side
     631           0 :           if (belem.dim() > 1)
     632           0 :             for (auto s : belem.side_index_range())
     633           0 :               if (belem.neighbor_ptr(s) == remote_elem)
     634             :                 {
     635             :                   // Find any corresponding infinite element side
     636           0 :                   std::unique_ptr<const Elem> remote_side(belem.build_side_ptr(s));
     637             : 
     638           0 :                   for (auto inf_s : el->side_index_range())
     639             :                     {
     640             :                       // The base side 0 shares all vertices but isn't
     641             :                       // remote
     642           0 :                       if (!inf_s)
     643           0 :                         continue;
     644             : 
     645             :                       // But another side, one which shares enough
     646             :                       // vertices to show it's the same side, is.
     647           0 :                       unsigned int n_shared_vertices = 0;
     648           0 :                       for (unsigned int i = 0; i != n_base_vertices; ++i)
     649           0 :                         for (auto & node : remote_side->node_ref_range())
     650           0 :                           if (side->node_ptr(i) == &node &&
     651           0 :                               el->is_node_on_side(i,inf_s))
     652           0 :                             ++n_shared_vertices;
     653             : 
     654           0 :                       if (n_shared_vertices + 1 >= belem.dim())
     655             :                         {
     656             :                           el->set_neighbor
     657           0 :                             (inf_s, const_cast<RemoteElem *>(remote_elem));
     658           0 :                           break;
     659             :                         }
     660             :                     }
     661           0 :                 }
     662             :         }
     663             : 
     664             :       // assign vertices to the new infinite element
     665       15810 :       for (unsigned int i=0; i<n_base_vertices; i++)
     666             :         {
     667       22092 :           el->set_node(i                , side->node_ptr(i));
     668       22092 :           el->set_node(i+n_base_vertices, outer_nodes[side->node_id(i)]);
     669             :         }
     670             : 
     671             : 
     672             :       // when this is a higher order element,
     673             :       // assign also the nodes in between
     674        3526 :       if (is_higher_order_elem)
     675             :         {
     676             :           // n_safe_base_nodes is the number of nodes in \p side
     677             :           // that may be safely assigned using below for loop.
     678             :           // Actually, n_safe_base_nodes is _identical_ with el->n_vertices(),
     679             :           // since for QUAD9, the 9th node was already assigned above
     680        2680 :           const unsigned int n_safe_base_nodes   = el->n_vertices();
     681             : 
     682       11580 :           for (unsigned int i=n_base_vertices; i<n_safe_base_nodes; i++)
     683             :             {
     684       16020 :               el->set_node(i+n_base_vertices,     side->node_ptr(i));
     685        8900 :               el->set_node(i+n_safe_base_nodes,
     686       21360 :                            outer_nodes[side->node_id(i)]);
     687             :             }
     688             :         }
     689             : 
     690             : 
     691             :       // add infinite element to mesh
     692        6342 :       this->_mesh.add_elem(std::move(el));
     693         710 :     } // for
     694             : 
     695             :   // If we're not in serial, we might not have all our remote_elem
     696             :   // neighbors set up properly on the new elements: two new infinite
     697             :   // elements which should be neighbors may not be rooted in finite
     698             :   // elmeents which are neighbors, and we were only looking for
     699             :   // remote_elem links on each root finite element.  We'll fix the
     700             :   // problem by first finding all the neighbors we can and then
     701             :   // recognizing that any missing neighbors on infinite elements must
     702             :   // be remote.
     703          86 :   if (!_mesh.is_serial())
     704             :     {
     705           0 :       _mesh.find_neighbors(/*reset_remote_elements=*/ false,
     706             :                            /*reset_current_list=*/    true,
     707           0 :                            /*assert_valid=*/          false);
     708             : 
     709           0 :       for (auto & p : ofaces)
     710             :         {
     711           0 :           Elem & belem = this->_mesh.elem_ref(p.first);
     712           0 :           Elem * inf_elem = belem.neighbor_ptr(p.second);
     713           0 :           libmesh_assert(inf_elem);
     714             : 
     715           0 :           for (auto s : make_range(inf_elem->n_sides()))
     716           0 :             if (!inf_elem->neighbor_ptr(s))
     717             :               inf_elem->set_neighbor
     718           0 :                 (s, const_cast<RemoteElem *>(remote_elem));
     719             :         }
     720             :     }
     721             : 
     722             : #ifdef DEBUG
     723          34 :   _mesh.libmesh_assert_valid_parallel_ids();
     724             : 
     725          34 :   if (be_verbose)
     726           8 :     libMesh::out << "  added "
     727           8 :                  << this->_mesh.n_elem() - n_conventional_elem
     728           8 :                  << " infinite elements and "
     729          16 :                  << onodes.size()
     730           8 :                  << " nodes to the mesh"
     731           8 :                  << std::endl
     732           8 :                  << std::endl;
     733             : #endif
     734          86 : }
     735             : 
     736             : } // namespace libMesh
     737             : 
     738             : 
     739             : 
     740             : 
     741             : 
     742             : #endif // LIBMESH_ENABLE_INFINITE_ELEMENTS

Generated by: LCOV version 1.14