libMesh::MeshTools::Subdivision Namespace Reference

Support functions for subdivision surface elements. More...

Functions

void find_one_ring (const Tri3Subdivision *elem, std::vector< const Node *> &nodes)
 
void all_subdivision (MeshBase &mesh)
 
void prepare_subdivision_mesh (MeshBase &mesh, bool ghosted=false)
 
void add_boundary_ghosts (MeshBase &mesh)
 
void tag_boundary_ghosts (MeshBase &mesh)
 

Variables

static const unsigned int next [3] = {1,2,0}
 
static const unsigned int prev [3] = {2,0,1}
 

Detailed Description

Support functions for subdivision surface elements.

Utility functions for subdivision surface operations on a Mesh.

Author
Roman Vetter
Norbert Stoop
Date
2014

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.

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::Tri3Subdivision::local_node_number(), mesh, libMesh::MeshBase::n_elem(), libMesh::MeshTools::n_elem(), libMesh::Elem::neighbor_ptr(), next, libMesh::Elem::node_ptr(), 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().

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

Referenced by prepare_subdivision_mesh().

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 (std::size_t id=0; id<ids.size(); ++id)
122  {
123  // add the boundary id to the list of new boundary ids
124  new_boundary_ids.push_back(ids[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 (std::size_t s = 0; s < new_boundary_elements.size(); ++s)
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 }
unsigned short int side
Definition: xdr_io.C:50
MeshBase & 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.

References libMesh::Tri3Subdivision::get_ordered_node(), libMesh::Tri3Subdivision::get_ordered_valence(), libMesh::Tri3Subdivision::is_subdivision_updated(), 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 }
static const unsigned int next[3]

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

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(), mesh, libMesh::MeshBase::node_ptr_range(), libMesh::MeshBase::prepare_for_use(), libMesh::Tri3Subdivision::prepare_subdivision_properties(), and tag_boundary_ghosts().

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 }
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)
Definition: mesh_tools.C:740
void build_nodes_to_elem_map(const MeshBase &mesh, std::vector< std::vector< dof_id_type >> &nodes_to_elem_map)
Definition: mesh_tools.C:245

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

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

Referenced by prepare_subdivision_mesh().

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 }
static const unsigned int prev[3]
MeshBase & mesh
static const unsigned int next[3]

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 109 of file mesh_subdivision_support.h.

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