LCOV - code coverage report
Current view: top level - src/meshgenerators - RenameBoundaryGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 95 97 97.9 %
Date: 2025-07-17 01:28:37 Functions: 5 5 100.0 %
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             : #include "RenameBoundaryGenerator.h"
      11             : #include "CastUniquePointer.h"
      12             : #include "MooseMeshUtils.h"
      13             : 
      14             : #include "libmesh/mesh_modification.h"
      15             : 
      16             : #include <set>
      17             : #include <sstream>
      18             : 
      19             : registerMooseObject("MooseApp", RenameBoundaryGenerator);
      20             : 
      21             : InputParameters
      22       15803 : RenameBoundaryGenerator::validParams()
      23             : {
      24       15803 :   InputParameters params = MeshGenerator::validParams();
      25             : 
      26       15803 :   params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify");
      27             : 
      28       15803 :   params.addRequiredParam<std::vector<BoundaryName>>(
      29             :       "old_boundary",
      30             :       "Elements with these boundary ID(s)/name(s) will be given the new boundary information "
      31             :       "specified in 'new_boundary'");
      32       15803 :   params.addRequiredParam<std::vector<BoundaryName>>(
      33             :       "new_boundary",
      34             :       "The new boundary ID(s)/name(s) to be given by the boundary elements defined in "
      35             :       "'old_boundary'.");
      36             : 
      37       15803 :   params.addClassDescription(
      38             :       "Changes the boundary IDs and/or boundary names for a given set of "
      39             :       "boundaries defined by either boundary ID or boundary name. The "
      40             :       "changes are independent of ordering. The merging of boundaries is supported.");
      41             : 
      42       15803 :   return params;
      43           0 : }
      44             : 
      45         754 : RenameBoundaryGenerator::RenameBoundaryGenerator(const InputParameters & params)
      46         754 :   : MeshGenerator(params), _input(getMesh("input"))
      47             : {
      48         754 :   _old_boundary = getParam<std::vector<BoundaryName>>("old_boundary");
      49             : 
      50         754 :   _new_boundary = getParam<std::vector<BoundaryName>>("new_boundary");
      51             : 
      52         754 :   if (_old_boundary.size() != _new_boundary.size())
      53           4 :     paramError("new_boundary", "Must be the same length as 'old_boundary'");
      54         750 : }
      55             : 
      56             : std::unique_ptr<MeshBase>
      57         715 : RenameBoundaryGenerator::generate()
      58             : {
      59         715 :   std::unique_ptr<MeshBase> mesh = std::move(_input);
      60             : 
      61         715 :   auto & boundary_info = mesh->get_boundary_info();
      62             : 
      63             :   // Get the current boundary IDs - take a copy so that we can also use it
      64             :   // to keep track of boundaries that we add before adding them to nodes/sides
      65         715 :   std::set<BoundaryID> boundary_ids = boundary_info.get_boundary_ids();
      66             :   // Take the union just in case someone else has added new boundaries in a
      67             :   // non-replicated manner
      68         715 :   mesh->comm().set_union(boundary_ids);
      69             : 
      70             :   // Helper for getting an unused boundary ID, and keeping track of it
      71             :   // so that we can generate more later
      72         231 :   auto get_unused_boundary_id = [this, &boundary_ids, &boundary_info]()
      73             :   {
      74         143 :     for (BoundaryID id = 0; id != Moose::INVALID_BOUNDARY_ID; ++id)
      75             :     {
      76         165 :       if (!boundary_ids.count(id) && !boundary_info.get_sideset_name_map().count(id) &&
      77          22 :           !boundary_info.get_nodeset_name_map().count(id))
      78             :       {
      79          22 :         boundary_ids.insert(id);
      80          22 :         return id;
      81             :       }
      82             :     }
      83             : 
      84           0 :     mooseError("Failed to find an unused ID!");
      85         715 :   };
      86             : 
      87             :   // Helper for checking whether or not a BoundaryName (which could be an ID or a name)
      88             :   // is really input as an ID
      89        5540 :   const auto is_boundary_id = [](const BoundaryName & boundary_name)
      90        5540 :   { return MooseUtils::isDigits(boundary_name); };
      91             : 
      92         715 :   const auto num_boundaries = _old_boundary.size();
      93             : 
      94             :   // Get the old boundary IDs and make sure they exist
      95         715 :   std::vector<BoundaryID> old_boundary_ids(num_boundaries, Moose::INVALID_BOUNDARY_ID);
      96         715 :   std::vector<std::string> old_boundary_names(num_boundaries);
      97         715 :   std::stringstream missing_boundary;
      98        3491 :   for (const auto i : make_range(num_boundaries))
      99             :   {
     100        2776 :     const BoundaryName & name = _old_boundary[i];
     101             : 
     102             :     // Convert the BoundaryName to an id and store
     103        2776 :     const auto id = MooseMeshUtils::getBoundaryID(name, *mesh);
     104        2776 :     old_boundary_ids[i] = id;
     105             : 
     106             :     // Boundary does not exist - store for a future error
     107        2776 :     if (!boundary_ids.count(id))
     108           8 :       missing_boundary << name << " ";
     109             : 
     110             :     // Keep track of the boundary names
     111             :     // If this BoundaryName is an ID, try to see if it has a name set
     112        2776 :     if (is_boundary_id(name))
     113             :     {
     114        2273 :       old_boundary_names[i] = boundary_info.get_sideset_name(id);
     115        2273 :       if (old_boundary_names[i].empty())
     116           4 :         old_boundary_names[i] = boundary_info.get_nodeset_name(id);
     117             :     }
     118             :     // If this BoundaryName is a name, use said name
     119             :     else
     120         503 :       old_boundary_names[i] = name;
     121             :   }
     122         715 :   if (missing_boundary.str().size())
     123           4 :     paramError("old_boundary",
     124             :                "The following boundaries were requested to be renamed, but do not exist: ",
     125           4 :                missing_boundary.str());
     126             : 
     127             :   // Get the boundary IDs that we're moving to
     128         711 :   std::vector<BoundaryID> new_boundary_ids(num_boundaries, Moose::INVALID_BOUNDARY_ID);
     129         711 :   std::map<BoundaryID, std::string> new_names;
     130        3471 :   for (const auto i : make_range(num_boundaries))
     131             :   {
     132        2764 :     const BoundaryName & name = _new_boundary[i];
     133             : 
     134             :     // If the user input an ID, we have the ID
     135        2764 :     if (is_boundary_id(name))
     136             :     {
     137        1409 :       const auto id = MooseMeshUtils::getBoundaryID(name, *mesh);
     138        1405 :       new_boundary_ids[i] = id;
     139             : 
     140             :       // In the case that this is a new boundary ID, keep track of it so that we
     141             :       // don't reuse it if we have to create temproraries
     142        1405 :       boundary_ids.insert(id);
     143             : 
     144             :       // Preserve the old boundary name if there was one
     145        1405 :       if (old_boundary_names[i].size())
     146             :       {
     147             :         // if the name was the same as the ID, change the name as well
     148        1405 :         if (old_boundary_names[i] == std::to_string(old_boundary_ids[i]))
     149          11 :           new_names[id] = std::to_string(new_boundary_ids[i]);
     150             :         else
     151        1394 :           new_names[id] = old_boundary_names[i];
     152             :       }
     153             :     }
     154             :     // If the user input a name, we will use the ID that it is coming from for the
     155             :     // "new" name if the new name does not name a current boundary. If the name does
     156             :     // exist, we will merge with said boundary.
     157             :     else
     158             :     {
     159        1355 :       bool name_already_exists = false;
     160             : 
     161             :       // If the target boundary already exists, merge into that one
     162             :       // Check both the old maps and the new map
     163        5420 :       for (const auto map : {&boundary_info.set_sideset_name_map(),
     164        1355 :                              &boundary_info.set_nodeset_name_map(),
     165        6775 :                              &new_names})
     166       18380 :         for (const auto & id_name_pair : *map)
     167       14315 :           if (!name_already_exists && id_name_pair.second == name)
     168             :           {
     169          11 :             new_boundary_ids[i] = id_name_pair.first;
     170          11 :             new_names[id_name_pair.first] = name;
     171          11 :             name_already_exists = true;
     172             :           }
     173             : 
     174             :       // Target name doesn't exist, so use the source id/name
     175        1355 :       if (!name_already_exists)
     176             :       {
     177        1344 :         new_boundary_ids[i] = old_boundary_ids[i];
     178        1344 :         new_names[new_boundary_ids[i]] = name;
     179             :       }
     180             :     }
     181             :   }
     182             : 
     183             :   // Create temproraries if needed; recall that this generator is independent
     184             :   // of input ordering and does _not_ merge sidesets.
     185             :   //
     186             :   // Take the example where we want to move 0 -> 1 and 1 -> 2. If we just
     187             :   // move them in order, we will actually end up with (0, 1) -> 2. This is
     188             :   // bad. In this case, we want to first make a temprorary for 1 (call it 3).
     189             :   // We then do: 0 -> 3, 1 -> 2, 3 -> 1 in order to get the desired behavior.
     190             :   // We will accomplish this by creating temproraries as needed, modifying
     191             :   // the initial move to the temproraries as needed, and then moving the
     192             :   // temproraries back. temp_change_ids here are the (from -> to) pairs
     193             :   // that we will move at the end.
     194         707 :   auto temp_new_boundary_ids = new_boundary_ids;
     195         707 :   std::vector<std::pair<BoundaryID, BoundaryID>> temp_change_ids;
     196             :   // Loop through all new IDs
     197        3467 :   for (const auto new_i : make_range(num_boundaries))
     198             :   {
     199             :     // Look at all of the old IDs that will be moved after the move to the new ID.
     200             :     // If any of the old IDs after are IDs that we are moving to, create a temprorary
     201             :     // and keep track of it so we can move it back at the end.
     202        7417 :     for (const auto old_i : make_range(new_i + 1, num_boundaries))
     203        4679 :       if (new_boundary_ids[new_i] == old_boundary_ids[old_i])
     204             :       {
     205          22 :         const auto temp_id = get_unused_boundary_id();
     206          22 :         temp_change_ids.emplace_back(temp_id, new_boundary_ids[new_i]);
     207          22 :         temp_new_boundary_ids[new_i] = temp_id;
     208          22 :         break;
     209             :       }
     210             :   }
     211             : 
     212             :   // First pass through changing the boundary ids
     213        3467 :   for (const auto i : make_range(num_boundaries))
     214        2760 :     MeshTools::Modification::change_boundary_id(
     215        2760 :         *mesh, old_boundary_ids[i], temp_new_boundary_ids[i]);
     216             : 
     217             :   // Pass through moving the temproraries to the actual boundaries, if necessary
     218         729 :   for (const auto & pair : temp_change_ids)
     219          22 :     MeshTools::Modification::change_boundary_id(*mesh, pair.first, pair.second);
     220             : 
     221             :   // First go through and remove all of the old names
     222        3467 :   for (const auto i : make_range(num_boundaries))
     223             :   {
     224        2760 :     if (boundary_info.get_sideset_name_map().count(old_boundary_ids[i]))
     225        1399 :       boundary_info.set_sideset_name_map().erase(old_boundary_ids[i]);
     226        2760 :     if (boundary_info.get_nodeset_name_map().count(old_boundary_ids[i]))
     227        1343 :       boundary_info.set_nodeset_name_map().erase(old_boundary_ids[i]);
     228             :   }
     229             : 
     230             :   // With the old names removed, add the new names if there are any to add
     231        3349 :   for (const auto & pair : new_names)
     232             :   {
     233        2642 :     boundary_info.sideset_name(pair.first) = pair.second;
     234        2642 :     boundary_info.nodeset_name(pair.first) = pair.second;
     235             :   }
     236             : 
     237         707 :   mesh->set_isnt_prepared();
     238        1414 :   return dynamic_pointer_cast<MeshBase>(mesh);
     239         707 : }

Generated by: LCOV version 1.14