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 "MeshCoarseningUtils.h" 11 : #include "Conversion.h" 12 : #include "MooseError.h" 13 : 14 : #include "libmesh/enum_elem_type.h" 15 : #include "libmesh/remote_elem.h" 16 : 17 : using namespace libMesh; 18 : 19 : namespace MeshCoarseningUtils 20 : { 21 : bool 22 11408 : getFineElementsFromInteriorNode(const libMesh::Node & interior_node, 23 : const libMesh::Node & reference_node, 24 : const libMesh::Elem & fine_elem, 25 : std::vector<const libMesh::Node *> & tentative_coarse_nodes, 26 : std::set<const libMesh::Elem *> & fine_elements) 27 : { 28 11408 : const auto elem_type = fine_elem.type(); 29 : 30 : // Add point neighbors of interior node to list of potentially refined elements 31 : // NOTE: we could potentially replace this with a simple call to point_neighbors 32 : // on a fine element with the interior node. It's not clear which approach is more 33 : // resilient to meshes with slits from discarded adaptivity information 34 11408 : fine_elements.insert(&fine_elem); 35 76864 : for (const auto neigh : fine_elem.neighbor_ptr_range()) 36 : { 37 65456 : if (!neigh || neigh == libMesh::remote_elem) 38 24080 : continue; 39 41376 : const auto node_index = neigh->get_node_index(&interior_node); 40 41376 : if (node_index != libMesh::invalid_uint && neigh->is_vertex(node_index)) 41 : { 42 : // Get the neighbor's neighbors, to catch point non-side neighbors 43 : // This is needed in 2D to get all quad neighbors 44 28608 : fine_elements.insert(neigh); 45 195232 : for (const auto neigh_two : neigh->neighbor_ptr_range()) 46 : { 47 166624 : if (!neigh_two || neigh_two == libMesh::remote_elem) 48 53320 : continue; 49 113304 : const auto node_index_2 = neigh_two->get_node_index(&interior_node); 50 113304 : if (node_index_2 != libMesh::invalid_uint && neigh_two->is_vertex(node_index_2)) 51 : { 52 : // Get the neighbor's neighbors 53 75744 : fine_elements.insert(neigh_two); 54 : 55 : // Get the neighbor's neighbors' neighbors, to catch point non-side neighbors 56 : // This is needed for 3D to get all hex neighbors 57 521072 : for (const auto neigh_three : neigh_two->neighbor_ptr_range()) 58 : { 59 445328 : if (!neigh_three || neigh_three == libMesh::remote_elem) 60 132608 : continue; 61 312720 : const auto node_index_3 = neigh_three->get_node_index(&interior_node); 62 312720 : if (node_index_3 != libMesh::invalid_uint && neigh_three->is_vertex(node_index_3)) 63 208008 : fine_elements.insert(neigh_three); 64 : } 65 : } 66 : } 67 : } 68 : } 69 : 70 : // If the fine elements are not all of the same type, we do not know how to get the opposite node 71 : // of the interior node in the fine elements 72 81384 : for (auto elem : fine_elements) 73 69976 : if (elem && fine_elem.type() != elem_type) 74 0 : return false; 75 : 76 11408 : if (elem_type == libMesh::QUAD4 || elem_type == libMesh::QUAD8 || elem_type == libMesh::QUAD9) 77 : { 78 : // We need 4 elements around the interior node 79 1496 : if (fine_elements.size() != 4) 80 472 : return false; 81 : 82 : // We need to order the fine elements so when we get the coarse element nodes they form 83 : // a non-twisted element 84 1024 : tentative_coarse_nodes.resize(4); 85 : 86 : // The exterior nodes are the opposite nodes of the interior_node! 87 1024 : unsigned int neighbor_i = 0; 88 5120 : for (auto neighbor : fine_elements) 89 : { 90 4096 : const auto interior_node_number = neighbor->get_node_index(&interior_node); 91 4096 : unsigned int opposite_node_index = (interior_node_number + 2) % 4; 92 : 93 4096 : tentative_coarse_nodes[neighbor_i++] = neighbor->node_ptr(opposite_node_index); 94 : } 95 : 96 : // Re-order nodes so that they will form a decent quad 97 : libMesh::Point axis = 98 1024 : (fine_elem.vertex_average() - interior_node).cross(interior_node - reference_node); 99 1024 : reorderNodes(tentative_coarse_nodes, interior_node, reference_node, axis); 100 1024 : return true; 101 : } 102 : // For hexes, similar strategy but we need to pick 4 nodes to form a side, then 4 other nodes 103 : // facing those initial nodes 104 9912 : else if (elem_type == libMesh::HEX8) 105 : { 106 : // We need 8 elements around the interior node 107 9912 : if (fine_elements.size() != 8) 108 3584 : return false; 109 : 110 6328 : tentative_coarse_nodes.resize(4); 111 : 112 : // Pick a node (mid-face for the coarse element) to form the base 113 : // We must use the same element reproducibly, despite the pointers being ordered in the set 114 : // We use the element id to choose the same element consistently 115 6328 : const Elem * one_fine_elem = nullptr; 116 6328 : unsigned int max_id = 0; 117 56952 : for (const auto elem_ptr : fine_elements) 118 50624 : if (elem_ptr->id() > max_id) 119 : { 120 44499 : max_id = elem_ptr->id(); 121 44499 : one_fine_elem = elem_ptr; 122 : } 123 6328 : const auto interior_node_index = one_fine_elem->get_node_index(&interior_node); 124 : 125 : // Find any side which contains the interior node 126 6328 : unsigned int an_interior_node_side = 0; 127 6328 : for (const auto s : make_range(one_fine_elem->n_sides())) 128 6328 : if (one_fine_elem->is_node_on_side(interior_node_index, s)) 129 : { 130 6328 : an_interior_node_side = s; 131 6328 : break; 132 : } 133 : // A node near a face of the coarse element seek is on the same side, but opposite from the 134 : // interior node 135 : const auto center_face_node_index = 136 6328 : one_fine_elem->opposite_node(interior_node_index, an_interior_node_side); 137 6328 : const auto center_face_node = one_fine_elem->node_ptr(center_face_node_index); 138 : 139 : // We gather the coarse element nodes from the fine elements that share the center face node we 140 : // just selected 141 6328 : unsigned int neighbor_i = 0; 142 6328 : std::vector<const libMesh::Elem *> other_fine_elems; 143 56952 : for (auto neighbor : fine_elements) 144 : { 145 50624 : if (neighbor->get_node_index(center_face_node) == libMesh::invalid_uint) 146 : { 147 25312 : other_fine_elems.push_back(neighbor); 148 25312 : continue; 149 : } 150 : // The coarse element node is the opposite nodes of the interior_node in a fine element 151 25312 : const auto interior_node_number = neighbor->get_node_index(&interior_node); 152 : unsigned int opposite_node_index = 153 25312 : getOppositeNodeIndex(neighbor->type(), interior_node_number); 154 : 155 25312 : tentative_coarse_nodes[neighbor_i++] = neighbor->node_ptr(opposite_node_index); 156 : } 157 : 158 : // Center face node was not shared with 4 elements 159 : // We could try again on any of the 5 other coarse element faces but we don't insist for now 160 6328 : if (neighbor_i != 4 || other_fine_elems.size() != 4) 161 0 : return false; 162 : 163 : // Sort the coarse nodes so we are reproducibly picking the same ordering of nodes 164 38315 : auto cmp_node = [](const Node * a, const Node * b) { return a->id() < b->id(); }; 165 6328 : std::sort(tentative_coarse_nodes.begin(), tentative_coarse_nodes.end(), cmp_node); 166 : 167 : // Re-order nodes so that they will form a decent quad 168 : // Pick the reference node for the rotation frame as the face center 169 6328 : const libMesh::Point clock_start = *tentative_coarse_nodes[0]; 170 6328 : libMesh::Point axis = interior_node - *center_face_node; 171 6328 : reorderNodes(tentative_coarse_nodes, *center_face_node, clock_start, axis); 172 : 173 : // Look through the 4 other fine elements to finish the coarse hex element nodes 174 31640 : for (const auto coarse_node_index : make_range(4)) 175 : { 176 : // Find the fine element containing each coarse node already found & ordered 177 25312 : const Elem * fine_elem = nullptr; 178 144229 : for (auto elem : fine_elements) 179 144229 : if (elem->get_node_index(tentative_coarse_nodes[coarse_node_index]) != 180 : libMesh::invalid_uint) 181 : { 182 25312 : fine_elem = elem; 183 25312 : break; 184 : } 185 : mooseAssert(fine_elem, "Search for fine element should have worked"); 186 : 187 : // Find the other fine element opposite the element containing the node (they share a side) 188 25312 : const Elem * fine_neighbor = nullptr; 189 126560 : for (auto neighbor : other_fine_elems) 190 : // Side neighbor. This requires the mesh to have correct neighbors 191 : // For meshes with lost AMR information, this wont work 192 101248 : if (neighbor->which_neighbor_am_i(fine_elem) != libMesh::invalid_uint) 193 : { 194 25312 : if (fine_neighbor) 195 0 : mooseError("Found two neighbors"); 196 25312 : fine_neighbor = neighbor; 197 : } 198 : // the fine element in the base of the coarse hex is not a neighbor to any element 199 : // in the top part. The mesh is probably slit in the middle of the potential coarse hex 200 : // element. We wont support this for now. 201 25312 : if (!fine_neighbor) 202 0 : return false; 203 : 204 : // Get the coarse node, opposite the interior node in that fine element 205 25312 : const auto interior_node_index_neighbor = fine_neighbor->get_node_index(&interior_node); 206 25312 : tentative_coarse_nodes.push_back(fine_neighbor->node_ptr( 207 25312 : getOppositeNodeIndex(fine_neighbor->type(), interior_node_index_neighbor))); 208 : } 209 : 210 : // Found 8 fine elements and 8 coarse element nodes as expected 211 6328 : if (tentative_coarse_nodes.size() == 8) 212 6328 : return true; 213 : else 214 0 : return false; 215 6328 : } 216 : else 217 0 : mooseError("Not implemented for element type " + Moose::stringify(elem_type)); 218 : } 219 : 220 : void 221 7440 : reorderNodes(std::vector<const libMesh::Node *> & nodes, 222 : const libMesh::Point & origin, 223 : const libMesh::Point & clock_start, 224 : libMesh::Point & axis) 225 : { 226 : mooseAssert(axis.norm() != 0, "Invalid rotation axis when ordering nodes"); 227 : mooseAssert(origin != clock_start, "Invalid starting direction when ordering nodes"); 228 : 229 : // We'll need to order the coarse nodes based on the clock-wise order of the elements 230 : // Define a frame in which to compute the angles of the fine elements centers 231 : // angle 0 is the [interior node, non-conformal node] vertex 232 7440 : auto start_clock = origin - clock_start; 233 7440 : start_clock /= start_clock.norm(); 234 7440 : axis /= axis.norm(); 235 : 236 7440 : std::vector<std::pair<unsigned int, libMesh::Real>> nodes_angles(nodes.size()); 237 37200 : for (const auto angle_i : index_range(nodes)) 238 : { 239 : mooseAssert(nodes[angle_i], "Nodes cant be nullptr"); 240 29760 : auto vec = *nodes[angle_i] - origin; 241 29760 : vec /= vec.norm(); 242 29760 : const auto angle = atan2(vec.cross(start_clock) * axis, vec * start_clock); 243 29760 : nodes_angles[angle_i] = std::make_pair(angle_i, angle); 244 : } 245 : 246 : // sort by angle, so it goes around the interior node 247 7440 : std::sort(nodes_angles.begin(), 248 : nodes_angles.end(), 249 55546 : [](auto & left, auto & right) { return left.second < right.second; }); 250 : 251 : // Re-sort the nodes based on their angle 252 7440 : std::vector<const libMesh::Node *> new_nodes(nodes.size()); 253 37200 : for (const auto & old_index : index_range(nodes)) 254 29760 : new_nodes[old_index] = nodes[nodes_angles[old_index].first]; 255 37200 : for (const auto & index : index_range(nodes)) 256 29760 : nodes[index] = new_nodes[index]; 257 7440 : } 258 : 259 : unsigned int 260 82520 : getOppositeNodeIndex(libMesh::ElemType elem_type, unsigned int node_index) 261 : { 262 82520 : switch (elem_type) 263 : { 264 2808 : case QUAD4: 265 2808 : return (node_index + 2) % 4; 266 79712 : case HEX8: 267 : { 268 : mooseAssert(node_index < 8, "Node index too high: " + std::to_string(node_index)); 269 79712 : return std::vector<unsigned int>({6, 7, 4, 5, 2, 3, 0, 1})[node_index]; 270 : } 271 0 : default: 272 0 : mooseError("Unsupported element type for retrieving the opposite node"); 273 : } 274 : } 275 : }