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 "MeshRepairGenerator.h" 11 : #include "CastUniquePointer.h" 12 : #include "MooseMeshUtils.h" 13 : 14 : #include "libmesh/mesh_tools.h" 15 : #include "libmesh/mesh_modification.h" 16 : 17 : registerMooseObject("MooseApp", MeshRepairGenerator); 18 : 19 : InputParameters 20 3198 : MeshRepairGenerator::validParams() 21 : { 22 : 23 3198 : InputParameters params = MeshGenerator::validParams(); 24 6396 : params.addClassDescription( 25 : "Mesh generator to perform various improvement / fixing operations on an input mesh"); 26 12792 : params.addRequiredParam<MeshGeneratorName>("input", 27 : "Name of the mesh generator providing the mesh"); 28 : 29 12792 : params.addParam<bool>("fix_node_overlap", false, "Whether to merge overlapping nodes"); 30 9594 : params.addParam<Real>( 31 6396 : "node_overlap_tol", 1e-8, "Absolute tolerance for merging overlapping nodes"); 32 : 33 9594 : params.addParam<bool>( 34 6396 : "fix_elements_orientation", false, "Whether to flip elements with negative volumes"); 35 : 36 9594 : params.addParam<bool>("separate_blocks_by_element_types", 37 6396 : false, 38 : "Create new blocks if multiple element types are present in a block"); 39 : 40 9594 : params.addParam<bool>("merge_boundary_ids_with_same_name", 41 6396 : false, 42 : "Merge boundaries if they have the same name but different boundary IDs"); 43 : 44 6396 : params.addParam<bool>( 45 : "renumber_contiguously", 46 6396 : false, 47 : "Whether to renumber the elements of the mesh so the numbering is contiguous"); 48 : 49 3198 : return params; 50 0 : } 51 : 52 67 : MeshRepairGenerator::MeshRepairGenerator(const InputParameters & parameters) 53 : : MeshGenerator(parameters), 54 67 : _input(getMesh("input")), 55 134 : _fix_overlapping_nodes(getParam<bool>("fix_node_overlap")), 56 134 : _node_overlap_tol(getParam<Real>("node_overlap_tol")), 57 134 : _fix_element_orientation(getParam<bool>("fix_elements_orientation")), 58 134 : _elem_type_separation(getParam<bool>("separate_blocks_by_element_types")), 59 201 : _boundary_id_merge(getParam<bool>("merge_boundary_ids_with_same_name")) 60 : { 61 38 : if (!_fix_overlapping_nodes && !_fix_element_orientation && !_elem_type_separation && 62 121 : !_boundary_id_merge && !getParam<bool>("renumber_contiguously")) 63 0 : mooseError("No specific item to fix. Are any of the parameters misspelled?"); 64 67 : } 65 : 66 : std::unique_ptr<MeshBase> 67 60 : MeshRepairGenerator::generate() 68 : { 69 60 : std::unique_ptr<MeshBase> mesh = std::move(_input); 70 : 71 : // We're trying to repair a potentially broken mesh; we'll just 72 : // start with a full prepare rather than trying to be efficient and 73 : // risking missing something. 74 60 : mesh->prepare_for_use(); 75 : 76 : // Blanket ban on distributed. This can be relaxed for some operations if needed 77 60 : if (!mesh->is_serial()) 78 0 : mooseError("MeshRepairGenerator requires a serial mesh. The mesh should not be distributed."); 79 : 80 60 : if (_fix_overlapping_nodes) 81 25 : fixOverlappingNodes(mesh); 82 : 83 : // Flip orientation of elements to keep positive volumes 84 60 : if (_fix_element_orientation) 85 9 : MeshTools::Modification::orient_elements(*mesh); 86 : 87 : // Disambiguate any block that has elements of multiple types 88 60 : if (_elem_type_separation) 89 9 : separateSubdomainsByElementType(mesh); 90 : 91 : // Assign a single boundary ID to boundaries that have the same name 92 60 : if (_boundary_id_merge) 93 9 : MooseMeshUtils::mergeBoundaryIDsWithSameName(*mesh); 94 : 95 : // Renumber the mesh despite any mesh flag 96 180 : if (getParam<bool>("renumber_contiguously")) 97 : { 98 8 : const auto prev_status = mesh->allow_renumbering(); 99 8 : mesh->allow_renumbering(true); 100 8 : mesh->renumber_nodes_and_elements(); 101 8 : mesh->allow_renumbering(prev_status); 102 : } 103 : 104 60 : mesh->unset_is_prepared(); 105 60 : return mesh; 106 0 : } 107 : 108 : void 109 25 : MeshRepairGenerator::fixOverlappingNodes(std::unique_ptr<MeshBase> & mesh) const 110 : { 111 25 : unsigned int num_fixed_nodes = 0; 112 25 : auto pl = mesh->sub_point_locator(); 113 25 : pl->set_close_to_point_tol(_node_overlap_tol); 114 : 115 25 : std::unordered_set<dof_id_type> nodes_removed; 116 : // loop on nodes 117 517 : for (auto & node : mesh->node_ptr_range()) 118 : { 119 : // this node has already been removed 120 246 : if (nodes_removed.count(node->id())) 121 66 : continue; 122 : 123 : // find all the elements around this node 124 180 : std::set<const Elem *> elements; 125 180 : (*pl)(*node, elements); 126 : 127 426 : for (auto & elem : elements) 128 : { 129 246 : bool found = false; 130 1103 : for (auto & elem_node : elem->node_ref_range()) 131 : { 132 1037 : if (node->id() == elem_node.id()) 133 : { 134 180 : found = true; 135 180 : break; 136 : } 137 : } 138 246 : if (!found) 139 : { 140 440 : for (auto & elem_node : elem->node_ref_range()) 141 : { 142 374 : if (elem_node.id() == node->id()) 143 0 : continue; 144 374 : const Real tol = _node_overlap_tol; 145 : // Compares the coordinates 146 374 : const auto x_node = (*node)(0); 147 374 : const auto x_elem_node = elem_node(0); 148 374 : const auto y_node = (*node)(1); 149 374 : const auto y_elem_node = elem_node(1); 150 374 : const auto z_node = (*node)(2); 151 374 : const auto z_elem_node = elem_node(2); 152 : 153 374 : if (MooseUtils::absoluteFuzzyEqual(x_node, x_elem_node, tol) && 154 472 : MooseUtils::absoluteFuzzyEqual(y_node, y_elem_node, tol) && 155 98 : MooseUtils::absoluteFuzzyEqual(z_node, z_elem_node, tol)) 156 : { 157 : // Merging two nodes from the same element is almost never a good idea 158 66 : if (elem->get_node_index(node) != libMesh::invalid_uint) 159 : { 160 0 : _console << "Two overlapping nodes in element " << elem->id() << " right by " 161 0 : << elem->vertex_average() << ".\n They will not be stitched" << std::endl; 162 0 : continue; 163 : } 164 : 165 : // Coordinates are the same but it's not the same node 166 : // Replace the node in the element 167 66 : const_cast<Elem *>(elem)->set_node(elem->get_node_index(&elem_node), node); 168 66 : nodes_removed.insert(elem_node.id()); 169 : 170 66 : num_fixed_nodes++; 171 66 : if (num_fixed_nodes < 10) 172 66 : _console << "Stitching nodes " << *node << " and " << elem_node 173 66 : << std::endl; 174 0 : else if (num_fixed_nodes == 10) 175 0 : _console << "Node stitching will now proceed silently." << std::endl; 176 : } 177 : } 178 : } 179 : } 180 205 : } 181 25 : _console << "Number of overlapping nodes which got merged: " << num_fixed_nodes << std::endl; 182 25 : if (mesh->allow_renumbering()) 183 9 : mesh->renumber_nodes_and_elements(); 184 : else 185 : { 186 16 : mesh->remove_orphaned_nodes(); 187 16 : mesh->update_parallel_id_counts(); 188 : } 189 25 : } 190 : 191 : void 192 9 : MeshRepairGenerator::separateSubdomainsByElementType(std::unique_ptr<MeshBase> & mesh) const 193 : { 194 9 : std::set<subdomain_id_type> ids; 195 9 : mesh->subdomain_ids(ids); 196 : // loop on sub-domain 197 18 : for (const auto id : ids) 198 : { 199 : // Gather all the element types and blocks 200 : // ElemType defines an enum for geometric element types 201 9 : std::set<ElemType> types; 202 : // loop on elements within this sub-domain 203 27 : for (auto & elem : mesh->active_subdomain_elements_ptr_range(id)) 204 27 : types.insert(elem->type()); 205 : 206 : // This call must be performed on all processes 207 9 : auto next_block_id = MooseMeshUtils::getNextFreeSubdomainID(*mesh); 208 : 209 9 : if (types.size() > 1) 210 : { 211 9 : subdomain_id_type i = 0; 212 27 : for (const auto type : types) 213 : { 214 18 : auto new_id = next_block_id + i++; 215 : // Create blocks when a block has multiple element types 216 18 : mesh->subdomain_name(new_id) = mesh->subdomain_name(id) + "_" + Moose::stringify(type); 217 : 218 : // Re-assign elements to the new blocks 219 72 : for (auto elem : mesh->active_subdomain_elements_ptr_range(id)) 220 27 : if (elem->type() == type) 221 36 : elem->subdomain_id() = new_id; 222 : } 223 : } 224 9 : } 225 9 : }