https://mooseframework.inl.gov
AdvancedExtruderGenerator.C
Go to the documentation of this file.
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 
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 
45  FancyExtruderGenerator,
46  "02/18/2023 24:00",
48 
51 {
53 
54  params.addRequiredParam<MeshGeneratorName>("input", "The mesh to extrude");
55 
56  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  params.addRequiredParam<std::vector<Real>>("heights", "The height of each elevation");
64 
65  params.addRangeCheckedParam<std::vector<Real>>(
66  "biases", "biases>0.0", "The axial growth factor used for mesh biasing for each elevation.");
67 
68  params.addRequiredParam<std::vector<unsigned int>>(
69  "num_layers", "The number of layers for each elevation - must be num_elevations in length!");
70 
71  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  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  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  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  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  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  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  params.addParam<std::vector<std::vector<subdomain_id_type>>>(
110  "upward_boundary_source_blocks", "Block ids used to generate upward interface boundaries.");
111 
112  params.addParam<std::vector<std::vector<boundary_id_type>>>("upward_boundary_ids",
113  "Upward interface boundary ids.");
114 
115  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  params.addParam<std::vector<std::vector<boundary_id_type>>>("downward_boundary_ids",
120  "Downward interface boundary ids.");
121  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  params.addParamNamesToGroup(
126  "subdomain_swaps boundary_swaps elem_integer_names_to_swap elem_integers_swaps", "ID Swap");
127  params.addParam<Real>("twist_pitch",
128  0,
129  "Pitch for helicoidal extrusion around an axis going through the origin "
130  "following the direction vector");
131  return params;
132 }
133 
135  : MeshGenerator(parameters),
136  _input(getMesh("input")),
137  _heights(getParam<std::vector<Real>>("heights")),
138  _biases(isParamValid("biases") ? getParam<std::vector<Real>>("biases")
139  : std::vector<Real>(_heights.size(), 1.0)),
140  _num_layers(getParam<std::vector<unsigned int>>("num_layers")),
141  _subdomain_swaps(getParam<std::vector<std::vector<subdomain_id_type>>>("subdomain_swaps")),
142  _boundary_swaps(getParam<std::vector<std::vector<boundary_id_type>>>("boundary_swaps")),
143  _elem_integer_names_to_swap(getParam<std::vector<std::string>>("elem_integer_names_to_swap")),
144  _elem_integers_swaps(
145  getParam<std::vector<std::vector<std::vector<dof_id_type>>>>("elem_integers_swaps")),
146  _direction(getParam<Point>("direction")),
147  _has_top_boundary(isParamValid("top_boundary")),
148  _top_boundary(isParamValid("top_boundary") ? getParam<BoundaryName>("top_boundary") : "0"),
149  _has_bottom_boundary(isParamValid("bottom_boundary")),
150  _bottom_boundary(isParamValid("bottom_boundary") ? getParam<BoundaryName>("bottom_boundary")
151  : "0"),
152  _upward_boundary_source_blocks(
153  isParamValid("upward_boundary_source_blocks")
154  ? getParam<std::vector<std::vector<subdomain_id_type>>>("upward_boundary_source_blocks")
155  : std::vector<std::vector<subdomain_id_type>>(_heights.size(),
156  std::vector<subdomain_id_type>())),
157  _upward_boundary_ids(
158  isParamValid("upward_boundary_ids")
159  ? getParam<std::vector<std::vector<boundary_id_type>>>("upward_boundary_ids")
160  : std::vector<std::vector<boundary_id_type>>(_heights.size(),
161  std::vector<boundary_id_type>())),
162  _downward_boundary_source_blocks(isParamValid("downward_boundary_source_blocks")
163  ? getParam<std::vector<std::vector<subdomain_id_type>>>(
164  "downward_boundary_source_blocks")
165  : std::vector<std::vector<subdomain_id_type>>(
166  _heights.size(), std::vector<subdomain_id_type>())),
167  _downward_boundary_ids(
168  isParamValid("downward_boundary_ids")
169  ? getParam<std::vector<std::vector<boundary_id_type>>>("downward_boundary_ids")
170  : std::vector<std::vector<boundary_id_type>>(_heights.size(),
171  std::vector<boundary_id_type>())),
172  _twist_pitch(getParam<Real>("twist_pitch"))
173 {
174  if (!_direction.norm())
175  paramError("direction", "Must have some length!");
176 
177  // Normalize it
178  _direction /= _direction.norm();
179 
180  const auto num_elevations = _heights.size();
181 
182  if (_num_layers.size() != num_elevations)
183  paramError("heights", "The length of 'heights' and 'num_layers' must be the same in ", name());
184 
185  if (_subdomain_swaps.size() && (_subdomain_swaps.size() != num_elevations))
186  paramError("subdomain_swaps",
187  "If specified, 'subdomain_swaps' (" + std::to_string(_subdomain_swaps.size()) +
188  ") must be the same length as 'heights' (" + std::to_string(num_elevations) +
189  ") in ",
190  name());
191 
192  try
193  {
195  name(), "subdomain_swaps", _subdomain_swaps, _subdomain_swap_pairs);
196  }
197  catch (const MooseException & e)
198  {
199  paramError("subdomain_swaps", e.what());
200  }
201 
202  if (_boundary_swaps.size() && (_boundary_swaps.size() != num_elevations))
203  paramError("boundary_swaps",
204  "If specified, 'boundary_swaps' (" + std::to_string(_boundary_swaps.size()) +
205  ") must be the same length as 'heights' (" + std::to_string(num_elevations) +
206  ") in ",
207  name());
208 
209  try
210  {
212  name(), "boundary_swaps", _boundary_swaps, _boundary_swap_pairs);
213  }
214  catch (const MooseException & e)
215  {
216  paramError("boundary_swaps", e.what());
217  }
218 
219  if (_elem_integers_swaps.size() &&
221  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  for (const auto & unit_elem_integers_swaps : _elem_integers_swaps)
226  if (unit_elem_integers_swaps.size() != num_elevations)
227  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  {
234  num_elevations,
238  }
239  catch (const MooseException & e)
240  {
241  paramError("elem_integers_swaps", e.what());
242  }
243 
244  bool has_negative_entry = false;
245  bool has_positive_entry = false;
246  for (const auto & h : _heights)
247  {
248  if (h > 0.0)
249  has_positive_entry = true;
250  else
251  has_negative_entry = true;
252  }
253 
254  if (has_negative_entry && has_positive_entry)
255  paramError("heights", "Cannot have both positive and negative heights!");
256  if (_biases.size() != _heights.size())
257  paramError("biases", "Size of this parameter, if provided, must be the same as heights.");
258 
260  _upward_boundary_ids.size() != num_elevations)
261  paramError("upward_boundary_ids",
262  "This parameter must have the same length (" +
263  std::to_string(_upward_boundary_ids.size()) +
264  ") as upward_boundary_source_blocks (" +
265  std::to_string(_upward_boundary_source_blocks.size()) + ") and heights (" +
266  std::to_string(num_elevations) + ")");
267  for (unsigned int i = 0; i < _upward_boundary_source_blocks.size(); i++)
268  if (_upward_boundary_source_blocks[i].size() != _upward_boundary_ids[i].size())
269  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 
274  _downward_boundary_ids.size() != num_elevations)
275  paramError("downward_boundary_ids",
276  "This parameter must have the same length (" +
277  std::to_string(_downward_boundary_ids.size()) +
278  ") as downward_boundary_source_blocks (" +
279  std::to_string(_downward_boundary_source_blocks.size()) + ") and heights (" +
280  std::to_string(num_elevations) + ")");
281  for (unsigned int i = 0; i < _downward_boundary_source_blocks.size(); i++)
282  if (_downward_boundary_source_blocks[i].size() != _downward_boundary_ids[i].size())
283  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 }
287 
288 std::unique_ptr<MeshBase>
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  auto mesh = buildMeshBaseObject(_input->mesh_dimension() + 1);
296  mesh->set_mesh_dimension(_input->mesh_dimension() + 1);
297 
298  // Check if the element integer names are existent in the input mesh.
299  for (unsigned int i = 0; i < _elem_integer_names_to_swap.size(); i++)
300  if (_input->has_elem_integer(_elem_integer_names_to_swap[i]))
302  _input->get_elem_integer_index(_elem_integer_names_to_swap[i]));
303  else
304  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  const unsigned int num_extra_elem_integers = _input->n_elem_integers();
312  std::vector<std::string> id_names;
313 
314  for (unsigned int i = 0; i < num_extra_elem_integers; i++)
315  {
316  id_names.push_back(_input->get_elem_integer_name(i));
317  if (!mesh->has_elem_integer(id_names[i]))
318  mesh->add_elem_integer(id_names[i]);
319  }
320 
321  // retrieve subdomain/sideset/nodeset name maps
322  const auto & input_subdomain_map = _input->get_subdomain_name_map();
323  const auto & input_sideset_map = _input->get_boundary_info().get_sideset_name_map();
324  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  for (const auto & swap : _subdomain_swaps)
328  for (const auto i : index_range(swap))
329  if (i % 2 == 0 && !MooseMeshUtils::hasSubdomainID(*_input, swap[i]))
330  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  for (const auto & swap : _boundary_swaps)
334  for (const auto i : index_range(swap))
335  if (i % 2 == 0 && !MooseMeshUtils::hasBoundaryID(*_input, swap[i]))
336  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  for (const auto & layer_vec : _upward_boundary_source_blocks)
340  for (const auto bid : layer_vec)
342  paramError(
343  "upward_boundary_source_blocks", "The block '", bid, "' was not found within the mesh");
344  for (const auto & layer_vec : _downward_boundary_source_blocks)
345  for (const auto bid : layer_vec)
347  paramError("downward_boundary_source_blocks",
348  "The block '",
349  bid,
350  "' was not found within the mesh");
351 
352  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  if (!input->is_serial())
357  mesh->delete_remote_elements();
358 
359  unsigned int total_num_layers = std::accumulate(_num_layers.begin(), _num_layers.end(), 0);
360 
361  auto total_num_elevations = _heights.size();
362 
363  dof_id_type orig_elem = input->n_elem();
364  dof_id_type orig_nodes = input->n_nodes();
365 
366 #ifdef LIBMESH_ENABLE_UNIQUE_ID
367  unique_id_type orig_unique_ids = input->parallel_max_unique_id();
368 #endif
369 
370  unsigned int order = 1;
371 
372  BoundaryInfo & boundary_info = mesh->get_boundary_info();
373  const BoundaryInfo & input_boundary_info = input->get_boundary_info();
374 
375  // Determine boundary IDs for the new user provided boundary names
376  std::vector<BoundaryName> new_boundary_names;
378  new_boundary_names.push_back(_bottom_boundary);
379  if (_has_top_boundary)
380  new_boundary_names.push_back(_top_boundary);
381  std::vector<boundary_id_type> new_boundary_ids =
382  MooseMeshUtils::getBoundaryIDs(*input, new_boundary_names, true);
383  const auto user_bottom_boundary_id =
384  _has_bottom_boundary ? new_boundary_ids.front() : libMesh::BoundaryInfo::invalid_id;
385  const auto user_top_boundary_id =
386  _has_top_boundary ? new_boundary_ids.back() : libMesh::BoundaryInfo::invalid_id;
387 
388  // We know a priori how many elements we'll need
389  mesh->reserve_elem(total_num_layers * orig_elem);
390 
391  // Look for higher order elements which introduce an extra layer
392  std::set<ElemType> higher_orders = {EDGE3, EDGE4, TRI6, TRI7, QUAD8, QUAD9};
393  bool extruding_quad_eights = false;
394  std::vector<ElemType> types;
395  MeshTools::elem_types(*input, types);
396  for (const auto elem_type : types)
397  {
398  if (higher_orders.count(elem_type))
399  order = 2;
400  if (elem_type == QUAD8)
401  extruding_quad_eights = true;
402  }
403  mesh->comm().max(order);
404  mesh->comm().max(extruding_quad_eights);
405 
406  // Reserve for the max number possibly needed
407  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  std::vector<boundary_id_type> ids_to_copy;
411 
412  Point old_distance;
413  Point current_distance;
414 
415  // Create translated layers of nodes in the direction of extrusion
416  for (const auto & node : input->node_ptr_range())
417  {
418  unsigned int current_node_layer = 0;
419 
420  old_distance.zero();
421 
422  // e is the elevation layer ordering
423  for (unsigned int e = 0; e < total_num_elevations; e++)
424  {
425  auto num_layers = _num_layers[e];
426 
427  auto height = _heights[e];
428 
429  auto bias = _biases[e];
430 
431  // k is the element layer ordering within each elevation layer
432  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  if (e == 0 && k == 0)
436  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  auto layer_index = (k - (e == 0 ? 1 : 0)) / order + 1;
442 
443  const auto step_size = MooseUtils::absoluteFuzzyEqual(bias, 1.0)
444  ? height / (Real)num_layers / (Real)order
445  : height * std::pow(bias, (Real)(layer_index - 1)) *
446  (1.0 - bias) /
447  (1.0 - std::pow(bias, (Real)(num_layers))) / (Real)order;
448 
449  current_distance = old_distance + _direction * step_size;
450 
451  // Handle helicoidal extrusion
453  {
454  // twist 1 should be 'normal' to the extruded shape
455  RealVectorValue twist1 = _direction.cross(*node);
456  // This happens for any node on the helicoidal extrusion axis
457  if (!MooseUtils::absoluteFuzzyEqual(twist1.norm(), .0))
458  twist1 /= twist1.norm();
459  const RealVectorValue twist2 = twist1.cross(_direction);
460 
461  auto twist = (cos(2. * libMesh::pi * layer_index * step_size / _twist_pitch) -
462  cos(2. * libMesh::pi * (layer_index - 1) * step_size / _twist_pitch)) *
463  twist2 +
464  (sin(2. * libMesh::pi * layer_index * step_size / _twist_pitch) -
465  sin(2. * libMesh::pi * (layer_index - 1) * step_size / _twist_pitch)) *
466  twist1;
467  twist *= std::sqrt(node->norm_sq() + libMesh::Utility::pow<2>(_direction * (*node)));
468  current_distance += twist;
469  }
470  }
471 
472  Node * new_node = mesh->add_point(*node + current_distance,
473  node->id() + (current_node_layer * orig_nodes),
474  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  ? node->unique_id()
482  : orig_unique_ids +
483  (current_node_layer - 1) * (orig_nodes + orig_elem) +
484  node->id();
485 
486  new_node->set_unique_id(uid);
487 #endif
488 
489  input_boundary_info.boundary_ids(node, ids_to_copy);
490  if (_boundary_swap_pairs.empty())
491  boundary_info.add_node(new_node, ids_to_copy);
492  else
493  for (const auto & id_to_copy : ids_to_copy)
494  {
495  boundary_info.add_node(new_node,
496  _boundary_swap_pairs[e].count(id_to_copy)
497  ? _boundary_swap_pairs[e][id_to_copy]
498  : id_to_copy);
499  }
500 
501  old_distance = current_distance;
502  current_node_layer++;
503  }
504  }
505  }
506 
507  const auto & side_ids = input_boundary_info.get_side_boundary_ids();
508 
509  boundary_id_type next_side_id =
510  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  input->comm().max(next_side_id);
516 
517  for (const auto & elem : input->element_ptr_range())
518  {
519  const ElemType etype = elem->type();
520 
521  // build_extrusion currently only works on coarse meshes
522  libmesh_assert(!elem->parent());
523 
524  unsigned int current_layer = 0;
525 
526  for (unsigned int e = 0; e != total_num_elevations; e++)
527  {
528  auto num_layers = _num_layers[e];
529 
530  for (unsigned int k = 0; k != num_layers; ++k)
531  {
532  std::unique_ptr<Elem> new_elem;
533  bool is_flipped(false);
534  switch (etype)
535  {
536  case EDGE2:
537  {
538  new_elem = std::make_unique<Quad4>();
539  new_elem->set_node(
540  0, mesh->node_ptr(elem->node_ptr(0)->id() + (current_layer * orig_nodes)));
541  new_elem->set_node(
542  1, mesh->node_ptr(elem->node_ptr(1)->id() + (current_layer * orig_nodes)));
543  new_elem->set_node(
544  2, mesh->node_ptr(elem->node_ptr(1)->id() + ((current_layer + 1) * orig_nodes)));
545  new_elem->set_node(
546  3, mesh->node_ptr(elem->node_ptr(0)->id() + ((current_layer + 1) * orig_nodes)));
547 
548  if (elem->neighbor_ptr(0) == remote_elem)
549  new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
550  if (elem->neighbor_ptr(1) == remote_elem)
551  new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
552 
553  break;
554  }
555  case EDGE3:
556  {
557  new_elem = std::make_unique<Quad9>();
558  new_elem->set_node(
559  0, mesh->node_ptr(elem->node_ptr(0)->id() + (2 * current_layer * orig_nodes)));
560  new_elem->set_node(
561  1, mesh->node_ptr(elem->node_ptr(1)->id() + (2 * current_layer * orig_nodes)));
562  new_elem->set_node(
563  2,
564  mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 2) * orig_nodes)));
565  new_elem->set_node(
566  3,
567  mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 2) * orig_nodes)));
568  new_elem->set_node(
569  4, mesh->node_ptr(elem->node_ptr(2)->id() + (2 * current_layer * orig_nodes)));
570  new_elem->set_node(
571  5,
572  mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 1) * orig_nodes)));
573  new_elem->set_node(
574  6,
575  mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 2) * orig_nodes)));
576  new_elem->set_node(
577  7,
578  mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 1) * orig_nodes)));
579  new_elem->set_node(
580  8,
581  mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 1) * orig_nodes)));
582 
583  if (elem->neighbor_ptr(0) == remote_elem)
584  new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
585  if (elem->neighbor_ptr(1) == remote_elem)
586  new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
587 
588  break;
589  }
590  case TRI3:
591  {
592  new_elem = std::make_unique<Prism6>();
593  new_elem->set_node(
594  0, mesh->node_ptr(elem->node_ptr(0)->id() + (current_layer * orig_nodes)));
595  new_elem->set_node(
596  1, mesh->node_ptr(elem->node_ptr(1)->id() + (current_layer * orig_nodes)));
597  new_elem->set_node(
598  2, mesh->node_ptr(elem->node_ptr(2)->id() + (current_layer * orig_nodes)));
599  new_elem->set_node(
600  3, mesh->node_ptr(elem->node_ptr(0)->id() + ((current_layer + 1) * orig_nodes)));
601  new_elem->set_node(
602  4, mesh->node_ptr(elem->node_ptr(1)->id() + ((current_layer + 1) * orig_nodes)));
603  new_elem->set_node(
604  5, mesh->node_ptr(elem->node_ptr(2)->id() + ((current_layer + 1) * orig_nodes)));
605 
606  if (elem->neighbor_ptr(0) == remote_elem)
607  new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
608  if (elem->neighbor_ptr(1) == remote_elem)
609  new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
610  if (elem->neighbor_ptr(2) == remote_elem)
611  new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
612 
613  if (new_elem->volume() < 0.0)
614  {
615  MooseMeshUtils::swapNodesInElem(*new_elem, 0, 3);
616  MooseMeshUtils::swapNodesInElem(*new_elem, 1, 4);
617  MooseMeshUtils::swapNodesInElem(*new_elem, 2, 5);
618  is_flipped = true;
619  }
620 
621  break;
622  }
623  case TRI6:
624  {
625  new_elem = std::make_unique<Prism18>();
626  new_elem->set_node(
627  0, mesh->node_ptr(elem->node_ptr(0)->id() + (2 * current_layer * orig_nodes)));
628  new_elem->set_node(
629  1, mesh->node_ptr(elem->node_ptr(1)->id() + (2 * current_layer * orig_nodes)));
630  new_elem->set_node(
631  2, mesh->node_ptr(elem->node_ptr(2)->id() + (2 * current_layer * orig_nodes)));
632  new_elem->set_node(
633  3,
634  mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 2) * orig_nodes)));
635  new_elem->set_node(
636  4,
637  mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 2) * orig_nodes)));
638  new_elem->set_node(
639  5,
640  mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 2) * orig_nodes)));
641  new_elem->set_node(
642  6, mesh->node_ptr(elem->node_ptr(3)->id() + (2 * current_layer * orig_nodes)));
643  new_elem->set_node(
644  7, mesh->node_ptr(elem->node_ptr(4)->id() + (2 * current_layer * orig_nodes)));
645  new_elem->set_node(
646  8, mesh->node_ptr(elem->node_ptr(5)->id() + (2 * current_layer * orig_nodes)));
647  new_elem->set_node(
648  9,
649  mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 1) * orig_nodes)));
650  new_elem->set_node(
651  10,
652  mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 1) * orig_nodes)));
653  new_elem->set_node(
654  11,
655  mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 1) * orig_nodes)));
656  new_elem->set_node(
657  12,
658  mesh->node_ptr(elem->node_ptr(3)->id() + ((2 * current_layer + 2) * orig_nodes)));
659  new_elem->set_node(
660  13,
661  mesh->node_ptr(elem->node_ptr(4)->id() + ((2 * current_layer + 2) * orig_nodes)));
662  new_elem->set_node(
663  14,
664  mesh->node_ptr(elem->node_ptr(5)->id() + ((2 * current_layer + 2) * orig_nodes)));
665  new_elem->set_node(
666  15,
667  mesh->node_ptr(elem->node_ptr(3)->id() + ((2 * current_layer + 1) * orig_nodes)));
668  new_elem->set_node(
669  16,
670  mesh->node_ptr(elem->node_ptr(4)->id() + ((2 * current_layer + 1) * orig_nodes)));
671  new_elem->set_node(
672  17,
673  mesh->node_ptr(elem->node_ptr(5)->id() + ((2 * current_layer + 1) * orig_nodes)));
674 
675  if (elem->neighbor_ptr(0) == remote_elem)
676  new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
677  if (elem->neighbor_ptr(1) == remote_elem)
678  new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
679  if (elem->neighbor_ptr(2) == remote_elem)
680  new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
681 
682  if (new_elem->volume() < 0.0)
683  {
684  MooseMeshUtils::swapNodesInElem(*new_elem, 0, 3);
685  MooseMeshUtils::swapNodesInElem(*new_elem, 1, 4);
686  MooseMeshUtils::swapNodesInElem(*new_elem, 2, 5);
687  MooseMeshUtils::swapNodesInElem(*new_elem, 6, 12);
688  MooseMeshUtils::swapNodesInElem(*new_elem, 7, 13);
689  MooseMeshUtils::swapNodesInElem(*new_elem, 8, 14);
690  is_flipped = true;
691  }
692 
693  break;
694  }
695  case TRI7:
696  {
697  new_elem = std::make_unique<Prism21>();
698  new_elem->set_node(
699  0, mesh->node_ptr(elem->node_ptr(0)->id() + (2 * current_layer * orig_nodes)));
700  new_elem->set_node(
701  1, mesh->node_ptr(elem->node_ptr(1)->id() + (2 * current_layer * orig_nodes)));
702  new_elem->set_node(
703  2, mesh->node_ptr(elem->node_ptr(2)->id() + (2 * current_layer * orig_nodes)));
704  new_elem->set_node(
705  3,
706  mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 2) * orig_nodes)));
707  new_elem->set_node(
708  4,
709  mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 2) * orig_nodes)));
710  new_elem->set_node(
711  5,
712  mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 2) * orig_nodes)));
713  new_elem->set_node(
714  6, mesh->node_ptr(elem->node_ptr(3)->id() + (2 * current_layer * orig_nodes)));
715  new_elem->set_node(
716  7, mesh->node_ptr(elem->node_ptr(4)->id() + (2 * current_layer * orig_nodes)));
717  new_elem->set_node(
718  8, mesh->node_ptr(elem->node_ptr(5)->id() + (2 * current_layer * orig_nodes)));
719  new_elem->set_node(
720  9,
721  mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 1) * orig_nodes)));
722  new_elem->set_node(
723  10,
724  mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 1) * orig_nodes)));
725  new_elem->set_node(
726  11,
727  mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 1) * orig_nodes)));
728  new_elem->set_node(
729  12,
730  mesh->node_ptr(elem->node_ptr(3)->id() + ((2 * current_layer + 2) * orig_nodes)));
731  new_elem->set_node(
732  13,
733  mesh->node_ptr(elem->node_ptr(4)->id() + ((2 * current_layer + 2) * orig_nodes)));
734  new_elem->set_node(
735  14,
736  mesh->node_ptr(elem->node_ptr(5)->id() + ((2 * current_layer + 2) * orig_nodes)));
737  new_elem->set_node(
738  15,
739  mesh->node_ptr(elem->node_ptr(3)->id() + ((2 * current_layer + 1) * orig_nodes)));
740  new_elem->set_node(
741  16,
742  mesh->node_ptr(elem->node_ptr(4)->id() + ((2 * current_layer + 1) * orig_nodes)));
743  new_elem->set_node(
744  17,
745  mesh->node_ptr(elem->node_ptr(5)->id() + ((2 * current_layer + 1) * orig_nodes)));
746  new_elem->set_node(
747  18, mesh->node_ptr(elem->node_ptr(6)->id() + (2 * current_layer * orig_nodes)));
748  new_elem->set_node(
749  19,
750  mesh->node_ptr(elem->node_ptr(6)->id() + ((2 * current_layer + 2) * orig_nodes)));
751  new_elem->set_node(
752  20,
753  mesh->node_ptr(elem->node_ptr(6)->id() + ((2 * current_layer + 1) * orig_nodes)));
754 
755  if (elem->neighbor_ptr(0) == remote_elem)
756  new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
757  if (elem->neighbor_ptr(1) == remote_elem)
758  new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
759  if (elem->neighbor_ptr(2) == remote_elem)
760  new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
761 
762  if (new_elem->volume() < 0.0)
763  {
764  MooseMeshUtils::swapNodesInElem(*new_elem, 0, 3);
765  MooseMeshUtils::swapNodesInElem(*new_elem, 1, 4);
766  MooseMeshUtils::swapNodesInElem(*new_elem, 2, 5);
767  MooseMeshUtils::swapNodesInElem(*new_elem, 6, 12);
768  MooseMeshUtils::swapNodesInElem(*new_elem, 7, 13);
769  MooseMeshUtils::swapNodesInElem(*new_elem, 8, 14);
770  MooseMeshUtils::swapNodesInElem(*new_elem, 18, 19);
771  is_flipped = true;
772  }
773 
774  break;
775  }
776  case QUAD4:
777  {
778  new_elem = std::make_unique<Hex8>();
779  new_elem->set_node(
780  0, mesh->node_ptr(elem->node_ptr(0)->id() + (current_layer * orig_nodes)));
781  new_elem->set_node(
782  1, mesh->node_ptr(elem->node_ptr(1)->id() + (current_layer * orig_nodes)));
783  new_elem->set_node(
784  2, mesh->node_ptr(elem->node_ptr(2)->id() + (current_layer * orig_nodes)));
785  new_elem->set_node(
786  3, mesh->node_ptr(elem->node_ptr(3)->id() + (current_layer * orig_nodes)));
787  new_elem->set_node(
788  4, mesh->node_ptr(elem->node_ptr(0)->id() + ((current_layer + 1) * orig_nodes)));
789  new_elem->set_node(
790  5, mesh->node_ptr(elem->node_ptr(1)->id() + ((current_layer + 1) * orig_nodes)));
791  new_elem->set_node(
792  6, mesh->node_ptr(elem->node_ptr(2)->id() + ((current_layer + 1) * orig_nodes)));
793  new_elem->set_node(
794  7, mesh->node_ptr(elem->node_ptr(3)->id() + ((current_layer + 1) * orig_nodes)));
795 
796  if (elem->neighbor_ptr(0) == remote_elem)
797  new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
798  if (elem->neighbor_ptr(1) == remote_elem)
799  new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
800  if (elem->neighbor_ptr(2) == remote_elem)
801  new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
802  if (elem->neighbor_ptr(3) == remote_elem)
803  new_elem->set_neighbor(4, const_cast<RemoteElem *>(remote_elem));
804 
805  if (new_elem->volume() < 0.0)
806  {
807  MooseMeshUtils::swapNodesInElem(*new_elem, 0, 4);
808  MooseMeshUtils::swapNodesInElem(*new_elem, 1, 5);
809  MooseMeshUtils::swapNodesInElem(*new_elem, 2, 6);
810  MooseMeshUtils::swapNodesInElem(*new_elem, 3, 7);
811  is_flipped = true;
812  }
813 
814  break;
815  }
816  case QUAD8:
817  {
818  new_elem = std::make_unique<Hex20>();
819  new_elem->set_node(
820  0, mesh->node_ptr(elem->node_ptr(0)->id() + (2 * current_layer * orig_nodes)));
821  new_elem->set_node(
822  1, mesh->node_ptr(elem->node_ptr(1)->id() + (2 * current_layer * orig_nodes)));
823  new_elem->set_node(
824  2, mesh->node_ptr(elem->node_ptr(2)->id() + (2 * current_layer * orig_nodes)));
825  new_elem->set_node(
826  3, mesh->node_ptr(elem->node_ptr(3)->id() + (2 * current_layer * orig_nodes)));
827  new_elem->set_node(
828  4,
829  mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 2) * orig_nodes)));
830  new_elem->set_node(
831  5,
832  mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 2) * orig_nodes)));
833  new_elem->set_node(
834  6,
835  mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 2) * orig_nodes)));
836  new_elem->set_node(
837  7,
838  mesh->node_ptr(elem->node_ptr(3)->id() + ((2 * current_layer + 2) * orig_nodes)));
839  new_elem->set_node(
840  8, mesh->node_ptr(elem->node_ptr(4)->id() + (2 * current_layer * orig_nodes)));
841  new_elem->set_node(
842  9, mesh->node_ptr(elem->node_ptr(5)->id() + (2 * current_layer * orig_nodes)));
843  new_elem->set_node(
844  10, mesh->node_ptr(elem->node_ptr(6)->id() + (2 * current_layer * orig_nodes)));
845  new_elem->set_node(
846  11, mesh->node_ptr(elem->node_ptr(7)->id() + (2 * current_layer * orig_nodes)));
847  new_elem->set_node(
848  12,
849  mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 1) * orig_nodes)));
850  new_elem->set_node(
851  13,
852  mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 1) * orig_nodes)));
853  new_elem->set_node(
854  14,
855  mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 1) * orig_nodes)));
856  new_elem->set_node(
857  15,
858  mesh->node_ptr(elem->node_ptr(3)->id() + ((2 * current_layer + 1) * orig_nodes)));
859  new_elem->set_node(
860  16,
861  mesh->node_ptr(elem->node_ptr(4)->id() + ((2 * current_layer + 2) * orig_nodes)));
862  new_elem->set_node(
863  17,
864  mesh->node_ptr(elem->node_ptr(5)->id() + ((2 * current_layer + 2) * orig_nodes)));
865  new_elem->set_node(
866  18,
867  mesh->node_ptr(elem->node_ptr(6)->id() + ((2 * current_layer + 2) * orig_nodes)));
868  new_elem->set_node(
869  19,
870  mesh->node_ptr(elem->node_ptr(7)->id() + ((2 * current_layer + 2) * orig_nodes)));
871 
872  if (elem->neighbor_ptr(0) == remote_elem)
873  new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
874  if (elem->neighbor_ptr(1) == remote_elem)
875  new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
876  if (elem->neighbor_ptr(2) == remote_elem)
877  new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
878  if (elem->neighbor_ptr(3) == remote_elem)
879  new_elem->set_neighbor(4, const_cast<RemoteElem *>(remote_elem));
880 
881  if (new_elem->volume() < 0.0)
882  {
883  MooseMeshUtils::swapNodesInElem(*new_elem, 0, 4);
884  MooseMeshUtils::swapNodesInElem(*new_elem, 1, 5);
885  MooseMeshUtils::swapNodesInElem(*new_elem, 2, 6);
886  MooseMeshUtils::swapNodesInElem(*new_elem, 3, 7);
887  MooseMeshUtils::swapNodesInElem(*new_elem, 8, 16);
888  MooseMeshUtils::swapNodesInElem(*new_elem, 9, 17);
889  MooseMeshUtils::swapNodesInElem(*new_elem, 10, 18);
890  MooseMeshUtils::swapNodesInElem(*new_elem, 11, 19);
891  is_flipped = true;
892  }
893 
894  break;
895  }
896  case QUAD9:
897  {
898  new_elem = std::make_unique<Hex27>();
899  new_elem->set_node(
900  0, mesh->node_ptr(elem->node_ptr(0)->id() + (2 * current_layer * orig_nodes)));
901  new_elem->set_node(
902  1, mesh->node_ptr(elem->node_ptr(1)->id() + (2 * current_layer * orig_nodes)));
903  new_elem->set_node(
904  2, mesh->node_ptr(elem->node_ptr(2)->id() + (2 * current_layer * orig_nodes)));
905  new_elem->set_node(
906  3, mesh->node_ptr(elem->node_ptr(3)->id() + (2 * current_layer * orig_nodes)));
907  new_elem->set_node(
908  4,
909  mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 2) * orig_nodes)));
910  new_elem->set_node(
911  5,
912  mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 2) * orig_nodes)));
913  new_elem->set_node(
914  6,
915  mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 2) * orig_nodes)));
916  new_elem->set_node(
917  7,
918  mesh->node_ptr(elem->node_ptr(3)->id() + ((2 * current_layer + 2) * orig_nodes)));
919  new_elem->set_node(
920  8, mesh->node_ptr(elem->node_ptr(4)->id() + (2 * current_layer * orig_nodes)));
921  new_elem->set_node(
922  9, mesh->node_ptr(elem->node_ptr(5)->id() + (2 * current_layer * orig_nodes)));
923  new_elem->set_node(
924  10, mesh->node_ptr(elem->node_ptr(6)->id() + (2 * current_layer * orig_nodes)));
925  new_elem->set_node(
926  11, mesh->node_ptr(elem->node_ptr(7)->id() + (2 * current_layer * orig_nodes)));
927  new_elem->set_node(
928  12,
929  mesh->node_ptr(elem->node_ptr(0)->id() + ((2 * current_layer + 1) * orig_nodes)));
930  new_elem->set_node(
931  13,
932  mesh->node_ptr(elem->node_ptr(1)->id() + ((2 * current_layer + 1) * orig_nodes)));
933  new_elem->set_node(
934  14,
935  mesh->node_ptr(elem->node_ptr(2)->id() + ((2 * current_layer + 1) * orig_nodes)));
936  new_elem->set_node(
937  15,
938  mesh->node_ptr(elem->node_ptr(3)->id() + ((2 * current_layer + 1) * orig_nodes)));
939  new_elem->set_node(
940  16,
941  mesh->node_ptr(elem->node_ptr(4)->id() + ((2 * current_layer + 2) * orig_nodes)));
942  new_elem->set_node(
943  17,
944  mesh->node_ptr(elem->node_ptr(5)->id() + ((2 * current_layer + 2) * orig_nodes)));
945  new_elem->set_node(
946  18,
947  mesh->node_ptr(elem->node_ptr(6)->id() + ((2 * current_layer + 2) * orig_nodes)));
948  new_elem->set_node(
949  19,
950  mesh->node_ptr(elem->node_ptr(7)->id() + ((2 * current_layer + 2) * orig_nodes)));
951  new_elem->set_node(
952  20, mesh->node_ptr(elem->node_ptr(8)->id() + (2 * current_layer * orig_nodes)));
953  new_elem->set_node(
954  21,
955  mesh->node_ptr(elem->node_ptr(4)->id() + ((2 * current_layer + 1) * orig_nodes)));
956  new_elem->set_node(
957  22,
958  mesh->node_ptr(elem->node_ptr(5)->id() + ((2 * current_layer + 1) * orig_nodes)));
959  new_elem->set_node(
960  23,
961  mesh->node_ptr(elem->node_ptr(6)->id() + ((2 * current_layer + 1) * orig_nodes)));
962  new_elem->set_node(
963  24,
964  mesh->node_ptr(elem->node_ptr(7)->id() + ((2 * current_layer + 1) * orig_nodes)));
965  new_elem->set_node(
966  25,
967  mesh->node_ptr(elem->node_ptr(8)->id() + ((2 * current_layer + 2) * orig_nodes)));
968  new_elem->set_node(
969  26,
970  mesh->node_ptr(elem->node_ptr(8)->id() + ((2 * current_layer + 1) * orig_nodes)));
971 
972  if (elem->neighbor_ptr(0) == remote_elem)
973  new_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
974  if (elem->neighbor_ptr(1) == remote_elem)
975  new_elem->set_neighbor(2, const_cast<RemoteElem *>(remote_elem));
976  if (elem->neighbor_ptr(2) == remote_elem)
977  new_elem->set_neighbor(3, const_cast<RemoteElem *>(remote_elem));
978  if (elem->neighbor_ptr(3) == remote_elem)
979  new_elem->set_neighbor(4, const_cast<RemoteElem *>(remote_elem));
980 
981  if (new_elem->volume() < 0.0)
982  {
983  MooseMeshUtils::swapNodesInElem(*new_elem, 0, 4);
984  MooseMeshUtils::swapNodesInElem(*new_elem, 1, 5);
985  MooseMeshUtils::swapNodesInElem(*new_elem, 2, 6);
986  MooseMeshUtils::swapNodesInElem(*new_elem, 3, 7);
987  MooseMeshUtils::swapNodesInElem(*new_elem, 8, 16);
988  MooseMeshUtils::swapNodesInElem(*new_elem, 9, 17);
989  MooseMeshUtils::swapNodesInElem(*new_elem, 10, 18);
990  MooseMeshUtils::swapNodesInElem(*new_elem, 11, 19);
991  MooseMeshUtils::swapNodesInElem(*new_elem, 20, 25);
992  is_flipped = true;
993  }
994 
995  break;
996  }
997  default:
998  mooseError("Extrusion is not implemented for element type " + Moose::stringify(etype));
999  }
1000 
1001  new_elem->set_id(elem->id() + (current_layer * orig_elem));
1002  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  ? elem->unique_id()
1010  : orig_unique_ids +
1011  (current_layer - 1) * (orig_nodes + orig_elem) +
1012  orig_nodes + elem->id();
1013 
1014  new_elem->set_unique_id(uid);
1015 #endif
1016 
1017  // maintain the subdomain_id
1018  new_elem->subdomain_id() = elem->subdomain_id();
1019 
1020  // define upward boundaries
1021  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  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  for (unsigned int i = 0; i < _upward_boundary_source_blocks[e].size(); i++)
1029  if (new_elem->subdomain_id() == _upward_boundary_source_blocks[e][i])
1030  boundary_info.add_side(
1031  new_elem.get(), is_flipped ? 0 : top_id, _upward_boundary_ids[e][i]);
1032  }
1033  // define downward boundaries
1034  if (k == 0)
1035  {
1036  const unsigned short top_id =
1037  new_elem->dim() == 3 ? cast_int<unsigned short>(elem->n_sides() + 1) : 2;
1038  for (unsigned int i = 0; i < _downward_boundary_source_blocks[e].size(); i++)
1039  if (new_elem->subdomain_id() == _downward_boundary_source_blocks[e][i])
1040  boundary_info.add_side(
1041  new_elem.get(), is_flipped ? top_id : 0, _downward_boundary_ids[e][i]);
1042  }
1043 
1044  // perform subdomain swaps
1045  if (_subdomain_swap_pairs.size())
1046  {
1047  auto & elevation_swap_pairs = _subdomain_swap_pairs[e];
1048 
1049  auto new_id_it = elevation_swap_pairs.find(elem->subdomain_id());
1050 
1051  if (new_id_it != elevation_swap_pairs.end())
1052  new_elem->subdomain_id() = new_id_it->second;
1053  }
1054 
1055  Elem * added_elem = mesh->add_elem(std::move(new_elem));
1056 
1057  // maintain extra integers
1058  for (unsigned int i = 0; i < num_extra_elem_integers; i++)
1059  added_elem->set_extra_integer(i, elem->get_extra_integer(i));
1060 
1061  if (_elem_integers_swap_pairs.size())
1062  {
1063  for (unsigned int i = 0; i < _elem_integer_indices_to_swap.size(); i++)
1064  {
1065  auto & elevation_extra_swap_pairs = _elem_integers_swap_pairs[i * _heights.size() + e];
1066 
1067  auto new_extra_id_it = elevation_extra_swap_pairs.find(
1068  elem->get_extra_integer(_elem_integer_indices_to_swap[i]));
1069 
1070  if (new_extra_id_it != elevation_extra_swap_pairs.end())
1071  added_elem->set_extra_integer(_elem_integer_indices_to_swap[i],
1072  new_extra_id_it->second);
1073  }
1074  }
1075 
1076  // Copy any old boundary ids on all sides
1077  for (auto s : elem->side_index_range())
1078  {
1079  input_boundary_info.boundary_ids(elem, s, ids_to_copy);
1080 
1081  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  if (_boundary_swap_pairs.empty())
1088  boundary_info.add_side(added_elem, cast_int<unsigned short>(s + 1), ids_to_copy);
1089  else
1090  for (const auto & id_to_copy : ids_to_copy)
1091  boundary_info.add_side(added_elem,
1092  cast_int<unsigned short>(s + 1),
1093  _boundary_swap_pairs[e].count(id_to_copy)
1094  ? _boundary_swap_pairs[e][id_to_copy]
1095  : 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  const unsigned short sidemap[2] = {3, 1};
1105  if (_boundary_swap_pairs.empty())
1106  boundary_info.add_side(added_elem, sidemap[s], ids_to_copy);
1107  else
1108  for (const auto & id_to_copy : ids_to_copy)
1109  boundary_info.add_side(added_elem,
1110  sidemap[s],
1111  _boundary_swap_pairs[e].count(id_to_copy)
1112  ? _boundary_swap_pairs[e][id_to_copy]
1113  : id_to_copy);
1114  }
1115  }
1116 
1117  // Give new boundary ids to bottom and top
1118  if (current_layer == 0)
1119  {
1120  const unsigned short top_id =
1121  added_elem->dim() == 3 ? cast_int<unsigned short>(elem->n_sides() + 1) : 2;
1123  {
1124  mooseAssert(user_bottom_boundary_id != libMesh::BoundaryInfo::invalid_id,
1125  "We should have retrieved a proper boundary ID");
1126  boundary_info.add_side(added_elem, is_flipped ? top_id : 0, user_bottom_boundary_id);
1127  }
1128  else
1129  boundary_info.add_side(added_elem, is_flipped ? top_id : 0, next_side_id);
1130  }
1131 
1132  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  added_elem->dim() == 3 ? cast_int<unsigned short>(elem->n_sides() + 1) : 2;
1139 
1140  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  boundary_info.add_side(added_elem, is_flipped ? 0 : top_id, user_top_boundary_id);
1145  }
1146  else
1147  boundary_info.add_side(
1148  added_elem, is_flipped ? 0 : top_id, cast_int<boundary_id_type>(next_side_id + 1));
1149  }
1150 
1151  current_layer++;
1152  }
1153  }
1154  }
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  unsigned int total_new_node_layers = total_num_layers * order;
1160  unsigned int new_unique_ids = orig_unique_ids + (total_new_node_layers - 1) * orig_elem +
1161  total_new_node_layers * orig_nodes;
1162  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  if (!input_subdomain_map.empty())
1167  mesh->set_subdomain_name_map().insert(input_subdomain_map.begin(), input_subdomain_map.end());
1168  if (!input_sideset_map.empty())
1169  mesh->get_boundary_info().set_sideset_name_map().insert(input_sideset_map.begin(),
1170  input_sideset_map.end());
1171  if (!input_nodeset_map.empty())
1172  mesh->get_boundary_info().set_nodeset_name_map().insert(input_nodeset_map.begin(),
1173  input_nodeset_map.end());
1174 
1176  boundary_info.sideset_name(new_boundary_ids.front()) = new_boundary_names.front();
1177  if (_has_top_boundary)
1178  boundary_info.sideset_name(new_boundary_ids.back()) = new_boundary_names.back();
1179 
1180  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  if (extruding_quad_eights)
1184  mesh->prepare_for_use();
1185 
1186  return mesh;
1187 }
void swapNodesInElem(Elem &elem, const unsigned int nd1, const unsigned int nd2)
Swap two nodes within an element.
std::vector< std::unordered_map< boundary_id_type, boundary_id_type > > _boundary_swap_pairs
Easier to work with version of _boundary_swaps.
ElemType
std::unique_ptr< MeshBase > & _input
Mesh that comes from another generator.
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sin(_arg) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tan
virtual const char * what() const
Get out the error message.
auto norm() const -> decltype(std::norm(Real()))
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether two variables are equal within an absolute tolerance.
Definition: MooseUtils.h:380
QUAD8
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
EDGE4
void swap(std::vector< T > &data, const std::size_t idx0, const std::size_t idx1, const libMesh::Parallel::Communicator &comm)
Swap function for serial or distributed vector of data.
Definition: Shuffle.h:494
std::vector< unsigned int > _elem_integer_indices_to_swap
Point _direction
The direction of the extrusion.
MeshBase & mesh
const std::vector< Real > & _heights
Height of each elevation.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
AdvancedExtruderGenerator(const InputParameters &parameters)
bool hasBoundaryID(const MeshBase &input_mesh, const BoundaryID id)
Whether a particular boundary ID exists in the mesh.
const Real _twist_pitch
Axial pitch for a full rotation.
const std::vector< std::vector< subdomain_id_type > > _downward_boundary_source_blocks
The list of input mesh&#39;s blocks that need to be assigned downward boundary interfaces for each layer ...
virtual const std::string & name() const
Get the name of the class.
Definition: MooseBase.h:57
void addRequiredParam(const std::string &name, const std::string &doc_string)
This method adds a parameter and documentation string to the InputParameters object that will be extr...
TRI3
const std::vector< std::vector< boundary_id_type > > _upward_boundary_ids
Upward boundary interfaces for each layer of elevation.
QUAD4
bool hasSubdomainID(const MeshBase &input_mesh, const SubdomainID &id)
Whether a particular subdomain ID exists in the mesh.
Extrudes a mesh to another dimension.
int8_t boundary_id_type
static const boundary_id_type invalid_id
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template cos(_arg) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(cos
TRI6
const std::vector< std::vector< boundary_id_type > > _downward_boundary_ids
Downward boundary interfaces for each layer of elevation.
libmesh_assert(ctx)
void idSwapParametersProcessor(const std::string &class_name, const std::string &id_name, const std::vector< std::vector< T >> &id_swaps, std::vector< std::unordered_map< T, T >> &id_swap_pairs, const unsigned int row_index_shift=0)
Reprocess the swap related input parameters to make pairs out of them to ease further processing...
std::vector< BoundaryID > getBoundaryIDs(const libMesh::MeshBase &mesh, const std::vector< BoundaryName > &boundary_name, bool generate_unknown, const std::set< BoundaryID > &mesh_boundary_ids)
Gets the boundary IDs with their names.
std::vector< std::unordered_map< subdomain_id_type, subdomain_id_type > > _subdomain_swap_pairs
Easier to work with version of _sudomain_swaps.
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
TypeVector< typename CompareTypes< Real, T2 >::supertype > cross(const TypeVector< T2 > &v) const
static InputParameters validParams()
Definition: MeshGenerator.C:23
std::string stringify(const T &t)
conversion to string
Definition: Conversion.h:64
const std::vector< std::vector< subdomain_id_type > > & _subdomain_swaps
Subdomains to swap out for each elevation.
registerMooseObjectRenamed("MooseApp", FancyExtruderGenerator, "02/18/2023 24:00", AdvancedExtruderGenerator)
const std::vector< std::vector< boundary_id_type > > & _boundary_swaps
Boundaries to swap out for each elevation.
const boundary_id_type top_id
Provides a way for users to bail out of the current solve.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const std::vector< std::vector< std::vector< dof_id_type > > > & _elem_integers_swaps
Extra element integers to swap out for each elevation and each element interger name.
EDGE2
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sqrt(_arg)) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tanh
const std::vector< Real > _biases
Bias growth factor of each elevation.
TRI7
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type.
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump...
registerMooseObject("MooseApp", AdvancedExtruderGenerator)
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object...
QUAD9
const std::vector< std::vector< subdomain_id_type > > _upward_boundary_source_blocks
The list of input mesh&#39;s blocks that need to be assigned upward boundary interfaces for each layer of...
const std::vector< unsigned int > & _num_layers
Number of layers in each elevation.
void extraElemIntegerSwapParametersProcessor(const std::string &class_name, const unsigned int num_sections, const unsigned int num_integers, const std::vector< std::vector< std::vector< dof_id_type >>> &elem_integers_swaps, std::vector< std::unordered_map< dof_id_type, dof_id_type >> &elem_integers_swap_pairs)
Reprocess the elem_integers_swaps into maps so they are easier to use.
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
std::unique_ptr< MeshBase > buildMeshBaseObject(unsigned int dim=libMesh::invalid_uint)
Build a MeshBase object whose underlying type will be determined by the Mesh input file block...
static InputParameters validParams()
EDGE3
const std::vector< std::string > & _elem_integer_names_to_swap
Names and indices of extra element integers to swap.
MooseUnits pow(const MooseUnits &, int)
Definition: Units.C:537
MeshGenerators are objects that can modify or add to an existing mesh.
Definition: MeshGenerator.h:32
uint8_t unique_id_type
void ErrorVector unsigned int
auto index_range(const T &sizable)
std::vector< std::unordered_map< dof_id_type, dof_id_type > > _elem_integers_swap_pairs
Easier to work with version of _elem_integers_swaps.
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template pow< 2 >(tan(_arg))+1.0) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(sqrt
uint8_t dof_id_type
const Real pi
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
This method takes a space delimited list of parameter names and adds them to the specified group name...
const RemoteElem * remote_elem