LCOV - code coverage report
Current view: top level - src/mesh - mesh_generation.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4624 (b6265e) with base 1fb77f Lines: 1175 1451 81.0 %
Date: 2026-11-03 16:49:57 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     1617406 : unsigned int idx(const ElemType type,
      83             :                  const unsigned int nx,
      84             :                  const unsigned int i,
      85             :                  const unsigned int j)
      86             : {
      87     1165292 :   switch(type)
      88             :     {
      89      328248 :     case INVALID_ELEM:
      90             :     case QUAD4:
      91             :     case QUADSHELL4:
      92             :     case TRI3:
      93             :     case TRISHELL3:
      94             :       {
      95      328248 :         return i + j*(nx+1);
      96             :       }
      97             : 
      98     1289158 :     case QUAD8:
      99             :     case QUADSHELL8:
     100             :     case QUAD9:
     101             :     case QUADSHELL9:
     102             :     case TRI6:
     103             :     case TRI7:
     104             :       {
     105     1289158 :         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     4297350 : 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     3116486 :   switch(type)
     127             :     {
     128      851852 :     case INVALID_ELEM:
     129             :     case HEX8:
     130             :     case PRISM6:
     131             :     case C0POLYHEDRON:
     132             :       {
     133      851852 :         return i + (nx+1)*(j + k*(ny+1));
     134             :       }
     135             : 
     136     3445498 :     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     3445498 :         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       40530 : 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       34809 :   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       40530 :   mesh.clear();
     341             : 
     342       11603 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     343             : 
     344       40530 :   if (nz != 0)
     345             :     {
     346       18552 :       mesh.set_mesh_dimension(3);
     347       18552 :       mesh.set_spatial_dimension(3);
     348             :     }
     349       21978 :   else if (ny != 0)
     350             :     {
     351       15838 :       mesh.set_mesh_dimension(2);
     352       15838 :       mesh.set_spatial_dimension(2);
     353             :     }
     354        6140 :   else if (nx != 0)
     355             :     {
     356        5849 :       mesh.set_mesh_dimension(1);
     357        5849 :       mesh.set_spatial_dimension(1);
     358             :     }
     359             :   else
     360             :     {
     361             :       // Will we get here?
     362         291 :       mesh.set_mesh_dimension(0);
     363         291 :       mesh.set_spatial_dimension(0);
     364             :     }
     365             : 
     366       40530 :   switch (mesh.mesh_dimension())
     367             :     {
     368             :       //---------------------------------------------------------------------
     369             :       // Build a 0D point
     370         291 :     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         375 :         mesh.add_point (Point(0, 0, 0), 0);
     380         291 :         Elem * elem = mesh.add_elem(Elem::build(NODEELEM));
     381         291 :         elem->set_node(0, mesh.node_ptr(0));
     382             : 
     383         207 :         break;
     384             :       }
     385             : 
     386             : 
     387             : 
     388             :       //---------------------------------------------------------------------
     389             :       // Build a 1D line
     390        5849 :     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        5849 :           case INVALID_ELEM:
     401             :           case EDGE2:
     402             :           case EDGE3:
     403             :           case EDGE4:
     404             :             {
     405        5849 :               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        1917 :           case INVALID_ELEM:
     417             :           case EDGE2:
     418             :             {
     419        1917 :               mesh.reserve_nodes(nx+1);
     420        1367 :               break;
     421             :             }
     422             : 
     423        3225 :           case EDGE3:
     424             :             {
     425        3225 :               mesh.reserve_nodes(2*nx+1);
     426        2303 :               break;
     427             :             }
     428             : 
     429         707 :           case EDGE4:
     430             :             {
     431         707 :               mesh.reserve_nodes(3*nx+1);
     432         505 :               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       31868 :               for (unsigned int i=0; i<=nx; i++)
     449             :               {
     450       39697 :                 const Node * const node = mesh.add_point (Point(static_cast<Real>(i)/nx, 0, 0), node_id++);
     451       29951 :                 if (i == 0)
     452        1917 :                   boundary_info.add_node(node, 0);
     453       29951 :                 if (i == nx)
     454        1917 :                   boundary_info.add_node(node, 1);
     455             :               }
     456             : 
     457         550 :               break;
     458             :             }
     459             : 
     460         922 :           case EDGE3:
     461             :             {
     462       15860 :               for (unsigned int i=0; i<=2*nx; i++)
     463             :               {
     464       16257 :                 const Node * const node = mesh.add_point (Point(static_cast<Real>(i)/(2*nx), 0, 0), node_id++);
     465       12635 :                 if (i == 0)
     466        3225 :                   boundary_info.add_node(node, 0);
     467       12635 :                 if (i == 2*nx)
     468        3225 :                   boundary_info.add_node(node, 1);
     469             :               }
     470         922 :               break;
     471             :             }
     472             : 
     473         202 :           case EDGE4:
     474             :             {
     475        4690 :               for (unsigned int i=0; i<=3*nx; i++)
     476             :               {
     477        5121 :                 const Node * const node = mesh.add_point (Point(static_cast<Real>(i)/(3*nx), 0, 0), node_id++);
     478        3983 :                 if (i == 0)
     479         707 :                   boundary_info.add_node(node, 0);
     480        3983 :                 if (i == 3*nx)
     481         707 :                   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       29951 :               for (unsigned int i=0; i<nx; i++)
     499             :                 {
     500       28034 :                   Elem * elem = mesh.add_elem(Elem::build_with_id(EDGE2, i));
     501       28034 :                   elem->set_node(0, mesh.node_ptr(i));
     502       28034 :                   elem->set_node(1, mesh.node_ptr(i+1));
     503             : 
     504       28034 :                   if (i == 0)
     505        1917 :                     boundary_info.add_side(elem, 0, 0);
     506             : 
     507       28034 :                   if (i == (nx-1))
     508        1917 :                     boundary_info.add_side(elem, 1, 1);
     509             : 
     510             :                 }
     511         550 :               break;
     512             :             }
     513             : 
     514         922 :           case EDGE3:
     515             :             {
     516        7930 :               for (unsigned int i=0; i<nx; i++)
     517             :                 {
     518        4705 :                   Elem * elem = mesh.add_elem(Elem::build_with_id(EDGE3, i));
     519        4705 :                   elem->set_node(0, mesh.node_ptr(2*i));
     520        4705 :                   elem->set_node(2, mesh.node_ptr(2*i+1));
     521        4705 :                   elem->set_node(1, mesh.node_ptr(2*i+2));
     522             : 
     523        4705 :                   if (i == 0)
     524        3225 :                     boundary_info.add_side(elem, 0, 0);
     525             : 
     526        4705 :                   if (i == (nx-1))
     527        3225 :                     boundary_info.add_side(elem, 1, 1);
     528             :                 }
     529         922 :               break;
     530             :             }
     531             : 
     532         202 :           case EDGE4:
     533             :             {
     534        1799 :               for (unsigned int i=0; i<nx; i++)
     535             :                 {
     536        1092 :                   Elem * elem = mesh.add_elem(Elem::build_with_id(EDGE4, i));
     537        1092 :                   elem->set_node(0, mesh.node_ptr(3*i));
     538        1092 :                   elem->set_node(2, mesh.node_ptr(3*i+1));
     539        1092 :                   elem->set_node(3, mesh.node_ptr(3*i+2));
     540        1092 :                   elem->set_node(1, mesh.node_ptr(3*i+3));
     541             : 
     542        1092 :                   if (i == 0)
     543         707 :                     boundary_info.add_side(elem, 0, 0);
     544             : 
     545        1092 :                   if (i == (nx-1))
     546         707 :                     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        5849 :         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        5849 :               (mesh.node_stored_range(),
     565       61075 :                [xmin, xmax](Node * node)
     566             :                {
     567       46569 :                  (*node)(0) = (*node)(0)*(xmax-xmin) + xmin;
     568       14506 :                });
     569             :           }
     570             : 
     571             :         // Add sideset names to boundary info
     572        5849 :         boundary_info.sideset_name(0) = "left";
     573        5849 :         boundary_info.sideset_name(1) = "right";
     574             : 
     575             :         // Add nodeset names to boundary info
     576        5849 :         boundary_info.nodeset_name(0) = "left";
     577        5849 :         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       15838 :     case 2:
     594             :       {
     595        4535 :         libmesh_assert_not_equal_to (nx, 0);
     596        4535 :         libmesh_assert_not_equal_to (ny, 0);
     597        4535 :         libmesh_assert_equal_to (nz, 0);
     598        4535 :         libmesh_assert_less (xmin, xmax);
     599        4535 :         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        8405 :           case INVALID_ELEM:
     606             :           case QUAD4:
     607             :           case QUADSHELL4:
     608             :           case QUAD8:
     609             :           case QUADSHELL8:
     610             :           case QUAD9:
     611             :           case QUADSHELL9:
     612             :             {
     613        8405 :               mesh.reserve_elem (nx*ny);
     614        5994 :               break;
     615             :             }
     616             : 
     617        7349 :           case TRI3:
     618             :           case TRISHELL3:
     619             :           case TRI6:
     620             :           case TRI7:
     621             :             {
     622        7349 :               mesh.reserve_elem (2*nx*ny);
     623        5249 :               break;
     624             :             }
     625             : 
     626          84 :           case C0POLYGON:
     627             :           {
     628          84 :             mesh.reserve_elem ((nx + 1) * (ny + 1));
     629          60 :             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        4194 :           case INVALID_ELEM:
     643             :           case QUAD4:
     644             :           case QUADSHELL4:
     645             :           case TRI3:
     646             :           case TRISHELL3:
     647             :             {
     648        4194 :               mesh.reserve_nodes( (nx+1)*(ny+1) );
     649        2987 :               break;
     650             :             }
     651             : 
     652        8679 :           case QUAD8:
     653             :           case QUADSHELL8:
     654             :           case QUAD9:
     655             :           case QUADSHELL9:
     656             :           case TRI6:
     657             :             {
     658        8679 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1) );
     659        6197 :               break;
     660             :             }
     661             : 
     662        2881 :           case TRI7:
     663             :             {
     664        2881 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1) + 2*nx*ny );
     665        2059 :               break;
     666             :             }
     667          84 :           case C0POLYGON:
     668             :             {
     669          84 :               mesh.reserve_nodes (4 + 3*nx*ny + 2*nx + 2*ny);
     670          60 :               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        4535 :         unsigned int node_id = 0;
     683             :         switch (type)
     684             :           {
     685        1207 :           case INVALID_ELEM:
     686             :           case QUAD4:
     687             :           case QUADSHELL4:
     688             :           case TRI3:
     689             :           case TRISHELL3:
     690             :             {
     691       17534 :               for (unsigned int j=0; j<=ny; j++)
     692      115180 :                 for (unsigned int i=0; i<=nx; i++)
     693             :                 {
     694             :                   const Node * const node =
     695      143510 :                       mesh.add_point(Point(static_cast<Real>(i) / static_cast<Real>(nx),
     696      101840 :                                            static_cast<Real>(j) / static_cast<Real>(ny),
     697             :                                            0.),
     698       91101 :                                      node_id++);
     699      101840 :                   if (j == 0)
     700       12784 :                     boundary_info.add_node(node, 0);
     701      101840 :                   if (j == ny)
     702       12784 :                     boundary_info.add_node(node, 2);
     703      101840 :                   if (i == 0)
     704       13340 :                     boundary_info.add_node(node, 3);
     705      101840 :                   if (i == nx)
     706       13340 :                     boundary_info.add_node(node, 1);
     707             :                 }
     708             : 
     709        1207 :               break;
     710             :             }
     711             : 
     712        3304 :           case QUAD8:
     713             :           case QUADSHELL8:
     714             :           case QUAD9:
     715             :           case QUADSHELL9:
     716             :           case TRI6:
     717             :           case TRI7:
     718             :             {
     719       64612 :               for (unsigned int j=0; j<=(2*ny); j++)
     720      665396 :                 for (unsigned int i=0; i<=(2*nx); i++)
     721             :                 {
     722             :                   const Node * const node =
     723      885664 :                       mesh.add_point(Point(static_cast<Real>(i) / static_cast<Real>(2 * nx),
     724      612344 :                                            static_cast<Real>(j) / static_cast<Real>(2 * ny),
     725             :                                            0),
     726      529672 :                                      node_id++);
     727      612344 :                   if (j == 0)
     728       54364 :                     boundary_info.add_node(node, 0);
     729      612344 :                   if (j == 2*ny)
     730       54364 :                     boundary_info.add_node(node, 2);
     731      612344 :                   if (i == 0)
     732       53052 :                     boundary_info.add_node(node, 3);
     733      612344 :                   if (i == 2*nx)
     734       53052 :                     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       11560 :               if (type == TRI7)
     740        7228 :                 for (unsigned int j=0; j<(3*ny); j += 3)
     741       25442 :                   for (unsigned int i=0; i<(3*nx); i += 3)
     742             :                     {
     743             :                       // The bottom-right triangle's center node
     744       30850 :                       mesh.add_point(Point(static_cast<Real>(i+2) / static_cast<Real>(3 * nx),
     745       21095 :                                            static_cast<Real>(j+1) / static_cast<Real>(3 * ny),
     746             :                                            0),
     747       18260 :                                      node_id++);
     748             :                       // The top-left triangle's center node
     749       21095 :                       mesh.add_point(Point(static_cast<Real>(i+1) / static_cast<Real>(3 * nx),
     750       21095 :                                            static_cast<Real>(j+2) / static_cast<Real>(3 * ny),
     751             :                                            0),
     752       18260 :                                      node_id++);
     753             :                     }
     754             : 
     755        3304 :               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        4535 :         unsigned int elem_id = 0;
     775             :         switch (type)
     776             :           {
     777             : 
     778         767 :           case INVALID_ELEM:
     779             :           case QUAD4:
     780             :           case QUADSHELL4:
     781             :             {
     782        9597 :               for (unsigned int j=0; j<ny; j++)
     783       82548 :                 for (unsigned int i=0; i<nx; i++)
     784             :                   {
     785       77240 :                     Elem * elem = mesh.add_elem(Elem::build_with_id(type == INVALID_ELEM ? QUAD4 : type, elem_id++));
     786       75606 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     787       75606 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+1,j)  ));
     788       75606 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     789       75606 :                     elem->set_node(3, mesh.node_ptr(idx(type,nx,i,j+1)  ));
     790             : 
     791       75606 :                     if (j == 0)
     792        6372 :                       boundary_info.add_side(elem, 0, 0);
     793             : 
     794       75606 :                     if (j == (ny-1))
     795        6372 :                       boundary_info.add_side(elem, 2, 2);
     796             : 
     797       75606 :                     if (i == 0)
     798        6942 :                       boundary_info.add_side(elem, 3, 3);
     799             : 
     800       75606 :                     if (i == (nx-1))
     801        6942 :                       boundary_info.add_side(elem, 1, 1);
     802             :                   }
     803         767 :               break;
     804             :             }
     805             : 
     806             : 
     807         440 :           case TRI3:
     808             :           case TRISHELL3:
     809             :             {
     810        3743 :               for (unsigned int j=0; j<ny; j++)
     811        6508 :                 for (unsigned int i=0; i<nx; i++)
     812             :                   {
     813             :                     // Add first Tri3
     814        4304 :                     Elem * elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
     815        4304 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     816        4304 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+1,j)  ));
     817        4304 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     818             : 
     819        4304 :                     if (j == 0)
     820        2218 :                       boundary_info.add_side(elem, 0, 0);
     821             : 
     822        4304 :                     if (i == (nx-1))
     823        2204 :                       boundary_info.add_side(elem, 1, 1);
     824             : 
     825             :                     // Add second Tri3
     826        4304 :                     elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
     827        4304 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     828        4304 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     829        4304 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i,j+1)  ));
     830             : 
     831        4304 :                     if (j == (ny-1))
     832        2218 :                       boundary_info.add_side(elem, 1, 2);
     833             : 
     834        4304 :                     if (i == 0)
     835        2204 :                       boundary_info.add_side(elem, 2, 3);
     836             :                   }
     837         440 :               break;
     838             :             }
     839             : 
     840             : 
     841             : 
     842        1644 :           case QUAD8:
     843             :           case QUADSHELL8:
     844             :           case QUAD9:
     845             :           case QUADSHELL9:
     846             :             {
     847       17209 :               for (unsigned int j=0; j<(2*ny); j += 2)
     848       92379 :                 for (unsigned int i=0; i<(2*nx); i += 2)
     849             :                   {
     850       80920 :                     Elem * elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
     851       80920 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     852       80920 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+2,j)  ));
     853       80920 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i+2,j+2)));
     854       80920 :                     elem->set_node(3, mesh.node_ptr(idx(type,nx,i,j+2)  ));
     855       80920 :                     elem->set_node(4, mesh.node_ptr(idx(type,nx,i+1,j)  ));
     856       80920 :                     elem->set_node(5, mesh.node_ptr(idx(type,nx,i+2,j+1)));
     857       80920 :                     elem->set_node(6, mesh.node_ptr(idx(type,nx,i+1,j+2)));
     858       80920 :                     elem->set_node(7, mesh.node_ptr(idx(type,nx,i,j+1)  ));
     859             : 
     860       80920 :                     if (type == QUAD9 || type == QUADSHELL9)
     861       63374 :                       elem->set_node(8, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     862             : 
     863       80920 :                     if (j == 0)
     864       12012 :                       boundary_info.add_side(elem, 0, 0);
     865             : 
     866       80920 :                     if (j == 2*(ny-1))
     867       12012 :                       boundary_info.add_side(elem, 2, 2);
     868             : 
     869       80920 :                     if (i == 0)
     870       11459 :                       boundary_info.add_side(elem, 3, 3);
     871             : 
     872       80920 :                     if (i == 2*(nx-1))
     873       11459 :                       boundary_info.add_side(elem, 1, 1);
     874             :                   }
     875        1644 :               break;
     876             :             }
     877             : 
     878             : 
     879        1660 :           case TRI6:
     880             :           case TRI7:
     881             :             {
     882       15097 :               for (unsigned int j=0; j<(2*ny); j += 2)
     883       57489 :                 for (unsigned int i=0; i<(2*nx); i += 2)
     884             :                   {
     885             :                     // Add first Tri in the bottom-right of its quad
     886       48202 :                     Elem * elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
     887       48202 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     888       48202 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+2,j)  ));
     889       48202 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i+2,j+2)));
     890       48202 :                     elem->set_node(3, mesh.node_ptr(idx(type,nx,i+1,j)  ));
     891       48202 :                     elem->set_node(4, mesh.node_ptr(idx(type,nx,i+2,j+1)));
     892       48202 :                     elem->set_node(5, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     893             : 
     894       48202 :                     if (type == TRI7)
     895       21095 :                       elem->set_node(6, mesh.node_ptr(elem->id()+(2*nx+1)*(2*ny+1)));
     896             : 
     897       48202 :                     if (j == 0)
     898        9390 :                       boundary_info.add_side(elem, 0, 0);
     899             : 
     900       48202 :                     if (i == 2*(nx-1))
     901        9287 :                       boundary_info.add_side(elem, 1, 1);
     902             : 
     903             :                     // Add second Tri in the top left of its quad
     904       48202 :                     elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
     905       48202 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     906       48202 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+2,j+2)));
     907       48202 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i,j+2)  ));
     908       48202 :                     elem->set_node(3, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     909       48202 :                     elem->set_node(4, mesh.node_ptr(idx(type,nx,i+1,j+2)));
     910       48202 :                     elem->set_node(5, mesh.node_ptr(idx(type,nx,i,j+1)  ));
     911             : 
     912       48202 :                     if (type == TRI7)
     913       21095 :                       elem->set_node(6, mesh.node_ptr(elem->id()+(2*nx+1)*(2*ny+1)));
     914             : 
     915       48202 :                     if (j == 2*(ny-1))
     916        9390 :                       boundary_info.add_side(elem, 1, 2);
     917             : 
     918       48202 :                     if (i == 0)
     919        9287 :                       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          84 :             const auto dx_tri = Real(1) / nx;
     933          84 :             const auto dy_tri = Real(1) / (ny + 1);
     934          60 :             std::unique_ptr<Elem> new_elem;
     935         686 :             for (const auto i : make_range(nx + 1))
     936             :             {
     937             :               // Make new nodes for bottom layer of triangles
     938             :               Node *node0, *node1, *node2;
     939         602 :               if (i == 0)
     940             :               {
     941         108 :                 node0 = mesh.add_point(Point(0., 0, 0.));
     942         108 :                 node1 = mesh.add_point(Point(0., dy_tri / 2., 0.));
     943         108 :                 node2 = mesh.add_point(Point(dx_tri / 2., 0., 0.));
     944          84 :                 node_list.push_back(node0);
     945          84 :                 node_list.push_back(node1);
     946          84 :                 node_list.push_back(node2);
     947             :               }
     948         518 :               else if (i < nx)
     949             :               {
     950         434 :                 node0 = node_list.back();
     951         558 :                 node1 = mesh.add_point(Point((i)*dx_tri, dy_tri / 2., 0.));
     952         558 :                 node2 = mesh.add_point(Point((i + 1. / 2.) * dx_tri, 0., 0.));
     953         434 :                 node_list.push_back(node1);
     954         434 :                 node_list.push_back(node2);
     955             :               }
     956             :               else
     957             :               {
     958          84 :                 node0 = node_list.back();
     959         108 :                 node1 = mesh.add_point(Point((i)*dx_tri, dy_tri / 2., 0.));
     960         108 :                 node2 = mesh.add_point(Point((i)*dx_tri, 0., 0.));
     961          84 :                 node_list.push_back(node1);
     962          84 :                 node_list.push_back(node2);
     963             :               }
     964             : 
     965         602 :               new_elem = std::make_unique<C0Polygon>(3);
     966             :               // Switch to Tri3 when exodus default output supports element type mixes
     967         602 :               new_elem->set_node(0, node0);
     968         602 :               new_elem->set_node(1, node1);
     969         602 :               new_elem->set_node(2, node2);
     970         774 :               auto * elem = mesh.add_elem(std::move(new_elem));
     971             : 
     972             :               // Set boundaries
     973         602 :               if (i == 0)
     974          84 :                 boundary_info.add_side(elem, 0, 3); // left
     975         518 :               else if (i == nx)
     976          84 :                 boundary_info.add_side(elem, 1, 1); // right
     977         602 :               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          84 :                 (Real(1) - (ny == 1 ?
     985             :                              dy_tri :
     986          84 :                              (Real(1) + (ny - 1) / 2.) * dy_tri)) / ny;
     987         602 :             for (const auto j : make_range(ny))
     988             :             {
     989        4851 :               for (const auto i : make_range(nx + (j % 2)))
     990             :               {
     991        4333 :                 if ((j % 2 == 0) || ((i > 0) && (i < nx)))
     992             :                 {
     993             :                   Node *n0, *n1, *n2, *n3, *n4, *n5;
     994        3843 :                   n0 = node_list[running_index++];
     995        3843 :                   n1 = node_list[running_index++];
     996        3843 :                   n2 = node_list[running_index];
     997             : 
     998        3843 :                   if (i == 0)
     999             :                   {
    1000         351 :                     n3 = mesh.add_point(Point(*n0) + RealVectorValue(0, hex_side, 0));
    1001         273 :                     node_list.push_back(n3);
    1002             :                   }
    1003             :                   else
    1004        3570 :                     n3 = node_list.back();
    1005             : 
    1006        4941 :                   n4 = mesh.add_point(Point(*n1) + RealVectorValue(0, hex_side + dy_tri, 0));
    1007        4941 :                   n5 = mesh.add_point(Point(*n2) + RealVectorValue(0, hex_side, 0));
    1008        3843 :                   node_list.push_back(n4);
    1009        3843 :                   node_list.push_back(n5);
    1010             : 
    1011        3843 :                   new_elem = std::make_unique<libMesh::C0Polygon>(6);
    1012        3843 :                   new_elem->set_node(0, n0);
    1013        3843 :                   new_elem->set_node(1, n1);
    1014        3843 :                   new_elem->set_node(2, n2);
    1015        3843 :                   new_elem->set_node(3, n5);
    1016        3843 :                   new_elem->set_node(4, n4);
    1017        3843 :                   new_elem->set_node(5, n3);
    1018        4941 :                   auto * elem = mesh.add_elem(std::move(new_elem));
    1019             : 
    1020             :                   // Set boundaries
    1021        3843 :                   if (i == 0)
    1022         273 :                     boundary_info.add_side(elem, 5, 3); // left
    1023        3570 :                   else if (i == nx)
    1024        2196 :                     boundary_info.add_side(elem, 2, 1); // right
    1025        1647 :                 }
    1026             :                 // The hexagons are offset, so we build on a quad on each external side to fill
    1027         490 :                 else if (i == 0 || i == nx)
    1028             :                 {
    1029             :                   Node *n0, *n1, *n2, *n3;
    1030         490 :                   n0 = node_list[running_index++];
    1031         490 :                   n1 = node_list[running_index];
    1032             : 
    1033         490 :                   if (i == 0)
    1034             :                   {
    1035         315 :                     n2 = mesh.add_point(Point(*n0) + RealVectorValue(0, hex_side + dy_tri, 0));
    1036         245 :                     node_list.push_back(n2);
    1037         315 :                     n3 = mesh.add_point(Point(*n1) + RealVectorValue(0, hex_side, 0));
    1038             :                   }
    1039             :                   else
    1040             :                   {
    1041         245 :                     n2 = node_list.back();
    1042         315 :                     n3 = mesh.add_point(Point(*n1) + RealVectorValue(0, hex_side + dy_tri, 0));
    1043             :                   }
    1044         490 :                   node_list.push_back(n3);
    1045             : 
    1046         490 :                   new_elem = std::make_unique<C0Polygon>(4);
    1047             :                   // Switch to Quad4 when exodus default output supports element type mixes
    1048         490 :                   new_elem->set_node(0, n0);
    1049         490 :                   new_elem->set_node(1, n1);
    1050         490 :                   new_elem->set_node(3, n2);
    1051         490 :                   new_elem->set_node(2, n3);
    1052         630 :                   auto * elem = mesh.add_elem(std::move(new_elem));
    1053             : 
    1054             :                   // Set boundaries
    1055         490 :                   if (i == 0)
    1056         245 :                     boundary_info.add_side(elem, 3, 3); // left
    1057         245 :                   else if (i == nx)
    1058         385 :                     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         518 :               running_index++;
    1065             : 
    1066             :               // Skip lower right corner node
    1067         518 :               if (j == 0)
    1068          84 :                 running_index++;
    1069             :             }
    1070             : 
    1071             :             // Build a final layer of triangles
    1072          84 :             const bool ny_odd = (ny % 2 == 1);
    1073         630 :             for (const auto i : make_range(nx + ny_odd))
    1074             :             {
    1075             :               // Use existing nodes, except at the corners
    1076             :               Node *node0, *node1, *node2;
    1077         546 :               if (i == 0 && ny_odd)
    1078             :               {
    1079          36 :                 node0 = mesh.add_point(Point(0., 1., 0.));
    1080          28 :                 node1 = node_list[running_index++];
    1081          36 :                 node2 = node_list[running_index];
    1082             :               }
    1083         518 :               else if (i < nx)
    1084             :               {
    1085         490 :                 node0 = node_list[running_index++];
    1086         490 :                 node1 = node_list[running_index++];
    1087         630 :                 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          28 :                 node0 = node_list[running_index++];
    1093          28 :                 node1 = node_list[running_index];
    1094          36 :                 node2 = mesh.add_point(Point(1., 1., 0.));
    1095             :               }
    1096             : 
    1097         546 :               new_elem = std::make_unique<C0Polygon>(3);
    1098             :               // Switch to Tri3 when exodus default output supports element type mixes
    1099         546 :               new_elem->set_node(0, node0);
    1100         546 :               new_elem->set_node(1, node1);
    1101         546 :               new_elem->set_node(2, node2);
    1102         702 :               auto * elem = mesh.add_elem(std::move(new_elem));
    1103             : 
    1104             :               // Set boundaries
    1105         546 :               if (i == 0)
    1106          84 :                 boundary_info.add_side(elem, 0, 3); // left
    1107         462 :               else if (i == nx)
    1108          28 :                 boundary_info.add_side(elem, 1, 1); // right
    1109         546 :               boundary_info.add_side(elem, 2, 2); // top
    1110             : 
    1111             :             }
    1112          24 :             break;
    1113          36 :           }
    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       15838 :         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       15838 :               (mesh.node_stored_range(),
    1134     1510171 :                [xmin, xmax, ymin, ymax](Node * node)
    1135             :                {
    1136      980217 :                  (*node)(0) = (*node)(0)*(xmax-xmin) + xmin;
    1137      766412 :                  (*node)(1) = ((*node)(1))*(ymax-ymin) + ymin;
    1138      552607 :                });
    1139             :           }
    1140             : 
    1141             :         // Add sideset names to boundary info
    1142       15838 :         boundary_info.sideset_name(0) = "bottom";
    1143       15838 :         boundary_info.sideset_name(1) = "right";
    1144       15838 :         boundary_info.sideset_name(2) = "top";
    1145       15838 :         boundary_info.sideset_name(3) = "left";
    1146             : 
    1147             :         // Add nodeset names to boundary info
    1148       15838 :         boundary_info.nodeset_name(0) = "bottom";
    1149       15838 :         boundary_info.nodeset_name(1) = "right";
    1150       15838 :         boundary_info.nodeset_name(2) = "top";
    1151       15838 :         boundary_info.nodeset_name(3) = "left";
    1152             : 
    1153        4535 :         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       18552 :     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       12032 :           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       12032 :               mesh.reserve_elem(nx*ny*nz);
    1196        8586 :               break;
    1197             :             }
    1198             : 
    1199        6520 :           case PRISM6:
    1200             :           case PRISM15:
    1201             :           case PRISM18:
    1202             :           case PRISM20:
    1203             :           case PRISM21:
    1204             :             {
    1205        6520 :               mesh.reserve_elem(2*nx*ny*nz);
    1206        4656 :               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        3242 :           case INVALID_ELEM:
    1221             :           case HEX8:
    1222             :           case PRISM6:
    1223             :           case C0POLYHEDRON:
    1224             :             {
    1225             :               const dof_id_type grid_nodes =
    1226        3242 :                 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        3242 :                 (type == C0POLYHEDRON) ?
    1233          35 :                 cast_int<dof_id_type>(nx*ny*nz) : 0;
    1234             : 
    1235        3242 :               mesh.reserve_nodes(grid_nodes + mid_polyhedron_nodes);
    1236        2306 :               break;
    1237             :             }
    1238             : 
    1239       10686 :           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       10686 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1)*(2*nz+1) );
    1255        7632 :               break;
    1256             :             }
    1257             : 
    1258        1621 :           case TET14:
    1259             :             {
    1260        3931 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1)*(2*nz+1) +
    1261        1621 :                                   24*nx*ny*nz +
    1262        1621 :                                   4*(nx*ny + ny*nz + nx*nz) );
    1263        1159 :               break;
    1264             :             }
    1265             : 
    1266        1344 :           case PRISM20:
    1267             :             {
    1268        2496 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1)*(2*nz+1) +
    1269        1344 :                                   2*nx*ny*(nz+1) );
    1270         960 :               break;
    1271             :             }
    1272             : 
    1273        1659 :           case PRISM21:
    1274             :             {
    1275        3081 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1)*(2*nz+1) +
    1276        1659 :                                   2*nx*ny*(2*nz+1) );
    1277        1185 :               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       11339 :               for (unsigned int k=0; k<=nz; k++)
    1297       33646 :                 for (unsigned int j=0; j<=ny; j++)
    1298      197614 :                   for (unsigned int i=0; i<=nx; i++)
    1299             :                   {
    1300             :                     const Node * const node =
    1301      243238 :                         mesh.add_point(Point(static_cast<Real>(i) / static_cast<Real>(nx),
    1302      172065 :                                              static_cast<Real>(j) / static_cast<Real>(ny),
    1303      172065 :                                              static_cast<Real>(k) / static_cast<Real>(nz)),
    1304      150864 :                                        node_id++);
    1305      172065 :                     if (k == 0)
    1306       34383 :                       boundary_info.add_node(node, 0);
    1307      172065 :                     if (k == nz)
    1308       34383 :                       boundary_info.add_node(node, 5);
    1309      172065 :                     if (j == 0)
    1310       29763 :                       boundary_info.add_node(node, 1);
    1311      172065 :                     if (j == ny)
    1312       29763 :                       boundary_info.add_node(node, 3);
    1313      172065 :                     if (i == 0)
    1314       25549 :                       boundary_info.add_node(node, 4);
    1315      172065 :                     if (i == nx)
    1316       25549 :                       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       71418 :               for (unsigned int k=0; k<=(2*nz); k++)
    1337      294064 :                 for (unsigned int j=0; j<=(2*ny); j++)
    1338     1809430 :                   for (unsigned int i=0; i<=(2*nx); i++)
    1339             :                   {
    1340             :                     const Node * const node =
    1341     2281336 :                         mesh.add_point(Point(static_cast<Real>(i) / static_cast<Real>(2 * nx),
    1342     1571474 :                                              static_cast<Real>(j) / static_cast<Real>(2 * ny),
    1343     1571474 :                                              static_cast<Real>(k) / static_cast<Real>(2 * nz)),
    1344     1356596 :                                        node_id++);
    1345     1571474 :                     if (k == 0)
    1346      249280 :                       boundary_info.add_node(node, 0);
    1347     1571474 :                     if (k == 2*nz)
    1348      249280 :                       boundary_info.add_node(node, 5);
    1349     1571474 :                     if (j == 0)
    1350      243810 :                       boundary_info.add_node(node, 1);
    1351     1571474 :                     if (j == 2*ny)
    1352      243810 :                       boundary_info.add_node(node, 3);
    1353     1571474 :                     if (i == 0)
    1354      237956 :                       boundary_info.add_node(node, 4);
    1355     1571474 :                     if (i == 2*nx)
    1356      237956 :                       boundary_info.add_node(node, 2);
    1357             :                   }
    1358             : 
    1359       15310 :               if (type == PRISM20 ||
    1360             :                   type == PRISM21)
    1361             :                 {
    1362        3003 :                   const unsigned int kmax = (type == PRISM20) ? nz : 2*nz;
    1363       11963 :                   for (unsigned int k=0; k<=kmax; k++)
    1364       21364 :                     for (unsigned int j=0; j<ny; j++)
    1365       31696 :                       for (unsigned int i=0; i<nx; i++)
    1366             :                         {
    1367             :                           const Node * const node1 =
    1368       27560 :                               mesh.add_point(Point((static_cast<Real>(i)+1/Real(3)) / static_cast<Real>(nx),
    1369       19292 :                                                    (static_cast<Real>(j)+1/Real(3)) / static_cast<Real>(ny),
    1370       19292 :                                                    static_cast<Real>(k) / static_cast<Real>(kmax)),
    1371       16536 :                                              node_id++);
    1372       19292 :                           if (k == 0)
    1373        5565 :                             boundary_info.add_node(node1, 0);
    1374       19292 :                           if (k == kmax)
    1375        5565 :                             boundary_info.add_node(node1, 5);
    1376             : 
    1377             :                           const Node * const node2 =
    1378       27560 :                               mesh.add_point(Point((static_cast<Real>(i)+2/Real(3)) / static_cast<Real>(nx),
    1379       19292 :                                                    (static_cast<Real>(j)+2/Real(3)) / static_cast<Real>(ny),
    1380        5512 :                                                    static_cast<Real>(k) / static_cast<Real>(kmax)),
    1381       16536 :                                              node_id++);
    1382       19292 :                           if (k == 0)
    1383        5565 :                             boundary_info.add_node(node2, 0);
    1384       19292 :                           if (k == kmax)
    1385        5565 :                             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        5507 :               for (unsigned int k=0; k<nz; k++)
    1408       13616 :                 for (unsigned int j=0; j<ny; j++)
    1409      110619 :                   for (unsigned int i=0; i<nx; i++)
    1410             :                     {
    1411      100395 :                       Elem * elem = mesh.add_elem(Elem::build_with_id(HEX8, elem_id++));
    1412      100395 :                       elem->set_node(0, mesh.node_ptr(idx(type,nx,ny,i,j,k)      ));
    1413      100395 :                       elem->set_node(1, mesh.node_ptr(idx(type,nx,ny,i+1,j,k)    ));
    1414      100395 :                       elem->set_node(2, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ));
    1415      100395 :                       elem->set_node(3, mesh.node_ptr(idx(type,nx,ny,i,j+1,k)    ));
    1416      100395 :                       elem->set_node(4, mesh.node_ptr(idx(type,nx,ny,i,j,k+1)    ));
    1417      100395 :                       elem->set_node(5, mesh.node_ptr(idx(type,nx,ny,i+1,j,k+1)  ));
    1418      100395 :                       elem->set_node(6, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)));
    1419      100395 :                       elem->set_node(7, mesh.node_ptr(idx(type,nx,ny,i,j+1,k+1)  ));
    1420             : 
    1421      100395 :                       if (k == 0)
    1422       18071 :                         boundary_info.add_side(elem, 0, 0);
    1423             : 
    1424      100395 :                       if (k == (nz-1))
    1425       18071 :                         boundary_info.add_side(elem, 5, 5);
    1426             : 
    1427      100395 :                       if (j == 0)
    1428       13591 :                         boundary_info.add_side(elem, 1, 1);
    1429             : 
    1430      100395 :                       if (j == (ny-1))
    1431       13591 :                         boundary_info.add_side(elem, 3, 3);
    1432             : 
    1433      100395 :                       if (i == 0)
    1434       10224 :                         boundary_info.add_side(elem, 4, 4);
    1435             : 
    1436      100395 :                       if (i == (nx-1))
    1437       10224 :                         boundary_info.add_side(elem, 2, 2);
    1438             :                     }
    1439         614 :               break;
    1440             :             }
    1441             : 
    1442             : 
    1443          35 :           case C0POLYHEDRON:
    1444             :             {
    1445          35 :               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         105 :               for (unsigned int k=0; k<nz; k++)
    1454         210 :                 for (unsigned int j=0; j<ny; j++)
    1455         420 :                   for (unsigned int i=0; i<nx; i++)
    1456             :                     {
    1457             :                       std::array<Node *, 8> elem_nodes =
    1458         280 :                         {{mesh.node_ptr(idx(type,nx,ny,i,j,k)      ),
    1459         280 :                           mesh.node_ptr(idx(type,nx,ny,i+1,j,k)    ),
    1460         280 :                           mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ),
    1461         280 :                           mesh.node_ptr(idx(type,nx,ny,i,j+1,k)    ),
    1462         280 :                           mesh.node_ptr(idx(type,nx,ny,i,j,k+1)    ),
    1463         280 :                           mesh.node_ptr(idx(type,nx,ny,i+1,j,k+1)  ),
    1464         280 :                           mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)),
    1465        1960 :                           mesh.node_ptr(idx(type,nx,ny,i,j+1,k+1)  )}};
    1466             : 
    1467         440 :                       std::vector<std::shared_ptr<Polygon>> sides(side_nodes.size());
    1468        1960 :                       for (auto s : index_range(side_nodes))
    1469             :                         {
    1470        2160 :                           sides[s] = std::make_shared<C0Polygon>(side_nodes[s].size());
    1471        8400 :                           for (auto n : index_range(side_nodes[s]))
    1472       10560 :                             sides[s]->set_node(n, elem_nodes[side_nodes[s][n]]);
    1473             :                         }
    1474             : 
    1475         280 :                       std::unique_ptr<Node> mid_elem_node;
    1476             :                       std::unique_ptr<Elem> new_elem =
    1477         360 :                         std::make_unique<C0Polyhedron>(sides, mid_elem_node);
    1478         280 :                       if (mid_elem_node)
    1479           0 :                         mesh.add_node(std::move(mid_elem_node));
    1480             : 
    1481         280 :                       new_elem->set_id() = elem_id++;
    1482         360 :                       Elem * elem = mesh.add_elem(std::move(new_elem));
    1483             : 
    1484         280 :                       if (k == 0)
    1485         140 :                         boundary_info.add_side(elem, 0, 0);
    1486             : 
    1487         280 :                       if (k == (nz-1))
    1488         140 :                         boundary_info.add_side(elem, 5, 5);
    1489             : 
    1490         280 :                       if (j == 0)
    1491         140 :                         boundary_info.add_side(elem, 1, 1);
    1492             : 
    1493         280 :                       if (j == (ny-1))
    1494         140 :                         boundary_info.add_side(elem, 3, 3);
    1495             : 
    1496         280 :                       if (i == 0)
    1497         140 :                         boundary_info.add_side(elem, 4, 4);
    1498             : 
    1499         280 :                       if (i == (nx-1))
    1500         140 :                         boundary_info.add_side(elem, 2, 2);
    1501         120 :                     }
    1502          10 :               break;
    1503             :             }
    1504             : 
    1505             : 
    1506             : 
    1507             : 
    1508         312 :           case PRISM6:
    1509             :             {
    1510        2485 :               for (unsigned int k=0; k<nz; k++)
    1511        3486 :                 for (unsigned int j=0; j<ny; j++)
    1512        5964 :                   for (unsigned int i=0; i<nx; i++)
    1513             :                     {
    1514             :                       // First Prism
    1515        3871 :                       Elem * elem = mesh.add_elem(Elem::build_with_id(PRISM6, elem_id++));
    1516        3871 :                       elem->set_node(0, mesh.node_ptr(idx(type,nx,ny,i,j,k)      ));
    1517        3871 :                       elem->set_node(1, mesh.node_ptr(idx(type,nx,ny,i+1,j,k)    ));
    1518        3871 :                       elem->set_node(2, mesh.node_ptr(idx(type,nx,ny,i,j+1,k)    ));
    1519        3871 :                       elem->set_node(3, mesh.node_ptr(idx(type,nx,ny,i,j,k+1)    ));
    1520        3871 :                       elem->set_node(4, mesh.node_ptr(idx(type,nx,ny,i+1,j,k+1)  ));
    1521        3871 :                       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        3871 :                       if (i==0)
    1525        2093 :                         boundary_info.add_side(elem, 3, 4);
    1526             : 
    1527        3871 :                       if (j==0)
    1528        2093 :                         boundary_info.add_side(elem, 1, 1);
    1529             : 
    1530        3871 :                       if (k==0)
    1531        2093 :                         boundary_info.add_side(elem, 0, 0);
    1532             : 
    1533        3871 :                       if (k == (nz-1))
    1534        2093 :                         boundary_info.add_side(elem, 4, 5);
    1535             : 
    1536             :                       // Second Prism
    1537        3871 :                       elem = mesh.add_elem(Elem::build_with_id(PRISM6, elem_id++));
    1538        3871 :                       elem->set_node(0, mesh.node_ptr(idx(type,nx,ny,i+1,j,k)    ));
    1539        3871 :                       elem->set_node(1, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ));
    1540        3871 :                       elem->set_node(2, mesh.node_ptr(idx(type,nx,ny,i,j+1,k)    ));
    1541        3871 :                       elem->set_node(3, mesh.node_ptr(idx(type,nx,ny,i+1,j,k+1)  ));
    1542        3871 :                       elem->set_node(4, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)));
    1543        3871 :                       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        3871 :                       if (i == (nx-1))
    1547        2093 :                         boundary_info.add_side(elem, 1, 2);
    1548             : 
    1549        3871 :                       if (j == (ny-1))
    1550        2093 :                         boundary_info.add_side(elem, 2, 3);
    1551             : 
    1552        3871 :                       if (k==0)
    1553        2093 :                         boundary_info.add_side(elem, 0, 0);
    1554             : 
    1555        3871 :                       if (k == (nz-1))
    1556        2093 :                         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       23487 :               for (unsigned int k=0; k<(2*nz); k += 2)
    1577       39283 :                 for (unsigned int j=0; j<(2*ny); j += 2)
    1578      134698 :                   for (unsigned int i=0; i<(2*nx); i += 2)
    1579             :                     {
    1580      109020 :                       ElemType build_type = (type == HEX20) ? HEX20 : HEX27;
    1581      109020 :                       Elem * elem = mesh.add_elem(Elem::build_with_id(build_type, elem_id++));
    1582             : 
    1583      109020 :                       elem->set_node(0,  mesh.node_ptr(idx(type,nx,ny,i,  j,  k)  ));
    1584      109020 :                       elem->set_node(1,  mesh.node_ptr(idx(type,nx,ny,i+2,j,  k)  ));
    1585      109020 :                       elem->set_node(2,  mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k)  ));
    1586      109020 :                       elem->set_node(3,  mesh.node_ptr(idx(type,nx,ny,i,  j+2,k)  ));
    1587      109020 :                       elem->set_node(4,  mesh.node_ptr(idx(type,nx,ny,i,  j,  k+2)));
    1588      109020 :                       elem->set_node(5,  mesh.node_ptr(idx(type,nx,ny,i+2,j,  k+2)));
    1589      109020 :                       elem->set_node(6,  mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k+2)));
    1590      109020 :                       elem->set_node(7,  mesh.node_ptr(idx(type,nx,ny,i,  j+2,k+2)));
    1591      109020 :                       elem->set_node(8,  mesh.node_ptr(idx(type,nx,ny,i+1,j,  k)  ));
    1592      109020 :                       elem->set_node(9,  mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k)  ));
    1593      109020 :                       elem->set_node(10, mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k)  ));
    1594      109020 :                       elem->set_node(11, mesh.node_ptr(idx(type,nx,ny,i,  j+1,k)  ));
    1595      109020 :                       elem->set_node(12, mesh.node_ptr(idx(type,nx,ny,i,  j,  k+1)));
    1596      109020 :                       elem->set_node(13, mesh.node_ptr(idx(type,nx,ny,i+2,j,  k+1)));
    1597      109020 :                       elem->set_node(14, mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k+1)));
    1598      109020 :                       elem->set_node(15, mesh.node_ptr(idx(type,nx,ny,i,  j+2,k+1)));
    1599      109020 :                       elem->set_node(16, mesh.node_ptr(idx(type,nx,ny,i+1,j,  k+2)));
    1600      109020 :                       elem->set_node(17, mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k+2)));
    1601      109020 :                       elem->set_node(18, mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k+2)));
    1602      109020 :                       elem->set_node(19, mesh.node_ptr(idx(type,nx,ny,i,  j+1,k+2)));
    1603             : 
    1604      109020 :                       if ((type == HEX27) || (type == TET4) || (type == TET10) || (type == TET14) ||
    1605        6884 :                           (type == PYRAMID5) || (type == PYRAMID13) || (type == PYRAMID14) ||
    1606        2418 :                           (type == PYRAMID18))
    1607             :                         {
    1608      104554 :                           elem->set_node(20, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ));
    1609      104554 :                           elem->set_node(21, mesh.node_ptr(idx(type,nx,ny,i+1,j,  k+1)));
    1610      104554 :                           elem->set_node(22, mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k+1)));
    1611      104554 :                           elem->set_node(23, mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k+1)));
    1612      104554 :                           elem->set_node(24, mesh.node_ptr(idx(type,nx,ny,i,  j+1,k+1)));
    1613      104554 :                           elem->set_node(25, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+2)));
    1614      104554 :                           elem->set_node(26, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)));
    1615             :                         }
    1616             : 
    1617      109020 :                       if (k == 0)
    1618       28218 :                         boundary_info.add_side(elem, 0, 0);
    1619             : 
    1620      109020 :                       if (k == 2*(nz-1))
    1621       28218 :                         boundary_info.add_side(elem, 5, 5);
    1622             : 
    1623      109020 :                       if (j == 0)
    1624       26919 :                         boundary_info.add_side(elem, 1, 1);
    1625             : 
    1626      109020 :                       if (j == 2*(ny-1))
    1627       26919 :                         boundary_info.add_side(elem, 3, 3);
    1628             : 
    1629      109020 :                       if (i == 0)
    1630       25678 :                         boundary_info.add_side(elem, 4, 4);
    1631             : 
    1632      109020 :                       if (i == 2*(nx-1))
    1633       25678 :                         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       12222 :               for (unsigned int k=0; k<(2*nz); k += 2)
    1647       16360 :                 for (unsigned int j=0; j<(2*ny); j += 2)
    1648       24856 :                   for (unsigned int i=0; i<(2*nx); i += 2)
    1649             :                     {
    1650             :                       // First Prism
    1651       15290 :                       Elem * elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
    1652       15290 :                       elem->set_node(0,  mesh.node_ptr(idx(type,nx,ny,i,  j,  k)  ));
    1653       15290 :                       elem->set_node(1,  mesh.node_ptr(idx(type,nx,ny,i+2,j,  k)  ));
    1654       15290 :                       elem->set_node(2,  mesh.node_ptr(idx(type,nx,ny,i,  j+2,k)  ));
    1655       15290 :                       elem->set_node(3,  mesh.node_ptr(idx(type,nx,ny,i,  j,  k+2)));
    1656       15290 :                       elem->set_node(4,  mesh.node_ptr(idx(type,nx,ny,i+2,j,  k+2)));
    1657       15290 :                       elem->set_node(5,  mesh.node_ptr(idx(type,nx,ny,i,  j+2,k+2)));
    1658       15290 :                       elem->set_node(6,  mesh.node_ptr(idx(type,nx,ny,i+1,j,  k)  ));
    1659       15290 :                       elem->set_node(7,  mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ));
    1660       15290 :                       elem->set_node(8,  mesh.node_ptr(idx(type,nx,ny,i,  j+1,k)  ));
    1661       15290 :                       elem->set_node(9,  mesh.node_ptr(idx(type,nx,ny,i,  j,  k+1)));
    1662       15290 :                       elem->set_node(10, mesh.node_ptr(idx(type,nx,ny,i+2,j,  k+1)));
    1663       15290 :                       elem->set_node(11, mesh.node_ptr(idx(type,nx,ny,i,  j+2,k+1)));
    1664       15290 :                       elem->set_node(12, mesh.node_ptr(idx(type,nx,ny,i+1,j,  k+2)));
    1665       15290 :                       elem->set_node(13, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+2)));
    1666       15290 :                       elem->set_node(14, mesh.node_ptr(idx(type,nx,ny,i,  j+1,k+2)));
    1667             : 
    1668       19706 :                       if (type == PRISM18 ||
    1669       13050 :                           type == PRISM20 ||
    1670             :                           type == PRISM21)
    1671             :                         {
    1672       12420 :                           elem->set_node(15, mesh.node_ptr(idx(type,nx,ny,i+1,j,  k+1)));
    1673       12420 :                           elem->set_node(16, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)));
    1674       12420 :                           elem->set_node(17, mesh.node_ptr(idx(type,nx,ny,i,  j+1,k+1)));
    1675             :                         }
    1676             : 
    1677       15290 :                       if (type == PRISM20)
    1678             :                         {
    1679        4235 :                           const dof_id_type base_idx = (2*nx+1)*(2*ny+1)*(2*nz+1);
    1680        4235 :                           elem->set_node(18, mesh.node_ptr(base_idx+((k/2)*(nx*ny)+j/2*nx+i/2)*2));
    1681        4235 :                           elem->set_node(19, mesh.node_ptr(base_idx+(((k/2)+1)*(nx*ny)+j/2*nx+i/2)*2));
    1682             :                         }
    1683             : 
    1684       15290 :                       if (type == PRISM21)
    1685             :                         {
    1686        4746 :                           const dof_id_type base_idx = (2*nx+1)*(2*ny+1)*(2*nz+1);
    1687        4746 :                           elem->set_node(18, mesh.node_ptr(base_idx+(k*(nx*ny)+j/2*nx+i/2)*2));
    1688        4746 :                           elem->set_node(19, mesh.node_ptr(base_idx+((k+2)*(nx*ny)+j/2*nx+i/2)*2));
    1689        4746 :                           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       15290 :                       if (i==0)
    1694        9566 :                         boundary_info.add_side(elem, 3, 4);
    1695             : 
    1696       15290 :                       if (j==0)
    1697        9566 :                         boundary_info.add_side(elem, 1, 1);
    1698             : 
    1699       15290 :                       if (k==0)
    1700        9616 :                         boundary_info.add_side(elem, 0, 0);
    1701             : 
    1702       15290 :                       if (k == 2*(nz-1))
    1703        9616 :                         boundary_info.add_side(elem, 4, 5);
    1704             : 
    1705             : 
    1706             :                       // Second Prism
    1707       15290 :                       elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
    1708       15290 :                       elem->set_node(0,  mesh.node_ptr(idx(type,nx,ny,i+2,j,k)     ));
    1709       15290 :                       elem->set_node(1,  mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k)   ));
    1710       15290 :                       elem->set_node(2,  mesh.node_ptr(idx(type,nx,ny,i,j+2,k)     ));
    1711       15290 :                       elem->set_node(3,  mesh.node_ptr(idx(type,nx,ny,i+2,j,k+2)   ));
    1712       15290 :                       elem->set_node(4,  mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k+2) ));
    1713       15290 :                       elem->set_node(5,  mesh.node_ptr(idx(type,nx,ny,i,j+2,k+2)   ));
    1714       15290 :                       elem->set_node(6,  mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k)  ));
    1715       15290 :                       elem->set_node(7,  mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k)  ));
    1716       15290 :                       elem->set_node(8,  mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ));
    1717       15290 :                       elem->set_node(9,  mesh.node_ptr(idx(type,nx,ny,i+2,j,k+1)  ));
    1718       15290 :                       elem->set_node(10, mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k+1)));
    1719       15290 :                       elem->set_node(11, mesh.node_ptr(idx(type,nx,ny,i,j+2,k+1)  ));
    1720       15290 :                       elem->set_node(12, mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k+2)));
    1721       15290 :                       elem->set_node(13, mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k+2)));
    1722       15290 :                       elem->set_node(14, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+2)));
    1723             : 
    1724       15290 :                       if (type == PRISM18 ||
    1725        7616 :                           type == PRISM20 ||
    1726             :                           type == PRISM21)
    1727             :                         {
    1728       12420 :                           elem->set_node(15,  mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k+1)));
    1729       12420 :                           elem->set_node(16,  mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k+1)));
    1730       12420 :                           elem->set_node(17,  mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)));
    1731             :                         }
    1732             : 
    1733       15290 :                       if (type == PRISM20)
    1734             :                         {
    1735        4235 :                           const dof_id_type base_idx = (2*nx+1)*(2*ny+1)*(2*nz+1);
    1736        4235 :                           elem->set_node(18, mesh.node_ptr(base_idx+((k/2)*(nx*ny)+j/2*nx+i/2)*2+1));
    1737        4235 :                           elem->set_node(19, mesh.node_ptr(base_idx+(((k/2)+1)*(nx*ny)+j/2*nx+i/2)*2+1));
    1738             :                         }
    1739             : 
    1740       15290 :                       if (type == PRISM21)
    1741             :                         {
    1742        4746 :                           const dof_id_type base_idx = (2*nx+1)*(2*ny+1)*(2*nz+1);
    1743        4746 :                           elem->set_node(18, mesh.node_ptr(base_idx+(k*(nx*ny)+j/2*nx+i/2)*2+1));
    1744        4746 :                           elem->set_node(19, mesh.node_ptr(base_idx+((k+2)*(nx*ny)+j/2*nx+i/2)*2+1));
    1745        4746 :                           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       15290 :                       if (i == 2*(nx-1))
    1750        9566 :                         boundary_info.add_side(elem, 1, 2);
    1751             : 
    1752       15290 :                       if (j == 2*(ny-1))
    1753        9566 :                         boundary_info.add_side(elem, 2, 3);
    1754             : 
    1755       15290 :                       if (k==0)
    1756        9616 :                         boundary_info.add_side(elem, 0, 0);
    1757             : 
    1758       15290 :                       if (k == 2*(nz-1))
    1759        9616 :                         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       18552 :         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       18552 :               (mesh.node_stored_range(),
    1789     4938624 :                [xmin, xmax, ymin, ymax, zmin, zmax](Node * node)
    1790             :                {
    1791     2274399 :                  (*node)(0) = ((*node)(0))*(xmax-xmin) + xmin;
    1792     2274399 :                  (*node)(1) = ((*node)(1))*(ymax-ymin) + ymin;
    1793     1782123 :                  (*node)(2) = ((*node)(2))*(zmax-zmin) + zmin;
    1794     1289847 :                });
    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       23862 :         if ((type == TET4) ||
    1807       17864 :             (type == TET10) ||
    1808       17402 :             (type == TET14) ||
    1809       14423 :             (type == PYRAMID5) ||
    1810        3960 :             (type == PYRAMID13) ||
    1811       17685 :             (type == PYRAMID14) ||
    1812        9865 :             (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        5490 :             std::unique_ptr<Elem> side;
    1819             : 
    1820        5490 :             if ((type == TET4) || (type == TET10) || (type == TET14))
    1821        4027 :               new_elements.reserve(24*mesh.n_elem());
    1822             :             else
    1823        1463 :               new_elements.reserve(6*mesh.n_elem());
    1824             : 
    1825             :             // Create tetrahedra or pyramids
    1826       47852 :             for (auto & base_hex : mesh.element_ptr_range())
    1827             :               {
    1828             :                 // Get a pointer to the node located at the HEX27 center
    1829       26708 :                 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      186956 :                 for (auto s : base_hex->side_index_range())
    1835             :                   {
    1836             :                     // Get the boundary ID(s) for this side
    1837      160248 :                     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      160248 :                     boundary_id_type b_id = ids.empty() ? BoundaryInfo::invalid_id : ids[0];
    1844             : 
    1845             :                     // Need to build the full-ordered side!
    1846      160248 :                     base_hex->build_side_ptr(side, s);
    1847             : 
    1848      160248 :                     if ((type == TET4) || (type == TET10) || (type == TET14))
    1849             :                       {
    1850             :                         // Build 4 sub-tets per side
    1851      529500 :                         for (unsigned int sub_tet=0; sub_tet<4; ++sub_tet)
    1852             :                           {
    1853      729600 :                             new_elements.push_back( Elem::build(TET4) );
    1854      117600 :                             auto & sub_elem = new_elements.back();
    1855      658800 :                             sub_elem->set_node(0, side->node_ptr(sub_tet));
    1856      658800 :                             sub_elem->set_node(1, side->node_ptr(8));                           // center of the face
    1857      541200 :                             sub_elem->set_node(2, side->node_ptr(sub_tet==3 ? 0 : sub_tet+1 )); // wrap-around
    1858      423600 :                             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      423600 :                             if (b_id != BoundaryInfo::invalid_id)
    1864      256376 :                               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       93168 :                         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       85404 :                         sub_elem->set_node(0, side->node_ptr(0));
    1878       85404 :                         sub_elem->set_node(1, side->node_ptr(3));
    1879       85404 :                         sub_elem->set_node(2, side->node_ptr(2));
    1880       85404 :                         sub_elem->set_node(3, side->node_ptr(1));
    1881             : 
    1882             :                         // Set the apex
    1883       54348 :                         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       54348 :                         if (b_id != BoundaryInfo::invalid_id)
    1888       35100 :                           boundary_info.add_side(sub_elem.get(), 4, b_id);
    1889             :                       } // end else type==PYRAMID*
    1890             :                   }
    1891        2354 :               }
    1892             : 
    1893             : 
    1894             :             // Delete the original HEX27 elements from the mesh, and the boundary info structure.
    1895       47852 :             for (auto & elem : mesh.element_ptr_range())
    1896             :               {
    1897       26708 :                 boundary_info.remove(elem); // Safe even if elem has no boundary info.
    1898       26708 :                 mesh.delete_elem(elem);
    1899        2354 :               }
    1900             : 
    1901             :             // Add the new elements
    1902      483438 :             for (auto i : index_range(new_elements))
    1903             :               {
    1904      465948 :                 new_elements[i]->set_id(i);
    1905      744204 :                 mesh.add_elem( std::move(new_elements[i]) );
    1906             :               }
    1907             : 
    1908        2354 :           } // 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       18552 :         if ((type == TET10) || (type == PYRAMID14))
    1913        1580 :           mesh.all_second_order();
    1914             : 
    1915       16972 :         else if (type == PYRAMID13)
    1916         343 :           mesh.all_second_order(/*full_ordered=*/false);
    1917             : 
    1918       16629 :         else if ((type == TET14) || (type == PYRAMID18))
    1919        2034 :           mesh.all_complete_order();
    1920             : 
    1921             : 
    1922             :         // Add sideset names to boundary info (Z axis out of the screen)
    1923       18552 :         boundary_info.sideset_name(0) = "back";
    1924       18552 :         boundary_info.sideset_name(1) = "bottom";
    1925       18552 :         boundary_info.sideset_name(2) = "right";
    1926       18552 :         boundary_info.sideset_name(3) = "top";
    1927       18552 :         boundary_info.sideset_name(4) = "left";
    1928       18552 :         boundary_info.sideset_name(5) = "front";
    1929             : 
    1930             :         // Add nodeset names to boundary info
    1931       18552 :         boundary_info.nodeset_name(0) = "back";
    1932       18552 :         boundary_info.nodeset_name(1) = "bottom";
    1933       18552 :         boundary_info.nodeset_name(2) = "right";
    1934       18552 :         boundary_info.nodeset_name(3) = "top";
    1935       18552 :         boundary_info.nodeset_name(4) = "left";
    1936       18552 :         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       40530 :   mesh.prepare_for_use ();
    1947       40530 : }
    1948             : 
    1949             : 
    1950             : 
    1951         116 : 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         116 :   build_cube(mesh,
    1960             :              0, 0, 0,
    1961             :              0., 0.,
    1962             :              0., 0.,
    1963             :              0., 0.,
    1964             :              type,
    1965             :              gauss_lobatto_grid);
    1966         116 : }
    1967             : 
    1968             : 
    1969         579 : 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         579 :   build_cube(mesh,
    1980             :              nx, 0, 0,
    1981             :              xmin, xmax,
    1982             :              0., 0.,
    1983             :              0., 0.,
    1984             :              type,
    1985             :              gauss_lobatto_grid);
    1986         579 : }
    1987             : 
    1988             : 
    1989             : 
    1990        2280 : 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        2280 :   build_cube (mesh,
    2004             :               nx, ny, 0,
    2005             :               xmin, xmax,
    2006             :               ymin, ymax,
    2007             :               0., 0.,
    2008             :               type,
    2009             :               gauss_lobatto_grid);
    2010        2280 : }
    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         233 : 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         233 :     cast_int<unsigned char>(mesh.mesh_dimension());
    2051         233 :   mesh.clear();
    2052         233 :   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         233 :   if (mesh.mesh_dimension() == 1)
    2061             :     {
    2062         233 :       switch (type)
    2063             :       {
    2064          89 :       case HEX8:
    2065             :       case HEX27:
    2066             :       case TET4:
    2067             :       case TET10:
    2068             :       case TET14:
    2069          89 :         mesh.set_mesh_dimension(3);
    2070          26 :         break;
    2071         116 :       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         116 :         mesh.set_mesh_dimension(2);
    2081          34 :         break;
    2082          28 :       case EDGE2:
    2083             :       case EDGE3:
    2084             :       case EDGE4:
    2085          28 :         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         233 :   const bool is_replicated = mesh.is_replicated();
    2099             : 
    2100             :   // Sphere is centered at origin by default
    2101          68 :   const Point cent;
    2102             : 
    2103         466 :   const Sphere sphere (cent, rad);
    2104             : 
    2105         233 :   switch (mesh.mesh_dimension())
    2106             :     {
    2107             :       //-----------------------------------------------------------------
    2108             :       // Build a line in one dimension
    2109          28 :     case 1:
    2110             :       {
    2111          28 :         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         116 :     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         116 :         if (flat)
    2130             :           {
    2131          34 :             const Real sqrt_2     = std::sqrt(2.);
    2132         116 :             const Real rad_2      = .25*rad;
    2133         116 :             const Real rad_sqrt_2 = rad/sqrt_2;
    2134             : 
    2135             :             // (Temporary) convenient storage for node pointers
    2136         150 :             std::vector<Node *> nodes(8);
    2137             : 
    2138             :             // Point 0
    2139         150 :             nodes[0] = mesh.add_point (Point(-rad_2,-rad_2, 0.), node_id++);
    2140             : 
    2141             :             // Point 1
    2142         150 :             nodes[1] = mesh.add_point (Point( rad_2,-rad_2, 0.), node_id++);
    2143             : 
    2144             :             // Point 2
    2145         150 :             nodes[2] = mesh.add_point (Point( rad_2, rad_2, 0.), node_id++);
    2146             : 
    2147             :             // Point 3
    2148         150 :             nodes[3] = mesh.add_point (Point(-rad_2, rad_2, 0.), node_id++);
    2149             : 
    2150             :             // Point 4
    2151         150 :             nodes[4] = mesh.add_point (Point(-rad_sqrt_2,-rad_sqrt_2, 0.), node_id++);
    2152             : 
    2153             :             // Point 5
    2154         150 :             nodes[5] = mesh.add_point (Point( rad_sqrt_2,-rad_sqrt_2, 0.), node_id++);
    2155             : 
    2156             :             // Point 6
    2157         150 :             nodes[6] = mesh.add_point (Point( rad_sqrt_2, rad_sqrt_2, 0.), node_id++);
    2158             : 
    2159             :             // Point 7
    2160         150 :             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         116 :               Elem * elem0 = mesh.add_elem (Elem::build(QUAD4));
    2167         116 :               elem0->set_node(0, nodes[0]);
    2168         116 :               elem0->set_node(1, nodes[1]);
    2169         116 :               elem0->set_node(2, nodes[2]);
    2170         116 :               elem0->set_node(3, nodes[3]);
    2171             :             }
    2172             : 
    2173             :             // Element 1
    2174             :             {
    2175         116 :               Elem * elem1 = mesh.add_elem (Elem::build(QUAD4));
    2176         116 :               elem1->set_node(0, nodes[4]);
    2177         116 :               elem1->set_node(1, nodes[0]);
    2178         116 :               elem1->set_node(2, nodes[3]);
    2179         116 :               elem1->set_node(3, nodes[7]);
    2180             :             }
    2181             : 
    2182             :             // Element 2
    2183             :             {
    2184         116 :               Elem * elem2 = mesh.add_elem (Elem::build(QUAD4));
    2185         116 :               elem2->set_node(0, nodes[4]);
    2186         116 :               elem2->set_node(1, nodes[5]);
    2187         116 :               elem2->set_node(2, nodes[1]);
    2188         116 :               elem2->set_node(3, nodes[0]);
    2189             :             }
    2190             : 
    2191             :             // Element 3
    2192             :             {
    2193         116 :               Elem * elem3 = mesh.add_elem (Elem::build(QUAD4));
    2194         116 :               elem3->set_node(0, nodes[1]);
    2195         116 :               elem3->set_node(1, nodes[5]);
    2196         116 :               elem3->set_node(2, nodes[6]);
    2197         116 :               elem3->set_node(3, nodes[2]);
    2198             :             }
    2199             : 
    2200             :             // Element 4
    2201             :             {
    2202         116 :               Elem * elem4 = mesh.add_elem (Elem::build(QUAD4));
    2203         116 :               elem4->set_node(0, nodes[3]);
    2204         116 :               elem4->set_node(1, nodes[2]);
    2205         116 :               elem4->set_node(2, nodes[6]);
    2206         116 :               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          89 :     case 3:
    2275             :       {
    2276             :         // (Currently) supported types
    2277          89 :         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          89 :           r_small = 0.25*rad,                      //  0.25 *radius
    2287          89 :           r_med   = (0.125*std::sqrt(2.)+0.5)*rad; // .67677*radius
    2288             : 
    2289             :         // (Temporary) convenient storage for node pointers
    2290         115 :         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         115 :         nodes[0] = mesh.add_point (Point(-r_small,-r_small, -r_small), node_id++);
    2300         115 :         nodes[1] = mesh.add_point (Point( r_small,-r_small, -r_small), node_id++);
    2301         115 :         nodes[2] = mesh.add_point (Point( r_small, r_small, -r_small), node_id++);
    2302         115 :         nodes[3] = mesh.add_point (Point(-r_small, r_small, -r_small), node_id++);
    2303         115 :         nodes[4] = mesh.add_point (Point(-r_small,-r_small,  r_small), node_id++);
    2304         115 :         nodes[5] = mesh.add_point (Point( r_small,-r_small,  r_small), node_id++);
    2305         115 :         nodes[6] = mesh.add_point (Point( r_small, r_small,  r_small), node_id++);
    2306         115 :         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         115 :         nodes[8]  = mesh.add_point (Point(-r_med,-r_med, -r_med), node_id++);
    2310         115 :         nodes[9]  = mesh.add_point (Point( r_med,-r_med, -r_med), node_id++);
    2311         115 :         nodes[10] = mesh.add_point (Point( r_med, r_med, -r_med), node_id++);
    2312         115 :         nodes[11] = mesh.add_point (Point(-r_med, r_med, -r_med), node_id++);
    2313         115 :         nodes[12] = mesh.add_point (Point(-r_med,-r_med,  r_med), node_id++);
    2314         115 :         nodes[13] = mesh.add_point (Point( r_med,-r_med,  r_med), node_id++);
    2315         115 :         nodes[14] = mesh.add_point (Point( r_med, r_med,  r_med), node_id++);
    2316         115 :         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          89 :           Elem * elem0 = mesh.add_elem(Elem::build(HEX8));
    2322          89 :           elem0->set_node(0, nodes[0]);
    2323          89 :           elem0->set_node(1, nodes[1]);
    2324          89 :           elem0->set_node(2, nodes[2]);
    2325          89 :           elem0->set_node(3, nodes[3]);
    2326          89 :           elem0->set_node(4, nodes[4]);
    2327          89 :           elem0->set_node(5, nodes[5]);
    2328          89 :           elem0->set_node(6, nodes[6]);
    2329          89 :           elem0->set_node(7, nodes[7]);
    2330             :         }
    2331             : 
    2332             :         // Element 1 - "bottom"
    2333             :         {
    2334          89 :           Elem * elem1 = mesh.add_elem(Elem::build(HEX8));
    2335          89 :           elem1->set_node(0, nodes[8]);
    2336          89 :           elem1->set_node(1, nodes[9]);
    2337          89 :           elem1->set_node(2, nodes[10]);
    2338          89 :           elem1->set_node(3, nodes[11]);
    2339          89 :           elem1->set_node(4, nodes[0]);
    2340          89 :           elem1->set_node(5, nodes[1]);
    2341          89 :           elem1->set_node(6, nodes[2]);
    2342          89 :           elem1->set_node(7, nodes[3]);
    2343             :         }
    2344             : 
    2345             :         // Element 2 - "front"
    2346             :         {
    2347          89 :           Elem * elem2 = mesh.add_elem(Elem::build(HEX8));
    2348          89 :           elem2->set_node(0, nodes[8]);
    2349          89 :           elem2->set_node(1, nodes[9]);
    2350          89 :           elem2->set_node(2, nodes[1]);
    2351          89 :           elem2->set_node(3, nodes[0]);
    2352          89 :           elem2->set_node(4, nodes[12]);
    2353          89 :           elem2->set_node(5, nodes[13]);
    2354          89 :           elem2->set_node(6, nodes[5]);
    2355          89 :           elem2->set_node(7, nodes[4]);
    2356             :         }
    2357             : 
    2358             :         // Element 3 - "right"
    2359             :         {
    2360          89 :           Elem * elem3 = mesh.add_elem(Elem::build(HEX8));
    2361          89 :           elem3->set_node(0, nodes[1]);
    2362          89 :           elem3->set_node(1, nodes[9]);
    2363          89 :           elem3->set_node(2, nodes[10]);
    2364          89 :           elem3->set_node(3, nodes[2]);
    2365          89 :           elem3->set_node(4, nodes[5]);
    2366          89 :           elem3->set_node(5, nodes[13]);
    2367          89 :           elem3->set_node(6, nodes[14]);
    2368          89 :           elem3->set_node(7, nodes[6]);
    2369             :         }
    2370             : 
    2371             :         // Element 4 - "back"
    2372             :         {
    2373          89 :           Elem * elem4 = mesh.add_elem(Elem::build(HEX8));
    2374          89 :           elem4->set_node(0, nodes[3]);
    2375          89 :           elem4->set_node(1, nodes[2]);
    2376          89 :           elem4->set_node(2, nodes[10]);
    2377          89 :           elem4->set_node(3, nodes[11]);
    2378          89 :           elem4->set_node(4, nodes[7]);
    2379          89 :           elem4->set_node(5, nodes[6]);
    2380          89 :           elem4->set_node(6, nodes[14]);
    2381          89 :           elem4->set_node(7, nodes[15]);
    2382             :         }
    2383             : 
    2384             :         // Element 5 - "left"
    2385             :         {
    2386          89 :           Elem * elem5 = mesh.add_elem(Elem::build(HEX8));
    2387          89 :           elem5->set_node(0, nodes[8]);
    2388          89 :           elem5->set_node(1, nodes[0]);
    2389          89 :           elem5->set_node(2, nodes[3]);
    2390          89 :           elem5->set_node(3, nodes[11]);
    2391          89 :           elem5->set_node(4, nodes[12]);
    2392          89 :           elem5->set_node(5, nodes[4]);
    2393          89 :           elem5->set_node(6, nodes[7]);
    2394          89 :           elem5->set_node(7, nodes[15]);
    2395             :         }
    2396             : 
    2397             :         // Element 6 - "top"
    2398             :         {
    2399          89 :           Elem * elem6 = mesh.add_elem(Elem::build(HEX8));
    2400          89 :           elem6->set_node(0, nodes[4]);
    2401          89 :           elem6->set_node(1, nodes[5]);
    2402          89 :           elem6->set_node(2, nodes[6]);
    2403          89 :           elem6->set_node(3, nodes[7]);
    2404          89 :           elem6->set_node(4, nodes[12]);
    2405          89 :           elem6->set_node(5, nodes[13]);
    2406          89 :           elem6->set_node(6, nodes[14]);
    2407          89 :           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         466 :   MeshRefinement mesh_refinement (mesh);
    2424             : 
    2425             :   // For avoiding extraneous element side construction
    2426         233 :   std::unique_ptr<Elem> side;
    2427             : 
    2428             :   // Loop over the elements, refine, pop nodes to boundary.
    2429         562 :   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         329 :       if (!is_replicated)
    2435          84 :         mesh.prepare_for_use();
    2436             : 
    2437         329 :       mesh_refinement.uniformly_refine(1);
    2438             : 
    2439             :       const bool move_only_boundary_nodes =
    2440         329 :         mesh.mesh_dimension() != 2 || flat;
    2441             :       MeshTools::Modification::interpolate_surface
    2442         658 :         (mesh, sphere, /*ids=*/{}, move_only_boundary_nodes);
    2443             :     }
    2444             : 
    2445             :   // A DistributedMesh needs a little prep before flattening
    2446         233 :   if (!is_replicated)
    2447          42 :     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         233 :   MeshTools::Modification::flatten(mesh);
    2454             : 
    2455             :   // Convert all the tensor product elements to simplices if requested
    2456         233 :   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          49 :       if (is_replicated)
    2461          42 :         mesh.prepare_for_use();
    2462             : 
    2463          49 :       MeshTools::Modification::all_tri(mesh);
    2464             :     }
    2465             : 
    2466             :   // Convert to second-order elements if the user requested it.
    2467         301 :   if (Elem::build(type)->default_order() != FIRST)
    2468             :     {
    2469         163 :       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         163 :           bool full_ordered = !((type==QUAD8) || (type==HEX20));
    2478         163 :           mesh.all_second_order(full_ordered);
    2479             :         }
    2480             : 
    2481             :       // And pop to the boundary again...
    2482       25924 :       for (const auto & elem : mesh.active_element_ptr_range())
    2483      124685 :         for (auto s : elem->side_index_range())
    2484      131158 :           if (elem->neighbor_ptr(s) == nullptr)
    2485             :             {
    2486        4178 :               elem->build_side_ptr(side, s);
    2487             : 
    2488             :               // Pop each point to the sphere boundary
    2489       37044 :               for (auto n : side->node_index_range())
    2490       42688 :                 side->point(n) =
    2491       42688 :                   sphere.closest_point(side->point(n));
    2492          67 :             }
    2493             :     }
    2494             : 
    2495             : 
    2496             :   // The meshes could probably use some smoothing.
    2497         233 :   if (mesh.mesh_dimension() > 1)
    2498             :     {
    2499         265 :       LaplaceMeshSmoother smoother(mesh, n_smooth);
    2500         205 :       smoother.smooth();
    2501             :     }
    2502             : 
    2503             :   // We'll give the whole sphere surface a boundary id of 0
    2504       91940 :   for (const auto & elem : mesh.active_element_ptr_range())
    2505      376577 :     for (auto s : elem->side_index_range())
    2506      378678 :       if (!elem->neighbor_ptr(s))
    2507        8513 :         boundary_info.add_side(elem, s, 0);
    2508             : 
    2509             :   // Done building the mesh.  Now prepare it for use.
    2510         233 :   mesh.prepare_for_use();
    2511         233 : }
    2512             : 
    2513             : #endif // #ifndef LIBMESH_ENABLE_AMR
    2514             : 
    2515             : 
    2516             : // Meshes the tensor product of a 1D and a 1D-or-2D domain.
    2517          49 : 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          49 :   if (!cross_section.n_elem())
    2526           0 :     return;
    2527             : 
    2528          49 :   dof_id_type orig_elem = cross_section.n_elem();
    2529          49 :   dof_id_type orig_nodes = cross_section.n_nodes();
    2530             : 
    2531             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    2532          49 :   unique_id_type orig_unique_ids = cross_section.parallel_max_unique_id();
    2533             : #endif
    2534             : 
    2535          49 :   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          49 :   if (!cross_section.is_serial())
    2549           0 :     mesh.delete_remote_elements();
    2550             : 
    2551             :   // We know a priori how many elements we'll need
    2552          49 :   mesh.reserve_elem(nz*orig_elem);
    2553             : 
    2554             :   // For straightforward meshes we need one or two additional layers per
    2555             :   // element.
    2556         182 :   if (cross_section.elements_begin() != cross_section.elements_end() &&
    2557         143 :       (*cross_section.elements_begin())->default_order() == SECOND)
    2558          14 :     order = 2;
    2559          49 :   mesh.comm().max(order);
    2560             : 
    2561          49 :   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        1794 :   for (const auto & node : cross_section.node_ptr_range())
    2567             :     {
    2568        6510 :       for (unsigned int k=0; k != order*nz+1; ++k)
    2569             :         {
    2570        5313 :           const dof_id_type new_node_id = node->id() + k * orig_nodes;
    2571        5313 :           Node * my_node = mesh.query_node_ptr(new_node_id);
    2572        5313 :           if (!my_node)
    2573             :             {
    2574             :               std::unique_ptr<Node> new_node = Node::build
    2575        6831 :                 (*node + (extrusion_vector * k / nz / order),
    2576        1518 :                  new_node_id);
    2577        5313 :               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        5313 :               const unique_id_type uid = (k == 0) ?
    2584         684 :                 node->unique_id() :
    2585        4116 :                 orig_unique_ids + (k-1)*(orig_nodes + orig_elem) + node->id();
    2586             : 
    2587        1518 :               new_node->set_unique_id(uid);
    2588             : #endif
    2589             : 
    2590        5313 :               cross_section_boundary_info.boundary_ids(node, ids_to_copy);
    2591        5313 :               boundary_info.add_node(new_node.get(), ids_to_copy);
    2592             : 
    2593        8349 :               mesh.add_node(std::move(new_node));
    2594        2277 :             }
    2595             :         }
    2596          21 :     }
    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          49 :     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          49 :   cross_section.comm().max(next_side_id);
    2608             : 
    2609         734 :   for (const auto & elem : cross_section.element_ptr_range())
    2610             :     {
    2611         455 :       const ElemType etype = elem->type();
    2612             : 
    2613             :       // build_extrusion currently only works on coarse meshes
    2614         130 :       libmesh_assert (!elem->parent());
    2615             : 
    2616        1421 :       for (unsigned int k=0; k != nz; ++k)
    2617             :         {
    2618         690 :           std::unique_ptr<Elem> new_elem;
    2619         966 :           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         168 :             case TRI3:
    2657             :               {
    2658         240 :                 new_elem = Elem::build(PRISM6);
    2659         216 :                 new_elem->set_node(0, mesh.node_ptr(elem->node_ptr(0)->id() + (k * orig_nodes)));
    2660         216 :                 new_elem->set_node(1, mesh.node_ptr(elem->node_ptr(1)->id() + (k * orig_nodes)));
    2661         216 :                 new_elem->set_node(2, mesh.node_ptr(elem->node_ptr(2)->id() + (k * orig_nodes)));
    2662         216 :                 new_elem->set_node(3, mesh.node_ptr(elem->node_ptr(0)->id() + ((k+1) * orig_nodes)));
    2663         216 :                 new_elem->set_node(4, mesh.node_ptr(elem->node_ptr(1)->id() + ((k+1) * orig_nodes)));
    2664         216 :                 new_elem->set_node(5, mesh.node_ptr(elem->node_ptr(2)->id() + ((k+1) * orig_nodes)));
    2665             : 
    2666         216 :                 if (elem->neighbor_ptr(0) == remote_elem)
    2667           0 :                   new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
    2668         216 :                 if (elem->neighbor_ptr(1) == remote_elem)
    2669           0 :                   new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
    2670         216 :                 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         448 :             case QUAD4:
    2742             :               {
    2743         640 :                 new_elem = Elem::build(HEX8);
    2744         576 :                 new_elem->set_node(0, mesh.node_ptr(elem->node_ptr(0)->id() + (k * orig_nodes)));
    2745         576 :                 new_elem->set_node(1, mesh.node_ptr(elem->node_ptr(1)->id() + (k * orig_nodes)));
    2746         576 :                 new_elem->set_node(2, mesh.node_ptr(elem->node_ptr(2)->id() + (k * orig_nodes)));
    2747         576 :                 new_elem->set_node(3, mesh.node_ptr(elem->node_ptr(3)->id() + (k * orig_nodes)));
    2748         576 :                 new_elem->set_node(4, mesh.node_ptr(elem->node_ptr(0)->id() + ((k+1) * orig_nodes)));
    2749         576 :                 new_elem->set_node(5, mesh.node_ptr(elem->node_ptr(1)->id() + ((k+1) * orig_nodes)));
    2750         576 :                 new_elem->set_node(6, mesh.node_ptr(elem->node_ptr(2)->id() + ((k+1) * orig_nodes)));
    2751         576 :                 new_elem->set_node(7, mesh.node_ptr(elem->node_ptr(3)->id() + ((k+1) * orig_nodes)));
    2752             : 
    2753         576 :                 if (elem->neighbor_ptr(0) == remote_elem)
    2754           0 :                   new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
    2755         576 :                 if (elem->neighbor_ptr(1) == remote_elem)
    2756           0 :                   new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
    2757         576 :                 if (elem->neighbor_ptr(2) == remote_elem)
    2758           0 :                   new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
    2759         576 :                 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         350 :             case QUAD9:
    2765             :               {
    2766         500 :                 new_elem = Elem::build(HEX27);
    2767         450 :                 new_elem->set_node(0, mesh.node_ptr(elem->node_ptr(0)->id() + (2*k * orig_nodes)));
    2768         450 :                 new_elem->set_node(1, mesh.node_ptr(elem->node_ptr(1)->id() + (2*k * orig_nodes)));
    2769         450 :                 new_elem->set_node(2, mesh.node_ptr(elem->node_ptr(2)->id() + (2*k * orig_nodes)));
    2770         450 :                 new_elem->set_node(3, mesh.node_ptr(elem->node_ptr(3)->id() + (2*k * orig_nodes)));
    2771         450 :                 new_elem->set_node(4, mesh.node_ptr(elem->node_ptr(0)->id() + ((2*k+2) * orig_nodes)));
    2772         450 :                 new_elem->set_node(5, mesh.node_ptr(elem->node_ptr(1)->id() + ((2*k+2) * orig_nodes)));
    2773         450 :                 new_elem->set_node(6, mesh.node_ptr(elem->node_ptr(2)->id() + ((2*k+2) * orig_nodes)));
    2774         450 :                 new_elem->set_node(7, mesh.node_ptr(elem->node_ptr(3)->id() + ((2*k+2) * orig_nodes)));
    2775         450 :                 new_elem->set_node(8, mesh.node_ptr(elem->node_ptr(4)->id() + (2*k * orig_nodes)));
    2776         450 :                 new_elem->set_node(9, mesh.node_ptr(elem->node_ptr(5)->id() + (2*k * orig_nodes)));
    2777         450 :                 new_elem->set_node(10, mesh.node_ptr(elem->node_ptr(6)->id() + (2*k * orig_nodes)));
    2778         450 :                 new_elem->set_node(11, mesh.node_ptr(elem->node_ptr(7)->id() + (2*k * orig_nodes)));
    2779         450 :                 new_elem->set_node(12, mesh.node_ptr(elem->node_ptr(0)->id() + ((2*k+1) * orig_nodes)));
    2780         450 :                 new_elem->set_node(13, mesh.node_ptr(elem->node_ptr(1)->id() + ((2*k+1) * orig_nodes)));
    2781         450 :                 new_elem->set_node(14, mesh.node_ptr(elem->node_ptr(2)->id() + ((2*k+1) * orig_nodes)));
    2782         450 :                 new_elem->set_node(15, mesh.node_ptr(elem->node_ptr(3)->id() + ((2*k+1) * orig_nodes)));
    2783         450 :                 new_elem->set_node(16, mesh.node_ptr(elem->node_ptr(4)->id() + ((2*k+2) * orig_nodes)));
    2784         450 :                 new_elem->set_node(17, mesh.node_ptr(elem->node_ptr(5)->id() + ((2*k+2) * orig_nodes)));
    2785         450 :                 new_elem->set_node(18, mesh.node_ptr(elem->node_ptr(6)->id() + ((2*k+2) * orig_nodes)));
    2786         450 :                 new_elem->set_node(19, mesh.node_ptr(elem->node_ptr(7)->id() + ((2*k+2) * orig_nodes)));
    2787         450 :                 new_elem->set_node(20, mesh.node_ptr(elem->node_ptr(8)->id() + (2*k * orig_nodes)));
    2788         450 :                 new_elem->set_node(21, mesh.node_ptr(elem->node_ptr(4)->id() + ((2*k+1) * orig_nodes)));
    2789         450 :                 new_elem->set_node(22, mesh.node_ptr(elem->node_ptr(5)->id() + ((2*k+1) * orig_nodes)));
    2790         450 :                 new_elem->set_node(23, mesh.node_ptr(elem->node_ptr(6)->id() + ((2*k+1) * orig_nodes)));
    2791         450 :                 new_elem->set_node(24, mesh.node_ptr(elem->node_ptr(7)->id() + ((2*k+1) * orig_nodes)));
    2792         450 :                 new_elem->set_node(25, mesh.node_ptr(elem->node_ptr(8)->id() + ((2*k+2) * orig_nodes)));
    2793         450 :                 new_elem->set_node(26, mesh.node_ptr(elem->node_ptr(8)->id() + ((2*k+1) * orig_nodes)));
    2794             : 
    2795         450 :                 if (elem->neighbor_ptr(0) == remote_elem)
    2796           0 :                   new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
    2797         450 :                 if (elem->neighbor_ptr(1) == remote_elem)
    2798           0 :                   new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
    2799         450 :                 if (elem->neighbor_ptr(2) == remote_elem)
    2800           0 :                   new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
    2801         450 :                 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         966 :           new_elem->set_id(elem->id() + (k * orig_elem));
    2814         966 :           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         966 :           const unique_id_type uid = (k == 0) ?
    2821         260 :             elem->unique_id() :
    2822         511 :             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         966 :           if (!elem_subdomain)
    2828             :             // maintain the subdomain_id
    2829         518 :             new_elem->subdomain_id() = elem->subdomain_id();
    2830             :           else
    2831             :             // Allow the user to choose new subdomain_ids
    2832         448 :             new_elem->subdomain_id() = elem_subdomain->get_subdomain_for_layer(elem, k);
    2833             : 
    2834        1242 :           Elem * added_elem = mesh.add_elem(std::move(new_elem));
    2835             : 
    2836             :           // Copy any old boundary ids on all sides
    2837        4938 :           for (auto s : elem->side_index_range())
    2838             :             {
    2839        3696 :               cross_section_boundary_info.boundary_ids(elem, s, ids_to_copy);
    2840             : 
    2841        3696 :               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        3696 :                   boundary_info.add_side(added_elem,
    2848        2640 :                                          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         966 :           if (k == 0)
    2865         455 :             boundary_info.add_side(added_elem, 0, next_side_id);
    2866         966 :           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         455 :               const unsigned short top_id = added_elem->dim() == 3 ?
    2872         455 :                 cast_int<unsigned short>(elem->n_sides()+1) : 2;
    2873             :               boundary_info.add_side
    2874         455 :                 (added_elem, top_id,
    2875         455 :                  cast_int<boundary_id_type>(next_side_id+1));
    2876             :             }
    2877         414 :         }
    2878          21 :     }
    2879             : 
    2880             :   // Done building the mesh.  Now prepare it for use.
    2881          49 :   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          70 : 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          70 :   const Real xavg = (xmin + xmax)/2;
    3011          70 :   const Real yavg = (ymin + ymax)/2;
    3012          70 :   const Real zavg = (zmin + zmax)/2;
    3013          90 :   mesh.add_point(Point(xavg,yavg,zmin), 0);
    3014          90 :   mesh.add_point(Point(xmax,yavg,zavg), 1);
    3015          90 :   mesh.add_point(Point(xavg,ymax,zavg), 2);
    3016          90 :   mesh.add_point(Point(xmin,yavg,zavg), 3);
    3017          90 :   mesh.add_point(Point(xavg,ymin,zavg), 4);
    3018          90 :   mesh.add_point(Point(xavg,yavg,zmax), 5);
    3019             : 
    3020        4192 :   auto add_tri = [&mesh, flip_tris](std::array<dof_id_type,3> nodes)
    3021             :   {
    3022         560 :     auto elem = mesh.add_elem(Elem::build(TRI3));
    3023         560 :     elem->set_node(0, mesh.node_ptr(nodes[0]));
    3024         560 :     elem->set_node(1, mesh.node_ptr(nodes[1]));
    3025         560 :     elem->set_node(2, mesh.node_ptr(nodes[2]));
    3026         560 :     if (flip_tris)
    3027         144 :       elem->flip(&mesh.get_boundary_info());
    3028         580 :   };
    3029             : 
    3030          70 :   add_tri({0,2,1});
    3031          70 :   add_tri({0,3,2});
    3032          70 :   add_tri({0,4,3});
    3033          70 :   add_tri({0,1,4});
    3034          70 :   add_tri({5,4,1});
    3035          70 :   add_tri({5,3,4});
    3036          70 :   add_tri({5,2,3});
    3037          70 :   add_tri({5,1,2});
    3038             : 
    3039          70 :   mesh.prepare_for_use();
    3040          70 : }
    3041             : 
    3042             : 
    3043             : } // namespace libMesh

Generated by: LCOV version 1.14