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 229 of file mesh_subdivision_support.C.

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

Referenced by prepare_subdivision_mesh().

230 {
231  static const Real tol = 1e-5;
232 
233  // add the mirrored ghost elements (without using iterators, because the mesh is modified in the course)
234  std::vector<Tri3Subdivision *> ghost_elems;
235  std::vector<Node *> ghost_nodes;
236  const unsigned int n_elem = mesh.n_elem();
237  for (unsigned int eid = 0; eid < n_elem; ++eid)
238  {
239  Elem * elem = mesh.elem_ptr(eid);
240  libmesh_assert_equal_to(elem->type(), TRI3SUBDIVISION);
241 
242  // If the triangle happens to be in a corner (two boundary
243  // edges), we perform a counter-clockwise loop by mirroring the
244  // previous triangle until we come back to the original
245  // triangle. This prevents degenerated triangles in the mesh
246  // corners and guarantees that the node in the middle of the
247  // loop is of valence=6.
248  for (auto i : elem->side_index_range())
249  {
250  libmesh_assert_not_equal_to(elem->neighbor_ptr(i), elem);
251 
252  if (elem->neighbor_ptr(i) == nullptr &&
253  elem->neighbor_ptr(next[i]) == nullptr)
254  {
255  Elem * nelem = elem;
256  unsigned int k = i;
257  for (unsigned int l=0;l<4;l++)
258  {
259  // this is the vertex to be mirrored
260  Point point = nelem->point(k) + nelem->point(next[k]) - nelem->point(prev[k]);
261 
262  // Check if the proposed vertex doesn't coincide
263  // with one of the existing vertices. This is
264  // necessary because for some triangulations, it can
265  // happen that two mirrored ghost vertices coincide,
266  // which would then lead to a zero size ghost
267  // element below.
268  Node * node = nullptr;
269  for (auto & ghost_node : ghost_nodes)
270  if ((*ghost_node - point).norm() < tol * (elem->point(k) - point).norm())
271  {
272  node = ghost_node;
273  break;
274  }
275 
276  // add the new vertex only if no other is nearby
277  if (node == nullptr)
278  {
279  node = mesh.add_point(point);
280  ghost_nodes.push_back(node);
281  }
282 
283  auto uelem = Elem::build(TRI3SUBDIVISION);
284  auto newelem = cast_ptr<Tri3Subdivision *>(uelem.get());
285 
286  // add the first new ghost element to the list just as in the non-corner case
287  if (l == 0)
288  ghost_elems.push_back(newelem);
289 
290  newelem->set_node(0, nelem->node_ptr(next[k]));
291  newelem->set_node(1, nelem->node_ptr(k));
292  newelem->set_node(2, node);
293  newelem->set_neighbor(0, nelem);
294  newelem->set_ghost(true);
295  if (l>0)
296  newelem->set_neighbor(2, nullptr);
297  nelem->set_neighbor(k, newelem);
298 
299  Elem * added_elem = mesh.add_elem(std::move(uelem));
300  mesh.get_boundary_info().add_node(nelem->node_ptr(k), 1);
301  mesh.get_boundary_info().add_node(nelem->node_ptr(next[k]), 1);
302  mesh.get_boundary_info().add_node(nelem->node_ptr(prev[k]), 1);
303  mesh.get_boundary_info().add_node(node, 1);
304 
305  nelem = added_elem;
306  k = 2 ;
307  }
308 
309  auto uelem = Elem::build(TRI3SUBDIVISION);
310  auto newelem = cast_ptr<Tri3Subdivision *>(uelem.get());
311 
312  newelem->set_node(0, elem->node_ptr(next[i]));
313  newelem->set_node(1, nelem->node_ptr(2));
314  newelem->set_node(2, elem->node_ptr(prev[i]));
315  newelem->set_neighbor(0, nelem);
316  nelem->set_neighbor(2, newelem);
317  newelem->set_ghost(true);
318  newelem->set_neighbor(2, elem);
319  elem->set_neighbor(next[i],newelem);
320 
321  mesh.add_elem(std::move(uelem));
322 
323  break;
324  }
325  }
326 
327  for (auto i : elem->side_index_range())
328  {
329  libmesh_assert_not_equal_to(elem->neighbor_ptr(i), elem);
330  if (elem->neighbor_ptr(i) == nullptr)
331  {
332  // this is the vertex to be mirrored
333  Point point = elem->point(i) + elem->point(next[i]) - elem->point(prev[i]);
334 
335  // Check if the proposed vertex doesn't coincide with
336  // one of the existing vertices. This is necessary
337  // because for some triangulations, it can happen that
338  // two mirrored ghost vertices coincide, which would
339  // then lead to a zero size ghost element below.
340  Node * node = nullptr;
341  for (auto & ghost_node : ghost_nodes)
342  if ((*ghost_node - point).norm() < tol * (elem->point(i) - point).norm())
343  {
344  node = ghost_node;
345  break;
346  }
347 
348  // add the new vertex only if no other is nearby
349  if (node == nullptr)
350  {
351  node = mesh.add_point(point);
352  ghost_nodes.push_back(node);
353  }
354 
355  auto uelem = Elem::build(TRI3SUBDIVISION);
356  auto newelem = cast_ptr<Tri3Subdivision *>(uelem.get());
357 
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(std::move(uelem));
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<std::unique_ptr<Elem>> 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  auto uelem = Elem::build(TRI3SUBDIVISION);
437  auto newelem = cast_ptr<Tri3Subdivision *>(uelem.get());
438 
439  newelem->set_node(0, nb2->node_ptr(j));
440  newelem->set_node(1, nb2->node_ptr(prev[j]));
441  newelem->set_node(2, node);
442  newelem->set_neighbor(0, nb2);
443  newelem->set_neighbor(1, nullptr);
444  newelem->set_ghost(true);
445  nb2->set_neighbor(prev[j], newelem);
446 
447  Elem * added_elem = mesh.add_elem(std::move(uelem));
448  mesh.get_boundary_info().add_node(nb2->node_ptr(j), 1);
449  mesh.get_boundary_info().add_node(nb2->node_ptr(prev[j]), 1);
450  mesh.get_boundary_info().add_node(node, 1);
451 
452  nb2 = cast_ptr<Tri3Subdivision *>(added_elem);
453  j = nb2->local_node_number(elem->node_id(i));
454  }
455 
456  auto uelem = Elem::build(TRI3SUBDIVISION);
457  auto newelem = cast_ptr<Tri3Subdivision *>(uelem.get());
458 
459  newelem->set_node(0, elem->node_ptr(next[i]));
460  newelem->set_node(1, elem->node_ptr(i));
461  newelem->set_node(2, nb2->node_ptr(prev[j]));
462  newelem->set_neighbor(0, elem);
463  newelem->set_neighbor(1, nb2);
464  newelem->set_neighbor(2, nullptr);
465  newelem->set_ghost(true);
466 
467  elem->set_neighbor(i, newelem);
468  nb2->set_neighbor(prev[j], newelem);
469 
470  missing_ghost_elems.push_back(std::move(uelem));
471  break;
472  }
473  } // end side loop
474  } // end ghost element loop
475 
476  // add the missing ghost elements to the mesh
477  for (auto & elem : missing_ghost_elems)
478  mesh.add_elem(std::move(elem));
479 }
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:969
static const unsigned int prev[3]
A lookup table for the decrement modulo 3 operation, for iterating through the three nodes per elemen...
MeshBase & mesh
libmesh_assert(ctx)
auto norm(const T &a) -> decltype(std::abs(a))
Definition: tensor_tools.h:74
static const unsigned int next[3]
A lookup table for the increment modulo 3 operation, for iterating through the three nodes per elemen...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real

◆ 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.

References libMesh::BoundaryInfo::add_side(), libMesh::BoundaryInfo::boundary_ids(), libMesh::Elem::build_with_id(), libMesh::MeshBase::get_boundary_info(), libMesh::index_range(), libMesh::MeshBase::insert_elem(), mesh, libMesh::BoundaryInfo::n_boundary_ids(), libMesh::MeshBase::prepare_for_use(), libMesh::BoundaryInfo::remove(), libMesh::TRI3, and libMesh::TRI3SUBDIVISION.

Referenced by prepare_subdivision_mesh().

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

◆ 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.

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().

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 }
libmesh_assert(ctx)
static const unsigned int next[3]
A lookup table for the increment modulo 3 operation, for iterating through the three nodes per elemen...

◆ 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 156 of file mesh_subdivision_support.C.

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

Referenced by main().

157 {
158  mesh.prepare_for_use();
159 
160  // convert all mesh elements to subdivision elements
162 
163  if (!ghosted)
164  {
165  // add the ghost elements for the boundaries
167  }
168  else
169  {
170  // This assumes that the mesh already has the ghosts. Only tagging them is required here.
172  }
173 
174  mesh.prepare_for_use();
175 
176  std::unordered_map<dof_id_type, std::vector<const Elem *>> nodes_to_elem_map;
177  MeshTools::build_nodes_to_elem_map(mesh, nodes_to_elem_map);
178 
179  // compute the node valences
180  for (auto & node : mesh.node_ptr_range())
181  {
182  std::vector<const Node *> neighbors;
183  MeshTools::find_nodal_neighbors(mesh, *node, nodes_to_elem_map, neighbors);
184  const unsigned int valence =
185  cast_int<unsigned int>(neighbors.size());
186  libmesh_assert_greater(valence, 1);
187  node->set_valence(valence);
188  }
189 
190  for (auto & elem : mesh.element_ptr_range())
191  {
192  Tri3Subdivision * tri3s = dynamic_cast<Tri3Subdivision *>(elem);
193  libmesh_assert(tri3s);
194  if (!tri3s->is_ghost())
195  tri3s->prepare_subdivision_properties();
196  }
197 }
void add_boundary_ghosts(MeshBase &mesh)
Adds a new layer of "ghost" elements along the domain boundaries.
void tag_boundary_ghosts(MeshBase &mesh)
Flags the outermost element layer along the domain boundaries as "ghost" elements.
MeshBase & mesh
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:1036
void all_subdivision(MeshBase &mesh)
Turns a triangulated mesh into a subdivision mesh.
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:449
libmesh_assert(ctx)

◆ 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 200 of file mesh_subdivision_support.C.

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

Referenced by prepare_subdivision_mesh().

201 {
202  for (auto & elem : mesh.element_ptr_range())
203  {
204  libmesh_assert_equal_to(elem->type(), TRI3SUBDIVISION);
205 
206  Tri3Subdivision * sd_elem = static_cast<Tri3Subdivision *>(elem);
207  for (auto i : elem->side_index_range())
208  {
209  if (elem->neighbor_ptr(i) == nullptr)
210  {
211  sd_elem->set_ghost(true);
212  // set all other neighbors to ghosts as well
213  if (elem->neighbor_ptr(next[i]))
214  {
215  Tri3Subdivision * nb = static_cast<Tri3Subdivision *>(elem->neighbor_ptr(next[i]));
216  nb->set_ghost(true);
217  }
218  if (elem->neighbor_ptr(prev[i]))
219  {
220  Tri3Subdivision * nb = static_cast<Tri3Subdivision *>(elem->neighbor_ptr(prev[i]));
221  nb->set_ghost(true);
222  }
223  }
224  }
225  }
226 }
static const unsigned int prev[3]
A lookup table for the decrement modulo 3 operation, for iterating through the three nodes per elemen...
MeshBase & mesh
static const unsigned int next[3]
A lookup table for the increment modulo 3 operation, for iterating through the three nodes per elemen...

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