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 : }