https://mooseframework.inl.gov
Functions
MeshCoarseningUtils Namespace Reference

Functions

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 element. More...
 
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. More...
 
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. More...
 
bool getFineElementsFromInteriorNode (const libMesh::Node &interior_node, const libMesh::Node &reference_node, const libMesh::Elem &fine_elem, std::vector< const libMesh::Node *> &tentative_coarse_nodes, std::set< const libMesh::Elem *> &fine_elements)
 
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. More...
 

Function Documentation

◆ getFineElementsFromInteriorNode() [1/2]

bool MeshCoarseningUtils::getFineElementsFromInteriorNode ( const libMesh::Node interior_node,
const libMesh::Node reference_node,
const libMesh::Elem fine_elem,
std::vector< const libMesh::Node *> &  tentative_coarse_nodes,
std::set< const libMesh::Elem *> &  fine_elements 
)

Definition at line 22 of file MeshCoarseningUtils.C.

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 }
const unsigned int invalid_uint
unsigned int get_node_index(const Node *node_ptr) const
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:302
virtual bool is_node_on_side(const unsigned int n, const unsigned int s) const=0
dof_id_type id() const
virtual unsigned int opposite_node(const unsigned int n, const unsigned int s) const
std::string stringify(const T &t)
conversion to string
Definition: Conversion.h:64
virtual unsigned int n_sides() const=0
const Node * node_ptr(const unsigned int i) const
IntRange< T > make_range(T beg, T end)
SimpleRange< NeighborPtrIter > neighbor_ptr_range()
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.
virtual ElemType type() const=0
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...
Point vertex_average() const
const RemoteElem * remote_elem

◆ getFineElementsFromInteriorNode() [2/2]

bool MeshCoarseningUtils::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 element.

Parameters
interior_nodethe node inside the coarse element
reference_nodeanother node used to order the vertex nodes in a clockwise order for QUAD elements
eleman element containing the node. Its neighbor lists must be up to date so it should come from a prepared mesh
tentative_coarse_nodesnodes to be used to form the coarse element
fine_elementsfine elements that are inside the coarse element

Referenced by MeshDiagnosticsGenerator::checkNonConformalMeshFromAdaptivity(), and CoarsenBlockGenerator::recursiveCoarsen().

◆ getOppositeNodeIndex()

unsigned int MeshCoarseningUtils::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.

Parameters
elem_typetype of the element
node_indexlocal index of the node of interest
Returns
local index of the opposite node, with regards to the element

Definition at line 260 of file MeshCoarseningUtils.C.

Referenced by getFineElementsFromInteriorNode(), and CoarsenBlockGenerator::recursiveCoarsen().

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 }
HEX8
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:302
QUAD4

◆ reorderNodes() [1/2]

void MeshCoarseningUtils::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.

Parameters
nodesthe vector containing the nodes to re-order
originthe center of the clock (circle to align nodes around)
clock_startthe start of the clock
axisthe rotation axis (will be normalized)

Definition at line 221 of file MeshCoarseningUtils.C.

Referenced by MeshDiagnosticsGenerator::checkNonConformalMeshFromAdaptivity(), and getFineElementsFromInteriorNode().

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 }
auto norm() const -> decltype(std::norm(Real()))
auto index_range(const T &sizable)

◆ reorderNodes() [2/2]

void MeshCoarseningUtils::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.

Parameters
nodesthe vector containing the nodes to re-order
originthe center of the clock (circle to align nodes around)
clock_startthe start of the clock
axisthe rotation axis (will be normalized)

Definition at line 221 of file MeshCoarseningUtils.C.

Referenced by MeshDiagnosticsGenerator::checkNonConformalMeshFromAdaptivity(), and getFineElementsFromInteriorNode().

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 }
auto norm() const -> decltype(std::norm(Real()))
auto index_range(const T &sizable)