LCOV - code coverage report
Current view: top level - src/meshgenerators - AdvancedExtruderGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 415 698 59.5 %
Date: 2025-07-17 01:28:37 Functions: 3 3 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #include "AdvancedExtruderGenerator.h"
      11             : #include "MooseUtils.h"
      12             : #include "MooseMeshUtils.h"
      13             : 
      14             : #include "libmesh/boundary_info.h"
      15             : #include "libmesh/function_base.h"
      16             : #include "libmesh/cell_prism6.h"
      17             : #include "libmesh/cell_prism18.h"
      18             : #include "libmesh/cell_prism21.h"
      19             : #include "libmesh/cell_hex8.h"
      20             : #include "libmesh/cell_hex20.h"
      21             : #include "libmesh/cell_hex27.h"
      22             : #include "libmesh/edge_edge2.h"
      23             : #include "libmesh/edge_edge3.h"
      24             : #include "libmesh/edge_edge4.h"
      25             : #include "libmesh/face_quad4.h"
      26             : #include "libmesh/face_quad8.h"
      27             : #include "libmesh/face_quad9.h"
      28             : #include "libmesh/face_tri3.h"
      29             : #include "libmesh/face_tri6.h"
      30             : #include "libmesh/face_tri7.h"
      31             : #include "libmesh/libmesh_logging.h"
      32             : #include "libmesh/mesh_communication.h"
      33             : #include "libmesh/mesh_modification.h"
      34             : #include "libmesh/mesh_tools.h"
      35             : #include "libmesh/parallel.h"
      36             : #include "libmesh/remote_elem.h"
      37             : #include "libmesh/string_to_enum.h"
      38             : #include "libmesh/unstructured_mesh.h"
      39             : #include "libmesh/point.h"
      40             : 
      41             : #include <numeric>
      42             : 
      43             : registerMooseObject("MooseApp", AdvancedExtruderGenerator);
      44             : registerMooseObjectRenamed("MooseApp",
      45             :                            FancyExtruderGenerator,
      46             :                            "02/18/2023 24:00",
      47             :                            AdvancedExtruderGenerator);
      48             : 
      49             : InputParameters
      50       28920 : AdvancedExtruderGenerator::validParams()
      51             : {
      52       28920 :   InputParameters params = MeshGenerator::validParams();
      53             : 
      54       28920 :   params.addRequiredParam<MeshGeneratorName>("input", "The mesh to extrude");
      55             : 
      56       28920 :   params.addClassDescription(
      57             :       "Extrudes a 1D mesh into 2D, or a 2D mesh into 3D, can have a variable height for each "
      58             :       "elevation, variable number of layers within each elevation, variable growth factors of "
      59             :       "axial element sizes within each elevation and remap subdomain_ids, boundary_ids and element "
      60             :       "extra integers within each elevation as well as interface boundaries between neighboring "
      61             :       "elevation layers.");
      62             : 
      63       28920 :   params.addRequiredParam<std::vector<Real>>("heights", "The height of each elevation");
      64             : 
      65       28920 :   params.addRangeCheckedParam<std::vector<Real>>(
      66             :       "biases", "biases>0.0", "The axial growth factor used for mesh biasing for each elevation.");
      67             : 
      68       28920 :   params.addRequiredParam<std::vector<unsigned int>>(
      69             :       "num_layers", "The number of layers for each elevation - must be num_elevations in length!");
      70             : 
      71       28920 :   params.addParam<std::vector<std::vector<subdomain_id_type>>>(
      72             :       "subdomain_swaps",
      73             :       {},
      74             :       "For each row, every two entries are interpreted as a pair of "
      75             :       "'from' and 'to' to remap the subdomains for that elevation");
      76             : 
      77       28920 :   params.addParam<std::vector<std::vector<boundary_id_type>>>(
      78             :       "boundary_swaps",
      79             :       {},
      80             :       "For each row, every two entries are interpreted as a pair of "
      81             :       "'from' and 'to' to remap the boundaries for that elevation");
      82             : 
      83       28920 :   params.addParam<std::vector<std::string>>(
      84             :       "elem_integer_names_to_swap",
      85             :       {},
      86             :       "Array of element extra integer names that need to be swapped during extrusion.");
      87             : 
      88       28920 :   params.addParam<std::vector<std::vector<std::vector<dof_id_type>>>>(
      89             :       "elem_integers_swaps",
      90             :       {},
      91             :       "For each row, every two entries are interpreted as a pair of 'from' and 'to' to remap the "
      92             :       "element extra integer for that elevation. If multiple element extra integers need to be "
      93             :       "swapped, the enties are stacked based on the order provided in "
      94             :       "'elem_integer_names_to_swap' to form the third dimension.");
      95             : 
      96       28920 :   params.addRequiredParam<Point>(
      97             :       "direction",
      98             :       "A vector that points in the direction to extrude (note, this will be "
      99             :       "normalized internally - so don't worry about it here)");
     100             : 
     101       28920 :   params.addParam<BoundaryName>(
     102             :       "top_boundary",
     103             :       "The boundary name to set on the top boundary. If omitted an ID will be generated.");
     104             : 
     105       28920 :   params.addParam<BoundaryName>(
     106             :       "bottom_boundary",
     107             :       "The boundary name to set on the bottom boundary. If omitted an ID will be generated.");
     108             : 
     109       28920 :   params.addParam<std::vector<std::vector<subdomain_id_type>>>(
     110             :       "upward_boundary_source_blocks", "Block ids used to generate upward interface boundaries.");
     111             : 
     112       28920 :   params.addParam<std::vector<std::vector<boundary_id_type>>>("upward_boundary_ids",
     113             :                                                               "Upward interface boundary ids.");
     114             : 
     115       28920 :   params.addParam<std::vector<std::vector<subdomain_id_type>>>(
     116             :       "downward_boundary_source_blocks",
     117             :       "Block ids used to generate downward interface boundaries.");
     118             : 
     119       28920 :   params.addParam<std::vector<std::vector<boundary_id_type>>>("downward_boundary_ids",
     120             :                                                               "Downward interface boundary ids.");
     121       28920 :   params.addParamNamesToGroup(
     122             :       "top_boundary bottom_boundary upward_boundary_source_blocks upward_boundary_ids "
     123             :       "downward_boundary_source_blocks downward_boundary_ids",
     124             :       "Boundary Assignment");
     125       28920 :   params.addParamNamesToGroup(
     126             :       "subdomain_swaps boundary_swaps elem_integer_names_to_swap elem_integers_swaps", "ID Swap");
     127       86760 :   params.addParam<Real>("twist_pitch",
     128       57840 :                         0,
     129             :                         "Pitch for helicoidal extrusion around an axis going through the origin "
     130             :                         "following the direction vector");
     131       28920 :   return params;
     132           0 : }
     133             : 
     134         197 : AdvancedExtruderGenerator::AdvancedExtruderGenerator(const InputParameters & parameters)
     135             :   : MeshGenerator(parameters),
     136         197 :     _input(getMesh("input")),
     137         197 :     _heights(getParam<std::vector<Real>>("heights")),
     138         581 :     _biases(isParamValid("biases") ? getParam<std::vector<Real>>("biases")
     139         384 :                                    : std::vector<Real>(_heights.size(), 1.0)),
     140         197 :     _num_layers(getParam<std::vector<unsigned int>>("num_layers")),
     141         197 :     _subdomain_swaps(getParam<std::vector<std::vector<subdomain_id_type>>>("subdomain_swaps")),
     142         197 :     _boundary_swaps(getParam<std::vector<std::vector<boundary_id_type>>>("boundary_swaps")),
     143         197 :     _elem_integer_names_to_swap(getParam<std::vector<std::string>>("elem_integer_names_to_swap")),
     144         197 :     _elem_integers_swaps(
     145         197 :         getParam<std::vector<std::vector<std::vector<dof_id_type>>>>("elem_integers_swaps")),
     146         197 :     _direction(getParam<Point>("direction")),
     147         197 :     _has_top_boundary(isParamValid("top_boundary")),
     148         197 :     _top_boundary(isParamValid("top_boundary") ? getParam<BoundaryName>("top_boundary") : "0"),
     149         197 :     _has_bottom_boundary(isParamValid("bottom_boundary")),
     150         197 :     _bottom_boundary(isParamValid("bottom_boundary") ? getParam<BoundaryName>("bottom_boundary")
     151             :                                                      : "0"),
     152         577 :     _upward_boundary_source_blocks(
     153         394 :         isParamValid("upward_boundary_source_blocks")
     154         197 :             ? getParam<std::vector<std::vector<subdomain_id_type>>>("upward_boundary_source_blocks")
     155         183 :             : std::vector<std::vector<subdomain_id_type>>(_heights.size(),
     156         380 :                                                           std::vector<subdomain_id_type>())),
     157         577 :     _upward_boundary_ids(
     158         394 :         isParamValid("upward_boundary_ids")
     159         197 :             ? getParam<std::vector<std::vector<boundary_id_type>>>("upward_boundary_ids")
     160         183 :             : std::vector<std::vector<boundary_id_type>>(_heights.size(),
     161         380 :                                                          std::vector<boundary_id_type>())),
     162         774 :     _downward_boundary_source_blocks(isParamValid("downward_boundary_source_blocks")
     163         197 :                                          ? getParam<std::vector<std::vector<subdomain_id_type>>>(
     164             :                                                "downward_boundary_source_blocks")
     165             :                                          : std::vector<std::vector<subdomain_id_type>>(
     166         380 :                                                _heights.size(), std::vector<subdomain_id_type>())),
     167         577 :     _downward_boundary_ids(
     168         394 :         isParamValid("downward_boundary_ids")
     169         197 :             ? getParam<std::vector<std::vector<boundary_id_type>>>("downward_boundary_ids")
     170         183 :             : std::vector<std::vector<boundary_id_type>>(_heights.size(),
     171         380 :                                                          std::vector<boundary_id_type>())),
     172         788 :     _twist_pitch(getParam<Real>("twist_pitch"))
     173             : {
     174         197 :   if (!_direction.norm())
     175           0 :     paramError("direction", "Must have some length!");
     176             : 
     177             :   // Normalize it
     178         197 :   _direction /= _direction.norm();
     179             : 
     180         197 :   const auto num_elevations = _heights.size();
     181             : 
     182         197 :   if (_num_layers.size() != num_elevations)
     183           0 :     paramError("heights", "The length of 'heights' and 'num_layers' must be the same in ", name());
     184             : 
     185         197 :   if (_subdomain_swaps.size() && (_subdomain_swaps.size() != num_elevations))
     186           0 :     paramError("subdomain_swaps",
     187           0 :                "If specified, 'subdomain_swaps' (" + std::to_string(_subdomain_swaps.size()) +
     188           0 :                    ") must be the same length as 'heights' (" + std::to_string(num_elevations) +
     189             :                    ") in ",
     190           0 :                name());
     191             : 
     192             :   try
     193             :   {
     194         197 :     MooseMeshUtils::idSwapParametersProcessor(
     195         197 :         name(), "subdomain_swaps", _subdomain_swaps, _subdomain_swap_pairs);
     196             :   }
     197           0 :   catch (const MooseException & e)
     198             :   {
     199           0 :     paramError("subdomain_swaps", e.what());
     200           0 :   }
     201             : 
     202         197 :   if (_boundary_swaps.size() && (_boundary_swaps.size() != num_elevations))
     203           0 :     paramError("boundary_swaps",
     204           0 :                "If specified, 'boundary_swaps' (" + std::to_string(_boundary_swaps.size()) +
     205           0 :                    ") must be the same length as 'heights' (" + std::to_string(num_elevations) +
     206             :                    ") in ",
     207           0 :                name());
     208             : 
     209             :   try
     210             :   {
     211         197 :     MooseMeshUtils::idSwapParametersProcessor(
     212         197 :         name(), "boundary_swaps", _boundary_swaps, _boundary_swap_pairs);
     213             :   }
     214           0 :   catch (const MooseException & e)
     215             :   {
     216           0 :     paramError("boundary_swaps", e.what());
     217           0 :   }
     218             : 
     219         209 :   if (_elem_integers_swaps.size() &&
     220          12 :       _elem_integers_swaps.size() != _elem_integer_names_to_swap.size())
     221           0 :     paramError("elem_integers_swaps",
     222             :                "If specified, 'elem_integers_swaps' must have the same length as the length of "
     223             :                "'elem_integer_names_to_swap'.");
     224             : 
     225         221 :   for (const auto & unit_elem_integers_swaps : _elem_integers_swaps)
     226          24 :     if (unit_elem_integers_swaps.size() != num_elevations)
     227           0 :       paramError("elem_integers_swaps",
     228             :                  "If specified, each element of 'elem_integers_swaps' must have the same length as "
     229             :                  "the length of 'heights'.");
     230             : 
     231             :   try
     232             :   {
     233         197 :     MooseMeshUtils::extraElemIntegerSwapParametersProcessor(name(),
     234             :                                                             num_elevations,
     235         197 :                                                             _elem_integer_names_to_swap.size(),
     236             :                                                             _elem_integers_swaps,
     237         197 :                                                             _elem_integers_swap_pairs);
     238             :   }
     239           0 :   catch (const MooseException & e)
     240             :   {
     241           0 :     paramError("elem_integers_swaps", e.what());
     242           0 :   }
     243             : 
     244         197 :   bool has_negative_entry = false;
     245         197 :   bool has_positive_entry = false;
     246         672 :   for (const auto & h : _heights)
     247             :   {
     248         475 :     if (h > 0.0)
     249         471 :       has_positive_entry = true;
     250             :     else
     251           4 :       has_negative_entry = true;
     252             :   }
     253             : 
     254         197 :   if (has_negative_entry && has_positive_entry)
     255           4 :     paramError("heights", "Cannot have both positive and negative heights!");
     256         193 :   if (_biases.size() != _heights.size())
     257           0 :     paramError("biases", "Size of this parameter, if provided, must be the same as heights.");
     258             : 
     259         386 :   if (_upward_boundary_source_blocks.size() != _upward_boundary_ids.size() ||
     260         193 :       _upward_boundary_ids.size() != num_elevations)
     261           0 :     paramError("upward_boundary_ids",
     262           0 :                "This parameter must have the same length (" +
     263           0 :                    std::to_string(_upward_boundary_ids.size()) +
     264           0 :                    ") as upward_boundary_source_blocks (" +
     265           0 :                    std::to_string(_upward_boundary_source_blocks.size()) + ") and heights (" +
     266           0 :                    std::to_string(num_elevations) + ")");
     267         656 :   for (unsigned int i = 0; i < _upward_boundary_source_blocks.size(); i++)
     268         463 :     if (_upward_boundary_source_blocks[i].size() != _upward_boundary_ids[i].size())
     269           0 :       paramError("upward_boundary_ids",
     270             :                  "Every element of this parameter must have the same length as the corresponding "
     271             :                  "element of upward_boundary_source_blocks.");
     272             : 
     273         386 :   if (_downward_boundary_source_blocks.size() != _downward_boundary_ids.size() ||
     274         193 :       _downward_boundary_ids.size() != num_elevations)
     275           0 :     paramError("downward_boundary_ids",
     276           0 :                "This parameter must have the same length (" +
     277           0 :                    std::to_string(_downward_boundary_ids.size()) +
     278           0 :                    ") as downward_boundary_source_blocks (" +
     279           0 :                    std::to_string(_downward_boundary_source_blocks.size()) + ") and heights (" +
     280           0 :                    std::to_string(num_elevations) + ")");
     281         656 :   for (unsigned int i = 0; i < _downward_boundary_source_blocks.size(); i++)
     282         463 :     if (_downward_boundary_source_blocks[i].size() != _downward_boundary_ids[i].size())
     283           0 :       paramError("downward_boundary_ids",
     284             :                  "Every element of this parameter must have the same length as the corresponding "
     285             :                  "element of downward_boundary_source_blocks.");
     286         193 : }
     287             : 
     288             : std::unique_ptr<MeshBase>
     289         183 : AdvancedExtruderGenerator::generate()
     290             : {
     291             :   // Note: bulk of this code originally from libmesh mesh_modification.C
     292             :   // Original copyright: Copyright (C) 2002-2019 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
     293             :   // Original license is LGPL so it can be used here.
     294             : 
     295         183 :   auto mesh = buildMeshBaseObject(_input->mesh_dimension() + 1);
     296         183 :   mesh->set_mesh_dimension(_input->mesh_dimension() + 1);
     297             : 
     298             :   // Check if the element integer names are existent in the input mesh.
     299         205 :   for (unsigned int i = 0; i < _elem_integer_names_to_swap.size(); i++)
     300          22 :     if (_input->has_elem_integer(_elem_integer_names_to_swap[i]))
     301          44 :       _elem_integer_indices_to_swap.push_back(
     302          22 :           _input->get_elem_integer_index(_elem_integer_names_to_swap[i]));
     303             :     else
     304           0 :       paramError("elem_integer_names_to_swap",
     305             :                  "Element ",
     306             :                  i + 1,
     307             :                  " of 'elem_integer_names_to_swap' in is not a valid extra element integer of the "
     308             :                  "input mesh.");
     309             : 
     310             :   // prepare for transferring extra element integers from original mesh to the extruded mesh.
     311         183 :   const unsigned int num_extra_elem_integers = _input->n_elem_integers();
     312         183 :   std::vector<std::string> id_names;
     313             : 
     314         205 :   for (unsigned int i = 0; i < num_extra_elem_integers; i++)
     315             :   {
     316          22 :     id_names.push_back(_input->get_elem_integer_name(i));
     317          22 :     if (!mesh->has_elem_integer(id_names[i]))
     318          22 :       mesh->add_elem_integer(id_names[i]);
     319             :   }
     320             : 
     321             :   // retrieve subdomain/sideset/nodeset name maps
     322         183 :   const auto & input_subdomain_map = _input->get_subdomain_name_map();
     323         183 :   const auto & input_sideset_map = _input->get_boundary_info().get_sideset_name_map();
     324         183 :   const auto & input_nodeset_map = _input->get_boundary_info().get_nodeset_name_map();
     325             : 
     326             :   // Check that the swaps source blocks are present in the mesh
     327         447 :   for (const auto & swap : _subdomain_swaps)
     328        1216 :     for (const auto i : index_range(swap))
     329         952 :       if (i % 2 == 0 && !MooseMeshUtils::hasSubdomainID(*_input, swap[i]))
     330           4 :         paramError("subdomain_swaps", "The block '", swap[i], "' was not found within the mesh");
     331             : 
     332             :   // Check that the swaps source boundaries are present in the mesh
     333         203 :   for (const auto & swap : _boundary_swaps)
     334         156 :     for (const auto i : index_range(swap))
     335         132 :       if (i % 2 == 0 && !MooseMeshUtils::hasBoundaryID(*_input, swap[i]))
     336           4 :         paramError("boundary_swaps", "The boundary '", swap[i], "' was not found within the mesh");
     337             : 
     338             :   // Check that the source blocks for layer top/bottom boundaries exist in the mesh
     339         574 :   for (const auto & layer_vec : _upward_boundary_source_blocks)
     340         463 :     for (const auto bid : layer_vec)
     341          64 :       if (!MooseMeshUtils::hasSubdomainID(*_input, bid))
     342           4 :         paramError(
     343             :             "upward_boundary_source_blocks", "The block '", bid, "' was not found within the mesh");
     344         558 :   for (const auto & layer_vec : _downward_boundary_source_blocks)
     345         451 :     for (const auto bid : layer_vec)
     346          64 :       if (!MooseMeshUtils::hasSubdomainID(*_input, bid))
     347           4 :         paramError("downward_boundary_source_blocks",
     348             :                    "The block '",
     349             :                    bid,
     350             :                    "' was not found within the mesh");
     351             : 
     352         167 :   std::unique_ptr<MeshBase> input = std::move(_input);
     353             : 
     354             :   // If we're using a distributed mesh... then make sure we don't have any remote elements hanging
     355             :   // around
     356         167 :   if (!input->is_serial())
     357          25 :     mesh->delete_remote_elements();
     358             : 
     359         167 :   unsigned int total_num_layers = std::accumulate(_num_layers.begin(), _num_layers.end(), 0);
     360             : 
     361         167 :   auto total_num_elevations = _heights.size();
     362             : 
     363         167 :   dof_id_type orig_elem = input->n_elem();
     364         167 :   dof_id_type orig_nodes = input->n_nodes();
     365             : 
     366             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
     367         167 :   unique_id_type orig_unique_ids = input->parallel_max_unique_id();
     368             : #endif
     369             : 
     370         167 :   unsigned int order = 1;
     371             : 
     372         167 :   BoundaryInfo & boundary_info = mesh->get_boundary_info();
     373         167 :   const BoundaryInfo & input_boundary_info = input->get_boundary_info();
     374             : 
     375             :   // Determine boundary IDs for the new user provided boundary names
     376         167 :   std::vector<BoundaryName> new_boundary_names;
     377         167 :   if (_has_bottom_boundary)
     378          92 :     new_boundary_names.push_back(_bottom_boundary);
     379         167 :   if (_has_top_boundary)
     380          92 :     new_boundary_names.push_back(_top_boundary);
     381             :   std::vector<boundary_id_type> new_boundary_ids =
     382         167 :       MooseMeshUtils::getBoundaryIDs(*input, new_boundary_names, true);
     383             :   const auto user_bottom_boundary_id =
     384         167 :       _has_bottom_boundary ? new_boundary_ids.front() : libMesh::BoundaryInfo::invalid_id;
     385             :   const auto user_top_boundary_id =
     386         167 :       _has_top_boundary ? new_boundary_ids.back() : libMesh::BoundaryInfo::invalid_id;
     387             : 
     388             :   // We know a priori how many elements we'll need
     389         167 :   mesh->reserve_elem(total_num_layers * orig_elem);
     390             : 
     391             :   // Look for higher order elements which introduce an extra layer
     392         167 :   std::set<ElemType> higher_orders = {EDGE3, EDGE4, TRI6, TRI7, QUAD8, QUAD9};
     393         167 :   bool extruding_quad_eights = false;
     394         167 :   std::vector<ElemType> types;
     395         167 :   MeshTools::elem_types(*input, types);
     396         359 :   for (const auto elem_type : types)
     397             :   {
     398         192 :     if (higher_orders.count(elem_type))
     399           8 :       order = 2;
     400         192 :     if (elem_type == QUAD8)
     401           8 :       extruding_quad_eights = true;
     402             :   }
     403         167 :   mesh->comm().max(order);
     404         167 :   mesh->comm().max(extruding_quad_eights);
     405             : 
     406             :   // Reserve for the max number possibly needed
     407         167 :   mesh->reserve_nodes((order * total_num_layers + 1) * orig_nodes);
     408             : 
     409             :   // Container to catch the boundary IDs handed back by the BoundaryInfo object
     410         167 :   std::vector<boundary_id_type> ids_to_copy;
     411             : 
     412         167 :   Point old_distance;
     413         167 :   Point current_distance;
     414             : 
     415             :   // Create translated layers of nodes in the direction of extrusion
     416       11276 :   for (const auto & node : input->node_ptr_range())
     417             :   {
     418       11109 :     unsigned int current_node_layer = 0;
     419             : 
     420       11109 :     old_distance.zero();
     421             : 
     422             :     // e is the elevation layer ordering
     423       33214 :     for (unsigned int e = 0; e < total_num_elevations; e++)
     424             :     {
     425       22105 :       auto num_layers = _num_layers[e];
     426             : 
     427       22105 :       auto height = _heights[e];
     428             : 
     429       22105 :       auto bias = _biases[e];
     430             : 
     431             :       // k is the element layer ordering within each elevation layer
     432       71683 :       for (unsigned int k = 0; k < order * num_layers + (e == 0 ? 1 : 0); ++k)
     433             :       {
     434             :         // For the first layer we don't need to move
     435       49578 :         if (e == 0 && k == 0)
     436       11109 :           current_distance.zero();
     437             :         else
     438             :         {
     439             :           // Shift the previous position by a certain fraction of 'height' along the extrusion
     440             :           // direction to get the new position.
     441       38469 :           auto layer_index = (k - (e == 0 ? 1 : 0)) / order + 1;
     442             : 
     443       38469 :           const auto step_size = MooseUtils::absoluteFuzzyEqual(bias, 1.0)
     444       38469 :                                      ? height / (Real)num_layers / (Real)order
     445        2310 :                                      : height * std::pow(bias, (Real)(layer_index - 1)) *
     446        2310 :                                            (1.0 - bias) /
     447       38469 :                                            (1.0 - std::pow(bias, (Real)(num_layers))) / (Real)order;
     448             : 
     449       38469 :           current_distance = old_distance + _direction * step_size;
     450             : 
     451             :           // Handle helicoidal extrusion
     452       38469 :           if (!MooseUtils::absoluteFuzzyEqual(_twist_pitch, 0.))
     453             :           {
     454             :             // twist 1 should be 'normal' to the extruded shape
     455        2772 :             RealVectorValue twist1 = _direction.cross(*node);
     456             :             // This happens for any node on the helicoidal extrusion axis
     457        2772 :             if (!MooseUtils::absoluteFuzzyEqual(twist1.norm(), .0))
     458        2718 :               twist1 /= twist1.norm();
     459        2772 :             const RealVectorValue twist2 = twist1.cross(_direction);
     460             : 
     461        5544 :             auto twist = (cos(2. * libMesh::pi * layer_index * step_size / _twist_pitch) -
     462        5544 :                           cos(2. * libMesh::pi * (layer_index - 1) * step_size / _twist_pitch)) *
     463        2772 :                              twist2 +
     464        5544 :                          (sin(2. * libMesh::pi * layer_index * step_size / _twist_pitch) -
     465        5544 :                           sin(2. * libMesh::pi * (layer_index - 1) * step_size / _twist_pitch)) *
     466        5544 :                              twist1;
     467        2772 :             twist *= std::sqrt(node->norm_sq() + libMesh::Utility::pow<2>(_direction * (*node)));
     468        2772 :             current_distance += twist;
     469             :           }
     470             :         }
     471             : 
     472       99156 :         Node * new_node = mesh->add_point(*node + current_distance,
     473       49578 :                                           node->id() + (current_node_layer * orig_nodes),
     474       49578 :                                           node->processor_id());
     475             : 
     476             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
     477             :         // Let's give the base of the extruded mesh the same
     478             :         // unique_ids as the source mesh, in case anyone finds that
     479             :         // a useful map to preserve.
     480             :         const unique_id_type uid = (current_node_layer == 0)
     481       49578 :                                        ? node->unique_id()
     482       38469 :                                        : orig_unique_ids +
     483       38469 :                                              (current_node_layer - 1) * (orig_nodes + orig_elem) +
     484       38469 :                                              node->id();
     485             : 
     486       49578 :         new_node->set_unique_id(uid);
     487             : #endif
     488             : 
     489       49578 :         input_boundary_info.boundary_ids(node, ids_to_copy);
     490       49578 :         if (_boundary_swap_pairs.empty())
     491       49578 :           boundary_info.add_node(new_node, ids_to_copy);
     492             :         else
     493           0 :           for (const auto & id_to_copy : ids_to_copy)
     494             :           {
     495           0 :             boundary_info.add_node(new_node,
     496           0 :                                    _boundary_swap_pairs[e].count(id_to_copy)
     497           0 :                                        ? _boundary_swap_pairs[e][id_to_copy]
     498           0 :                                        : id_to_copy);
     499             :           }
     500             : 
     501       49578 :         old_distance = current_distance;
     502       49578 :         current_node_layer++;
     503             :       }
     504             :     }
     505         167 :   }
     506             : 
     507         167 :   const auto & side_ids = input_boundary_info.get_side_boundary_ids();
     508             : 
     509             :   boundary_id_type next_side_id =
     510         167 :       side_ids.empty() ? 0 : cast_int<boundary_id_type>(*side_ids.rbegin() + 1);
     511             : 
     512             :   // side_ids may not include ids from remote elements, in which case
     513             :   // some processors may have underestimated the next_side_id; let's
     514             :   // fix that.
     515         167 :   input->comm().max(next_side_id);
     516             : 
     517        9357 :   for (const auto & elem : input->element_ptr_range())
     518             :   {
     519        9190 :     const ElemType etype = elem->type();
     520             : 
     521             :     // build_extrusion currently only works on coarse meshes
     522             :     libmesh_assert(!elem->parent());
     523             : 
     524        9190 :     unsigned int current_layer = 0;
     525             : 
     526       27100 :     for (unsigned int e = 0; e != total_num_elevations; e++)
     527             :     {
     528       17910 :       auto num_layers = _num_layers[e];
     529             : 
     530       48576 :       for (unsigned int k = 0; k != num_layers; ++k)
     531             :       {
     532       30666 :         std::unique_ptr<Elem> new_elem;
     533       30666 :         bool is_flipped(false);
     534       30666 :         switch (etype)
     535             :         {
     536          10 :           case EDGE2:
     537             :           {
     538          10 :             new_elem = std::make_unique<Quad4>();
     539          20 :             new_elem->set_node(
     540          10 :                 0, mesh->node_ptr(elem->node_ptr(0)->id() + (current_layer * orig_nodes)));
     541          20 :             new_elem->set_node(
     542          10 :                 1, mesh->node_ptr(elem->node_ptr(1)->id() + (current_layer * orig_nodes)));
     543          20 :             new_elem->set_node(
     544          10 :                 2, mesh->node_ptr(elem->node_ptr(1)->id() + ((current_layer + 1) * orig_nodes)));
     545          20 :             new_elem->set_node(
     546          10 :                 3, mesh->node_ptr(elem->node_ptr(0)->id() + ((current_layer + 1) * orig_nodes)));
     547             : 
     548          10 :             if (elem->neighbor_ptr(0) == remote_elem)
     549           0 :               new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
     550          10 :             if (elem->neighbor_ptr(1) == remote_elem)
     551           0 :               new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
     552             : 
     553          10 :             break;
     554             :           }
     555           0 :           case EDGE3:
     556             :           {
     557           0 :             new_elem = std::make_unique<Quad9>();
     558           0 :             new_elem->set_node(
     559           0 :                 0, mesh->node_ptr(elem->node_ptr(0)->id() + (2 * current_layer * orig_nodes)));
     560           0 :             new_elem->set_node(
     561           0 :                 1, mesh->node_ptr(elem->node_ptr(1)->id() + (2 * current_layer * orig_nodes)));
     562           0 :             new_elem->set_node(
     563             :                 2,
     564           0 :                 mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 2) * orig_nodes)));
     565           0 :             new_elem->set_node(
     566             :                 3,
     567           0 :                 mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 2) * orig_nodes)));
     568           0 :             new_elem->set_node(
     569           0 :                 4, mesh->node_ptr(elem->node_ptr(2)->id() + (2 * current_layer * orig_nodes)));
     570           0 :             new_elem->set_node(
     571             :                 5,
     572           0 :                 mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 1) * orig_nodes)));
     573           0 :             new_elem->set_node(
     574             :                 6,
     575           0 :                 mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 2) * orig_nodes)));
     576           0 :             new_elem->set_node(
     577             :                 7,
     578           0 :                 mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 1) * orig_nodes)));
     579           0 :             new_elem->set_node(
     580             :                 8,
     581           0 :                 mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 1) * orig_nodes)));
     582             : 
     583           0 :             if (elem->neighbor_ptr(0) == remote_elem)
     584           0 :               new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
     585           0 :             if (elem->neighbor_ptr(1) == remote_elem)
     586           0 :               new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
     587             : 
     588           0 :             break;
     589             :           }
     590         936 :           case TRI3:
     591             :           {
     592         936 :             new_elem = std::make_unique<Prism6>();
     593        1872 :             new_elem->set_node(
     594         936 :                 0, mesh->node_ptr(elem->node_ptr(0)->id() + (current_layer * orig_nodes)));
     595        1872 :             new_elem->set_node(
     596         936 :                 1, mesh->node_ptr(elem->node_ptr(1)->id() + (current_layer * orig_nodes)));
     597        1872 :             new_elem->set_node(
     598         936 :                 2, mesh->node_ptr(elem->node_ptr(2)->id() + (current_layer * orig_nodes)));
     599        1872 :             new_elem->set_node(
     600         936 :                 3, mesh->node_ptr(elem->node_ptr(0)->id() + ((current_layer + 1) * orig_nodes)));
     601        1872 :             new_elem->set_node(
     602         936 :                 4, mesh->node_ptr(elem->node_ptr(1)->id() + ((current_layer + 1) * orig_nodes)));
     603        1872 :             new_elem->set_node(
     604         936 :                 5, mesh->node_ptr(elem->node_ptr(2)->id() + ((current_layer + 1) * orig_nodes)));
     605             : 
     606         936 :             if (elem->neighbor_ptr(0) == remote_elem)
     607           0 :               new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
     608         936 :             if (elem->neighbor_ptr(1) == remote_elem)
     609          12 :               new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
     610         936 :             if (elem->neighbor_ptr(2) == remote_elem)
     611           0 :               new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
     612             : 
     613         936 :             if (new_elem->volume() < 0.0)
     614             :             {
     615           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 0, 3);
     616           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 1, 4);
     617           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 2, 5);
     618           0 :               is_flipped = true;
     619             :             }
     620             : 
     621         936 :             break;
     622             :           }
     623           0 :           case TRI6:
     624             :           {
     625           0 :             new_elem = std::make_unique<Prism18>();
     626           0 :             new_elem->set_node(
     627           0 :                 0, mesh->node_ptr(elem->node_ptr(0)->id() + (2 * current_layer * orig_nodes)));
     628           0 :             new_elem->set_node(
     629           0 :                 1, mesh->node_ptr(elem->node_ptr(1)->id() + (2 * current_layer * orig_nodes)));
     630           0 :             new_elem->set_node(
     631           0 :                 2, mesh->node_ptr(elem->node_ptr(2)->id() + (2 * current_layer * orig_nodes)));
     632           0 :             new_elem->set_node(
     633             :                 3,
     634           0 :                 mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 2) * orig_nodes)));
     635           0 :             new_elem->set_node(
     636             :                 4,
     637           0 :                 mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 2) * orig_nodes)));
     638           0 :             new_elem->set_node(
     639             :                 5,
     640           0 :                 mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 2) * orig_nodes)));
     641           0 :             new_elem->set_node(
     642           0 :                 6, mesh->node_ptr(elem->node_ptr(3)->id() + (2 * current_layer * orig_nodes)));
     643           0 :             new_elem->set_node(
     644           0 :                 7, mesh->node_ptr(elem->node_ptr(4)->id() + (2 * current_layer * orig_nodes)));
     645           0 :             new_elem->set_node(
     646           0 :                 8, mesh->node_ptr(elem->node_ptr(5)->id() + (2 * current_layer * orig_nodes)));
     647           0 :             new_elem->set_node(
     648             :                 9,
     649           0 :                 mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 1) * orig_nodes)));
     650           0 :             new_elem->set_node(
     651             :                 10,
     652           0 :                 mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 1) * orig_nodes)));
     653           0 :             new_elem->set_node(
     654             :                 11,
     655           0 :                 mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 1) * orig_nodes)));
     656           0 :             new_elem->set_node(
     657             :                 12,
     658           0 :                 mesh->node_ptr(elem->node_ptr(3)->id() + ((2 * current_layer + 2) * orig_nodes)));
     659           0 :             new_elem->set_node(
     660             :                 13,
     661           0 :                 mesh->node_ptr(elem->node_ptr(4)->id() + ((2 * current_layer + 2) * orig_nodes)));
     662           0 :             new_elem->set_node(
     663             :                 14,
     664           0 :                 mesh->node_ptr(elem->node_ptr(5)->id() + ((2 * current_layer + 2) * orig_nodes)));
     665           0 :             new_elem->set_node(
     666             :                 15,
     667           0 :                 mesh->node_ptr(elem->node_ptr(3)->id() + ((2 * current_layer + 1) * orig_nodes)));
     668           0 :             new_elem->set_node(
     669             :                 16,
     670           0 :                 mesh->node_ptr(elem->node_ptr(4)->id() + ((2 * current_layer + 1) * orig_nodes)));
     671           0 :             new_elem->set_node(
     672             :                 17,
     673           0 :                 mesh->node_ptr(elem->node_ptr(5)->id() + ((2 * current_layer + 1) * orig_nodes)));
     674             : 
     675           0 :             if (elem->neighbor_ptr(0) == remote_elem)
     676           0 :               new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
     677           0 :             if (elem->neighbor_ptr(1) == remote_elem)
     678           0 :               new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
     679           0 :             if (elem->neighbor_ptr(2) == remote_elem)
     680           0 :               new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
     681             : 
     682           0 :             if (new_elem->volume() < 0.0)
     683             :             {
     684           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 0, 3);
     685           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 1, 4);
     686           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 2, 5);
     687           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 6, 12);
     688           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 7, 13);
     689           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 8, 14);
     690           0 :               is_flipped = true;
     691             :             }
     692             : 
     693           0 :             break;
     694             :           }
     695           0 :           case TRI7:
     696             :           {
     697           0 :             new_elem = std::make_unique<Prism21>();
     698           0 :             new_elem->set_node(
     699           0 :                 0, mesh->node_ptr(elem->node_ptr(0)->id() + (2 * current_layer * orig_nodes)));
     700           0 :             new_elem->set_node(
     701           0 :                 1, mesh->node_ptr(elem->node_ptr(1)->id() + (2 * current_layer * orig_nodes)));
     702           0 :             new_elem->set_node(
     703           0 :                 2, mesh->node_ptr(elem->node_ptr(2)->id() + (2 * current_layer * orig_nodes)));
     704           0 :             new_elem->set_node(
     705             :                 3,
     706           0 :                 mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 2) * orig_nodes)));
     707           0 :             new_elem->set_node(
     708             :                 4,
     709           0 :                 mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 2) * orig_nodes)));
     710           0 :             new_elem->set_node(
     711             :                 5,
     712           0 :                 mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 2) * orig_nodes)));
     713           0 :             new_elem->set_node(
     714           0 :                 6, mesh->node_ptr(elem->node_ptr(3)->id() + (2 * current_layer * orig_nodes)));
     715           0 :             new_elem->set_node(
     716           0 :                 7, mesh->node_ptr(elem->node_ptr(4)->id() + (2 * current_layer * orig_nodes)));
     717           0 :             new_elem->set_node(
     718           0 :                 8, mesh->node_ptr(elem->node_ptr(5)->id() + (2 * current_layer * orig_nodes)));
     719           0 :             new_elem->set_node(
     720             :                 9,
     721           0 :                 mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 1) * orig_nodes)));
     722           0 :             new_elem->set_node(
     723             :                 10,
     724           0 :                 mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 1) * orig_nodes)));
     725           0 :             new_elem->set_node(
     726             :                 11,
     727           0 :                 mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 1) * orig_nodes)));
     728           0 :             new_elem->set_node(
     729             :                 12,
     730           0 :                 mesh->node_ptr(elem->node_ptr(3)->id() + ((2 * current_layer + 2) * orig_nodes)));
     731           0 :             new_elem->set_node(
     732             :                 13,
     733           0 :                 mesh->node_ptr(elem->node_ptr(4)->id() + ((2 * current_layer + 2) * orig_nodes)));
     734           0 :             new_elem->set_node(
     735             :                 14,
     736           0 :                 mesh->node_ptr(elem->node_ptr(5)->id() + ((2 * current_layer + 2) * orig_nodes)));
     737           0 :             new_elem->set_node(
     738             :                 15,
     739           0 :                 mesh->node_ptr(elem->node_ptr(3)->id() + ((2 * current_layer + 1) * orig_nodes)));
     740           0 :             new_elem->set_node(
     741             :                 16,
     742           0 :                 mesh->node_ptr(elem->node_ptr(4)->id() + ((2 * current_layer + 1) * orig_nodes)));
     743           0 :             new_elem->set_node(
     744             :                 17,
     745           0 :                 mesh->node_ptr(elem->node_ptr(5)->id() + ((2 * current_layer + 1) * orig_nodes)));
     746           0 :             new_elem->set_node(
     747           0 :                 18, mesh->node_ptr(elem->node_ptr(6)->id() + (2 * current_layer * orig_nodes)));
     748           0 :             new_elem->set_node(
     749             :                 19,
     750           0 :                 mesh->node_ptr(elem->node_ptr(6)->id() + ((2 * current_layer + 2) * orig_nodes)));
     751           0 :             new_elem->set_node(
     752             :                 20,
     753           0 :                 mesh->node_ptr(elem->node_ptr(6)->id() + ((2 * current_layer + 1) * orig_nodes)));
     754             : 
     755           0 :             if (elem->neighbor_ptr(0) == remote_elem)
     756           0 :               new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
     757           0 :             if (elem->neighbor_ptr(1) == remote_elem)
     758           0 :               new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
     759           0 :             if (elem->neighbor_ptr(2) == remote_elem)
     760           0 :               new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
     761             : 
     762           0 :             if (new_elem->volume() < 0.0)
     763             :             {
     764           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 0, 3);
     765           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 1, 4);
     766           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 2, 5);
     767           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 6, 12);
     768           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 7, 13);
     769           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 8, 14);
     770           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 18, 19);
     771           0 :               is_flipped = true;
     772             :             }
     773             : 
     774           0 :             break;
     775             :           }
     776       29704 :           case QUAD4:
     777             :           {
     778       29704 :             new_elem = std::make_unique<Hex8>();
     779       59408 :             new_elem->set_node(
     780       29704 :                 0, mesh->node_ptr(elem->node_ptr(0)->id() + (current_layer * orig_nodes)));
     781       59408 :             new_elem->set_node(
     782       29704 :                 1, mesh->node_ptr(elem->node_ptr(1)->id() + (current_layer * orig_nodes)));
     783       59408 :             new_elem->set_node(
     784       29704 :                 2, mesh->node_ptr(elem->node_ptr(2)->id() + (current_layer * orig_nodes)));
     785       59408 :             new_elem->set_node(
     786       29704 :                 3, mesh->node_ptr(elem->node_ptr(3)->id() + (current_layer * orig_nodes)));
     787       59408 :             new_elem->set_node(
     788       29704 :                 4, mesh->node_ptr(elem->node_ptr(0)->id() + ((current_layer + 1) * orig_nodes)));
     789       59408 :             new_elem->set_node(
     790       29704 :                 5, mesh->node_ptr(elem->node_ptr(1)->id() + ((current_layer + 1) * orig_nodes)));
     791       59408 :             new_elem->set_node(
     792       29704 :                 6, mesh->node_ptr(elem->node_ptr(2)->id() + ((current_layer + 1) * orig_nodes)));
     793       59408 :             new_elem->set_node(
     794       29704 :                 7, mesh->node_ptr(elem->node_ptr(3)->id() + ((current_layer + 1) * orig_nodes)));
     795             : 
     796       29704 :             if (elem->neighbor_ptr(0) == remote_elem)
     797          94 :               new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
     798       29704 :             if (elem->neighbor_ptr(1) == remote_elem)
     799         318 :               new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
     800       29704 :             if (elem->neighbor_ptr(2) == remote_elem)
     801         110 :               new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
     802       29704 :             if (elem->neighbor_ptr(3) == remote_elem)
     803         306 :               new_elem->set_neighbor(4, const_cast<RemoteElem *>(remote_elem));
     804             : 
     805       29704 :             if (new_elem->volume() < 0.0)
     806             :             {
     807        2024 :               MooseMeshUtils::swapNodesInElem(*new_elem, 0, 4);
     808        2024 :               MooseMeshUtils::swapNodesInElem(*new_elem, 1, 5);
     809        2024 :               MooseMeshUtils::swapNodesInElem(*new_elem, 2, 6);
     810        2024 :               MooseMeshUtils::swapNodesInElem(*new_elem, 3, 7);
     811        2024 :               is_flipped = true;
     812             :             }
     813             : 
     814       29704 :             break;
     815             :           }
     816          16 :           case QUAD8:
     817             :           {
     818          16 :             new_elem = std::make_unique<Hex20>();
     819          32 :             new_elem->set_node(
     820          16 :                 0, mesh->node_ptr(elem->node_ptr(0)->id() + (2 * current_layer * orig_nodes)));
     821          32 :             new_elem->set_node(
     822          16 :                 1, mesh->node_ptr(elem->node_ptr(1)->id() + (2 * current_layer * orig_nodes)));
     823          32 :             new_elem->set_node(
     824          16 :                 2, mesh->node_ptr(elem->node_ptr(2)->id() + (2 * current_layer * orig_nodes)));
     825          32 :             new_elem->set_node(
     826          16 :                 3, mesh->node_ptr(elem->node_ptr(3)->id() + (2 * current_layer * orig_nodes)));
     827          32 :             new_elem->set_node(
     828             :                 4,
     829          16 :                 mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 2) * orig_nodes)));
     830          32 :             new_elem->set_node(
     831             :                 5,
     832          16 :                 mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 2) * orig_nodes)));
     833          32 :             new_elem->set_node(
     834             :                 6,
     835          16 :                 mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 2) * orig_nodes)));
     836          32 :             new_elem->set_node(
     837             :                 7,
     838          16 :                 mesh->node_ptr(elem->node_ptr(3)->id() + ((2 * current_layer + 2) * orig_nodes)));
     839          32 :             new_elem->set_node(
     840          16 :                 8, mesh->node_ptr(elem->node_ptr(4)->id() + (2 * current_layer * orig_nodes)));
     841          32 :             new_elem->set_node(
     842          16 :                 9, mesh->node_ptr(elem->node_ptr(5)->id() + (2 * current_layer * orig_nodes)));
     843          32 :             new_elem->set_node(
     844          16 :                 10, mesh->node_ptr(elem->node_ptr(6)->id() + (2 * current_layer * orig_nodes)));
     845          32 :             new_elem->set_node(
     846          16 :                 11, mesh->node_ptr(elem->node_ptr(7)->id() + (2 * current_layer * orig_nodes)));
     847          32 :             new_elem->set_node(
     848             :                 12,
     849          16 :                 mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 1) * orig_nodes)));
     850          32 :             new_elem->set_node(
     851             :                 13,
     852          16 :                 mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 1) * orig_nodes)));
     853          32 :             new_elem->set_node(
     854             :                 14,
     855          16 :                 mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 1) * orig_nodes)));
     856          32 :             new_elem->set_node(
     857             :                 15,
     858          16 :                 mesh->node_ptr(elem->node_ptr(3)->id() + ((2 * current_layer + 1) * orig_nodes)));
     859          32 :             new_elem->set_node(
     860             :                 16,
     861          16 :                 mesh->node_ptr(elem->node_ptr(4)->id() + ((2 * current_layer + 2) * orig_nodes)));
     862          32 :             new_elem->set_node(
     863             :                 17,
     864          16 :                 mesh->node_ptr(elem->node_ptr(5)->id() + ((2 * current_layer + 2) * orig_nodes)));
     865          32 :             new_elem->set_node(
     866             :                 18,
     867          16 :                 mesh->node_ptr(elem->node_ptr(6)->id() + ((2 * current_layer + 2) * orig_nodes)));
     868          32 :             new_elem->set_node(
     869             :                 19,
     870          16 :                 mesh->node_ptr(elem->node_ptr(7)->id() + ((2 * current_layer + 2) * orig_nodes)));
     871             : 
     872          16 :             if (elem->neighbor_ptr(0) == remote_elem)
     873           0 :               new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
     874          16 :             if (elem->neighbor_ptr(1) == remote_elem)
     875           0 :               new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
     876          16 :             if (elem->neighbor_ptr(2) == remote_elem)
     877           0 :               new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
     878          16 :             if (elem->neighbor_ptr(3) == remote_elem)
     879           0 :               new_elem->set_neighbor(4, const_cast<RemoteElem *>(remote_elem));
     880             : 
     881          16 :             if (new_elem->volume() < 0.0)
     882             :             {
     883           8 :               MooseMeshUtils::swapNodesInElem(*new_elem, 0, 4);
     884           8 :               MooseMeshUtils::swapNodesInElem(*new_elem, 1, 5);
     885           8 :               MooseMeshUtils::swapNodesInElem(*new_elem, 2, 6);
     886           8 :               MooseMeshUtils::swapNodesInElem(*new_elem, 3, 7);
     887           8 :               MooseMeshUtils::swapNodesInElem(*new_elem, 8, 16);
     888           8 :               MooseMeshUtils::swapNodesInElem(*new_elem, 9, 17);
     889           8 :               MooseMeshUtils::swapNodesInElem(*new_elem, 10, 18);
     890           8 :               MooseMeshUtils::swapNodesInElem(*new_elem, 11, 19);
     891           8 :               is_flipped = true;
     892             :             }
     893             : 
     894          16 :             break;
     895             :           }
     896           0 :           case QUAD9:
     897             :           {
     898           0 :             new_elem = std::make_unique<Hex27>();
     899           0 :             new_elem->set_node(
     900           0 :                 0, mesh->node_ptr(elem->node_ptr(0)->id() + (2 * current_layer * orig_nodes)));
     901           0 :             new_elem->set_node(
     902           0 :                 1, mesh->node_ptr(elem->node_ptr(1)->id() + (2 * current_layer * orig_nodes)));
     903           0 :             new_elem->set_node(
     904           0 :                 2, mesh->node_ptr(elem->node_ptr(2)->id() + (2 * current_layer * orig_nodes)));
     905           0 :             new_elem->set_node(
     906           0 :                 3, mesh->node_ptr(elem->node_ptr(3)->id() + (2 * current_layer * orig_nodes)));
     907           0 :             new_elem->set_node(
     908             :                 4,
     909           0 :                 mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 2) * orig_nodes)));
     910           0 :             new_elem->set_node(
     911             :                 5,
     912           0 :                 mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 2) * orig_nodes)));
     913           0 :             new_elem->set_node(
     914             :                 6,
     915           0 :                 mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 2) * orig_nodes)));
     916           0 :             new_elem->set_node(
     917             :                 7,
     918           0 :                 mesh->node_ptr(elem->node_ptr(3)->id() + ((2 * current_layer + 2) * orig_nodes)));
     919           0 :             new_elem->set_node(
     920           0 :                 8, mesh->node_ptr(elem->node_ptr(4)->id() + (2 * current_layer * orig_nodes)));
     921           0 :             new_elem->set_node(
     922           0 :                 9, mesh->node_ptr(elem->node_ptr(5)->id() + (2 * current_layer * orig_nodes)));
     923           0 :             new_elem->set_node(
     924           0 :                 10, mesh->node_ptr(elem->node_ptr(6)->id() + (2 * current_layer * orig_nodes)));
     925           0 :             new_elem->set_node(
     926           0 :                 11, mesh->node_ptr(elem->node_ptr(7)->id() + (2 * current_layer * orig_nodes)));
     927           0 :             new_elem->set_node(
     928             :                 12,
     929           0 :                 mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 1) * orig_nodes)));
     930           0 :             new_elem->set_node(
     931             :                 13,
     932           0 :                 mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 1) * orig_nodes)));
     933           0 :             new_elem->set_node(
     934             :                 14,
     935           0 :                 mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 1) * orig_nodes)));
     936           0 :             new_elem->set_node(
     937             :                 15,
     938           0 :                 mesh->node_ptr(elem->node_ptr(3)->id() + ((2 * current_layer + 1) * orig_nodes)));
     939           0 :             new_elem->set_node(
     940             :                 16,
     941           0 :                 mesh->node_ptr(elem->node_ptr(4)->id() + ((2 * current_layer + 2) * orig_nodes)));
     942           0 :             new_elem->set_node(
     943             :                 17,
     944           0 :                 mesh->node_ptr(elem->node_ptr(5)->id() + ((2 * current_layer + 2) * orig_nodes)));
     945           0 :             new_elem->set_node(
     946             :                 18,
     947           0 :                 mesh->node_ptr(elem->node_ptr(6)->id() + ((2 * current_layer + 2) * orig_nodes)));
     948           0 :             new_elem->set_node(
     949             :                 19,
     950           0 :                 mesh->node_ptr(elem->node_ptr(7)->id() + ((2 * current_layer + 2) * orig_nodes)));
     951           0 :             new_elem->set_node(
     952           0 :                 20, mesh->node_ptr(elem->node_ptr(8)->id() + (2 * current_layer * orig_nodes)));
     953           0 :             new_elem->set_node(
     954             :                 21,
     955           0 :                 mesh->node_ptr(elem->node_ptr(4)->id() + ((2 * current_layer + 1) * orig_nodes)));
     956           0 :             new_elem->set_node(
     957             :                 22,
     958           0 :                 mesh->node_ptr(elem->node_ptr(5)->id() + ((2 * current_layer + 1) * orig_nodes)));
     959           0 :             new_elem->set_node(
     960             :                 23,
     961           0 :                 mesh->node_ptr(elem->node_ptr(6)->id() + ((2 * current_layer + 1) * orig_nodes)));
     962           0 :             new_elem->set_node(
     963             :                 24,
     964           0 :                 mesh->node_ptr(elem->node_ptr(7)->id() + ((2 * current_layer + 1) * orig_nodes)));
     965           0 :             new_elem->set_node(
     966             :                 25,
     967           0 :                 mesh->node_ptr(elem->node_ptr(8)->id() + ((2 * current_layer + 2) * orig_nodes)));
     968           0 :             new_elem->set_node(
     969             :                 26,
     970           0 :                 mesh->node_ptr(elem->node_ptr(8)->id() + ((2 * current_layer + 1) * orig_nodes)));
     971             : 
     972           0 :             if (elem->neighbor_ptr(0) == remote_elem)
     973           0 :               new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
     974           0 :             if (elem->neighbor_ptr(1) == remote_elem)
     975           0 :               new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
     976           0 :             if (elem->neighbor_ptr(2) == remote_elem)
     977           0 :               new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
     978           0 :             if (elem->neighbor_ptr(3) == remote_elem)
     979           0 :               new_elem->set_neighbor(4, const_cast<RemoteElem *>(remote_elem));
     980             : 
     981           0 :             if (new_elem->volume() < 0.0)
     982             :             {
     983           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 0, 4);
     984           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 1, 5);
     985           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 2, 6);
     986           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 3, 7);
     987           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 8, 16);
     988           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 9, 17);
     989           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 10, 18);
     990           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 11, 19);
     991           0 :               MooseMeshUtils::swapNodesInElem(*new_elem, 20, 25);
     992           0 :               is_flipped = true;
     993             :             }
     994             : 
     995           0 :             break;
     996             :           }
     997           0 :           default:
     998           0 :             mooseError("Extrusion is not implemented for element type " + Moose::stringify(etype));
     999             :         }
    1000             : 
    1001       30666 :         new_elem->set_id(elem->id() + (current_layer * orig_elem));
    1002       30666 :         new_elem->processor_id() = elem->processor_id();
    1003             : 
    1004             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    1005             :         // Let's give the base of the extruded mesh the same
    1006             :         // unique_ids as the source mesh, in case anyone finds that
    1007             :         // a useful map to preserve.
    1008             :         const unique_id_type uid = (current_layer == 0)
    1009       30666 :                                        ? elem->unique_id()
    1010       21476 :                                        : orig_unique_ids +
    1011       21476 :                                              (current_layer - 1) * (orig_nodes + orig_elem) +
    1012       21476 :                                              orig_nodes + elem->id();
    1013             : 
    1014       30666 :         new_elem->set_unique_id(uid);
    1015             : #endif
    1016             : 
    1017             :         // maintain the subdomain_id
    1018       30666 :         new_elem->subdomain_id() = elem->subdomain_id();
    1019             : 
    1020             :         // define upward boundaries
    1021       30666 :         if (k == num_layers - 1)
    1022             :         {
    1023             :           // Identify the side index of the new element that is part of the upward boundary
    1024             :           const unsigned short top_id =
    1025       17910 :               new_elem->dim() == 3 ? cast_int<unsigned short>(elem->n_sides() + 1) : 2;
    1026             :           // Assign sideset id to the side if the element belongs to a specified
    1027             :           // upward_boundary_source_blocks
    1028       23238 :           for (unsigned int i = 0; i < _upward_boundary_source_blocks[e].size(); i++)
    1029        5328 :             if (new_elem->subdomain_id() == _upward_boundary_source_blocks[e][i])
    1030        3552 :               boundary_info.add_side(
    1031        3552 :                   new_elem.get(), is_flipped ? 0 : top_id, _upward_boundary_ids[e][i]);
    1032             :         }
    1033             :         // define downward boundaries
    1034       30666 :         if (k == 0)
    1035             :         {
    1036             :           const unsigned short top_id =
    1037       17910 :               new_elem->dim() == 3 ? cast_int<unsigned short>(elem->n_sides() + 1) : 2;
    1038       23238 :           for (unsigned int i = 0; i < _downward_boundary_source_blocks[e].size(); i++)
    1039        5328 :             if (new_elem->subdomain_id() == _downward_boundary_source_blocks[e][i])
    1040        3552 :               boundary_info.add_side(
    1041        3552 :                   new_elem.get(), is_flipped ? top_id : 0, _downward_boundary_ids[e][i]);
    1042             :         }
    1043             : 
    1044             :         // perform subdomain swaps
    1045       30666 :         if (_subdomain_swap_pairs.size())
    1046             :         {
    1047       13476 :           auto & elevation_swap_pairs = _subdomain_swap_pairs[e];
    1048             : 
    1049       13476 :           auto new_id_it = elevation_swap_pairs.find(elem->subdomain_id());
    1050             : 
    1051       13476 :           if (new_id_it != elevation_swap_pairs.end())
    1052       13476 :             new_elem->subdomain_id() = new_id_it->second;
    1053             :         }
    1054             : 
    1055       30666 :         Elem * added_elem = mesh->add_elem(std::move(new_elem));
    1056             : 
    1057             :         // maintain extra integers
    1058       43338 :         for (unsigned int i = 0; i < num_extra_elem_integers; i++)
    1059       12672 :           added_elem->set_extra_integer(i, elem->get_extra_integer(i));
    1060             : 
    1061       30666 :         if (_elem_integers_swap_pairs.size())
    1062             :         {
    1063       19008 :           for (unsigned int i = 0; i < _elem_integer_indices_to_swap.size(); i++)
    1064             :           {
    1065       12672 :             auto & elevation_extra_swap_pairs = _elem_integers_swap_pairs[i * _heights.size() + e];
    1066             : 
    1067       12672 :             auto new_extra_id_it = elevation_extra_swap_pairs.find(
    1068       12672 :                 elem->get_extra_integer(_elem_integer_indices_to_swap[i]));
    1069             : 
    1070       12672 :             if (new_extra_id_it != elevation_extra_swap_pairs.end())
    1071        7392 :               added_elem->set_extra_integer(_elem_integer_indices_to_swap[i],
    1072        7392 :                                             new_extra_id_it->second);
    1073             :           }
    1074             :         }
    1075             : 
    1076             :         // Copy any old boundary ids on all sides
    1077      152374 :         for (auto s : elem->side_index_range())
    1078             :         {
    1079      121708 :           input_boundary_info.boundary_ids(elem, s, ids_to_copy);
    1080             : 
    1081      121708 :           if (added_elem->dim() == 3)
    1082             :           {
    1083             :             // For 2D->3D extrusion, we give the boundary IDs
    1084             :             // for side s on the old element to side s+1 on the
    1085             :             // new element.  This is just a happy coincidence as
    1086             :             // far as I can tell...
    1087      121688 :             if (_boundary_swap_pairs.empty())
    1088      121688 :               boundary_info.add_side(added_elem, cast_int<unsigned short>(s + 1), ids_to_copy);
    1089             :             else
    1090           0 :               for (const auto & id_to_copy : ids_to_copy)
    1091           0 :                 boundary_info.add_side(added_elem,
    1092           0 :                                        cast_int<unsigned short>(s + 1),
    1093           0 :                                        _boundary_swap_pairs[e].count(id_to_copy)
    1094           0 :                                            ? _boundary_swap_pairs[e][id_to_copy]
    1095           0 :                                            : id_to_copy);
    1096             :           }
    1097             :           else
    1098             :           {
    1099             :             // For 1D->2D extrusion, the boundary IDs map as:
    1100             :             // Old elem -> New elem
    1101             :             // 0        -> 3
    1102             :             // 1        -> 1
    1103             :             libmesh_assert_less(s, 2);
    1104          20 :             const unsigned short sidemap[2] = {3, 1};
    1105          20 :             if (_boundary_swap_pairs.empty())
    1106          20 :               boundary_info.add_side(added_elem, sidemap[s], ids_to_copy);
    1107             :             else
    1108           0 :               for (const auto & id_to_copy : ids_to_copy)
    1109           0 :                 boundary_info.add_side(added_elem,
    1110           0 :                                        sidemap[s],
    1111           0 :                                        _boundary_swap_pairs[e].count(id_to_copy)
    1112           0 :                                            ? _boundary_swap_pairs[e][id_to_copy]
    1113           0 :                                            : id_to_copy);
    1114             :           }
    1115             :         }
    1116             : 
    1117             :         // Give new boundary ids to bottom and top
    1118       30666 :         if (current_layer == 0)
    1119             :         {
    1120             :           const unsigned short top_id =
    1121        9190 :               added_elem->dim() == 3 ? cast_int<unsigned short>(elem->n_sides() + 1) : 2;
    1122        9190 :           if (_has_bottom_boundary)
    1123             :           {
    1124             :             mooseAssert(user_bottom_boundary_id != libMesh::BoundaryInfo::invalid_id,
    1125             :                         "We should have retrieved a proper boundary ID");
    1126        2574 :             boundary_info.add_side(added_elem, is_flipped ? top_id : 0, user_bottom_boundary_id);
    1127             :           }
    1128             :           else
    1129        6616 :             boundary_info.add_side(added_elem, is_flipped ? top_id : 0, next_side_id);
    1130             :         }
    1131             : 
    1132       30666 :         if (current_layer == total_num_layers - 1)
    1133             :         {
    1134             :           // For 2D->3D extrusion, the "top" ID is 1+the original
    1135             :           // element's number of sides.  For 1D->2D extrusion, the
    1136             :           // "top" ID is side 2.
    1137             :           const unsigned short top_id =
    1138        9190 :               added_elem->dim() == 3 ? cast_int<unsigned short>(elem->n_sides() + 1) : 2;
    1139             : 
    1140        9190 :           if (_has_top_boundary)
    1141             :           {
    1142             :             mooseAssert(user_top_boundary_id != libMesh::BoundaryInfo::invalid_id,
    1143             :                         "We should have retrieved a proper boundary ID");
    1144        2574 :             boundary_info.add_side(added_elem, is_flipped ? 0 : top_id, user_top_boundary_id);
    1145             :           }
    1146             :           else
    1147        6616 :             boundary_info.add_side(
    1148        6616 :                 added_elem, is_flipped ? 0 : top_id, cast_int<boundary_id_type>(next_side_id + 1));
    1149             :         }
    1150             : 
    1151       30666 :         current_layer++;
    1152       30666 :       }
    1153             :     }
    1154         167 :   }
    1155             : 
    1156             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    1157             :   // Update the value of next_unique_id based on newly created nodes and elements
    1158             :   // Note: Number of element layers is one less than number of node layers
    1159         167 :   unsigned int total_new_node_layers = total_num_layers * order;
    1160         167 :   unsigned int new_unique_ids = orig_unique_ids + (total_new_node_layers - 1) * orig_elem +
    1161             :                                 total_new_node_layers * orig_nodes;
    1162         167 :   mesh->set_next_unique_id(new_unique_ids);
    1163             : #endif
    1164             : 
    1165             :   // Copy all the subdomain/sideset/nodeset name maps to the extruded mesh
    1166         167 :   if (!input_subdomain_map.empty())
    1167          26 :     mesh->set_subdomain_name_map().insert(input_subdomain_map.begin(), input_subdomain_map.end());
    1168         167 :   if (!input_sideset_map.empty())
    1169         167 :     mesh->get_boundary_info().set_sideset_name_map().insert(input_sideset_map.begin(),
    1170             :                                                             input_sideset_map.end());
    1171         167 :   if (!input_nodeset_map.empty())
    1172         110 :     mesh->get_boundary_info().set_nodeset_name_map().insert(input_nodeset_map.begin(),
    1173             :                                                             input_nodeset_map.end());
    1174             : 
    1175         167 :   if (_has_bottom_boundary)
    1176          92 :     boundary_info.sideset_name(new_boundary_ids.front()) = new_boundary_names.front();
    1177         167 :   if (_has_top_boundary)
    1178          92 :     boundary_info.sideset_name(new_boundary_ids.back()) = new_boundary_names.back();
    1179             : 
    1180         167 :   mesh->set_isnt_prepared();
    1181             :   // Creating the layered meshes creates a lot of leftover nodes, notably in the boundary_info,
    1182             :   // which will crash both paraview and trigger exodiff. Best to be safe.
    1183         167 :   if (extruding_quad_eights)
    1184           8 :     mesh->prepare_for_use();
    1185             : 
    1186         334 :   return mesh;
    1187         167 : }

Generated by: LCOV version 1.14