LCOV - code coverage report
Current view: top level - src/mesh - mesh_modification.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4640 (c5f9f0) with base a68dd6 Lines: 619 747 82.9 %
Date: 2026-11-02 23:02:27 Functions: 33 37 89.2 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14