libMesh
Functions | Variables
libMesh::MeshTools::Subdivision Namespace Reference

Utility functions for subdivision surface operations on a Mesh. More...

Functions

void find_one_ring (const Tri3Subdivision *elem, std::vector< const Node * > &nodes)
 Determines the 1-ring of element elem, and writes it to the nodes vector. More...
 
void all_subdivision (MeshBase &mesh)
 Turns a triangulated mesh into a subdivision mesh. More...
 
void prepare_subdivision_mesh (MeshBase &mesh, bool ghosted=false)
 Prepares the mesh for use with subdivision elements. More...
 
void add_boundary_ghosts (MeshBase &mesh)
 Adds a new layer of "ghost" elements along the domain boundaries. More...
 
void tag_boundary_ghosts (MeshBase &mesh)
 Flags the outermost element layer along the domain boundaries as "ghost" elements. More...
 

Variables

static const unsigned int next [3] = {1,2,0}
 A lookup table for the increment modulo 3 operation, for iterating through the three nodes per element in positive direction. More...
 
static const unsigned int prev [3] = {2,0,1}
 A lookup table for the decrement modulo 3 operation, for iterating through the three nodes per element in negative direction. More...
 

Detailed Description

Utility functions for subdivision surface operations on a Mesh.

Author
Roman Vetter
Norbert Stoop
Date
2014

Support functions for subdivision surface elements.

Function Documentation

◆ add_boundary_ghosts()

void libMesh::MeshTools::Subdivision::add_boundary_ghosts ( MeshBase mesh)

Adds a new layer of "ghost" elements along the domain boundaries.

This function normally needn't be called by the user, because it is invoked by prepare_subdivision_mesh.

Definition at line 233 of file mesh_subdivision_support.C.

234 {
235  static const Real tol = 1e-5;
236 
237  // add the mirrored ghost elements (without using iterators, because the mesh is modified in the course)
238  std::vector<Tri3Subdivision *> ghost_elems;
239  std::vector<Node *> ghost_nodes;
240  const unsigned int n_elem = mesh.n_elem();
241  for (unsigned int eid = 0; eid < n_elem; ++eid)
242  {
243  Elem * elem = mesh.elem_ptr(eid);
244  libmesh_assert_equal_to(elem->type(), TRI3SUBDIVISION);
245 
246  // If the triangle happens to be in a corner (two boundary
247  // edges), we perform a counter-clockwise loop by mirroring the
248  // previous triangle until we come back to the original
249  // triangle. This prevents degenerated triangles in the mesh
250  // corners and guarantees that the node in the middle of the
251  // loop is of valence=6.
252  for (auto i : elem->side_index_range())
253  {
254  libmesh_assert_not_equal_to(elem->neighbor_ptr(i), elem);
255 
256  if (elem->neighbor_ptr(i) == nullptr &&
257  elem->neighbor_ptr(next[i]) == nullptr)
258  {
259  Elem * nelem = elem;
260  unsigned int k = i;
261  for (unsigned int l=0;l<4;l++)
262  {
263  // this is the vertex to be mirrored
264  Point point = nelem->point(k) + nelem->point(next[k]) - nelem->point(prev[k]);
265 
266  // Check if the proposed vertex doesn't coincide
267  // with one of the existing vertices. This is
268  // necessary because for some triangulations, it can
269  // happen that two mirrored ghost vertices coincide,
270  // which would then lead to a zero size ghost
271  // element below.
272  Node * node = nullptr;
273  for (auto & ghost_node : ghost_nodes)
274  if ((*ghost_node - point).norm() < tol * (elem->point(k) - point).norm())
275  {
276  node = ghost_node;
277  break;
278  }
279 
280  // add the new vertex only if no other is nearby
281  if (node == nullptr)
282  {
283  node = mesh.add_point(point);
284  ghost_nodes.push_back(node);
285  }
286 
287  Tri3Subdivision * newelem = new Tri3Subdivision();
288 
289  // add the first new ghost element to the list just as in the non-corner case
290  if (l == 0)
291  ghost_elems.push_back(newelem);
292 
293  newelem->set_node(0) = nelem->node_ptr(next[k]);
294  newelem->set_node(1) = nelem->node_ptr(k);
295  newelem->set_node(2) = node;
296  newelem->set_neighbor(0, nelem);
297  newelem->set_ghost(true);
298  if (l>0)
299  newelem->set_neighbor(2, nullptr);
300  nelem->set_neighbor(k, newelem);
301 
302  mesh.add_elem(newelem);
303  mesh.get_boundary_info().add_node(nelem->node_ptr(k), 1);
304  mesh.get_boundary_info().add_node(nelem->node_ptr(next[k]), 1);
305  mesh.get_boundary_info().add_node(nelem->node_ptr(prev[k]), 1);
306  mesh.get_boundary_info().add_node(node, 1);
307 
308  nelem = newelem;
309  k = 2 ;
310  }
311 
312  Tri3Subdivision * newelem = new Tri3Subdivision();
313 
314  newelem->set_node(0) = elem->node_ptr(next[i]);
315  newelem->set_node(1) = nelem->node_ptr(2);
316  newelem->set_node(2) = elem->node_ptr(prev[i]);
317  newelem->set_neighbor(0, nelem);
318  nelem->set_neighbor(2, newelem);
319  newelem->set_ghost(true);
320  newelem->set_neighbor(2, elem);
321  elem->set_neighbor(next[i],newelem);
322 
323  mesh.add_elem(newelem);
324 
325  break;
326  }
327  }
328 
329  for (auto i : elem->side_index_range())
330  {
331  libmesh_assert_not_equal_to(elem->neighbor_ptr(i), elem);
332  if (elem->neighbor_ptr(i) == nullptr)
333  {
334  // this is the vertex to be mirrored
335  Point point = elem->point(i) + elem->point(next[i]) - elem->point(prev[i]);
336 
337  // Check if the proposed vertex doesn't coincide with
338  // one of the existing vertices. This is necessary
339  // because for some triangulations, it can happen that
340  // two mirrored ghost vertices coincide, which would
341  // then lead to a zero size ghost element below.
342  Node * node = nullptr;
343  for (auto & ghost_node : ghost_nodes)
344  if ((*ghost_node - point).norm() < tol * (elem->point(i) - point).norm())
345  {
346  node = ghost_node;
347  break;
348  }
349 
350  // add the new vertex only if no other is nearby
351  if (node == nullptr)
352  {
353  node = mesh.add_point(point);
354  ghost_nodes.push_back(node);
355  }
356 
357  Tri3Subdivision * newelem = new Tri3Subdivision();
358  ghost_elems.push_back(newelem);
359 
360  newelem->set_node(0) = elem->node_ptr(next[i]);
361  newelem->set_node(1) = elem->node_ptr(i);
362  newelem->set_node(2) = node;
363  newelem->set_neighbor(0, elem);
364  newelem->set_ghost(true);
365  elem->set_neighbor(i, newelem);
366 
367  mesh.add_elem(newelem);
368  mesh.get_boundary_info().add_node(elem->node_ptr(i), 1);
369  mesh.get_boundary_info().add_node(elem->node_ptr(next[i]), 1);
370  mesh.get_boundary_info().add_node(elem->node_ptr(prev[i]), 1);
371  mesh.get_boundary_info().add_node(node, 1);
372  }
373  }
374  }
375 
376  // add the missing ghost elements (connecting new ghost nodes)
377  std::vector<Tri3Subdivision *> missing_ghost_elems;
378  for (auto & elem : ghost_elems)
379  {
380  libmesh_assert(elem->is_ghost());
381 
382  for (auto i : elem->side_index_range())
383  {
384  if (elem->neighbor_ptr(i) == nullptr &&
385  elem->neighbor_ptr(prev[i]) != nullptr)
386  {
387  // go around counter-clockwise
388  Tri3Subdivision * nb1 = static_cast<Tri3Subdivision *>(elem->neighbor_ptr(prev[i]));
389  Tri3Subdivision * nb2 = nb1;
390  unsigned int j = i;
391  unsigned int n_nb = 0;
392  while (nb1 != nullptr && nb1->id() != elem->id())
393  {
394  j = nb1->local_node_number(elem->node_id(i));
395  nb2 = nb1;
396  nb1 = static_cast<Tri3Subdivision *>(nb1->neighbor_ptr(prev[j]));
397  libmesh_assert(nb1 == nullptr || nb1->id() != nb2->id());
398  n_nb++;
399  }
400 
401  libmesh_assert_not_equal_to(nb2->id(), elem->id());
402 
403  // Above, we merged coinciding ghost vertices. Therefore, we need
404  // to exclude the case where there is no ghost element to add between
405  // these two (identical) ghost nodes.
406  if (elem->node_ptr(next[i])->id() == nb2->node_ptr(prev[j])->id())
407  break;
408 
409  // If the number of already present neighbors is less than 4, we add another extra element
410  // so that the node in the middle of the loop ends up being of valence=6.
411  // This case usually happens when the middle node corresponds to a corner of the original mesh,
412  // and the extra element below prevents degenerated triangles in the mesh corners.
413  if (n_nb < 4)
414  {
415  // this is the vertex to be mirrored
416  Point point = nb2->point(j) + nb2->point(prev[j]) - nb2->point(next[j]);
417 
418  // Check if the proposed vertex doesn't coincide with one of the existing vertices.
419  // This is necessary because for some triangulations, it can happen that two mirrored
420  // ghost vertices coincide, which would then lead to a zero size ghost element below.
421  Node * node = nullptr;
422  for (auto & ghost_node : ghost_nodes)
423  if ((*ghost_node - point).norm() < tol * (nb2->point(j) - point).norm())
424  {
425  node = ghost_node;
426  break;
427  }
428 
429  // add the new vertex only if no other is nearby
430  if (node == nullptr)
431  {
432  node = mesh.add_point(point);
433  ghost_nodes.push_back(node);
434  }
435 
436  Tri3Subdivision * newelem = new Tri3Subdivision();
437 
438  newelem->set_node(0) = nb2->node_ptr(j);
439  newelem->set_node(1) = nb2->node_ptr(prev[j]);
440  newelem->set_node(2) = node;
441  newelem->set_neighbor(0, nb2);
442  newelem->set_neighbor(1, nullptr);
443  newelem->set_ghost(true);
444  nb2->set_neighbor(prev[j], newelem);
445 
446  mesh.add_elem(newelem);
447  mesh.get_boundary_info().add_node(nb2->node_ptr(j), 1);
448  mesh.get_boundary_info().add_node(nb2->node_ptr(prev[j]), 1);
449  mesh.get_boundary_info().add_node(node, 1);
450 
451  nb2 = newelem;
452  j = nb2->local_node_number(elem->node_id(i));
453  }
454 
455  Tri3Subdivision * newelem = new Tri3Subdivision();
456  newelem->set_node(0) = elem->node_ptr(next[i]);
457  newelem->set_node(1) = elem->node_ptr(i);
458  newelem->set_node(2) = nb2->node_ptr(prev[j]);
459  newelem->set_neighbor(0, elem);
460  newelem->set_neighbor(1, nb2);
461  newelem->set_neighbor(2, nullptr);
462  newelem->set_ghost(true);
463 
464  elem->set_neighbor(i, newelem);
465  nb2->set_neighbor(prev[j], newelem);
466 
467  missing_ghost_elems.push_back(newelem);
468  break;
469  }
470  } // end side loop
471  } // end ghost element loop
472 
473  // add the missing ghost elements to the mesh
474  for (auto & elem : missing_ghost_elems)
475  mesh.add_elem(elem);
476 }

References libMesh::MeshBase::add_elem(), libMesh::BoundaryInfo::add_node(), libMesh::MeshBase::add_point(), libMesh::MeshBase::elem_ptr(), libMesh::MeshBase::get_boundary_info(), libMesh::DofObject::id(), libMesh::libmesh_assert(), libMesh::Tri3Subdivision::local_node_number(), mesh, libMesh::MeshBase::n_elem(), libMesh::MeshTools::n_elem(), libMesh::Elem::neighbor_ptr(), next, libMesh::Elem::node_ptr(), std::norm(), libMesh::Elem::point(), prev, libMesh::Real, libMesh::Tri3Subdivision::set_ghost(), libMesh::Elem::set_neighbor(), libMesh::Elem::set_node(), libMesh::Elem::side_index_range(), libMesh::TRI3SUBDIVISION, and libMesh::Elem::type().

Referenced by prepare_subdivision_mesh().

◆ all_subdivision()

void libMesh::MeshTools::Subdivision::all_subdivision ( MeshBase mesh)

Turns a triangulated mesh into a subdivision mesh.

This function normally needn't be called by the user, because it is invoked by prepare_subdivision_mesh.

Definition at line 90 of file mesh_subdivision_support.C.

91 {
92  std::vector<Elem *> new_elements;
93  new_elements.reserve(mesh.n_elem());
94  const bool mesh_has_boundary_data =
95  (mesh.get_boundary_info().n_boundary_ids() > 0);
96 
97  std::vector<Elem *> new_boundary_elements;
98  std::vector<short int> new_boundary_sides;
99  std::vector<boundary_id_type> new_boundary_ids;
100 
101  // Container to catch ids handed back from BoundaryInfo
102  std::vector<boundary_id_type> ids;
103 
104  for (const auto & elem : mesh.element_ptr_range())
105  {
106  libmesh_assert_equal_to(elem->type(), TRI3);
107 
108  Elem * tri = new Tri3Subdivision;
109  tri->set_id(elem->id());
110  tri->subdomain_id() = elem->subdomain_id();
111  tri->set_node(0) = elem->node_ptr(0);
112  tri->set_node(1) = elem->node_ptr(1);
113  tri->set_node(2) = elem->node_ptr(2);
114 
115  if (mesh_has_boundary_data)
116  {
117  for (auto side : elem->side_index_range())
118  {
119  mesh.get_boundary_info().boundary_ids(elem, side, ids);
120 
121  for (const auto & id : ids)
122  {
123  // add the boundary id to the list of new boundary ids
124  new_boundary_ids.push_back(id);
125  new_boundary_elements.push_back(tri);
126  new_boundary_sides.push_back(side);
127  }
128  }
129 
130  // remove the original element from the BoundaryInfo structure
131  mesh.get_boundary_info().remove(elem);
132  }
133 
134  new_elements.push_back(tri);
135  mesh.insert_elem(tri);
136  }
137  mesh.prepare_for_use();
138 
139  if (mesh_has_boundary_data)
140  {
141  // If the old mesh had boundary data, the new mesh better have some too.
142  libmesh_assert_greater(new_boundary_elements.size(), 0);
143 
144  // We should also be sure that the lengths of the new boundary data vectors
145  // are all the same.
146  libmesh_assert_equal_to(new_boundary_sides.size(), new_boundary_elements.size());
147  libmesh_assert_equal_to(new_boundary_sides.size(), new_boundary_ids.size());
148 
149  // Add the new boundary info to the mesh.
150  for (auto s : index_range(new_boundary_elements))
151  mesh.get_boundary_info().add_side(new_boundary_elements[s],
152  new_boundary_sides[s],
153  new_boundary_ids[s]);
154  }
155 
156  mesh.prepare_for_use();
157 }

References libMesh::BoundaryInfo::add_side(), libMesh::BoundaryInfo::boundary_ids(), libMesh::MeshBase::element_ptr_range(), libMesh::MeshBase::get_boundary_info(), libMesh::index_range(), libMesh::MeshBase::insert_elem(), mesh, libMesh::BoundaryInfo::n_boundary_ids(), libMesh::MeshBase::n_elem(), libMesh::MeshBase::prepare_for_use(), libMesh::BoundaryInfo::remove(), libMesh::DofObject::set_id(), and libMesh::TRI3.

Referenced by prepare_subdivision_mesh().

◆ find_one_ring()

void libMesh::MeshTools::Subdivision::find_one_ring ( const Tri3Subdivision elem,
std::vector< const Node * > &  nodes 
)

Determines the 1-ring of element elem, and writes it to the nodes vector.

This is necessary because subdivision elements have a larger local support than conventionally interpolated elements. The 1-ring may, for instance, look like this:

*    N+4 - N+1 - N+2
*    / \   / \   / \
*   /   \ /   \ /   \
* N+5 -- N --- 1 -- N+3
*   \   / \ e / \   /
*    \ /   \ /   \ /
*    N-1--- 0 --- 2
*      \   /|\   /
*       \ / | \ /
*        5--4--3
* 

Definition at line 31 of file mesh_subdivision_support.C.

33 {
34  libmesh_assert(elem->is_subdivision_updated());
35  libmesh_assert(elem->get_ordered_node(0));
36 
37  unsigned int valence = elem->get_ordered_valence(0);
38  nodes.resize(valence + 6);
39 
40  // The first three vertices in the patch are the ones from the element triangle
41  nodes[0] = elem->get_ordered_node(0);
42  nodes[1] = elem->get_ordered_node(1);
43  nodes[valence] = elem->get_ordered_node(2);
44 
45  const unsigned int nn0 = elem->local_node_number(nodes[0]->id());
46 
47  const Tri3Subdivision * nb = dynamic_cast<const Tri3Subdivision *>(elem->neighbor_ptr(nn0));
48  libmesh_assert(nb);
49 
50  unsigned int j, i = 1;
51 
52  do
53  {
54  ++i;
55  j = nb->local_node_number(nodes[0]->id());
56  nodes[i] = nb->node_ptr(next[j]);
57  nb = static_cast<const Tri3Subdivision *>(nb->neighbor_ptr(j));
58  } while (nb != elem);
59 
60  /* for nodes connected with N (= valence[0]) */
61  nb = static_cast<const Tri3Subdivision *>(elem->neighbor_ptr(next[nn0]));
62  j = nb->local_node_number(nodes[1]->id());
63  nodes[valence+1] = nb->node_ptr(next[j]);
64 
65  nb = static_cast<const Tri3Subdivision *>(nb->neighbor_ptr(next[j]));
66  j = nb->local_node_number(nodes[valence+1]->id());
67  nodes[valence+4] = nb->node_ptr(next[j]);
68 
69  nb = static_cast<const Tri3Subdivision *>(nb->neighbor_ptr(next[j]));
70  j = nb->local_node_number(nodes[valence+4]->id());
71  nodes[valence+5] = nb->node_ptr(next[j]);
72 
73  /* for nodes connected with 1 */
74  nb = static_cast<const Tri3Subdivision *>(elem->neighbor_ptr(next[nn0]));
75  j = nb->local_node_number(nodes[1]->id());
76  // nodes[valence+1] has been determined already
77 
78  nb = static_cast<const Tri3Subdivision *>(nb->neighbor_ptr(j));
79  j = nb->local_node_number(nodes[1]->id());
80  nodes[valence+2] = nb->node_ptr(next[j]);
81 
82  nb = static_cast<const Tri3Subdivision *>(nb->neighbor_ptr(j));
83  j = nb->local_node_number(nodes[1]->id());
84  nodes[valence+3] = nb->node_ptr(next[j]);
85 
86  return;
87 }

References libMesh::Tri3Subdivision::get_ordered_node(), libMesh::Tri3Subdivision::get_ordered_valence(), libMesh::Tri3Subdivision::is_subdivision_updated(), libMesh::libmesh_assert(), libMesh::Tri3Subdivision::local_node_number(), libMesh::Elem::neighbor_ptr(), next, and libMesh::Elem::node_ptr().

Referenced by libMesh::FEMap::compute_map(), libMesh::DofMap::dof_indices(), and libMesh::DofMap::old_dof_indices().

◆ prepare_subdivision_mesh()

void libMesh::MeshTools::Subdivision::prepare_subdivision_mesh ( MeshBase mesh,
bool  ghosted = false 
)

Prepares the mesh for use with subdivision elements.

The ghosted flag determines how boundaries are treated. If false, a new layer of "ghost" elements is appended along the domain boundaries. If true, the outermost element layer is taken as ghosts, i.e. no new elements are added.

Definition at line 160 of file mesh_subdivision_support.C.

161 {
162  mesh.prepare_for_use();
163 
164  // convert all mesh elements to subdivision elements
166 
167  if (!ghosted)
168  {
169  // add the ghost elements for the boundaries
171  }
172  else
173  {
174  // This assumes that the mesh already has the ghosts. Only tagging them is required here.
176  }
177 
178  mesh.prepare_for_use();
179 
180  std::unordered_map<dof_id_type, std::vector<const Elem *>> nodes_to_elem_map;
181  MeshTools::build_nodes_to_elem_map(mesh, nodes_to_elem_map);
182 
183  // compute the node valences
184  for (auto & node : mesh.node_ptr_range())
185  {
186  std::vector<const Node *> neighbors;
187  MeshTools::find_nodal_neighbors(mesh, *node, nodes_to_elem_map, neighbors);
188  const unsigned int valence =
189  cast_int<unsigned int>(neighbors.size());
190  libmesh_assert_greater(valence, 1);
191  node->set_valence(valence);
192  }
193 
194  for (auto & elem : mesh.element_ptr_range())
195  {
196  Tri3Subdivision * tri3s = dynamic_cast<Tri3Subdivision *>(elem);
197  libmesh_assert(tri3s);
198  if (!tri3s->is_ghost())
199  tri3s->prepare_subdivision_properties();
200  }
201 }

References add_boundary_ghosts(), all_subdivision(), libMesh::MeshTools::build_nodes_to_elem_map(), libMesh::MeshBase::element_ptr_range(), libMesh::MeshTools::find_nodal_neighbors(), libMesh::Tri3Subdivision::is_ghost(), libMesh::libmesh_assert(), mesh, libMesh::MeshBase::node_ptr_range(), libMesh::MeshBase::prepare_for_use(), libMesh::Tri3Subdivision::prepare_subdivision_properties(), and tag_boundary_ghosts().

Referenced by main().

◆ tag_boundary_ghosts()

void libMesh::MeshTools::Subdivision::tag_boundary_ghosts ( MeshBase mesh)

Flags the outermost element layer along the domain boundaries as "ghost" elements.

This function normally needn't be called by the user, because it is invoked by prepare_subdivision_mesh.

Definition at line 204 of file mesh_subdivision_support.C.

205 {
206  for (auto & elem : mesh.element_ptr_range())
207  {
208  libmesh_assert_equal_to(elem->type(), TRI3SUBDIVISION);
209 
210  Tri3Subdivision * sd_elem = static_cast<Tri3Subdivision *>(elem);
211  for (auto i : elem->side_index_range())
212  {
213  if (elem->neighbor_ptr(i) == nullptr)
214  {
215  sd_elem->set_ghost(true);
216  // set all other neighbors to ghosts as well
217  if (elem->neighbor_ptr(next[i]))
218  {
219  Tri3Subdivision * nb = static_cast<Tri3Subdivision *>(elem->neighbor_ptr(next[i]));
220  nb->set_ghost(true);
221  }
222  if (elem->neighbor_ptr(prev[i]))
223  {
224  Tri3Subdivision * nb = static_cast<Tri3Subdivision *>(elem->neighbor_ptr(prev[i]));
225  nb->set_ghost(true);
226  }
227  }
228  }
229  }
230 }

References libMesh::MeshBase::element_ptr_range(), mesh, next, prev, libMesh::Tri3Subdivision::set_ghost(), and libMesh::TRI3SUBDIVISION.

Referenced by prepare_subdivision_mesh().

Variable Documentation

◆ next

const unsigned int libMesh::MeshTools::Subdivision::next[3] = {1,2,0}
static

◆ prev

const unsigned int libMesh::MeshTools::Subdivision::prev[3] = {2,0,1}
static

A lookup table for the decrement modulo 3 operation, for iterating through the three nodes per element in negative direction.

Definition at line 108 of file mesh_subdivision_support.h.

Referenced by add_boundary_ghosts(), assemble_shell(), libMesh::ConstCouplingRow::ConstCouplingRow(), libMesh::Utility::is_sorted(), libMesh::Tri3Subdivision::prepare_subdivision_properties(), tag_boundary_ghosts(), and SystemsTest::testProjectMatrix1D().

libMesh::MeshTools::Subdivision::next
static const unsigned int next[3]
A lookup table for the increment modulo 3 operation, for iterating through the three nodes per elemen...
Definition: mesh_subdivision_support.h:102
libMesh::MeshTools::n_elem
dof_id_type n_elem(const MeshBase::const_element_iterator &begin, const MeshBase::const_element_iterator &end)
Count up the number of elements of a specific type (as defined by an iterator range).
Definition: mesh_tools.C:705
libMesh::MeshTools::Subdivision::all_subdivision
void all_subdivision(MeshBase &mesh)
Turns a triangulated mesh into a subdivision mesh.
Definition: mesh_subdivision_support.C:90
libMesh::index_range
IntRange< std::size_t > index_range(const std::vector< T > &vec)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:106
mesh
MeshBase & mesh
Definition: mesh_communication.C:1257
libMesh::MeshTools::build_nodes_to_elem_map
void build_nodes_to_elem_map(const MeshBase &mesh, std::vector< std::vector< dof_id_type >> &nodes_to_elem_map)
After calling this function the input vector nodes_to_elem_map will contain the node to element conne...
Definition: mesh_tools.C:248
libMesh::MeshTools::Subdivision::add_boundary_ghosts
void add_boundary_ghosts(MeshBase &mesh)
Adds a new layer of "ghost" elements along the domain boundaries.
Definition: mesh_subdivision_support.C:233
libMesh::MeshTools::Subdivision::tag_boundary_ghosts
void tag_boundary_ghosts(MeshBase &mesh)
Flags the outermost element layer along the domain boundaries as "ghost" elements.
Definition: mesh_subdivision_support.C:204
libMesh::libmesh_assert
libmesh_assert(ctx)
libMesh::TRI3
Definition: enum_elem_type.h:39
libMesh::MeshTools::find_nodal_neighbors
void find_nodal_neighbors(const MeshBase &mesh, const Node &n, const std::vector< std::vector< const Elem * >> &nodes_to_elem_map, std::vector< const Node * > &neighbors)
Given a mesh and a node in the mesh, the vector will be filled with every node directly attached to t...
Definition: mesh_tools.C:743
std::norm
MetaPhysicL::DualNumber< T, D > norm(const MetaPhysicL::DualNumber< T, D > &in)
libMesh::MeshTools::Subdivision::prev
static const unsigned int prev[3]
A lookup table for the decrement modulo 3 operation, for iterating through the three nodes per elemen...
Definition: mesh_subdivision_support.h:108
libMesh::TRI3SUBDIVISION
Definition: enum_elem_type.h:69
libMesh::Real
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
Definition: libmesh_common.h:121