LCOV - code coverage report
Current view: top level - src/mesh - mesh_generation.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4510 (71b108) with base 165cb7 Lines: 1172 1447 81.0 %
Date: 2026-07-31 22:57:43 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    17027647 : unsigned int idx(const ElemType type,
      82             :                  const unsigned int nx,
      83             :                  const unsigned int i,
      84             :                  const unsigned int j)
      85             : {
      86    16598199 :   switch(type)
      87             :     {
      88     3440080 :     case INVALID_ELEM:
      89             :     case QUAD4:
      90             :     case QUADSHELL4:
      91             :     case TRI3:
      92             :     case TRISHELL3:
      93             :       {
      94     3440080 :         return i + j*(nx+1);
      95             :       }
      96             : 
      97    13587567 :     case QUAD8:
      98             :     case QUADSHELL8:
      99             :     case QUAD9:
     100             :     case QUADSHELL9:
     101             :     case TRI6:
     102             :     case TRI7:
     103             :       {
     104    13587567 :         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    45155366 : 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    44061990 :   switch(type)
     126             :     {
     127     8715332 :     case INVALID_ELEM:
     128             :     case HEX8:
     129             :     case PRISM6:
     130             :     case C0POLYHEDRON:
     131             :       {
     132     8715332 :         return i + (nx+1)*(j + k*(ny+1));
     133             :       }
     134             : 
     135    36440034 :     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    36440034 :         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      279998 : 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       15800 :   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      279998 :   mesh.clear();
     340             : 
     341        7900 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     342             : 
     343      279998 :   if (nz != 0)
     344             :     {
     345      129988 :       mesh.set_mesh_dimension(3);
     346      129988 :       mesh.set_spatial_dimension(3);
     347             :     }
     348      150010 :   else if (ny != 0)
     349             :     {
     350      112887 :       mesh.set_mesh_dimension(2);
     351      112887 :       mesh.set_spatial_dimension(2);
     352             :     }
     353       37123 :   else if (nx != 0)
     354             :     {
     355       34851 :       mesh.set_mesh_dimension(1);
     356       34851 :       mesh.set_spatial_dimension(1);
     357             :     }
     358             :   else
     359             :     {
     360             :       // Will we get here?
     361        2272 :       mesh.set_mesh_dimension(0);
     362        2272 :       mesh.set_spatial_dimension(0);
     363             :     }
     364             : 
     365      279998 :   switch (mesh.mesh_dimension())
     366             :     {
     367             :       //---------------------------------------------------------------------
     368             :       // Build a 0D point
     369        2272 :     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        2336 :         mesh.add_point (Point(0, 0, 0), 0);
     379        2272 :         Elem * elem = mesh.add_elem(Elem::build(NODEELEM));
     380        2272 :         elem->set_node(0, mesh.node_ptr(0));
     381             : 
     382        2208 :         break;
     383             :       }
     384             : 
     385             : 
     386             : 
     387             :       //---------------------------------------------------------------------
     388             :       // Build a 1D line
     389       34851 :     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       34851 :           case INVALID_ELEM:
     400             :           case EDGE2:
     401             :           case EDGE3:
     402             :           case EDGE4:
     403             :             {
     404       34851 :               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       11425 :           case INVALID_ELEM:
     416             :           case EDGE2:
     417             :             {
     418       11425 :               mesh.reserve_nodes(nx+1);
     419       11103 :               break;
     420             :             }
     421             : 
     422       21367 :           case EDGE3:
     423             :             {
     424       21367 :               mesh.reserve_nodes(2*nx+1);
     425       20765 :               break;
     426             :             }
     427             : 
     428        2059 :           case EDGE4:
     429             :             {
     430        2059 :               mesh.reserve_nodes(3*nx+1);
     431        2001 :               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      332317 :               for (unsigned int i=0; i<=nx; i++)
     448             :               {
     449      330048 :                 const Node * const node = mesh.add_point (Point(static_cast<Real>(i)/nx, 0, 0), node_id++);
     450      320892 :                 if (i == 0)
     451       11425 :                   boundary_info.add_node(node, 0);
     452      320892 :                 if (i == nx)
     453       11425 :                   boundary_info.add_node(node, 1);
     454             :               }
     455             : 
     456         322 :               break;
     457             :             }
     458             : 
     459         602 :           case EDGE3:
     460             :             {
     461      114222 :               for (unsigned int i=0; i<=2*nx; i++)
     462             :               {
     463       95473 :                 const Node * const node = mesh.add_point (Point(static_cast<Real>(i)/(2*nx), 0, 0), node_id++);
     464       92855 :                 if (i == 0)
     465       21367 :                   boundary_info.add_node(node, 0);
     466       92855 :                 if (i == 2*nx)
     467       21367 :                   boundary_info.add_node(node, 1);
     468             :               }
     469         602 :               break;
     470             :             }
     471             : 
     472          58 :           case EDGE4:
     473             :             {
     474       20519 :               for (unsigned int i=0; i<=3*nx; i++)
     475             :               {
     476       18980 :                 const Node * const node = mesh.add_point (Point(static_cast<Real>(i)/(3*nx), 0, 0), node_id++);
     477       18460 :                 if (i == 0)
     478        2059 :                   boundary_info.add_node(node, 0);
     479       18460 :                 if (i == 3*nx)
     480        2059 :                   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      320892 :               for (unsigned int i=0; i<nx; i++)
     498             :                 {
     499      309467 :                   Elem * elem = mesh.add_elem(Elem::build_with_id(EDGE2, i));
     500      309467 :                   elem->set_node(0, mesh.node_ptr(i));
     501      309467 :                   elem->set_node(1, mesh.node_ptr(i+1));
     502             : 
     503      309467 :                   if (i == 0)
     504       11425 :                     boundary_info.add_side(elem, 0, 0);
     505             : 
     506      309467 :                   if (i == (nx-1))
     507       11425 :                     boundary_info.add_side(elem, 1, 1);
     508             : 
     509             :                 }
     510         322 :               break;
     511             :             }
     512             : 
     513         602 :           case EDGE3:
     514             :             {
     515       57111 :               for (unsigned int i=0; i<nx; i++)
     516             :                 {
     517       35744 :                   Elem * elem = mesh.add_elem(Elem::build_with_id(EDGE3, i));
     518       35744 :                   elem->set_node(0, mesh.node_ptr(2*i));
     519       35744 :                   elem->set_node(2, mesh.node_ptr(2*i+1));
     520       35744 :                   elem->set_node(1, mesh.node_ptr(2*i+2));
     521             : 
     522       35744 :                   if (i == 0)
     523       21367 :                     boundary_info.add_side(elem, 0, 0);
     524             : 
     525       35744 :                   if (i == (nx-1))
     526       21367 :                     boundary_info.add_side(elem, 1, 1);
     527             :                 }
     528         602 :               break;
     529             :             }
     530             : 
     531          58 :           case EDGE4:
     532             :             {
     533        7526 :               for (unsigned int i=0; i<nx; i++)
     534             :                 {
     535        5467 :                   Elem * elem = mesh.add_elem(Elem::build_with_id(EDGE4, i));
     536        5467 :                   elem->set_node(0, mesh.node_ptr(3*i));
     537        5467 :                   elem->set_node(2, mesh.node_ptr(3*i+1));
     538        5467 :                   elem->set_node(3, mesh.node_ptr(3*i+2));
     539        5467 :                   elem->set_node(1, mesh.node_ptr(3*i+3));
     540             : 
     541        5467 :                   if (i == 0)
     542        2059 :                     boundary_info.add_side(elem, 0, 0);
     543             : 
     544        5467 :                   if (i == (nx-1))
     545        2059 :                     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       34851 :         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      500927 :             for (Node * node : mesh.node_ptr_range())
     563      465094 :               (*node)(0) = (*node)(0)*(xmax-xmin) + xmin;
     564             :           }
     565             : 
     566             :         // Add sideset names to boundary info
     567       34851 :         boundary_info.sideset_name(0) = "left";
     568       34851 :         boundary_info.sideset_name(1) = "right";
     569             : 
     570             :         // Add nodeset names to boundary info
     571       34851 :         boundary_info.nodeset_name(0) = "left";
     572       34851 :         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      112887 :     case 2:
     589             :       {
     590        3168 :         libmesh_assert_not_equal_to (nx, 0);
     591        3168 :         libmesh_assert_not_equal_to (ny, 0);
     592        3168 :         libmesh_assert_equal_to (nz, 0);
     593        3168 :         libmesh_assert_less (xmin, xmax);
     594        3168 :         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       57394 :           case INVALID_ELEM:
     601             :           case QUAD4:
     602             :           case QUADSHELL4:
     603             :           case QUAD8:
     604             :           case QUADSHELL8:
     605             :           case QUAD9:
     606             :           case QUADSHELL9:
     607             :             {
     608       57394 :               mesh.reserve_elem (nx*ny);
     609       55782 :               break;
     610             :             }
     611             : 
     612       54854 :           case TRI3:
     613             :           case TRISHELL3:
     614             :           case TRI6:
     615             :           case TRI7:
     616             :             {
     617       54854 :               mesh.reserve_elem (2*nx*ny);
     618       53316 :               break;
     619             :             }
     620             : 
     621         639 :           case C0POLYGON:
     622             :           {
     623         639 :             mesh.reserve_elem ((nx + 1) * (ny + 1));
     624         621 :             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       30154 :           case INVALID_ELEM:
     638             :           case QUAD4:
     639             :           case QUADSHELL4:
     640             :           case TRI3:
     641             :           case TRISHELL3:
     642             :             {
     643       30154 :               mesh.reserve_nodes( (nx+1)*(ny+1) );
     644       29302 :               break;
     645             :             }
     646             : 
     647       60804 :           case QUAD8:
     648             :           case QUADSHELL8:
     649             :           case QUAD9:
     650             :           case QUADSHELL9:
     651             :           case TRI6:
     652             :             {
     653       60804 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1) );
     654       59102 :               break;
     655             :             }
     656             : 
     657       21290 :           case TRI7:
     658             :             {
     659       21290 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1) + 2*nx*ny );
     660       20694 :               break;
     661             :             }
     662         639 :           case C0POLYGON:
     663             :             {
     664         639 :               mesh.reserve_nodes (4 + 3*nx*ny + 2*nx + 2*ny);
     665         621 :               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        3168 :         unsigned int node_id = 0;
     678             :         switch (type)
     679             :           {
     680         852 :           case INVALID_ELEM:
     681             :           case QUAD4:
     682             :           case QUADSHELL4:
     683             :           case TRI3:
     684             :           case TRISHELL3:
     685             :             {
     686      140362 :               for (unsigned int j=0; j<=ny; j++)
     687     1136386 :                 for (unsigned int i=0; i<=nx; i++)
     688             :                 {
     689             :                   const Node * const node =
     690     1995952 :                       mesh.add_point(Point(static_cast<Real>(i) / static_cast<Real>(nx),
     691     1026178 :                                            static_cast<Real>(j) / static_cast<Real>(ny),
     692             :                                            0.),
     693     1016340 :                                      node_id++);
     694     1026178 :                   if (j == 0)
     695      104244 :                     boundary_info.add_node(node, 0);
     696     1026178 :                   if (j == ny)
     697      104244 :                     boundary_info.add_node(node, 2);
     698     1026178 :                   if (i == 0)
     699      110208 :                     boundary_info.add_node(node, 3);
     700     1026178 :                   if (i == nx)
     701      110208 :                     boundary_info.add_node(node, 1);
     702             :                 }
     703             : 
     704         852 :               break;
     705             :             }
     706             : 
     707        2298 :           case QUAD8:
     708             :           case QUADSHELL8:
     709             :           case QUAD9:
     710             :           case QUADSHELL9:
     711             :           case TRI6:
     712             :           case TRI7:
     713             :             {
     714      515426 :               for (unsigned int j=0; j<=(2*ny); j++)
     715     6654386 :                 for (unsigned int i=0; i<=(2*nx); i++)
     716             :                 {
     717             :                   const Node * const node =
     718    12130856 :                       mesh.add_point(Point(static_cast<Real>(i) / static_cast<Real>(2 * nx),
     719     6221054 :                                            static_cast<Real>(j) / static_cast<Real>(2 * ny),
     720             :                                            0),
     721     6144839 :                                      node_id++);
     722     6221054 :                   if (j == 0)
     723      446932 :                     boundary_info.add_node(node, 0);
     724     6221054 :                   if (j == 2*ny)
     725      446932 :                     boundary_info.add_node(node, 2);
     726     6221054 :                   if (i == 0)
     727      433332 :                     boundary_info.add_node(node, 3);
     728     6221054 :                   if (i == 2*nx)
     729      433332 :                     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       82094 :               if (type == TRI7)
     735       58461 :                 for (unsigned int j=0; j<(3*ny); j += 3)
     736      270192 :                   for (unsigned int i=0; i<(3*nx); i += 3)
     737             :                     {
     738             :                       // The bottom-right triangle's center node
     739      455238 :                       mesh.add_point(Point(static_cast<Real>(i+2) / static_cast<Real>(3 * nx),
     740      233021 :                                            static_cast<Real>(j+1) / static_cast<Real>(3 * ny),
     741             :                                            0),
     742      230320 :                                      node_id++);
     743             :                       // The top-left triangle's center node
     744      233021 :                       mesh.add_point(Point(static_cast<Real>(i+1) / static_cast<Real>(3 * nx),
     745      233021 :                                            static_cast<Real>(j+2) / static_cast<Real>(3 * ny),
     746             :                                            0),
     747      230320 :                                      node_id++);
     748             :                     }
     749             : 
     750        2298 :               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        3168 :         unsigned int elem_id = 0;
     770             :         switch (type)
     771             :           {
     772             : 
     773         530 :           case INVALID_ELEM:
     774             :           case QUAD4:
     775             :           case QUADSHELL4:
     776             :             {
     777       81668 :               for (unsigned int j=0; j<ny; j++)
     778      868544 :                 for (unsigned int i=0; i<nx; i++)
     779             :                   {
     780      827713 :                     Elem * elem = mesh.add_elem(Elem::build_with_id(type == INVALID_ELEM ? QUAD4 : type, elem_id++));
     781      805600 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     782      805600 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+1,j)  ));
     783      805600 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     784      805600 :                     elem->set_node(3, mesh.node_ptr(idx(type,nx,i,j+1)  ));
     785             : 
     786      805600 :                     if (j == 0)
     787       56838 :                       boundary_info.add_side(elem, 0, 0);
     788             : 
     789      805600 :                     if (j == (ny-1))
     790       56838 :                       boundary_info.add_side(elem, 2, 2);
     791             : 
     792      805600 :                     if (i == 0)
     793       62944 :                       boundary_info.add_side(elem, 3, 3);
     794             : 
     795      805600 :                     if (i == (nx-1))
     796       62944 :                       boundary_info.add_side(elem, 1, 1);
     797             :                   }
     798         530 :               break;
     799             :             }
     800             : 
     801             : 
     802         322 :           case TRI3:
     803             :           case TRISHELL3:
     804             :             {
     805       28540 :               for (unsigned int j=0; j<ny; j++)
     806       53390 :                 for (unsigned int i=0; i<nx; i++)
     807             :                   {
     808             :                     // Add first Tri3
     809       36280 :                     Elem * elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
     810       36280 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     811       36280 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+1,j)  ));
     812       36280 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     813             : 
     814       36280 :                     if (j == 0)
     815       17252 :                       boundary_info.add_side(elem, 0, 0);
     816             : 
     817       36280 :                     if (i == (nx-1))
     818       17110 :                       boundary_info.add_side(elem, 1, 1);
     819             : 
     820             :                     // Add second Tri3
     821       36280 :                     elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
     822       36280 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     823       36280 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     824       36280 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i,j+1)  ));
     825             : 
     826       36280 :                     if (j == (ny-1))
     827       17252 :                       boundary_info.add_side(elem, 1, 2);
     828             : 
     829       36280 :                     if (i == 0)
     830       17110 :                       boundary_info.add_side(elem, 2, 3);
     831             :                   }
     832         322 :               break;
     833             :             }
     834             : 
     835             : 
     836             : 
     837        1082 :           case QUAD8:
     838             :           case QUADSHELL8:
     839             :           case QUAD9:
     840             :           case QUADSHELL9:
     841             :             {
     842      133644 :               for (unsigned int j=0; j<(2*ny); j += 2)
     843      923231 :                 for (unsigned int i=0; i<(2*nx); i += 2)
     844             :                   {
     845      828257 :                     Elem * elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
     846      828257 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     847      828257 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+2,j)  ));
     848      828257 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i+2,j+2)));
     849      828257 :                     elem->set_node(3, mesh.node_ptr(idx(type,nx,i,j+2)  ));
     850      828257 :                     elem->set_node(4, mesh.node_ptr(idx(type,nx,i+1,j)  ));
     851      828257 :                     elem->set_node(5, mesh.node_ptr(idx(type,nx,i+2,j+1)));
     852      828257 :                     elem->set_node(6, mesh.node_ptr(idx(type,nx,i+1,j+2)));
     853      828257 :                     elem->set_node(7, mesh.node_ptr(idx(type,nx,i,j+1)  ));
     854             : 
     855      828257 :                     if (type == QUAD9 || type == QUADSHELL9)
     856      631943 :                       elem->set_node(8, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     857             : 
     858      828257 :                     if (j == 0)
     859      100583 :                       boundary_info.add_side(elem, 0, 0);
     860             : 
     861      828257 :                     if (j == 2*(ny-1))
     862      100583 :                       boundary_info.add_side(elem, 2, 2);
     863             : 
     864      828257 :                     if (i == 0)
     865       94974 :                       boundary_info.add_side(elem, 3, 3);
     866             : 
     867      828257 :                     if (i == 2*(nx-1))
     868       94974 :                       boundary_info.add_side(elem, 1, 1);
     869             :                   }
     870        1082 :               break;
     871             :             }
     872             : 
     873             : 
     874        1216 :           case TRI6:
     875             :           case TRI7:
     876             :             {
     877      124069 :               for (unsigned int j=0; j<(2*ny); j += 2)
     878      608109 :                 for (unsigned int i=0; i<(2*nx); i += 2)
     879             :                   {
     880             :                     // Add first Tri in the bottom-right of its quad
     881      527464 :                     Elem * elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
     882      527464 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     883      527464 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+2,j)  ));
     884      527464 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i+2,j+2)));
     885      527464 :                     elem->set_node(3, mesh.node_ptr(idx(type,nx,i+1,j)  ));
     886      527464 :                     elem->set_node(4, mesh.node_ptr(idx(type,nx,i+2,j+1)));
     887      527464 :                     elem->set_node(5, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     888             : 
     889      527464 :                     if (type == TRI7)
     890      233021 :                       elem->set_node(6, mesh.node_ptr(elem->id()+(2*nx+1)*(2*ny+1)));
     891             : 
     892      527464 :                     if (j == 0)
     893       81836 :                       boundary_info.add_side(elem, 0, 0);
     894             : 
     895      527464 :                     if (i == 2*(nx-1))
     896       80645 :                       boundary_info.add_side(elem, 1, 1);
     897             : 
     898             :                     // Add second Tri in the top left of its quad
     899      527464 :                     elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
     900      527464 :                     elem->set_node(0, mesh.node_ptr(idx(type,nx,i,j)    ));
     901      527464 :                     elem->set_node(1, mesh.node_ptr(idx(type,nx,i+2,j+2)));
     902      527464 :                     elem->set_node(2, mesh.node_ptr(idx(type,nx,i,j+2)  ));
     903      527464 :                     elem->set_node(3, mesh.node_ptr(idx(type,nx,i+1,j+1)));
     904      527464 :                     elem->set_node(4, mesh.node_ptr(idx(type,nx,i+1,j+2)));
     905      527464 :                     elem->set_node(5, mesh.node_ptr(idx(type,nx,i,j+1)  ));
     906             : 
     907      527464 :                     if (type == TRI7)
     908      233021 :                       elem->set_node(6, mesh.node_ptr(elem->id()+(2*nx+1)*(2*ny+1)));
     909             : 
     910      527464 :                     if (j == 2*(ny-1))
     911       81836 :                       boundary_info.add_side(elem, 1, 2);
     912             : 
     913      527464 :                     if (i == 0)
     914       80645 :                       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         639 :             const auto dx_tri = Real(1) / nx;
     928         639 :             const auto dy_tri = Real(1) / (ny + 1);
     929         621 :             std::unique_ptr<Elem> new_elem;
     930        4544 :             for (const auto i : make_range(nx + 1))
     931             :             {
     932             :               // Make new nodes for bottom layer of triangles
     933             :               Node *node0, *node1, *node2;
     934        3905 :               if (i == 0)
     935             :               {
     936         657 :                 node0 = mesh.add_point(Point(0., 0, 0.));
     937         657 :                 node1 = mesh.add_point(Point(0., dy_tri / 2., 0.));
     938         657 :                 node2 = mesh.add_point(Point(dx_tri / 2., 0., 0.));
     939         639 :                 node_list.push_back(node0);
     940         639 :                 node_list.push_back(node1);
     941         639 :                 node_list.push_back(node2);
     942             :               }
     943        3266 :               else if (i < nx)
     944             :               {
     945        2627 :                 node0 = node_list.back();
     946        2701 :                 node1 = mesh.add_point(Point((i)*dx_tri, dy_tri / 2., 0.));
     947        2701 :                 node2 = mesh.add_point(Point((i + 1. / 2.) * dx_tri, 0., 0.));
     948        2627 :                 node_list.push_back(node1);
     949        2627 :                 node_list.push_back(node2);
     950             :               }
     951             :               else
     952             :               {
     953         639 :                 node0 = node_list.back();
     954         657 :                 node1 = mesh.add_point(Point((i)*dx_tri, dy_tri / 2., 0.));
     955         657 :                 node2 = mesh.add_point(Point((i)*dx_tri, 0., 0.));
     956         639 :                 node_list.push_back(node1);
     957         639 :                 node_list.push_back(node2);
     958             :               }
     959             : 
     960        3905 :               new_elem = std::make_unique<C0Polygon>(3);
     961             :               // Switch to Tri3 when exodus default output supports element type mixes
     962        3905 :               new_elem->set_node(0, node0);
     963        3905 :               new_elem->set_node(1, node1);
     964        3905 :               new_elem->set_node(2, node2);
     965        4015 :               auto * elem = mesh.add_elem(std::move(new_elem));
     966             : 
     967             :               // Set boundaries
     968        3905 :               if (i == 0)
     969         639 :                 boundary_info.add_side(elem, 0, 3); // left
     970        3266 :               else if (i == nx)
     971         639 :                 boundary_info.add_side(elem, 1, 1); // right
     972        3905 :               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         639 :                 (Real(1) - (ny == 1 ?
     980             :                              dy_tri :
     981         639 :                              (Real(1) + (ny - 1) / 2.) * dy_tri)) / ny;
     982        3905 :             for (const auto j : make_range(ny))
     983             :             {
     984       22365 :               for (const auto i : make_range(nx + (j % 2)))
     985             :               {
     986       19099 :                 if ((j % 2 == 0) || ((i > 0) && (i < nx)))
     987             :                 {
     988             :                   Node *n0, *n1, *n2, *n3, *n4, *n5;
     989       16117 :                   n0 = node_list[running_index++];
     990       16117 :                   n1 = node_list[running_index++];
     991       16117 :                   n2 = node_list[running_index];
     992             : 
     993       16117 :                   if (i == 0)
     994             :                   {
     995        1825 :                     n3 = mesh.add_point(Point(*n0) + RealVectorValue(0, hex_side, 0));
     996        1775 :                     node_list.push_back(n3);
     997             :                   }
     998             :                   else
     999       14342 :                     n3 = node_list.back();
    1000             : 
    1001       16571 :                   n4 = mesh.add_point(Point(*n1) + RealVectorValue(0, hex_side + dy_tri, 0));
    1002       16571 :                   n5 = mesh.add_point(Point(*n2) + RealVectorValue(0, hex_side, 0));
    1003       16117 :                   node_list.push_back(n4);
    1004       16117 :                   node_list.push_back(n5);
    1005             : 
    1006       16117 :                   new_elem = std::make_unique<libMesh::C0Polygon>(6);
    1007       16117 :                   new_elem->set_node(0, n0);
    1008       16117 :                   new_elem->set_node(1, n1);
    1009       16117 :                   new_elem->set_node(2, n2);
    1010       16117 :                   new_elem->set_node(3, n5);
    1011       16117 :                   new_elem->set_node(4, n4);
    1012       16117 :                   new_elem->set_node(5, n3);
    1013       16571 :                   auto * elem = mesh.add_elem(std::move(new_elem));
    1014             : 
    1015             :                   // Set boundaries
    1016       16117 :                   if (i == 0)
    1017        1775 :                     boundary_info.add_side(elem, 5, 3); // left
    1018       14342 :                   else if (i == nx)
    1019         908 :                     boundary_info.add_side(elem, 2, 1); // right
    1020       15209 :                 }
    1021             :                 // The hexagons are offset, so we build on a quad on each external side to fill
    1022        2982 :                 else if (i == 0 || i == nx)
    1023             :                 {
    1024             :                   Node *n0, *n1, *n2, *n3;
    1025        2982 :                   n0 = node_list[running_index++];
    1026        2982 :                   n1 = node_list[running_index];
    1027             : 
    1028        2982 :                   if (i == 0)
    1029             :                   {
    1030        1533 :                     n2 = mesh.add_point(Point(*n0) + RealVectorValue(0, hex_side + dy_tri, 0));
    1031        1491 :                     node_list.push_back(n2);
    1032        1533 :                     n3 = mesh.add_point(Point(*n1) + RealVectorValue(0, hex_side, 0));
    1033             :                   }
    1034             :                   else
    1035             :                   {
    1036        1491 :                     n2 = node_list.back();
    1037        1533 :                     n3 = mesh.add_point(Point(*n1) + RealVectorValue(0, hex_side + dy_tri, 0));
    1038             :                   }
    1039        2982 :                   node_list.push_back(n3);
    1040             : 
    1041        2982 :                   new_elem = std::make_unique<C0Polygon>(4);
    1042             :                   // Switch to Quad4 when exodus default output supports element type mixes
    1043        2982 :                   new_elem->set_node(0, n0);
    1044        2982 :                   new_elem->set_node(1, n1);
    1045        2982 :                   new_elem->set_node(3, n2);
    1046        2982 :                   new_elem->set_node(2, n3);
    1047        3066 :                   auto * elem = mesh.add_elem(std::move(new_elem));
    1048             : 
    1049             :                   // Set boundaries
    1050        2982 :                   if (i == 0)
    1051        1491 :                     boundary_info.add_side(elem, 3, 3); // left
    1052        1491 :                   else if (i == nx)
    1053        1575 :                     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        3266 :               running_index++;
    1060             : 
    1061             :               // Skip lower right corner node
    1062        3266 :               if (j == 0)
    1063         639 :                 running_index++;
    1064             :             }
    1065             : 
    1066             :             // Build a final layer of triangles
    1067         639 :             const bool ny_odd = (ny % 2 == 1);
    1068        4189 :             for (const auto i : make_range(nx + ny_odd))
    1069             :             {
    1070             :               // Use existing nodes, except at the corners
    1071             :               Node *node0, *node1, *node2;
    1072        3550 :               if (i == 0 && ny_odd)
    1073             :               {
    1074         292 :                 node0 = mesh.add_point(Point(0., 1., 0.));
    1075         284 :                 node1 = node_list[running_index++];
    1076         292 :                 node2 = node_list[running_index];
    1077             :               }
    1078        3266 :               else if (i < nx)
    1079             :               {
    1080        2982 :                 node0 = node_list[running_index++];
    1081        2982 :                 node1 = node_list[running_index++];
    1082        3066 :                 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         284 :                 node0 = node_list[running_index++];
    1088         284 :                 node1 = node_list[running_index];
    1089         292 :                 node2 = mesh.add_point(Point(1., 1., 0.));
    1090             :               }
    1091             : 
    1092        3550 :               new_elem = std::make_unique<C0Polygon>(3);
    1093             :               // Switch to Tri3 when exodus default output supports element type mixes
    1094        3550 :               new_elem->set_node(0, node0);
    1095        3550 :               new_elem->set_node(1, node1);
    1096        3550 :               new_elem->set_node(2, node2);
    1097        3650 :               auto * elem = mesh.add_elem(std::move(new_elem));
    1098             : 
    1099             :               // Set boundaries
    1100        3550 :               if (i == 0)
    1101         639 :                 boundary_info.add_side(elem, 0, 3); // left
    1102        2911 :               else if (i == nx)
    1103         284 :                 boundary_info.add_side(elem, 1, 1); // right
    1104        3550 :               boundary_info.add_side(elem, 2, 2); // top
    1105             : 
    1106             :             }
    1107          18 :             break;
    1108         603 :           }
    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      112887 :         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     7983379 :             for (Node * node : mesh.node_ptr_range())
    1128             :               {
    1129     7760773 :                 (*node)(0) = ((*node)(0))*(xmax-xmin) + xmin;
    1130     7760773 :                 (*node)(1) = ((*node)(1))*(ymax-ymin) + ymin;
    1131      106551 :               }
    1132             :           }
    1133             : 
    1134             :         // Add sideset names to boundary info
    1135      112887 :         boundary_info.sideset_name(0) = "bottom";
    1136      112887 :         boundary_info.sideset_name(1) = "right";
    1137      112887 :         boundary_info.sideset_name(2) = "top";
    1138      112887 :         boundary_info.sideset_name(3) = "left";
    1139             : 
    1140             :         // Add nodeset names to boundary info
    1141      112887 :         boundary_info.nodeset_name(0) = "bottom";
    1142      112887 :         boundary_info.nodeset_name(1) = "right";
    1143      112887 :         boundary_info.nodeset_name(2) = "top";
    1144      112887 :         boundary_info.nodeset_name(3) = "left";
    1145             : 
    1146        3168 :         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      129988 :     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       81769 :           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       81769 :               mesh.reserve_elem(nx*ny*nz);
    1189       79445 :               break;
    1190             :             }
    1191             : 
    1192       48219 :           case PRISM6:
    1193             :           case PRISM15:
    1194             :           case PRISM18:
    1195             :           case PRISM20:
    1196             :           case PRISM21:
    1197             :             {
    1198       48219 :               mesh.reserve_elem(2*nx*ny*nz);
    1199       46857 :               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       22003 :           case INVALID_ELEM:
    1214             :           case HEX8:
    1215             :           case PRISM6:
    1216             :           case C0POLYHEDRON:
    1217             :             {
    1218             :               const dof_id_type grid_nodes =
    1219       22003 :                 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       22003 :                 (type == C0POLYHEDRON) ?
    1226         355 :                 cast_int<dof_id_type>(nx*ny*nz) : 0;
    1227             : 
    1228       22003 :               mesh.reserve_nodes(grid_nodes + mid_polyhedron_nodes);
    1229       21357 :               break;
    1230             :             }
    1231             : 
    1232       73134 :           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       73134 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1)*(2*nz+1) );
    1248       71072 :               break;
    1249             :             }
    1250             : 
    1251       12202 :           case TET14:
    1252             :             {
    1253       13902 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1)*(2*nz+1) +
    1254       12202 :                                   24*nx*ny*nz +
    1255       12202 :                                   4*(nx*ny + ny*nz + nx*nz) );
    1256       11862 :               break;
    1257             :             }
    1258             : 
    1259       10295 :           case PRISM20:
    1260             :             {
    1261       11165 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1)*(2*nz+1) +
    1262       10295 :                                   2*nx*ny*(nz+1) );
    1263       10005 :               break;
    1264             :             }
    1265             : 
    1266       12354 :           case PRISM21:
    1267             :             {
    1268       13398 :               mesh.reserve_nodes( (2*nx+1)*(2*ny+1)*(2*nz+1) +
    1269       12354 :                                   2*nx*ny*(2*nz+1) );
    1270       12006 :               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       81398 :               for (unsigned int k=0; k<=nz; k++)
    1290      271876 :                 for (unsigned int j=0; j<=ny; j++)
    1291     1887706 :                   for (unsigned int i=0; i<=nx; i++)
    1292             :                   {
    1293             :                     const Node * const node =
    1294     3255414 :                         mesh.add_point(Point(static_cast<Real>(i) / static_cast<Real>(nx),
    1295     1675225 :                                              static_cast<Real>(j) / static_cast<Real>(ny),
    1296     1675225 :                                              static_cast<Real>(k) / static_cast<Real>(nz)),
    1297     1655488 :                                        node_id++);
    1298     1675225 :                     if (k == 0)
    1299      302083 :                       boundary_info.add_node(node, 0);
    1300     1675225 :                     if (k == nz)
    1301      302083 :                       boundary_info.add_node(node, 5);
    1302     1675225 :                     if (j == 0)
    1303      255223 :                       boundary_info.add_node(node, 1);
    1304     1675225 :                     if (j == ny)
    1305      255223 :                       boundary_info.add_node(node, 3);
    1306     1675225 :                     if (i == 0)
    1307      212481 :                       boundary_info.add_node(node, 4);
    1308     1675225 :                     if (i == nx)
    1309      212481 :                       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      521076 :               for (unsigned int k=0; k<=(2*nz); k++)
    1330     2326450 :                 for (unsigned int j=0; j<=(2*ny); j++)
    1331    17273344 :                   for (unsigned int i=0; i<=(2*nx); i++)
    1332             :                   {
    1333             :                     const Node * const node =
    1334    29973906 :                         mesh.add_point(Point(static_cast<Real>(i) / static_cast<Real>(2 * nx),
    1335    15359985 :                                              static_cast<Real>(j) / static_cast<Real>(2 * ny),
    1336    15359985 :                                              static_cast<Real>(k) / static_cast<Real>(2 * nz)),
    1337    15173994 :                                        node_id++);
    1338    15359985 :                     if (k == 0)
    1339     2027851 :                       boundary_info.add_node(node, 0);
    1340    15359985 :                     if (k == 2*nz)
    1341     2027851 :                       boundary_info.add_node(node, 5);
    1342    15359985 :                     if (j == 0)
    1343     1972461 :                       boundary_info.add_node(node, 1);
    1344    15359985 :                     if (j == 2*ny)
    1345     1972461 :                       boundary_info.add_node(node, 3);
    1346    15359985 :                     if (i == 0)
    1347     1913359 :                       boundary_info.add_node(node, 4);
    1348    15359985 :                     if (i == 2*nx)
    1349     1913359 :                       boundary_info.add_node(node, 2);
    1350             :                   }
    1351             : 
    1352      107985 :               if (type == PRISM20 ||
    1353             :                   type == PRISM21)
    1354             :                 {
    1355       22649 :                   const unsigned int kmax = (type == PRISM20) ? nz : 2*nz;
    1356       91377 :                   for (unsigned int k=0; k<=kmax; k++)
    1357      166992 :                     for (unsigned int j=0; j<ny; j++)
    1358      255600 :                       for (unsigned int i=0; i<nx; i++)
    1359             :                         {
    1360             :                           const Node * const node1 =
    1361      305808 :                               mesh.add_point(Point((static_cast<Real>(i)+1/Real(3)) / static_cast<Real>(nx),
    1362      157336 :                                                    (static_cast<Real>(j)+1/Real(3)) / static_cast<Real>(ny),
    1363      157336 :                                                    static_cast<Real>(k) / static_cast<Real>(kmax)),
    1364      155120 :                                              node_id++);
    1365      157336 :                           if (k == 0)
    1366       44801 :                             boundary_info.add_node(node1, 0);
    1367      157336 :                           if (k == kmax)
    1368       44801 :                             boundary_info.add_node(node1, 5);
    1369             : 
    1370             :                           const Node * const node2 =
    1371      305808 :                               mesh.add_point(Point((static_cast<Real>(i)+2/Real(3)) / static_cast<Real>(nx),
    1372      157336 :                                                    (static_cast<Real>(j)+2/Real(3)) / static_cast<Real>(ny),
    1373        4432 :                                                    static_cast<Real>(k) / static_cast<Real>(kmax)),
    1374      155120 :                                              node_id++);
    1375      157336 :                           if (k == 0)
    1376       44801 :                             boundary_info.add_node(node2, 0);
    1377      157336 :                           if (k == kmax)
    1378       44801 :                             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       39728 :               for (unsigned int k=0; k<nz; k++)
    1401      122272 :                 for (unsigned int j=0; j<ny; j++)
    1402     1133649 :                   for (unsigned int i=0; i<nx; i++)
    1403             :                     {
    1404     1037480 :                       Elem * elem = mesh.add_elem(Elem::build_with_id(HEX8, elem_id++));
    1405     1037480 :                       elem->set_node(0, mesh.node_ptr(idx(type,nx,ny,i,j,k)      ));
    1406     1037480 :                       elem->set_node(1, mesh.node_ptr(idx(type,nx,ny,i+1,j,k)    ));
    1407     1037480 :                       elem->set_node(2, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ));
    1408     1037480 :                       elem->set_node(3, mesh.node_ptr(idx(type,nx,ny,i,j+1,k)    ));
    1409     1037480 :                       elem->set_node(4, mesh.node_ptr(idx(type,nx,ny,i,j,k+1)    ));
    1410     1037480 :                       elem->set_node(5, mesh.node_ptr(idx(type,nx,ny,i+1,j,k+1)  ));
    1411     1037480 :                       elem->set_node(6, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)));
    1412     1037480 :                       elem->set_node(7, mesh.node_ptr(idx(type,nx,ny,i,j+1,k+1)  ));
    1413             : 
    1414     1037480 :                       if (k == 0)
    1415      175760 :                         boundary_info.add_side(elem, 0, 0);
    1416             : 
    1417     1037480 :                       if (k == (nz-1))
    1418      175760 :                         boundary_info.add_side(elem, 5, 5);
    1419             : 
    1420     1037480 :                       if (j == 0)
    1421      130320 :                         boundary_info.add_side(elem, 1, 1);
    1422             : 
    1423     1037480 :                       if (j == (ny-1))
    1424      130320 :                         boundary_info.add_side(elem, 3, 3);
    1425             : 
    1426     1037480 :                       if (i == 0)
    1427       96169 :                         boundary_info.add_side(elem, 4, 4);
    1428             : 
    1429     1037480 :                       if (i == (nx-1))
    1430       96169 :                         boundary_info.add_side(elem, 2, 2);
    1431             :                     }
    1432         410 :               break;
    1433             :             }
    1434             : 
    1435             : 
    1436         355 :           case C0POLYHEDRON:
    1437             :             {
    1438         355 :               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        1065 :               for (unsigned int k=0; k<nz; k++)
    1447        2130 :                 for (unsigned int j=0; j<ny; j++)
    1448        4260 :                   for (unsigned int i=0; i<nx; i++)
    1449             :                     {
    1450             :                       std::array<Node *, 8> elem_nodes =
    1451        2840 :                         {{mesh.node_ptr(idx(type,nx,ny,i,j,k)      ),
    1452        2840 :                           mesh.node_ptr(idx(type,nx,ny,i+1,j,k)    ),
    1453        2840 :                           mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ),
    1454        2840 :                           mesh.node_ptr(idx(type,nx,ny,i,j+1,k)    ),
    1455        2840 :                           mesh.node_ptr(idx(type,nx,ny,i,j,k+1)    ),
    1456        2840 :                           mesh.node_ptr(idx(type,nx,ny,i+1,j,k+1)  ),
    1457        2840 :                           mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)),
    1458       19880 :                           mesh.node_ptr(idx(type,nx,ny,i,j+1,k+1)  )}};
    1459             : 
    1460        3000 :                       std::vector<std::shared_ptr<Polygon>> sides(side_nodes.size());
    1461       19880 :                       for (auto s : index_range(side_nodes))
    1462             :                         {
    1463       17520 :                           sides[s] = std::make_shared<C0Polygon>(side_nodes[s].size());
    1464       85200 :                           for (auto n : index_range(side_nodes[s]))
    1465       72000 :                             sides[s]->set_node(n, elem_nodes[side_nodes[s][n]]);
    1466             :                         }
    1467             : 
    1468        2840 :                       std::unique_ptr<Node> mid_elem_node;
    1469             :                       std::unique_ptr<Elem> new_elem =
    1470        2920 :                         std::make_unique<C0Polyhedron>(sides, mid_elem_node);
    1471        2840 :                       if (mid_elem_node)
    1472           0 :                         mesh.add_node(std::move(mid_elem_node));
    1473             : 
    1474        2840 :                       new_elem->set_id() = elem_id++;
    1475        2920 :                       Elem * elem = mesh.add_elem(std::move(new_elem));
    1476             : 
    1477        2840 :                       if (k == 0)
    1478        1420 :                         boundary_info.add_side(elem, 0, 0);
    1479             : 
    1480        2840 :                       if (k == (nz-1))
    1481        1420 :                         boundary_info.add_side(elem, 5, 5);
    1482             : 
    1483        2840 :                       if (j == 0)
    1484        1420 :                         boundary_info.add_side(elem, 1, 1);
    1485             : 
    1486        2840 :                       if (j == (ny-1))
    1487        1420 :                         boundary_info.add_side(elem, 3, 3);
    1488             : 
    1489        2840 :                       if (i == 0)
    1490        1420 :                         boundary_info.add_side(elem, 4, 4);
    1491             : 
    1492        2840 :                       if (i == (nx-1))
    1493        1420 :                         boundary_info.add_side(elem, 2, 2);
    1494        2680 :                     }
    1495          10 :               break;
    1496             :             }
    1497             : 
    1498             : 
    1499             : 
    1500             : 
    1501         226 :           case PRISM6:
    1502             :             {
    1503       18602 :               for (unsigned int k=0; k<nz; k++)
    1504       27264 :                 for (unsigned int j=0; j<ny; j++)
    1505       49416 :                   for (unsigned int i=0; i<nx; i++)
    1506             :                     {
    1507             :                       // First Prism
    1508       32731 :                       Elem * elem = mesh.add_elem(Elem::build_with_id(PRISM6, elem_id++));
    1509       32731 :                       elem->set_node(0, mesh.node_ptr(idx(type,nx,ny,i,j,k)      ));
    1510       32731 :                       elem->set_node(1, mesh.node_ptr(idx(type,nx,ny,i+1,j,k)    ));
    1511       32731 :                       elem->set_node(2, mesh.node_ptr(idx(type,nx,ny,i,j+1,k)    ));
    1512       32731 :                       elem->set_node(3, mesh.node_ptr(idx(type,nx,ny,i,j,k+1)    ));
    1513       32731 :                       elem->set_node(4, mesh.node_ptr(idx(type,nx,ny,i+1,j,k+1)  ));
    1514       32731 :                       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       32731 :                       if (i==0)
    1518       16685 :                         boundary_info.add_side(elem, 3, 4);
    1519             : 
    1520       32731 :                       if (j==0)
    1521       16685 :                         boundary_info.add_side(elem, 1, 1);
    1522             : 
    1523       32731 :                       if (k==0)
    1524       16685 :                         boundary_info.add_side(elem, 0, 0);
    1525             : 
    1526       32731 :                       if (k == (nz-1))
    1527       16685 :                         boundary_info.add_side(elem, 4, 5);
    1528             : 
    1529             :                       // Second Prism
    1530       32731 :                       elem = mesh.add_elem(Elem::build_with_id(PRISM6, elem_id++));
    1531       32731 :                       elem->set_node(0, mesh.node_ptr(idx(type,nx,ny,i+1,j,k)    ));
    1532       32731 :                       elem->set_node(1, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ));
    1533       32731 :                       elem->set_node(2, mesh.node_ptr(idx(type,nx,ny,i,j+1,k)    ));
    1534       32731 :                       elem->set_node(3, mesh.node_ptr(idx(type,nx,ny,i+1,j,k+1)  ));
    1535       32731 :                       elem->set_node(4, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)));
    1536       32731 :                       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       32731 :                       if (i == (nx-1))
    1540       16685 :                         boundary_info.add_side(elem, 1, 2);
    1541             : 
    1542       32731 :                       if (j == (ny-1))
    1543       16685 :                         boundary_info.add_side(elem, 2, 3);
    1544             : 
    1545       32731 :                       if (k==0)
    1546       16685 :                         boundary_info.add_side(elem, 0, 0);
    1547             : 
    1548       32731 :                       if (k == (nz-1))
    1549       16685 :                         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      168700 :               for (unsigned int k=0; k<(2*nz); k += 2)
    1570      324917 :                 for (unsigned int j=0; j<(2*ny); j += 2)
    1571     1426934 :                   for (unsigned int i=0; i<(2*nx); i += 2)
    1572             :                     {
    1573     1202928 :                       ElemType build_type = (type == HEX20) ? HEX20 : HEX27;
    1574     1202928 :                       Elem * elem = mesh.add_elem(Elem::build_with_id(build_type, elem_id++));
    1575             : 
    1576     1202928 :                       elem->set_node(0,  mesh.node_ptr(idx(type,nx,ny,i,  j,  k)  ));
    1577     1202928 :                       elem->set_node(1,  mesh.node_ptr(idx(type,nx,ny,i+2,j,  k)  ));
    1578     1202928 :                       elem->set_node(2,  mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k)  ));
    1579     1202928 :                       elem->set_node(3,  mesh.node_ptr(idx(type,nx,ny,i,  j+2,k)  ));
    1580     1202928 :                       elem->set_node(4,  mesh.node_ptr(idx(type,nx,ny,i,  j,  k+2)));
    1581     1202928 :                       elem->set_node(5,  mesh.node_ptr(idx(type,nx,ny,i+2,j,  k+2)));
    1582     1202928 :                       elem->set_node(6,  mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k+2)));
    1583     1202928 :                       elem->set_node(7,  mesh.node_ptr(idx(type,nx,ny,i,  j+2,k+2)));
    1584     1202928 :                       elem->set_node(8,  mesh.node_ptr(idx(type,nx,ny,i+1,j,  k)  ));
    1585     1202928 :                       elem->set_node(9,  mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k)  ));
    1586     1202928 :                       elem->set_node(10, mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k)  ));
    1587     1202928 :                       elem->set_node(11, mesh.node_ptr(idx(type,nx,ny,i,  j+1,k)  ));
    1588     1202928 :                       elem->set_node(12, mesh.node_ptr(idx(type,nx,ny,i,  j,  k+1)));
    1589     1202928 :                       elem->set_node(13, mesh.node_ptr(idx(type,nx,ny,i+2,j,  k+1)));
    1590     1202928 :                       elem->set_node(14, mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k+1)));
    1591     1202928 :                       elem->set_node(15, mesh.node_ptr(idx(type,nx,ny,i,  j+2,k+1)));
    1592     1202928 :                       elem->set_node(16, mesh.node_ptr(idx(type,nx,ny,i+1,j,  k+2)));
    1593     1202928 :                       elem->set_node(17, mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k+2)));
    1594     1202928 :                       elem->set_node(18, mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k+2)));
    1595     1202928 :                       elem->set_node(19, mesh.node_ptr(idx(type,nx,ny,i,  j+1,k+2)));
    1596             : 
    1597     1202928 :                       if ((type == HEX27) || (type == TET4) || (type == TET10) || (type == TET14) ||
    1598        5426 :                           (type == PYRAMID5) || (type == PYRAMID13) || (type == PYRAMID14) ||
    1599        1870 :                           (type == PYRAMID18))
    1600             :                         {
    1601     1167570 :                           elem->set_node(20, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ));
    1602     1167570 :                           elem->set_node(21, mesh.node_ptr(idx(type,nx,ny,i+1,j,  k+1)));
    1603     1167570 :                           elem->set_node(22, mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k+1)));
    1604     1167570 :                           elem->set_node(23, mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k+1)));
    1605     1167570 :                           elem->set_node(24, mesh.node_ptr(idx(type,nx,ny,i,  j+1,k+1)));
    1606     1167570 :                           elem->set_node(25, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+2)));
    1607     1167570 :                           elem->set_node(26, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)));
    1608             :                         }
    1609             : 
    1610     1202928 :                       if (k == 0)
    1611      250098 :                         boundary_info.add_side(elem, 0, 0);
    1612             : 
    1613     1202928 :                       if (k == 2*(nz-1))
    1614      250098 :                         boundary_info.add_side(elem, 5, 5);
    1615             : 
    1616     1202928 :                       if (j == 0)
    1617      236511 :                         boundary_info.add_side(elem, 1, 1);
    1618             : 
    1619     1202928 :                       if (j == 2*(ny-1))
    1620      236511 :                         boundary_info.add_side(elem, 3, 3);
    1621             : 
    1622     1202928 :                       if (i == 0)
    1623      224006 :                         boundary_info.add_side(elem, 4, 4);
    1624             : 
    1625     1202928 :                       if (i == 2*(nx-1))
    1626      224006 :                         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       91838 :               for (unsigned int k=0; k<(2*nz); k += 2)
    1640      126216 :                 for (unsigned int j=0; j<(2*ny); j += 2)
    1641      195192 :                   for (unsigned int i=0; i<(2*nx); i += 2)
    1642             :                     {
    1643             :                       // First Prism
    1644      120618 :                       Elem * elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
    1645      120618 :                       elem->set_node(0,  mesh.node_ptr(idx(type,nx,ny,i,  j,  k)  ));
    1646      120618 :                       elem->set_node(1,  mesh.node_ptr(idx(type,nx,ny,i+2,j,  k)  ));
    1647      120618 :                       elem->set_node(2,  mesh.node_ptr(idx(type,nx,ny,i,  j+2,k)  ));
    1648      120618 :                       elem->set_node(3,  mesh.node_ptr(idx(type,nx,ny,i,  j,  k+2)));
    1649      120618 :                       elem->set_node(4,  mesh.node_ptr(idx(type,nx,ny,i+2,j,  k+2)));
    1650      120618 :                       elem->set_node(5,  mesh.node_ptr(idx(type,nx,ny,i,  j+2,k+2)));
    1651      120618 :                       elem->set_node(6,  mesh.node_ptr(idx(type,nx,ny,i+1,j,  k)  ));
    1652      120618 :                       elem->set_node(7,  mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ));
    1653      120618 :                       elem->set_node(8,  mesh.node_ptr(idx(type,nx,ny,i,  j+1,k)  ));
    1654      120618 :                       elem->set_node(9,  mesh.node_ptr(idx(type,nx,ny,i,  j,  k+1)));
    1655      120618 :                       elem->set_node(10, mesh.node_ptr(idx(type,nx,ny,i+2,j,  k+1)));
    1656      120618 :                       elem->set_node(11, mesh.node_ptr(idx(type,nx,ny,i,  j+2,k+1)));
    1657      120618 :                       elem->set_node(12, mesh.node_ptr(idx(type,nx,ny,i+1,j,  k+2)));
    1658      120618 :                       elem->set_node(13, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+2)));
    1659      120618 :                       elem->set_node(14, mesh.node_ptr(idx(type,nx,ny,i,  j+1,k+2)));
    1660             : 
    1661      124170 :                       if (type == PRISM18 ||
    1662      118770 :                           type == PRISM20 ||
    1663             :                           type == PRISM21)
    1664             :                         {
    1665       98324 :                           elem->set_node(15, mesh.node_ptr(idx(type,nx,ny,i+1,j,  k+1)));
    1666       98324 :                           elem->set_node(16, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)));
    1667       98324 :                           elem->set_node(17, mesh.node_ptr(idx(type,nx,ny,i,  j+1,k+1)));
    1668             :                         }
    1669             : 
    1670      120618 :                       if (type == PRISM20)
    1671             :                         {
    1672       36139 :                           const dof_id_type base_idx = (2*nx+1)*(2*ny+1)*(2*nz+1);
    1673       36139 :                           elem->set_node(18, mesh.node_ptr(base_idx+((k/2)*(nx*ny)+j/2*nx+i/2)*2));
    1674       36139 :                           elem->set_node(19, mesh.node_ptr(base_idx+(((k/2)+1)*(nx*ny)+j/2*nx+i/2)*2));
    1675             :                         }
    1676             : 
    1677      120618 :                       if (type == PRISM21)
    1678             :                         {
    1679       38198 :                           const dof_id_type base_idx = (2*nx+1)*(2*ny+1)*(2*nz+1);
    1680       38198 :                           elem->set_node(18, mesh.node_ptr(base_idx+(k*(nx*ny)+j/2*nx+i/2)*2));
    1681       38198 :                           elem->set_node(19, mesh.node_ptr(base_idx+((k+2)*(nx*ny)+j/2*nx+i/2)*2));
    1682       38198 :                           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      120618 :                       if (i==0)
    1687       74574 :                         boundary_info.add_side(elem, 3, 4);
    1688             : 
    1689      120618 :                       if (j==0)
    1690       74574 :                         boundary_info.add_side(elem, 1, 1);
    1691             : 
    1692      120618 :                       if (k==0)
    1693       74624 :                         boundary_info.add_side(elem, 0, 0);
    1694             : 
    1695      120618 :                       if (k == 2*(nz-1))
    1696       74624 :                         boundary_info.add_side(elem, 4, 5);
    1697             : 
    1698             : 
    1699             :                       // Second Prism
    1700      120618 :                       elem = mesh.add_elem(Elem::build_with_id(type, elem_id++));
    1701      120618 :                       elem->set_node(0,  mesh.node_ptr(idx(type,nx,ny,i+2,j,k)     ));
    1702      120618 :                       elem->set_node(1,  mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k)   ));
    1703      120618 :                       elem->set_node(2,  mesh.node_ptr(idx(type,nx,ny,i,j+2,k)     ));
    1704      120618 :                       elem->set_node(3,  mesh.node_ptr(idx(type,nx,ny,i+2,j,k+2)   ));
    1705      120618 :                       elem->set_node(4,  mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k+2) ));
    1706      120618 :                       elem->set_node(5,  mesh.node_ptr(idx(type,nx,ny,i,j+2,k+2)   ));
    1707      120618 :                       elem->set_node(6,  mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k)  ));
    1708      120618 :                       elem->set_node(7,  mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k)  ));
    1709      120618 :                       elem->set_node(8,  mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k)  ));
    1710      120618 :                       elem->set_node(9,  mesh.node_ptr(idx(type,nx,ny,i+2,j,k+1)  ));
    1711      120618 :                       elem->set_node(10, mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k+1)));
    1712      120618 :                       elem->set_node(11, mesh.node_ptr(idx(type,nx,ny,i,j+2,k+1)  ));
    1713      120618 :                       elem->set_node(12, mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k+2)));
    1714      120618 :                       elem->set_node(13, mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k+2)));
    1715      120618 :                       elem->set_node(14, mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+2)));
    1716             : 
    1717      120618 :                       if (type == PRISM18 ||
    1718       60492 :                           type == PRISM20 ||
    1719             :                           type == PRISM21)
    1720             :                         {
    1721       98324 :                           elem->set_node(15,  mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k+1)));
    1722       98324 :                           elem->set_node(16,  mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k+1)));
    1723       98324 :                           elem->set_node(17,  mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)));
    1724             :                         }
    1725             : 
    1726      120618 :                       if (type == PRISM20)
    1727             :                         {
    1728       36139 :                           const dof_id_type base_idx = (2*nx+1)*(2*ny+1)*(2*nz+1);
    1729       36139 :                           elem->set_node(18, mesh.node_ptr(base_idx+((k/2)*(nx*ny)+j/2*nx+i/2)*2+1));
    1730       36139 :                           elem->set_node(19, mesh.node_ptr(base_idx+(((k/2)+1)*(nx*ny)+j/2*nx+i/2)*2+1));
    1731             :                         }
    1732             : 
    1733      120618 :                       if (type == PRISM21)
    1734             :                         {
    1735       38198 :                           const dof_id_type base_idx = (2*nx+1)*(2*ny+1)*(2*nz+1);
    1736       38198 :                           elem->set_node(18, mesh.node_ptr(base_idx+(k*(nx*ny)+j/2*nx+i/2)*2+1));
    1737       38198 :                           elem->set_node(19, mesh.node_ptr(base_idx+((k+2)*(nx*ny)+j/2*nx+i/2)*2+1));
    1738       38198 :                           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      120618 :                       if (i == 2*(nx-1))
    1743       74574 :                         boundary_info.add_side(elem, 1, 2);
    1744             : 
    1745      120618 :                       if (j == 2*(ny-1))
    1746       74574 :                         boundary_info.add_side(elem, 2, 3);
    1747             : 
    1748      120618 :                       if (k==0)
    1749       74624 :                         boundary_info.add_side(elem, 0, 0);
    1750             : 
    1751      120618 :                       if (k == 2*(nz-1))
    1752       74624 :                         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      129988 :         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    17606172 :             for (Node * node : mesh.node_ptr_range())
    1781             :               {
    1782    17349882 :                 (*node)(0) = ((*node)(0))*(xmax-xmin) + xmin;
    1783    17349882 :                 (*node)(1) = ((*node)(1))*(ymax-ymin) + ymin;
    1784    17349882 :                 (*node)(2) = ((*node)(2))*(zmax-zmin) + zmin;
    1785      122616 :               }
    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      133674 :         if ((type == TET4) ||
    1798      129488 :             (type == TET10) ||
    1799      129148 :             (type == TET14) ||
    1800      100024 :             (type == PYRAMID5) ||
    1801        2692 :             (type == PYRAMID13) ||
    1802       97251 :             (type == PYRAMID14) ||
    1803       91943 :             (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       40536 :             std::unique_ptr<Elem> side;
    1810             : 
    1811       40536 :             if ((type == TET4) || (type == TET10) || (type == TET14))
    1812       29886 :               new_elements.reserve(24*mesh.n_elem());
    1813             :             else
    1814       10650 :               new_elements.reserve(6*mesh.n_elem());
    1815             : 
    1816             :             // Create tetrahedra or pyramids
    1817      548874 :             for (auto & base_hex : mesh.element_ptr_range())
    1818             :               {
    1819             :                 // Get a pointer to the node located at the HEX27 center
    1820      240789 :                 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     1685523 :                 for (auto s : base_hex->side_index_range())
    1826             :                   {
    1827             :                     // Get the boundary ID(s) for this side
    1828     1444734 :                     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     1444734 :                     boundary_id_type b_id = ids.empty() ? BoundaryInfo::invalid_id : ids[0];
    1835             : 
    1836             :                     // Need to build the full-ordered side!
    1837     1444734 :                     base_hex->build_side_ptr(side, s);
    1838             : 
    1839     1444734 :                     if ((type == TET4) || (type == TET10) || (type == TET14))
    1840             :                       {
    1841             :                         // Build 4 sub-tets per side
    1842     5010600 :                         for (unsigned int sub_tet=0; sub_tet<4; ++sub_tet)
    1843             :                           {
    1844     7915200 :                             new_elements.push_back( Elem::build(TET4) );
    1845      101760 :                             auto & sub_elem = new_elements.back();
    1846     4212000 :                             sub_elem->set_node(0, side->node_ptr(sub_tet));
    1847     4212000 :                             sub_elem->set_node(1, side->node_ptr(8));                           // center of the face
    1848     4110240 :                             sub_elem->set_node(2, side->node_ptr(sub_tet==3 ? 0 : sub_tet+1 )); // wrap-around
    1849     4008480 :                             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     4008480 :                             if (b_id != BoundaryInfo::invalid_id)
    1855     1726312 :                               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      872760 :                         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      467550 :                         sub_elem->set_node(0, side->node_ptr(0));
    1869      467550 :                         sub_elem->set_node(1, side->node_ptr(3));
    1870      467550 :                         sub_elem->set_node(2, side->node_ptr(2));
    1871      467550 :                         sub_elem->set_node(3, side->node_ptr(1));
    1872             : 
    1873             :                         // Set the apex
    1874      442614 :                         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      442614 :                         if (b_id != BoundaryInfo::invalid_id)
    1879      222066 :                           boundary_info.add_side(sub_elem.get(), 4, b_id);
    1880             :                       } // end else type==PYRAMID*
    1881             :                   }
    1882       38256 :               }
    1883             : 
    1884             : 
    1885             :             // Delete the original HEX27 elements from the mesh, and the boundary info structure.
    1886      548874 :             for (auto & elem : mesh.element_ptr_range())
    1887             :               {
    1888      240789 :                 boundary_info.remove(elem); // Safe even if elem has no boundary info.
    1889      240789 :                 mesh.delete_elem(elem);
    1890       38256 :               }
    1891             : 
    1892             :             // Add the new elements
    1893     4491630 :             for (auto i : index_range(new_elements))
    1894             :               {
    1895      399798 :                 new_elements[i]->set_id(i);
    1896     4679550 :                 mesh.add_elem( std::move(new_elements[i]) );
    1897             :               }
    1898             : 
    1899       38256 :           } // 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      129988 :         if ((type == TET10) || (type == PYRAMID14))
    1904       11791 :           mesh.all_second_order();
    1905             : 
    1906      118197 :         else if (type == PYRAMID13)
    1907        2698 :           mesh.all_second_order(/*full_ordered=*/false);
    1908             : 
    1909      115499 :         else if ((type == TET14) || (type == PYRAMID18))
    1910       14687 :           mesh.all_complete_order();
    1911             : 
    1912             : 
    1913             :         // Add sideset names to boundary info (Z axis out of the screen)
    1914      129988 :         boundary_info.sideset_name(0) = "back";
    1915      129988 :         boundary_info.sideset_name(1) = "bottom";
    1916      129988 :         boundary_info.sideset_name(2) = "right";
    1917      129988 :         boundary_info.sideset_name(3) = "top";
    1918      129988 :         boundary_info.sideset_name(4) = "left";
    1919      129988 :         boundary_info.sideset_name(5) = "front";
    1920             : 
    1921             :         // Add nodeset names to boundary info
    1922      129988 :         boundary_info.nodeset_name(0) = "back";
    1923      129988 :         boundary_info.nodeset_name(1) = "bottom";
    1924      129988 :         boundary_info.nodeset_name(2) = "right";
    1925      129988 :         boundary_info.nodeset_name(3) = "top";
    1926      129988 :         boundary_info.nodeset_name(4) = "left";
    1927      129988 :         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      279998 :   mesh.prepare_for_use ();
    1938      279998 : }
    1939             : 
    1940             : 
    1941             : 
    1942         994 : 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         994 :   build_cube(mesh,
    1951             :              0, 0, 0,
    1952             :              0., 0.,
    1953             :              0., 0.,
    1954             :              0., 0.,
    1955             :              type,
    1956             :              gauss_lobatto_grid);
    1957         994 : }
    1958             : 
    1959             : 
    1960        4819 : 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        4819 :   build_cube(mesh,
    1971             :              nx, 0, 0,
    1972             :              xmin, xmax,
    1973             :              0., 0.,
    1974             :              0., 0.,
    1975             :              type,
    1976             :              gauss_lobatto_grid);
    1977        4819 : }
    1978             : 
    1979             : 
    1980             : 
    1981       21582 : 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       21582 :   build_cube (mesh,
    1995             :               nx, ny, 0,
    1996             :               xmin, xmax,
    1997             :               ymin, ymax,
    1998             :               0., 0.,
    1999             :               type,
    2000             :               gauss_lobatto_grid);
    2001       21582 : }
    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        2281 : 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        2281 :     cast_int<unsigned char>(mesh.mesh_dimension());
    2042        2281 :   mesh.clear();
    2043        2281 :   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        2281 :   if (mesh.mesh_dimension() == 1)
    2052             :     {
    2053        2281 :       switch (type)
    2054             :       {
    2055         857 :       case HEX8:
    2056             :       case HEX27:
    2057             :       case TET4:
    2058             :       case TET10:
    2059             :       case TET14:
    2060         857 :         mesh.set_mesh_dimension(3);
    2061          26 :         break;
    2062        1140 :       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        1140 :         mesh.set_mesh_dimension(2);
    2072          34 :         break;
    2073         284 :       case EDGE2:
    2074             :       case EDGE3:
    2075             :       case EDGE4:
    2076         284 :         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        2281 :   const bool is_replicated = mesh.is_replicated();
    2090             : 
    2091             :   // Sphere is centered at origin by default
    2092          68 :   const Point cent;
    2093             : 
    2094        4562 :   const Sphere sphere (cent, rad);
    2095             : 
    2096        2281 :   switch (mesh.mesh_dimension())
    2097             :     {
    2098             :       //-----------------------------------------------------------------
    2099             :       // Build a line in one dimension
    2100         284 :     case 1:
    2101             :       {
    2102         284 :         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        1140 :     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        1140 :         if (flat)
    2121             :           {
    2122          34 :             const Real sqrt_2     = std::sqrt(2.);
    2123        1140 :             const Real rad_2      = .25*rad;
    2124        1140 :             const Real rad_sqrt_2 = rad/sqrt_2;
    2125             : 
    2126             :             // (Temporary) convenient storage for node pointers
    2127        1174 :             std::vector<Node *> nodes(8);
    2128             : 
    2129             :             // Point 0
    2130        1174 :             nodes[0] = mesh.add_point (Point(-rad_2,-rad_2, 0.), node_id++);
    2131             : 
    2132             :             // Point 1
    2133        1174 :             nodes[1] = mesh.add_point (Point( rad_2,-rad_2, 0.), node_id++);
    2134             : 
    2135             :             // Point 2
    2136        1174 :             nodes[2] = mesh.add_point (Point( rad_2, rad_2, 0.), node_id++);
    2137             : 
    2138             :             // Point 3
    2139        1174 :             nodes[3] = mesh.add_point (Point(-rad_2, rad_2, 0.), node_id++);
    2140             : 
    2141             :             // Point 4
    2142        1174 :             nodes[4] = mesh.add_point (Point(-rad_sqrt_2,-rad_sqrt_2, 0.), node_id++);
    2143             : 
    2144             :             // Point 5
    2145        1174 :             nodes[5] = mesh.add_point (Point( rad_sqrt_2,-rad_sqrt_2, 0.), node_id++);
    2146             : 
    2147             :             // Point 6
    2148        1174 :             nodes[6] = mesh.add_point (Point( rad_sqrt_2, rad_sqrt_2, 0.), node_id++);
    2149             : 
    2150             :             // Point 7
    2151        1174 :             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        1140 :               Elem * elem0 = mesh.add_elem (Elem::build(QUAD4));
    2158        1140 :               elem0->set_node(0, nodes[0]);
    2159        1140 :               elem0->set_node(1, nodes[1]);
    2160        1140 :               elem0->set_node(2, nodes[2]);
    2161        1140 :               elem0->set_node(3, nodes[3]);
    2162             :             }
    2163             : 
    2164             :             // Element 1
    2165             :             {
    2166        1140 :               Elem * elem1 = mesh.add_elem (Elem::build(QUAD4));
    2167        1140 :               elem1->set_node(0, nodes[4]);
    2168        1140 :               elem1->set_node(1, nodes[0]);
    2169        1140 :               elem1->set_node(2, nodes[3]);
    2170        1140 :               elem1->set_node(3, nodes[7]);
    2171             :             }
    2172             : 
    2173             :             // Element 2
    2174             :             {
    2175        1140 :               Elem * elem2 = mesh.add_elem (Elem::build(QUAD4));
    2176        1140 :               elem2->set_node(0, nodes[4]);
    2177        1140 :               elem2->set_node(1, nodes[5]);
    2178        1140 :               elem2->set_node(2, nodes[1]);
    2179        1140 :               elem2->set_node(3, nodes[0]);
    2180             :             }
    2181             : 
    2182             :             // Element 3
    2183             :             {
    2184        1140 :               Elem * elem3 = mesh.add_elem (Elem::build(QUAD4));
    2185        1140 :               elem3->set_node(0, nodes[1]);
    2186        1140 :               elem3->set_node(1, nodes[5]);
    2187        1140 :               elem3->set_node(2, nodes[6]);
    2188        1140 :               elem3->set_node(3, nodes[2]);
    2189             :             }
    2190             : 
    2191             :             // Element 4
    2192             :             {
    2193        1140 :               Elem * elem4 = mesh.add_elem (Elem::build(QUAD4));
    2194        1140 :               elem4->set_node(0, nodes[3]);
    2195        1140 :               elem4->set_node(1, nodes[2]);
    2196        1140 :               elem4->set_node(2, nodes[6]);
    2197        1140 :               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         857 :     case 3:
    2266             :       {
    2267             :         // (Currently) supported types
    2268         857 :         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         857 :           r_small = 0.25*rad,                      //  0.25 *radius
    2278         857 :           r_med   = (0.125*std::sqrt(2.)+0.5)*rad; // .67677*radius
    2279             : 
    2280             :         // (Temporary) convenient storage for node pointers
    2281         883 :         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         883 :         nodes[0] = mesh.add_point (Point(-r_small,-r_small, -r_small), node_id++);
    2291         883 :         nodes[1] = mesh.add_point (Point( r_small,-r_small, -r_small), node_id++);
    2292         883 :         nodes[2] = mesh.add_point (Point( r_small, r_small, -r_small), node_id++);
    2293         883 :         nodes[3] = mesh.add_point (Point(-r_small, r_small, -r_small), node_id++);
    2294         883 :         nodes[4] = mesh.add_point (Point(-r_small,-r_small,  r_small), node_id++);
    2295         883 :         nodes[5] = mesh.add_point (Point( r_small,-r_small,  r_small), node_id++);
    2296         883 :         nodes[6] = mesh.add_point (Point( r_small, r_small,  r_small), node_id++);
    2297         883 :         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         883 :         nodes[8]  = mesh.add_point (Point(-r_med,-r_med, -r_med), node_id++);
    2301         883 :         nodes[9]  = mesh.add_point (Point( r_med,-r_med, -r_med), node_id++);
    2302         883 :         nodes[10] = mesh.add_point (Point( r_med, r_med, -r_med), node_id++);
    2303         883 :         nodes[11] = mesh.add_point (Point(-r_med, r_med, -r_med), node_id++);
    2304         883 :         nodes[12] = mesh.add_point (Point(-r_med,-r_med,  r_med), node_id++);
    2305         883 :         nodes[13] = mesh.add_point (Point( r_med,-r_med,  r_med), node_id++);
    2306         883 :         nodes[14] = mesh.add_point (Point( r_med, r_med,  r_med), node_id++);
    2307         883 :         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         857 :           Elem * elem0 = mesh.add_elem(Elem::build(HEX8));
    2313         857 :           elem0->set_node(0, nodes[0]);
    2314         857 :           elem0->set_node(1, nodes[1]);
    2315         857 :           elem0->set_node(2, nodes[2]);
    2316         857 :           elem0->set_node(3, nodes[3]);
    2317         857 :           elem0->set_node(4, nodes[4]);
    2318         857 :           elem0->set_node(5, nodes[5]);
    2319         857 :           elem0->set_node(6, nodes[6]);
    2320         857 :           elem0->set_node(7, nodes[7]);
    2321             :         }
    2322             : 
    2323             :         // Element 1 - "bottom"
    2324             :         {
    2325         857 :           Elem * elem1 = mesh.add_elem(Elem::build(HEX8));
    2326         857 :           elem1->set_node(0, nodes[8]);
    2327         857 :           elem1->set_node(1, nodes[9]);
    2328         857 :           elem1->set_node(2, nodes[10]);
    2329         857 :           elem1->set_node(3, nodes[11]);
    2330         857 :           elem1->set_node(4, nodes[0]);
    2331         857 :           elem1->set_node(5, nodes[1]);
    2332         857 :           elem1->set_node(6, nodes[2]);
    2333         857 :           elem1->set_node(7, nodes[3]);
    2334             :         }
    2335             : 
    2336             :         // Element 2 - "front"
    2337             :         {
    2338         857 :           Elem * elem2 = mesh.add_elem(Elem::build(HEX8));
    2339         857 :           elem2->set_node(0, nodes[8]);
    2340         857 :           elem2->set_node(1, nodes[9]);
    2341         857 :           elem2->set_node(2, nodes[1]);
    2342         857 :           elem2->set_node(3, nodes[0]);
    2343         857 :           elem2->set_node(4, nodes[12]);
    2344         857 :           elem2->set_node(5, nodes[13]);
    2345         857 :           elem2->set_node(6, nodes[5]);
    2346         857 :           elem2->set_node(7, nodes[4]);
    2347             :         }
    2348             : 
    2349             :         // Element 3 - "right"
    2350             :         {
    2351         857 :           Elem * elem3 = mesh.add_elem(Elem::build(HEX8));
    2352         857 :           elem3->set_node(0, nodes[1]);
    2353         857 :           elem3->set_node(1, nodes[9]);
    2354         857 :           elem3->set_node(2, nodes[10]);
    2355         857 :           elem3->set_node(3, nodes[2]);
    2356         857 :           elem3->set_node(4, nodes[5]);
    2357         857 :           elem3->set_node(5, nodes[13]);
    2358         857 :           elem3->set_node(6, nodes[14]);
    2359         857 :           elem3->set_node(7, nodes[6]);
    2360             :         }
    2361             : 
    2362             :         // Element 4 - "back"
    2363             :         {
    2364         857 :           Elem * elem4 = mesh.add_elem(Elem::build(HEX8));
    2365         857 :           elem4->set_node(0, nodes[3]);
    2366         857 :           elem4->set_node(1, nodes[2]);
    2367         857 :           elem4->set_node(2, nodes[10]);
    2368         857 :           elem4->set_node(3, nodes[11]);
    2369         857 :           elem4->set_node(4, nodes[7]);
    2370         857 :           elem4->set_node(5, nodes[6]);
    2371         857 :           elem4->set_node(6, nodes[14]);
    2372         857 :           elem4->set_node(7, nodes[15]);
    2373             :         }
    2374             : 
    2375             :         // Element 5 - "left"
    2376             :         {
    2377         857 :           Elem * elem5 = mesh.add_elem(Elem::build(HEX8));
    2378         857 :           elem5->set_node(0, nodes[8]);
    2379         857 :           elem5->set_node(1, nodes[0]);
    2380         857 :           elem5->set_node(2, nodes[3]);
    2381         857 :           elem5->set_node(3, nodes[11]);
    2382         857 :           elem5->set_node(4, nodes[12]);
    2383         857 :           elem5->set_node(5, nodes[4]);
    2384         857 :           elem5->set_node(6, nodes[7]);
    2385         857 :           elem5->set_node(7, nodes[15]);
    2386             :         }
    2387             : 
    2388             :         // Element 6 - "top"
    2389             :         {
    2390         857 :           Elem * elem6 = mesh.add_elem(Elem::build(HEX8));
    2391         857 :           elem6->set_node(0, nodes[4]);
    2392         857 :           elem6->set_node(1, nodes[5]);
    2393         857 :           elem6->set_node(2, nodes[6]);
    2394         857 :           elem6->set_node(3, nodes[7]);
    2395         857 :           elem6->set_node(4, nodes[12]);
    2396         857 :           elem6->set_node(5, nodes[13]);
    2397         857 :           elem6->set_node(6, nodes[14]);
    2398         857 :           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        4562 :   MeshRefinement mesh_refinement (mesh);
    2415             : 
    2416             :   // For avoiding extraneous element side construction
    2417        2281 :   std::unique_ptr<Elem> side;
    2418             : 
    2419             :   // Loop over the elements, refine, pop nodes to boundary.
    2420        5490 :   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        3209 :       if (!is_replicated)
    2426        2196 :         mesh.prepare_for_use();
    2427             : 
    2428        3209 :       mesh_refinement.uniformly_refine(1);
    2429             : 
    2430             :       const bool move_only_boundary_nodes =
    2431        3209 :         mesh.mesh_dimension() != 2 || flat;
    2432             :       MeshTools::Modification::interpolate_surface
    2433        6418 :         (mesh, sphere, /*ids=*/{}, move_only_boundary_nodes);
    2434             :     }
    2435             : 
    2436             :   // A DistributedMesh needs a little prep before flattening
    2437        2281 :   if (!is_replicated)
    2438        1706 :     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        2281 :   MeshTools::Modification::flatten(mesh);
    2445             : 
    2446             :   // Convert all the tensor product elements to simplices if requested
    2447        2281 :   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         497 :       if (is_replicated)
    2452         106 :         mesh.prepare_for_use();
    2453             : 
    2454         497 :       MeshTools::Modification::all_tri(mesh);
    2455             :     }
    2456             : 
    2457             :   // Convert to second-order elements if the user requested it.
    2458        2349 :   if (Elem::build(type)->default_order() != FIRST)
    2459             :     {
    2460        1571 :       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        1571 :           bool full_ordered = !((type==QUAD8) || (type==HEX20));
    2469        1571 :           mesh.all_second_order(full_ordered);
    2470             :         }
    2471             : 
    2472             :       // And pop to the boundary again...
    2473      176670 :       for (const auto & elem : mesh.active_element_ptr_range())
    2474      612961 :         for (auto s : elem->side_index_range())
    2475      545469 :           if (elem->neighbor_ptr(s) == nullptr)
    2476             :             {
    2477       20319 :               elem->build_side_ptr(side, s);
    2478             : 
    2479             :               // Pop each point to the sphere boundary
    2480      183824 :               for (auto n : side->node_index_range())
    2481      173327 :                 side->point(n) =
    2482      173327 :                   sphere.closest_point(side->point(n));
    2483        1475 :             }
    2484             :     }
    2485             : 
    2486             : 
    2487             :   // The meshes could probably use some smoothing.
    2488        2281 :   if (mesh.mesh_dimension() > 1)
    2489             :     {
    2490        2057 :       LaplaceMeshSmoother smoother(mesh, n_smooth);
    2491        1997 :       smoother.smooth();
    2492             :     }
    2493             : 
    2494             :   // We'll give the whole sphere surface a boundary id of 0
    2495      810930 :   for (const auto & elem : mesh.active_element_ptr_range())
    2496     2343660 :     for (auto s : elem->side_index_range())
    2497     1988314 :       if (!elem->neighbor_ptr(s))
    2498       53135 :         boundary_info.add_side(elem, s, 0);
    2499             : 
    2500             :   // Done building the mesh.  Now prepare it for use.
    2501        2281 :   mesh.prepare_for_use();
    2502        2281 : }
    2503             : 
    2504             : #endif // #ifndef LIBMESH_ENABLE_AMR
    2505             : 
    2506             : 
    2507             : // Meshes the tensor product of a 1D and a 1D-or-2D domain.
    2508         497 : 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         497 :   if (!cross_section.n_elem())
    2517           0 :     return;
    2518             : 
    2519         497 :   dof_id_type orig_elem = cross_section.n_elem();
    2520         497 :   dof_id_type orig_nodes = cross_section.n_nodes();
    2521             : 
    2522             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    2523         497 :   unique_id_type orig_unique_ids = cross_section.parallel_max_unique_id();
    2524             : #endif
    2525             : 
    2526         497 :   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         497 :   if (!cross_section.is_serial())
    2540         384 :     mesh.delete_remote_elements();
    2541             : 
    2542             :   // We know a priori how many elements we'll need
    2543         497 :   mesh.reserve_elem(nz*orig_elem);
    2544             : 
    2545             :   // For straightforward meshes we need one or two additional layers per
    2546             :   // element.
    2547        1843 :   if (cross_section.elements_begin() != cross_section.elements_end() &&
    2548        1225 :       (*cross_section.elements_begin())->default_order() == SECOND)
    2549         111 :     order = 2;
    2550         497 :   mesh.comm().max(order);
    2551             : 
    2552         497 :   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       15888 :   for (const auto & node : cross_section.node_ptr_range())
    2558             :     {
    2559       43092 :       for (unsigned int k=0; k != order*nz+1; ++k)
    2560             :         {
    2561       35296 :           const dof_id_type new_node_id = node->id() + k * orig_nodes;
    2562       35296 :           Node * my_node = mesh.query_node_ptr(new_node_id);
    2563       35296 :           if (!my_node)
    2564             :             {
    2565             :               std::unique_ptr<Node> new_node = Node::build
    2566       36814 :                 (*node + (extrusion_vector * k / nz / order),
    2567        1518 :                  new_node_id);
    2568       35296 :               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       35296 :               const unique_id_type uid = (k == 0) ?
    2575         684 :                 node->unique_id() :
    2576       27500 :                 orig_unique_ids + (k-1)*(orig_nodes + orig_elem) + node->id();
    2577             : 
    2578        1518 :               new_node->set_unique_id(uid);
    2579             : #endif
    2580             : 
    2581       35296 :               cross_section_boundary_info.boundary_ids(node, ids_to_copy);
    2582       35296 :               boundary_info.add_node(new_node.get(), ids_to_copy);
    2583             : 
    2584       38332 :               mesh.add_node(std::move(new_node));
    2585       32260 :             }
    2586             :         }
    2587         469 :     }
    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         497 :     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         497 :   cross_section.comm().max(next_side_id);
    2599             : 
    2600        6566 :   for (const auto & elem : cross_section.element_ptr_range())
    2601             :     {
    2602        2923 :       const ElemType etype = elem->type();
    2603             : 
    2604             :       // build_extrusion currently only works on coarse meshes
    2605         130 :       libmesh_assert (!elem->parent());
    2606             : 
    2607       10181 :       for (unsigned int k=0; k != nz; ++k)
    2608             :         {
    2609        6982 :           std::unique_ptr<Elem> new_elem;
    2610        7258 :           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         860 :             case TRI3:
    2648             :               {
    2649        1624 :                 new_elem = Elem::build(PRISM6);
    2650         908 :                 new_elem->set_node(0, mesh.node_ptr(elem->node_ptr(0)->id() + (k * orig_nodes)));
    2651         908 :                 new_elem->set_node(1, mesh.node_ptr(elem->node_ptr(1)->id() + (k * orig_nodes)));
    2652         908 :                 new_elem->set_node(2, mesh.node_ptr(elem->node_ptr(2)->id() + (k * orig_nodes)));
    2653         908 :                 new_elem->set_node(3, mesh.node_ptr(elem->node_ptr(0)->id() + ((k+1) * orig_nodes)));
    2654         908 :                 new_elem->set_node(4, mesh.node_ptr(elem->node_ptr(1)->id() + ((k+1) * orig_nodes)));
    2655         908 :                 new_elem->set_node(5, mesh.node_ptr(elem->node_ptr(2)->id() + ((k+1) * orig_nodes)));
    2656             : 
    2657         908 :                 if (elem->neighbor_ptr(0) == remote_elem)
    2658           0 :                   new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
    2659         908 :                 if (elem->neighbor_ptr(1) == remote_elem)
    2660           0 :                   new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
    2661         908 :                 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        4544 :             case QUAD4:
    2733             :               {
    2734        8832 :                 new_elem = Elem::build(HEX8);
    2735        4672 :                 new_elem->set_node(0, mesh.node_ptr(elem->node_ptr(0)->id() + (k * orig_nodes)));
    2736        4672 :                 new_elem->set_node(1, mesh.node_ptr(elem->node_ptr(1)->id() + (k * orig_nodes)));
    2737        4672 :                 new_elem->set_node(2, mesh.node_ptr(elem->node_ptr(2)->id() + (k * orig_nodes)));
    2738        4672 :                 new_elem->set_node(3, mesh.node_ptr(elem->node_ptr(3)->id() + (k * orig_nodes)));
    2739        4672 :                 new_elem->set_node(4, mesh.node_ptr(elem->node_ptr(0)->id() + ((k+1) * orig_nodes)));
    2740        4672 :                 new_elem->set_node(5, mesh.node_ptr(elem->node_ptr(1)->id() + ((k+1) * orig_nodes)));
    2741        4672 :                 new_elem->set_node(6, mesh.node_ptr(elem->node_ptr(2)->id() + ((k+1) * orig_nodes)));
    2742        4672 :                 new_elem->set_node(7, mesh.node_ptr(elem->node_ptr(3)->id() + ((k+1) * orig_nodes)));
    2743             : 
    2744        4672 :                 if (elem->neighbor_ptr(0) == remote_elem)
    2745           0 :                   new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
    2746        4672 :                 if (elem->neighbor_ptr(1) == remote_elem)
    2747           0 :                   new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
    2748        4672 :                 if (elem->neighbor_ptr(2) == remote_elem)
    2749           0 :                   new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
    2750        4672 :                 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        1854 :             case QUAD9:
    2756             :               {
    2757        3508 :                 new_elem = Elem::build(HEX27);
    2758        1954 :                 new_elem->set_node(0, mesh.node_ptr(elem->node_ptr(0)->id() + (2*k * orig_nodes)));
    2759        1954 :                 new_elem->set_node(1, mesh.node_ptr(elem->node_ptr(1)->id() + (2*k * orig_nodes)));
    2760        1954 :                 new_elem->set_node(2, mesh.node_ptr(elem->node_ptr(2)->id() + (2*k * orig_nodes)));
    2761        1954 :                 new_elem->set_node(3, mesh.node_ptr(elem->node_ptr(3)->id() + (2*k * orig_nodes)));
    2762        1954 :                 new_elem->set_node(4, mesh.node_ptr(elem->node_ptr(0)->id() + ((2*k+2) * orig_nodes)));
    2763        1954 :                 new_elem->set_node(5, mesh.node_ptr(elem->node_ptr(1)->id() + ((2*k+2) * orig_nodes)));
    2764        1954 :                 new_elem->set_node(6, mesh.node_ptr(elem->node_ptr(2)->id() + ((2*k+2) * orig_nodes)));
    2765        1954 :                 new_elem->set_node(7, mesh.node_ptr(elem->node_ptr(3)->id() + ((2*k+2) * orig_nodes)));
    2766        1954 :                 new_elem->set_node(8, mesh.node_ptr(elem->node_ptr(4)->id() + (2*k * orig_nodes)));
    2767        1954 :                 new_elem->set_node(9, mesh.node_ptr(elem->node_ptr(5)->id() + (2*k * orig_nodes)));
    2768        1954 :                 new_elem->set_node(10, mesh.node_ptr(elem->node_ptr(6)->id() + (2*k * orig_nodes)));
    2769        1954 :                 new_elem->set_node(11, mesh.node_ptr(elem->node_ptr(7)->id() + (2*k * orig_nodes)));
    2770        1954 :                 new_elem->set_node(12, mesh.node_ptr(elem->node_ptr(0)->id() + ((2*k+1) * orig_nodes)));
    2771        1954 :                 new_elem->set_node(13, mesh.node_ptr(elem->node_ptr(1)->id() + ((2*k+1) * orig_nodes)));
    2772        1954 :                 new_elem->set_node(14, mesh.node_ptr(elem->node_ptr(2)->id() + ((2*k+1) * orig_nodes)));
    2773        1954 :                 new_elem->set_node(15, mesh.node_ptr(elem->node_ptr(3)->id() + ((2*k+1) * orig_nodes)));
    2774        1954 :                 new_elem->set_node(16, mesh.node_ptr(elem->node_ptr(4)->id() + ((2*k+2) * orig_nodes)));
    2775        1954 :                 new_elem->set_node(17, mesh.node_ptr(elem->node_ptr(5)->id() + ((2*k+2) * orig_nodes)));
    2776        1954 :                 new_elem->set_node(18, mesh.node_ptr(elem->node_ptr(6)->id() + ((2*k+2) * orig_nodes)));
    2777        1954 :                 new_elem->set_node(19, mesh.node_ptr(elem->node_ptr(7)->id() + ((2*k+2) * orig_nodes)));
    2778        1954 :                 new_elem->set_node(20, mesh.node_ptr(elem->node_ptr(8)->id() + (2*k * orig_nodes)));
    2779        1954 :                 new_elem->set_node(21, mesh.node_ptr(elem->node_ptr(4)->id() + ((2*k+1) * orig_nodes)));
    2780        1954 :                 new_elem->set_node(22, mesh.node_ptr(elem->node_ptr(5)->id() + ((2*k+1) * orig_nodes)));
    2781        1954 :                 new_elem->set_node(23, mesh.node_ptr(elem->node_ptr(6)->id() + ((2*k+1) * orig_nodes)));
    2782        1954 :                 new_elem->set_node(24, mesh.node_ptr(elem->node_ptr(7)->id() + ((2*k+1) * orig_nodes)));
    2783        1954 :                 new_elem->set_node(25, mesh.node_ptr(elem->node_ptr(8)->id() + ((2*k+2) * orig_nodes)));
    2784        1954 :                 new_elem->set_node(26, mesh.node_ptr(elem->node_ptr(8)->id() + ((2*k+1) * orig_nodes)));
    2785             : 
    2786        1954 :                 if (elem->neighbor_ptr(0) == remote_elem)
    2787           0 :                   new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
    2788        1954 :                 if (elem->neighbor_ptr(1) == remote_elem)
    2789           0 :                   new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
    2790        1954 :                 if (elem->neighbor_ptr(2) == remote_elem)
    2791           0 :                   new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
    2792        1954 :                 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        7258 :           new_elem->set_id(elem->id() + (k * orig_elem));
    2805        7258 :           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        7258 :           const unique_id_type uid = (k == 0) ?
    2812         260 :             elem->unique_id() :
    2813        4335 :             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        7258 :           if (!elem_subdomain)
    2819             :             // maintain the subdomain_id
    2820        2714 :             new_elem->subdomain_id() = elem->subdomain_id();
    2821             :           else
    2822             :             // Allow the user to choose new subdomain_ids
    2823        4544 :             new_elem->subdomain_id() = elem_subdomain->get_subdomain_for_layer(elem, k);
    2824             : 
    2825        7534 :           Elem * added_elem = mesh.add_elem(std::move(new_elem));
    2826             : 
    2827             :           // Copy any old boundary ids on all sides
    2828       35706 :           for (auto s : elem->side_index_range())
    2829             :             {
    2830       28172 :               cross_section_boundary_info.boundary_ids(elem, s, ids_to_copy);
    2831             : 
    2832       28172 :               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       28172 :                   boundary_info.add_side(added_elem,
    2839       27116 :                                          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        7258 :           if (k == 0)
    2856        2923 :             boundary_info.add_side(added_elem, 0, next_side_id);
    2857        7258 :           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        2923 :               const unsigned short top_id = added_elem->dim() == 3 ?
    2863        2923 :                 cast_int<unsigned short>(elem->n_sides()+1) : 2;
    2864             :               boundary_info.add_side
    2865        2923 :                 (added_elem, top_id,
    2866        2923 :                  cast_int<boundary_id_type>(next_side_id+1));
    2867             :             }
    2868        6706 :         }
    2869         469 :     }
    2870             : 
    2871             :   // Done building the mesh.  Now prepare it for use.
    2872         497 :   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         378 : 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         378 :   const Real xavg = (xmin + xmax)/2;
    3002         378 :   const Real yavg = (ymin + ymax)/2;
    3003         378 :   const Real zavg = (zmin + zmax)/2;
    3004         390 :   mesh.add_point(Point(xavg,yavg,zmin), 0);
    3005         390 :   mesh.add_point(Point(xmax,yavg,zavg), 1);
    3006         390 :   mesh.add_point(Point(xavg,ymax,zavg), 2);
    3007         390 :   mesh.add_point(Point(xmin,yavg,zavg), 3);
    3008         390 :   mesh.add_point(Point(xavg,ymin,zavg), 4);
    3009         390 :   mesh.add_point(Point(xavg,yavg,zmax), 5);
    3010             : 
    3011       17024 :   auto add_tri = [&mesh, flip_tris](std::array<dof_id_type,3> nodes)
    3012             :   {
    3013        3024 :     auto elem = mesh.add_elem(Elem::build(TRI3));
    3014        3024 :     elem->set_node(0, mesh.node_ptr(nodes[0]));
    3015        3024 :     elem->set_node(1, mesh.node_ptr(nodes[1]));
    3016        3024 :     elem->set_node(2, mesh.node_ptr(nodes[2]));
    3017        3024 :     if (flip_tris)
    3018        1168 :       elem->flip(&mesh.get_boundary_info());
    3019        3036 :   };
    3020             : 
    3021         378 :   add_tri({0,2,1});
    3022         378 :   add_tri({0,3,2});
    3023         378 :   add_tri({0,4,3});
    3024         378 :   add_tri({0,1,4});
    3025         378 :   add_tri({5,4,1});
    3026         378 :   add_tri({5,3,4});
    3027         378 :   add_tri({5,2,3});
    3028         378 :   add_tri({5,1,2});
    3029             : 
    3030         378 :   mesh.prepare_for_use();
    3031         378 : }
    3032             : 
    3033             : 
    3034             : } // namespace libMesh

Generated by: LCOV version 1.14