LCOV - code coverage report
Current view: top level - src/utils - MooseMeshUtils.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 250 318 78.6 %
Date: 2025-07-17 01:28:37 Functions: 24 29 82.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : // MOOSE includes
      11             : #include "MooseMeshUtils.h"
      12             : 
      13             : #include "libmesh/elem.h"
      14             : #include "libmesh/boundary_info.h"
      15             : #include "libmesh/parallel.h"
      16             : #include "libmesh/parallel_algebra.h"
      17             : #include "libmesh/utility.h"
      18             : 
      19             : using namespace libMesh;
      20             : 
      21             : namespace MooseMeshUtils
      22             : {
      23             : 
      24             : void
      25         194 : mergeBoundaryIDsWithSameName(MeshBase & mesh)
      26             : {
      27             :   // We check if we have the same boundary name with different IDs. If we do, we assign the
      28             :   // first ID to every occurrence.
      29         194 :   const auto & side_bd_name_map = mesh.get_boundary_info().get_sideset_name_map();
      30         194 :   const auto & node_bd_name_map = mesh.get_boundary_info().get_nodeset_name_map();
      31         194 :   std::map<boundary_id_type, boundary_id_type> same_name_ids;
      32             : 
      33         388 :   auto populate_map = [](const std::map<boundary_id_type, std::string> & map,
      34             :                          std::map<boundary_id_type, boundary_id_type> & same_ids)
      35             :   {
      36        2276 :     for (const auto & pair_outer : map)
      37       15448 :       for (const auto & pair_inner : map)
      38             :         // The last condition is needed to make sure we only store one combination
      39       14896 :         if (pair_outer.second == pair_inner.second && pair_outer.first != pair_inner.first &&
      40       14896 :             same_ids.find(pair_inner.first) == same_ids.end())
      41         668 :           same_ids[pair_outer.first] = pair_inner.first;
      42         388 :   };
      43             : 
      44         194 :   populate_map(side_bd_name_map, same_name_ids);
      45         194 :   populate_map(node_bd_name_map, same_name_ids);
      46             : 
      47         560 :   for (const auto & [id1, id2] : same_name_ids)
      48         366 :     mesh.get_boundary_info().renumber_id(id2, id1);
      49         194 : }
      50             : 
      51             : void
      52         403 : changeBoundaryId(MeshBase & mesh,
      53             :                  const boundary_id_type old_id,
      54             :                  const boundary_id_type new_id,
      55             :                  bool delete_prev)
      56             : {
      57             :   // Get a reference to our BoundaryInfo object, we will use it several times below...
      58         403 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
      59             : 
      60             :   // Container to catch ids passed back from BoundaryInfo
      61         403 :   std::vector<boundary_id_type> old_ids;
      62             : 
      63             :   // Only level-0 elements store BCs.  Loop over them.
      64      616259 :   for (auto & elem : as_range(mesh.level_elements_begin(0), mesh.level_elements_end(0)))
      65             :   {
      66      307928 :     unsigned int n_sides = elem->n_sides();
      67     2124708 :     for (const auto s : make_range(n_sides))
      68             :     {
      69     1816780 :       boundary_info.boundary_ids(elem, s, old_ids);
      70     1816780 :       if (std::find(old_ids.begin(), old_ids.end(), old_id) != old_ids.end())
      71             :       {
      72       16997 :         std::vector<boundary_id_type> new_ids(old_ids);
      73       16997 :         std::replace(new_ids.begin(), new_ids.end(), old_id, new_id);
      74       16997 :         if (delete_prev)
      75             :         {
      76       16997 :           boundary_info.remove_side(elem, s);
      77       16997 :           boundary_info.add_side(elem, s, new_ids);
      78             :         }
      79             :         else
      80           0 :           boundary_info.add_side(elem, s, new_ids);
      81       16997 :       }
      82             :     }
      83         403 :   }
      84             : 
      85             :   // Remove any remaining references to the old ID from the
      86             :   // BoundaryInfo object.  This prevents things like empty sidesets
      87             :   // from showing up when printing information, etc.
      88         403 :   if (delete_prev)
      89         403 :     boundary_info.remove_id(old_id);
      90             : 
      91             :   // global information may now be out of sync
      92         403 :   mesh.set_isnt_prepared();
      93         403 : }
      94             : 
      95             : std::vector<boundary_id_type>
      96        9189 : getBoundaryIDs(const MeshBase & mesh,
      97             :                const std::vector<BoundaryName> & boundary_name,
      98             :                bool generate_unknown)
      99             : {
     100             :   return getBoundaryIDs(
     101        9189 :       mesh, boundary_name, generate_unknown, mesh.get_boundary_info().get_boundary_ids());
     102             : }
     103             : 
     104             : std::vector<boundary_id_type>
     105      121731 : getBoundaryIDs(const MeshBase & mesh,
     106             :                const std::vector<BoundaryName> & boundary_name,
     107             :                bool generate_unknown,
     108             :                const std::set<BoundaryID> & mesh_boundary_ids)
     109             : {
     110      121731 :   const BoundaryInfo & boundary_info = mesh.get_boundary_info();
     111      121731 :   const std::map<BoundaryID, std::string> & sideset_map = boundary_info.get_sideset_name_map();
     112      121731 :   const std::map<BoundaryID, std::string> & nodeset_map = boundary_info.get_nodeset_name_map();
     113             : 
     114      121731 :   BoundaryID max_boundary_local_id = 0;
     115             :   /* It is required to generate a new ID for a given name. It is used often in mesh modifiers such
     116             :    * as SideSetsBetweenSubdomains. Then we need to check the current boundary ids since they are
     117             :    * changing during "mesh modify()", and figure out the right max boundary ID. Most of mesh
     118             :    * modifiers are running in serial, and we won't involve a global communication.
     119             :    */
     120      121731 :   if (generate_unknown)
     121             :   {
     122      117222 :     const auto & bids = mesh.is_prepared() ? mesh.get_boundary_info().get_global_boundary_ids()
     123         753 :                                            : mesh.get_boundary_info().get_boundary_ids();
     124      117222 :     max_boundary_local_id = bids.empty() ? 0 : *(bids.rbegin());
     125             :     /* We should not hit this often */
     126      117222 :     if (!mesh.is_prepared() && !mesh.is_serial())
     127          87 :       mesh.comm().max(max_boundary_local_id);
     128             :   }
     129             : 
     130      121731 :   BoundaryID max_boundary_id = mesh_boundary_ids.empty() ? 0 : *(mesh_boundary_ids.rbegin());
     131             : 
     132      121731 :   max_boundary_id =
     133      121731 :       max_boundary_id > max_boundary_local_id ? max_boundary_id : max_boundary_local_id;
     134             : 
     135      121731 :   std::vector<BoundaryID> ids(boundary_name.size());
     136      275713 :   for (const auto i : index_range(boundary_name))
     137             :   {
     138      154101 :     if (boundary_name[i] == "ANY_BOUNDARY_ID")
     139             :     {
     140         119 :       ids.assign(mesh_boundary_ids.begin(), mesh_boundary_ids.end());
     141         119 :       if (i)
     142           0 :         mooseWarning("You passed \"ANY_BOUNDARY_ID\" in addition to other boundary_names.  This "
     143             :                      "may be a logic error.");
     144         119 :       break;
     145             :     }
     146             : 
     147      153982 :     if (boundary_name[i].empty() && !generate_unknown)
     148           0 :       mooseError("Incoming boundary name is empty and we are not generating unknown boundary IDs. "
     149             :                  "This is invalid.");
     150             : 
     151             :     BoundaryID id;
     152             : 
     153      153982 :     if (boundary_name[i].empty() || !MooseUtils::isDigits(boundary_name[i]))
     154             :     {
     155             :       /**
     156             :        * If the conversion from a name to a number fails, that means that this must be a named
     157             :        * boundary.  We will look in the complete map for this sideset and create a new name/ID pair
     158             :        * if requested.
     159             :        */
     160      305495 :       if (generate_unknown &&
     161      218120 :           !MooseUtils::doesMapContainValue(sideset_map, std::string(boundary_name[i])) &&
     162      113129 :           !MooseUtils::doesMapContainValue(nodeset_map, std::string(boundary_name[i])))
     163        7918 :         id = ++max_boundary_id;
     164             :       else
     165       97073 :         id = boundary_info.get_id_by_name(boundary_name[i]);
     166             :     }
     167             :     else
     168       48991 :       id = getIDFromName<BoundaryName, BoundaryID>(boundary_name[i]);
     169             : 
     170      153982 :     ids[i] = id;
     171             :   }
     172             : 
     173      243462 :   return ids;
     174           0 : }
     175             : 
     176             : std::set<BoundaryID>
     177         187 : getBoundaryIDSet(const MeshBase & mesh,
     178             :                  const std::vector<BoundaryName> & boundary_name,
     179             :                  bool generate_unknown)
     180             : {
     181         187 :   auto boundaries = getBoundaryIDs(mesh, boundary_name, generate_unknown);
     182         374 :   return std::set<BoundaryID>(boundaries.begin(), boundaries.end());
     183         187 : }
     184             : 
     185             : std::vector<subdomain_id_type>
     186      232562 : getSubdomainIDs(const MeshBase & mesh, const std::vector<SubdomainName> & subdomain_names)
     187             : {
     188      232562 :   std::vector<SubdomainID> ids(subdomain_names.size());
     189             : 
     190      342588 :   for (const auto i : index_range(subdomain_names))
     191      110026 :     ids[i] = MooseMeshUtils::getSubdomainID(subdomain_names[i], mesh);
     192             : 
     193      232562 :   return ids;
     194           0 : }
     195             : 
     196             : std::set<subdomain_id_type>
     197           0 : getSubdomainIDs(const MeshBase & mesh, const std::set<SubdomainName> & subdomain_names)
     198             : {
     199           0 :   std::set<SubdomainID> ids;
     200           0 :   for (const auto & name : subdomain_names)
     201           0 :     ids.insert(MooseMeshUtils::getSubdomainID(name, mesh));
     202           0 :   return ids;
     203           0 : }
     204             : 
     205             : BoundaryID
     206      524867 : getBoundaryID(const BoundaryName & boundary_name, const MeshBase & mesh)
     207             : {
     208      524867 :   BoundaryID id = Moose::INVALID_BOUNDARY_ID;
     209      524867 :   if (boundary_name.empty())
     210           0 :     return id;
     211             : 
     212      524867 :   if (!MooseUtils::isDigits(boundary_name))
     213      345273 :     id = mesh.get_boundary_info().get_id_by_name(boundary_name);
     214             :   else
     215      179594 :     id = getIDFromName<BoundaryName, BoundaryID>(boundary_name);
     216             : 
     217      524863 :   return id;
     218             : }
     219             : 
     220             : SubdomainID
     221      621580 : getSubdomainID(const SubdomainName & subdomain_name, const MeshBase & mesh)
     222             : {
     223      621580 :   if (subdomain_name == "ANY_BLOCK_ID")
     224           0 :     mooseError("getSubdomainID() does not work with \"ANY_BLOCK_ID\"");
     225             : 
     226      621580 :   SubdomainID id = Moose::INVALID_BLOCK_ID;
     227      621580 :   if (subdomain_name.empty())
     228           0 :     return id;
     229             : 
     230      621580 :   if (!MooseUtils::isDigits(subdomain_name))
     231      374114 :     id = mesh.get_id_by_name(subdomain_name);
     232             :   else
     233      247466 :     id = getIDFromName<SubdomainName, SubdomainID>(subdomain_name);
     234             : 
     235      621580 :   return id;
     236             : }
     237             : 
     238             : void
     239           0 : changeSubdomainId(MeshBase & mesh, const subdomain_id_type old_id, const subdomain_id_type new_id)
     240             : {
     241           0 :   for (const auto & elem : mesh.element_ptr_range())
     242           0 :     if (elem->subdomain_id() == old_id)
     243           0 :       elem->subdomain_id() = new_id;
     244             : 
     245             :   // global cached information may now be out of sync
     246           0 :   mesh.set_isnt_prepared();
     247           0 : }
     248             : 
     249             : Point
     250         268 : meshCentroidCalculator(const MeshBase & mesh)
     251             : {
     252         268 :   Point centroid_pt = Point(0.0, 0.0, 0.0);
     253         268 :   Real vol_tmp = 0.0;
     254         268 :   for (const auto & elem :
     255        4692 :        as_range(mesh.active_local_elements_begin(), mesh.active_local_elements_end()))
     256             :   {
     257        4156 :     Real elem_vol = elem->volume();
     258        4156 :     centroid_pt += (elem->true_centroid()) * elem_vol;
     259        4156 :     vol_tmp += elem_vol;
     260         268 :   }
     261         268 :   mesh.comm().sum(centroid_pt);
     262         268 :   mesh.comm().sum(vol_tmp);
     263         268 :   centroid_pt /= vol_tmp;
     264         536 :   return centroid_pt;
     265             : }
     266             : 
     267             : std::unordered_map<dof_id_type, dof_id_type>
     268         255 : getExtraIDUniqueCombinationMap(const MeshBase & mesh,
     269             :                                const std::set<SubdomainID> & block_ids,
     270             :                                std::vector<ExtraElementIDName> extra_ids)
     271             : {
     272             :   // check block restriction
     273         255 :   const bool block_restricted = !block_ids.empty();
     274             :   // get element id name of interest in recursive parsing algorithm
     275         255 :   ExtraElementIDName id_name = extra_ids.back();
     276         255 :   extra_ids.pop_back();
     277         255 :   const auto id_index = mesh.get_elem_integer_index(id_name);
     278             : 
     279             :   // create base parsed id set
     280         255 :   if (extra_ids.empty())
     281             :   {
     282             :     // get set of extra id values;
     283         149 :     std::vector<dof_id_type> ids;
     284             :     {
     285         149 :       std::set<dof_id_type> ids_set;
     286     1025967 :       for (const auto & elem : mesh.active_element_ptr_range())
     287             :       {
     288      512909 :         if (block_restricted && block_ids.find(elem->subdomain_id()) == block_ids.end())
     289         530 :           continue;
     290      512379 :         const auto id = elem->get_extra_integer(id_index);
     291      512379 :         ids_set.insert(id);
     292         149 :       }
     293         149 :       mesh.comm().set_union(ids_set);
     294         149 :       ids.assign(ids_set.begin(), ids_set.end());
     295         149 :     }
     296             : 
     297             :     // determine new extra id values;
     298         149 :     std::unordered_map<dof_id_type, dof_id_type> parsed_ids;
     299     1025967 :     for (auto & elem : mesh.active_element_ptr_range())
     300             :     {
     301      512909 :       if (block_restricted && block_ids.find(elem->subdomain_id()) == block_ids.end())
     302         530 :         continue;
     303      512379 :       parsed_ids[elem->id()] = std::distance(
     304     1024758 :           ids.begin(), std::lower_bound(ids.begin(), ids.end(), elem->get_extra_integer(id_index)));
     305         149 :     }
     306         149 :     return parsed_ids;
     307         149 :   }
     308             : 
     309             :   // if extra_ids is not empty, recursively call getExtraIDUniqueCombinationMap
     310             :   const auto base_parsed_ids =
     311         106 :       MooseMeshUtils::getExtraIDUniqueCombinationMap(mesh, block_ids, extra_ids);
     312             :   // parsing extra ids based on ref_parsed_ids
     313         106 :   std::vector<std::pair<dof_id_type, dof_id_type>> unique_ids;
     314             :   {
     315         106 :     std::set<std::pair<dof_id_type, dof_id_type>> unique_ids_set;
     316      778602 :     for (const auto & elem : mesh.active_element_ptr_range())
     317             :     {
     318      389248 :       if (block_restricted && block_ids.find(elem->subdomain_id()) == block_ids.end())
     319         480 :         continue;
     320      388768 :       const dof_id_type id1 = libmesh_map_find(base_parsed_ids, elem->id());
     321      388768 :       const dof_id_type id2 = elem->get_extra_integer(id_index);
     322      388768 :       const std::pair<dof_id_type, dof_id_type> ids = std::make_pair(id1, id2);
     323      388768 :       unique_ids_set.insert(ids);
     324         106 :     }
     325         106 :     mesh.comm().set_union(unique_ids_set);
     326         106 :     unique_ids.assign(unique_ids_set.begin(), unique_ids_set.end());
     327         106 :   }
     328             : 
     329         106 :   std::unordered_map<dof_id_type, dof_id_type> parsed_ids;
     330             : 
     331      778602 :   for (const auto & elem : mesh.active_element_ptr_range())
     332             :   {
     333      389248 :     if (block_restricted && block_ids.find(elem->subdomain_id()) == block_ids.end())
     334         480 :       continue;
     335      388768 :     const dof_id_type id1 = libmesh_map_find(base_parsed_ids, elem->id());
     336      388768 :     const dof_id_type id2 = elem->get_extra_integer(id_index);
     337      388768 :     const dof_id_type new_id = std::distance(
     338             :         unique_ids.begin(),
     339      388768 :         std::lower_bound(unique_ids.begin(), unique_ids.end(), std::make_pair(id1, id2)));
     340      388768 :     parsed_ids[elem->id()] = new_id;
     341         106 :   }
     342             : 
     343         106 :   return parsed_ids;
     344         255 : }
     345             : 
     346             : bool
     347         286 : isCoPlanar(const std::vector<Point> vec_pts, const Point plane_nvec, const Point fixed_pt)
     348             : {
     349        2486 :   for (const auto & pt : vec_pts)
     350        2204 :     if (!MooseUtils::absoluteFuzzyEqual((pt - fixed_pt) * plane_nvec, 0.0))
     351           4 :       return false;
     352         282 :   return true;
     353             : }
     354             : 
     355             : bool
     356           0 : isCoPlanar(const std::vector<Point> vec_pts, const Point plane_nvec)
     357             : {
     358           0 :   return isCoPlanar(vec_pts, plane_nvec, vec_pts.front());
     359             : }
     360             : 
     361             : bool
     362           0 : isCoPlanar(const std::vector<Point> vec_pts)
     363             : {
     364             :   // Assuming that overlapped Points are allowed, the Points that are overlapped with vec_pts[0] are
     365             :   // removed before further calculation.
     366           0 :   std::vector<Point> vec_pts_nonzero{vec_pts[0]};
     367           0 :   for (const auto i : index_range(vec_pts))
     368           0 :     if (!MooseUtils::absoluteFuzzyEqual((vec_pts[i] - vec_pts[0]).norm(), 0.0))
     369           0 :       vec_pts_nonzero.push_back(vec_pts[i]);
     370             :   // 3 or fewer points are always coplanar
     371           0 :   if (vec_pts_nonzero.size() <= 3)
     372           0 :     return true;
     373             :   else
     374             :   {
     375           0 :     for (const auto i : make_range(vec_pts_nonzero.size() - 1))
     376             :     {
     377           0 :       const Point tmp_pt = (vec_pts_nonzero[i] - vec_pts_nonzero[0])
     378           0 :                                .cross(vec_pts_nonzero[i + 1] - vec_pts_nonzero[0]);
     379             :       // if the three points are not collinear, use cross product as the normal vector of the plane
     380           0 :       if (!MooseUtils::absoluteFuzzyEqual(tmp_pt.norm(), 0.0))
     381           0 :         return isCoPlanar(vec_pts_nonzero, tmp_pt.unit());
     382             :     }
     383             :   }
     384             :   // If all the points are collinear, they are also coplanar
     385           0 :   return true;
     386           0 : }
     387             : 
     388             : SubdomainID
     389         376 : getNextFreeSubdomainID(MeshBase & input_mesh)
     390             : {
     391             :   // Call this to get most up to date block id information
     392         376 :   input_mesh.cache_elem_data();
     393             : 
     394         376 :   std::set<SubdomainID> preexisting_subdomain_ids;
     395         376 :   input_mesh.subdomain_ids(preexisting_subdomain_ids);
     396         376 :   if (preexisting_subdomain_ids.empty())
     397           0 :     return 0;
     398             :   else
     399             :   {
     400             :     const auto highest_subdomain_id =
     401         376 :         *std::max_element(preexisting_subdomain_ids.begin(), preexisting_subdomain_ids.end());
     402             :     mooseAssert(highest_subdomain_id < std::numeric_limits<SubdomainID>::max(),
     403             :                 "A SubdomainID with max possible value was found");
     404         376 :     return highest_subdomain_id + 1;
     405             :   }
     406         376 : }
     407             : 
     408             : BoundaryID
     409         496 : getNextFreeBoundaryID(MeshBase & input_mesh)
     410             : {
     411         496 :   auto boundary_ids = input_mesh.get_boundary_info().get_boundary_ids();
     412         496 :   if (boundary_ids.empty())
     413          28 :     return 0;
     414         468 :   return (*boundary_ids.rbegin() + 1);
     415         496 : }
     416             : 
     417             : bool
     418       11738 : hasSubdomainID(const MeshBase & input_mesh, const SubdomainID & id)
     419             : {
     420       11738 :   std::set<SubdomainID> mesh_blocks;
     421       11738 :   input_mesh.subdomain_ids(mesh_blocks);
     422             : 
     423             :   // On a distributed mesh we may have sideset IDs that only exist on
     424             :   // other processors
     425       11738 :   if (!input_mesh.is_replicated())
     426        1238 :     input_mesh.comm().set_union(mesh_blocks);
     427             : 
     428       23476 :   return mesh_blocks.count(id) && (id != Moose::INVALID_BLOCK_ID);
     429       11738 : }
     430             : 
     431             : bool
     432        9669 : hasSubdomainName(const MeshBase & input_mesh, const SubdomainName & name)
     433             : {
     434        9669 :   const auto id = getSubdomainID(name, input_mesh);
     435       19338 :   return hasSubdomainID(input_mesh, id);
     436             : }
     437             : 
     438             : bool
     439        3650 : hasBoundaryID(const MeshBase & input_mesh, const BoundaryID id)
     440             : {
     441        3650 :   const BoundaryInfo & boundary_info = input_mesh.get_boundary_info();
     442        3650 :   std::set<boundary_id_type> boundary_ids = boundary_info.get_boundary_ids();
     443             : 
     444             :   // On a distributed mesh we may have boundary IDs that only exist on
     445             :   // other processors
     446        3650 :   if (!input_mesh.is_replicated())
     447         690 :     input_mesh.comm().set_union(boundary_ids);
     448             : 
     449        7300 :   return boundary_ids.count(id) && (id != Moose::INVALID_BOUNDARY_ID);
     450        3650 : }
     451             : 
     452             : bool
     453        3486 : hasBoundaryName(const MeshBase & input_mesh, const BoundaryName & name)
     454             : {
     455        3486 :   const auto id = getBoundaryID(name, input_mesh);
     456        3486 :   return hasBoundaryID(input_mesh, id);
     457             : }
     458             : 
     459             : void
     460         454 : makeOrderedNodeList(std::vector<std::pair<dof_id_type, dof_id_type>> & node_assm,
     461             :                     std::vector<dof_id_type> & elem_id_list,
     462             :                     std::vector<dof_id_type> & midpoint_node_list,
     463             :                     std::vector<dof_id_type> & ordered_node_list,
     464             :                     std::vector<dof_id_type> & ordered_elem_id_list)
     465             : {
     466             :   // a flag to indicate if the ordered_node_list has been reversed
     467         454 :   bool is_flipped = false;
     468             :   // Start from the first element, try to find a chain of nodes
     469             :   mooseAssert(node_assm.size(), "Node list must not be empty");
     470         454 :   ordered_node_list.push_back(node_assm.front().first);
     471         454 :   if (midpoint_node_list.front() != DofObject::invalid_id)
     472           0 :     ordered_node_list.push_back(midpoint_node_list.front());
     473         454 :   ordered_node_list.push_back(node_assm.front().second);
     474         454 :   ordered_elem_id_list.push_back(elem_id_list.front());
     475             :   // Remove the element that has just been added to ordered_node_list
     476         454 :   node_assm.erase(node_assm.begin());
     477         454 :   midpoint_node_list.erase(midpoint_node_list.begin());
     478         454 :   elem_id_list.erase(elem_id_list.begin());
     479         454 :   const unsigned int node_assm_size_0 = node_assm.size();
     480        2681 :   for (unsigned int i = 0; i < node_assm_size_0; i++)
     481             :   {
     482             :     // Find nodes to expand the chain
     483        2231 :     dof_id_type end_node_id = ordered_node_list.back();
     484        7507 :     auto isMatch1 = [end_node_id](std::pair<dof_id_type, dof_id_type> old_id_pair)
     485        7507 :     { return old_id_pair.first == end_node_id; };
     486        1976 :     auto isMatch2 = [end_node_id](std::pair<dof_id_type, dof_id_type> old_id_pair)
     487        1976 :     { return old_id_pair.second == end_node_id; };
     488        2231 :     auto result = std::find_if(node_assm.begin(), node_assm.end(), isMatch1);
     489             :     bool match_first;
     490        2231 :     if (result == node_assm.end())
     491             :     {
     492        1116 :       match_first = false;
     493        1116 :       result = std::find_if(node_assm.begin(), node_assm.end(), isMatch2);
     494             :     }
     495             :     else
     496             :     {
     497        1115 :       match_first = true;
     498             :     }
     499             :     // If found, add the node to boundary_ordered_node_list
     500        2231 :     if (result != node_assm.end())
     501             :     {
     502        1996 :       const auto elem_index = std::distance(node_assm.begin(), result);
     503        1996 :       if (midpoint_node_list[elem_index] != DofObject::invalid_id)
     504           0 :         ordered_node_list.push_back(midpoint_node_list[elem_index]);
     505        1996 :       ordered_node_list.push_back(match_first ? (*result).second : (*result).first);
     506        1996 :       node_assm.erase(result);
     507        1996 :       midpoint_node_list.erase(midpoint_node_list.begin() + elem_index);
     508        1996 :       ordered_elem_id_list.push_back(elem_id_list[elem_index]);
     509        1996 :       elem_id_list.erase(elem_id_list.begin() + elem_index);
     510             :     }
     511             :     // If there are still elements in node_assm and result ==
     512             :     // node_assm.end(), this means the curve is not a loop, the
     513             :     // ordered_node_list is flipped and try the other direction that has not
     514             :     // been examined yet.
     515             :     else
     516             :     {
     517         235 :       if (is_flipped)
     518             :         // Flipped twice; this means the node list has at least two segments.
     519           4 :         throw MooseException("The node list provided has more than one segments.");
     520             : 
     521             :       // mark the first flip event.
     522         231 :       is_flipped = true;
     523         231 :       std::reverse(ordered_node_list.begin(), ordered_node_list.end());
     524         231 :       std::reverse(midpoint_node_list.begin(), midpoint_node_list.end());
     525         231 :       std::reverse(ordered_elem_id_list.begin(), ordered_elem_id_list.end());
     526             :       // As this iteration is wasted, set the iterator backward
     527         231 :       i--;
     528             :     }
     529             :   }
     530         450 : }
     531             : 
     532             : void
     533         208 : makeOrderedNodeList(std::vector<std::pair<dof_id_type, dof_id_type>> & node_assm,
     534             :                     std::vector<dof_id_type> & elem_id_list,
     535             :                     std::vector<dof_id_type> & ordered_node_list,
     536             :                     std::vector<dof_id_type> & ordered_elem_id_list)
     537             : {
     538         208 :   std::vector<dof_id_type> dummy_midpoint_node_list(node_assm.size(), DofObject::invalid_id);
     539         208 :   makeOrderedNodeList(
     540             :       node_assm, elem_id_list, dummy_midpoint_node_list, ordered_node_list, ordered_elem_id_list);
     541         208 : }
     542             : 
     543             : void
     544        8160 : swapNodesInElem(Elem & elem, const unsigned int nd1, const unsigned int nd2)
     545             : {
     546        8160 :   Node * n_temp = elem.node_ptr(nd1);
     547        8160 :   elem.set_node(nd1, elem.node_ptr(nd2));
     548        8160 :   elem.set_node(nd2, n_temp);
     549        8160 : }
     550             : 
     551             : void
     552         197 : extraElemIntegerSwapParametersProcessor(
     553             :     const std::string & class_name,
     554             :     const unsigned int num_sections,
     555             :     const unsigned int num_integers,
     556             :     const std::vector<std::vector<std::vector<dof_id_type>>> & elem_integers_swaps,
     557             :     std::vector<std::unordered_map<dof_id_type, dof_id_type>> & elem_integers_swap_pairs)
     558             : {
     559         197 :   elem_integers_swap_pairs.reserve(num_sections * num_integers);
     560         221 :   for (const auto i : make_range(num_integers))
     561             :   {
     562          24 :     const auto & elem_integer_swaps = elem_integers_swaps[i];
     563          24 :     std::vector<std::unordered_map<dof_id_type, dof_id_type>> elem_integer_swap_pairs;
     564             :     try
     565             :     {
     566          24 :       MooseMeshUtils::idSwapParametersProcessor(class_name,
     567             :                                                 "elem_integers_swaps",
     568             :                                                 elem_integer_swaps,
     569             :                                                 elem_integer_swap_pairs,
     570             :                                                 i * num_sections);
     571             :     }
     572           0 :     catch (const MooseException & e)
     573             :     {
     574           0 :       throw MooseException(e.what());
     575           0 :     }
     576             : 
     577          24 :     elem_integers_swap_pairs.insert(elem_integers_swap_pairs.end(),
     578             :                                     elem_integer_swap_pairs.begin(),
     579             :                                     elem_integer_swap_pairs.end());
     580          24 :   }
     581         197 : }
     582             : 
     583             : std::unique_ptr<ReplicatedMesh>
     584           0 : buildBoundaryMesh(const ReplicatedMesh & input_mesh, const boundary_id_type boundary_id)
     585             : {
     586           0 :   auto poly_mesh = std::make_unique<ReplicatedMesh>(input_mesh.comm());
     587             : 
     588           0 :   auto side_list = input_mesh.get_boundary_info().build_side_list();
     589             : 
     590           0 :   std::unordered_map<dof_id_type, dof_id_type> old_new_node_map;
     591           0 :   for (const auto & bside : side_list)
     592             :   {
     593           0 :     if (std::get<2>(bside) != boundary_id)
     594           0 :       continue;
     595             : 
     596           0 :     const Elem * elem = input_mesh.elem_ptr(std::get<0>(bside));
     597           0 :     const auto side = std::get<1>(bside);
     598           0 :     auto side_elem = elem->build_side_ptr(side);
     599           0 :     auto copy = side_elem->build(side_elem->type());
     600             : 
     601           0 :     for (const auto i : side_elem->node_index_range())
     602             :     {
     603           0 :       auto & n = side_elem->node_ref(i);
     604             : 
     605           0 :       if (old_new_node_map.count(n.id()))
     606           0 :         copy->set_node(i, poly_mesh->node_ptr(old_new_node_map[n.id()]));
     607             :       else
     608             :       {
     609           0 :         Node * node = poly_mesh->add_point(side_elem->point(i));
     610           0 :         copy->set_node(i, node);
     611           0 :         old_new_node_map[n.id()] = node->id();
     612             :       }
     613             :     }
     614           0 :     poly_mesh->add_elem(copy.release());
     615           0 :   }
     616           0 :   poly_mesh->skip_partitioning(true);
     617           0 :   poly_mesh->prepare_for_use();
     618           0 :   if (poly_mesh->n_elem() == 0)
     619           0 :     mooseError("The input mesh does not have a boundary with id ", boundary_id);
     620             : 
     621           0 :   return poly_mesh;
     622           0 : }
     623             : }

Generated by: LCOV version 1.14