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 "RenameBlockGenerator.h"
11 :
12 : #include "CastUniquePointer.h"
13 : #include "MooseMeshUtils.h"
14 :
15 : #include "libmesh/mesh_modification.h"
16 :
17 : registerMooseObject("MooseApp", RenameBlockGenerator);
18 :
19 : InputParameters
20 16027 : RenameBlockGenerator::validParams()
21 : {
22 16027 : InputParameters params = MeshGenerator::validParams();
23 :
24 16027 : params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify");
25 :
26 16027 : params.addDeprecatedParam<std::vector<SubdomainID>>(
27 : "old_block_id",
28 : "Elements with this block number will be given the new_block_number or "
29 : "new_block_name. You must supply either old_block_id or old_block_name. "
30 : "You may supply a vector of old_block_id, in which case the new_block "
31 : "information must also be a vector.",
32 : "Use 'old_block' instead of 'old_block_id'.");
33 16027 : params.addDeprecatedParam<std::vector<SubdomainName>>(
34 : "old_block_name",
35 : "Elements with this block name will be given the new_block_number or "
36 : "new_block_name. You must supply either old_block_id or old_block_name. "
37 : "You may supply a vector of old_block_name, in which case the new_block "
38 : "information must also be a vector.",
39 : "Use 'old_block' instead of 'old_block_name'.");
40 16027 : params.addDeprecatedParam<std::vector<SubdomainID>>(
41 : "new_block_id",
42 : "Elements with the old block number (or name) will be given this block "
43 : "number. If the old blocks are named, their names will be passed onto the "
44 : "newly numbered blocks.",
45 : "Use 'new_block' instead of 'new_block_id'.");
46 16027 : params.addDeprecatedParam<std::vector<SubdomainName>>(
47 : "new_block_name",
48 : "Elements with the old block number (or name) will be given this block "
49 : "name. No change of block ID is performed, unless multiple old blocks are "
50 : "given the same name, in which case they are all given the first old block "
51 : "number.",
52 : "Use 'new_block' instead of 'new_block_name'.");
53 :
54 16027 : params.addParam<std::vector<SubdomainName>>(
55 : "old_block",
56 : "Elements with these block ID(s)/name(s) will be given the new block information specified "
57 : "in 'new_block'");
58 16027 : params.addParam<std::vector<SubdomainName>>(
59 : "new_block",
60 : "The new block ID(s)/name(s) to be given by the elements defined in 'old_block'.");
61 :
62 16027 : params.addClassDescription("Changes the block IDs and/or block names for a given set of blocks "
63 : "defined by either block ID or block name. The changes are "
64 : "independent of ordering. The merging of blocks is supported.");
65 :
66 16027 : return params;
67 0 : }
68 :
69 882 : RenameBlockGenerator::RenameBlockGenerator(const InputParameters & parameters)
70 882 : : MeshGenerator(parameters), _input(getMesh("input"))
71 : {
72 878 : if (isParamValid("old_block_id") && isParamValid("old_block_name"))
73 4 : paramError("old_block_id",
74 : "Cannot use in combination with 'old_block_name'. Please use 'old_block' "
75 : "instead; 'old_block_id' and 'old_block_name' are deprecated.");
76 874 : if (isParamValid("new_block_id") && isParamValid("new_block_name"))
77 4 : paramError("new_block_id",
78 : "Cannot use in combination with 'new_block_name'. Please use 'new_block' "
79 : "instead; 'new_block_id' and 'new_block_name' are deprecated.");
80 :
81 870 : if (isParamValid("old_block"))
82 : {
83 838 : if (isParamValid("old_block_id"))
84 4 : paramError("old_block_id",
85 : "Cannot use with 'old_block'. Use only 'old_block'; 'old_block_id' is "
86 : "deprecated.");
87 834 : if (isParamValid("old_block_name"))
88 4 : paramError("old_block_name",
89 : "Cannot use with 'old_block'. Use only 'old_block'; 'old_block_name' is "
90 : "deprecated.");
91 830 : _old_block = getParam<std::vector<SubdomainName>>("old_block");
92 830 : _old_block_param_name = "old_block";
93 : }
94 32 : else if (isParamValid("old_block_id"))
95 : {
96 60 : for (const auto id : getParam<std::vector<SubdomainID>>("old_block_id"))
97 48 : _old_block.push_back(std::to_string(id));
98 12 : _old_block_param_name = "old_block_id";
99 : }
100 : else
101 : {
102 40 : _old_block = isParamValid("old_block_name")
103 40 : ? getParam<std::vector<SubdomainName>>("old_block_name")
104 20 : : std::vector<SubdomainName>{};
105 20 : _old_block_param_name = "old_block_name";
106 : }
107 :
108 862 : std::string new_block_param_name;
109 862 : if (isParamValid("new_block"))
110 : {
111 838 : if (isParamValid("new_block_id"))
112 4 : paramError("new_block_id",
113 : "Cannot use with 'new_block'. Use only 'new_block'; 'new_block_id' is "
114 : "deprecated.");
115 834 : if (isParamValid("new_block_name"))
116 4 : paramError("new_block_name",
117 : "Cannot use with 'new_block'. Use only 'new_block'; 'new_block_name' is "
118 : "deprecated.");
119 830 : _new_block = getParam<std::vector<SubdomainName>>("new_block");
120 830 : new_block_param_name = "new_block";
121 : }
122 24 : else if (isParamValid("new_block_id"))
123 : {
124 60 : for (const auto id : getParam<std::vector<SubdomainID>>("new_block_id"))
125 48 : _new_block.push_back(std::to_string(id));
126 12 : new_block_param_name = "new_block_id";
127 : }
128 : else
129 : {
130 24 : _new_block = isParamValid("new_block_name")
131 24 : ? getParam<std::vector<SubdomainName>>("new_block_name")
132 12 : : std::vector<SubdomainName>{};
133 12 : new_block_param_name = "new_block_name";
134 : }
135 :
136 854 : if (_old_block.size() != _new_block.size())
137 4 : paramError(new_block_param_name, "Must be the same length as '", _old_block_param_name, "'");
138 850 : }
139 :
140 : std::unique_ptr<MeshBase>
141 675 : RenameBlockGenerator::generate()
142 : {
143 675 : std::unique_ptr<MeshBase> mesh = std::move(_input);
144 :
145 : // MeshBase::subdomain_name will insert, so we need a const ref
146 675 : const MeshBase & const_mesh = *mesh;
147 :
148 : // Get the subdomains in the mesh (this is global)
149 675 : std::set<subdomain_id_type> block_ids;
150 675 : mesh->subdomain_ids(block_ids);
151 :
152 : // Helper for getting an unused block ID, and keeping track of it
153 : // so that we can generate more later
154 418 : auto get_unused_block_id = [this, &block_ids, &const_mesh]()
155 : {
156 286 : for (const auto id : make_range(Moose::INVALID_BLOCK_ID))
157 286 : if (!block_ids.count(id) && !const_mesh.get_subdomain_name_map().count(id))
158 : {
159 44 : block_ids.insert(id);
160 44 : return id;
161 : }
162 :
163 0 : mooseError("Failed to find an unused ID!");
164 675 : };
165 :
166 675 : const auto num_blocks = _old_block.size();
167 :
168 : // Helper for checking whether or not a SubdomainName (which could be an ID or a name)
169 : // is really input as an ID
170 4440 : const auto is_subdomain_id = [](const SubdomainName & subdomain_name)
171 4440 : { return MooseUtils::isDigits(subdomain_name); };
172 :
173 : // Get the old block IDs and make sure they exist
174 675 : std::vector<SubdomainID> old_block_ids(num_blocks, Moose::INVALID_BLOCK_ID);
175 675 : std::vector<SubdomainName> old_block_names(num_blocks);
176 675 : std::stringstream missing_block;
177 2901 : for (const auto i : make_range(num_blocks))
178 : {
179 2226 : const SubdomainName & name = _old_block[i];
180 :
181 : // Convert the SubdomainName to an id and store
182 2226 : const auto id = MooseMeshUtils::getSubdomainID(name, *mesh);
183 2226 : old_block_ids[i] = id;
184 :
185 : // Block does not exist - store for a future error
186 2226 : if (!block_ids.count(id))
187 8 : missing_block << name << " ";
188 :
189 : // Keep track of the block names
190 : // If this SubdomainName is an ID, try to see if it has a name set
191 2226 : if (is_subdomain_id(name))
192 2090 : old_block_names[i] = const_mesh.subdomain_name(id);
193 : // If this SubdomainName is a name, use said name
194 : else
195 136 : old_block_names[i] = name;
196 : }
197 675 : if (missing_block.str().size())
198 4 : paramError(_old_block_param_name,
199 : "The following blocks were requested to be renamed, but do not exist: ",
200 4 : missing_block.str());
201 :
202 : // Get the block IDs that we're moving to
203 671 : std::vector<SubdomainID> new_block_ids(num_blocks, Moose::INVALID_BLOCK_ID);
204 671 : std::map<SubdomainID, std::string> new_names;
205 2885 : for (const auto i : make_range(num_blocks))
206 : {
207 2214 : const SubdomainName & name = _new_block[i];
208 :
209 : // If the user input an ID, we have the ID
210 2214 : if (is_subdomain_id(name))
211 : {
212 309 : const auto id = MooseMeshUtils::getSubdomainID(name, *mesh);
213 309 : new_block_ids[i] = id;
214 :
215 : // In the case that this is a new block ID, keep track of it so that we
216 : // don't reuse it if we have to create temporaries
217 309 : block_ids.insert(id);
218 :
219 : // Preserve the old block name if there was one
220 309 : if (old_block_names[i].size())
221 154 : new_names[id] = old_block_names[i];
222 : }
223 : // If the user input a name, we will use the ID that it is coming from for the
224 : // "new" name if the new name does not name a current block. If the name does
225 : // exist, we will merge with said block.
226 : else
227 : {
228 1905 : bool name_already_exists = false;
229 :
230 : // If the target block already exists, merge into that one
231 : // Check both the old maps and the new map
232 5715 : for (const auto map : {&const_mesh.get_subdomain_name_map(),
233 7620 : const_cast<const std::map<SubdomainID, std::string> *>(&new_names)})
234 53368 : for (const auto & id_name_pair : *map)
235 49558 : if (!name_already_exists && id_name_pair.second == name)
236 : {
237 11 : new_block_ids[i] = id_name_pair.first;
238 11 : new_names[id_name_pair.first] = name;
239 11 : name_already_exists = true;
240 : }
241 :
242 : // Target name doesn't exist, so use the source id/name
243 1905 : if (!name_already_exists)
244 : {
245 1894 : new_block_ids[i] = old_block_ids[i];
246 1894 : new_names[new_block_ids[i]] = name;
247 : }
248 : }
249 : }
250 :
251 : // Create temporaries if needed; recall that this generator is independent
252 : // of input ordering and does _not_ merge subdomains.
253 : //
254 : // Take the example where we want to move 0 -> 1 and 1 -> 2. If we just
255 : // move them in order, we will actually end up with (0, 1) -> 2. This is
256 : // bad. In this case, we want to first make a temporary for 1 (call it 3).
257 : // We then do: 0 -> 3, 1 -> 2, 3 -> 1 in order to get the desired behavior.
258 : // We will accomplish this by creating temporaries as needed, modifying
259 : // the initial move to the temporaries as needed, and then moving the
260 : // temporaries back. temp_change_ids here are the (from -> to) pairs
261 : // that we will move at the end.
262 671 : auto temp_new_block_ids = new_block_ids;
263 671 : std::vector<std::pair<SubdomainID, SubdomainID>> temp_change_ids;
264 : // Loop through all new IDs
265 2885 : for (const auto new_i : make_range(num_blocks))
266 : {
267 : // Look at all of the old IDs that will be moved after the move to the new ID.
268 : // If any of the old IDs after are IDs that we are moving to, create a temporary
269 : // and keep track of it so we can move it back at the end.
270 51387 : for (const auto old_i : make_range(new_i + 1, num_blocks))
271 49217 : if (new_block_ids[new_i] == old_block_ids[old_i])
272 : {
273 44 : const auto temp_id = get_unused_block_id();
274 44 : temp_change_ids.emplace_back(temp_id, new_block_ids[new_i]);
275 44 : temp_new_block_ids[new_i] = temp_id;
276 44 : break;
277 : }
278 : }
279 :
280 : // First pass through changing the block ids
281 2885 : for (const auto i : make_range(num_blocks))
282 2214 : MeshTools::Modification::change_subdomain_id(*mesh, old_block_ids[i], temp_new_block_ids[i]);
283 : // Pass through moving the temporaries to the actual blocks, if necessary
284 715 : for (const auto & pair : temp_change_ids)
285 44 : MeshTools::Modification::change_subdomain_id(*mesh, pair.first, pair.second);
286 :
287 : // First go through and remove all of the old names
288 2885 : for (const auto i : make_range(num_blocks))
289 2214 : if (mesh->get_subdomain_name_map().count(old_block_ids[i]))
290 286 : mesh->set_subdomain_name_map().erase(old_block_ids[i]);
291 : // With the old names removed, add the new names if there are any to add
292 2708 : for (const auto & pair : new_names)
293 2037 : mesh->subdomain_name(pair.first) = pair.second;
294 :
295 671 : mesh->set_isnt_prepared();
296 1342 : return dynamic_pointer_cast<MeshBase>(mesh);
297 671 : }
|