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 4888 : RenameBlockGenerator::validParams()
21 : {
22 4888 : InputParameters params = MeshGenerator::validParams();
23 :
24 19552 : params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify");
25 :
26 29328 : 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 29328 : 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 29328 : 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 29328 : 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 19552 : 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 19552 : 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 4888 : 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 4888 : return params;
67 0 : }
68 :
69 913 : RenameBlockGenerator::RenameBlockGenerator(const InputParameters & parameters)
70 2736 : : MeshGenerator(parameters), _input(getMesh("input"))
71 : {
72 2766 : if (isParamValid("old_block_id") && isParamValid("old_block_name"))
73 6 : 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 2757 : if (isParamValid("new_block_id") && isParamValid("new_block_name"))
77 6 : 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 2712 : if (isParamValid("old_block"))
82 : {
83 2622 : if (isParamValid("old_block_id"))
84 6 : paramError("old_block_id",
85 : "Cannot use with 'old_block'. Use only 'old_block'; 'old_block_id' is "
86 : "deprecated.");
87 2613 : if (isParamValid("old_block_name"))
88 9 : paramError("old_block_name",
89 : "Cannot use with 'old_block'. Use only 'old_block'; 'old_block_name' is "
90 : "deprecated.");
91 1736 : _old_block = getParam<std::vector<SubdomainName>>("old_block");
92 868 : _old_block_param_name = "old_block";
93 : }
94 90 : else if (isParamValid("old_block_id"))
95 : {
96 84 : 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 36 : _old_block = isParamValid("old_block_name")
103 60 : ? getParam<std::vector<SubdomainName>>("old_block_name")
104 18 : : std::vector<SubdomainName>{};
105 18 : _old_block_param_name = "old_block_name";
106 : }
107 :
108 898 : std::string new_block_param_name;
109 2694 : if (isParamValid("new_block"))
110 : {
111 2622 : if (isParamValid("new_block_id"))
112 6 : paramError("new_block_id",
113 : "Cannot use with 'new_block'. Use only 'new_block'; 'new_block_id' is "
114 : "deprecated.");
115 2613 : if (isParamValid("new_block_name"))
116 6 : paramError("new_block_name",
117 : "Cannot use with 'new_block'. Use only 'new_block'; 'new_block_name' is "
118 : "deprecated.");
119 1736 : _new_block = getParam<std::vector<SubdomainName>>("new_block");
120 868 : new_block_param_name = "new_block";
121 : }
122 72 : else if (isParamValid("new_block_id"))
123 : {
124 84 : 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 48 : ? getParam<std::vector<SubdomainName>>("new_block_name")
132 12 : : std::vector<SubdomainName>{};
133 12 : new_block_param_name = "new_block_name";
134 : }
135 :
136 892 : if (_old_block.size() != _new_block.size())
137 3 : paramError(new_block_param_name, "Must be the same length as '", _old_block_param_name, "'");
138 889 : }
139 :
140 : std::unique_ptr<MeshBase>
141 681 : RenameBlockGenerator::generate()
142 : {
143 681 : std::unique_ptr<MeshBase> mesh = std::move(_input);
144 :
145 : // MeshBase::subdomain_name will insert, so we need a const ref
146 681 : const MeshBase & const_mesh = *mesh;
147 :
148 : // Get the subdomains in the mesh (this is global)
149 681 : if (!mesh->preparation().has_cached_elem_data)
150 576 : mesh->cache_elem_data();
151 681 : std::set<subdomain_id_type> block_ids;
152 681 : mesh->subdomain_ids(block_ids);
153 :
154 : // Helper for getting an unused block ID, and keeping track of it
155 : // so that we can generate more later
156 44 : auto get_unused_block_id = [this, &block_ids, &const_mesh]()
157 : {
158 286 : for (const auto id : make_range(Moose::INVALID_BLOCK_ID))
159 286 : if (!block_ids.count(id) && !const_mesh.get_subdomain_name_map().count(id))
160 : {
161 44 : block_ids.insert(id);
162 44 : return id;
163 : }
164 :
165 0 : mooseError("Failed to find an unused ID!");
166 681 : };
167 :
168 681 : const auto num_blocks = _old_block.size();
169 :
170 : // Helper for checking whether or not a SubdomainName (which could be an ID or a name)
171 : // is really input as an ID
172 4333 : const auto is_subdomain_id = [](const SubdomainName & subdomain_name)
173 4333 : { return MooseUtils::isDigits(subdomain_name); };
174 :
175 : // Get the old block IDs and make sure they exist
176 1362 : std::vector<SubdomainID> old_block_ids(num_blocks, Moose::INVALID_BLOCK_ID);
177 681 : std::vector<SubdomainName> old_block_names(num_blocks);
178 681 : std::stringstream missing_block;
179 2852 : for (const auto i : make_range(num_blocks))
180 : {
181 2171 : const SubdomainName & name = _old_block[i];
182 :
183 : // Convert the SubdomainName to an id and store
184 2171 : const auto id = MooseMeshUtils::getSubdomainID(name, *mesh);
185 2171 : old_block_ids[i] = id;
186 :
187 : // Block does not exist - store for a future error
188 2171 : if (!block_ids.count(id))
189 6 : missing_block << name << " ";
190 :
191 : // Keep track of the block names
192 : // If this SubdomainName is an ID, try to see if it has a name set
193 2171 : if (is_subdomain_id(name))
194 2036 : old_block_names[i] = const_mesh.subdomain_name(id);
195 : // If this SubdomainName is a name, use said name
196 : else
197 135 : old_block_names[i] = name;
198 : }
199 681 : if (missing_block.str().size())
200 3 : paramError(_old_block_param_name,
201 : "The following blocks were requested to be renamed, but do not exist: ",
202 3 : missing_block.str());
203 :
204 : // Get the block IDs that we're moving to
205 678 : std::vector<SubdomainID> new_block_ids(num_blocks, Moose::INVALID_BLOCK_ID);
206 678 : std::map<SubdomainID, std::string> new_names;
207 2840 : for (const auto i : make_range(num_blocks))
208 : {
209 2162 : const SubdomainName & name = _new_block[i];
210 :
211 : // If the user input an ID, we have the ID
212 2162 : if (is_subdomain_id(name))
213 : {
214 352 : const auto id = MooseMeshUtils::getSubdomainID(name, *mesh);
215 352 : new_block_ids[i] = id;
216 :
217 : // In the case that this is a new block ID, keep track of it so that we
218 : // don't reuse it if we have to create temporaries
219 352 : block_ids.insert(id);
220 :
221 : // Preserve the old block name if there was one
222 352 : if (old_block_names[i].size())
223 154 : new_names[id] = old_block_names[i];
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 block. If the name does
227 : // exist, we will merge with said block.
228 : else
229 : {
230 1810 : bool name_already_exists = false;
231 :
232 : // If the target block already exists, merge into that one
233 : // Check both the old maps and the new map
234 5430 : for (const auto map : {&const_mesh.get_subdomain_name_map(),
235 7240 : const_cast<const std::map<SubdomainID, std::string> *>(&new_names)})
236 47847 : for (const auto & id_name_pair : *map)
237 44227 : if (!name_already_exists && id_name_pair.second == name)
238 : {
239 11 : new_block_ids[i] = id_name_pair.first;
240 11 : new_names[id_name_pair.first] = name;
241 11 : name_already_exists = true;
242 : }
243 :
244 : // Target name doesn't exist, so use the source id/name
245 1810 : if (!name_already_exists)
246 : {
247 1799 : new_block_ids[i] = old_block_ids[i];
248 1799 : new_names[new_block_ids[i]] = name;
249 : }
250 : }
251 : }
252 :
253 : // Create temporaries if needed; recall that this generator is independent
254 : // of input ordering and does _not_ merge subdomains.
255 : //
256 : // Take the example where we want to move 0 -> 1 and 1 -> 2. If we just
257 : // move them in order, we will actually end up with (0, 1) -> 2. This is
258 : // bad. In this case, we want to first make a temporary for 1 (call it 3).
259 : // We then do: 0 -> 3, 1 -> 2, 3 -> 1 in order to get the desired behavior.
260 : // We will accomplish this by creating temporaries as needed, modifying
261 : // the initial move to the temporaries as needed, and then moving the
262 : // temporaries back. temp_change_ids here are the (from -> to) pairs
263 : // that we will move at the end.
264 678 : auto temp_new_block_ids = new_block_ids;
265 678 : std::vector<std::pair<SubdomainID, SubdomainID>> temp_change_ids;
266 : // Loop through all new IDs
267 2840 : for (const auto new_i : make_range(num_blocks))
268 : {
269 : // Look at all of the old IDs that will be moved after the move to the new ID.
270 : // If any of the old IDs after are IDs that we are moving to, create a temporary
271 : // and keep track of it so we can move it back at the end.
272 46124 : for (const auto old_i : make_range(new_i + 1, num_blocks))
273 44006 : if (new_block_ids[new_i] == old_block_ids[old_i])
274 : {
275 44 : const auto temp_id = get_unused_block_id();
276 44 : temp_change_ids.emplace_back(temp_id, new_block_ids[new_i]);
277 44 : temp_new_block_ids[new_i] = temp_id;
278 44 : break;
279 : }
280 : }
281 :
282 : // First pass through changing the block ids
283 2840 : for (const auto i : make_range(num_blocks))
284 2162 : MeshTools::Modification::change_subdomain_id(*mesh, old_block_ids[i], temp_new_block_ids[i]);
285 : // Pass through moving the temporaries to the actual blocks, if necessary
286 722 : for (const auto & pair : temp_change_ids)
287 44 : MeshTools::Modification::change_subdomain_id(*mesh, pair.first, pair.second);
288 :
289 : // First go through and remove all of the old names
290 2840 : for (const auto i : make_range(num_blocks))
291 2162 : if (mesh->get_subdomain_name_map().count(old_block_ids[i]))
292 334 : mesh->set_subdomain_name_map().erase(old_block_ids[i]);
293 : // With the old names removed, add the new names if there are any to add
294 2620 : for (const auto & pair : new_names)
295 1942 : mesh->subdomain_name(pair.first) = pair.second;
296 :
297 678 : mesh->unset_is_prepared();
298 1356 : return dynamic_pointer_cast<MeshBase>(mesh);
299 678 : }
|