https://mooseframework.inl.gov
RenameBoundaryGenerator.C
Go to the documentation of this file.
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 
11 #include "CastUniquePointer.h"
12 #include "MooseMeshUtils.h"
13 
14 #include "libmesh/mesh_modification.h"
15 
16 #include <set>
17 #include <sstream>
18 
20 
23 {
25 
26  params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify");
27 
28  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  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  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  return params;
43 }
44 
46  : MeshGenerator(params), _input(getMesh("input"))
47 {
48  _old_boundary = getParam<std::vector<BoundaryName>>("old_boundary");
49 
50  _new_boundary = getParam<std::vector<BoundaryName>>("new_boundary");
51 
52  if (_old_boundary.size() != _new_boundary.size())
53  paramError("new_boundary", "Must be the same length as 'old_boundary'");
54 }
55 
56 std::unique_ptr<MeshBase>
58 {
59  std::unique_ptr<MeshBase> mesh = std::move(_input);
60 
61  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  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  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  auto get_unused_boundary_id = [this, &boundary_ids, &boundary_info]()
73  {
74  for (BoundaryID id = 0; id != Moose::INVALID_BOUNDARY_ID; ++id)
75  {
76  if (!boundary_ids.count(id) && !boundary_info.get_sideset_name_map().count(id) &&
77  !boundary_info.get_nodeset_name_map().count(id))
78  {
79  boundary_ids.insert(id);
80  return id;
81  }
82  }
83 
84  mooseError("Failed to find an unused ID!");
85  };
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  const auto is_boundary_id = [](const BoundaryName & boundary_name)
90  { return MooseUtils::isDigits(boundary_name); };
91 
92  const auto num_boundaries = _old_boundary.size();
93 
94  // Get the old boundary IDs and make sure they exist
95  std::vector<BoundaryID> old_boundary_ids(num_boundaries, Moose::INVALID_BOUNDARY_ID);
96  std::vector<std::string> old_boundary_names(num_boundaries);
97  std::stringstream missing_boundary;
98  for (const auto i : make_range(num_boundaries))
99  {
100  const BoundaryName & name = _old_boundary[i];
101 
102  // Convert the BoundaryName to an id and store
103  const auto id = MooseMeshUtils::getBoundaryID(name, *mesh);
104  old_boundary_ids[i] = id;
105 
106  // Boundary does not exist - store for a future error
107  if (!boundary_ids.count(id))
108  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  if (is_boundary_id(name))
113  {
114  old_boundary_names[i] = boundary_info.get_sideset_name(id);
115  if (old_boundary_names[i].empty())
116  old_boundary_names[i] = boundary_info.get_nodeset_name(id);
117  }
118  // If this BoundaryName is a name, use said name
119  else
120  old_boundary_names[i] = name;
121  }
122  if (missing_boundary.str().size())
123  paramError("old_boundary",
124  "The following boundaries were requested to be renamed, but do not exist: ",
125  missing_boundary.str());
126 
127  // Get the boundary IDs that we're moving to
128  std::vector<BoundaryID> new_boundary_ids(num_boundaries, Moose::INVALID_BOUNDARY_ID);
129  std::map<BoundaryID, std::string> new_names;
130  for (const auto i : make_range(num_boundaries))
131  {
132  const BoundaryName & name = _new_boundary[i];
133 
134  // If the user input an ID, we have the ID
135  if (is_boundary_id(name))
136  {
137  const auto id = MooseMeshUtils::getBoundaryID(name, *mesh);
138  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  boundary_ids.insert(id);
143 
144  // Preserve the old boundary name if there was one
145  if (old_boundary_names[i].size())
146  {
147  // if the name was the same as the ID, change the name as well
148  if (old_boundary_names[i] == std::to_string(old_boundary_ids[i]))
149  new_names[id] = std::to_string(new_boundary_ids[i]);
150  else
151  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  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  for (const auto map : {&boundary_info.set_sideset_name_map(),
164  &boundary_info.set_nodeset_name_map(),
165  &new_names})
166  for (const auto & id_name_pair : *map)
167  if (!name_already_exists && id_name_pair.second == name)
168  {
169  new_boundary_ids[i] = id_name_pair.first;
170  new_names[id_name_pair.first] = name;
171  name_already_exists = true;
172  }
173 
174  // Target name doesn't exist, so use the source id/name
175  if (!name_already_exists)
176  {
177  new_boundary_ids[i] = old_boundary_ids[i];
178  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  auto temp_new_boundary_ids = new_boundary_ids;
195  std::vector<std::pair<BoundaryID, BoundaryID>> temp_change_ids;
196  // Loop through all new IDs
197  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  for (const auto old_i : make_range(new_i + 1, num_boundaries))
203  if (new_boundary_ids[new_i] == old_boundary_ids[old_i])
204  {
205  const auto temp_id = get_unused_boundary_id();
206  temp_change_ids.emplace_back(temp_id, new_boundary_ids[new_i]);
207  temp_new_boundary_ids[new_i] = temp_id;
208  break;
209  }
210  }
211 
212  // First pass through changing the boundary ids
213  for (const auto i : make_range(num_boundaries))
214  MeshTools::Modification::change_boundary_id(
215  *mesh, old_boundary_ids[i], temp_new_boundary_ids[i]);
216 
217  // Pass through moving the temproraries to the actual boundaries, if necessary
218  for (const auto & pair : temp_change_ids)
219  MeshTools::Modification::change_boundary_id(*mesh, pair.first, pair.second);
220 
221  // First go through and remove all of the old names
222  for (const auto i : make_range(num_boundaries))
223  {
224  if (boundary_info.get_sideset_name_map().count(old_boundary_ids[i]))
225  boundary_info.set_sideset_name_map().erase(old_boundary_ids[i]);
226  if (boundary_info.get_nodeset_name_map().count(old_boundary_ids[i]))
227  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  for (const auto & pair : new_names)
232  {
233  boundary_info.sideset_name(pair.first) = pair.second;
234  boundary_info.nodeset_name(pair.first) = pair.second;
235  }
236 
237  mesh->set_isnt_prepared();
238  return dynamic_pointer_cast<MeshBase>(mesh);
239 }
std::unique_ptr< MeshBase > & _input
MeshGenerator for re-numbering or re-naming boundaries.
const BoundaryID INVALID_BOUNDARY_ID
Definition: MooseTypes.C:22
std::vector< BoundaryName > _old_boundary
The old boundaries.
MeshBase & mesh
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
std::unique_ptr< T_DEST, T_DELETER > dynamic_pointer_cast(std::unique_ptr< T_SRC, T_DELETER > &src)
These are reworked from https://stackoverflow.com/a/11003103.
std::vector< BoundaryName > _new_boundary
The new boundaries.
registerMooseObject("MooseApp", RenameBoundaryGenerator)
virtual const std::string & name() const
Get the name of the class.
Definition: MooseBase.h:57
void addRequiredParam(const std::string &name, const std::string &doc_string)
This method adds a parameter and documentation string to the InputParameters object that will be extr...
BoundaryID getBoundaryID(const BoundaryName &boundary_name, const MeshBase &mesh)
Gets the boundary ID associated with the given BoundaryName.
boundary_id_type BoundaryID
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
static InputParameters validParams()
Definition: MeshGenerator.C:23
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
static InputParameters validParams()
IntRange< T > make_range(T beg, T end)
RenameBoundaryGenerator(const InputParameters &parameters)
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type.
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump...
bool isDigits(const std::string &str)
Courtesy https://stackoverflow.com/a/8889045 and https://en.cppreference.com/w/cpp/string/byte/isdigi...
Definition: MooseUtils.h:1208
MeshGenerators are objects that can modify or add to an existing mesh.
Definition: MeshGenerator.h:32