libMesh
Loading...
Searching...
No Matches
mesh_subdivision_support.C
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3
4// This library is free software; you can redistribute it and/or
5// modify it under the terms of the GNU Lesser General Public
6// License as published by the Free Software Foundation; either
7// version 2.1 of the License, or (at your option) any later version.
8
9// This library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12// Lesser General Public License for more details.
13
14// You should have received a copy of the GNU Lesser General Public
15// License along with this library; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18
19
20// C++ includes
21
22// Local includes
23#include "libmesh/mesh_tools.h"
24#include "libmesh/mesh_subdivision_support.h"
25#include "libmesh/boundary_info.h"
26
27namespace libMesh
28{
29
30
32 std::vector<const Node *> & nodes)
33{
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));
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}
88
89
91{
92 const bool mesh_has_boundary_data =
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
129 }
130
131 mesh.insert_elem(std::move(tri));
132 }
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
153}
154
155
157{
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
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())
196 }
197}
198
199
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}
227
228
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);
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));
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);
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));
449 mesh.get_boundary_info().add_node(nb2->node_ptr(prev[j]), 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}
480
481} // namespace libMesh
std::size_t n_boundary_ids() const
void boundary_ids(const Node *node, std::vector< boundary_id_type > &vec_to_fill) const
Fills a user-provided std::vector with the boundary ids associated with Node node.
void add_node(const Node *node, const boundary_id_type id)
Add Node node with boundary id id to the boundary information data structures.
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
Add side side of element number elem with boundary id id to the boundary information data structure.
void remove(const Node *node)
Removes the boundary conditions associated with node node, if any exist.
dof_id_type id() const
Definition dof_object.h:819
This is the base class from which all geometric element types are derived.
Definition elem.h:96
const Point & point(const unsigned int i) const
Definition elem.h:2462
static std::unique_ptr< Elem > build_with_id(const ElemType type, dof_id_type id)
Calls the build() method above with a nullptr parent, and additionally sets the newly-created Elem's ...
Definition elem.C:556
void set_neighbor(const unsigned int i, Elem *n)
Assigns n as the neighbor.
Definition elem.h:2635
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition elem.C:442
const Node * node_ptr(const unsigned int i) const
Definition elem.h:2516
virtual ElemType type() const =0
const Elem * neighbor_ptr(unsigned int i) const
Definition elem.h:2615
IntRange< unsigned short > side_index_range() const
Definition elem.h:2727
This is the MeshBase class.
Definition mesh_base.h:81
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition mesh_base.h:170
virtual dof_id_type n_elem() const =0
virtual Elem * insert_elem(Elem *e)=0
Insert elem e to the element array, preserving its id and replacing/deleting any existing element wit...
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
Prepare a newly created (or read) mesh for use.
Definition mesh_base.C:824
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
Add a new Node at Point p to the end of the vertex array, with processor_id procid.
virtual const Elem * elem_ptr(const dof_id_type i) const =0
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
A Node is like a Point, but with more information.
Definition node.h:55
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
The Tri3Subdivision element is a three-noded subdivision surface shell element used in mechanics calc...
unsigned int get_ordered_valence(unsigned int node_id) const
void prepare_subdivision_properties()
Prepares the element for use by reordering the nodes such that the irregular node (valence !...
void set_ghost(bool ghosted)
Sets the boolean flag identifying ghost elements.
Node * get_ordered_node(unsigned int node_id) const
unsigned int local_node_number(unsigned int node_id) const
MeshBase & mesh
void add_boundary_ghosts(MeshBase &mesh)
Adds a new layer of "ghost" elements along the domain boundaries.
void all_subdivision(MeshBase &mesh)
Turns a triangulated mesh into a subdivision mesh.
void tag_boundary_ghosts(MeshBase &mesh)
Flags the outermost element layer along the domain boundaries as "ghost" elements.
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.
void prepare_subdivision_mesh(MeshBase &mesh, bool ghosted=false)
Prepares the mesh for use with subdivision elements.
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:456
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).
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...
The libMesh namespace provides an interface to certain functionality in the library.
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:153
libmesh_assert(ctx)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real