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