LCOV - code coverage report
Current view: top level - src/mesh - mesh_generation.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4640 (c5f9f0) with base a68dd6 Lines: 1176 1451 81.0 %
Date: 2026-11-02 23:02:27 Functions: 15 23 65.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             : // libmesh includes
      21             : #include "libmesh/mesh_generation.h"
      22             : #include "libmesh/unstructured_mesh.h"
      23             : #include "libmesh/mesh_refinement.h"
      24             : #include "libmesh/edge_edge2.h"
      25             : #include "libmesh/edge_edge3.h"
      26             : #include "libmesh/edge_edge4.h"
      27             : #include "libmesh/face_tri3.h"
      28             : #include "libmesh/face_tri6.h"
      29             : #include "libmesh/face_tri7.h"
      30             : #include "libmesh/face_quad4.h"
      31             : #include "libmesh/face_quad8.h"
      32             : #include "libmesh/face_quad9.h"
      33             : #include "libmesh/face_c0polygon.h"
      34             : #include "libmesh/cell_c0polyhedron.h"
      35             : #include "libmesh/cell_hex8.h"
      36             : #include "libmesh/cell_hex20.h"
      37             : #include "libmesh/cell_hex27.h"
      38             : #include "libmesh/cell_prism6.h"
      39             : #include "libmesh/cell_prism15.h"
      40             : #include "libmesh/cell_prism18.h"
      41             : #include "libmesh/cell_prism20.h"
      42             : #include "libmesh/cell_prism21.h"
      43             : #include "libmesh/cell_tet4.h"
      44             : #include "libmesh/cell_pyramid5.h"
      45             : #include "libmesh/libmesh_logging.h"
      46             : #include "libmesh/boundary_info.h"
      47             : #include "libmesh/remote_elem.h"
      48             : #include "libmesh/sphere.h"
      49             : #include "libmesh/mesh_modification.h"
      50             : #include "libmesh/mesh_smoother_laplace.h"
      51             : #include "libmesh/node_elem.h"
      52             : #include "libmesh/vector_value.h"
      53             : #include "libmesh/function_base.h"
      54             : #include "libmesh/enum_order.h"
      55             : #include "libmesh/int_range.h"
      56             : #include "libmesh/parallel.h"
      57             : #include "libmesh/parallel_ghost_sync.h"
      58             : #include "libmesh/enum_to_string.h"
      59             : #include "libmesh/node_range.h"
      60             : 
      61             : // C++ includes
      62             : #include <array>
      63             : #include <cstdlib> // *must* precede <cmath> for proper std:abs() on PGI, Sun Studio CC
      64             : #include <cmath> // for std::sqrt
      65             : #include <unordered_set>
      66             : 
      67             : 
      68             : namespace libMesh
      69             : {
      70             : 
      71             : namespace MeshTools {
      72             : namespace Generation {
      73             : namespace Private {
      74             : /**
      75             :  * A useful inline function which replaces the macros
      76             :  * used previously.  Not private since this is a namespace,
      77             :  * but would be if this were a class.  The first one returns
      78             :  * the proper node number for 2D elements while the second
      79             :  * one returns the node number for 3D elements.
      80             :  */
      81             : inline
      82    17759778 : unsigned int idx(const ElemType type,
      83             :                  const unsigned int nx,
      84             :                  const unsigned int i,
      85             :                  const unsigned int j)
      86             : {
      87    17309696 :   switch(type)
      88             :     {
      89     3535128 :     case INVALID_ELEM:
      90             :     case QUAD4:
      91             :     case QUADSHELL4:
      92             :     case TRI3:
      93             :     case TRISHELL3:
      94             :       {
      95     3535128 :         return i + j*(nx+1);
      96             :       }
      97             : 
      98    14224650 :     case QUAD8:
      99             :     case QUADSHELL8:
     100             :     case QUAD9:
     101             :     case QUADSHELL9:
     102             :     case TRI6:
     103             :     case TRI7:
     104             :       {
     105    14224650 :         return i + j*(2*nx+1);
     106             :       }
     107             : 
     108           0 :     default:
     109           0 :       libmesh_error_msg("ERROR: Unrecognized 2D element type == " << Utility::enum_to_string(type));
     110             :     }
     111             : 
     112             :   return libMesh::invalid_uint;
     113             : }
     114             : 
     115             : 
     116             : 
     117             : // Same as the function above, but for 3D elements
     118             : inline
     119    48261190 : unsigned int idx(const ElemType type,
     120             :                  const unsigned int nx,
     121             :                  const unsigned int ny,
     122             :                  const unsigned int i,
     123             :                  const unsigned int j,
     124             :                  const unsigned int k)
     125             : {
     126    47080326 :   switch(type)
     127             :     {
     128     8887436 :     case INVALID_ELEM:
     129             :     case HEX8:
     130             :     case PRISM6:
     131             :     case C0POLYHEDRON:
     132             :       {
     133     8887436 :         return i + (nx+1)*(j + k*(ny+1));
     134             :       }
     135             : 
     136    39373754 :     case HEX20:
     137             :     case HEX27:
     138             :     case TET4:  // TET4's are created from an initial HEX27 discretization
     139             :     case TET10: // TET10's are created from an initial HEX27 discretization
     140             :     case TET14: // TET14's are created from an initial HEX27 discretization
     141             :     case PYRAMID5: // PYRAMID5's are created from an initial HEX27 discretization
     142             :     case PYRAMID13:
     143             :     case PYRAMID14:
     144             :     case PYRAMID18:
     145             :     case PRISM15:
     146             :     case PRISM18:
     147             :     case PRISM20:
     148             :     case PRISM21:
     149             :       {
     150    39373754 :         return i + (2*nx+1)*(j + k*(2*ny+1));
     151             :       }
     152             : 
     153           0 :     default:
     154           0 :       libmesh_error_msg("ERROR: Unrecognized element type == " << Utility::enum_to_string(type));
     155             :     }
     156             : 
     157             :   return libMesh::invalid_uint;
     158             : }
     159             : 
     160             : 
     161             : /**
     162             :  * This object is passed to MeshTools::Modification::redistribute() to
     163             :  * redistribute the points on a uniform grid into the Gauss-Lobatto
     164             :  * points on the actual grid.
     165             :  */
     166             : class GaussLobattoRedistributionFunction : public FunctionBase<Real>
     167             : {
     168             : public:
     169             :   /**
     170             :    * Constructor.
     171             :    */
     172           0 :   GaussLobattoRedistributionFunction(unsigned int nx,
     173             :                                      Real xmin,
     174             :                                      Real xmax,
     175             :                                      unsigned int ny=0,
     176             :                                      Real ymin=0,
     177             :                                      Real ymax=0,
     178             :                                      unsigned int nz=0,
     179             :                                      Real zmin=0,
     180           0 :                                      Real zmax=0) :
     181           0 :     FunctionBase<Real>(nullptr)
     182             :   {
     183           0 :     _nelem.resize(3);
     184           0 :     _nelem[0] = nx;
     185           0 :     _nelem[1] = ny;
     186           0 :     _nelem[2] = nz;
     187             : 
     188           0 :     _mins.resize(3);
     189           0 :     _mins[0] = xmin;
     190           0 :     _mins[1] = ymin;
     191           0 :     _mins[2] = zmin;
     192             : 
     193           0 :     _widths.resize(3);
     194           0 :     _widths[0] = xmax - xmin;
     195           0 :     _widths[1] = ymax - ymin;
     196           0 :     _widths[2] = zmax - zmin;
     197             : 
     198             :     // Precompute the cosine values.
     199           0 :     _cosines.resize(3);
     200           0 :     for (unsigned dir=0; dir<3; ++dir)
     201           0 :       if (_nelem[dir] != 0)
     202             :         {
     203           0 :           _cosines[dir].resize(_nelem[dir]+1);
     204           0 :           for (auto i : index_range(_cosines[dir]))
     205           0 :             _cosines[dir][i] = std::cos(libMesh::pi * Real(i) / _nelem[dir]);
     206             :         }
     207           0 :   }
     208             : 
     209             :   /**
     210             :    * The 5 special functions can be defaulted for this class.
     211             :    */
     212             :   GaussLobattoRedistributionFunction (GaussLobattoRedistributionFunction &&) = default;
     213           0 :   GaussLobattoRedistributionFunction (const GaussLobattoRedistributionFunction &) = default;
     214             :   GaussLobattoRedistributionFunction & operator= (const GaussLobattoRedistributionFunction &) = default;
     215             :   GaussLobattoRedistributionFunction & operator= (GaussLobattoRedistributionFunction &&) = default;
     216           0 :   virtual ~GaussLobattoRedistributionFunction () = default;
     217             : 
     218             :   /**
     219             :    * We must provide a way to clone ourselves to satisfy the pure
     220             :    * virtual interface.  We use the autogenerated copy constructor.
     221             :    */
     222           0 :   virtual std::unique_ptr<FunctionBase<Real>> clone () const override
     223             :   {
     224           0 :     return std::make_unique<GaussLobattoRedistributionFunction>(*this);
     225             :   }
     226             : 
     227             :   /**
     228             :    * This is the actual function that
     229             :    * MeshTools::Modification::redistribute() calls.  Moves the points
     230             :    * of the grid to the Gauss-Lobatto points.
     231             :    */
     232           0 :   virtual void operator() (const Point & p,
     233             :                            const Real /*time*/,
     234             :                            DenseVector<Real> & output) override
     235             :   {
     236           0 :     output.resize(3);
     237             : 
     238           0 :     for (unsigned dir=0; dir<3; ++dir)
     239           0 :       if (_nelem[dir] != 0)
     240             :         {
     241             :           // Figure out the index of the current point.
     242           0 :           Real float_index = (p(dir) - _mins[dir]) * _nelem[dir] / _widths[dir];
     243             : 
     244             :           // std::modf separates the fractional and integer parts of the index.
     245           0 :           Real integer_part_f = 0;
     246           0 :           const Real fractional_part = std::modf(float_index, &integer_part_f);
     247             : 
     248           0 :           const int integer_part = int(integer_part_f);
     249             : 
     250             :           // Vertex node?
     251           0 :           if (std::abs(fractional_part) < TOLERANCE || std::abs(fractional_part - 1.0) < TOLERANCE)
     252             :             {
     253           0 :               int index = int(round(float_index));
     254             : 
     255             :               // Move node to the Gauss-Lobatto position.
     256           0 :               output(dir) = _mins[dir] + _widths[dir] * 0.5 * (1.0 - _cosines[dir][index]);
     257             :             }
     258             : 
     259             :           // Mid-edge (quadratic) node?
     260           0 :           else if (std::abs(fractional_part - 0.5) < TOLERANCE)
     261             :             {
     262             :               // Move node to the Gauss-Lobatto position, which is the average of
     263             :               // the node to the left and the node to the right.
     264           0 :               output(dir) = _mins[dir] + _widths[dir] * 0.5 *
     265           0 :                 (1.0 - 0.5*(_cosines[dir][integer_part] + _cosines[dir][integer_part+1]));
     266             :             }
     267             : 
     268             :           // 1D only: Left interior (cubic) node?
     269           0 :           else if (std::abs(fractional_part - 1./3.) < TOLERANCE)
     270             :             {
     271             :               // Move node to the Gauss-Lobatto position, which is
     272             :               // 2/3*left_vertex + 1/3*right_vertex.
     273           0 :               output(dir) = _mins[dir] + _widths[dir] * 0.5 *
     274           0 :                 (1.0 - 2./3.*_cosines[dir][integer_part] - 1./3.*_cosines[dir][integer_part+1]);
     275             :             }
     276             : 
     277             :           // 1D only: Right interior (cubic) node?
     278           0 :           else if (std::abs(fractional_part - 2./3.) < TOLERANCE)
     279             :             {
     280             :               // Move node to the Gauss-Lobatto position, which is
     281             :               // 1/3*left_vertex + 2/3*right_vertex.
     282           0 :               output(dir) = _mins[dir] + _widths[dir] * 0.5 *
     283           0 :                 (1.0 - 1./3.*_cosines[dir][integer_part] - 2./3.*_cosines[dir][integer_part+1]);
     284             :             }
     285             : 
     286             :           else
     287           0 :             libmesh_error_msg("Cannot redistribute node: " << p);
     288             :         }
     289           0 :   }
     290             : 
     291             :   /**
     292             :    * We must also override operator() which returns a Real, but this function
     293             :    * should never be called, so it's left unimplemented.
     294             :    */
     295           0 :   virtual Real operator() (const Point & /*p*/,
     296             :                            const Real /*time*/) override
     297             :   {
     298           0 :     libmesh_not_implemented();
     299             :   }
     300             : 
     301             : protected:
     302             :   // Stored data
     303             :   std::vector<Real> _mins;
     304             :   std::vector<unsigned int> _nelem;
     305             :   std::vector<Real> _widths;
     306             : 
     307             :   // Precomputed values
     308             :   std::vector<std::vector<Real>> _cosines;
     309             : };
     310             : 
     311             : 
     312             : } // namespace Private
     313             : } // namespace Generation
     314             : } // namespace MeshTools
     315             : 
     316             : // ------------------------------------------------------------
     317             : // MeshTools::Generation function for mesh generation
     318      410857 : void MeshTools::Generation::build_cube(UnstructuredMesh & mesh,
     319             :                                        const unsigned int nx,
     320             :                                        const unsigned int ny,
     321             :                                        const unsigned int nz,
     322             :                                        const Real xmin, const Real xmax,
     323             :                                        const Real ymin, const Real ymax,
     324             :                                        const Real zmin, const Real zmax,
     325             :                                        const ElemType type,
     326             :                                        const bool gauss_lobatto_grid)
     327             : {
     328       34761 :   LOG_SCOPE("build_cube()", "MeshTools::Generation");
     329             : 
     330             :   // Declare that we are using the indexing utility routine
     331             :   // in the "Private" part of our current namespace.  If this doesn't
     332             :   // work in GCC 2.95.3 we can either remove it or stop supporting
     333             :   // 2.95.3 altogether.
     334             :   // Changing this to import the whole namespace... just importing idx
     335             :   // causes an internal compiler error for Intel Compiler 11.0 on Linux
     336             :   // in debug mode.
     337             :   using namespace MeshTools::Generation::Private;
     338             : 
     339             :   // Clear the mesh and start from scratch
     340      410857 :   mesh.clear();
     341             : 
     342       11587 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     343             : 
     344      410857 :   if (nz != 0)
     345             :     {
     346      187640 :       mesh.set_mesh_dimension(3);
     347      187640 :       mesh.set_spatial_dimension(3);
     348             :     }
     349      223217 :   else if (ny != 0)
     350             :     {
     351      160824 :       mesh.set_mesh_dimension(2);
     352      160824 :       mesh.set_spatial_dimension(2);
     353             :     }
     354       62393 :   else if (nx != 0)
     355             :     {
     356       59414 :       mesh.set_mesh_dimension(1);
     357       59414 :       mesh.set_spatial_dimension(1);
     358             :     }
     359             :   else
     360             :     {
     361             :       // Will we get here?
     362        2979 :       mesh.set_mesh_dimension(0);
     363        2979 :       mesh.set_spatial_dimension(0);
     364             :     }
     365             : 
     366      410857 :   switch (mesh.mesh_dimension())
     367             :     {
     368             :       //---------------------------------------------------------------------
     369             :       // Build a 0D point
     370        2979 :     case 0:
     371             :       {
     372          84 :         libmesh_assert_equal_to (nx, 0);
     373          84 :         libmesh_assert_equal_to (ny, 0);
     374          84 :         libmesh_assert_equal_to (nz, 0);
     375             : 
     376          84 :         libmesh_assert (type == INVALID_ELEM || type == NODEELEM);
     377             : 
     378             :         // Build one nodal element for the mesh
     379        3063 :         mesh.add_point (Point(0, 0, 0), 0);
     380        2979 :         Elem * elem = mesh.add_elem(Elem::build(NODEELEM));
     381        2979 :         elem->set_node(0, mesh.node_ptr(0));
     382             : 
     383        2895 :         break;
     384             :       }
     385             : 
     386             : 
     387             : 
     388             :       //---------------------------------------------------------------------
     389             :       // Build a 1D line
     390       59414 :     case 1:
     391             :       {
     392        1674 :         libmesh_assert_not_equal_to (nx, 0);
     393        1674 :         libmesh_assert_equal_to (ny, 0);
     394        1674 :         libmesh_assert_equal_to (nz, 0);
     395        1674 :         libmesh_assert_less (xmin, xmax);
     396             : 
     397             :         // Reserve elements
     398             :         switch (type)
     399             :           {
     400       59414 :           case INVALID_ELEM:
     401             :           case EDGE2:
     402             :           case EDGE3:
     403             :           case EDGE4:
     404             :             {
     405       59414 :               mesh.reserve_elem (nx);
     406        1674 :               break;
     407             :             }
     408             : 
     409           0 :           default:
     410           0 :             libmesh_error_msg("ERROR: Unrecognized 1D element type == " << Utility::enum_to_string(type));
     411             :           }
     412             : 
     413             :         // Reserve nodes
     414             :         switch (type)
     415             :           {
     416       19516 :           case INVALID_ELEM:
     417             :           case EDGE2:
     418             :             {
     419       19516 :               mesh.reserve_nodes(nx+1);
     420       18966 :               break;
     421             :             }
     422             : 
     423       32727 :           case EDGE3:
     424             :             {
     425       32727 :               mesh.reserve_nodes(2*nx+1);
     426       31805 :               break;
     427             :             }
     428             : 
     429        7171 :           case EDGE4:
     430             :             {
     431        7171 :               mesh.reserve_nodes(3*nx+1);
     432        6969 :               break;
     433             :             }
     434             : 
     435           0 :           default:
     436           0 :             libmesh_error_msg("ERROR: Unrecognized 1D element type == " << Utility::enum_to_string(type));
     437             :           }
     438             : 
     439             : 
     440             :         // Build the nodes, depends on whether we're using linears,
     441             :         // quadratics or cubics and whether using uniform grid or Gauss-Lobatto
     442        1674 :         unsigned int node_id = 0;
     443             :         switch(type)
     444             :           {
     445         550 :           case INVALID_ELEM:
     446             :           case EDGE2:
     447             :             {
     448      361334 :               for (unsigned int i=0; i<=nx; i++)
     449             :               {
     450      351564 :                 const Node * const node = mesh.add_point (Point(static_cast<Real>(i)/nx, 0, 0), node_id++);
     451      341818 :                 if (i == 0)
     452       19516 :                   boundary_info.add_node(node, 0);
     453      341818 :                 if (i == nx)
     454       19516 :                   boundary_info.add_node(node, 1);
     455             :               }
     456             : 
     457         550 :               break;
     458             :             }
     459             : 
     460         922 :           case EDGE3:
     461             :             {
     462      161224 :               for (unsigned int i=0; i<=2*nx; i++)
     463             :               {
     464      132119 :                 const Node * const node = mesh.add_point (Point(static_cast<Real>(i)/(2*nx), 0, 0), node_id++);
     465      128497 :                 if (i == 0)
     466       32727 :                   boundary_info.add_node(node, 0);
     467      128497 :                 if (i == 2*nx)
     468       32727 :                   boundary_info.add_node(node, 1);
     469             :               }
     470         922 :               break;
     471             :             }
     472             : 
     473         202 :           case EDGE4:
     474             :             {
     475       47570 :               for (unsigned int i=0; i<=3*nx; i++)
     476             :               {
     477       41537 :                 const Node * const node = mesh.add_point (Point(static_cast<Real>(i)/(3*nx), 0, 0), node_id++);
     478       40399 :                 if (i == 0)
     479        7171 :                   boundary_info.add_node(node, 0);
     480       40399 :                 if (i == 3*nx)
     481        7171 :                   boundary_info.add_node(node, 1);
     482             :               }
     483             : 
     484         202 :               break;
     485             :             }
     486             : 
     487           0 :           default:
     488           0 :             libmesh_error_msg("ERROR: Unrecognized 1D element type == " << Utility::enum_to_string(type));
     489             : 
     490             :           }
     491             : 
     492             :         // Build the elements of the mesh
     493             :         switch(type)
     494             :           {
     495         550 :           case INVALID_ELEM:
     496             :           case EDGE2:
     497             :             {
     498      341818 :               for (unsigned int i=0; i<nx; i++)
     499             :                 {
     500      322302 :                   Elem * elem = mesh.add_elem(Elem::build_with_id(EDGE2, i));
     501      322302 :                   elem->set_node(0, mesh.node_ptr(i));
     502      322302 :                   elem->set_node(1, mesh.node_ptr(i+1));
     503             : 
     504      322302 :                   if (i == 0)
     505       19516 :                     boundary_info.add_side(elem, 0, 0);
     506             : 
     507      322302 :                   if (i == (nx-1))
     508       19516 :                     boundary_info.add_side(elem, 1, 1);
     509             : 
     510             :                 }
     511         550 :               break;
     512             :             }
     513             : 
     514         922 :           case EDGE3:
     515             :             {
     516       80612 :               for (unsigned int i=0; i<nx; i++)
     517             :                 {
     518       47885 :                   Elem * elem = mesh.add_elem(Elem::build_with_id(EDGE3, i));
     519       47885 :                   elem->set_node(0, mesh.node_ptr(2*i));
     520       47885 :                   elem->set_node(2, mesh.node_ptr(2*i+1));
     521       47885 :                   elem->set_node(1, mesh.node_ptr(2*i+2));
     522             : 
     523       47885 :                   if (i == 0)
     524       32727 :                     boundary_info.add_side(elem, 0, 0);
     525             : 
     526       47885 :                   if (i == (nx-1))
     527       32727 :                     boundary_info.add_side(elem, 1, 1);
     528             :                 }
     529         922 :               break;
     530             :             }
     531             : 
     532         202 :           case EDGE4:
     533             :             {
     534       18247 :               for (unsigned int i=0; i<nx; i++)
     535             :                 {
     536       11076 :                   Elem * elem = mesh.add_elem(Elem::build_with_id(EDGE4, i));
     537       11076 :                   elem->set_node(0, mesh.node_ptr(3*i));
     538       11076 :                   elem->set_node(2, mesh.node_ptr(3*i+1));
     539       11076 :                   elem->set_node(3, mesh.node_ptr(3*i+2));
     540       11076 :                   elem->set_node(1, mesh.node_ptr(3*i+3));
     541             : 
     542       11076 :                   if (i == 0)
     543        7171 :                     boundary_info.add_side(elem, 0, 0);
     544             : 
     545       11076 :                   if (i == (nx-1))
     546        7171 :                     boundary_info.add_side(elem, 1, 1);
     547             :                 }
     548         202 :               break;
     549             :             }
     550             : 
     551           0 :           default:
     552           0 :             libmesh_error_msg("ERROR: Unrecognized 1D element type == " << Utility::enum_to_string(type));
     553             :           }
     554             : 
     555             :         // Move the nodes to their final locations.
     556       59414 :         if (gauss_lobatto_grid)
     557             :           {
     558           0 :             GaussLobattoRedistributionFunction func(nx, xmin, xmax);
     559           0 :             MeshTools::Modification::redistribute(mesh, func);
     560           0 :           }
     561             :         else // !gauss_lobatto_grid
     562             :           {
     563             :             Threads::parallel_for_each
     564       59414 :               (mesh.node_stored_range(),
     565      525220 :                [xmin, xmax](Node * node)
     566             :                {
     567      510714 :                  (*node)(0) = (*node)(0)*(xmax-xmin) + xmin;
     568       14506 :                });
     569             :           }
     570             : 
     571             :         // Add sideset names to boundary info
     572       59414 :         boundary_info.sideset_name(0) = "left";
     573       59414 :         boundary_info.sideset_name(1) = "right";
     574             : 
     575             :         // Add nodeset names to boundary info
     576       59414 :         boundary_info.nodeset_name(0) = "left";
     577       59414 :         boundary_info.nodeset_name(1) = "right";
     578             : 
     579        1674 :         break;
     580             :       }
     581             : 
     582             : 
     583             : 
     584             : 
     585             : 
     586             : 
     587             : 
     588             : 
     589             : 
     590             : 
     591             :       //---------------------------------------------------------------------
     592             :       // Build a 2D quadrilateral
     593      160824 :     case 2:
     594             :       {
     595        4519 :         libmesh_assert_not_equal_to (nx, 0);
     596        4519 :         libmesh_assert_not_equal_to (ny, 0);
     597        4519 :         libmesh_assert_equal_to (nz, 0);
     598        4519 :         libmesh_assert_less (xmin, xmax);
     599        4519 :         libmesh_assert_less (ymin, ymax);
     600             : 
     601             :         // Reserve elements.  The TRI3 and TRI6 meshes
     602             :         // have twice as many elements...
     603             :         switch (type)
     604             :           {
     605       85238 :           case INVALID_ELEM:
     606             :           case QUAD4:
     607             :           case QUADSHELL4:
     608             :           case QUAD8:
     609             :           case QUADSHELL8:
     610             :           case QUAD9:
     611             :           case QUADSHELL9:
     612             :             {
     613       85238 :               mesh.reserve_elem (nx*ny);
     614       82841 :               break;
     615             :             }
     616             : 
     617       74734 :           case TRI3:
     618             :           case TRISHELL3:
     619             :           case TRI6:
     620             :           case TRI7:
     621             :             {
     622       74734 :               mesh.reserve_elem (2*nx*ny);
     623       72636 :               break;
     624             :             }
     625             : 
     626         852 :           case C0POLYGON:
     627             :           {
     628         852 :             mesh.reserve_elem ((nx + 1) * (ny + 1));
     629         828 :             break;
     630             :           }
     631             : 
     632           0 :           default:
     633           0 :             libmesh_error_msg("ERROR: Unrecognized 2D element type == " << Utility::enum_to_string(type));
     634             :           }
     635             : 
     636             : 
     637             : 
     638             :         // Reserve nodes.  The quadratic element types
     639             :         // need to reserve more nodes than the linear types.
     640             :         switch (type)
     641             :           {
     642       42591 :           case INVALID_ELEM:
     643             :           case QUAD4:
     644             :           case QUADSHELL4:
     645             :           case TRI3:
     646             :           case TRISHELL3:
     647             :             {
     648       42591 :               mesh.reserve_nodes( (nx+1)*(ny+1) );
     649       41388 :               break;
     650             :             }
     651             : 
     652       88068 :           case QUAD8:
     653             :           case QUADSHELL8:
     654             :           case QUAD9:
     655             :           case QUADSHELL9:
     656             :           case TRI6:
     657             :             {
     658       88068 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1) );
     659       85598 :               break;
     660             :             }
     661             : 
     662       29313 :           case TRI7:
     663             :             {
     664       29313 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1) + 2*nx*ny );
     665       28491 :               break;
     666             :             }
     667         852 :           case C0POLYGON:
     668             :             {
     669         852 :               mesh.reserve_nodes (4 + 3*nx*ny + 2*nx + 2*ny);
     670         828 :               break;
     671             :             }
     672             : 
     673           0 :           default:
     674           0 :             libmesh_error_msg("ERROR: Unrecognized 2D element type == " << Utility::enum_to_string(type));
     675             :           }
     676             : 
     677             : 
     678             : 
     679             :         // Build the nodes. Depends on whether you are using a linear
     680             :         // or quadratic element, and whether you are using a uniform
     681             :         // grid or the Gauss-Lobatto grid points.
     682        4519 :         unsigned int node_id = 0;
     683             :         switch (type)
     684             :           {
     685        1203 :           case INVALID_ELEM:
     686             :           case QUAD4:
     687             :           case QUADSHELL4:
     688             :           case TRI3:
     689             :           case TRISHELL3:
     690             :             {
     691      180028 :               for (unsigned int j=0; j<=ny; j++)
     692     1226416 :                 for (unsigned int i=0; i<=nx; i++)
     693             :                 {
     694             :                   const Node * const node =
     695     2118004 :                       mesh.add_point(Point(static_cast<Real>(i) / static_cast<Real>(nx),
     696     1088979 :                                            static_cast<Real>(j) / static_cast<Real>(ny),
     697             :                                            0.),
     698     1078249 :                                      node_id++);
     699     1088979 :                   if (j == 0)
     700      132041 :                     boundary_info.add_node(node, 0);
     701     1088979 :                   if (j == ny)
     702      132041 :                     boundary_info.add_node(node, 2);
     703     1088979 :                   if (i == 0)
     704      137437 :                     boundary_info.add_node(node, 3);
     705     1088979 :                   if (i == nx)
     706      137437 :                     boundary_info.add_node(node, 1);
     707             :                 }
     708             : 
     709        1203 :               break;
     710             :             }
     711             : 
     712        3292 :           case QUAD8:
     713             :           case QUADSHELL8:
     714             :           case QUAD9:
     715             :           case QUADSHELL9:
     716             :           case TRI6:
     717             :           case TRI7:
     718             :             {
     719      666514 :               for (unsigned int j=0; j<=(2*ny); j++)
     720     7228634 :                 for (unsigned int i=0; i<=(2*nx); i++)
     721             :                 {
     722             :                   const Node * const node =
     723    13021922 :                       mesh.add_point(Point(static_cast<Real>(i) / static_cast<Real>(2 * nx),
     724     6679501 :                                            static_cast<Real>(j) / static_cast<Real>(2 * ny),
     725             :                                            0),
     726     6596829 :                                      node_id++);
     727     6679501 :                   if (j == 0)
     728      562733 :                     boundary_info.add_node(node, 0);
     729     6679501 :                   if (j == 2*ny)
     730      562733 :                     boundary_info.add_node(node, 2);
     731     6679501 :                   if (i == 0)
     732      549133 :                     boundary_info.add_node(node, 3);
     733     6679501 :                   if (i == 2*nx)
     734      549133 :                     boundary_info.add_node(node, 1);
     735             :                 }
     736             : 
     737             :               // We'll add any interior Tri7 nodes last, to keep from
     738             :               // messing with our idx function
     739      117381 :               if (type == TRI7)
     740       75004 :                 for (unsigned int j=0; j<(3*ny); j += 3)
     741      288226 :                   for (unsigned int i=0; i<(3*nx); i += 3)
     742             :                     {
     743             :                       // The bottom-right triangle's center node
     744      473730 :                       mesh.add_point(Point(static_cast<Real>(i+2) / static_cast<Real>(3 * nx),
     745      242535 :                                            static_cast<Real>(j+1) / static_cast<Real>(3 * ny),
     746             :                                            0),
     747      239700 :                                      node_id++);
     748             :                       // The top-left triangle's center node
     749      242535 :                       mesh.add_point(Point(static_cast<Real>(i+1) / static_cast<Real>(3 * nx),
     750      242535 :                                            static_cast<Real>(j+2) / static_cast<Real>(3 * ny),
     751             :                                            0),
     752      239700 :                                      node_id++);
     753             :                     }
     754             : 
     755        3292 :               break;
     756             :             }
     757             : 
     758          24 :             case C0POLYGON:
     759             :             {
     760             :               // we create the nodes at the same time as the elements
     761          24 :               break;
     762             :             }
     763             : 
     764           0 :           default:
     765           0 :             libmesh_error_msg("ERROR: Unrecognized 2D element type == " << Utility::enum_to_string(type));
     766             :           }
     767             : 
     768             : 
     769             : 
     770             : 
     771             : 
     772             : 
     773             :         // Build the elements.  Each one is a bit different.
     774        4519 :         unsigned int elem_id = 0;
     775             :         switch (type)
     776             :           {
     777             : 
     778         765 :           case INVALID_ELEM:
     779             :           case QUAD4:
     780             :           case QUADSHELL4:
     781             :             {
     782       99667 :               for (unsigned int j=0; j<ny; j++)
     783      891336 :                 for (unsigned int i=0; i<nx; i++)
     784             :                   {
     785      841722 :                     Elem * elem = mesh.add_elem(Elem::build_with_id(type == INVALID_ELEM ? QUAD4 : type, elem_id++));
     786      818712 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     787      818712 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+1,j)  ));
     788      818712 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     789      818712 :                     elem->set_node(3, mesh.node_ptr(idx(type,nx,i,j+1)  ));
     790             : 
     791      818712 :                     if (j == 0)
     792       67086 :                       boundary_info.add_side(elem, 0, 0);
     793             : 
     794      818712 :                     if (j == (ny-1))
     795       67086 :                       boundary_info.add_side(elem, 2, 2);
     796             : 
     797      818712 :                     if (i == 0)
     798       72624 :                       boundary_info.add_side(elem, 3, 3);
     799             : 
     800      818712 :                     if (i == (nx-1))
     801       72624 :                       boundary_info.add_side(elem, 1, 1);
     802             :                   }
     803         765 :               break;
     804             :             }
     805             : 
     806             : 
     807         438 :           case TRI3:
     808             :           case TRISHELL3:
     809             :             {
     810       37770 :               for (unsigned int j=0; j<ny; j++)
     811       65602 :                 for (unsigned int i=0; i<nx; i++)
     812             :                   {
     813             :                     // Add first Tri3
     814       43380 :                     Elem * elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
     815       43380 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     816       43380 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+1,j)  ));
     817       43380 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     818             : 
     819       43380 :                     if (j == 0)
     820       22364 :                       boundary_info.add_side(elem, 0, 0);
     821             : 
     822       43380 :                     if (i == (nx-1))
     823       22222 :                       boundary_info.add_side(elem, 1, 1);
     824             : 
     825             :                     // Add second Tri3
     826       43380 :                     elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
     827       43380 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     828       43380 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     829       43380 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i,j+1)  ));
     830             : 
     831       43380 :                     if (j == (ny-1))
     832       22364 :                       boundary_info.add_side(elem, 1, 2);
     833             : 
     834       43380 :                     if (i == 0)
     835       22222 :                       boundary_info.add_side(elem, 2, 3);
     836             :                   }
     837         438 :               break;
     838             :             }
     839             : 
     840             : 
     841             : 
     842        1632 :           case QUAD8:
     843             :           case QUADSHELL8:
     844             :           case QUAD9:
     845             :           case QUADSHELL9:
     846             :             {
     847      176528 :               for (unsigned int j=0; j<(2*ny); j += 2)
     848      992953 :                 for (unsigned int i=0; i<(2*nx); i += 2)
     849             :                   {
     850      874620 :                     Elem * elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
     851      874620 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     852      874620 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+2,j)  ));
     853      874620 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i+2,j+2)));
     854      874620 :                     elem->set_node(3, mesh.node_ptr(idx(type,nx,i,j+2)  ));
     855      874620 :                     elem->set_node(4, mesh.node_ptr(idx(type,nx,i+1,j)  ));
     856      874620 :                     elem->set_node(5, mesh.node_ptr(idx(type,nx,i+2,j+1)));
     857      874620 :                     elem->set_node(6, mesh.node_ptr(idx(type,nx,i+1,j+2)));
     858      874620 :                     elem->set_node(7, mesh.node_ptr(idx(type,nx,i,j+1)  ));
     859             : 
     860      874620 :                     if (type == QUAD9 || type == QUADSHELL9)
     861      668082 :                       elem->set_node(8, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     862             : 
     863      874620 :                     if (j == 0)
     864      123942 :                       boundary_info.add_side(elem, 0, 0);
     865             : 
     866      874620 :                     if (j == 2*(ny-1))
     867      123942 :                       boundary_info.add_side(elem, 2, 2);
     868             : 
     869      874620 :                     if (i == 0)
     870      118333 :                       boundary_info.add_side(elem, 3, 3);
     871             : 
     872      874620 :                     if (i == 2*(nx-1))
     873      118333 :                       boundary_info.add_side(elem, 1, 1);
     874             :                   }
     875        1632 :               break;
     876             :             }
     877             : 
     878             : 
     879        1660 :           case TRI6:
     880             :           case TRI7:
     881             :             {
     882      156729 :               for (unsigned int j=0; j<(2*ny); j += 2)
     883      644177 :                 for (unsigned int i=0; i<(2*nx); i += 2)
     884             :                   {
     885             :                     // Add first Tri in the bottom-right of its quad
     886      546634 :                     Elem * elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
     887      546634 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     888      546634 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+2,j)  ));
     889      546634 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i+2,j+2)));
     890      546634 :                     elem->set_node(3, mesh.node_ptr(idx(type,nx,i+1,j)  ));
     891      546634 :                     elem->set_node(4, mesh.node_ptr(idx(type,nx,i+2,j+1)));
     892      546634 :                     elem->set_node(5, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     893             : 
     894      546634 :                     if (type == TRI7)
     895      242535 :                       elem->set_node(6, mesh.node_ptr(elem->id()+(2*nx+1)*(2*ny+1)));
     896             : 
     897      546634 :                     if (j == 0)
     898       98734 :                       boundary_info.add_side(elem, 0, 0);
     899             : 
     900      546634 :                     if (i == 2*(nx-1))
     901       97543 :                       boundary_info.add_side(elem, 1, 1);
     902             : 
     903             :                     // Add second Tri in the top left of its quad
     904      546634 :                     elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
     905      546634 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     906      546634 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+2,j+2)));
     907      546634 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i,j+2)  ));
     908      546634 :                     elem->set_node(3, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     909      546634 :                     elem->set_node(4, mesh.node_ptr(idx(type,nx,i+1,j+2)));
     910      546634 :                     elem->set_node(5, mesh.node_ptr(idx(type,nx,i,j+1)  ));
     911             : 
     912      546634 :                     if (type == TRI7)
     913      242535 :                       elem->set_node(6, mesh.node_ptr(elem->id()+(2*nx+1)*(2*ny+1)));
     914             : 
     915      546634 :                     if (j == 2*(ny-1))
     916       98734 :                       boundary_info.add_side(elem, 1, 2);
     917             : 
     918      546634 :                     if (i == 0)
     919       97543 :                       boundary_info.add_side(elem, 2, 3);
     920             :                   }
     921        1660 :               break;
     922             :             };
     923             : 
     924          24 :           case C0POLYGON:
     925             :           {
     926             :             // Build a 2D paving using hexagons (center), quads (part of y-boundary)
     927             :             // and triangles (x-boundaries).
     928             :             // Vector to re-use previously created nodes
     929          48 :             std::vector<Node *> node_list;
     930             : 
     931             :             // Start with a layer of triangles on the boundary
     932         852 :             const auto dx_tri = Real(1) / nx;
     933         852 :             const auto dy_tri = Real(1) / (ny + 1);
     934         828 :             std::unique_ptr<Elem> new_elem;
     935        6958 :             for (const auto i : make_range(nx + 1))
     936             :             {
     937             :               // Make new nodes for bottom layer of triangles
     938             :               Node *node0, *node1, *node2;
     939        6106 :               if (i == 0)
     940             :               {
     941         876 :                 node0 = mesh.add_point(Point(0., 0, 0.));
     942         876 :                 node1 = mesh.add_point(Point(0., dy_tri / 2., 0.));
     943         876 :                 node2 = mesh.add_point(Point(dx_tri / 2., 0., 0.));
     944         852 :                 node_list.push_back(node0);
     945         852 :                 node_list.push_back(node1);
     946         852 :                 node_list.push_back(node2);
     947             :               }
     948        5254 :               else if (i < nx)
     949             :               {
     950        4402 :                 node0 = node_list.back();
     951        4526 :                 node1 = mesh.add_point(Point((i)*dx_tri, dy_tri / 2., 0.));
     952        4526 :                 node2 = mesh.add_point(Point((i + 1. / 2.) * dx_tri, 0., 0.));
     953        4402 :                 node_list.push_back(node1);
     954        4402 :                 node_list.push_back(node2);
     955             :               }
     956             :               else
     957             :               {
     958         852 :                 node0 = node_list.back();
     959         876 :                 node1 = mesh.add_point(Point((i)*dx_tri, dy_tri / 2., 0.));
     960         876 :                 node2 = mesh.add_point(Point((i)*dx_tri, 0., 0.));
     961         852 :                 node_list.push_back(node1);
     962         852 :                 node_list.push_back(node2);
     963             :               }
     964             : 
     965        6106 :               new_elem = std::make_unique<C0Polygon>(3);
     966             :               // Switch to Tri3 when exodus default output supports element type mixes
     967        6106 :               new_elem->set_node(0, node0);
     968        6106 :               new_elem->set_node(1, node1);
     969        6106 :               new_elem->set_node(2, node2);
     970        6278 :               auto * elem = mesh.add_elem(std::move(new_elem));
     971             : 
     972             :               // Set boundaries
     973        6106 :               if (i == 0)
     974         852 :                 boundary_info.add_side(elem, 0, 3); // left
     975        5254 :               else if (i == nx)
     976         852 :                 boundary_info.add_side(elem, 1, 1); // right
     977        6106 :               boundary_info.add_side(elem, 2, 0); // bottom
     978             :             }
     979             :             // Start with the second node to build hexagons
     980          24 :             unsigned int running_index = 1;
     981             : 
     982             :             // Build layers of hexagons
     983          24 :             const auto hex_side =
     984         852 :                 (Real(1) - (ny == 1 ?
     985             :                              dy_tri :
     986         852 :                              (Real(1) + (ny - 1) / 2.) * dy_tri)) / ny;
     987        6106 :             for (const auto j : make_range(ny))
     988             :             {
     989       49203 :               for (const auto i : make_range(nx + (j % 2)))
     990             :               {
     991       43949 :                 if ((j % 2 == 0) || ((i > 0) && (i < nx)))
     992             :                 {
     993             :                   Node *n0, *n1, *n2, *n3, *n4, *n5;
     994       38979 :                   n0 = node_list[running_index++];
     995       38979 :                   n1 = node_list[running_index++];
     996       38979 :                   n2 = node_list[running_index];
     997             : 
     998       38979 :                   if (i == 0)
     999             :                   {
    1000        2847 :                     n3 = mesh.add_point(Point(*n0) + RealVectorValue(0, hex_side, 0));
    1001        2769 :                     node_list.push_back(n3);
    1002             :                   }
    1003             :                   else
    1004       36210 :                     n3 = node_list.back();
    1005             : 
    1006       40077 :                   n4 = mesh.add_point(Point(*n1) + RealVectorValue(0, hex_side + dy_tri, 0));
    1007       40077 :                   n5 = mesh.add_point(Point(*n2) + RealVectorValue(0, hex_side, 0));
    1008       38979 :                   node_list.push_back(n4);
    1009       38979 :                   node_list.push_back(n5);
    1010             : 
    1011       38979 :                   new_elem = std::make_unique<libMesh::C0Polygon>(6);
    1012       38979 :                   new_elem->set_node(0, n0);
    1013       38979 :                   new_elem->set_node(1, n1);
    1014       38979 :                   new_elem->set_node(2, n2);
    1015       38979 :                   new_elem->set_node(3, n5);
    1016       38979 :                   new_elem->set_node(4, n4);
    1017       38979 :                   new_elem->set_node(5, n3);
    1018       40077 :                   auto * elem = mesh.add_elem(std::move(new_elem));
    1019             : 
    1020             :                   // Set boundaries
    1021       38979 :                   if (i == 0)
    1022        2769 :                     boundary_info.add_side(elem, 5, 3); // left
    1023       36210 :                   else if (i == nx)
    1024        2196 :                     boundary_info.add_side(elem, 2, 1); // right
    1025       36783 :                 }
    1026             :                 // The hexagons are offset, so we build on a quad on each external side to fill
    1027        4970 :                 else if (i == 0 || i == nx)
    1028             :                 {
    1029             :                   Node *n0, *n1, *n2, *n3;
    1030        4970 :                   n0 = node_list[running_index++];
    1031        4970 :                   n1 = node_list[running_index];
    1032             : 
    1033        4970 :                   if (i == 0)
    1034             :                   {
    1035        2555 :                     n2 = mesh.add_point(Point(*n0) + RealVectorValue(0, hex_side + dy_tri, 0));
    1036        2485 :                     node_list.push_back(n2);
    1037        2555 :                     n3 = mesh.add_point(Point(*n1) + RealVectorValue(0, hex_side, 0));
    1038             :                   }
    1039             :                   else
    1040             :                   {
    1041        2485 :                     n2 = node_list.back();
    1042        2555 :                     n3 = mesh.add_point(Point(*n1) + RealVectorValue(0, hex_side + dy_tri, 0));
    1043             :                   }
    1044        4970 :                   node_list.push_back(n3);
    1045             : 
    1046        4970 :                   new_elem = std::make_unique<C0Polygon>(4);
    1047             :                   // Switch to Quad4 when exodus default output supports element type mixes
    1048        4970 :                   new_elem->set_node(0, n0);
    1049        4970 :                   new_elem->set_node(1, n1);
    1050        4970 :                   new_elem->set_node(3, n2);
    1051        4970 :                   new_elem->set_node(2, n3);
    1052        5110 :                   auto * elem = mesh.add_elem(std::move(new_elem));
    1053             : 
    1054             :                   // Set boundaries
    1055        4970 :                   if (i == 0)
    1056        2485 :                     boundary_info.add_side(elem, 3, 3); // left
    1057        2485 :                   else if (i == nx)
    1058        2625 :                     boundary_info.add_side(elem, 1, 1); // right
    1059             :                 }
    1060             :                 else
    1061           0 :                   libmesh_assert(false);
    1062             :               }
    1063             :               // Increment once to switch to next 'row' of nodes
    1064        5254 :               running_index++;
    1065             : 
    1066             :               // Skip lower right corner node
    1067        5254 :               if (j == 0)
    1068         852 :                 running_index++;
    1069             :             }
    1070             : 
    1071             :             // Build a final layer of triangles
    1072         852 :             const bool ny_odd = (ny % 2 == 1);
    1073        6390 :             for (const auto i : make_range(nx + ny_odd))
    1074             :             {
    1075             :               // Use existing nodes, except at the corners
    1076             :               Node *node0, *node1, *node2;
    1077        5538 :               if (i == 0 && ny_odd)
    1078             :               {
    1079         292 :                 node0 = mesh.add_point(Point(0., 1., 0.));
    1080         284 :                 node1 = node_list[running_index++];
    1081         292 :                 node2 = node_list[running_index];
    1082             :               }
    1083        5254 :               else if (i < nx)
    1084             :               {
    1085        4970 :                 node0 = node_list[running_index++];
    1086        4970 :                 node1 = node_list[running_index++];
    1087        5110 :                 node2 = node_list[running_index];
    1088             :               }
    1089             :               // This case only reached if ny is odd and we are using a triangle in top right corner
    1090             :               else
    1091             :               {
    1092         284 :                 node0 = node_list[running_index++];
    1093         284 :                 node1 = node_list[running_index];
    1094         292 :                 node2 = mesh.add_point(Point(1., 1., 0.));
    1095             :               }
    1096             : 
    1097        5538 :               new_elem = std::make_unique<C0Polygon>(3);
    1098             :               // Switch to Tri3 when exodus default output supports element type mixes
    1099        5538 :               new_elem->set_node(0, node0);
    1100        5538 :               new_elem->set_node(1, node1);
    1101        5538 :               new_elem->set_node(2, node2);
    1102        5694 :               auto * elem = mesh.add_elem(std::move(new_elem));
    1103             : 
    1104             :               // Set boundaries
    1105        5538 :               if (i == 0)
    1106         852 :                 boundary_info.add_side(elem, 0, 3); // left
    1107        4686 :               else if (i == nx)
    1108         284 :                 boundary_info.add_side(elem, 1, 1); // right
    1109        5538 :               boundary_info.add_side(elem, 2, 2); // top
    1110             : 
    1111             :             }
    1112          24 :             break;
    1113         804 :           }
    1114             : 
    1115             : 
    1116           0 :           default:
    1117           0 :             libmesh_error_msg("ERROR: Unrecognized 2D element type == " << Utility::enum_to_string(type));
    1118             :           }
    1119             : 
    1120             : 
    1121             : 
    1122             : 
    1123             :         // Scale the nodal positions
    1124      160824 :         if (gauss_lobatto_grid)
    1125             :           {
    1126             :             GaussLobattoRedistributionFunction func(nx, xmin, xmax,
    1127           0 :                                                     ny, ymin, ymax);
    1128           0 :             MeshTools::Modification::redistribute(mesh, func);
    1129           0 :           }
    1130             :         else // !gauss_lobatto_grid
    1131             :           {
    1132             :             Threads::parallel_for_each
    1133      160824 :               (mesh.node_stored_range(),
    1134     1792647 :                [xmin, xmax, ymin, ymax](Node * node)
    1135             :                {
    1136     8568089 :                  (*node)(0) = (*node)(0)*(xmax-xmin) + xmin;
    1137     8355364 :                  (*node)(1) = ((*node)(1))*(ymax-ymin) + ymin;
    1138     8142639 :                });
    1139             :           }
    1140             : 
    1141             :         // Add sideset names to boundary info
    1142      160824 :         boundary_info.sideset_name(0) = "bottom";
    1143      160824 :         boundary_info.sideset_name(1) = "right";
    1144      160824 :         boundary_info.sideset_name(2) = "top";
    1145      160824 :         boundary_info.sideset_name(3) = "left";
    1146             : 
    1147             :         // Add nodeset names to boundary info
    1148      160824 :         boundary_info.nodeset_name(0) = "bottom";
    1149      160824 :         boundary_info.nodeset_name(1) = "right";
    1150      160824 :         boundary_info.nodeset_name(2) = "top";
    1151      160824 :         boundary_info.nodeset_name(3) = "left";
    1152             : 
    1153        4519 :         break;
    1154             :       }
    1155             : 
    1156             : 
    1157             : 
    1158             : 
    1159             : 
    1160             : 
    1161             : 
    1162             : 
    1163             : 
    1164             : 
    1165             : 
    1166             :       //---------------------------------------------------------------------
    1167             :       // Build a 3D mesh using hexes, tets, prisms, or pyramids.
    1168      187640 :     case 3:
    1169             :       {
    1170        5310 :         libmesh_assert_not_equal_to (nx, 0);
    1171        5310 :         libmesh_assert_not_equal_to (ny, 0);
    1172        5310 :         libmesh_assert_not_equal_to (nz, 0);
    1173        5310 :         libmesh_assert_less (xmin, xmax);
    1174        5310 :         libmesh_assert_less (ymin, ymax);
    1175        5310 :         libmesh_assert_less (zmin, zmax);
    1176             : 
    1177             : 
    1178             :         // Reserve elements.  Meshes with prismatic elements require
    1179             :         // twice as many elements.
    1180             :         switch (type)
    1181             :           {
    1182      121600 :           case INVALID_ELEM:
    1183             :           case HEX8:
    1184             :           case HEX20:
    1185             :           case HEX27:
    1186             :           case C0POLYHEDRON:
    1187             :           case TET4:  // TET4's are created from an initial HEX27 discretization
    1188             :           case TET10: // TET10's are created from an initial HEX27 discretization
    1189             :           case TET14: // TET14's are created from an initial HEX27 discretization
    1190             :           case PYRAMID5: // PYRAMIDs are created from an initial HEX27 discretization
    1191             :           case PYRAMID13:
    1192             :           case PYRAMID14:
    1193             :           case PYRAMID18:
    1194             :             {
    1195      121600 :               mesh.reserve_elem(nx*ny*nz);
    1196      118154 :               break;
    1197             :             }
    1198             : 
    1199       66040 :           case PRISM6:
    1200             :           case PRISM15:
    1201             :           case PRISM18:
    1202             :           case PRISM20:
    1203             :           case PRISM21:
    1204             :             {
    1205       66040 :               mesh.reserve_elem(2*nx*ny*nz);
    1206       64176 :               break;
    1207             :             }
    1208             : 
    1209           0 :           default:
    1210           0 :             libmesh_error_msg("ERROR: Unrecognized 3D element type == " << Utility::enum_to_string(type));
    1211             :           }
    1212             : 
    1213             : 
    1214             : 
    1215             : 
    1216             : 
    1217             :         // Reserve nodes.  Quadratic elements need twice as many nodes as linear elements.
    1218             :         switch (type)
    1219             :           {
    1220       32298 :           case INVALID_ELEM:
    1221             :           case HEX8:
    1222             :           case PRISM6:
    1223             :           case C0POLYHEDRON:
    1224             :             {
    1225             :               const dof_id_type grid_nodes =
    1226       32298 :                 cast_int<dof_id_type>((nx+1)*(ny+1)*(nz+1));
    1227             : 
    1228             :               // Reserve one interior node per polyhedron for the robust
    1229             :               // fallback tetrahedralization used when the preferred
    1230             :               // tetrahedralization cannot be constructed.
    1231             :               const dof_id_type mid_polyhedron_nodes =
    1232       32298 :                 (type == C0POLYHEDRON) ?
    1233         355 :                 cast_int<dof_id_type>(nx*ny*nz) : 0;
    1234             : 
    1235       32298 :               mesh.reserve_nodes(grid_nodes + mid_polyhedron_nodes);
    1236       31362 :               break;
    1237             :             }
    1238             : 
    1239      108350 :           case HEX20:
    1240             :           case HEX27:
    1241             :           case TET4: // TET4's are created from an initial HEX27 discretization
    1242             :           case TET10: // TET10's are created from an initial HEX27 discretization
    1243             :           case PYRAMID5: // PYRAMIDs are created from an initial HEX27 discretization
    1244             :           case PYRAMID13:
    1245             :           case PYRAMID14:
    1246             :           case PYRAMID18:
    1247             :           case PRISM15:
    1248             :           case PRISM18:
    1249             :             {
    1250             :               // FYI: The resulting TET4 mesh will have exactly
    1251             :               // 5*(nx*ny*nz) + 2*(nx*ny + nx*nz + ny*nz) + (nx+ny+nz) + 1
    1252             :               // nodes once the additional mid-edge nodes for the HEX27 discretization
    1253             :               // have been deleted.
    1254      108350 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1)*(2*nz+1) );
    1255      105296 :               break;
    1256             :             }
    1257             : 
    1258       16533 :           case TET14:
    1259             :             {
    1260       18843 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1)*(2*nz+1) +
    1261       16533 :                                   24*nx*ny*nz +
    1262       16533 :                                   4*(nx*ny + ny*nz + nx*nz) );
    1263       16071 :               break;
    1264             :             }
    1265             : 
    1266       13632 :           case PRISM20:
    1267             :             {
    1268       14784 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1)*(2*nz+1) +
    1269       13632 :                                   2*nx*ny*(nz+1) );
    1270       13248 :               break;
    1271             :             }
    1272             : 
    1273       16827 :           case PRISM21:
    1274             :             {
    1275       18249 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1)*(2*nz+1) +
    1276       16827 :                                   2*nx*ny*(2*nz+1) );
    1277       16353 :               break;
    1278             :             }
    1279             : 
    1280           0 :           default:
    1281           0 :             libmesh_error_msg("ERROR: Unrecognized 3D element type == " << Utility::enum_to_string(type));
    1282             :           }
    1283             : 
    1284             : 
    1285             : 
    1286             : 
    1287             :         // Build the nodes.
    1288        5310 :         unsigned int node_id = 0;
    1289             :         switch (type)
    1290             :           {
    1291         936 :           case INVALID_ELEM:
    1292             :           case HEX8:
    1293             :           case PRISM6:
    1294             :           case C0POLYHEDRON:
    1295             :             {
    1296      113419 :               for (unsigned int k=0; k<=nz; k++)
    1297      340462 :                 for (unsigned int j=0; j<=ny; j++)
    1298     2038510 :                   for (unsigned int i=0; i<=nx; i++)
    1299             :                   {
    1300             :                     const Node * const node =
    1301     3457446 :                         mesh.add_point(Point(static_cast<Real>(i) / static_cast<Real>(nx),
    1302     1779169 :                                              static_cast<Real>(j) / static_cast<Real>(ny),
    1303     1779169 :                                              static_cast<Real>(k) / static_cast<Real>(nz)),
    1304     1757968 :                                        node_id++);
    1305     1779169 :                     if (k == 0)
    1306      348943 :                       boundary_info.add_node(node, 0);
    1307     1779169 :                     if (k == nz)
    1308      348943 :                       boundary_info.add_node(node, 5);
    1309     1779169 :                     if (j == 0)
    1310      302083 :                       boundary_info.add_node(node, 1);
    1311     1779169 :                     if (j == ny)
    1312      302083 :                       boundary_info.add_node(node, 3);
    1313     1779169 :                     if (i == 0)
    1314      259341 :                       boundary_info.add_node(node, 4);
    1315     1779169 :                     if (i == nx)
    1316      259341 :                       boundary_info.add_node(node, 2);
    1317             :                   }
    1318             : 
    1319         936 :               break;
    1320             :             }
    1321             : 
    1322        4374 :           case HEX20:
    1323             :           case HEX27:
    1324             :           case TET4: // TET4's are created from an initial HEX27 discretization
    1325             :           case TET10: // TET10's are created from an initial HEX27 discretization
    1326             :           case TET14: // TET14's are created from an initial HEX27 discretization
    1327             :           case PYRAMID5: // PYRAMIDs are created from an initial HEX27 discretization
    1328             :           case PYRAMID13:
    1329             :           case PYRAMID14:
    1330             :           case PYRAMID18:
    1331             :           case PRISM15:
    1332             :           case PRISM18:
    1333             :           case PRISM20:
    1334             :           case PRISM21:
    1335             :             {
    1336      726266 :               for (unsigned int k=0; k<=(2*nz); k++)
    1337     3036592 :                 for (unsigned int j=0; j<=(2*ny); j++)
    1338    19876630 :                   for (unsigned int i=0; i<=(2*nx); i++)
    1339             :                   {
    1340             :                     const Node * const node =
    1341    33960312 :                         mesh.add_point(Point(static_cast<Real>(i) / static_cast<Real>(2 * nx),
    1342    17410962 :                                              static_cast<Real>(j) / static_cast<Real>(2 * ny),
    1343    17410962 :                                              static_cast<Real>(k) / static_cast<Real>(2 * nz)),
    1344    17196084 :                                        node_id++);
    1345    17410962 :                     if (k == 0)
    1346     2580160 :                       boundary_info.add_node(node, 0);
    1347    17410962 :                     if (k == 2*nz)
    1348     2580160 :                       boundary_info.add_node(node, 5);
    1349    17410962 :                     if (j == 0)
    1350     2524770 :                       boundary_info.add_node(node, 1);
    1351    17410962 :                     if (j == 2*ny)
    1352     2524770 :                       boundary_info.add_node(node, 3);
    1353    17410962 :                     if (i == 0)
    1354     2465668 :                       boundary_info.add_node(node, 4);
    1355    17410962 :                     if (i == 2*nx)
    1356     2465668 :                       boundary_info.add_node(node, 2);
    1357             :                   }
    1358             : 
    1359      155342 :               if (type == PRISM20 ||
    1360             :                   type == PRISM21)
    1361             :                 {
    1362       30459 :                   const unsigned int kmax = (type == PRISM20) ? nz : 2*nz;
    1363      121339 :                   for (unsigned int k=0; k<=kmax; k++)
    1364      216692 :                     for (unsigned int j=0; j<ny; j++)
    1365      321488 :                       for (unsigned int i=0; i<nx; i++)
    1366             :                         {
    1367             :                           const Node * const node1 =
    1368      380328 :                               mesh.add_point(Point((static_cast<Real>(i)+1/Real(3)) / static_cast<Real>(nx),
    1369      195676 :                                                    (static_cast<Real>(j)+1/Real(3)) / static_cast<Real>(ny),
    1370      195676 :                                                    static_cast<Real>(k) / static_cast<Real>(kmax)),
    1371      192920 :                                              node_id++);
    1372      195676 :                           if (k == 0)
    1373       56445 :                             boundary_info.add_node(node1, 0);
    1374      195676 :                           if (k == kmax)
    1375       56445 :                             boundary_info.add_node(node1, 5);
    1376             : 
    1377             :                           const Node * const node2 =
    1378      380328 :                               mesh.add_point(Point((static_cast<Real>(i)+2/Real(3)) / static_cast<Real>(nx),
    1379      195676 :                                                    (static_cast<Real>(j)+2/Real(3)) / static_cast<Real>(ny),
    1380        5512 :                                                    static_cast<Real>(k) / static_cast<Real>(kmax)),
    1381      192920 :                                              node_id++);
    1382      195676 :                           if (k == 0)
    1383       56445 :                             boundary_info.add_node(node2, 0);
    1384      195676 :                           if (k == kmax)
    1385       56445 :                             boundary_info.add_node(node2, 5);
    1386             :                         }
    1387             :                 }
    1388             : 
    1389        4374 :               break;
    1390             :             }
    1391             : 
    1392             : 
    1393           0 :           default:
    1394           0 :             libmesh_error_msg("ERROR: Unrecognized 3D element type == " << Utility::enum_to_string(type));
    1395             :           }
    1396             : 
    1397             : 
    1398             : 
    1399             : 
    1400             :         // Build the elements.
    1401        5310 :         unsigned int elem_id = 0;
    1402             :         switch (type)
    1403             :           {
    1404         614 :           case INVALID_ELEM:
    1405             :           case HEX8:
    1406             :             {
    1407       54851 :               for (unsigned int k=0; k<nz; k++)
    1408      139312 :                 for (unsigned int j=0; j<ny; j++)
    1409     1154523 :                   for (unsigned int i=0; i<nx; i++)
    1410             :                     {
    1411     1049195 :                       Elem * elem = mesh.add_elem(Elem::build_with_id(HEX8, elem_id++));
    1412     1049195 :                       elem->set_node(0, mesh.node_ptr(idx(type,nx,ny,i,j,k)      ));
    1413     1049195 :                       elem->set_node(1, mesh.node_ptr(idx(type,nx,ny,i+1,j,k)    ));
    1414     1049195 :                       elem->set_node(2, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ));
    1415     1049195 :                       elem->set_node(3, mesh.node_ptr(idx(type,nx,ny,i,j+1,k)    ));
    1416     1049195 :                       elem->set_node(4, mesh.node_ptr(idx(type,nx,ny,i,j,k+1)    ));
    1417     1049195 :                       elem->set_node(5, mesh.node_ptr(idx(type,nx,ny,i+1,j,k+1)  ));
    1418     1049195 :                       elem->set_node(6, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)));
    1419     1049195 :                       elem->set_node(7, mesh.node_ptr(idx(type,nx,ny,i,j+1,k+1)  ));
    1420             : 
    1421     1049195 :                       if (k == 0)
    1422      184919 :                         boundary_info.add_side(elem, 0, 0);
    1423             : 
    1424     1049195 :                       if (k == (nz-1))
    1425      184919 :                         boundary_info.add_side(elem, 5, 5);
    1426             : 
    1427     1049195 :                       if (j == 0)
    1428      139479 :                         boundary_info.add_side(elem, 1, 1);
    1429             : 
    1430     1049195 :                       if (j == (ny-1))
    1431      139479 :                         boundary_info.add_side(elem, 3, 3);
    1432             : 
    1433     1049195 :                       if (i == 0)
    1434      105328 :                         boundary_info.add_side(elem, 4, 4);
    1435             : 
    1436     1049195 :                       if (i == (nx-1))
    1437      105328 :                         boundary_info.add_side(elem, 2, 2);
    1438             :                     }
    1439         614 :               break;
    1440             :             }
    1441             : 
    1442             : 
    1443         355 :           case C0POLYHEDRON:
    1444             :             {
    1445         355 :               const std::array<std::array<unsigned int, 4>, 6> side_nodes =
    1446             :                 {{{0, 1, 2, 3},   // min z
    1447             :                   {0, 1, 5, 4},   // min y
    1448             :                   {2, 6, 5, 1},   // max x
    1449             :                   {2, 3, 7, 6},   // max y
    1450             :                   {0, 4, 7, 3},   // min x
    1451             :                   {5, 6, 7, 4}}}; // max z
    1452             : 
    1453        1065 :               for (unsigned int k=0; k<nz; k++)
    1454        2130 :                 for (unsigned int j=0; j<ny; j++)
    1455        4260 :                   for (unsigned int i=0; i<nx; i++)
    1456             :                     {
    1457             :                       std::array<Node *, 8> elem_nodes =
    1458        2840 :                         {{mesh.node_ptr(idx(type,nx,ny,i,j,k)      ),
    1459        2840 :                           mesh.node_ptr(idx(type,nx,ny,i+1,j,k)    ),
    1460        2840 :                           mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ),
    1461        2840 :                           mesh.node_ptr(idx(type,nx,ny,i,j+1,k)    ),
    1462        2840 :                           mesh.node_ptr(idx(type,nx,ny,i,j,k+1)    ),
    1463        2840 :                           mesh.node_ptr(idx(type,nx,ny,i+1,j,k+1)  ),
    1464        2840 :                           mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)),
    1465       19880 :                           mesh.node_ptr(idx(type,nx,ny,i,j+1,k+1)  )}};
    1466             : 
    1467        3000 :                       std::vector<std::shared_ptr<Polygon>> sides(side_nodes.size());
    1468       19880 :                       for (auto s : index_range(side_nodes))
    1469             :                         {
    1470       17520 :                           sides[s] = std::make_shared<C0Polygon>(side_nodes[s].size());
    1471       85200 :                           for (auto n : index_range(side_nodes[s]))
    1472       72000 :                             sides[s]->set_node(n, elem_nodes[side_nodes[s][n]]);
    1473             :                         }
    1474             : 
    1475        2840 :                       std::unique_ptr<Node> mid_elem_node;
    1476             :                       std::unique_ptr<Elem> new_elem =
    1477        2920 :                         std::make_unique<C0Polyhedron>(sides, mid_elem_node);
    1478        2840 :                       if (mid_elem_node)
    1479           0 :                         mesh.add_node(std::move(mid_elem_node));
    1480             : 
    1481        2840 :                       new_elem->set_id() = elem_id++;
    1482        2920 :                       Elem * elem = mesh.add_elem(std::move(new_elem));
    1483             : 
    1484        2840 :                       if (k == 0)
    1485        1420 :                         boundary_info.add_side(elem, 0, 0);
    1486             : 
    1487        2840 :                       if (k == (nz-1))
    1488        1420 :                         boundary_info.add_side(elem, 5, 5);
    1489             : 
    1490        2840 :                       if (j == 0)
    1491        1420 :                         boundary_info.add_side(elem, 1, 1);
    1492             : 
    1493        2840 :                       if (j == (ny-1))
    1494        1420 :                         boundary_info.add_side(elem, 3, 3);
    1495             : 
    1496        2840 :                       if (i == 0)
    1497        1420 :                         boundary_info.add_side(elem, 4, 4);
    1498             : 
    1499        2840 :                       if (i == (nx-1))
    1500        1420 :                         boundary_info.add_side(elem, 2, 2);
    1501        2680 :                     }
    1502          10 :               break;
    1503             :             }
    1504             : 
    1505             : 
    1506             : 
    1507             : 
    1508         312 :           case PRISM6:
    1509             :             {
    1510       25205 :               for (unsigned int k=0; k<nz; k++)
    1511       35358 :                 for (unsigned int j=0; j<ny; j++)
    1512       60492 :                   for (unsigned int i=0; i<nx; i++)
    1513             :                     {
    1514             :                       // First Prism
    1515       39263 :                       Elem * elem = mesh.add_elem(Elem::build_with_id(PRISM6, elem_id++));
    1516       39263 :                       elem->set_node(0, mesh.node_ptr(idx(type,nx,ny,i,j,k)      ));
    1517       39263 :                       elem->set_node(1, mesh.node_ptr(idx(type,nx,ny,i+1,j,k)    ));
    1518       39263 :                       elem->set_node(2, mesh.node_ptr(idx(type,nx,ny,i,j+1,k)    ));
    1519       39263 :                       elem->set_node(3, mesh.node_ptr(idx(type,nx,ny,i,j,k+1)    ));
    1520       39263 :                       elem->set_node(4, mesh.node_ptr(idx(type,nx,ny,i+1,j,k+1)  ));
    1521       39263 :                       elem->set_node(5, mesh.node_ptr(idx(type,nx,ny,i,j+1,k+1)  ));
    1522             : 
    1523             :                       // Add sides for first prism to boundary info object
    1524       39263 :                       if (i==0)
    1525       21229 :                         boundary_info.add_side(elem, 3, 4);
    1526             : 
    1527       39263 :                       if (j==0)
    1528       21229 :                         boundary_info.add_side(elem, 1, 1);
    1529             : 
    1530       39263 :                       if (k==0)
    1531       21229 :                         boundary_info.add_side(elem, 0, 0);
    1532             : 
    1533       39263 :                       if (k == (nz-1))
    1534       21229 :                         boundary_info.add_side(elem, 4, 5);
    1535             : 
    1536             :                       // Second Prism
    1537       39263 :                       elem = mesh.add_elem(Elem::build_with_id(PRISM6, elem_id++));
    1538       39263 :                       elem->set_node(0, mesh.node_ptr(idx(type,nx,ny,i+1,j,k)    ));
    1539       39263 :                       elem->set_node(1, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ));
    1540       39263 :                       elem->set_node(2, mesh.node_ptr(idx(type,nx,ny,i,j+1,k)    ));
    1541       39263 :                       elem->set_node(3, mesh.node_ptr(idx(type,nx,ny,i+1,j,k+1)  ));
    1542       39263 :                       elem->set_node(4, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)));
    1543       39263 :                       elem->set_node(5, mesh.node_ptr(idx(type,nx,ny,i,j+1,k+1)  ));
    1544             : 
    1545             :                       // Add sides for second prism to boundary info object
    1546       39263 :                       if (i == (nx-1))
    1547       21229 :                         boundary_info.add_side(elem, 1, 2);
    1548             : 
    1549       39263 :                       if (j == (ny-1))
    1550       21229 :                         boundary_info.add_side(elem, 2, 3);
    1551             : 
    1552       39263 :                       if (k==0)
    1553       21229 :                         boundary_info.add_side(elem, 0, 0);
    1554             : 
    1555       39263 :                       if (k == (nz-1))
    1556       21229 :                         boundary_info.add_side(elem, 4, 5);
    1557             :                     }
    1558         312 :               break;
    1559             :             }
    1560             : 
    1561             : 
    1562             : 
    1563             : 
    1564             : 
    1565             : 
    1566        2822 :           case HEX20:
    1567             :           case HEX27:
    1568             :           case TET4: // TET4's are created from an initial HEX27 discretization
    1569             :           case TET10: // TET10's are created from an initial HEX27 discretization
    1570             :           case TET14: // TET14's are created from an initial HEX27 discretization
    1571             :           case PYRAMID5: // PYRAMIDs are created from an initial HEX27 discretization
    1572             :           case PYRAMID13:
    1573             :           case PYRAMID14:
    1574             :           case PYRAMID18:
    1575             :             {
    1576      239487 :               for (unsigned int k=0; k<(2*nz); k += 2)
    1577      412531 :                 for (unsigned int j=0; j<(2*ny); j += 2)
    1578     1548202 :                   for (unsigned int i=0; i<(2*nx); i += 2)
    1579             :                     {
    1580     1274780 :                       ElemType build_type = (type == HEX20) ? HEX20 : HEX27;
    1581     1274780 :                       Elem * elem = mesh.add_elem(Elem::build_with_id(build_type, elem_id++));
    1582             : 
    1583     1274780 :                       elem->set_node(0,  mesh.node_ptr(idx(type,nx,ny,i,  j,  k)  ));
    1584     1274780 :                       elem->set_node(1,  mesh.node_ptr(idx(type,nx,ny,i+2,j,  k)  ));
    1585     1274780 :                       elem->set_node(2,  mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k)  ));
    1586     1274780 :                       elem->set_node(3,  mesh.node_ptr(idx(type,nx,ny,i,  j+2,k)  ));
    1587     1274780 :                       elem->set_node(4,  mesh.node_ptr(idx(type,nx,ny,i,  j,  k+2)));
    1588     1274780 :                       elem->set_node(5,  mesh.node_ptr(idx(type,nx,ny,i+2,j,  k+2)));
    1589     1274780 :                       elem->set_node(6,  mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k+2)));
    1590     1274780 :                       elem->set_node(7,  mesh.node_ptr(idx(type,nx,ny,i,  j+2,k+2)));
    1591     1274780 :                       elem->set_node(8,  mesh.node_ptr(idx(type,nx,ny,i+1,j,  k)  ));
    1592     1274780 :                       elem->set_node(9,  mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k)  ));
    1593     1274780 :                       elem->set_node(10, mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k)  ));
    1594     1274780 :                       elem->set_node(11, mesh.node_ptr(idx(type,nx,ny,i,  j+1,k)  ));
    1595     1274780 :                       elem->set_node(12, mesh.node_ptr(idx(type,nx,ny,i,  j,  k+1)));
    1596     1274780 :                       elem->set_node(13, mesh.node_ptr(idx(type,nx,ny,i+2,j,  k+1)));
    1597     1274780 :                       elem->set_node(14, mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k+1)));
    1598     1274780 :                       elem->set_node(15, mesh.node_ptr(idx(type,nx,ny,i,  j+2,k+1)));
    1599     1274780 :                       elem->set_node(16, mesh.node_ptr(idx(type,nx,ny,i+1,j,  k+2)));
    1600     1274780 :                       elem->set_node(17, mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k+2)));
    1601     1274780 :                       elem->set_node(18, mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k+2)));
    1602     1274780 :                       elem->set_node(19, mesh.node_ptr(idx(type,nx,ny,i,  j+1,k+2)));
    1603             : 
    1604     1274780 :                       if ((type == HEX27) || (type == TET4) || (type == TET10) || (type == TET14) ||
    1605        6884 :                           (type == PYRAMID5) || (type == PYRAMID13) || (type == PYRAMID14) ||
    1606        2418 :                           (type == PYRAMID18))
    1607             :                         {
    1608     1229482 :                           elem->set_node(20, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ));
    1609     1229482 :                           elem->set_node(21, mesh.node_ptr(idx(type,nx,ny,i+1,j,  k+1)));
    1610     1229482 :                           elem->set_node(22, mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k+1)));
    1611     1229482 :                           elem->set_node(23, mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k+1)));
    1612     1229482 :                           elem->set_node(24, mesh.node_ptr(idx(type,nx,ny,i,  j+1,k+1)));
    1613     1229482 :                           elem->set_node(25, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+2)));
    1614     1229482 :                           elem->set_node(26, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)));
    1615             :                         }
    1616             : 
    1617     1274780 :                       if (k == 0)
    1618      299514 :                         boundary_info.add_side(elem, 0, 0);
    1619             : 
    1620     1274780 :                       if (k == 2*(nz-1))
    1621      299514 :                         boundary_info.add_side(elem, 5, 5);
    1622             : 
    1623     1274780 :                       if (j == 0)
    1624      285927 :                         boundary_info.add_side(elem, 1, 1);
    1625             : 
    1626     1274780 :                       if (j == 2*(ny-1))
    1627      285927 :                         boundary_info.add_side(elem, 3, 3);
    1628             : 
    1629     1274780 :                       if (i == 0)
    1630      273422 :                         boundary_info.add_side(elem, 4, 4);
    1631             : 
    1632     1274780 :                       if (i == 2*(nx-1))
    1633      273422 :                         boundary_info.add_side(elem, 2, 2);
    1634             :                     }
    1635        2822 :               break;
    1636             :             }
    1637             : 
    1638             : 
    1639             : 
    1640             : 
    1641        1552 :           case PRISM15:
    1642             :           case PRISM18:
    1643             :           case PRISM20:
    1644             :           case PRISM21:
    1645             :             {
    1646      123646 :               for (unsigned int k=0; k<(2*nz); k += 2)
    1647      164840 :                 for (unsigned int j=0; j<(2*ny); j += 2)
    1648      247448 :                   for (unsigned int i=0; i<(2*nx); i += 2)
    1649             :                     {
    1650             :                       // First Prism
    1651      151290 :                       Elem * elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
    1652      151290 :                       elem->set_node(0,  mesh.node_ptr(idx(type,nx,ny,i,  j,  k)  ));
    1653      151290 :                       elem->set_node(1,  mesh.node_ptr(idx(type,nx,ny,i+2,j,  k)  ));
    1654      151290 :                       elem->set_node(2,  mesh.node_ptr(idx(type,nx,ny,i,  j+2,k)  ));
    1655      151290 :                       elem->set_node(3,  mesh.node_ptr(idx(type,nx,ny,i,  j,  k+2)));
    1656      151290 :                       elem->set_node(4,  mesh.node_ptr(idx(type,nx,ny,i+2,j,  k+2)));
    1657      151290 :                       elem->set_node(5,  mesh.node_ptr(idx(type,nx,ny,i,  j+2,k+2)));
    1658      151290 :                       elem->set_node(6,  mesh.node_ptr(idx(type,nx,ny,i+1,j,  k)  ));
    1659      151290 :                       elem->set_node(7,  mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ));
    1660      151290 :                       elem->set_node(8,  mesh.node_ptr(idx(type,nx,ny,i,  j+1,k)  ));
    1661      151290 :                       elem->set_node(9,  mesh.node_ptr(idx(type,nx,ny,i,  j,  k+1)));
    1662      151290 :                       elem->set_node(10, mesh.node_ptr(idx(type,nx,ny,i+2,j,  k+1)));
    1663      151290 :                       elem->set_node(11, mesh.node_ptr(idx(type,nx,ny,i,  j+2,k+1)));
    1664      151290 :                       elem->set_node(12, mesh.node_ptr(idx(type,nx,ny,i+1,j,  k+2)));
    1665      151290 :                       elem->set_node(13, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+2)));
    1666      151290 :                       elem->set_node(14, mesh.node_ptr(idx(type,nx,ny,i,  j+1,k+2)));
    1667             : 
    1668      155706 :                       if (type == PRISM18 ||
    1669      149050 :                           type == PRISM20 ||
    1670             :                           type == PRISM21)
    1671             :                         {
    1672      122180 :                           elem->set_node(15, mesh.node_ptr(idx(type,nx,ny,i+1,j,  k+1)));
    1673      122180 :                           elem->set_node(16, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)));
    1674      122180 :                           elem->set_node(17, mesh.node_ptr(idx(type,nx,ny,i,  j+1,k+1)));
    1675             :                         }
    1676             : 
    1677      151290 :                       if (type == PRISM20)
    1678             :                         {
    1679       42955 :                           const dof_id_type base_idx = (2*nx+1)*(2*ny+1)*(2*nz+1);
    1680       42955 :                           elem->set_node(18, mesh.node_ptr(base_idx+((k/2)*(nx*ny)+j/2*nx+i/2)*2));
    1681       42955 :                           elem->set_node(19, mesh.node_ptr(base_idx+(((k/2)+1)*(nx*ny)+j/2*nx+i/2)*2));
    1682             :                         }
    1683             : 
    1684      151290 :                       if (type == PRISM21)
    1685             :                         {
    1686       48138 :                           const dof_id_type base_idx = (2*nx+1)*(2*ny+1)*(2*nz+1);
    1687       48138 :                           elem->set_node(18, mesh.node_ptr(base_idx+(k*(nx*ny)+j/2*nx+i/2)*2));
    1688       48138 :                           elem->set_node(19, mesh.node_ptr(base_idx+((k+2)*(nx*ny)+j/2*nx+i/2)*2));
    1689       48138 :                           elem->set_node(20, mesh.node_ptr(base_idx+((k+1)*(nx*ny)+j/2*nx+i/2)*2));
    1690             :                         }
    1691             : 
    1692             :                       // Add sides for first prism to boundary info object
    1693      151290 :                       if (i==0)
    1694       96158 :                         boundary_info.add_side(elem, 3, 4);
    1695             : 
    1696      151290 :                       if (j==0)
    1697       96158 :                         boundary_info.add_side(elem, 1, 1);
    1698             : 
    1699      151290 :                       if (k==0)
    1700       96208 :                         boundary_info.add_side(elem, 0, 0);
    1701             : 
    1702      151290 :                       if (k == 2*(nz-1))
    1703       96208 :                         boundary_info.add_side(elem, 4, 5);
    1704             : 
    1705             : 
    1706             :                       // Second Prism
    1707      151290 :                       elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
    1708      151290 :                       elem->set_node(0,  mesh.node_ptr(idx(type,nx,ny,i+2,j,k)     ));
    1709      151290 :                       elem->set_node(1,  mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k)   ));
    1710      151290 :                       elem->set_node(2,  mesh.node_ptr(idx(type,nx,ny,i,j+2,k)     ));
    1711      151290 :                       elem->set_node(3,  mesh.node_ptr(idx(type,nx,ny,i+2,j,k+2)   ));
    1712      151290 :                       elem->set_node(4,  mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k+2) ));
    1713      151290 :                       elem->set_node(5,  mesh.node_ptr(idx(type,nx,ny,i,j+2,k+2)   ));
    1714      151290 :                       elem->set_node(6,  mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k)  ));
    1715      151290 :                       elem->set_node(7,  mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k)  ));
    1716      151290 :                       elem->set_node(8,  mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ));
    1717      151290 :                       elem->set_node(9,  mesh.node_ptr(idx(type,nx,ny,i+2,j,k+1)  ));
    1718      151290 :                       elem->set_node(10, mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k+1)));
    1719      151290 :                       elem->set_node(11, mesh.node_ptr(idx(type,nx,ny,i,j+2,k+1)  ));
    1720      151290 :                       elem->set_node(12, mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k+2)));
    1721      151290 :                       elem->set_node(13, mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k+2)));
    1722      151290 :                       elem->set_node(14, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+2)));
    1723             : 
    1724      151290 :                       if (type == PRISM18 ||
    1725       77248 :                           type == PRISM20 ||
    1726             :                           type == PRISM21)
    1727             :                         {
    1728      122180 :                           elem->set_node(15,  mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k+1)));
    1729      122180 :                           elem->set_node(16,  mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k+1)));
    1730      122180 :                           elem->set_node(17,  mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)));
    1731             :                         }
    1732             : 
    1733      151290 :                       if (type == PRISM20)
    1734             :                         {
    1735       42955 :                           const dof_id_type base_idx = (2*nx+1)*(2*ny+1)*(2*nz+1);
    1736       42955 :                           elem->set_node(18, mesh.node_ptr(base_idx+((k/2)*(nx*ny)+j/2*nx+i/2)*2+1));
    1737       42955 :                           elem->set_node(19, mesh.node_ptr(base_idx+(((k/2)+1)*(nx*ny)+j/2*nx+i/2)*2+1));
    1738             :                         }
    1739             : 
    1740      151290 :                       if (type == PRISM21)
    1741             :                         {
    1742       48138 :                           const dof_id_type base_idx = (2*nx+1)*(2*ny+1)*(2*nz+1);
    1743       48138 :                           elem->set_node(18, mesh.node_ptr(base_idx+(k*(nx*ny)+j/2*nx+i/2)*2+1));
    1744       48138 :                           elem->set_node(19, mesh.node_ptr(base_idx+((k+2)*(nx*ny)+j/2*nx+i/2)*2+1));
    1745       48138 :                           elem->set_node(20, mesh.node_ptr(base_idx+((k+1)*(nx*ny)+j/2*nx+i/2)*2+1));
    1746             :                         }
    1747             : 
    1748             :                       // Add sides for second prism to boundary info object
    1749      151290 :                       if (i == 2*(nx-1))
    1750       96158 :                         boundary_info.add_side(elem, 1, 2);
    1751             : 
    1752      151290 :                       if (j == 2*(ny-1))
    1753       96158 :                         boundary_info.add_side(elem, 2, 3);
    1754             : 
    1755      151290 :                       if (k==0)
    1756       96208 :                         boundary_info.add_side(elem, 0, 0);
    1757             : 
    1758      151290 :                       if (k == 2*(nz-1))
    1759       96208 :                         boundary_info.add_side(elem, 4, 5);
    1760             : 
    1761             :                     }
    1762        1552 :               break;
    1763             :             }
    1764             : 
    1765             : 
    1766             : 
    1767             : 
    1768             : 
    1769           0 :           default:
    1770           0 :             libmesh_error_msg("ERROR: Unrecognized 3D element type == " << Utility::enum_to_string(type));
    1771             :           }
    1772             : 
    1773             : 
    1774             : 
    1775             : 
    1776             :         //.......................................
    1777             :         // Scale the nodal positions
    1778      187640 :         if (gauss_lobatto_grid)
    1779             :           {
    1780             :             GaussLobattoRedistributionFunction func(nx, xmin, xmax,
    1781             :                                                     ny, ymin, ymax,
    1782           0 :                                                     nz, zmin, zmax);
    1783           0 :             MeshTools::Modification::redistribute(mesh, func);
    1784           0 :           }
    1785             :         else // !gauss_lobatto_grid
    1786             :           {
    1787             :             Threads::parallel_for_each
    1788      187640 :               (mesh.node_stored_range(),
    1789     5276800 :                [xmin, xmax, ymin, ymax, zmin, zmax](Node * node)
    1790             :                {
    1791    20073759 :                  (*node)(0) = ((*node)(0))*(xmax-xmin) + xmin;
    1792    20073759 :                  (*node)(1) = ((*node)(1))*(ymax-ymin) + ymin;
    1793    19581483 :                  (*node)(2) = ((*node)(2))*(zmax-zmin) + zmin;
    1794    19089207 :                });
    1795             :           }
    1796             : 
    1797             : 
    1798             : 
    1799             :         // Additional work for tets and pyramids: we take the existing
    1800             :         // HEX27 discretization and split each element into 24
    1801             :         // sub-tets or 6 sub-pyramids.
    1802             :         //
    1803             :         // 24 isn't the minimum-possible number of tets, but it
    1804             :         // obviates any concerns about the edge orientations between
    1805             :         // the various elements.
    1806      192950 :         if ((type == TET4) ||
    1807      186952 :             (type == TET10) ||
    1808      186490 :             (type == TET14) ||
    1809      146647 :             (type == PYRAMID5) ||
    1810        3960 :             (type == PYRAMID13) ||
    1811      143509 :             (type == PYRAMID14) ||
    1812      135689 :             (type == PYRAMID18))
    1813             :           {
    1814             :             // Temporary storage for new elements. (24 tets per hex, 6 pyramids)
    1815        4704 :             std::vector<std::unique_ptr<Elem>> new_elements;
    1816             : 
    1817             :             // For avoiding extraneous construction of element sides
    1818       55730 :             std::unique_ptr<Elem> side;
    1819             : 
    1820       55730 :             if ((type == TET4) || (type == TET10) || (type == TET14))
    1821       40891 :               new_elements.reserve(24*mesh.n_elem());
    1822             :             else
    1823       14839 :               new_elements.reserve(6*mesh.n_elem());
    1824             : 
    1825             :             // Create tetrahedra or pyramids
    1826      659564 :             for (auto & base_hex : mesh.element_ptr_range())
    1827             :               {
    1828             :                 // Get a pointer to the node located at the HEX27 center
    1829      282324 :                 Node * apex_node = base_hex->node_ptr(26);
    1830             : 
    1831             :                 // Container to catch ids handed back from BoundaryInfo
    1832       14976 :                 std::vector<boundary_id_type> ids;
    1833             : 
    1834     1976268 :                 for (auto s : base_hex->side_index_range())
    1835             :                   {
    1836             :                     // Get the boundary ID(s) for this side
    1837     1693944 :                     boundary_info.boundary_ids(base_hex, s, ids);
    1838             : 
    1839             :                     // We're creating this Mesh, so there should be 0 or 1 boundary IDs.
    1840       44928 :                     libmesh_assert(ids.size() <= 1);
    1841             : 
    1842             :                     // A convenient name for the side's ID.
    1843     1693944 :                     boundary_id_type b_id = ids.empty() ? BoundaryInfo::invalid_id : ids[0];
    1844             : 
    1845             :                     // Need to build the full-ordered side!
    1846     1693944 :                     base_hex->build_side_ptr(side, s);
    1847             : 
    1848     1693944 :                     if ((type == TET4) || (type == TET10) || (type == TET14))
    1849             :                       {
    1850             :                         // Build 4 sub-tets per side
    1851     5713500 :                         for (unsigned int sub_tet=0; sub_tet<4; ++sub_tet)
    1852             :                           {
    1853     9024000 :                             new_elements.push_back( Elem::build(TET4) );
    1854      117600 :                             auto & sub_elem = new_elements.back();
    1855     4806000 :                             sub_elem->set_node(0, side->node_ptr(sub_tet));
    1856     4806000 :                             sub_elem->set_node(1, side->node_ptr(8));                           // center of the face
    1857     4688400 :                             sub_elem->set_node(2, side->node_ptr(sub_tet==3 ? 0 : sub_tet+1 )); // wrap-around
    1858     4570800 :                             sub_elem->set_node(3, apex_node);                                   // apex node always used!
    1859             : 
    1860             :                             // If the original hex was a boundary hex, add the new sub_tet's side
    1861             :                             // 0 with the same b_id.  Note: the tets are all aligned so that their
    1862             :                             // side 0 is on the boundary.
    1863     4570800 :                             if (b_id != BoundaryInfo::invalid_id)
    1864     2129272 :                               boundary_info.add_side(sub_elem.get(), 0, b_id);
    1865       29400 :                           }
    1866             :                       } // end if ((type == TET4) || (type == TET10) || (type == TET14))
    1867             : 
    1868             :                     else // type==PYRAMID*
    1869             :                       {
    1870             :                         // Build 1 sub-pyramid per side.
    1871     1086960 :                         new_elements.push_back( Elem::build(PYRAMID5) );
    1872       15528 :                         auto & sub_elem = new_elements.back();
    1873             : 
    1874             :                         // Set the base.  Note that since the apex is *inside* the base_hex,
    1875             :                         // and the pyramid uses a counter-clockwise base numbering, we need to
    1876             :                         // reverse the [1] and [3] node indices.
    1877      582300 :                         sub_elem->set_node(0, side->node_ptr(0));
    1878      582300 :                         sub_elem->set_node(1, side->node_ptr(3));
    1879      582300 :                         sub_elem->set_node(2, side->node_ptr(2));
    1880      582300 :                         sub_elem->set_node(3, side->node_ptr(1));
    1881             : 
    1882             :                         // Set the apex
    1883      551244 :                         sub_elem->set_node(4, apex_node);
    1884             : 
    1885             :                         // If the original hex was a boundary hex, add the new sub_pyr's side
    1886             :                         // 4 (the square base) with the same b_id.
    1887      551244 :                         if (b_id != BoundaryInfo::invalid_id)
    1888      284700 :                           boundary_info.add_side(sub_elem.get(), 4, b_id);
    1889             :                       } // end else type==PYRAMID*
    1890             :                   }
    1891       52594 :               }
    1892             : 
    1893             : 
    1894             :             // Delete the original HEX27 elements from the mesh, and the boundary info structure.
    1895      659564 :             for (auto & elem : mesh.element_ptr_range())
    1896             :               {
    1897      282324 :                 boundary_info.remove(elem); // Safe even if elem has no boundary info.
    1898      282324 :                 mesh.delete_elem(elem);
    1899       52594 :               }
    1900             : 
    1901             :             // Add the new elements
    1902     5177774 :             for (auto i : index_range(new_elements))
    1903             :               {
    1904      465948 :                 new_elements[i]->set_id(i);
    1905     5388300 :                 mesh.add_elem( std::move(new_elements[i]) );
    1906             :               }
    1907             : 
    1908       52594 :           } // end if (type == TET*,PYRAMID*)
    1909             : 
    1910             : 
    1911             :         // Use all_second_order to convert the TET4's to TET10's or PYRAMID5's to PYRAMID14's
    1912      187640 :         if ((type == TET10) || (type == PYRAMID14))
    1913       15980 :           mesh.all_second_order();
    1914             : 
    1915      171660 :         else if (type == PYRAMID13)
    1916        3479 :           mesh.all_second_order(/*full_ordered=*/false);
    1917             : 
    1918      168181 :         else if ((type == TET14) || (type == PYRAMID18))
    1919       20722 :           mesh.all_complete_order();
    1920             : 
    1921             : 
    1922             :         // Add sideset names to boundary info (Z axis out of the screen)
    1923      187640 :         boundary_info.sideset_name(0) = "back";
    1924      187640 :         boundary_info.sideset_name(1) = "bottom";
    1925      187640 :         boundary_info.sideset_name(2) = "right";
    1926      187640 :         boundary_info.sideset_name(3) = "top";
    1927      187640 :         boundary_info.sideset_name(4) = "left";
    1928      187640 :         boundary_info.sideset_name(5) = "front";
    1929             : 
    1930             :         // Add nodeset names to boundary info
    1931      187640 :         boundary_info.nodeset_name(0) = "back";
    1932      187640 :         boundary_info.nodeset_name(1) = "bottom";
    1933      187640 :         boundary_info.nodeset_name(2) = "right";
    1934      187640 :         boundary_info.nodeset_name(3) = "top";
    1935      187640 :         boundary_info.nodeset_name(4) = "left";
    1936      187640 :         boundary_info.nodeset_name(5) = "front";
    1937             : 
    1938        5310 :         break;
    1939             :       } // end case dim==3
    1940             : 
    1941           0 :     default:
    1942           0 :       libmesh_error_msg("Unknown dimension " << mesh.mesh_dimension());
    1943             :     }
    1944             : 
    1945             :   // Done building the mesh.  Now prepare it for use.
    1946      410857 :   mesh.prepare_for_use ();
    1947      410857 : }
    1948             : 
    1949             : 
    1950             : 
    1951        1204 : void MeshTools::Generation::build_point (UnstructuredMesh & mesh,
    1952             :                                          const ElemType type,
    1953             :                                          const bool gauss_lobatto_grid)
    1954             : {
    1955             :   // This method only makes sense in 0D!
    1956             :   // But we now just turn a non-0D mesh into a 0D mesh
    1957             :   //libmesh_assert_equal_to (mesh.mesh_dimension(), 1);
    1958             : 
    1959        1204 :   build_cube(mesh,
    1960             :              0, 0, 0,
    1961             :              0., 0.,
    1962             :              0., 0.,
    1963             :              0., 0.,
    1964             :              type,
    1965             :              gauss_lobatto_grid);
    1966        1204 : }
    1967             : 
    1968             : 
    1969        5952 : void MeshTools::Generation::build_line (UnstructuredMesh & mesh,
    1970             :                                         const unsigned int nx,
    1971             :                                         const Real xmin, const Real xmax,
    1972             :                                         const ElemType type,
    1973             :                                         const bool gauss_lobatto_grid)
    1974             : {
    1975             :   // This method only makes sense in 1D!
    1976             :   // But we now just turn a non-1D mesh into a 1D mesh
    1977             :   //libmesh_assert_equal_to (mesh.mesh_dimension(), 1);
    1978             : 
    1979        5952 :   build_cube(mesh,
    1980             :              nx, 0, 0,
    1981             :              xmin, xmax,
    1982             :              0., 0.,
    1983             :              0., 0.,
    1984             :              type,
    1985             :              gauss_lobatto_grid);
    1986        5952 : }
    1987             : 
    1988             : 
    1989             : 
    1990       23298 : void MeshTools::Generation::build_square (UnstructuredMesh & mesh,
    1991             :                                           const unsigned int nx,
    1992             :                                           const unsigned int ny,
    1993             :                                           const Real xmin, const Real xmax,
    1994             :                                           const Real ymin, const Real ymax,
    1995             :                                           const ElemType type,
    1996             :                                           const bool gauss_lobatto_grid)
    1997             : {
    1998             :   // This method only makes sense in 2D!
    1999             :   // But we now just turn a non-2D mesh into a 2D mesh
    2000             :   //libmesh_assert_equal_to (mesh.mesh_dimension(), 2);
    2001             : 
    2002             :   // Call the build_cube() member to actually do the work for us.
    2003       23298 :   build_cube (mesh,
    2004             :               nx, ny, 0,
    2005             :               xmin, xmax,
    2006             :               ymin, ymax,
    2007             :               0., 0.,
    2008             :               type,
    2009             :               gauss_lobatto_grid);
    2010       23298 : }
    2011             : 
    2012             : 
    2013             : 
    2014             : 
    2015             : 
    2016             : 
    2017             : 
    2018             : 
    2019             : 
    2020             : #ifndef LIBMESH_ENABLE_AMR
    2021             : void MeshTools::Generation::build_sphere (UnstructuredMesh &,
    2022             :                                           const Real,
    2023             :                                           const unsigned int,
    2024             :                                           const ElemType,
    2025             :                                           const unsigned int,
    2026             :                                           const bool)
    2027             : {
    2028             :   libmesh_error_msg("Building a circle/sphere only works with AMR.");
    2029             : }
    2030             : 
    2031             : #else
    2032             : 
    2033        2281 : void MeshTools::Generation::build_sphere (UnstructuredMesh & mesh,
    2034             :                                           const Real rad,
    2035             :                                           const unsigned int nr,
    2036             :                                           const ElemType type,
    2037             :                                           const unsigned int n_smooth,
    2038             :                                           const bool flat)
    2039             : {
    2040          68 :   libmesh_assert_greater (rad, 0.);
    2041             :   //libmesh_assert_greater (nr, 0); // must refine at least once otherwise will end up with a square/cube
    2042             : 
    2043         204 :   LOG_SCOPE("build_sphere()", "MeshTools::Generation");
    2044             : 
    2045             :   // Clear the mesh and start from scratch, but save the original
    2046             :   // mesh_dimension, since the original intent of this function was to
    2047             :   // allow the geometric entity (line, circle, ball, sphere)
    2048             :   // constructed to be determined by the mesh's dimension.
    2049             :   unsigned char orig_mesh_dimension =
    2050        2281 :     cast_int<unsigned char>(mesh.mesh_dimension());
    2051        2281 :   mesh.clear();
    2052        2281 :   mesh.set_mesh_dimension(orig_mesh_dimension);
    2053             : 
    2054             :   // If mesh.mesh_dimension()==1, it *could* be because the user
    2055             :   // constructed a Mesh without specifying a dimension (since this is
    2056             :   // allowed now) and hence it got the default dimension of 1.  In
    2057             :   // this case, we will try to infer the dimension they *really*
    2058             :   // wanted from the requested ElemType, and if they don't match, go
    2059             :   // with the ElemType.
    2060        2281 :   if (mesh.mesh_dimension() == 1)
    2061             :     {
    2062        2281 :       switch (type)
    2063             :       {
    2064         857 :       case HEX8:
    2065             :       case HEX27:
    2066             :       case TET4:
    2067             :       case TET10:
    2068             :       case TET14:
    2069         857 :         mesh.set_mesh_dimension(3);
    2070          26 :         break;
    2071        1140 :       case TRI3:
    2072             :       case TRI6:
    2073             :       case TRI7:
    2074             :       case QUAD4:
    2075             :       case QUADSHELL4:
    2076             :       case QUAD8:
    2077             :       case QUADSHELL8:
    2078             :       case QUAD9:
    2079             :       case QUADSHELL9:
    2080        1140 :         mesh.set_mesh_dimension(2);
    2081          34 :         break;
    2082         284 :       case EDGE2:
    2083             :       case EDGE3:
    2084             :       case EDGE4:
    2085         284 :         mesh.set_mesh_dimension(1);
    2086           8 :         break;
    2087           0 :       case INVALID_ELEM:
    2088             :         // Just keep the existing dimension
    2089           0 :         break;
    2090           0 :       default:
    2091           0 :         libmesh_error_msg("build_sphere(): Please specify a mesh dimension or a valid ElemType (EDGE{2,3,4}, TRI{3,6,7}, QUAD{4,8,9}, HEX{8,27}, TET{4,10,14})");
    2092             :       }
    2093             :     }
    2094             : 
    2095          68 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
    2096             : 
    2097             :   // Building while distributed is a little more complicated
    2098        2281 :   const bool is_replicated = mesh.is_replicated();
    2099             : 
    2100             :   // Sphere is centered at origin by default
    2101          68 :   const Point cent;
    2102             : 
    2103        4562 :   const Sphere sphere (cent, rad);
    2104             : 
    2105        2281 :   switch (mesh.mesh_dimension())
    2106             :     {
    2107             :       //-----------------------------------------------------------------
    2108             :       // Build a line in one dimension
    2109         284 :     case 1:
    2110             :       {
    2111         284 :         build_line (mesh, 3, -rad, rad, type);
    2112             : 
    2113           8 :         break;
    2114             :       }
    2115             : 
    2116             : 
    2117             : 
    2118             : 
    2119             :       //-----------------------------------------------------------------
    2120             :       // Build a circle or hollow sphere in two dimensions
    2121        1140 :     case 2:
    2122             :       {
    2123             :         // For DistributedMesh, if we don't specify node IDs the Mesh
    2124             :         // will try to pick an appropriate (unique) one for us.  But
    2125             :         // since we are adding these nodes on all processors, we want
    2126             :         // to be sure they have consistent IDs across all processors.
    2127          34 :         unsigned node_id = 0;
    2128             : 
    2129        1140 :         if (flat)
    2130             :           {
    2131          34 :             const Real sqrt_2     = std::sqrt(2.);
    2132        1140 :             const Real rad_2      = .25*rad;
    2133        1140 :             const Real rad_sqrt_2 = rad/sqrt_2;
    2134             : 
    2135             :             // (Temporary) convenient storage for node pointers
    2136        1174 :             std::vector<Node *> nodes(8);
    2137             : 
    2138             :             // Point 0
    2139        1174 :             nodes[0] = mesh.add_point (Point(-rad_2,-rad_2, 0.), node_id++);
    2140             : 
    2141             :             // Point 1
    2142        1174 :             nodes[1] = mesh.add_point (Point( rad_2,-rad_2, 0.), node_id++);
    2143             : 
    2144             :             // Point 2
    2145        1174 :             nodes[2] = mesh.add_point (Point( rad_2, rad_2, 0.), node_id++);
    2146             : 
    2147             :             // Point 3
    2148        1174 :             nodes[3] = mesh.add_point (Point(-rad_2, rad_2, 0.), node_id++);
    2149             : 
    2150             :             // Point 4
    2151        1174 :             nodes[4] = mesh.add_point (Point(-rad_sqrt_2,-rad_sqrt_2, 0.), node_id++);
    2152             : 
    2153             :             // Point 5
    2154        1174 :             nodes[5] = mesh.add_point (Point( rad_sqrt_2,-rad_sqrt_2, 0.), node_id++);
    2155             : 
    2156             :             // Point 6
    2157        1174 :             nodes[6] = mesh.add_point (Point( rad_sqrt_2, rad_sqrt_2, 0.), node_id++);
    2158             : 
    2159             :             // Point 7
    2160        1174 :             nodes[7] = mesh.add_point (Point(-rad_sqrt_2, rad_sqrt_2, 0.), node_id++);
    2161             : 
    2162             :             // Build the elements & set node pointers
    2163             : 
    2164             :             // Element 0
    2165             :             {
    2166        1140 :               Elem * elem0 = mesh.add_elem (Elem::build(QUAD4));
    2167        1140 :               elem0->set_node(0, nodes[0]);
    2168        1140 :               elem0->set_node(1, nodes[1]);
    2169        1140 :               elem0->set_node(2, nodes[2]);
    2170        1140 :               elem0->set_node(3, nodes[3]);
    2171             :             }
    2172             : 
    2173             :             // Element 1
    2174             :             {
    2175        1140 :               Elem * elem1 = mesh.add_elem (Elem::build(QUAD4));
    2176        1140 :               elem1->set_node(0, nodes[4]);
    2177        1140 :               elem1->set_node(1, nodes[0]);
    2178        1140 :               elem1->set_node(2, nodes[3]);
    2179        1140 :               elem1->set_node(3, nodes[7]);
    2180             :             }
    2181             : 
    2182             :             // Element 2
    2183             :             {
    2184        1140 :               Elem * elem2 = mesh.add_elem (Elem::build(QUAD4));
    2185        1140 :               elem2->set_node(0, nodes[4]);
    2186        1140 :               elem2->set_node(1, nodes[5]);
    2187        1140 :               elem2->set_node(2, nodes[1]);
    2188        1140 :               elem2->set_node(3, nodes[0]);
    2189             :             }
    2190             : 
    2191             :             // Element 3
    2192             :             {
    2193        1140 :               Elem * elem3 = mesh.add_elem (Elem::build(QUAD4));
    2194        1140 :               elem3->set_node(0, nodes[1]);
    2195        1140 :               elem3->set_node(1, nodes[5]);
    2196        1140 :               elem3->set_node(2, nodes[6]);
    2197        1140 :               elem3->set_node(3, nodes[2]);
    2198             :             }
    2199             : 
    2200             :             // Element 4
    2201             :             {
    2202        1140 :               Elem * elem4 = mesh.add_elem (Elem::build(QUAD4));
    2203        1140 :               elem4->set_node(0, nodes[3]);
    2204        1140 :               elem4->set_node(1, nodes[2]);
    2205        1140 :               elem4->set_node(2, nodes[6]);
    2206        1140 :               elem4->set_node(3, nodes[7]);
    2207             :             }
    2208             : 
    2209             :           }
    2210             :         else
    2211             :           {
    2212             :             // Create the 12 vertices of a regular unit icosahedron
    2213           0 :             Real t = 0.5 * (1 + std::sqrt(5.0));
    2214           0 :             Real s = rad / std::sqrt(1 + t*t);
    2215           0 :             t *= s;
    2216             : 
    2217           0 :             mesh.add_point (Point(-s,  t,  0), node_id++);
    2218           0 :             mesh.add_point (Point( s,  t,  0), node_id++);
    2219           0 :             mesh.add_point (Point(-s, -t,  0), node_id++);
    2220           0 :             mesh.add_point (Point( s, -t,  0), node_id++);
    2221             : 
    2222           0 :             mesh.add_point (Point( 0, -s,  t), node_id++);
    2223           0 :             mesh.add_point (Point( 0,  s,  t), node_id++);
    2224           0 :             mesh.add_point (Point( 0, -s, -t), node_id++);
    2225           0 :             mesh.add_point (Point( 0,  s, -t), node_id++);
    2226             : 
    2227           0 :             mesh.add_point (Point( t,  0, -s), node_id++);
    2228           0 :             mesh.add_point (Point( t,  0,  s), node_id++);
    2229           0 :             mesh.add_point (Point(-t,  0, -s), node_id++);
    2230           0 :             mesh.add_point (Point(-t,  0,  s), node_id++);
    2231             : 
    2232             :             // Create the 20 triangles of the icosahedron
    2233             :             static const unsigned int idx1 [6] = {11, 5, 1, 7, 10, 11};
    2234             :             static const unsigned int idx2 [6] = {9, 4, 2, 6, 8, 9};
    2235             :             static const unsigned int idx3 [6] = {1, 5, 11, 10, 7, 1};
    2236             : 
    2237           0 :             for (unsigned int i = 0; i < 5; ++i)
    2238             :               {
    2239             :                 // 5 elems around point 0
    2240           0 :                 Elem * new_elem = mesh.add_elem(Elem::build(TRI3));
    2241           0 :                 new_elem->set_node(0, mesh.node_ptr(0));
    2242           0 :                 new_elem->set_node(1, mesh.node_ptr(idx1[i]));
    2243           0 :                 new_elem->set_node(2, mesh.node_ptr(idx1[i+1]));
    2244             : 
    2245             :                 // 5 adjacent elems
    2246           0 :                 new_elem = mesh.add_elem(Elem::build(TRI3));
    2247           0 :                 new_elem->set_node(0, mesh.node_ptr(idx3[i]));
    2248           0 :                 new_elem->set_node(1, mesh.node_ptr(idx3[i+1]));
    2249           0 :                 new_elem->set_node(2, mesh.node_ptr(idx2[i]));
    2250             : 
    2251             :                 // 5 elems around point 3
    2252           0 :                 new_elem = mesh.add_elem(Elem::build(TRI3));
    2253           0 :                 new_elem->set_node(0, mesh.node_ptr(3));
    2254           0 :                 new_elem->set_node(1, mesh.node_ptr(idx2[i]));
    2255           0 :                 new_elem->set_node(2, mesh.node_ptr(idx2[i+1]));
    2256             : 
    2257             :                 // 5 adjacent elems
    2258           0 :                 new_elem = mesh.add_elem(Elem::build(TRI3));
    2259           0 :                 new_elem->set_node(0, mesh.node_ptr(idx2[i+1]));
    2260           0 :                 new_elem->set_node(1, mesh.node_ptr(idx2[i]));
    2261           0 :                 new_elem->set_node(2, mesh.node_ptr(idx3[i+1]));
    2262             :               }
    2263             :           }
    2264             : 
    2265          34 :         break;
    2266             :       } // end case 2
    2267             : 
    2268             : 
    2269             : 
    2270             : 
    2271             : 
    2272             :       //-----------------------------------------------------------------
    2273             :       // Build a sphere in three dimensions
    2274         857 :     case 3:
    2275             :       {
    2276             :         // (Currently) supported types
    2277         857 :         if (!((type == HEX8) || (type == HEX27) || (type == TET4) ||
    2278             :               (type == TET10) || (type == TET14)))
    2279             :           {
    2280           0 :             libmesh_error_msg("Error: Only HEX8/27 and TET4/10/14 are currently supported in 3D.");
    2281             :           }
    2282             : 
    2283             : 
    2284             :         // 3D analog of 2D initial grid:
    2285             :         const Real
    2286         857 :           r_small = 0.25*rad,                      //  0.25 *radius
    2287         857 :           r_med   = (0.125*std::sqrt(2.)+0.5)*rad; // .67677*radius
    2288             : 
    2289             :         // (Temporary) convenient storage for node pointers
    2290         883 :         std::vector<Node *> nodes(16);
    2291             : 
    2292             :         // For DistributedMesh, if we don't specify node IDs the Mesh
    2293             :         // will try to pick an appropriate (unique) one for us.  But
    2294             :         // since we are adding these nodes on all processors, we want
    2295             :         // to be sure they have consistent IDs across all processors.
    2296          26 :         unsigned node_id = 0;
    2297             : 
    2298             :         // Points 0-7 are the initial HEX8
    2299         883 :         nodes[0] = mesh.add_point (Point(-r_small,-r_small, -r_small), node_id++);
    2300         883 :         nodes[1] = mesh.add_point (Point( r_small,-r_small, -r_small), node_id++);
    2301         883 :         nodes[2] = mesh.add_point (Point( r_small, r_small, -r_small), node_id++);
    2302         883 :         nodes[3] = mesh.add_point (Point(-r_small, r_small, -r_small), node_id++);
    2303         883 :         nodes[4] = mesh.add_point (Point(-r_small,-r_small,  r_small), node_id++);
    2304         883 :         nodes[5] = mesh.add_point (Point( r_small,-r_small,  r_small), node_id++);
    2305         883 :         nodes[6] = mesh.add_point (Point( r_small, r_small,  r_small), node_id++);
    2306         883 :         nodes[7] = mesh.add_point (Point(-r_small, r_small,  r_small), node_id++);
    2307             : 
    2308             :         //  Points 8-15 are for the outer hexes, we number them in the same way
    2309         883 :         nodes[8]  = mesh.add_point (Point(-r_med,-r_med, -r_med), node_id++);
    2310         883 :         nodes[9]  = mesh.add_point (Point( r_med,-r_med, -r_med), node_id++);
    2311         883 :         nodes[10] = mesh.add_point (Point( r_med, r_med, -r_med), node_id++);
    2312         883 :         nodes[11] = mesh.add_point (Point(-r_med, r_med, -r_med), node_id++);
    2313         883 :         nodes[12] = mesh.add_point (Point(-r_med,-r_med,  r_med), node_id++);
    2314         883 :         nodes[13] = mesh.add_point (Point( r_med,-r_med,  r_med), node_id++);
    2315         883 :         nodes[14] = mesh.add_point (Point( r_med, r_med,  r_med), node_id++);
    2316         883 :         nodes[15] = mesh.add_point (Point(-r_med, r_med,  r_med), node_id++);
    2317             : 
    2318             :         // Now create the elements and add them to the mesh
    2319             :         // Element 0 - center element
    2320             :         {
    2321         857 :           Elem * elem0 = mesh.add_elem(Elem::build(HEX8));
    2322         857 :           elem0->set_node(0, nodes[0]);
    2323         857 :           elem0->set_node(1, nodes[1]);
    2324         857 :           elem0->set_node(2, nodes[2]);
    2325         857 :           elem0->set_node(3, nodes[3]);
    2326         857 :           elem0->set_node(4, nodes[4]);
    2327         857 :           elem0->set_node(5, nodes[5]);
    2328         857 :           elem0->set_node(6, nodes[6]);
    2329         857 :           elem0->set_node(7, nodes[7]);
    2330             :         }
    2331             : 
    2332             :         // Element 1 - "bottom"
    2333             :         {
    2334         857 :           Elem * elem1 = mesh.add_elem(Elem::build(HEX8));
    2335         857 :           elem1->set_node(0, nodes[8]);
    2336         857 :           elem1->set_node(1, nodes[9]);
    2337         857 :           elem1->set_node(2, nodes[10]);
    2338         857 :           elem1->set_node(3, nodes[11]);
    2339         857 :           elem1->set_node(4, nodes[0]);
    2340         857 :           elem1->set_node(5, nodes[1]);
    2341         857 :           elem1->set_node(6, nodes[2]);
    2342         857 :           elem1->set_node(7, nodes[3]);
    2343             :         }
    2344             : 
    2345             :         // Element 2 - "front"
    2346             :         {
    2347         857 :           Elem * elem2 = mesh.add_elem(Elem::build(HEX8));
    2348         857 :           elem2->set_node(0, nodes[8]);
    2349         857 :           elem2->set_node(1, nodes[9]);
    2350         857 :           elem2->set_node(2, nodes[1]);
    2351         857 :           elem2->set_node(3, nodes[0]);
    2352         857 :           elem2->set_node(4, nodes[12]);
    2353         857 :           elem2->set_node(5, nodes[13]);
    2354         857 :           elem2->set_node(6, nodes[5]);
    2355         857 :           elem2->set_node(7, nodes[4]);
    2356             :         }
    2357             : 
    2358             :         // Element 3 - "right"
    2359             :         {
    2360         857 :           Elem * elem3 = mesh.add_elem(Elem::build(HEX8));
    2361         857 :           elem3->set_node(0, nodes[1]);
    2362         857 :           elem3->set_node(1, nodes[9]);
    2363         857 :           elem3->set_node(2, nodes[10]);
    2364         857 :           elem3->set_node(3, nodes[2]);
    2365         857 :           elem3->set_node(4, nodes[5]);
    2366         857 :           elem3->set_node(5, nodes[13]);
    2367         857 :           elem3->set_node(6, nodes[14]);
    2368         857 :           elem3->set_node(7, nodes[6]);
    2369             :         }
    2370             : 
    2371             :         // Element 4 - "back"
    2372             :         {
    2373         857 :           Elem * elem4 = mesh.add_elem(Elem::build(HEX8));
    2374         857 :           elem4->set_node(0, nodes[3]);
    2375         857 :           elem4->set_node(1, nodes[2]);
    2376         857 :           elem4->set_node(2, nodes[10]);
    2377         857 :           elem4->set_node(3, nodes[11]);
    2378         857 :           elem4->set_node(4, nodes[7]);
    2379         857 :           elem4->set_node(5, nodes[6]);
    2380         857 :           elem4->set_node(6, nodes[14]);
    2381         857 :           elem4->set_node(7, nodes[15]);
    2382             :         }
    2383             : 
    2384             :         // Element 5 - "left"
    2385             :         {
    2386         857 :           Elem * elem5 = mesh.add_elem(Elem::build(HEX8));
    2387         857 :           elem5->set_node(0, nodes[8]);
    2388         857 :           elem5->set_node(1, nodes[0]);
    2389         857 :           elem5->set_node(2, nodes[3]);
    2390         857 :           elem5->set_node(3, nodes[11]);
    2391         857 :           elem5->set_node(4, nodes[12]);
    2392         857 :           elem5->set_node(5, nodes[4]);
    2393         857 :           elem5->set_node(6, nodes[7]);
    2394         857 :           elem5->set_node(7, nodes[15]);
    2395             :         }
    2396             : 
    2397             :         // Element 6 - "top"
    2398             :         {
    2399         857 :           Elem * elem6 = mesh.add_elem(Elem::build(HEX8));
    2400         857 :           elem6->set_node(0, nodes[4]);
    2401         857 :           elem6->set_node(1, nodes[5]);
    2402         857 :           elem6->set_node(2, nodes[6]);
    2403         857 :           elem6->set_node(3, nodes[7]);
    2404         857 :           elem6->set_node(4, nodes[12]);
    2405         857 :           elem6->set_node(5, nodes[13]);
    2406         857 :           elem6->set_node(6, nodes[14]);
    2407         857 :           elem6->set_node(7, nodes[15]);
    2408             :         }
    2409             : 
    2410          26 :         break;
    2411             :       } // end case 3
    2412             : 
    2413           0 :     default:
    2414           0 :       libmesh_error_msg("Unknown dimension " << mesh.mesh_dimension());
    2415             : 
    2416             : 
    2417             : 
    2418             :     } // end switch (dim)
    2419             : 
    2420             :   // Now we have the beginnings of a sphere.
    2421             :   // Add some more elements by doing uniform refinements and
    2422             :   // popping nodes to the boundary.
    2423        4562 :   MeshRefinement mesh_refinement (mesh);
    2424             : 
    2425             :   // For avoiding extraneous element side construction
    2426        2281 :   std::unique_ptr<Elem> side;
    2427             : 
    2428             :   // Loop over the elements, refine, pop nodes to boundary.
    2429        5490 :   for (unsigned int r=0; r<nr; r++)
    2430             :     {
    2431             :       // A DistributedMesh needs a little prep before refinement, and
    2432             :       // may need us to keep track of ghost node movement.
    2433          96 :       std::unordered_set<dof_id_type> moved_ghost_nodes;
    2434        3209 :       if (!is_replicated)
    2435        2196 :         mesh.prepare_for_use();
    2436             : 
    2437        3209 :       mesh_refinement.uniformly_refine(1);
    2438             : 
    2439             :       const bool move_only_boundary_nodes =
    2440        3209 :         mesh.mesh_dimension() != 2 || flat;
    2441             :       MeshTools::Modification::interpolate_surface
    2442        6418 :         (mesh, sphere, /*ids=*/{}, move_only_boundary_nodes);
    2443             :     }
    2444             : 
    2445             :   // A DistributedMesh needs a little prep before flattening
    2446        2281 :   if (!is_replicated)
    2447        1706 :     mesh.prepare_for_use();
    2448             : 
    2449             :   // The mesh now contains a refinement hierarchy due to the refinements
    2450             :   // used to generate the grid.  In order to call other support functions
    2451             :   // like all_tri() and all_second_order, you need a "flat" mesh file (with no
    2452             :   // refinement trees) so
    2453        2281 :   MeshTools::Modification::flatten(mesh);
    2454             : 
    2455             :   // Convert all the tensor product elements to simplices if requested
    2456        2281 :   if ((type == TRI7) || (type == TRI6) || (type == TRI3) ||
    2457         116 :       (type == TET4) || (type == TET10) || (type == TET14))
    2458             :     {
    2459             :       // A DistributedMesh needs a little prep before all_tri()
    2460         497 :       if (is_replicated)
    2461         106 :         mesh.prepare_for_use();
    2462             : 
    2463         497 :       MeshTools::Modification::all_tri(mesh);
    2464             :     }
    2465             : 
    2466             :   // Convert to second-order elements if the user requested it.
    2467        2349 :   if (Elem::build(type)->default_order() != FIRST)
    2468             :     {
    2469        1571 :       if (type == TET14)
    2470           0 :         mesh.all_complete_order();
    2471             :       else
    2472             :         {
    2473             :           // type is second-order, determine if it is the
    2474             :           // "full-ordered" second-order element, or the "serendipity"
    2475             :           // second order element.  Note also that all_second_order
    2476             :           // can't be called once the mesh has been refined.
    2477        1571 :           bool full_ordered = !((type==QUAD8) || (type==HEX20));
    2478        1571 :           mesh.all_second_order(full_ordered);
    2479             :         }
    2480             : 
    2481             :       // And pop to the boundary again...
    2482      176670 :       for (const auto & elem : mesh.active_element_ptr_range())
    2483      612961 :         for (auto s : elem->side_index_range())
    2484      545469 :           if (elem->neighbor_ptr(s) == nullptr)
    2485             :             {
    2486       20319 :               elem->build_side_ptr(side, s);
    2487             : 
    2488             :               // Pop each point to the sphere boundary
    2489      183824 :               for (auto n : side->node_index_range())
    2490      173327 :                 side->point(n) =
    2491      173327 :                   sphere.closest_point(side->point(n));
    2492        1475 :             }
    2493             :     }
    2494             : 
    2495             : 
    2496             :   // The meshes could probably use some smoothing.
    2497        2281 :   if (mesh.mesh_dimension() > 1)
    2498             :     {
    2499        2057 :       LaplaceMeshSmoother smoother(mesh, n_smooth);
    2500        1997 :       smoother.smooth();
    2501             :     }
    2502             : 
    2503             :   // We'll give the whole sphere surface a boundary id of 0
    2504      810930 :   for (const auto & elem : mesh.active_element_ptr_range())
    2505     2343660 :     for (auto s : elem->side_index_range())
    2506     1988314 :       if (!elem->neighbor_ptr(s))
    2507       53135 :         boundary_info.add_side(elem, s, 0);
    2508             : 
    2509             :   // Done building the mesh.  Now prepare it for use.
    2510        2281 :   mesh.prepare_for_use();
    2511        2281 : }
    2512             : 
    2513             : #endif // #ifndef LIBMESH_ENABLE_AMR
    2514             : 
    2515             : 
    2516             : // Meshes the tensor product of a 1D and a 1D-or-2D domain.
    2517         497 : void MeshTools::Generation::build_extrusion (UnstructuredMesh & mesh,
    2518             :                                              const MeshBase & cross_section,
    2519             :                                              const unsigned int nz,
    2520             :                                              RealVectorValue extrusion_vector,
    2521             :                                              QueryElemSubdomainIDBase * elem_subdomain)
    2522             : {
    2523          28 :   LOG_SCOPE("build_extrusion()", "MeshTools::Generation");
    2524             : 
    2525         497 :   if (!cross_section.n_elem())
    2526           0 :     return;
    2527             : 
    2528         497 :   dof_id_type orig_elem = cross_section.n_elem();
    2529         497 :   dof_id_type orig_nodes = cross_section.n_nodes();
    2530             : 
    2531             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    2532         497 :   unique_id_type orig_unique_ids = cross_section.parallel_max_unique_id();
    2533             : #endif
    2534             : 
    2535         497 :   unsigned int order = 1;
    2536             : 
    2537          14 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
    2538          14 :   const BoundaryInfo & cross_section_boundary_info = cross_section.get_boundary_info();
    2539             : 
    2540             :   // Copy name maps from old to new boundary.  We won't copy the whole
    2541             :   // BoundaryInfo because that copies bc ids too, and we need to set
    2542             :   // those more carefully.
    2543          14 :   boundary_info.set_sideset_name_map() = cross_section_boundary_info.get_sideset_name_map();
    2544          14 :   boundary_info.set_nodeset_name_map() = cross_section_boundary_info.get_nodeset_name_map();
    2545          14 :   boundary_info.set_edgeset_name_map() = cross_section_boundary_info.get_edgeset_name_map();
    2546             : 
    2547             :   // If cross_section is distributed, so is its extrusion
    2548         497 :   if (!cross_section.is_serial())
    2549         384 :     mesh.delete_remote_elements();
    2550             : 
    2551             :   // We know a priori how many elements we'll need
    2552         497 :   mesh.reserve_elem(nz*orig_elem);
    2553             : 
    2554             :   // For straightforward meshes we need one or two additional layers per
    2555             :   // element.
    2556        1843 :   if (cross_section.elements_begin() != cross_section.elements_end() &&
    2557        1225 :       (*cross_section.elements_begin())->default_order() == SECOND)
    2558         111 :     order = 2;
    2559         497 :   mesh.comm().max(order);
    2560             : 
    2561         497 :   mesh.reserve_nodes((order*nz+1)*orig_nodes);
    2562             : 
    2563             :   // Container to catch the boundary IDs handed back by the BoundaryInfo object
    2564          28 :   std::vector<boundary_id_type> ids_to_copy;
    2565             : 
    2566       15888 :   for (const auto & node : cross_section.node_ptr_range())
    2567             :     {
    2568       43092 :       for (unsigned int k=0; k != order*nz+1; ++k)
    2569             :         {
    2570       35296 :           const dof_id_type new_node_id = node->id() + k * orig_nodes;
    2571       35296 :           Node * my_node = mesh.query_node_ptr(new_node_id);
    2572       35296 :           if (!my_node)
    2573             :             {
    2574             :               std::unique_ptr<Node> new_node = Node::build
    2575       36814 :                 (*node + (extrusion_vector * k / nz / order),
    2576        1518 :                  new_node_id);
    2577       35296 :               new_node->processor_id() = node->processor_id();
    2578             : 
    2579             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    2580             :               // Let's give the base of the extruded mesh the same
    2581             :               // unique_ids as the source mesh, in case anyone finds that
    2582             :               // a useful map to preserve.
    2583       35296 :               const unique_id_type uid = (k == 0) ?
    2584         684 :                 node->unique_id() :
    2585       27500 :                 orig_unique_ids + (k-1)*(orig_nodes + orig_elem) + node->id();
    2586             : 
    2587        1518 :               new_node->set_unique_id(uid);
    2588             : #endif
    2589             : 
    2590       35296 :               cross_section_boundary_info.boundary_ids(node, ids_to_copy);
    2591       35296 :               boundary_info.add_node(new_node.get(), ids_to_copy);
    2592             : 
    2593       38332 :               mesh.add_node(std::move(new_node));
    2594       32260 :             }
    2595             :         }
    2596         469 :     }
    2597             : 
    2598             :   const std::set<boundary_id_type> & side_ids =
    2599          14 :     cross_section_boundary_info.get_side_boundary_ids();
    2600             : 
    2601          14 :   boundary_id_type next_side_id = side_ids.empty() ?
    2602         497 :     0 : cast_int<boundary_id_type>(*side_ids.rbegin() + 1);
    2603             : 
    2604             :   // side_ids may not include ids from remote elements, in which case
    2605             :   // some processors may have underestimated the next_side_id; let's
    2606             :   // fix that.
    2607         497 :   cross_section.comm().max(next_side_id);
    2608             : 
    2609        6566 :   for (const auto & elem : cross_section.element_ptr_range())
    2610             :     {
    2611        2923 :       const ElemType etype = elem->type();
    2612             : 
    2613             :       // build_extrusion currently only works on coarse meshes
    2614         130 :       libmesh_assert (!elem->parent());
    2615             : 
    2616       10181 :       for (unsigned int k=0; k != nz; ++k)
    2617             :         {
    2618        6982 :           std::unique_ptr<Elem> new_elem;
    2619        7258 :           switch (etype)
    2620             :             {
    2621           0 :             case EDGE2:
    2622             :               {
    2623           0 :                 new_elem = Elem::build(QUAD4);
    2624           0 :                 new_elem->set_node(0, mesh.node_ptr(elem->node_ptr(0)->id() + (k * orig_nodes)));
    2625           0 :                 new_elem->set_node(1, mesh.node_ptr(elem->node_ptr(1)->id() + (k * orig_nodes)));
    2626           0 :                 new_elem->set_node(2, mesh.node_ptr(elem->node_ptr(1)->id() + ((k+1) * orig_nodes)));
    2627           0 :                 new_elem->set_node(3, mesh.node_ptr(elem->node_ptr(0)->id() + ((k+1) * orig_nodes)));
    2628             : 
    2629           0 :                 if (elem->neighbor_ptr(0) == remote_elem)
    2630           0 :                   new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
    2631           0 :                 if (elem->neighbor_ptr(1) == remote_elem)
    2632           0 :                   new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
    2633             : 
    2634           0 :                 break;
    2635             :               }
    2636           0 :             case EDGE3:
    2637             :               {
    2638           0 :                 new_elem = Elem::build(QUAD9);
    2639           0 :                 new_elem->set_node(0, mesh.node_ptr(elem->node_ptr(0)->id() + (2*k * orig_nodes)));
    2640           0 :                 new_elem->set_node(1, mesh.node_ptr(elem->node_ptr(1)->id() + (2*k * orig_nodes)));
    2641           0 :                 new_elem->set_node(2, mesh.node_ptr(elem->node_ptr(1)->id() + ((2*k+2) * orig_nodes)));
    2642           0 :                 new_elem->set_node(3, mesh.node_ptr(elem->node_ptr(0)->id() + ((2*k+2) * orig_nodes)));
    2643           0 :                 new_elem->set_node(4, mesh.node_ptr(elem->node_ptr(2)->id() + (2*k * orig_nodes)));
    2644           0 :                 new_elem->set_node(5, mesh.node_ptr(elem->node_ptr(1)->id() + ((2*k+1) * orig_nodes)));
    2645           0 :                 new_elem->set_node(6, mesh.node_ptr(elem->node_ptr(2)->id() + ((2*k+2) * orig_nodes)));
    2646           0 :                 new_elem->set_node(7, mesh.node_ptr(elem->node_ptr(0)->id() + ((2*k+1) * orig_nodes)));
    2647           0 :                 new_elem->set_node(8, mesh.node_ptr(elem->node_ptr(2)->id() + ((2*k+1) * orig_nodes)));
    2648             : 
    2649           0 :                 if (elem->neighbor_ptr(0) == remote_elem)
    2650           0 :                   new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
    2651           0 :                 if (elem->neighbor_ptr(1) == remote_elem)
    2652           0 :                   new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
    2653             : 
    2654           0 :                 break;
    2655             :               }
    2656         860 :             case TRI3:
    2657             :               {
    2658        1624 :                 new_elem = Elem::build(PRISM6);
    2659         908 :                 new_elem->set_node(0, mesh.node_ptr(elem->node_ptr(0)->id() + (k * orig_nodes)));
    2660         908 :                 new_elem->set_node(1, mesh.node_ptr(elem->node_ptr(1)->id() + (k * orig_nodes)));
    2661         908 :                 new_elem->set_node(2, mesh.node_ptr(elem->node_ptr(2)->id() + (k * orig_nodes)));
    2662         908 :                 new_elem->set_node(3, mesh.node_ptr(elem->node_ptr(0)->id() + ((k+1) * orig_nodes)));
    2663         908 :                 new_elem->set_node(4, mesh.node_ptr(elem->node_ptr(1)->id() + ((k+1) * orig_nodes)));
    2664         908 :                 new_elem->set_node(5, mesh.node_ptr(elem->node_ptr(2)->id() + ((k+1) * orig_nodes)));
    2665             : 
    2666         908 :                 if (elem->neighbor_ptr(0) == remote_elem)
    2667           0 :                   new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
    2668         908 :                 if (elem->neighbor_ptr(1) == remote_elem)
    2669           0 :                   new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
    2670         908 :                 if (elem->neighbor_ptr(2) == remote_elem)
    2671           0 :                   new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
    2672             : 
    2673          48 :                 break;
    2674             :               }
    2675           0 :             case TRI6:
    2676             :               {
    2677           0 :                 new_elem = Elem::build(PRISM18);
    2678           0 :                 new_elem->set_node(0, mesh.node_ptr(elem->node_ptr(0)->id() + (2*k * orig_nodes)));
    2679           0 :                 new_elem->set_node(1, mesh.node_ptr(elem->node_ptr(1)->id() + (2*k * orig_nodes)));
    2680           0 :                 new_elem->set_node(2, mesh.node_ptr(elem->node_ptr(2)->id() + (2*k * orig_nodes)));
    2681           0 :                 new_elem->set_node(3, mesh.node_ptr(elem->node_ptr(0)->id() + ((2*k+2) * orig_nodes)));
    2682           0 :                 new_elem->set_node(4, mesh.node_ptr(elem->node_ptr(1)->id() + ((2*k+2) * orig_nodes)));
    2683           0 :                 new_elem->set_node(5, mesh.node_ptr(elem->node_ptr(2)->id() + ((2*k+2) * orig_nodes)));
    2684           0 :                 new_elem->set_node(6, mesh.node_ptr(elem->node_ptr(3)->id() + (2*k * orig_nodes)));
    2685           0 :                 new_elem->set_node(7, mesh.node_ptr(elem->node_ptr(4)->id() + (2*k * orig_nodes)));
    2686           0 :                 new_elem->set_node(8, mesh.node_ptr(elem->node_ptr(5)->id() + (2*k * orig_nodes)));
    2687           0 :                 new_elem->set_node(9, mesh.node_ptr(elem->node_ptr(0)->id() + ((2*k+1) * orig_nodes)));
    2688           0 :                 new_elem->set_node(10, mesh.node_ptr(elem->node_ptr(1)->id() + ((2*k+1) * orig_nodes)));
    2689           0 :                 new_elem->set_node(11, mesh.node_ptr(elem->node_ptr(2)->id() + ((2*k+1) * orig_nodes)));
    2690           0 :                 new_elem->set_node(12, mesh.node_ptr(elem->node_ptr(3)->id() + ((2*k+2) * orig_nodes)));
    2691           0 :                 new_elem->set_node(13, mesh.node_ptr(elem->node_ptr(4)->id() + ((2*k+2) * orig_nodes)));
    2692           0 :                 new_elem->set_node(14, mesh.node_ptr(elem->node_ptr(5)->id() + ((2*k+2) * orig_nodes)));
    2693           0 :                 new_elem->set_node(15, mesh.node_ptr(elem->node_ptr(3)->id() + ((2*k+1) * orig_nodes)));
    2694           0 :                 new_elem->set_node(16, mesh.node_ptr(elem->node_ptr(4)->id() + ((2*k+1) * orig_nodes)));
    2695           0 :                 new_elem->set_node(17, mesh.node_ptr(elem->node_ptr(5)->id() + ((2*k+1) * orig_nodes)));
    2696             : 
    2697           0 :                 if (elem->neighbor_ptr(0) == remote_elem)
    2698           0 :                   new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
    2699           0 :                 if (elem->neighbor_ptr(1) == remote_elem)
    2700           0 :                   new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
    2701           0 :                 if (elem->neighbor_ptr(2) == remote_elem)
    2702           0 :                   new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
    2703             : 
    2704           0 :                 break;
    2705             :               }
    2706           0 :             case TRI7:
    2707             :               {
    2708           0 :                 new_elem = Elem::build(PRISM21);
    2709           0 :                 new_elem->set_node(0, mesh.node_ptr(elem->node_ptr(0)->id() + (2*k * orig_nodes)));
    2710           0 :                 new_elem->set_node(1, mesh.node_ptr(elem->node_ptr(1)->id() + (2*k * orig_nodes)));
    2711           0 :                 new_elem->set_node(2, mesh.node_ptr(elem->node_ptr(2)->id() + (2*k * orig_nodes)));
    2712           0 :                 new_elem->set_node(3, mesh.node_ptr(elem->node_ptr(0)->id() + ((2*k+2) * orig_nodes)));
    2713           0 :                 new_elem->set_node(4, mesh.node_ptr(elem->node_ptr(1)->id() + ((2*k+2) * orig_nodes)));
    2714           0 :                 new_elem->set_node(5, mesh.node_ptr(elem->node_ptr(2)->id() + ((2*k+2) * orig_nodes)));
    2715           0 :                 new_elem->set_node(6, mesh.node_ptr(elem->node_ptr(3)->id() + (2*k * orig_nodes)));
    2716           0 :                 new_elem->set_node(7, mesh.node_ptr(elem->node_ptr(4)->id() + (2*k * orig_nodes)));
    2717           0 :                 new_elem->set_node(8, mesh.node_ptr(elem->node_ptr(5)->id() + (2*k * orig_nodes)));
    2718           0 :                 new_elem->set_node(9, mesh.node_ptr(elem->node_ptr(0)->id() + ((2*k+1) * orig_nodes)));
    2719           0 :                 new_elem->set_node(10, mesh.node_ptr(elem->node_ptr(1)->id() + ((2*k+1) * orig_nodes)));
    2720           0 :                 new_elem->set_node(11, mesh.node_ptr(elem->node_ptr(2)->id() + ((2*k+1) * orig_nodes)));
    2721           0 :                 new_elem->set_node(12, mesh.node_ptr(elem->node_ptr(3)->id() + ((2*k+2) * orig_nodes)));
    2722           0 :                 new_elem->set_node(13, mesh.node_ptr(elem->node_ptr(4)->id() + ((2*k+2) * orig_nodes)));
    2723           0 :                 new_elem->set_node(14, mesh.node_ptr(elem->node_ptr(5)->id() + ((2*k+2) * orig_nodes)));
    2724           0 :                 new_elem->set_node(15, mesh.node_ptr(elem->node_ptr(3)->id() + ((2*k+1) * orig_nodes)));
    2725           0 :                 new_elem->set_node(16, mesh.node_ptr(elem->node_ptr(4)->id() + ((2*k+1) * orig_nodes)));
    2726           0 :                 new_elem->set_node(17, mesh.node_ptr(elem->node_ptr(5)->id() + ((2*k+1) * orig_nodes)));
    2727             : 
    2728           0 :                 new_elem->set_node(18, mesh.node_ptr(elem->node_ptr(6)->id() + (2*k * orig_nodes)));
    2729           0 :                 new_elem->set_node(19, mesh.node_ptr(elem->node_ptr(6)->id() + ((2*k+2) * orig_nodes)));
    2730           0 :                 new_elem->set_node(20, mesh.node_ptr(elem->node_ptr(6)->id() + ((2*k+1) * orig_nodes)));
    2731             : 
    2732           0 :                 if (elem->neighbor_ptr(0) == remote_elem)
    2733           0 :                   new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
    2734           0 :                 if (elem->neighbor_ptr(1) == remote_elem)
    2735           0 :                   new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
    2736           0 :                 if (elem->neighbor_ptr(2) == remote_elem)
    2737           0 :                   new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
    2738             : 
    2739           0 :                 break;
    2740             :               }
    2741        4544 :             case QUAD4:
    2742             :               {
    2743        8832 :                 new_elem = Elem::build(HEX8);
    2744        4672 :                 new_elem->set_node(0, mesh.node_ptr(elem->node_ptr(0)->id() + (k * orig_nodes)));
    2745        4672 :                 new_elem->set_node(1, mesh.node_ptr(elem->node_ptr(1)->id() + (k * orig_nodes)));
    2746        4672 :                 new_elem->set_node(2, mesh.node_ptr(elem->node_ptr(2)->id() + (k * orig_nodes)));
    2747        4672 :                 new_elem->set_node(3, mesh.node_ptr(elem->node_ptr(3)->id() + (k * orig_nodes)));
    2748        4672 :                 new_elem->set_node(4, mesh.node_ptr(elem->node_ptr(0)->id() + ((k+1) * orig_nodes)));
    2749        4672 :                 new_elem->set_node(5, mesh.node_ptr(elem->node_ptr(1)->id() + ((k+1) * orig_nodes)));
    2750        4672 :                 new_elem->set_node(6, mesh.node_ptr(elem->node_ptr(2)->id() + ((k+1) * orig_nodes)));
    2751        4672 :                 new_elem->set_node(7, mesh.node_ptr(elem->node_ptr(3)->id() + ((k+1) * orig_nodes)));
    2752             : 
    2753        4672 :                 if (elem->neighbor_ptr(0) == remote_elem)
    2754           0 :                   new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
    2755        4672 :                 if (elem->neighbor_ptr(1) == remote_elem)
    2756           0 :                   new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
    2757        4672 :                 if (elem->neighbor_ptr(2) == remote_elem)
    2758           0 :                   new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
    2759        4672 :                 if (elem->neighbor_ptr(3) == remote_elem)
    2760           0 :                   new_elem->set_neighbor(4, const_cast<RemoteElem *>(remote_elem));
    2761             : 
    2762         128 :                 break;
    2763             :               }
    2764        1854 :             case QUAD9:
    2765             :               {
    2766        3508 :                 new_elem = Elem::build(HEX27);
    2767        1954 :                 new_elem->set_node(0, mesh.node_ptr(elem->node_ptr(0)->id() + (2*k * orig_nodes)));
    2768        1954 :                 new_elem->set_node(1, mesh.node_ptr(elem->node_ptr(1)->id() + (2*k * orig_nodes)));
    2769        1954 :                 new_elem->set_node(2, mesh.node_ptr(elem->node_ptr(2)->id() + (2*k * orig_nodes)));
    2770        1954 :                 new_elem->set_node(3, mesh.node_ptr(elem->node_ptr(3)->id() + (2*k * orig_nodes)));
    2771        1954 :                 new_elem->set_node(4, mesh.node_ptr(elem->node_ptr(0)->id() + ((2*k+2) * orig_nodes)));
    2772        1954 :                 new_elem->set_node(5, mesh.node_ptr(elem->node_ptr(1)->id() + ((2*k+2) * orig_nodes)));
    2773        1954 :                 new_elem->set_node(6, mesh.node_ptr(elem->node_ptr(2)->id() + ((2*k+2) * orig_nodes)));
    2774        1954 :                 new_elem->set_node(7, mesh.node_ptr(elem->node_ptr(3)->id() + ((2*k+2) * orig_nodes)));
    2775        1954 :                 new_elem->set_node(8, mesh.node_ptr(elem->node_ptr(4)->id() + (2*k * orig_nodes)));
    2776        1954 :                 new_elem->set_node(9, mesh.node_ptr(elem->node_ptr(5)->id() + (2*k * orig_nodes)));
    2777        1954 :                 new_elem->set_node(10, mesh.node_ptr(elem->node_ptr(6)->id() + (2*k * orig_nodes)));
    2778        1954 :                 new_elem->set_node(11, mesh.node_ptr(elem->node_ptr(7)->id() + (2*k * orig_nodes)));
    2779        1954 :                 new_elem->set_node(12, mesh.node_ptr(elem->node_ptr(0)->id() + ((2*k+1) * orig_nodes)));
    2780        1954 :                 new_elem->set_node(13, mesh.node_ptr(elem->node_ptr(1)->id() + ((2*k+1) * orig_nodes)));
    2781        1954 :                 new_elem->set_node(14, mesh.node_ptr(elem->node_ptr(2)->id() + ((2*k+1) * orig_nodes)));
    2782        1954 :                 new_elem->set_node(15, mesh.node_ptr(elem->node_ptr(3)->id() + ((2*k+1) * orig_nodes)));
    2783        1954 :                 new_elem->set_node(16, mesh.node_ptr(elem->node_ptr(4)->id() + ((2*k+2) * orig_nodes)));
    2784        1954 :                 new_elem->set_node(17, mesh.node_ptr(elem->node_ptr(5)->id() + ((2*k+2) * orig_nodes)));
    2785        1954 :                 new_elem->set_node(18, mesh.node_ptr(elem->node_ptr(6)->id() + ((2*k+2) * orig_nodes)));
    2786        1954 :                 new_elem->set_node(19, mesh.node_ptr(elem->node_ptr(7)->id() + ((2*k+2) * orig_nodes)));
    2787        1954 :                 new_elem->set_node(20, mesh.node_ptr(elem->node_ptr(8)->id() + (2*k * orig_nodes)));
    2788        1954 :                 new_elem->set_node(21, mesh.node_ptr(elem->node_ptr(4)->id() + ((2*k+1) * orig_nodes)));
    2789        1954 :                 new_elem->set_node(22, mesh.node_ptr(elem->node_ptr(5)->id() + ((2*k+1) * orig_nodes)));
    2790        1954 :                 new_elem->set_node(23, mesh.node_ptr(elem->node_ptr(6)->id() + ((2*k+1) * orig_nodes)));
    2791        1954 :                 new_elem->set_node(24, mesh.node_ptr(elem->node_ptr(7)->id() + ((2*k+1) * orig_nodes)));
    2792        1954 :                 new_elem->set_node(25, mesh.node_ptr(elem->node_ptr(8)->id() + ((2*k+2) * orig_nodes)));
    2793        1954 :                 new_elem->set_node(26, mesh.node_ptr(elem->node_ptr(8)->id() + ((2*k+1) * orig_nodes)));
    2794             : 
    2795        1954 :                 if (elem->neighbor_ptr(0) == remote_elem)
    2796           0 :                   new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
    2797        1954 :                 if (elem->neighbor_ptr(1) == remote_elem)
    2798           0 :                   new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
    2799        1954 :                 if (elem->neighbor_ptr(2) == remote_elem)
    2800           0 :                   new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
    2801        1954 :                 if (elem->neighbor_ptr(3) == remote_elem)
    2802           0 :                   new_elem->set_neighbor(4, const_cast<RemoteElem *>(remote_elem));
    2803             : 
    2804         100 :                 break;
    2805             :               }
    2806           0 :             default:
    2807             :               {
    2808           0 :                 libmesh_not_implemented();
    2809             :                 break;
    2810             :               }
    2811             :             }
    2812             : 
    2813        7258 :           new_elem->set_id(elem->id() + (k * orig_elem));
    2814        7258 :           new_elem->processor_id() = elem->processor_id();
    2815             : 
    2816             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    2817             :           // Let's give the base of the extruded mesh the same
    2818             :           // unique_ids as the source mesh, in case anyone finds that
    2819             :           // a useful map to preserve.
    2820        7258 :           const unique_id_type uid = (k == 0) ?
    2821         260 :             elem->unique_id() :
    2822        4335 :             orig_unique_ids + (k-1)*(orig_nodes + orig_elem) + orig_nodes + elem->id();
    2823             : 
    2824         276 :           new_elem->set_unique_id(uid);
    2825             : #endif
    2826             : 
    2827        7258 :           if (!elem_subdomain)
    2828             :             // maintain the subdomain_id
    2829        2714 :             new_elem->subdomain_id() = elem->subdomain_id();
    2830             :           else
    2831             :             // Allow the user to choose new subdomain_ids
    2832        4544 :             new_elem->subdomain_id() = elem_subdomain->get_subdomain_for_layer(elem, k);
    2833             : 
    2834        7534 :           Elem * added_elem = mesh.add_elem(std::move(new_elem));
    2835             : 
    2836             :           // Copy any old boundary ids on all sides
    2837       35706 :           for (auto s : elem->side_index_range())
    2838             :             {
    2839       28172 :               cross_section_boundary_info.boundary_ids(elem, s, ids_to_copy);
    2840             : 
    2841       28172 :               if (added_elem->dim() == 3)
    2842             :                 {
    2843             :                   // For 2D->3D extrusion, we give the boundary IDs
    2844             :                   // for side s on the old element to side s+1 on the
    2845             :                   // new element.  This is just a happy coincidence as
    2846             :                   // far as I can tell...
    2847       28172 :                   boundary_info.add_side(added_elem,
    2848       27116 :                                          cast_int<unsigned short>(s+1),
    2849             :                                          ids_to_copy);
    2850             :                 }
    2851             :               else
    2852             :                 {
    2853             :                   // For 1D->2D extrusion, the boundary IDs map as:
    2854             :                   // Old elem -> New elem
    2855             :                   // 0        -> 3
    2856             :                   // 1        -> 1
    2857           0 :                   libmesh_assert_less(s, 2);
    2858           0 :                   const unsigned short sidemap[2] = {3, 1};
    2859           0 :                   boundary_info.add_side(added_elem, sidemap[s], ids_to_copy);
    2860             :                 }
    2861             :             }
    2862             : 
    2863             :           // Give new boundary ids to bottom and top
    2864        7258 :           if (k == 0)
    2865        2923 :             boundary_info.add_side(added_elem, 0, next_side_id);
    2866        7258 :           if (k == nz-1)
    2867             :             {
    2868             :               // For 2D->3D extrusion, the "top" ID is 1+the original
    2869             :               // element's number of sides.  For 1D->2D extrusion, the
    2870             :               // "top" ID is side 2.
    2871        2923 :               const unsigned short top_id = added_elem->dim() == 3 ?
    2872        2923 :                 cast_int<unsigned short>(elem->n_sides()+1) : 2;
    2873             :               boundary_info.add_side
    2874        2923 :                 (added_elem, top_id,
    2875        2923 :                  cast_int<boundary_id_type>(next_side_id+1));
    2876             :             }
    2877        6706 :         }
    2878         469 :     }
    2879             : 
    2880             :   // Done building the mesh.  Now prepare it for use.
    2881         497 :   mesh.prepare_for_use();
    2882             : }
    2883             : 
    2884             : 
    2885             : 
    2886             : 
    2887             : #if defined(LIBMESH_HAVE_TRIANGLE) && LIBMESH_DIM > 1
    2888             : 
    2889             : // Triangulates a 2D rectangular region with or without holes
    2890           0 : void MeshTools::Generation::build_delaunay_square(UnstructuredMesh & mesh,
    2891             :                                                   const unsigned int nx, // num. of elements in x-dir
    2892             :                                                   const unsigned int ny, // num. of elements in y-dir
    2893             :                                                   const Real xmin, const Real xmax,
    2894             :                                                   const Real ymin, const Real ymax,
    2895             :                                                   const ElemType type,
    2896             :                                                   const std::vector<TriangleInterface::Hole*> * holes)
    2897             : {
    2898             :   // Check for reasonable size
    2899           0 :   libmesh_assert_greater_equal (nx, 1); // need at least 1 element in x-direction
    2900           0 :   libmesh_assert_greater_equal (ny, 1); // need at least 1 element in y-direction
    2901           0 :   libmesh_assert_less (xmin, xmax);
    2902           0 :   libmesh_assert_less (ymin, ymax);
    2903             : 
    2904             :   // Clear out any data which may have been in the Mesh
    2905           0 :   mesh.clear();
    2906             : 
    2907           0 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
    2908             : 
    2909             :   // Make sure the new Mesh will be 2D
    2910           0 :   mesh.set_mesh_dimension(2);
    2911             : 
    2912             :   // The x and y spacing between boundary points
    2913           0 :   const Real delta_x = (xmax-xmin) / static_cast<Real>(nx);
    2914           0 :   const Real delta_y = (ymax-ymin) / static_cast<Real>(ny);
    2915             : 
    2916             :   // Bottom
    2917           0 :   for (unsigned int p=0; p<=nx; ++p)
    2918           0 :     mesh.add_point(Point(xmin + p*delta_x, ymin));
    2919             : 
    2920             :   // Right side
    2921           0 :   for (unsigned int p=1; p<ny; ++p)
    2922           0 :     mesh.add_point(Point(xmax, ymin + p*delta_y));
    2923             : 
    2924             :   // Top
    2925           0 :   for (unsigned int p=0; p<=nx; ++p)
    2926           0 :     mesh.add_point(Point(xmax - p*delta_x, ymax));
    2927             : 
    2928             :   // Left side
    2929           0 :   for (unsigned int p=1; p<ny; ++p)
    2930           0 :     mesh.add_point(Point(xmin,  ymax - p*delta_y));
    2931             : 
    2932             :   // Be sure we added as many points as we thought we did
    2933           0 :   libmesh_assert_equal_to (mesh.n_nodes(), 2*(nx+ny));
    2934             : 
    2935             :   // Construct the Triangle Interface object
    2936           0 :   TriangleInterface t(mesh);
    2937             : 
    2938             :   // Set custom variables for the triangulation
    2939           0 :   t.desired_area()       = 0.5 * (xmax-xmin)*(ymax-ymin) / static_cast<Real>(nx*ny);
    2940           0 :   t.triangulation_type() = TriangleInterface::PSLG;
    2941           0 :   t.elem_type()          = type;
    2942             : 
    2943           0 :   if (holes != nullptr)
    2944           0 :     t.attach_hole_list(holes);
    2945             : 
    2946             :   // Triangulate!
    2947           0 :   t.triangulate();
    2948             : 
    2949             :   // For avoiding extraneous side element construction
    2950           0 :   std::unique_ptr<const Elem> side;
    2951             : 
    2952             :   // The mesh is now generated, but we still need to mark the boundaries
    2953             :   // to be consistent with the other build_square routines.  Note that all
    2954             :   // hole boundary elements get the same ID, 4.
    2955           0 :   for (auto & elem : mesh.element_ptr_range())
    2956           0 :     for (auto s : elem->side_index_range())
    2957           0 :       if (elem->neighbor_ptr(s) == nullptr)
    2958             :         {
    2959           0 :           elem->build_side_ptr(side, s);
    2960             : 
    2961             :           // Check the location of the side's midpoint.  Since
    2962             :           // the square has straight sides, the midpoint is not
    2963             :           // on the corner and thus it is uniquely on one of the
    2964             :           // sides.
    2965           0 :           Point side_midpoint= 0.5f*( side->point(0) + side->point(1) );
    2966             : 
    2967             :           // The boundary ids are set following the same convention as Quad4 sides
    2968             :           // bottom = 0
    2969             :           // right  = 1
    2970             :           // top = 2
    2971             :           // left = 3
    2972             :           // hole = 4
    2973           0 :           boundary_id_type bc_id=4;
    2974             : 
    2975             :           // bottom
    2976           0 :           if      (std::fabs(side_midpoint(1) - ymin) < TOLERANCE)
    2977           0 :             bc_id=0;
    2978             : 
    2979             :           // right
    2980           0 :           else if (std::fabs(side_midpoint(0) - xmax) < TOLERANCE)
    2981           0 :             bc_id=1;
    2982             : 
    2983             :           // top
    2984           0 :           else if (std::fabs(side_midpoint(1) - ymax) < TOLERANCE)
    2985           0 :             bc_id=2;
    2986             : 
    2987             :           // left
    2988           0 :           else if (std::fabs(side_midpoint(0) - xmin) < TOLERANCE)
    2989           0 :             bc_id=3;
    2990             : 
    2991             :           // If the point is not on any of the external boundaries, it
    2992             :           // is on one of the holes....
    2993             : 
    2994             :           // Finally, add this element's information to the boundary info object.
    2995           0 :           boundary_info.add_side(elem->id(), s, bc_id);
    2996             :         }
    2997             : 
    2998           0 : } // end build_delaunay_square
    2999             : 
    3000             : #endif // LIBMESH_HAVE_TRIANGLE && LIBMESH_DIM > 1
    3001             : 
    3002             : 
    3003         662 : void MeshTools::Generation::surface_octahedron
    3004             :   (UnstructuredMesh & mesh,
    3005             :    Real xmin, Real xmax,
    3006             :    Real ymin, Real ymax,
    3007             :    Real zmin, Real zmax,
    3008             :    bool flip_tris)
    3009             : {
    3010         662 :   const Real xavg = (xmin + xmax)/2;
    3011         662 :   const Real yavg = (ymin + ymax)/2;
    3012         662 :   const Real zavg = (zmin + zmax)/2;
    3013         682 :   mesh.add_point(Point(xavg,yavg,zmin), 0);
    3014         682 :   mesh.add_point(Point(xmax,yavg,zavg), 1);
    3015         682 :   mesh.add_point(Point(xavg,ymax,zavg), 2);
    3016         682 :   mesh.add_point(Point(xmin,yavg,zavg), 3);
    3017         682 :   mesh.add_point(Point(xavg,ymin,zavg), 4);
    3018         682 :   mesh.add_point(Point(xavg,yavg,zmax), 5);
    3019             : 
    3020       28896 :   auto add_tri = [&mesh, flip_tris](std::array<dof_id_type,3> nodes)
    3021             :   {
    3022        5296 :     auto elem = mesh.add_elem(Elem::build(TRI3));
    3023        5296 :     elem->set_node(0, mesh.node_ptr(nodes[0]));
    3024        5296 :     elem->set_node(1, mesh.node_ptr(nodes[1]));
    3025        5296 :     elem->set_node(2, mesh.node_ptr(nodes[2]));
    3026        5296 :     if (flip_tris)
    3027        1168 :       elem->flip(&mesh.get_boundary_info());
    3028        5316 :   };
    3029             : 
    3030         662 :   add_tri({0,2,1});
    3031         662 :   add_tri({0,3,2});
    3032         662 :   add_tri({0,4,3});
    3033         662 :   add_tri({0,1,4});
    3034         662 :   add_tri({5,4,1});
    3035         662 :   add_tri({5,3,4});
    3036         662 :   add_tri({5,2,3});
    3037         662 :   add_tri({5,1,2});
    3038             : 
    3039         662 :   mesh.prepare_for_use();
    3040         662 : }
    3041             : 
    3042             : 
    3043             : } // namespace libMesh

Generated by: LCOV version 1.14