LCOV - code coverage report
Current view: top level - src/mesh - mesh_generation.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4481 (67a8c4) with base cc8438 Lines: 1152 1427 80.7 %
Date: 2026-06-12 15:24:28 Functions: 15 23 65.2 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14