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 15890 : RenameBoundaryGenerator::validParams() 23 : { 24 15890 : InputParameters params = MeshGenerator::validParams(); 25 : 26 15890 : params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify"); 27 : 28 15890 : 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 15890 : 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 15890 : 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 15890 : return params; 43 0 : } 44 : 45 797 : RenameBoundaryGenerator::RenameBoundaryGenerator(const InputParameters & params) 46 797 : : MeshGenerator(params), _input(getMesh("input")) 47 : { 48 797 : _old_boundary = getParam<std::vector<BoundaryName>>("old_boundary"); 49 : 50 797 : _new_boundary = getParam<std::vector<BoundaryName>>("new_boundary"); 51 : 52 797 : if (_old_boundary.size() != _new_boundary.size()) 53 4 : paramError("new_boundary", "Must be the same length as 'old_boundary'"); 54 793 : } 55 : 56 : std::unique_ptr<MeshBase> 57 758 : RenameBoundaryGenerator::generate() 58 : { 59 758 : std::unique_ptr<MeshBase> mesh = std::move(_input); 60 : 61 758 : 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 758 : 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 758 : 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 252 : auto get_unused_boundary_id = [this, &boundary_ids, &boundary_info]() 73 : { 74 156 : for (BoundaryID id = 0; id != Moose::INVALID_BOUNDARY_ID; ++id) 75 : { 76 180 : if (!boundary_ids.count(id) && !boundary_info.get_sideset_name_map().count(id) && 77 24 : !boundary_info.get_nodeset_name_map().count(id)) 78 : { 79 24 : boundary_ids.insert(id); 80 24 : return id; 81 : } 82 : } 83 : 84 0 : mooseError("Failed to find an unused ID!"); 85 758 : }; 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 5884 : const auto is_boundary_id = [](const BoundaryName & boundary_name) 90 5884 : { return MooseUtils::isDigits(boundary_name); }; 91 : 92 758 : const auto num_boundaries = _old_boundary.size(); 93 : 94 : // Get the old boundary IDs and make sure they exist 95 758 : std::vector<BoundaryID> old_boundary_ids(num_boundaries, Moose::INVALID_BOUNDARY_ID); 96 758 : std::vector<std::string> old_boundary_names(num_boundaries); 97 758 : std::stringstream missing_boundary; 98 3706 : for (const auto i : make_range(num_boundaries)) 99 : { 100 2948 : const BoundaryName & name = _old_boundary[i]; 101 : 102 : // Convert the BoundaryName to an id and store 103 2948 : const auto id = MooseMeshUtils::getBoundaryID(name, *mesh); 104 2948 : old_boundary_ids[i] = id; 105 : 106 : // Boundary does not exist - store for a future error 107 2948 : 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 2948 : if (is_boundary_id(name)) 113 : { 114 2397 : old_boundary_names[i] = boundary_info.get_sideset_name(id); 115 2397 : 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 551 : old_boundary_names[i] = name; 121 : } 122 758 : 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 754 : std::vector<BoundaryID> new_boundary_ids(num_boundaries, Moose::INVALID_BOUNDARY_ID); 129 754 : std::map<BoundaryID, std::string> new_names; 130 3686 : for (const auto i : make_range(num_boundaries)) 131 : { 132 2936 : const BoundaryName & name = _new_boundary[i]; 133 : 134 : // If the user input an ID, we have the ID 135 2936 : if (is_boundary_id(name)) 136 : { 137 1492 : const auto id = MooseMeshUtils::getBoundaryID(name, *mesh); 138 1488 : 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 1488 : boundary_ids.insert(id); 143 : 144 : // Preserve the old boundary name if there was one 145 1488 : if (old_boundary_names[i].size()) 146 : { 147 : // if the name was the same as the ID, change the name as well 148 1488 : if (old_boundary_names[i] == std::to_string(old_boundary_ids[i])) 149 12 : new_names[id] = std::to_string(new_boundary_ids[i]); 150 : else 151 1476 : 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 1444 : 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 5776 : for (const auto map : {&boundary_info.set_sideset_name_map(), 164 1444 : &boundary_info.set_nodeset_name_map(), 165 7220 : &new_names}) 166 19624 : for (const auto & id_name_pair : *map) 167 15292 : if (!name_already_exists && id_name_pair.second == name) 168 : { 169 12 : new_boundary_ids[i] = id_name_pair.first; 170 12 : new_names[id_name_pair.first] = name; 171 12 : name_already_exists = true; 172 : } 173 : 174 : // Target name doesn't exist, so use the source id/name 175 1444 : if (!name_already_exists) 176 : { 177 1432 : new_boundary_ids[i] = old_boundary_ids[i]; 178 1432 : 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 750 : auto temp_new_boundary_ids = new_boundary_ids; 195 750 : std::vector<std::pair<BoundaryID, BoundaryID>> temp_change_ids; 196 : // Loop through all new IDs 197 3682 : 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 7885 : for (const auto old_i : make_range(new_i + 1, num_boundaries)) 203 4977 : if (new_boundary_ids[new_i] == old_boundary_ids[old_i]) 204 : { 205 24 : const auto temp_id = get_unused_boundary_id(); 206 24 : temp_change_ids.emplace_back(temp_id, new_boundary_ids[new_i]); 207 24 : temp_new_boundary_ids[new_i] = temp_id; 208 24 : break; 209 : } 210 : } 211 : 212 : // First pass through changing the boundary ids 213 3682 : for (const auto i : make_range(num_boundaries)) 214 2932 : MeshTools::Modification::change_boundary_id( 215 2932 : *mesh, old_boundary_ids[i], temp_new_boundary_ids[i]); 216 : 217 : // Pass through moving the temproraries to the actual boundaries, if necessary 218 774 : for (const auto & pair : temp_change_ids) 219 24 : MeshTools::Modification::change_boundary_id(*mesh, pair.first, pair.second); 220 : 221 : // First go through and remove all of the old names 222 3682 : for (const auto i : make_range(num_boundaries)) 223 : { 224 2932 : if (boundary_info.get_sideset_name_map().count(old_boundary_ids[i])) 225 1492 : boundary_info.set_sideset_name_map().erase(old_boundary_ids[i]); 226 2932 : if (boundary_info.get_nodeset_name_map().count(old_boundary_ids[i])) 227 1434 : 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 3550 : for (const auto & pair : new_names) 232 : { 233 2800 : boundary_info.sideset_name(pair.first) = pair.second; 234 2800 : boundary_info.nodeset_name(pair.first) = pair.second; 235 : } 236 : 237 750 : mesh->set_isnt_prepared(); 238 1500 : return dynamic_pointer_cast<MeshBase>(mesh); 239 750 : }