www.mooseframework.org
RenameBoundaryGenerator.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://www.mooseframework.org
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  params.addDeprecatedParam<std::vector<BoundaryID>>(
28  "old_boundary_id",
29  "Elements with these boundary ID(s) will be given the new boundary ID(s) and/or name(s). You "
30  "must supply either 'old_boundary_id' or 'old_boundary_name'.",
31  "Use 'old_boundary' instead of 'old_boundary_id'.");
32  params.addDeprecatedParam<std::vector<BoundaryName>>(
33  "old_boundary_name",
34  "Elements with these boundary name(s) will be given the new boundary ID(s) and/or name(s). "
35  "You must supply either 'old_boundary_id' or 'old_boundary_name'.",
36  "Use 'old_boundary' instead of 'old_boundary_name'");
37  params.addDeprecatedParam<std::vector<BoundaryID>>(
38  "new_boundary_id",
39  "The new boundary ID(s) for the elements defined by "
40  "'old_boundary_id' or 'old_boundary_name'.",
41  "Use 'new_boundary' instead of 'new_boundary_id'.");
42  params.addDeprecatedParam<std::vector<BoundaryName>>(
43  "new_boundary_name",
44  "The new boundary name(s) for the elements defined by "
45  "'old_boundary_id' or 'old_boundary_name'. If 'new_boundary_id' is not provided and a "
46  "boundary with the given name does not exist, a new boundary ID will be created.",
47  "Use 'new_boundary' instead of 'new_boundary_name'.");
48 
49  params.addParam<std::vector<BoundaryName>>(
50  "old_boundary",
51  "Elements with these boundary ID(s)/name(s) will be given the new boundary information "
52  "specified in 'new_boundary'");
53  params.addParam<std::vector<BoundaryName>>(
54  "new_boundary",
55  "The new boundary ID(s)/name(s) to be given by the boundary elements defined in "
56  "'old_boundary'.");
57 
58  params.addClassDescription(
59  "Changes the boundary IDs and/or boundary names for a given set of "
60  "boundaries defined by either boundary ID or boundary name. The "
61  "changes are independent of ordering. The merging of boundaries is supported.");
62 
63  return params;
64 }
65 
67  : MeshGenerator(params), _input(getMesh("input"))
68 {
69  if (isParamValid("old_boundary_id") && isParamValid("old_boundary_name"))
70  paramError("old_boundary_id",
71  "Cannot use in combination with 'old_boundary_name'. Please use 'old_boundary' "
72  "instead; 'old_boundary_id' and 'old_boundary_name' are deprecated.");
73  if (isParamValid("new_boundary_id") && isParamValid("new_boundary_name"))
74  paramError("new_boundary_id",
75  "Cannot use in combination with 'new_boundary_name'. Please use 'new_boundary' "
76  "instead; 'new_boundary_id' and 'new_boundary_name' are deprecated.");
77 
78  if (isParamValid("old_boundary_id") && isParamValid("old_boundary"))
79  paramError("old_boundary_id",
80  "Cannot use with 'old_boundary'. Use only 'old_boundary'; 'old_boundary_id' is "
81  "deprecated.");
82  if (isParamValid("old_boundary_name") && isParamValid("old_boundary"))
83  paramError(
84  "old_boundary_name",
85  "Cannot use with 'old_boundary_name'. Use only 'old_boundary'; 'old_boundary_id_name' is "
86  "deprecated.");
87 
88  if (params.isParamValid("old_boundary"))
89  {
90  _old_boundary = getParam<std::vector<BoundaryName>>("old_boundary");
91  _old_boundary_param_name = "old_boundary";
92  }
93  else if (params.isParamValid("old_boundary_id"))
94  {
95  for (const auto id : getParam<std::vector<BoundaryID>>("old_boundary_id"))
96  _old_boundary.push_back(std::to_string(id));
97  _old_boundary_param_name = "old_boundary_id";
98  }
99  else
100  {
101  _old_boundary = getParam<std::vector<BoundaryName>>("old_boundary_name");
102  _old_boundary_param_name = "old_boundary_name";
103  }
104 
105  if (params.isParamValid("new_boundary"))
106  {
107  _new_boundary = getParam<std::vector<BoundaryName>>("new_boundary");
108  _new_boundary_param_name = "new_boundary";
109  }
110  else if (params.isParamValid("new_boundary_id"))
111  {
112  for (const auto id : getParam<std::vector<BoundaryID>>("new_boundary_id"))
113  _new_boundary.push_back(std::to_string(id));
114  _new_boundary_param_name = "new_boundary_id";
115  }
116  else
117  {
118  _new_boundary = getParam<std::vector<BoundaryName>>("new_boundary_name");
119  _new_boundary_param_name = "new_boundary_name";
120  }
121 
122  if (_old_boundary.size() != _new_boundary.size())
123  paramError(
124  _new_boundary_param_name, "Must be the same length as '", _old_boundary_param_name, "'");
125 }
126 
127 std::unique_ptr<MeshBase>
129 {
130  std::unique_ptr<MeshBase> mesh = std::move(_input);
131 
132  auto & boundary_info = mesh->get_boundary_info();
133 
134  // Get the current boundary IDs - take a copy so that we can also use it
135  // to keep track of boundaries that we add before adding them to nodes/sides
136  std::set<BoundaryID> boundary_ids = boundary_info.get_boundary_ids();
137  // Take the union just in case someone else has added new boundaries in a
138  // non-replicated manner
139  mesh->comm().set_union(boundary_ids);
140 
141  // Helper for getting an unused boundary ID, and keeping track of it
142  // so that we can generate more later
143  auto get_unused_boundary_id = [this, &boundary_ids, &boundary_info]()
144  {
145  for (BoundaryID id = 0; id != Moose::INVALID_BOUNDARY_ID; ++id)
146  {
147  if (!boundary_ids.count(id) && !boundary_info.get_sideset_name_map().count(id) &&
148  !boundary_info.get_nodeset_name_map().count(id))
149  {
150  boundary_ids.insert(id);
151  return id;
152  }
153  }
154 
155  mooseError("Failed to find an unused ID!");
156  };
157 
158  // Helper for checking whether or not a BoundaryName (which could be an ID or a name)
159  // is really input as an ID
160  const auto is_boundary_id = [](const BoundaryName & boundary_name)
161  { return MooseUtils::isDigits(boundary_name); };
162 
163  const auto num_boundaries = _old_boundary.size();
164 
165  // Get the old boundary IDs and make sure they exist
166  std::vector<BoundaryID> old_boundary_ids(num_boundaries, Moose::INVALID_BOUNDARY_ID);
167  std::vector<std::string> old_boundary_names(num_boundaries);
168  std::stringstream missing_boundary;
169  for (std::size_t i = 0; i < num_boundaries; ++i)
170  {
171  const BoundaryName & name = _old_boundary[i];
172 
173  // Convert the BoundaryName to an id and store
174  const auto id = MooseMeshUtils::getBoundaryID(name, *mesh);
175  old_boundary_ids[i] = id;
176 
177  // Boundary does not exist - store for a future error
178  if (!boundary_ids.count(id))
179  missing_boundary << name << " ";
180 
181  // Keep track of the boundary names
182  // If this BoundaryName is an ID, try to see if it has a name set
183  if (is_boundary_id(name))
184  {
185  old_boundary_names[i] = boundary_info.get_sideset_name(id);
186  if (old_boundary_names[i].empty())
187  old_boundary_names[i] = boundary_info.get_nodeset_name(id);
188  }
189  // If this BoundaryName is a name, use said name
190  else
191  old_boundary_names[i] = name;
192  }
193  if (missing_boundary.str().size())
195  "The following boundaries were requested to be renamed, but do not exist: ",
196  missing_boundary.str());
197 
198  // Get the boundary IDs that we're moving to
199  std::vector<BoundaryID> new_boundary_ids(num_boundaries, Moose::INVALID_BOUNDARY_ID);
200  std::map<BoundaryID, std::string> new_names;
201  for (std::size_t i = 0; i < num_boundaries; ++i)
202  {
203  const BoundaryName & name = _new_boundary[i];
204 
205  // If the user input an ID, we have the ID
206  if (is_boundary_id(name))
207  {
208  const auto id = MooseMeshUtils::getBoundaryID(name, *mesh);
209  new_boundary_ids[i] = id;
210 
211  // In the case that this is a new boundary ID, keep track of it so that we
212  // don't reuse it if we have to create temproraries
213  boundary_ids.insert(id);
214 
215  // Preserve the old boundary name if there was one
216  if (old_boundary_names[i].size())
217  {
218  // if the name was the same as the ID, change the name as well
219  if (old_boundary_names[i] == std::to_string(old_boundary_ids[i]))
220  new_names[id] = std::to_string(new_boundary_ids[i]);
221  else
222  new_names[id] = old_boundary_names[i];
223  }
224  }
225  // If the user input a name, we will use the ID that it is coming from for the
226  // "new" name if the new name does not name a current boundary. If the name does
227  // exist, we will merge with said boundary.
228  else
229  {
230  bool name_already_exists = false;
231 
232  // If the target boundary already exists, merge into that one
233  // Check both the old maps and the new map
234  for (const auto map : {&boundary_info.set_sideset_name_map(),
235  &boundary_info.set_nodeset_name_map(),
236  &new_names})
237  for (const auto & id_name_pair : *map)
238  if (!name_already_exists && id_name_pair.second == name)
239  {
240  new_boundary_ids[i] = id_name_pair.first;
241  new_names[id_name_pair.first] = name;
242  name_already_exists = true;
243  }
244 
245  // Target name doesn't exist, so use the source id/name
246  if (!name_already_exists)
247  {
248  new_boundary_ids[i] = old_boundary_ids[i];
249  new_names[new_boundary_ids[i]] = name;
250  }
251  }
252  }
253 
254  // Create temproraries if needed; recall that this generator is independent
255  // of input ordering and does _not_ merge sidesets.
256  //
257  // Take the example where we want to move 0 -> 1 and 1 -> 2. If we just
258  // move them in order, we will actually end up with (0, 1) -> 2. This is
259  // bad. In this case, we want to first make a temprorary for 1 (call it 3).
260  // We then do: 0 -> 3, 1 -> 2, 3 -> 1 in order to get the desired behavior.
261  // We will accomplish this by creating temproraries as needed, modifying
262  // the initial move to the temproraries as needed, and then moving the
263  // temproraries back. temp_change_ids here are the (from -> to) pairs
264  // that we will move at the end.
265  auto temp_new_boundary_ids = new_boundary_ids;
266  std::vector<std::pair<BoundaryID, BoundaryID>> temp_change_ids;
267  // Loop through all new IDs
268  for (std::size_t new_i = 0; new_i < num_boundaries; ++new_i)
269  {
270  // Look at all of the old IDs that will be moved after the move to the new ID.
271  // If any of the old IDs after are IDs that we are moving to, create a temprorary
272  // and keep track of it so we can move it back at the end.
273  for (std::size_t old_i = new_i + 1; old_i < num_boundaries; ++old_i)
274  if (new_boundary_ids[new_i] == old_boundary_ids[old_i])
275  {
276  const auto temp_id = get_unused_boundary_id();
277  temp_change_ids.emplace_back(temp_id, new_boundary_ids[new_i]);
278  temp_new_boundary_ids[new_i] = temp_id;
279  break;
280  }
281  }
282 
283  // First pass through changing the boundary ids
284  for (std::size_t i = 0; i < num_boundaries; ++i)
285  MeshTools::Modification::change_boundary_id(
286  *mesh, old_boundary_ids[i], temp_new_boundary_ids[i]);
287 
288  // Pass through moving the temproraries to the actual boundaries, if necessary
289  for (const auto & pair : temp_change_ids)
290  MeshTools::Modification::change_boundary_id(*mesh, pair.first, pair.second);
291 
292  // First go through and remove all of the old names
293  for (std::size_t i = 0; i < num_boundaries; ++i)
294  {
295  if (boundary_info.get_sideset_name_map().count(old_boundary_ids[i]))
296  boundary_info.set_sideset_name_map().erase(old_boundary_ids[i]);
297  if (boundary_info.get_nodeset_name_map().count(old_boundary_ids[i]))
298  boundary_info.set_nodeset_name_map().erase(old_boundary_ids[i]);
299  }
300 
301  // With the old names removed, add the new names if there are any to add
302  for (const auto & pair : new_names)
303  {
304  boundary_info.sideset_name(pair.first) = pair.second;
305  boundary_info.nodeset_name(pair.first) = pair.second;
306  }
307 
308  mesh->set_isnt_prepared();
309  return dynamic_pointer_cast<MeshBase>(mesh);
310 }
std::unique_ptr< MeshBase > & _input
MeshGenerator for re-numbering or re-naming boundaries.
void addDeprecatedParam(const std::string &name, const T &value, const std::string &doc_string, const std::string &deprecation_message)
const BoundaryID INVALID_BOUNDARY_ID
Definition: MooseTypes.C:24
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:56
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...
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
BoundaryID getBoundaryID(const BoundaryName &boundary_name, const MeshBase &mesh)
Gets the boundary ID associated with the given BoundaryName.
boundary_id_type BoundaryID
const T & getParam(const std::string &name) const
Retrieve a parameter for the object.
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()
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...
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an option parameter and a documentation string to the InputParameters object...
std::string _new_boundary_param_name
The name of the parameter that specifies the new boundaries.
std::string _old_boundary_param_name
The name of the parameter that specifies the old boundaries.
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:1200
MeshGenerators are objects that can modify or add to an existing mesh.
Definition: MeshGenerator.h:32
bool isParamValid(const std::string &name) const
This method returns parameters that have been initialized in one fashion or another, i.e.