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
17using namespace libMesh;
18
19namespace MeshCoarseningUtils
20{
21bool
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 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 fine_elements.insert(&fine_elem);
35 for (const auto neigh : fine_elem.neighbor_ptr_range())
36 {
37 if (!neigh || neigh == libMesh::remote_elem)
38 continue;
39 const auto node_index = neigh->get_node_index(&interior_node);
40 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 fine_elements.insert(neigh);
45 for (const auto neigh_two : neigh->neighbor_ptr_range())
46 {
47 if (!neigh_two || neigh_two == libMesh::remote_elem)
48 continue;
49 const auto node_index_2 = neigh_two->get_node_index(&interior_node);
50 if (node_index_2 != libMesh::invalid_uint && neigh_two->is_vertex(node_index_2))
51 {
52 // Get the neighbor's neighbors
53 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 for (const auto neigh_three : neigh_two->neighbor_ptr_range())
58 {
59 if (!neigh_three || neigh_three == libMesh::remote_elem)
60 continue;
61 const auto node_index_3 = neigh_three->get_node_index(&interior_node);
62 if (node_index_3 != libMesh::invalid_uint && neigh_three->is_vertex(node_index_3))
63 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 for (auto elem : fine_elements)
73 if (elem && fine_elem.type() != elem_type)
74 return false;
75
76 if (elem_type == libMesh::QUAD4 || elem_type == libMesh::QUAD8 || elem_type == libMesh::QUAD9)
77 {
78 // We need 4 elements around the interior node
79 if (fine_elements.size() != 4)
80 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 tentative_coarse_nodes.resize(4);
85
86 // The exterior nodes are the opposite nodes of the interior_node!
87 unsigned int neighbor_i = 0;
88 for (auto neighbor : fine_elements)
89 {
90 const auto interior_node_number = neighbor->get_node_index(&interior_node);
91 unsigned int opposite_node_index = (interior_node_number + 2) % 4;
92
93 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 (fine_elem.vertex_average() - interior_node).cross(interior_node - reference_node);
99 reorderNodes(tentative_coarse_nodes, interior_node, reference_node, axis);
100 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 else if (elem_type == libMesh::HEX8)
105 {
106 // We need 8 elements around the interior node
107 if (fine_elements.size() != 8)
108 return false;
109
110 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 const Elem * one_fine_elem = nullptr;
116 unsigned int max_id = 0;
117 for (const auto elem_ptr : fine_elements)
118 if (elem_ptr->id() > max_id)
119 {
120 max_id = elem_ptr->id();
121 one_fine_elem = elem_ptr;
122 }
123 const auto interior_node_index = one_fine_elem->get_node_index(&interior_node);
124
125 // Find any side which contains the interior node
126 unsigned int an_interior_node_side = 0;
127 for (const auto s : make_range(one_fine_elem->n_sides()))
128 if (one_fine_elem->is_node_on_side(interior_node_index, s))
129 {
130 an_interior_node_side = s;
131 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 one_fine_elem->opposite_node(interior_node_index, an_interior_node_side);
137 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 unsigned int neighbor_i = 0;
142 std::vector<const libMesh::Elem *> other_fine_elems;
143 for (auto neighbor : fine_elements)
144 {
145 if (neighbor->get_node_index(center_face_node) == libMesh::invalid_uint)
146 {
147 other_fine_elems.push_back(neighbor);
148 continue;
149 }
150 // The coarse element node is the opposite nodes of the interior_node in a fine element
151 const auto interior_node_number = neighbor->get_node_index(&interior_node);
152 unsigned int opposite_node_index =
153 getOppositeNodeIndex(neighbor->type(), interior_node_number);
154
155 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 if (neighbor_i != 4 || other_fine_elems.size() != 4)
161 return false;
162
163 // Sort the coarse nodes so we are reproducibly picking the same ordering of nodes
164 auto cmp_node = [](const Node * a, const Node * b) { return a->id() < b->id(); };
165 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 const libMesh::Point clock_start = *tentative_coarse_nodes[0];
170 libMesh::Point axis = interior_node - *center_face_node;
171 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 for (const auto coarse_node_index : make_range(4))
175 {
176 // Find the fine element containing each coarse node already found & ordered
177 const Elem * fine_elem = nullptr;
178 for (auto elem : fine_elements)
179 if (elem->get_node_index(tentative_coarse_nodes[coarse_node_index]) !=
181 {
182 fine_elem = elem;
183 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 const Elem * fine_neighbor = nullptr;
189 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 if (neighbor->which_neighbor_am_i(fine_elem) != libMesh::invalid_uint)
193 {
194 if (fine_neighbor)
195 mooseError("Found two neighbors");
196 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 if (!fine_neighbor)
202 return false;
203
204 // Get the coarse node, opposite the interior node in that fine element
205 const auto interior_node_index_neighbor = fine_neighbor->get_node_index(&interior_node);
206 tentative_coarse_nodes.push_back(fine_neighbor->node_ptr(
207 getOppositeNodeIndex(fine_neighbor->type(), interior_node_index_neighbor)));
208 }
209
210 // Found 8 fine elements and 8 coarse element nodes as expected
211 if (tentative_coarse_nodes.size() == 8)
212 return true;
213 else
214 return false;
215 }
216 else
217 mooseError("Not implemented for element type " + Moose::stringify(elem_type));
218}
219
220void
221reorderNodes(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 auto start_clock = origin - clock_start;
233 start_clock /= start_clock.norm();
234 axis /= axis.norm();
235
236 std::vector<std::pair<unsigned int, libMesh::Real>> nodes_angles(nodes.size());
237 for (const auto angle_i : index_range(nodes))
238 {
239 mooseAssert(nodes[angle_i], "Nodes cant be nullptr");
240 auto vec = *nodes[angle_i] - origin;
241 vec /= vec.norm();
242 const auto angle = atan2(vec.cross(start_clock) * axis, vec * start_clock);
243 nodes_angles[angle_i] = std::make_pair(angle_i, angle);
244 }
245
246 // sort by angle, so it goes around the interior node
247 std::sort(nodes_angles.begin(),
248 nodes_angles.end(),
249 [](auto & left, auto & right) { return left.second < right.second; });
250
251 // Re-sort the nodes based on their angle
252 std::vector<const libMesh::Node *> new_nodes(nodes.size());
253 for (const auto & old_index : index_range(nodes))
254 new_nodes[old_index] = nodes[nodes_angles[old_index].first];
255 for (const auto & index : index_range(nodes))
256 nodes[index] = new_nodes[index];
257}
258
259unsigned int
260getOppositeNodeIndex(libMesh::ElemType elem_type, unsigned int node_index)
261{
262 switch (elem_type)
263 {
264 case QUAD4:
265 return (node_index + 2) % 4;
266 case HEX8:
267 {
268 mooseAssert(node_index < 8, "Node index too high: " + std::to_string(node_index));
269 return std::vector<unsigned int>({6, 7, 4, 5, 2, 3, 0, 1})[node_index];
270 }
271 default:
272 mooseError("Unsupported element type for retrieving the opposite node");
273 }
274}
275}
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
dof_id_type id() const
virtual bool is_node_on_side(const unsigned int n, const unsigned int s) const=0
SimpleRange< NeighborPtrIter > neighbor_ptr_range()
virtual unsigned int opposite_node(const unsigned int n, const unsigned int s) const
const Node * node_ptr(const unsigned int i) const
unsigned int get_node_index(const Node *node_ptr) const
virtual ElemType type() const=0
virtual unsigned int n_sides() 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
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
auto index_range(const T &sizable)
const unsigned int invalid_uint
const RemoteElem * remote_elem
IntRange< T > make_range(T beg, T end)