libMesh
Loading...
Searching...
No Matches
mesh_modification.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#include <cstdlib> // *must* precede <cmath> for proper std:abs() on PGI, Sun Studio CC
22#include <cmath> // for std::acos()
23#include <algorithm>
24#include <limits>
25#include <map>
26#include <array>
27
28// Local includes
29#include "libmesh/boundary_info.h"
30#include "libmesh/function_base.h"
31#include "libmesh/cell_tet4.h"
32#include "libmesh/cell_tet10.h"
33#include "libmesh/cell_c0polyhedron.h"
34#include "libmesh/cell_polyhedron.h"
35#include "libmesh/elem_range.h"
36#include "libmesh/face_c0polygon.h"
37#include "libmesh/face_polygon.h"
38#include "libmesh/face_tri3.h"
39#include "libmesh/face_tri6.h"
40#include "libmesh/libmesh_logging.h"
41#include "libmesh/mesh_communication.h"
42#include "libmesh/mesh_modification.h"
43#include "libmesh/mesh_tools.h"
44#include "libmesh/parallel.h"
45#include "libmesh/parallel_ghost_sync.h"
46#include "libmesh/remote_elem.h"
47#include "libmesh/surface.h"
48#include "libmesh/enum_to_string.h"
49#include "libmesh/unstructured_mesh.h"
50#include "libmesh/elem_side_builder.h"
51#include "libmesh/tensor_value.h"
52
53namespace
54{
55using namespace libMesh;
56
57bool split_first_diagonal(const Elem * elem,
58 unsigned int diag_1_node_1,
59 unsigned int diag_1_node_2,
60 unsigned int diag_2_node_1,
61 unsigned int diag_2_node_2)
62{
63 return ((elem->node_id(diag_1_node_1) > elem->node_id(diag_2_node_1) &&
64 elem->node_id(diag_1_node_1) > elem->node_id(diag_2_node_2)) ||
65 (elem->node_id(diag_1_node_2) > elem->node_id(diag_2_node_1) &&
66 elem->node_id(diag_1_node_2) > elem->node_id(diag_2_node_2)));
67}
68
69
70// Return the local index of the vertex on \p elem with the highest
71// node id.
72unsigned int highest_vertex_on(const Elem * elem)
73{
74 unsigned int highest_n = 0;
75 dof_id_type highest_n_id = elem->node_id(0);
76 for (auto n : make_range(1u, elem->n_vertices()))
77 {
78 const dof_id_type n_id = elem->node_id(n);
79 if (n_id > highest_n_id)
80 {
81 highest_n = n;
82 highest_n_id = n_id;
83 }
84 }
85
86 return highest_n;
87}
88
89
90static const std::array<std::array<unsigned int, 3>, 8> opposing_nodes =
91{{ {2,5,7},{3,4,6},{0,5,7},{1,4,6},{1,3,6},{0,2,7},{1,3,4},{0,2,5} }};
92
93
94// Find the highest id on these side nodes of this element
95std::pair<unsigned int, unsigned int>
96split_diagonal(const Elem * elem,
97 const std::vector<unsigned int> & nodes_on_side)
98{
99 libmesh_assert_equal_to(elem->type(), HEX8);
100
101 unsigned int highest_n = nodes_on_side.front();
102 dof_id_type highest_n_id = elem->node_id(nodes_on_side.front());
103 for (auto n : nodes_on_side)
104 {
105 const dof_id_type n_id = elem->node_id(n);
106 if (n_id > highest_n_id)
107 {
108 highest_n = n;
109 highest_n_id = n_id;
110 }
111 }
112
113 for (auto n : nodes_on_side)
114 {
115 for (auto n2 : opposing_nodes[highest_n])
116 if (n2 == n)
117 return std::make_pair(highest_n, n2);
118 }
119
120 libmesh_error();
121
122 return std::make_pair(libMesh::invalid_uint, libMesh::invalid_uint);
123}
124
125
126// Reconstruct a C++20 feature in C++14
127template <typename T>
128struct reversion_wrapper { T& iterable; };
129
130template <typename T>
131auto begin (reversion_wrapper<T> w) {return std::rbegin(w.iterable);}
132
133template <typename T>
134auto end (reversion_wrapper<T> w) {return std::rend(w.iterable);}
135
136template <typename T>
137reversion_wrapper<T> reverse(T&& iterable) {return {iterable};}
138
139}
140
141
142namespace libMesh
143{
144
145
146// ------------------------------------------------------------
147// MeshTools::Modification functions for mesh modification
149 const Real factor,
150 const bool perturb_boundary)
151{
154 libmesh_assert ((factor >= 0.) && (factor <= 1.));
155
156 LOG_SCOPE("distort()", "MeshTools::Modification");
157
158 // If we are not perturbing boundary nodes, make a
159 // quickly-searchable list of node ids we can check against.
160 std::unordered_set<dof_id_type> boundary_node_ids;
161 if (!perturb_boundary)
162 boundary_node_ids = MeshTools::find_boundary_nodes (mesh);
163
164 // Now calculate the minimum distance to
165 // neighboring nodes for each node.
166 // hmin holds these distances.
167 std::vector<float> hmin (mesh.max_node_id(),
168 std::numeric_limits<float>::max());
169
170 for (const auto & elem : mesh.active_element_ptr_range())
171 for (auto & n : elem->node_ref_range())
172 hmin[n.id()] = std::min(hmin[n.id()],
173 static_cast<float>(elem->hmin()));
174
175 // Now actually move the nodes
176 {
177 const unsigned int seed = 123456;
178
179 // seed the random number generator.
180 // We'll loop from 1 to n_nodes on every processor, even those
181 // that don't have a particular node, so that the pseudorandom
182 // numbers will be the same everywhere.
183 std::srand(seed);
184
185 // If the node is on the boundary or
186 // the node is not used by any element (hmin[n]<1.e20)
187 // then we should not move it.
188 // [Note: Testing for (in)equality might be wrong
189 // (different types, namely float and double)]
190 for (auto n : make_range(mesh.max_node_id()))
191 if ((perturb_boundary || !boundary_node_ids.count(n)) && hmin[n] < 1.e20)
192 {
193 // the direction, random but unit normalized
194 Point dir (static_cast<Real>(std::rand())/static_cast<Real>(RAND_MAX),
195 (mesh.mesh_dimension() > 1) ? static_cast<Real>(std::rand())/static_cast<Real>(RAND_MAX) : 0.,
196 ((mesh.mesh_dimension() == 3) ? static_cast<Real>(std::rand())/static_cast<Real>(RAND_MAX) : 0.));
197
198 dir(0) = (dir(0)-.5)*2.;
199#if LIBMESH_DIM > 1
200 if (mesh.mesh_dimension() > 1)
201 dir(1) = (dir(1)-.5)*2.;
202#endif
203#if LIBMESH_DIM > 2
204 if (mesh.mesh_dimension() == 3)
205 dir(2) = (dir(2)-.5)*2.;
206#endif
207
208 dir = dir.unit();
209
210 Node * node = mesh.query_node_ptr(n);
211 if (!node)
212 continue;
213
214 (*node)(0) += dir(0)*factor*hmin[n];
215#if LIBMESH_DIM > 1
216 if (mesh.mesh_dimension() > 1)
217 (*node)(1) += dir(1)*factor*hmin[n];
218#endif
219#if LIBMESH_DIM > 2
220 if (mesh.mesh_dimension() == 3)
221 (*node)(2) += dir(2)*factor*hmin[n];
222#endif
223 }
224 }
225
226 // We haven't changed any topology, but just changing geometry could
227 // have invalidated a point locator.
229}
230
231
232
234{
235 LOG_SCOPE("permute_elements()", "MeshTools::Modification");
236
237 // We don't yet support doing permute() on a parent element, which
238 // would require us to consistently permute all its children and
239 // give them different local child numbers.
240 unsigned int n_levels = MeshTools::n_levels(mesh);
241 if (n_levels > 1)
242 libmesh_error();
243
244 const unsigned int seed = 123456;
245
246 // seed the random number generator.
247 // We'll loop from 1 to max_elem_id on every processor, even those
248 // that don't have a particular element, so that the pseudorandom
249 // numbers will be the same everywhere.
250 std::srand(seed);
251
252
253 for (auto e_id : make_range(mesh.max_elem_id()))
254 {
255 int my_rand = std::rand();
256
257 Elem * elem = mesh.query_elem_ptr(e_id);
258
259 if (!elem)
260 continue;
261
262 const unsigned int max_permutation = elem->n_permutations();
263 if (!max_permutation)
264 continue;
265
266 const unsigned int perm = my_rand % max_permutation;
267
268 elem->permute(perm);
269 }
270}
271
272
274{
275 LOG_SCOPE("orient_elements()", "MeshTools::Modification");
276
277 // We don't yet support doing orient() on a parent element, which
278 // would require us to consistently orient all its children and
279 // give them different local child numbers.
280 unsigned int n_levels = MeshTools::n_levels(mesh);
281 if (n_levels > 1)
282 libmesh_not_implemented_msg("orient_elements() does not support refined meshes");
283
284 BoundaryInfo & boundary_info = mesh.get_boundary_info();
285 for (auto elem : mesh.element_ptr_range())
286 elem->orient(&boundary_info);
287}
288
289
290
292 const FunctionBase<Real> & mapfunc)
293{
296
297 LOG_SCOPE("redistribute()", "MeshTools::Modification");
298
299 DenseVector<Real> output_vec(LIBMESH_DIM);
300
301 // FIXME - we should thread this later.
302 std::unique_ptr<FunctionBase<Real>> myfunc = mapfunc.clone();
303
304 for (auto & node : mesh.node_ptr_range())
305 {
306 (*myfunc)(*node, output_vec);
307
308 (*node)(0) = output_vec(0);
309#if LIBMESH_DIM > 1
310 (*node)(1) = output_vec(1);
311#endif
312#if LIBMESH_DIM > 2
313 (*node)(2) = output_vec(2);
314#endif
315 }
316
317 // If we just moved a mesh in or out out of the X axis or XY plane
318 // then we might have changed its spatial_dimension()
320
321 // We haven't changed any topology, but just changing geometry could
322 // have invalidated a point locator.
324}
325
326
327
329 const Real xt,
330 const Real yt,
331 const Real zt)
332{
333 const Point p(xt, yt, zt);
334
335 for (auto & node : mesh.node_ptr_range())
336 *node += p;
337
338 // If we just moved a mesh in or out out of the X axis or XY plane
339 // then we might have changed its spatial_dimension()
341
342 // We haven't changed any topology, but just changing geometry could
343 // have invalidated a point locator.
345}
346
347
348// void MeshTools::Modification::rotate2D (MeshBase & mesh,
349// const Real alpha)
350// {
351// libmesh_assert_not_equal_to (mesh.mesh_dimension(), 1);
352
353// const Real pi = std::acos(-1);
354// const Real a = alpha/180.*pi;
355// for (unsigned int n=0; n<mesh.n_nodes(); n++)
356// {
357// const Point p = mesh.node_ref(n);
358// const Real x = p(0);
359// const Real y = p(1);
360// const Real z = p(2);
361// mesh.node_ref(n) = Point(std::cos(a)*x - std::sin(a)*y,
362// std::sin(a)*x + std::cos(a)*y,
363// z);
364// }
365
366// }
367
368
369
372 const Real phi,
373 const Real theta,
374 const Real psi)
375{
376 // We won't change any topology, but just changing geometry could
377 // invalidate a point locator.
379
380#if LIBMESH_DIM == 3
381 const auto R = RealTensorValue::intrinsic_rotation_matrix(phi, theta, psi);
382
383 for (auto & node : mesh.node_ptr_range())
384 {
385 Point & pt = *node;
386 pt = R * pt;
387 }
388
389 // If we just moved a mesh in or out out of the X axis or XY plane
390 // then we might have changed its spatial_dimension()
392
393 return R;
394
395#else
396 libmesh_ignore(mesh, phi, theta, psi);
397 libmesh_error_msg("MeshTools::Modification::rotate() requires libMesh to be compiled with LIBMESH_DIM==3");
398 // We'll never get here
399 return RealTensorValue();
400#endif
401}
402
403
405 const Real xs,
406 const Real ys,
407 const Real zs)
408{
409 const Real x_scale = xs;
410 Real y_scale = ys;
411 Real z_scale = zs;
412
413 if (ys == 0.)
414 {
415 libmesh_assert_equal_to (zs, 0.);
416
417 y_scale = z_scale = x_scale;
418 }
419
420 // Scale the x coordinate in all dimensions
421 for (auto & node : mesh.node_ptr_range())
422 (*node)(0) *= x_scale;
423
424 // Only scale the y coordinate in 2 and 3D
425 if (LIBMESH_DIM < 2)
426 return;
427
428 for (auto & node : mesh.node_ptr_range())
429 (*node)(1) *= y_scale;
430
431 // Only scale the z coordinate in 3D
432 if (LIBMESH_DIM < 3)
433 return;
434
435 for (auto & node : mesh.node_ptr_range())
436 (*node)(2) *= z_scale;
437
438 // If we just collapsed a manifold onto the X axis or XY plane
439 // then we might have changed its spatial_dimension()
441
442 // We haven't changed any topology, but just changing geometry could
443 // have invalidated a point locator.
445}
446
447
448
450{
451 LOG_SCOPE("all_tri()", "MeshTools::Modification");
452
453 if (!mesh.is_replicated() && !mesh.is_prepared())
455
456 // The number of elements in the original mesh before any additions
457 // or deletions.
458 const dof_id_type n_orig_elem = mesh.n_elem();
459 const dof_id_type max_orig_id = mesh.max_elem_id();
460
461 // We store pointers to the newly created elements in a vector
462 // until they are ready to be added to the mesh. This is because
463 // adding new elements on the fly can cause reallocation and invalidation
464 // of existing mesh element_iterators.
465 std::vector<std::unique_ptr<Elem>> new_elements;
466
467 unsigned int max_subelems = 1; // in 1D nothing needs to change
468 if (mesh.mesh_dimension() == 2) // in 2D quads can split into 2 tris
469 max_subelems = 2;
470 if (mesh.mesh_dimension() == 3) // in 3D hexes can split into 6 tets
471 max_subelems = 6;
472
473 // 2D polygons and 3D polyhedra can be split into an arbitrary
474 // number of triangles/tetrahedra depending on their topology, so we
475 // have to scan the mesh to find the largest split we will need.
476 for (const Elem * elem : mesh.element_ptr_range())
477 {
478 if (const Polygon * poly = dynamic_cast<const Polygon *>(elem))
479 max_subelems = std::max(max_subelems, poly->n_subtriangles());
480 else if (const Polyhedron * polyhedron = dynamic_cast<const Polyhedron *>(elem))
481 max_subelems = std::max(max_subelems, polyhedron->n_subelements());
482 }
483 mesh.comm().max(max_subelems);
484
485 new_elements.reserve (max_subelems*n_orig_elem);
486
487 // If the original mesh has *side* boundary data, we carry that over
488 // to the new mesh with triangular elements. We currently only
489 // support bringing over side-based BCs to the all-tri mesh, but
490 // that could probably be extended to node and edge-based BCs as
491 // well.
492 const bool mesh_has_boundary_data = (mesh.get_boundary_info().n_boundary_conds() > 0);
493
494 // Temporary vectors to store the new boundary element pointers, side numbers, and boundary ids
495 std::vector<Elem *> new_bndry_elements;
496 std::vector<unsigned short int> new_bndry_sides;
497 std::vector<boundary_id_type> new_bndry_ids;
498
499 // We may need to add new points if we run into a 1.5th order
500 // element; if we do that on a DistributedMesh in a ghost element then
501 // we will need to fix their ids / unique_ids
502 bool added_new_ghost_point = false;
503
504 // Iterate over the elements, splitting:
505 // QUADs into pairs of conforming triangles
506 // PYRAMIDs into pairs of conforming tets,
507 // PRISMs into triplets of conforming tets, and
508 // HEXs into quintets or sextets of conforming tets.
509 // We split on the shortest diagonal to give us better
510 // triangle quality in 2D, and we split based on node ids
511 // to guarantee consistency in 3D.
512 // C0POLYGONs into their sub-triangles
513 // C0POLYHEDRA into their sub-elements (currently only tets)
514
515 // FIXME: This algorithm does not work on refined grids!
516 {
517#ifdef LIBMESH_ENABLE_UNIQUE_ID
519#endif
520
521 // For avoiding extraneous allocation when building side elements
522 std::unique_ptr<const Elem> elem_side, subside_elem;
523
524 for (auto & elem : mesh.element_ptr_range())
525 {
526 const ElemType etype = elem->type();
527
528 // all_tri currently only works on coarse meshes
529 if (elem->parent())
530 libmesh_not_implemented_msg("Cannot convert a refined element into simplices\n");
531
532 // The new elements we will split the original into. Reserving
533 // for the maximum number of sub-elements created for each element
534 std::vector<std::unique_ptr<Elem>> subelem(max_subelems);
535
536 auto set_nodes = [&elem, &subelem]
537 (const std::initializer_list<std::initializer_list<int>> & node_ids) {
538 int i=0;
539 for (auto row : node_ids)
540 {
541 int j=0;
542 Elem * sub = subelem[i++].get();
543 libmesh_assert(sub);
544 for (auto node_id : row)
545 sub->set_node(j++, elem->node_ptr(node_id));
546 }
547 };
548
549 switch (etype)
550 {
551 case QUAD4:
552 {
553 subelem[0] = Elem::build(TRI3);
554 subelem[1] = Elem::build(TRI3);
555
556 // Check for possible edge swap
557 if ((elem->point(0) - elem->point(2)).norm() <
558 (elem->point(1) - elem->point(3)).norm())
559 set_nodes({{0,1,2},{0,2,3}});
560 else
561 set_nodes({{0,1,3},{1,2,3}});
562
563 break;
564 }
565
566 case QUAD8:
567 {
568 if (elem->processor_id() != mesh.processor_id())
569 added_new_ghost_point = true;
570
571 subelem[0] = Elem::build(TRI6);
572 subelem[1] = Elem::build(TRI6);
573
574 // Add a new node at the center (vertex average) of the element.
575 Node * new_node = mesh.add_point((mesh.point(elem->node_id(0)) +
576 mesh.point(elem->node_id(1)) +
577 mesh.point(elem->node_id(2)) +
578 mesh.point(elem->node_id(3)))/4,
580 elem->processor_id());
581
582 // Check for possible edge swap
583 if ((elem->point(0) - elem->point(2)).norm() <
584 (elem->point(1) - elem->point(3)).norm())
585 {
586 set_nodes({{0,1,2,4,5},{0,2,3,3,6,7}});
587 subelem[0]->set_node(5, new_node);
588 subelem[1]->set_node(3, new_node);
589 }
590 else
591 {
592 set_nodes({{3,0,1,7,4},{1,2,3,5,6}});
593 subelem[0]->set_node(5, new_node);
594 subelem[1]->set_node(5, new_node);
595 }
596
597 break;
598 }
599
600 case QUAD9:
601 {
602 subelem[0] = Elem::build(TRI6);
603 subelem[1] = Elem::build(TRI6);
604
605 // Check for possible edge swap
606 if ((elem->point(0) - elem->point(2)).norm() <
607 (elem->point(1) - elem->point(3)).norm())
608 set_nodes({{0,1,2,4,5,8},{0,2,3,8,6,7}});
609 else
610 set_nodes({{0,1,3,4,8,7},{1,2,3,5,6,8}});
611
612 break;
613 }
614
615 case HEX8:
616 {
617 BoundaryInfo & boundary_info = mesh.get_boundary_info();
618
619 // Hexes all split into six tetrahedra
620 subelem[0] = Elem::build(TET4);
621 subelem[1] = Elem::build(TET4);
622 subelem[2] = Elem::build(TET4);
623 subelem[3] = Elem::build(TET4);
624 subelem[4] = Elem::build(TET4);
625 subelem[5] = Elem::build(TET4);
626
627 // On faces, we choose the node with the highest
628 // global id, and we split on the diagonal which
629 // includes that node. This ensures that (even in
630 // parallel, even on distributed meshes) the same
631 // diagonal split will be chosen for elements on either
632 // side of the same quad face.
633 const unsigned int highest_n = highest_vertex_on(elem);
634
635 // opposing_node[n] is the local node number of the node
636 // on the farthest corner of a hex8 from local node n
637 static const std::array<unsigned int, 8> opposing_node =
638 {6, 7, 4, 5, 2, 3, 0, 1};
639
640 static const std::vector<std::vector<unsigned int>> sides_opposing_highest =
641 {{2,3,5},{3,4,5},{1,4,5},{1,2,5},{0,2,3},{0,3,4},{0,1,4},{0,1,2}};
642 static const std::vector<std::vector<unsigned int>> nodes_neighboring_highest =
643 {{1,3,4},{0,2,5},{1,3,6},{0,2,7},{0,5,7},{1,4,6},{2,5,7},{3,4,6}};
644
645 // Start by looking in three directions away from the
646 // highest-id node. In each direction there will be two
647 // different possibilities for the split depending on
648 // how the opposing face nodes are numbered.
649 //
650 // This is tricky enough that I'm not going to worry
651 // about manually keeping tets oriented; we'll just call
652 // orient() on each as we go.
653
654 unsigned int next_subelem = 0;
655 for (auto side : sides_opposing_highest[highest_n])
656 {
657 const std::vector<unsigned int> nodes_on_side =
658 elem->nodes_on_side(side);
659
660 auto [dn, dn2] = split_diagonal(elem, nodes_on_side);
661
662 unsigned int split_on_neighbor = false;
663 for (auto n : nodes_neighboring_highest[highest_n])
664 if (dn == n || dn2 == n)
665 {
666 split_on_neighbor = true;
667 break;
668 }
669
670 // Add one or two elements for each opposing side,
671 // depending on whether the diagonal split there
672 // connects to the neighboring diagonal split or
673 // not.
674 if (split_on_neighbor)
675 {
676 subelem[next_subelem]->set_node(0, elem->node_ptr(highest_n));
677 subelem[next_subelem]->set_node(1, elem->node_ptr(dn));
678 subelem[next_subelem]->set_node(2, elem->node_ptr(dn2));
679 for (auto n : nodes_on_side)
680 if (n != dn && n != dn2)
681 {
682 subelem[next_subelem]->set_node(3, elem->node_ptr(n));
683 break;
684 }
685 subelem[next_subelem]->orient(&boundary_info);
686 ++next_subelem;
687
688 subelem[next_subelem]->set_node(0, elem->node_ptr(highest_n));
689 subelem[next_subelem]->set_node(1, elem->node_ptr(dn));
690 subelem[next_subelem]->set_node(2, elem->node_ptr(dn2));
691 for (auto n : reverse(nodes_on_side))
692 if (n != dn && n != dn2)
693 {
694 subelem[next_subelem]->set_node(3, elem->node_ptr(n));
695 break;
696 }
697 subelem[next_subelem]->orient(&boundary_info);
698 ++next_subelem;
699 }
700 else
701 {
702 subelem[next_subelem]->set_node(0, elem->node_ptr(highest_n));
703 subelem[next_subelem]->set_node(1, elem->node_ptr(dn));
704 subelem[next_subelem]->set_node(2, elem->node_ptr(dn2));
705 for (auto n : nodes_on_side)
706 for (auto n2 : nodes_neighboring_highest[highest_n])
707 if (n == n2)
708 {
709 subelem[next_subelem]->set_node(3, elem->node_ptr(n));
710 goto break_both_loops;
711 }
712
713 break_both_loops:
714 subelem[next_subelem]->orient(&boundary_info);
715 ++next_subelem;
716 }
717 }
718
719 // At this point we've created between 3 and 6 tets.
720 // What's left to do depends on how many.
721
722 // If we just chopped off three vertices into three
723 // tets, then the best way to split this hex would be
724 // the symmetric five-split. Chop off the opposing
725 // vertex too, and then the remaining interior is our
726 // final tet.
727 if (next_subelem == 3)
728 {
729 subelem[next_subelem]->set_node(0, elem->node_ptr(opposing_nodes[highest_n][0]));
730 subelem[next_subelem]->set_node(1, elem->node_ptr(opposing_nodes[highest_n][1]));
731 subelem[next_subelem]->set_node(2, elem->node_ptr(opposing_nodes[highest_n][2]));
732 subelem[next_subelem]->set_node(3, elem->node_ptr(opposing_node[highest_n]));
733 subelem[next_subelem]->orient(&boundary_info);
734 ++next_subelem;
735
736 subelem[next_subelem]->set_node(0, elem->node_ptr(opposing_nodes[highest_n][0]));
737 subelem[next_subelem]->set_node(1, elem->node_ptr(opposing_nodes[highest_n][1]));
738 subelem[next_subelem]->set_node(2, elem->node_ptr(opposing_nodes[highest_n][2]));
739 subelem[next_subelem]->set_node(3, elem->node_ptr(highest_n));
740 subelem[next_subelem]->orient(&boundary_info);
741 ++next_subelem;
742
743 // We don't need the 6th tet after all
744 subelem[next_subelem].reset();
745 ++next_subelem;
746 }
747
748 // If we just chopped off one (or two) vertices into
749 // tets, then the remaining gap is best (or only) filled
750 // by pairing another tet with each.
751 if (next_subelem == 4 ||
752 next_subelem == 5)
753 {
754 for (auto side : sides_opposing_highest[highest_n])
755 {
756 const std::vector<unsigned int> nodes_on_side =
757 elem->nodes_on_side(side);
758
759 auto [dn, dn2] = split_diagonal(elem, nodes_on_side);
760
761 unsigned int split_on_neighbor = false;
762 for (auto n : nodes_neighboring_highest[highest_n])
763 if (dn == n || dn2 == n)
764 {
765 split_on_neighbor = true;
766 break;
767 }
768
769 // The two !split_on_neighbor sides are where we
770 // need the two remaining tets
771 if (!split_on_neighbor)
772 {
773 subelem[next_subelem]->set_node(0, elem->node_ptr(highest_n));
774 subelem[next_subelem]->set_node(1, elem->node_ptr(dn));
775 subelem[next_subelem]->set_node(2, elem->node_ptr(dn2));
776 subelem[next_subelem]->set_node(3, elem->node_ptr(opposing_node[highest_n]));
777 subelem[next_subelem]->orient(&boundary_info);
778 ++next_subelem;
779 }
780 }
781 }
782
783 // Whether we got there by creating six tets from the
784 // first for loop or by patching up the split afterward,
785 // we should have considered six tets (possibly
786 // including one deleted one...) at this point.
787 libmesh_assert(next_subelem == 6);
788
789 break;
790 }
791
792 case PRISM6:
793 {
794 // Prisms all split into three tetrahedra
795 subelem[0] = Elem::build(TET4);
796 subelem[1] = Elem::build(TET4);
797 subelem[2] = Elem::build(TET4);
798
799 // Triangular faces are not split.
800
801 // On quad faces, we choose the node with the highest
802 // global id, and we split on the diagonal which
803 // includes that node. This ensures that (even in
804 // parallel, even on distributed meshes) the same
805 // diagonal split will be chosen for elements on either
806 // side of the same quad face. It also ensures that we
807 // always have a mix of "clockwise" and
808 // "counterclockwise" split faces (two of one and one
809 // of the other on each prism; this is useful since the
810 // alternative all-clockwise or all-counterclockwise
811 // face splittings can't be turned into tets without
812 // adding more nodes
813
814 // Split on 0-4 diagonal
815 if (split_first_diagonal(elem, 0,4, 1,3))
816 {
817 // Split on 0-5 diagonal
818 if (split_first_diagonal(elem, 0,5, 2,3))
819 {
820 // Split on 1-5 diagonal
821 if (split_first_diagonal(elem, 1,5, 2,4))
822 set_nodes({{0,4,5,3},{0,4,1,5},{0,1,2,5}});
823 else // Split on 2-4 diagonal
824 {
825 libmesh_assert (split_first_diagonal(elem, 2,4, 1,5));
826 set_nodes({{0,4,5,3},{0,4,2,5},{0,1,2,4}});
827 }
828 }
829 else // Split on 2-3 diagonal
830 {
831 libmesh_assert (split_first_diagonal(elem, 2,3, 0,5));
832
833 // 0-4 and 2-3 split implies 2-4 split
834 libmesh_assert (split_first_diagonal(elem, 2,4, 1,5));
835
836 set_nodes({{0,4,2,3},{3,4,2,5},{0,1,2,4}});
837 }
838 }
839 else // Split on 1-3 diagonal
840 {
841 libmesh_assert (split_first_diagonal(elem, 1,3, 0,4));
842
843 // Split on 0-5 diagonal
844 if (split_first_diagonal(elem, 0,5, 2,3))
845 {
846 // 1-3 and 0-5 split implies 1-5 split
847 libmesh_assert (split_first_diagonal(elem, 1,5, 2,4));
848
849 set_nodes({{1,3,4,5},{1,0,3,5},{0,1,2,5}});
850 }
851 else // Split on 2-3 diagonal
852 {
853 libmesh_assert (split_first_diagonal(elem, 2,3, 0,5));
854
855 // Split on 1-5 diagonal
856 if (split_first_diagonal(elem, 1,5, 2,4))
857 set_nodes({{0,1,2,3},{3,1,2,5},{1,3,4,5}});
858 else // Split on 2-4 diagonal
859 {
860 libmesh_assert (split_first_diagonal(elem, 2,4, 1,5));
861 set_nodes({{0,1,2,3},{2,3,4,5},{3,1,2,4}});
862 }
863 }
864 }
865
866 break;
867 }
868
869 case PRISM20:
870 case PRISM21:
871 libmesh_experimental(); // We should upgrade this to TET14...
872 libmesh_fallthrough();
873 case PRISM18:
874 {
875 subelem[0] = Elem::build(TET10);
876 subelem[1] = Elem::build(TET10);
877 subelem[2] = Elem::build(TET10);
878
879 // Split on 0-4 diagonal
880 if (split_first_diagonal(elem, 0,4, 1,3))
881 {
882 // Split on 0-5 diagonal
883 if (split_first_diagonal(elem, 0,5, 2,3))
884 {
885 // Split on 1-5 diagonal
886 if (split_first_diagonal(elem, 1,5, 2,4))
887 set_nodes({{0,4,5,3,15,13,17,9,12,14},
888 {0,4,1,5,15,10,6,17,13,16},
889 {0,1,2,5,6,7,8,17,16,11}});
890 else // Split on 2-4 diagonal
891 {
892 libmesh_assert (split_first_diagonal(elem, 2,4, 1,5));
893
894 set_nodes({{0,4,5,3,15,13,17,9,12,14},
895 {0,4,2,5,15,16,8,17,13,11},
896 {0,1,2,4,6,7,8,15,10,16}});
897 }
898 }
899 else // Split on 2-3 diagonal
900 {
901 libmesh_assert (split_first_diagonal(elem, 2,3, 0,5));
902
903 // 0-4 and 2-3 split implies 2-4 split
904 libmesh_assert (split_first_diagonal(elem, 2,4, 1,5));
905
906 set_nodes({{0,4,2,3,15,16,8,9,12,17},
907 {3,4,2,5,12,16,17,14,13,11},
908 {0,1,2,4,6,7,8,15,10,16}});
909 }
910 }
911 else // Split on 1-3 diagonal
912 {
913 libmesh_assert (split_first_diagonal(elem, 1,3, 0,4));
914
915 // Split on 0-5 diagonal
916 if (split_first_diagonal(elem, 0,5, 2,3))
917 {
918 // 1-3 and 0-5 split implies 1-5 split
919 libmesh_assert (split_first_diagonal(elem, 1,5, 2,4));
920
921 set_nodes({{1,3,4,5,15,12,10,16,14,13},
922 {1,0,3,5,6,9,15,16,17,14},
923 {0,1,2,5,6,7,8,17,16,11}});
924 }
925 else // Split on 2-3 diagonal
926 {
927 libmesh_assert (split_first_diagonal(elem, 2,3, 0,5));
928
929 // Split on 1-5 diagonal
930 if (split_first_diagonal(elem, 1,5, 2,4))
931 set_nodes({{0,1,2,3,6,7,8,9,15,17},
932 {3,1,2,5,15,7,17,14,16,11},
933 {1,3,4,5,15,12,10,16,14,13}});
934 else // Split on 2-4 diagonal
935 {
936 libmesh_assert (split_first_diagonal(elem, 2,4, 1,5));
937
938 set_nodes({{0,1,2,3,6,7,8,9,15,17},
939 {2,3,4,5,17,12,16,11,14,13},
940 {3,1,2,4,15,7,17,12,10,16}});
941 }
942 }
943 }
944
945 break;
946 }
947
948 case PYRAMID5:
949 {
950 // Pyramids all split into two tetrahedra
951 subelem[0] = Elem::build(TET4);
952 subelem[1] = Elem::build(TET4);
953
954 // Choose how to split the quad face in a way that will
955 // be consistent from possibly-different-type elements
956 // splitting from the other side
957 //
958 // Split on 0-2 diagonal
959 if (split_first_diagonal(elem, 0,2, 1,3))
960 set_nodes({{0,1,2,4},{0,2,3,4}});
961 // Split on 1-3 diagonal
962 else
963 {
964 libmesh_assert (split_first_diagonal(elem, 1,3, 0,2));
965 set_nodes({{0,1,3,4},{1,2,3,4}});
966 }
967
968 break;
969 }
970
971 case PYRAMID14:
972 {
973 // Pyramids all split into two tetrahedra
974 subelem[0] = Elem::build(TET10);
975 subelem[1] = Elem::build(TET10);
976
977 // Choose how to split the quad face in a way that will
978 // be consistent from possibly-different-type elements
979 // splitting from the other side
980 //
981 // Split on 0-2 diagonal
982 if (split_first_diagonal(elem, 0,2, 1,3))
983 set_nodes({{0,1,2,4,5,6,13,9,10,11},
984 {0,2,3,4,13,7,8,9,11,12}});
985 // Split on 1-3 diagonal
986 else
987 {
988 libmesh_assert (split_first_diagonal(elem, 1,3, 0,2));
989 set_nodes({{0,1,3,4,5,13,8,9,10,12},
990 {1,2,3,4,6,7,13,10,11,12}});
991 }
992
993 break;
994 }
995
996 case C0POLYGON:
997 {
998 // Split a C0Polygon into the triangles defined by its
999 // current triangulation. This relies on the user having
1000 // a valid triangulation (the constructor sets a default
1001 // one, and the user can refresh it via retriangulate()
1002 // after moving nodes).
1003 const C0Polygon * polygon = cast_ptr<const C0Polygon *>(elem);
1004 const unsigned int n_subtri = polygon->n_subtriangles();
1005 for (unsigned int t = 0; t != n_subtri; ++t)
1006 {
1007 const std::array<int, 3> tri = polygon->subtriangle(t);
1008 if (tri[0] < 0 || tri[1] < 0 || tri[2] < 0)
1009 libmesh_not_implemented_msg
1010 ("Cannot convert a C0Polygon whose triangulation\n"
1011 "introduces special (non-vertex) points");
1012 subelem[t] = Elem::build(TRI3);
1013 subelem[t]->set_node(0, elem->node_ptr(tri[0]));
1014 subelem[t]->set_node(1, elem->node_ptr(tri[1]));
1015 subelem[t]->set_node(2, elem->node_ptr(tri[2]));
1016 }
1017
1018 break;
1019 }
1020
1021 case C0POLYHEDRON:
1022 {
1023 // Split a C0Polyhedron into the tetrahedra defined by its
1024 // current tetrahedralization. If the polyhedron required
1025 // a mid-element node, the user is expected to have added
1026 // that node to the mesh during construction; we just
1027 // reference it via the polyhedron's node pointers.
1028 const C0Polyhedron * polyhedron =
1029 cast_ptr<const C0Polyhedron *>(elem);
1030 const unsigned int n_sub = polyhedron->n_subelements();
1031 for (unsigned int t = 0; t != n_sub; ++t)
1032 {
1033 const std::array<int, 4> tet = polyhedron->subelement(t);
1034 if (tet[0] < 0 || tet[1] < 0 || tet[2] < 0 || tet[3] < 0)
1035 libmesh_not_implemented_msg
1036 ("Cannot convert a C0Polyhedron whose triangulation\n"
1037 "introduces special (non-vertex) points");
1038 subelem[t] = Elem::build(TET4);
1039 subelem[t]->set_node(0, elem->node_ptr(tet[0]));
1040 subelem[t]->set_node(1, elem->node_ptr(tet[1]));
1041 subelem[t]->set_node(2, elem->node_ptr(tet[2]));
1042 subelem[t]->set_node(3, elem->node_ptr(tet[3]));
1043 }
1044 // There is a concern that two neighbor polyhedra might have
1045 // a triangulation of a side that does not match. But the
1046 // default triangulation is based on the side's triangulation
1047 // and the side element is supposed to be shared (that's why
1048 // shared pointers to polygons are used to build the polyhedra).
1049 // So the default one should work.
1050
1051 break;
1052 }
1053
1054 // No need to split elements that are already simplicial:
1055 case EDGE2:
1056 case EDGE3:
1057 case EDGE4:
1058 case TRI3:
1059 case TRI6:
1060 case TRI7:
1061 case TET4:
1062 case TET10:
1063 case TET14:
1064 case INFEDGE2:
1065 // No way to split infinite quad/prism elements, so
1066 // hopefully no need to
1067 case INFQUAD4:
1068 case INFQUAD6:
1069 case INFPRISM6:
1070 case INFPRISM12:
1071 continue;
1072 // If we're left with an unimplemented element we're
1073 // probably out of luck. TODO: implement hex20, hex27,
1074 // pyramid13,...
1075 default:
1076 libmesh_not_implemented_msg
1077 ("Error, encountered unimplemented element "
1078 << Utility::enum_to_string<ElemType>(etype)
1079 << " in MeshTools::Modification::all_tri()...");
1080 } // end switch (etype)
1081
1082 // Be sure the correct data is set for all subelems.
1083 const unsigned int nei = elem->n_extra_integers();
1084 for (unsigned int i=0; i != max_subelems; ++i)
1085 if (subelem[i]) {
1086 subelem[i]->processor_id() = elem->processor_id();
1087 subelem[i]->subdomain_id() = elem->subdomain_id();
1088
1089 // Copy any extra element data. Since the subelements
1090 // haven't been added to the mesh yet any allocation has
1091 // to be done manually.
1092 subelem[i]->add_extra_integers(nei);
1093 for (unsigned int ei=0; ei != nei; ++ei)
1094 subelem[ei]->set_extra_integer(ei, elem->get_extra_integer(ei));
1095
1096
1097 // Copy any mapping data.
1098 subelem[i]->set_mapping_type(elem->mapping_type());
1099 subelem[i]->set_mapping_data(elem->mapping_data());
1100 }
1101
1102 // On a mesh with boundary data, we need to move that data to
1103 // the new elements.
1104
1105 // On a mesh which is distributed, we need to move
1106 // remote_elem links to the new elements.
1107 bool mesh_is_serial = mesh.is_serial();
1108
1109 if (mesh_has_boundary_data || !mesh_is_serial)
1110 {
1111 // Container to key boundary IDs handed back by the BoundaryInfo object.
1112 std::vector<boundary_id_type> bc_ids;
1113
1114 for (auto sn : elem->side_index_range())
1115 {
1116 mesh.get_boundary_info().boundary_ids(elem, sn, bc_ids);
1117
1118 if (bc_ids.empty() && elem->neighbor_ptr(sn) != remote_elem)
1119 continue;
1120
1121 // Make a sorted list of node ids for elem->side(sn)
1122 elem->build_side_ptr(elem_side, sn);
1123 std::vector<dof_id_type> elem_side_nodes(elem_side->n_nodes());
1124 for (unsigned int esn=0,
1125 n_esn = cast_int<unsigned int>(elem_side_nodes.size());
1126 esn != n_esn; ++esn)
1127 elem_side_nodes[esn] = elem_side->node_id(esn);
1128 std::sort(elem_side_nodes.begin(), elem_side_nodes.end());
1129
1130 for (unsigned int i=0; i != max_subelems; ++i)
1131 if (subelem[i])
1132 {
1133 for (auto subside : subelem[i]->side_index_range())
1134 {
1135 subelem[i]->build_side_ptr(subside_elem, subside);
1136
1137 // Make a list of *vertex* node ids for this subside, see if they are all present
1138 // in elem->side(sn). Note 1: we can't just compare elem->key(sn) to
1139 // subelem[i]->key(subside) in the Prism cases, since the new side is
1140 // a different type. Note 2: we only use vertex nodes since, in the future,
1141 // a Hex20 or Prism15's QUAD8 face may be split into two Tri6 faces, and the
1142 // original face will not contain the mid-edge node.
1143 std::vector<dof_id_type> subside_nodes(subside_elem->n_vertices());
1144 for (unsigned int ssn=0,
1145 n_ssn = cast_int<unsigned int>(subside_nodes.size());
1146 ssn != n_ssn; ++ssn)
1147 subside_nodes[ssn] = subside_elem->node_id(ssn);
1148 std::sort(subside_nodes.begin(), subside_nodes.end());
1149
1150 // std::includes returns true if every element of the second sorted range is
1151 // contained in the first sorted range.
1152 if (std::includes(elem_side_nodes.begin(), elem_side_nodes.end(),
1153 subside_nodes.begin(), subside_nodes.end()))
1154 {
1155 for (const auto & b_id : bc_ids)
1156 if (b_id != BoundaryInfo::invalid_id)
1157 {
1158 new_bndry_ids.push_back(b_id);
1159 new_bndry_elements.push_back(subelem[i].get());
1160 new_bndry_sides.push_back(subside);
1161 }
1162
1163 // If the original element had a RemoteElem neighbor on side 'sn',
1164 // then the subelem has one on side 'subside'.
1165 if (elem->neighbor_ptr(sn) == remote_elem)
1166 subelem[i]->set_neighbor(subside, const_cast<RemoteElem*>(remote_elem));
1167 }
1168 }
1169 } // end for loop over subelem
1170 } // end for loop over sides
1171
1172 // Remove the original element from the BoundaryInfo structure.
1174
1175 } // end if (mesh_has_boundary_data)
1176
1177 // Determine new IDs for the split elements which will be
1178 // the same on all processors, therefore keeping the Mesh
1179 // in sync. Note: we offset the new IDs by max_orig_id to
1180 // avoid overwriting any of the original IDs.
1181 for (unsigned int i=0; i != max_subelems; ++i)
1182 if (subelem[i])
1183 {
1184 // Determine new IDs for the split elements which will be
1185 // the same on all processors, therefore keeping the Mesh
1186 // in sync. Note: we offset the new IDs by the max of the
1187 // pre-existing ids to avoid conflicting with originals.
1188 subelem[i]->set_id( max_orig_id + max_subelems*elem->id() + i );
1189
1190#ifdef LIBMESH_ENABLE_UNIQUE_ID
1191 subelem[i]->set_unique_id(max_unique_id + max_subelems*elem->unique_id() + i);
1192#endif
1193
1194 // Prepare to add the newly-created simplices
1195 new_elements.push_back(std::move(subelem[i]));
1196 }
1197
1198 // Delete the original element
1199 mesh.delete_elem(elem);
1200 } // End for loop over elements
1201 } // end scope
1202
1203
1204 // Now, iterate over the new elements vector, and add them each to
1205 // the Mesh.
1206 for (auto & elem : new_elements)
1207 mesh.add_elem(std::move(elem));
1208
1209 if (mesh_has_boundary_data)
1210 {
1211 // If the old mesh had boundary data, the new mesh better have
1212 // some. However, we can't assert that the size of
1213 // new_bndry_elements vector is > 0, since we may not have split
1214 // any elements actually on the boundary. We also can't assert
1215 // that the original number of boundary sides is equal to the
1216 // sum of the boundary sides currently in the mesh and the
1217 // newly-added boundary sides, since in 3D, we may have split a
1218 // boundary QUAD into two boundary TRIs. Therefore, we won't be
1219 // too picky about the actual number of BCs, and just assert that
1220 // there are some, somewhere.
1221#ifndef NDEBUG
1222 bool nbe_nonempty = new_bndry_elements.size();
1223 mesh.comm().max(nbe_nonempty);
1224 libmesh_assert(nbe_nonempty ||
1226#endif
1227
1228 // We should also be sure that the lengths of the new boundary data vectors
1229 // are all the same.
1230 libmesh_assert_equal_to (new_bndry_elements.size(), new_bndry_sides.size());
1231 libmesh_assert_equal_to (new_bndry_sides.size(), new_bndry_ids.size());
1232
1233 // Add the new boundary info to the mesh
1234 for (auto s : index_range(new_bndry_elements))
1235 mesh.get_boundary_info().add_side(new_bndry_elements[s],
1236 new_bndry_sides[s],
1237 new_bndry_ids[s]);
1238 }
1239
1240 // In a DistributedMesh any newly added ghost node ids may be
1241 // inconsistent, and unique_ids of newly added ghost nodes remain
1242 // unset.
1243 // make_nodes_parallel_consistent() will fix all this.
1244 if (!mesh.is_serial())
1245 {
1246 mesh.comm().max(added_new_ghost_point);
1247
1248 if (added_new_ghost_point)
1250 }
1251
1252 // Prepare the newly created mesh for use.
1254
1255 // Let the new_elements and new_bndry_elements vectors go out of scope.
1256}
1257
1258
1259
1261{
1262 LOG_SCOPE("all_rbb()", "MeshTools::Modification");
1263
1264 // By default, use 1.0 as the weight on every RATIONAL_BERNSTEIN
1265 // mapped node
1266 const Real default_weight = 1.0;
1267
1268 const auto weight_index =
1269 (mesh.add_node_datum<Real>("rational_weight", true,
1270 &default_weight));
1271
1273 mesh.set_default_mapping_data(weight_index);
1274
1275 // Out of loop to reduce heap allocations
1276 std::unique_ptr<Elem> edge_ptr, face_ptr;
1277
1278 for (auto & elem : mesh.element_ptr_range())
1279 {
1280 if (elem->level())
1281 libmesh_not_implemented_msg
1282 ("all_rbb() currently only supports flat meshes with no refinement levels");
1283
1284#ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
1285 if (elem->infinite())
1286 libmesh_not_implemented_msg
1287 ("all_rbb() currently only supports finite geometric elements");
1288#endif
1289
1291 elem->set_mapping_data(weight_index);
1292
1293 // Nothing to do unless we have curves to correct
1294 if (elem->default_order() == FIRST)
1295 continue;
1296
1297 // Modify the center node of an "edge" - possibly an actual edge
1298 // element's node, possibly a center node between points on a
1299 // face's or cell's edge - for RBB interpolation. This should fit
1300 // a circular curve exactly in cases where the original nodes are
1301 // equispaced and the outer nodes' weights are equal, and should
1302 // be a good fit otherwise.
1303 //
1304 // We want to use this to interpolate "internal" conceptual
1305 // edges of a Hex27 too, so we'll handle the cases where w0 and
1306 // w1 aren't 1, as well as the cases where the Nodes n0 and n1
1307 // are already control points which don't match their
1308 // corresponding physical points.
1309 auto make_edge_rbb = [default_weight, weight_index]
1310 (const Node & n0, const Node & n1, Node & n_center,
1311 const Point & p0, const Point & p1)
1312 {
1313 // Skip edges we've already modified; the center node for
1314 // these is no longer at the curve point we wish to
1315 // interpolate, it should already be at the control point that
1316 // accomplishes the interpolation.
1317 const Real old_weight = n_center.get_extra_datum<Real>(weight_index);
1318 if (old_weight != default_weight)
1319 return;
1320
1321 Point & p2 = n_center;
1322
1323 const Real w0 = n0.get_extra_datum<Real>(weight_index);
1324 const Real w1 = n1.get_extra_datum<Real>(weight_index);
1325
1326 const Point e02 = p2-p0,
1327 e21 = p1-p2;
1328 const Real chord_02_len_sq = e02.norm_sq(),
1329 chord_21_len_sq = e21.norm_sq();
1330
1331 // First find the cosine of phi, the angle between our two
1332 // subchords (turning from the direction of one to the
1333 // direction of the other; this is the supplementary angle to
1334 // the angle at the midpoint). This is the same as half of
1335 // the angle of our circular arc, which nicely enough is also
1336 // the angle we take cos and sec of in NURBS formulae
1337 const Real cos_phi = (e02*e21)/std::sqrt(chord_02_len_sq*chord_21_len_sq);
1338
1339 // There's a way to do really large arcs using negative
1340 // weights, but we're going to get lousy approximation quality
1341 // from isoparametric elements if we go too low, as well as
1342 // bad numerics here, so let's just disallow it.
1343 if (cos_phi < 0.5)
1344 libmesh_not_implemented_msg
1345 ("all_rbb() is not recommended for extremely sharp curves on one edge");
1346
1347 const Real w_center = cos_phi*std::sqrt(w0*w1);
1348
1349 n_center.set_extra_datum<Real>(weight_index, w_center);
1350
1351 // Now let's get the control point location. This comes from
1352 // a lot of back-and-forth with Gemini, but fortunately I'm
1353 // rewriting it after I've already added unit tests that
1354 // should scream if it's badly wrong.
1355 const Real w_mid = w0/4 + w1/4 + w_center/2;
1356 n_center *= 2*w_mid;
1357 n_center -= (w0 * p0 + w1 * p1)/2;
1358 n_center /= w_center;
1359 };
1360
1361 auto make_face_rbb = [weight_index] (Elem & face)
1362 {
1363 // Prisms and pyramids may need to skip some faces while
1364 // adjusting others
1365 if (face.type() == TRI6)
1366 return;
1367
1368 if (face.type() != QUAD9)
1369 libmesh_not_implemented_msg
1370 ("all_rbb() currently only supports mid-face nodes on Quad9 faces");
1371
1372 // We only use [4,8) but matching indices is nice and stack is
1373 // cheap.
1374 Real w[9];
1375
1376 for (unsigned int i : make_range(4u, 8u))
1377 w[i] = face.node_ref(i).get_extra_datum<Real>(weight_index);
1378
1379 // We can't currently handle arbitrary vertex weights
1380#ifndef NDEBUG
1381 for (unsigned int i : make_range(4u))
1382 libmesh_assert_equal_to
1383 (face.node_ref(i).get_extra_datum<Real>(weight_index), 1);
1384#endif
1385
1386 // For the mid-face point, if we want to exactly match
1387 // any cylinders and cones and spheres, we're actually already
1388 // entirely constrained by the other points.
1389 //
1390 // This formula gives the minimum-energy Steiner surface based
1391 // on the outer 8 points.
1392 //
1393 // That's an isogeometric representation of a cylinder aligned
1394 // to either axis, or of a sphere where the quad edges are on
1395 // latitude/longitude lines, or of a cone where two edges are
1396 // segments of cone generating lines and the other two are
1397 // arcs perpendicular to the axis.
1398 //
1399 // It's not perfectly isogeometric for the spheres we generate
1400 // (where the quad edges are all great circles), but it should
1401 // still converge asymptotically faster than non-rational
1402 // quadratic Lagrange.
1403 const Point xi_avg = (face.point(7) + face.point(5))/2;
1404 const Point eta_avg = (face.point(4) + face.point(6))/2;
1405 const Point vertex_avg = (face.point(0) + face.point(1) +
1406 face.point(2) + face.point(3))/4;
1407
1408 const Real w_xi = (w[7] + w[5])/2;
1409 const Real w_eta = (w[4] + w[6])/2;
1410 const Real w_mid = w_xi * w_eta;
1411
1412 Node & midnode = face.node_ref(8);
1413 midnode.set_extra_datum<Real>(weight_index, w_mid);
1414 midnode = ((1+w_mid)/(w_xi+w_eta) * (w_xi*xi_avg + w_eta*eta_avg) - vertex_avg)/w_mid;
1415 };
1416
1417 // If we're on a Hex27, our formula for the mid-volume node
1418 // relies on the locations of the mid-face points. We could
1419 // re-calculate those later but let's just save them now.
1420 Point midfacepts[6];
1421 if (elem->type() == HEX27)
1422 for (auto i : make_range(6))
1423 midfacepts[i] = elem->point(20+i);
1424
1425 // Check each edge for a curve, and adjust it if needed.
1426 for (auto e : elem->edge_index_range())
1427 {
1428 elem->build_edge_ptr(edge_ptr, e);
1429
1430 // We should add EDGE4 once we have QUAD16/TRI10/HEX64 to
1431 // use it
1432 if (edge_ptr->type() != EDGE3)
1433 libmesh_not_implemented_msg
1434 ("all_rbb() currently only supports meshes with 2- and/or 3-node edges");
1435
1436 make_edge_rbb(edge_ptr->node_ref(0), edge_ptr->node_ref(1),
1437 edge_ptr->node_ref(2),
1438 edge_ptr->node_ref(0), edge_ptr->node_ref(1));
1439
1440 }
1441
1442 // If we're in 3D, we may have face nodes that also need to be
1443 // adjusted to replace an interpolated curve with a spline
1444 // curve. We know what to do with a quad face, but we'll have
1445 // to scream and die if we see a Tri7 face node.
1446 bool check_face_points = (elem->dim() > 2) &&
1447 (elem->n_nodes() > elem->n_edges() + elem->n_vertices());
1448
1449 if (check_face_points)
1450 for (auto f : elem->side_index_range())
1451 {
1452 // Prisms and pyramids may need to skip some faces while
1453 // adjusting others
1454 if (elem->side_type(f) == TRI6)
1455 continue;
1456
1457 elem->build_side_ptr(face_ptr, f);
1458
1459 make_face_rbb(*face_ptr);
1460 }
1461
1462 bool check_interior_points =
1463 elem->n_nodes() > elem->n_edges() + elem->n_vertices() + elem->n_faces();
1464
1465 if (check_interior_points)
1466 {
1467 if (elem->type() == EDGE3)
1468 {
1469 make_edge_rbb(elem->node_ref(0), elem->node_ref(1),
1470 elem->node_ref(2),
1471 elem->node_ref(0), elem->node_ref(1));
1472 }
1473 else if (elem->dim() == 2)
1474 {
1475 make_face_rbb(*elem);
1476 }
1477 else if (elem->type() == HEX27)
1478 {
1479 // We still have the midnode left to go. We want
1480 // something here that will preserve the tensor product
1481 // structure for 2.5D extrusions of IGA faces, but also
1482 // be at least near to the minimum-energy control point
1483 // and weight for general cases. We'll treat opposing
1484 // mid-face nodes as the endpoints of a (more general
1485 // than our edges, since they might have non-1 weights)
1486 // Edge3, and see what we'd need on the midnode to
1487 // interpolate the center point with them. If we've got
1488 // something isogeometric like an extrusion then our
1489 // results should agree; for a quick-but-good output in
1490 // general we'll take an average.
1491 const int opposite_sides[3][2] = {{0,5}, {1,3}, {2,4}};
1492
1493 Node & midnode = elem->node_ref(26);
1494 const Point original_midpoint = midnode;
1495
1496 // Averaging in projective space
1497 Point sum_weighted_point = 0;
1498 Real sum_weight = 0;
1499
1500 for (int i : make_range(3))
1501 {
1502 Node & n0 = elem->node_ref(20+opposite_sides[i][0]);
1503 Node & n1 = elem->node_ref(20+opposite_sides[i][1]);
1504
1505 make_edge_rbb(n0, n1, midnode,
1506 midfacepts[opposite_sides[i][0]],
1507 midfacepts[opposite_sides[i][1]]);
1508
1509 const Real midweight =
1510 midnode.get_extra_datum<Real>(weight_index);
1511 sum_weight += midweight;
1512 sum_weighted_point += midweight * midnode;
1513
1514 // Reset for next run
1515 midnode = original_midpoint;
1516 midnode.set_extra_datum<Real>(weight_index,
1517 default_weight);
1518
1519 }
1520
1521 const Real midweight = sum_weight/3;
1522 midnode.set_extra_datum<Real>(weight_index,
1523 midweight);
1524
1525 midnode = sum_weighted_point / 3 / midweight;
1526 }
1527 else
1528 libmesh_not_implemented_msg
1529 ("all_rbb() doesn't yet support " << elem->type());
1530 }
1531 }
1532}
1533
1534
1535
1537 const unsigned int n_iterations,
1538 const Real power)
1539{
1543 libmesh_assert_equal_to (mesh.mesh_dimension(), 2);
1544
1545 /*
1546 * Create a quickly-searchable list of boundary nodes.
1547 */
1548 std::unordered_set<dof_id_type> boundary_node_ids =
1550
1551 // For avoiding extraneous element side allocation
1552 ElemSideBuilder side_builder;
1553
1554 for (unsigned int iter=0; iter<n_iterations; iter++)
1555 {
1556 /*
1557 * loop over the mesh refinement level
1558 */
1559 unsigned int n_levels = MeshTools::n_levels(mesh);
1560 for (unsigned int refinement_level=0; refinement_level != n_levels;
1561 refinement_level++)
1562 {
1563 // initialize the storage (have to do it on every level to get empty vectors
1564 std::vector<Point> new_positions;
1565 std::vector<Real> weight;
1566 new_positions.resize(mesh.n_nodes());
1567 weight.resize(mesh.n_nodes());
1568
1569 {
1570 // Loop over the elements to calculate new node positions
1571 for (const auto & elem : as_range(mesh.level_elements_begin(refinement_level),
1572 mesh.level_elements_end(refinement_level)))
1573 {
1574 /*
1575 * We relax all nodes on level 0 first
1576 * If the element is refined (level > 0), we interpolate the
1577 * parents nodes with help of the embedding matrix
1578 */
1579 if (refinement_level == 0)
1580 {
1581 for (auto s : elem->side_index_range())
1582 {
1583 /*
1584 * Only operate on sides which are on the
1585 * boundary or for which the current element's
1586 * id is greater than its neighbor's.
1587 * Sides get only built once.
1588 */
1589 if ((elem->neighbor_ptr(s) != nullptr) &&
1590 (elem->id() > elem->neighbor_ptr(s)->id()))
1591 {
1592 const Elem & side = side_builder(*elem, s);
1593 const Node & node0 = side.node_ref(0);
1594 const Node & node1 = side.node_ref(1);
1595
1596 Real node_weight = 1.;
1597 // calculate the weight of the nodes
1598 if (power > 0)
1599 {
1600 Point diff = node0-node1;
1601 node_weight = std::pow(diff.norm(), power);
1602 }
1603
1604 const dof_id_type id0 = node0.id(), id1 = node1.id();
1605 new_positions[id0].add_scaled( node1, node_weight );
1606 new_positions[id1].add_scaled( node0, node_weight );
1607 weight[id0] += node_weight;
1608 weight[id1] += node_weight;
1609 }
1610 } // element neighbor loop
1611 }
1612#ifdef LIBMESH_ENABLE_AMR
1613 else // refinement_level > 0
1614 {
1615 /*
1616 * Find the positions of the hanging nodes of refined elements.
1617 * We do this by calculating their position based on the parent
1618 * (one level less refined) element, and the embedding matrix
1619 */
1620
1621 const Elem * parent = elem->parent();
1622
1623 /*
1624 * find out which child I am
1625 */
1626 unsigned int c = parent->which_child_am_i(elem);
1627 /*
1628 *loop over the childs (that is, the current elements) nodes
1629 */
1630 for (auto nc : elem->node_index_range())
1631 {
1632 /*
1633 * the new position of the node
1634 */
1635 Point point;
1636 for (auto n : parent->node_index_range())
1637 {
1638 /*
1639 * The value from the embedding matrix
1640 */
1641 const Real em_val = parent->embedding_matrix(c,nc,n);
1642
1643 if (em_val != 0.)
1644 point.add_scaled (parent->point(n), em_val);
1645 }
1646
1647 const dof_id_type id = elem->node_ptr(nc)->id();
1648 new_positions[id] = point;
1649 weight[id] = 1.;
1650 }
1651 } // if element refinement_level
1652#endif // #ifdef LIBMESH_ENABLE_AMR
1653
1654 } // element loop
1655
1656 /*
1657 * finally reposition the vertex nodes
1658 */
1659 for (auto nid : make_range(mesh.n_nodes()))
1660 if (!boundary_node_ids.count(nid) && weight[nid] > 0.)
1661 mesh.node_ref(nid) = new_positions[nid]/weight[nid];
1662 }
1663
1664 // Now handle the additional second_order nodes by calculating
1665 // their position based on the vertex positions
1666 // we do a second loop over the level elements
1667 for (auto & elem : as_range(mesh.level_elements_begin(refinement_level),
1668 mesh.level_elements_end(refinement_level)))
1669 {
1670 const unsigned int son_begin = elem->n_vertices();
1671 const unsigned int son_end = elem->n_nodes();
1672 for (unsigned int n=son_begin; n<son_end; n++)
1673 {
1674 const unsigned int n_adjacent_vertices =
1676
1677 Point point;
1678 for (unsigned int v=0; v<n_adjacent_vertices; v++)
1679 point.add(elem->point( elem->second_order_adjacent_vertex(n,v) ));
1680
1681 const dof_id_type id = elem->node_ptr(n)->id();
1682 mesh.node_ref(id) = point/n_adjacent_vertices;
1683 }
1684 }
1685 } // refinement_level loop
1686 } // end iteration
1687
1688 // We haven't changed any topology, but just changing geometry could
1689 // have invalidated a point locator.
1691}
1692
1693
1694
1696 const Surface & surface,
1697 std::set<std::size_t> ids,
1698 bool use_boundary_nodes)
1699{
1700 const bool is_serial = mesh.is_serial();
1701 const processor_id_type mesh_pid = mesh.processor_id();
1702
1703 // We might have to move ghost nodes on a distributed mesh if their
1704 // owners don't see a requisite element or boundary they're on.
1705 std::unordered_set<dof_id_type> moved_ghost_nodes;
1706
1707 auto move_node = [& moved_ghost_nodes, & surface, is_serial, mesh_pid]
1708 (Node & node) {
1709 node = surface.closest_point(node);
1710
1711 if (!is_serial && node.processor_id() != mesh_pid)
1712 moved_ghost_nodes.insert(node.id());
1713 };
1714
1715 const bool no_ids = ids.empty();
1716 const BoundaryInfo & boundary_info = mesh.get_boundary_info();
1717
1718 for (const auto & elem : mesh.active_element_ptr_range())
1719 {
1720 if (elem->mapping_type() != LAGRANGE_MAP)
1721 libmesh_not_implemented();
1722
1723 if (use_boundary_nodes)
1724 {
1725 for (auto s : elem->side_index_range())
1726 {
1727 if (no_ids)
1728 {
1729 // If we're not using boundary ids, we're
1730 // interpolating all external and no internal
1731 // boundaries
1732 if (elem->neighbor_ptr(s))
1733 continue;
1734 }
1735 else
1736 {
1737 if (std::none_of(ids.begin(), ids.end(),
1738 [&boundary_info,elem,s](std::size_t bcid)
1739 {return boundary_info.has_boundary_id(elem, s, bcid);}))
1740 continue;
1741 }
1742
1743 for (auto n : elem->nodes_on_side(s))
1744 move_node(elem->node_ref(n));
1745 }
1746 }
1747 else
1748 {
1749 if (no_ids || ids.count(elem->subdomain_id()))
1750 for (Node & node : elem->node_ref_range())
1751 move_node(node);
1752 }
1753 }
1754
1755 if (!is_serial)
1756 {
1757 std::map<processor_id_type, std::vector<dof_id_type>> moved_nodes_map;
1758 for (auto id : moved_ghost_nodes)
1759 {
1760 const Node & node = mesh.node_ref(id);
1761 moved_nodes_map[node.processor_id()].push_back(node.id());
1762 }
1763
1764 auto action_functor =
1765 [& mesh, & surface]
1766 (processor_id_type /* pid */,
1767 const std::vector<dof_id_type> & my_moved_nodes)
1768 {
1769 for (auto id : my_moved_nodes)
1770 {
1771 Node & node = mesh.node_ref(id);
1772 node = surface.closest_point(node);
1773 }
1774 };
1775
1776 // First get new node positions to their owners
1778 (mesh.comm(), moved_nodes_map, action_functor);
1779
1780 // Then get node positions to anyone else with them ghosted
1781 SyncNodalPositions sync_object(mesh);
1783 (mesh.comm(), mesh.nodes_begin(), mesh.nodes_end(),
1784 sync_object);
1785 }
1786
1787 // We haven't changed any topology, but just changing geometry could
1788 // have invalidated a point locator.
1790}
1791
1792
1793
1794#ifdef LIBMESH_ENABLE_AMR
1796{
1798
1799 // Algorithm:
1800 // .) For each active element in the mesh: construct a
1801 // copy which is the same in every way *except* it is
1802 // a level 0 element. Store the pointers to these in
1803 // a separate vector. Save any boundary information as well.
1804 // Delete the active element from the mesh.
1805 // .) Loop over all (remaining) elements in the mesh, delete them.
1806 // .) Add the level-0 copies back to the mesh
1807
1808 // Temporary storage for new element pointers
1809 std::vector<std::unique_ptr<Elem>> new_elements;
1810
1811 // BoundaryInfo Storage for element ids, sides, and BC ids
1812 std::vector<Elem *> saved_boundary_elements;
1813 std::vector<boundary_id_type> saved_bc_ids;
1814 std::vector<unsigned short int> saved_bc_sides;
1815
1816 // Container to catch boundary ids passed back by BoundaryInfo
1817 std::vector<boundary_id_type> bc_ids;
1818
1819 // Reserve a reasonable amt. of space for each
1820 new_elements.reserve(mesh.n_active_elem());
1821 saved_boundary_elements.reserve(mesh.get_boundary_info().n_boundary_conds());
1822 saved_bc_ids.reserve(mesh.get_boundary_info().n_boundary_conds());
1823 saved_bc_sides.reserve(mesh.get_boundary_info().n_boundary_conds());
1824
1825 for (auto & elem : mesh.active_element_ptr_range())
1826 {
1827 // Make a new element of the same type
1828 auto copy = Elem::build(elem->type());
1829
1830 // Set node pointers (they still point to nodes in the original mesh)
1831 for (auto n : elem->node_index_range())
1832 copy->set_node(n, elem->node_ptr(n));
1833
1834 // Copy over ids
1835 copy->processor_id() = elem->processor_id();
1836 copy->subdomain_id() = elem->subdomain_id();
1837
1838 // Retain the original element's ID(s) as well, otherwise
1839 // the Mesh may try to create them for you...
1840 copy->set_id( elem->id() );
1841#ifdef LIBMESH_ENABLE_UNIQUE_ID
1842 copy->set_unique_id(elem->unique_id());
1843#endif
1844
1845 // This element could have boundary info or DistributedMesh
1846 // remote_elem links as well. We need to save the (elem,
1847 // side, bc_id) triples and those links
1848 for (auto s : elem->side_index_range())
1849 {
1850 if (elem->neighbor_ptr(s) == remote_elem)
1851 copy->set_neighbor(s, const_cast<RemoteElem *>(remote_elem));
1852
1853 mesh.get_boundary_info().boundary_ids(elem, s, bc_ids);
1854 for (const auto & bc_id : bc_ids)
1855 if (bc_id != BoundaryInfo::invalid_id)
1856 {
1857 saved_boundary_elements.push_back(copy.get());
1858 saved_bc_ids.push_back(bc_id);
1859 saved_bc_sides.push_back(s);
1860 }
1861 }
1862
1863 // Copy any extra element data. Since the copy hasn't been
1864 // added to the mesh yet any allocation has to be done manually.
1865 const unsigned int nei = elem->n_extra_integers();
1866 copy->add_extra_integers(nei);
1867 for (unsigned int i=0; i != nei; ++i)
1868 copy->set_extra_integer(i, elem->get_extra_integer(i));
1869
1870 // Copy any mapping data.
1871 copy->set_mapping_type(elem->mapping_type());
1872 copy->set_mapping_data(elem->mapping_data());
1873
1874 // We're done with this element
1875 mesh.delete_elem(elem);
1876
1877 // But save the copy
1878 new_elements.push_back(std::move(copy));
1879 }
1880
1881 // Make sure we saved the same number of boundary conditions
1882 // in each vector.
1883 libmesh_assert_equal_to (saved_boundary_elements.size(), saved_bc_ids.size());
1884 libmesh_assert_equal_to (saved_bc_ids.size(), saved_bc_sides.size());
1885
1886 // Loop again, delete any remaining elements
1887 for (auto & elem : mesh.element_ptr_range())
1888 mesh.delete_elem(elem);
1889
1890 // Add the copied (now level-0) elements back to the mesh
1891 for (auto & new_elem : new_elements)
1892 {
1893 // Save the original ID, because the act of adding the Elem can
1894 // change new_elem's id!
1895 dof_id_type orig_id = new_elem->id();
1896
1897 Elem * added_elem = mesh.add_elem(std::move(new_elem));
1898
1899 // If the Elem, as it was re-added to the mesh, now has a
1900 // different ID (this is unlikely, so it's just an assert)
1901 // the boundary information will no longer be correct.
1902 libmesh_assert_equal_to (orig_id, added_elem->id());
1903
1904 // Avoid compiler warnings in opt mode.
1905 libmesh_ignore(added_elem, orig_id);
1906 }
1907
1908 // Finally, also add back the saved boundary information
1909 for (auto e : index_range(saved_boundary_elements))
1910 mesh.get_boundary_info().add_side(saved_boundary_elements[e],
1911 saved_bc_sides[e],
1912 saved_bc_ids[e]);
1913
1914 // Trim unused and renumber nodes and elements
1916}
1917#endif // #ifdef LIBMESH_ENABLE_AMR
1918
1919
1920
1922 const boundary_id_type old_id,
1923 const boundary_id_type new_id)
1924{
1925 // This is just a shim around the member implementation, now
1926 mesh.get_boundary_info().renumber_id(old_id, new_id);
1927}
1928
1929
1930
1932 const subdomain_id_type old_id,
1933 const subdomain_id_type new_id)
1934{
1935 if (old_id == new_id)
1936 {
1937 // If the IDs are the same, this is a no-op.
1938 return;
1939 }
1940
1943 [old_id, new_id](const ElemRange & range)
1944 {
1945 for (Elem * elem : range)
1946 if (elem->subdomain_id() == old_id)
1947 elem->subdomain_id() = new_id;
1948 });
1949
1950 // We just invalidated mesh.get_subdomain_ids(), but it might not be
1951 // efficient to fix that here.
1953}
1954
1955
1956} // namespace libMesh
void max(const T &r, T &o, Request &req) const
The BoundaryInfo class contains information relevant to boundary conditions including storing faces,...
std::size_t n_boundary_conds() 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.
static const boundary_id_type invalid_id
Number used for internal use.
void renumber_id(boundary_id_type old_id, boundary_id_type new_id)
Changes all entities (nodes, sides, edges, shellfaces) with boundary id old_id to instead be labeled ...
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.
The C0Polygon is an element in 2D with an arbitrary (but fixed) number of first-order (EDGE2) sides.
The C0Polyhedron is an element in 3D with an arbitrary (but fixed) number of polygonal first-order (C...
Defines a dense vector for use in Finite Element-type computations.
dof_id_type get_extra_integer(const unsigned int index) const
Gets the value on this object of the extra integer associated with index, which should have been obta...
unsigned int n_extra_integers() const
Returns how many extra integers are associated to the DofObject.
processor_id_type processor_id() const
Definition dof_object.h:881
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition dof_object.h:473
unique_id_type unique_id() const
Definition dof_object.h:835
T get_extra_datum(const unsigned int index) const
Gets the value on this object of the extra datum associated with index, which should have been obtain...
void set_extra_datum(const unsigned int index, const T value)
Sets the value on this object of the extra datum associated with index, which should have been obtain...
dof_id_type id() const
Definition dof_object.h:819
Helper for building element sides that minimizes the construction of new elements.
This is the base class from which all geometric element types are derived.
Definition elem.h:96
virtual Node *& set_node(const unsigned int i)
Definition elem.h:2567
unsigned char mapping_data() const
Definition elem.h:3153
virtual unsigned int n_vertices() const =0
const Point & point(const unsigned int i) const
Definition elem.h:2462
const Node & node_ref(const unsigned int i) const
Definition elem.h:2538
virtual unsigned int n_nodes() const =0
const Elem * parent() const
Definition elem.h:3047
virtual unsigned int n_permutations() const =0
Returns the number of independent permutations of element nodes - e.g.
SimpleRange< NodeRefIter > node_ref_range()
Returns a range with all nodes of an element, usable in range-based for loops.
Definition elem.h:2682
virtual Order default_order() const =0
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition elem.C:442
virtual std::vector< unsigned int > nodes_on_side(const unsigned int) const =0
virtual unsigned int n_second_order_adjacent_vertices(const unsigned int n) const
Definition elem.C:3073
virtual Real embedding_matrix(const unsigned int child_num, const unsigned int child_node_num, const unsigned int parent_node_num) const =0
unsigned int which_child_am_i(const Elem *e) const
Definition elem.h:3209
virtual Real hmin() const
Definition elem.C:683
void orient(BoundaryInfo *boundary_info)
Flips the element (by swapping node and neighbor pointers) to have a mapping Jacobian of opposite sig...
Definition elem.h:3303
virtual unsigned short dim() const =0
virtual void permute(unsigned int perm_num)=0
Permutes the element (by swapping node and neighbor pointers) according to the specified index.
subdomain_id_type subdomain_id() const
Definition elem.h:2591
unsigned int level() const
Definition elem.h:3091
ElemMappingType mapping_type() const
Definition elem.h:3137
virtual unsigned int n_faces() const =0
const Node * node_ptr(const unsigned int i) const
Definition elem.h:2516
virtual std::unique_ptr< Elem > build_edge_ptr(const unsigned int i)=0
IntRange< unsigned short > edge_index_range() const
Definition elem.h:2709
virtual ElemType type() const =0
IntRange< unsigned short > node_index_range() const
Definition elem.h:2700
void set_mapping_type(const ElemMappingType type)
Sets the value of the mapping type for the element.
Definition elem.h:3145
virtual unsigned int n_edges() const =0
virtual ElemType side_type(const unsigned int s) const =0
dof_id_type node_id(const unsigned int i) const
Definition elem.h:2484
void set_mapping_data(const unsigned char data)
Sets the value of the mapping data for the element.
Definition elem.h:3161
virtual unsigned short int second_order_adjacent_vertex(const unsigned int n, const unsigned int v) const
Definition elem.C:3081
const Elem * neighbor_ptr(unsigned int i) const
Definition elem.h:2615
virtual bool infinite() const =0
IntRange< unsigned short > side_index_range() const
Definition elem.h:2727
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
Base class for functors that can be evaluated at a point and (optionally) time.
virtual std::unique_ptr< FunctionBase< Output > > clone() const =0
This is the MeshBase class.
Definition mesh_base.h:81
virtual const Node & node_ref(const dof_id_type i) const
Definition mesh_base.h:745
virtual const Point & point(const dof_id_type i) const =0
virtual bool is_serial() const
Definition mesh_base.h:357
void set_default_mapping_data(const unsigned char data)
Set the default master space to physical space mapping basis functions to be used on newly added elem...
Definition mesh_base.h:968
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition mesh_base.h:170
bool is_prepared() const
Definition mesh_base.C:1064
unsigned int mesh_dimension() const
Definition mesh_base.C:430
virtual dof_id_type n_elem() const =0
virtual bool is_replicated() const
Definition mesh_base.h:379
const ElemRange & element_stored_range()
Definition mesh_base.C:1939
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 dof_id_type n_nodes() const =0
virtual void delete_elem(Elem *e)=0
Removes element e from the mesh.
virtual dof_id_type max_node_id() const =0
virtual const Node * query_node_ptr(const dof_id_type i) const =0
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 dof_id_type max_elem_id() const =0
void unset_has_cached_elem_data()
Tells this we have done some operation (e.g.
Definition mesh_base.h:281
void set_default_mapping_type(const ElemMappingType type)
Set the default master space to physical space mapping basis functions to be used on newly added elem...
Definition mesh_base.h:950
unsigned int add_node_datum(const std::string &name, bool allocate_data=true, const T *default_value=nullptr)
Register a datum (of type T) to be added to each node in the mesh.
Definition mesh_base.h:2679
virtual const Elem * query_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.
void clear_point_locator()
Releases the current PointLocator object.
Definition mesh_base.C:1866
virtual dof_id_type n_active_elem() const =0
virtual unique_id_type parallel_max_unique_id() const =0
This is the MeshCommunication class.
void make_nodes_parallel_consistent(MeshBase &)
Copy processor_ids and ids on ghost nodes from their local processors.
A Node is like a Point, but with more information.
Definition node.h:55
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
The Polygon is an element in 2D with an arbitrary (but fixed) number of sides.
virtual std::array< int, 3 > subtriangle(unsigned int i) const
unsigned int n_subtriangles() const
The Polyhedron is an element in 3D with an arbitrary number of polygonal faces.
virtual std::array< int, 4 > subelement(unsigned int i) const
unsigned int n_subelements() const
In parallel meshes where a ghost element has neighbors which do not exist on the local processor,...
Definition remote_elem.h:61
The StoredRange class defines a contiguous, divisible set of objects.
The base class for all "surface" related geometric objects.
Definition surface.h:43
virtual Point closest_point(const Point &p) const =0
This class defines a tensor in LIBMESH_DIM dimensional Real or Complex space.
static TensorValue< Real > intrinsic_rotation_matrix(Real phi, Real theta, Real psi)
Generate the intrinsic rotation matrix associated with the provided Euler angles.
auto norm_sq() const
void add(const TypeVector< T2 > &)
Add to this vector without creating a temporary.
auto norm() const
void add_scaled(const TypeVector< T2 > &, const T &)
Add a scaled value to this vector without creating a temporary.
TypeVector< T > unit() const
MeshBase & mesh
void push_parallel_vector_data(const Communicator &comm, MapToVectors &&data, const ActionFunctor &act_on_data)
void all_tri(MeshBase &mesh)
Subdivides any non-simplex elements in a Mesh to produce simplex (triangular in 2D,...
void smooth(MeshBase &, unsigned int, Real)
Smooth the mesh with a simple Laplace smoothing algorithm.
void change_subdomain_id(MeshBase &mesh, const subdomain_id_type old_id, const subdomain_id_type new_id)
Finds any subdomain ids that are currently old_id, changes them to new_id.
void flatten(MeshBase &mesh)
Removes all the refinement tree structure of Mesh, leaving only the highest-level (most-refined) elem...
void change_boundary_id(MeshBase &mesh, const boundary_id_type old_id, const boundary_id_type new_id)
Finds any boundary ids that are currently old_id, changes them to new_id.
void scale(MeshBase &mesh, const Real xs, const Real ys=0., const Real zs=0.)
Scales the mesh.
void interpolate_surface(MeshBase &mesh, const Surface &surface, std::set< std::size_t > ids={}, bool use_boundary_nodes=true)
Move nodes in mesh to their closest points on the specified surface.
void all_rbb(MeshBase &mesh)
Converts all element geometric mappings from the default Lagrange to the more flexible Rational-Bezie...
void redistribute(MeshBase &mesh, const FunctionBase< Real > &mapfunc)
Deterministically perturb the nodal locations.
void distort(MeshBase &mesh, const Real factor, const bool perturb_boundary=false)
Randomly perturb the nodal locations.
void orient_elements(MeshBase &mesh)
Redo the nodal ordering of each element as necessary to give the element Jacobian a positive orientat...
RealTensorValue rotate(MeshBase &mesh, const Real phi, const Real theta=0., const Real psi=0.)
Rotates the mesh in 3D space.
void translate(MeshBase &mesh, const Real xt=0., const Real yt=0., const Real zt=0.)
Translates the mesh.
void permute_elements(MeshBase &mesh)
Randomly permute the nodal ordering of each element (without twisting the element mapping).
dof_id_type weight(const MeshBase &mesh, const processor_id_type pid)
Definition mesh_tools.C:444
std::unordered_set< dof_id_type > find_boundary_nodes(const MeshBase &mesh)
Returns a std::set containing Node IDs for all of the boundary nodes.
Definition mesh_tools.C:524
unsigned int n_levels(const MeshBase &mesh)
Definition mesh_tools.C:826
void sync_dofobject_data_by_id(const Communicator &comm, const Iterator &range_begin, const Iterator &range_end, SyncFunctor &sync)
Request data about a range of ghost dofobjects uniquely identified by their id.
void parallel_for(const Range &range, const Body &body, unsigned int n_threads=libMesh::n_threads())
Execute the provided function object in parallel on the specified range.
The libMesh namespace provides an interface to certain functionality in the library.
uint8_t unique_id_type
Definition id_types.h:86
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
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
ElemType
Defines an enum for geometric element types.
int8_t boundary_id_type
Definition id_types.h:51
void libmesh_ignore(const Args &...)
TensorValue< Real > RealTensorValue
Useful typedefs to allow transparent switching between Real and Complex data types.
@ RATIONAL_BERNSTEIN_MAP
libmesh_assert(ctx)
const unsigned int invalid_uint
A number which is used quite often to represent an invalid or uninitialized value for an unsigned int...
Definition libmesh.h:303
const RemoteElem * remote_elem
Definition remote_elem.C:57
uint8_t dof_id_type
Definition id_types.h:67
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
uint8_t processor_id_type
Definition id_types.h:104
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition int_range.h:176