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

Generated by: LCOV version 1.14