https://mooseframework.inl.gov
RevolveGenerator.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 
10 #include "RevolveGenerator.h"
12 
13 #include "libmesh/cell_prism6.h"
14 #include "libmesh/cell_prism15.h"
15 #include "libmesh/cell_prism18.h"
16 #include "libmesh/cell_prism21.h"
17 #include "libmesh/cell_pyramid5.h"
18 #include "libmesh/cell_pyramid13.h"
19 #include "libmesh/cell_pyramid14.h"
20 #include "libmesh/cell_pyramid18.h"
21 #include "libmesh/cell_tet4.h"
22 #include "libmesh/cell_tet10.h"
23 #include "libmesh/cell_tet14.h"
24 #include "libmesh/cell_hex8.h"
25 #include "libmesh/cell_hex20.h"
26 #include "libmesh/cell_hex27.h"
27 #include "libmesh/face_tri3.h"
28 #include "libmesh/face_tri7.h"
29 #include "libmesh/face_quad4.h"
30 #include "libmesh/face_quad9.h"
31 #include "libmesh/point.h"
32 #include "libmesh/mesh_tools.h"
33 
34 using namespace libMesh;
35 
36 // C++ includes
37 #include <cmath>
38 
40 
43 {
45  params.addClassDescription("This RevolveGenerator object is designed to revolve a 1D mesh into "
46  "2D, or a 2D mesh into 3D based on an axis.");
47 
48  params.addRequiredParam<MeshGeneratorName>("input", "The mesh to revolve");
49 
50  params.addRequiredParam<Point>("axis_point", "A point on the axis of revolution");
51 
52  params.addRequiredParam<Point>("axis_direction", "The direction of the axis of revolution");
53 
54  params.addRangeCheckedParam<std::vector<Real>>(
55  "revolving_angles",
56  "revolving_angles<=360.0 & revolving_angles>0.0",
57  "The angles delineating each azimuthal section of revolution around the axis in degrees");
58 
59  params.addParam<std::vector<std::vector<subdomain_id_type>>>(
60  "subdomain_swaps",
61  {},
62  "For each row, every two entries are interpreted as a pair of "
63  "'from' and 'to' to remap the subdomains for that azimuthal section");
64 
65  params.addParam<std::vector<std::vector<boundary_id_type>>>(
66  "boundary_swaps",
67  {},
68  "For each row, every two entries are interpreted as a pair of "
69  "'from' and 'to' to remap the boundaries for that elevation");
70 
71  params.addParam<std::vector<std::string>>(
72  "elem_integer_names_to_swap",
73  {},
74  "Array of element extra integer names that need to be swapped during revolving.");
75 
76  params.addParam<std::vector<std::vector<std::vector<dof_id_type>>>>(
77  "elem_integers_swaps",
78  {},
79  "For each row, every two entries are interpreted as a pair of 'from' and 'to' to remap the "
80  "element extra integer for that elevation. If multiple element extra integers need to be "
81  "swapped, the enties are stacked based on the order provided in "
82  "'elem_integer_names_to_swap' to form the third dimension.");
83 
84  params.addParam<boundary_id_type>(
85  "start_boundary",
86  "The boundary ID to set on the starting boundary for a partial revolution.");
87 
88  params.addParam<boundary_id_type>(
89  "end_boundary", "The boundary ID to set on the ending boundary for partial revolving.");
90 
91  params.addParam<bool>(
92  "clockwise", true, "Revolve clockwise around the axis or not (i.e., counterclockwise)");
93 
94  params.addRequiredParam<std::vector<unsigned int>>(
95  "nums_azimuthal_intervals",
96  "List of the numbers of azimuthal interval discretization for each azimuthal section");
97 
98  params.addParam<bool>("preserve_volumes",
99  false,
100  "Whether the volume of the revolved mesh is preserving the circular area "
101  "by modifying (expanding) the radius to account for polygonization.");
102 
103  params.addParamNamesToGroup("start_boundary end_boundary", "Boundary Assignment");
104  params.addParamNamesToGroup(
105  "subdomain_swaps boundary_swaps elem_integer_names_to_swap elem_integers_swaps", "ID Swap");
106 
107  return params;
108 }
109 
111  : PolygonMeshGeneratorBase(parameters),
112  _input(getMesh("input")),
113  _axis_point(getParam<Point>("axis_point")),
114  _axis_direction(getParam<Point>("axis_direction")),
115  _revolving_angles(isParamValid("revolving_angles")
116  ? getParam<std::vector<Real>>("revolving_angles")
117  : std::vector<Real>(1, 360.0)),
118  _subdomain_swaps(getParam<std::vector<std::vector<subdomain_id_type>>>("subdomain_swaps")),
119  _boundary_swaps(getParam<std::vector<std::vector<boundary_id_type>>>("boundary_swaps")),
120  _elem_integer_names_to_swap(getParam<std::vector<std::string>>("elem_integer_names_to_swap")),
121  _elem_integers_swaps(
122  getParam<std::vector<std::vector<std::vector<dof_id_type>>>>("elem_integers_swaps")),
123  _clockwise(getParam<bool>("clockwise")),
124  _nums_azimuthal_intervals(getParam<std::vector<unsigned int>>("nums_azimuthal_intervals")),
125  _preserve_volumes(getParam<bool>("preserve_volumes")),
126  _has_start_boundary(isParamValid("start_boundary")),
127  _start_boundary(isParamValid("start_boundary") ? getParam<boundary_id_type>("start_boundary")
128  : 0),
129  _has_end_boundary(isParamValid("end_boundary")),
130  _end_boundary(isParamValid("end_boundary") ? getParam<boundary_id_type>("end_boundary") : 0),
131  _radius_correction_factor(1.0)
132 {
133  if (_revolving_angles.size() != _nums_azimuthal_intervals.size())
134  paramError("nums_azimuthal_intervals",
135  "The number of azimuthal intervals should be the same as the number of revolving "
136  "angles.");
137  if (_subdomain_swaps.size() && (_subdomain_swaps.size() != _nums_azimuthal_intervals.size()))
138  paramError(
139  "subdomain_swaps",
140  "If specified, 'subdomain_swaps' must be the same length as 'nums_azimuthal_intervals'.");
141 
142  if (_boundary_swaps.size() && (_boundary_swaps.size() != _nums_azimuthal_intervals.size()))
143  paramError(
144  "boundary_swaps",
145  "If specified, 'boundary_swaps' must be the same length as 'nums_azimuthal_intervals'.");
146 
147  for (const auto & unit_elem_integers_swaps : _elem_integers_swaps)
148  if (unit_elem_integers_swaps.size() != _nums_azimuthal_intervals.size())
149  paramError("elem_integers_swaps",
150  "If specified, each element of 'elem_integers_swaps' must have the same length as "
151  "the length of 'nums_azimuthal_intervals'.");
152 
153  if (_elem_integers_swaps.size() &&
155  paramError("elem_integers_swaps",
156  "If specified, 'elem_integers_swaps' must have the same length as the length of "
157  "'elem_integer_names_to_swap'.");
158 
160  MooseUtils::absoluteFuzzyEqual(
161  std::accumulate(_revolving_angles.begin(), _revolving_angles.end(), 0), 360.0)
162  ? true
163  : false;
164  if (MooseUtils::absoluteFuzzyGreaterThan(
165  std::accumulate(_revolving_angles.begin(), _revolving_angles.end(), 0), 360.0))
166  paramError("revolving_angles",
167  "The sum of revolving angles should be less than or equal to 360.");
168 
171  paramError("full_circle_revolving",
172  "starting or ending boundaries can only be assigned for partial revolving.");
173 
174  try
175  {
177  name(), "subdomain_swaps", _subdomain_swaps, _subdomain_swap_pairs);
178  }
179  catch (const MooseException & e)
180  {
181  paramError("subdomain_swaps", e.what());
182  }
183 
184  try
185  {
187  name(), "boundary_swaps", _boundary_swaps, _boundary_swap_pairs);
188  }
189  catch (const MooseException & e)
190  {
191  paramError("boundary_swaps", e.what());
192  }
193 
194  try
195  {
201  }
202  catch (const MooseException & e)
203  {
204  paramError("elem_integers_swaps", e.what());
205  }
206 }
207 
208 std::unique_ptr<MeshBase>
210 {
211  // Note: Inspired by AdvancedExtruderGenerator::generate()
212 
213  auto mesh = buildMeshBaseObject();
214 
215  // Only works for 1D and 2D input meshes
216  if (_input->mesh_dimension() > 2)
217  paramError("input", "This mesh generator only works for 1D and 2D input meshes.");
218 
219  mesh->set_mesh_dimension(_input->mesh_dimension() + 1);
220 
221  // Check if the element integer names are existent in the input mesh.
222  for (const auto i : index_range(_elem_integer_names_to_swap))
223  if (_input->has_elem_integer(_elem_integer_names_to_swap[i]))
225  _input->get_elem_integer_index(_elem_integer_names_to_swap[i]));
226  else
227  paramError("elem_integer_names_to_swap",
228  "Element ",
229  i + 1,
230  " of 'elem_integer_names_to_swap' is not a valid extra element integer of the "
231  "input mesh.");
232 
233  // prepare for transferring extra element integers from original mesh to the revolved mesh.
234  const unsigned int num_extra_elem_integers = _input->n_elem_integers();
235  std::vector<std::string> id_names;
236 
237  for (const auto i : make_range(num_extra_elem_integers))
238  {
239  id_names.push_back(_input->get_elem_integer_name(i));
240  if (!mesh->has_elem_integer(id_names[i]))
241  mesh->add_elem_integer(id_names[i]);
242  }
243 
244  // retrieve subdomain/sideset/nodeset name maps
245  const auto & input_subdomain_map = _input->get_subdomain_name_map();
246  const auto & input_sideset_map = _input->get_boundary_info().get_sideset_name_map();
247  const auto & input_nodeset_map = _input->get_boundary_info().get_nodeset_name_map();
248 
249  std::unique_ptr<MeshBase> input = std::move(_input);
250 
251  // If we're using a distributed mesh... then make sure we don't have any remote elements hanging
252  // around
253  if (!input->is_serial())
255 
256  // Subdomain IDs for on-axis elements must be new
257  if (!input->preparation().has_cached_elem_data)
258  input->cache_elem_data();
259 
260  // check that subdomain swap sources exist in the mesh
261  std::set<subdomain_id_type> blocks;
262  input->subdomain_ids(blocks, true);
263  for (const auto & swap_map : _subdomain_swap_pairs)
264  for (const auto & [bid, tbid] : swap_map)
265  {
266  libmesh_ignore(tbid);
267  if (blocks.count(bid) == 0)
268  paramError("subdomain_swaps",
269  "Source subdomain " + std::to_string(bid) + " was not found in the mesh");
270  }
271 
272  std::set<subdomain_id_type> subdomain_ids_set;
273  input->subdomain_ids(subdomain_ids_set);
274  const subdomain_id_type max_subdomain_id = *subdomain_ids_set.rbegin();
275  const subdomain_id_type tri_to_pyramid_subdomain_id_shift =
276  std::max((int)max_subdomain_id, 1) + 1;
277  const subdomain_id_type tri_to_tet_subdomain_id_shift =
278  std::max((int)max_subdomain_id, 1) * 2 + 1;
279  const subdomain_id_type quad_to_prism_subdomain_id_shift = std::max((int)max_subdomain_id, 1) + 1;
280  const subdomain_id_type quad_to_pyramid_subdomain_id_shift =
281  std::max((int)max_subdomain_id, 1) * 2 + 1;
282  const subdomain_id_type quad_to_hi_pyramid_subdomain_id_shift =
283  std::max((int)max_subdomain_id, 1) * 3 + 1;
284  const subdomain_id_type edge_to_tri_subdomain_id_shift = std::max((int)max_subdomain_id, 1) + 1;
285 
286  // Get the centroid of the input mesh
287  const auto input_centroid = MooseMeshUtils::meshCentroidCalculator(*input);
288  const Point axis_centroid_cross = (input_centroid - _axis_point).cross(_axis_direction);
289 
290  if (MooseUtils::absoluteFuzzyEqual(axis_centroid_cross.norm(), 0.0))
291  mooseError("The input mesh is either across the axis or overlapped with the axis!");
292 
293  Real inner_product_1d(0.0);
294  bool inner_product_1d_initialized(false);
295  // record ids of nodes on the axis
296  std::vector<dof_id_type> node_ids_on_axis;
297  for (const auto & node : input->node_ptr_range())
298  {
299  const Point axis_node_cross = (*node - _axis_point).cross(_axis_direction);
300  // if the cross product is zero, then the node is on the axis
301  if (!MooseUtils::absoluteFuzzyEqual(axis_node_cross.norm(), 0.0))
302  {
303  if (MooseUtils::absoluteFuzzyLessThan(axis_node_cross * axis_centroid_cross, 0.0))
304  mooseError("The input mesh is across the axis.");
305  else if (MooseUtils::absoluteFuzzyLessThan(axis_node_cross * axis_centroid_cross,
306  axis_centroid_cross.norm() *
307  axis_node_cross.norm()))
308  mooseError("The input mesh is not in the same plane with the rotation axis.");
309  }
310  else
311  node_ids_on_axis.push_back(node->id());
312 
313  // Only for 1D input mesh, we need to check if the axis is perpendicular to the input mesh
314  if (input->mesh_dimension() == 1)
315  {
316  const Real temp_inner_product = (*node - _axis_point) * _axis_direction.unit();
317  if (inner_product_1d_initialized)
318  {
319  if (!MooseUtils::absoluteFuzzyEqual(temp_inner_product, inner_product_1d))
320  mooseError("The 1D input mesh is not perpendicular to the rotation axis.");
321  }
322  else
323  {
324  inner_product_1d_initialized = true;
325  inner_product_1d = temp_inner_product;
326  }
327  }
328  }
329 
330  // If there are any on-axis nodes, we need to check if there are any QUAD8 elements with one
331  // vertex on the axis. If so, we need to replace it with a QUAD9 element.
332  if (!node_ids_on_axis.empty())
333  {
334  // Sort the vector for using set_intersection
335  std::sort(node_ids_on_axis.begin(), node_ids_on_axis.end());
336  // For QUAD8 elements with one vertex on the axis, we need to replace it with a QUAD9 element
337  std::set<subdomain_id_type> converted_quad8_subdomain_ids;
338  for (const auto & elem : input->element_ptr_range())
339  {
340  if (elem->type() == QUAD8)
341  {
342  std::vector<dof_id_type> elem_vertex_node_ids;
343  for (unsigned int i = 0; i < 4; i++)
344  {
345  elem_vertex_node_ids.push_back(elem->node_id(i));
346  }
347  std::sort(elem_vertex_node_ids.begin(), elem_vertex_node_ids.end());
348  std::vector<dof_id_type> common_node_ids;
349  std::set_intersection(node_ids_on_axis.begin(),
350  node_ids_on_axis.end(),
351  elem_vertex_node_ids.begin(),
352  elem_vertex_node_ids.end(),
353  std::back_inserter(common_node_ids));
354  // Temporarily shift the subdomain ID to mark the element
355  if (common_node_ids.size() == 1)
356  {
357  // we borrow quad_to_hi_pyramid_subdomain_id_shift here
358  elem->subdomain_id() += quad_to_hi_pyramid_subdomain_id_shift;
359  converted_quad8_subdomain_ids.emplace(elem->subdomain_id());
360  }
361  }
362  }
363  // Convert the recorded subdomains
364  input->all_second_order_range(
365  input->active_subdomain_set_elements_ptr_range(converted_quad8_subdomain_ids));
366  // Restore the subdomain ID; we do not worry about repeated subdomain IDs because those QUAD9
367  // will become PYRAMID and PRISM elements with new shifts
368  for (auto elem : input->active_subdomain_set_elements_ptr_range(converted_quad8_subdomain_ids))
369  elem->subdomain_id() -= quad_to_hi_pyramid_subdomain_id_shift;
370  }
371 
372  // We should only record this info after QUAD8->QUAD9 conversion
373  dof_id_type orig_elem = input->n_elem();
374  dof_id_type orig_nodes = input->n_nodes();
375 
376 #ifdef LIBMESH_ENABLE_UNIQUE_ID
377  // Add the number of original elements as revolving may create two elements per layer for one
378  // original element
379  unique_id_type orig_unique_ids = input->parallel_max_unique_id() + orig_elem;
380 #endif
381 
382  // get rotation vectors
383  const auto rotation_vectors = rotationVectors(_axis_point, _axis_direction, input_centroid);
384 
385  unsigned int order = 1;
386 
387  BoundaryInfo & boundary_info = mesh->get_boundary_info();
388  const BoundaryInfo & input_boundary_info = input->get_boundary_info();
389 
390  const unsigned int total_num_azimuthal_intervals =
391  std::accumulate(_nums_azimuthal_intervals.begin(), _nums_azimuthal_intervals.end(), 0);
392  // We know a priori how many elements we'll need
393  // In the worst case, all quad elements will become two elements per layer
394  mesh->reserve_elem(total_num_azimuthal_intervals * orig_elem * 2);
395  const dof_id_type elem_id_shift = total_num_azimuthal_intervals * orig_elem;
396 
397  // Look for higher order elements which introduce an extra layer
398  std::set<ElemType> higher_orders = {EDGE3, TRI6, TRI7, QUAD8, QUAD9};
399  std::vector<ElemType> types;
400  MeshTools::elem_types(*input, types);
401  for (const auto elem_type : types)
402  if (higher_orders.count(elem_type))
403  order = 2;
404  mesh->comm().max(order);
405 
406  // Collect azimuthal angles and use them to calculate the correction factor if applicable
407  std::vector<Real> azi_array;
408  for (const auto & i : index_range(_revolving_angles))
409  {
410  const Real section_start_angle =
411  azi_array.empty() ? 0.0 : (azi_array.back() + _unit_angles.back());
412  _unit_angles.push_back(_revolving_angles[i] / _nums_azimuthal_intervals[i] / order);
413  for (unsigned int j = 0; j < _nums_azimuthal_intervals[i] * order; j++)
414  azi_array.push_back(section_start_angle + _unit_angles.back() * (Real)j);
415  }
416  if (_preserve_volumes)
417  {
419  azi_array, _full_circle_revolving, order);
420 
421  // In the meanwhile, modify the input mesh for radius correction if applicable
422  for (const auto & node : input->node_ptr_range())
423  nodeModification(*node);
424  }
425 
427  (order * total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
428  orig_nodes);
429 
430  // Container to catch the boundary IDs handed back by the BoundaryInfo object
431  std::vector<boundary_id_type> ids_to_copy;
432 
433  Point old_distance;
434  Point current_distance;
435  if (!_clockwise)
436  std::transform(_unit_angles.begin(),
437  _unit_angles.end(),
438  _unit_angles.begin(),
439  [](auto & c) { return c * (-1.0) * M_PI / 180.0; });
440  else
441  std::transform(_unit_angles.begin(),
442  _unit_angles.end(),
443  _unit_angles.begin(),
444  [](auto & c) { return c * M_PI / 180.0; });
445  std::vector<dof_id_type> nodes_on_axis;
446 
447  for (const auto & node : input->node_ptr_range())
448  {
449  // Calculate the radius and corresponding center point on the rotation axis
450  // If the radius is 0, then the node is on the axis
451  const auto radius_and_center = getRotationCenterAndRadius(*node, _axis_point, _axis_direction);
452  const bool isOnAxis = MooseUtils::absoluteFuzzyEqual(radius_and_center.first, 0.0);
453  if (isOnAxis)
454  {
455  nodes_on_axis.push_back(node->id());
456  }
457 
458  unsigned int current_node_layer = 0;
459 
460  old_distance.zero();
461 
462  const unsigned int num_rotations = _revolving_angles.size();
463  for (unsigned int e = 0; e < num_rotations; e++)
464  {
465  auto num_layers = _nums_azimuthal_intervals[e];
466 
467  auto angle = _unit_angles[e];
468 
469  const auto base_angle =
470  std::accumulate(_revolving_angles.begin(), _revolving_angles.begin() + e, 0.0) / 180.0 *
471  M_PI;
472 
473  for (unsigned int k = 0;
474  k < order * num_layers + (e == 0 ? 1 : 0) -
475  (e == num_rotations - 1 ? (unsigned int)_full_circle_revolving : 0);
476  ++k)
477  {
478  bool is_node_created(false);
479  if (!isOnAxis)
480  {
481  // For the first layer we don't need to move
482  if (e == 0 && k == 0)
483  current_distance.zero();
484  else
485  {
486  auto layer_index = (k - (e == 0 ? 1 : 0)) + 1;
487 
488  // Calculate the rotation angle in XY Plane
489  const Point vector_xy =
490  Point(-2.0 * radius_and_center.first *
491  std::sin((base_angle + angle * (Real)layer_index) / 2.0) *
492  std::sin((base_angle + angle * (Real)layer_index) / 2.0),
493  2.0 * radius_and_center.first *
494  std::sin((base_angle + angle * (Real)layer_index) / 2.0) *
495  std::cos((base_angle + angle * (Real)layer_index) / 2.0),
496  0.0);
497  current_distance = Point(rotation_vectors[0] * vector_xy,
498  rotation_vectors[1] * vector_xy,
499  rotation_vectors[2] * vector_xy);
500  }
501 
502  is_node_created = true;
503  }
504  else if (e == 0 && k == 0)
505  {
506  // On-axis nodes are only added once
507  current_distance.zero();
508  is_node_created = true;
509  }
510 
511  if (is_node_created)
512  {
513  Node * new_node = mesh->add_point(*node + current_distance,
514  node->id() + (current_node_layer * orig_nodes),
515  node->processor_id());
516 #ifdef LIBMESH_ENABLE_UNIQUE_ID
517  // Let's give the base nodes of the revolved mesh the same
518  // unique_ids as the source mesh, in case anyone finds that
519  // a useful map to preserve.
520  const unique_id_type uid =
521  (current_node_layer == 0)
522  ? node->unique_id()
523  : (orig_unique_ids + (current_node_layer - 1) * (orig_nodes + orig_elem * 2) +
524  node->id());
525  new_node->set_unique_id(uid);
526 #endif
527 
528  input_boundary_info.boundary_ids(node, ids_to_copy);
529  if (_boundary_swap_pairs.empty())
530  boundary_info.add_node(new_node, ids_to_copy);
531  else
532  for (const auto & id_to_copy : ids_to_copy)
533  boundary_info.add_node(new_node,
534  _boundary_swap_pairs[e].count(id_to_copy)
535  ? _boundary_swap_pairs[e][id_to_copy]
536  : id_to_copy);
537  }
538 
539  current_node_layer++;
540  }
541  }
542  }
543 
544  for (const auto & elem : input->element_ptr_range())
545  {
546  const ElemType etype = elem->type();
547 
548  // revolving currently only works on coarse meshes
549  mooseAssert(!elem->parent(), "RevolveGenerator only works on coarse meshes.");
550 
551  unsigned int current_layer = 0;
552 
553  const unsigned int num_rotations = _revolving_angles.size();
554 
555  for (unsigned int e = 0; e < num_rotations; e++)
556  {
557  auto num_layers = _nums_azimuthal_intervals[e];
558 
559  for (unsigned int k = 0; k < num_layers; ++k)
560  {
561  std::unique_ptr<Elem> new_elem;
562  std::unique_ptr<Elem> new_elem_1;
563  bool is_flipped(false);
564  // In some cases, two elements per layer are generated by revolving one element. So we
565  // reserve an additional flag for the potential second element.
566  bool is_flipped_additional(false);
567  dof_id_type axis_node_case(-1);
568  std::vector<std::pair<dof_id_type, dof_id_type>> side_pairs;
569  switch (etype)
570  {
571  case EDGE2:
572  {
573  // Possible scenarios:
574  // 1. None of the nodes are on the axis
575  // Then a quad4 element is created
576  // 2. One of the nodes is on the axis
577  // Then a tri3 element is created
578  const auto nodes_cates = onAxisNodesIdentifier(*elem, nodes_on_axis);
579  if (nodes_cates.first.empty())
580  {
582  elem,
583  mesh,
584  new_elem,
585  current_layer,
586  orig_nodes,
587  total_num_azimuthal_intervals,
588  side_pairs,
589  is_flipped);
590  }
591  else
592  {
593  createTRIfromEDGE(nodes_cates,
594  TRI3,
595  elem,
596  mesh,
597  new_elem,
598  current_layer,
599  orig_nodes,
600  total_num_azimuthal_intervals,
601  side_pairs,
602  axis_node_case,
603  is_flipped);
604  }
605  break;
606  }
607  case EDGE3:
608  {
609  // Possible scenarios:
610  // 1. None of the nodes are on the axis
611  // Then a QUAD9 element is created
612  // 2. One of the nodes is on the axis
613  // Then a TRI7 element is created
614  const auto nodes_cates = onAxisNodesIdentifier(*elem, nodes_on_axis);
615  if (nodes_cates.first.empty())
616  {
618  elem,
619  mesh,
620  new_elem,
621  current_layer,
622  orig_nodes,
623  total_num_azimuthal_intervals,
624  side_pairs,
625  is_flipped);
626  }
627  else
628  {
629  createTRIfromEDGE(nodes_cates,
630  TRI7,
631  elem,
632  mesh,
633  new_elem,
634  current_layer,
635  orig_nodes,
636  total_num_azimuthal_intervals,
637  side_pairs,
638  axis_node_case,
639  is_flipped);
640  }
641  break;
642  }
643  case TRI3:
644  {
645  // Possible scenarios:
646  // 1. None of the nodes are on the axis
647  // Then a prism6 element is created
648  // 2. One of the nodes is on the axis
649  // Then a pyramid5 element is created
650  // 3. Two of the nodes are on the axis
651  // Then a tet4 element is created
652  const auto nodes_cates = onAxisNodesIdentifier(*elem, nodes_on_axis);
653  if (nodes_cates.first.empty())
654  {
656  elem,
657  mesh,
658  new_elem,
659  current_layer,
660  orig_nodes,
661  total_num_azimuthal_intervals,
662  side_pairs,
663  is_flipped);
664  }
665  else if (nodes_cates.first.size() == 1)
666  {
667  createPYRAMIDfromTRI(nodes_cates,
668  PYRAMID5,
669  elem,
670  mesh,
671  new_elem,
672  current_layer,
673  orig_nodes,
674  total_num_azimuthal_intervals,
675  side_pairs,
676  axis_node_case,
677  is_flipped);
678  }
679  else if (nodes_cates.first.size() == 2)
680  {
681  createTETfromTRI(nodes_cates,
682  TET4,
683  elem,
684  mesh,
685  new_elem,
686  current_layer,
687  orig_nodes,
688  total_num_azimuthal_intervals,
689  side_pairs,
690  axis_node_case,
691  is_flipped);
692  }
693  else
694  mooseError("A degenerate TRI3 elements overlapped with the rotation axis cannot be "
695  "revolved.");
696 
697  break;
698  }
699  case TRI6:
700  {
701  // Possible scenarios:
702  // 1. None of the nodes are on the axis
703  // Then a prism18 element is created
704  // 2. One of the nodes is on the axis
705  // Then a pyramid13 element is created
706  // 3. Three of the nodes are on the axis
707  // Then a tet10 element is created
708  // NOTE: We do not support two nodes on the axis for tri6 elements
709  const auto nodes_cates = onAxisNodesIdentifier(*elem, nodes_on_axis);
710  if (nodes_cates.first.empty())
711  {
713  elem,
714  mesh,
715  new_elem,
716  current_layer,
717  orig_nodes,
718  total_num_azimuthal_intervals,
719  side_pairs,
720  is_flipped);
721  }
722  else if (nodes_cates.first.size() == 1)
723  {
724  createPYRAMIDfromTRI(nodes_cates,
725  PYRAMID13,
726  elem,
727  mesh,
728  new_elem,
729  current_layer,
730  orig_nodes,
731  total_num_azimuthal_intervals,
732  side_pairs,
733  axis_node_case,
734  is_flipped);
735  }
736  else if (nodes_cates.first.size() == 3)
737  {
738  createTETfromTRI(nodes_cates,
739  TET10,
740  elem,
741  mesh,
742  new_elem,
743  current_layer,
744  orig_nodes,
745  total_num_azimuthal_intervals,
746  side_pairs,
747  axis_node_case,
748  is_flipped);
749  }
750  else
751  mooseError(
752  "You either have a degenerate TRI6 element, or the mid-point of the "
753  "on-axis edge is not colinear with the two vertices, which is not supported.");
754  break;
755  }
756  case TRI7:
757  {
758  // Possible scenarios:
759  // 1. None of the nodes are on the axis
760  // Then a prism21 element is created
761  // 2. One of the nodes is on the axis
762  // Then a pyramid18 element is created
763  // 3. Three of the nodes are on the axis
764  // Then a tet14 element is created
765  // NOTE: We do not support two nodes on the axis for tri7 elements
766  const auto nodes_cates = onAxisNodesIdentifier(*elem, nodes_on_axis);
767  if (nodes_cates.first.empty())
768  {
770  elem,
771  mesh,
772  new_elem,
773  current_layer,
774  orig_nodes,
775  total_num_azimuthal_intervals,
776  side_pairs,
777  is_flipped);
778  }
779  else if (nodes_cates.first.size() == 1)
780  {
781  createPYRAMIDfromTRI(nodes_cates,
782  PYRAMID18,
783  elem,
784  mesh,
785  new_elem,
786  current_layer,
787  orig_nodes,
788  total_num_azimuthal_intervals,
789  side_pairs,
790  axis_node_case,
791  is_flipped);
792  }
793  else if (nodes_cates.first.size() == 3)
794  {
795  createTETfromTRI(nodes_cates,
796  TET14,
797  elem,
798  mesh,
799  new_elem,
800  current_layer,
801  orig_nodes,
802  total_num_azimuthal_intervals,
803  side_pairs,
804  axis_node_case,
805  is_flipped);
806  }
807  else
808  mooseError("You either have a degenerate TRI6 element, or the mid-point of the "
809  "on-axis edge of the TRI6 element is not colinear with the two vertices, "
810  "which is not supported.");
811  break;
812  }
813  case QUAD4:
814  {
815  // Possible scenarios:
816  // 1. None of the nodes are on the axis
817  // Then a hex8 element is created
818  // 2. One of the nodes is on the axis
819  // Then a pyramid5 element and a prism6 element are created
820  // 3. Two of the nodes are on the axis
821  // Then a prism6 is created
822  const auto nodes_cates = onAxisNodesIdentifier(*elem, nodes_on_axis);
823  if (nodes_cates.first.empty())
824  {
826  elem,
827  mesh,
828  new_elem,
829  current_layer,
830  orig_nodes,
831  total_num_azimuthal_intervals,
832  side_pairs,
833  is_flipped);
834  }
835  else if (nodes_cates.first.size() == 1)
836  {
837  createPYRAMIDPRISMfromQUAD(nodes_cates,
838  PYRAMID5,
839  PRISM6,
840  elem,
841  mesh,
842  new_elem,
843  new_elem_1,
844  current_layer,
845  orig_nodes,
846  total_num_azimuthal_intervals,
847  side_pairs,
848  axis_node_case,
849  is_flipped,
850  is_flipped_additional);
851  }
852  else if (nodes_cates.first.size() == 2)
853  {
854  createPRISMfromQUAD(nodes_cates,
855  PRISM6,
856  elem,
857  mesh,
858  new_elem,
859  current_layer,
860  orig_nodes,
861  total_num_azimuthal_intervals,
862  side_pairs,
863  axis_node_case,
864  is_flipped);
865  }
866 
867  else
868  mooseError("Degenerate QUAD4 element with 3 or more aligned nodes cannot be "
869  "azimuthally revolved");
870 
871  break;
872  }
873  case QUAD8:
874  {
875  // Possible scenarios:
876  // 1. None of the nodes are on the axis
877  // Then a hex20 element is created
878  // 2. One of the nodes is on the axis
879  // In that case, it is already converted to a QUAD9 element before,
880  // SO we do not need to worry about this case
881  // 3. Three of the nodes are on the axis
882  // Then a prism15 is created
883  // NOTE: We do not support two nodes on the axis for quad8 elements
884  const auto nodes_cates = onAxisNodesIdentifier(*elem, nodes_on_axis);
885  if (nodes_cates.first.empty())
886  {
888  elem,
889  mesh,
890  new_elem,
891  current_layer,
892  orig_nodes,
893  total_num_azimuthal_intervals,
894  side_pairs,
895  is_flipped);
896  }
897  else if (nodes_cates.first.size() == 3)
898  {
899  createPRISMfromQUAD(nodes_cates,
900  PRISM15,
901  elem,
902  mesh,
903  new_elem,
904  current_layer,
905  orig_nodes,
906  total_num_azimuthal_intervals,
907  side_pairs,
908  axis_node_case,
909  is_flipped);
910  }
911  else
912  mooseError("You either have a degenerate QUAD8 element, or the mid-point of the "
913  "on-axis edge of the QUAD8 element is not colinear with the two vertices, "
914  "which is not supported.");
915 
916  break;
917  }
918  case QUAD9:
919  {
920  // Possible scenarios:
921  // 1. None of the nodes are on the axis
922  // Then a hex27 element is created
923  // 2. One of the nodes is on the axis
924  // Then a pyramid14 element and a prism18 element are created
925  // 3. Two of the nodes are on the axis
926  // Then a prism18 is created
927  // (we do not create prism20/21 here just to make prism18 the only possible prism
928  // elements for simplicity)
929  const auto nodes_cates = onAxisNodesIdentifier(*elem, nodes_on_axis);
930  if (nodes_cates.first.empty())
931  {
933  elem,
934  mesh,
935  new_elem,
936  current_layer,
937  orig_nodes,
938  total_num_azimuthal_intervals,
939  side_pairs,
940  is_flipped);
941  }
942  else if (nodes_cates.first.size() == 1)
943  {
944  createPYRAMIDPRISMfromQUAD(nodes_cates,
945  PYRAMID14,
946  PRISM18,
947  elem,
948  mesh,
949  new_elem,
950  new_elem_1,
951  current_layer,
952  orig_nodes,
953  total_num_azimuthal_intervals,
954  side_pairs,
955  axis_node_case,
956  is_flipped,
957  is_flipped_additional);
958  }
959  else if (nodes_cates.first.size() == 3)
960  {
961  createPRISMfromQUAD(nodes_cates,
962  PRISM18,
963  elem,
964  mesh,
965  new_elem,
966  current_layer,
967  orig_nodes,
968  total_num_azimuthal_intervals,
969  side_pairs,
970  axis_node_case,
971  is_flipped);
972  }
973  else
974  mooseError("You either have a degenerate QUAD9 element, or the mid-point of the "
975  "on-axis edge of the QUAD9 element is not colinear with the two vertices, "
976  "which is not supported.");
977  break;
978  }
979  default:
980  mooseError("The input mesh contains unsupported element type(s).");
981  }
982  new_elem->set_id(elem->id() + (current_layer * orig_elem));
983  new_elem->processor_id() = elem->processor_id();
984  if (new_elem_1)
985  {
986  new_elem_1->set_id(elem->id() + (current_layer * orig_elem) + elem_id_shift);
987  new_elem_1->processor_id() = elem->processor_id();
988  }
989 
990 #ifdef LIBMESH_ENABLE_UNIQUE_ID
991  // Let's give the base elements of the revolved mesh the same
992  // unique_ids as the source mesh, in case anyone finds that
993  // a useful map to preserve.
994  const unique_id_type uid =
995  (current_layer == 0)
996  ? elem->unique_id()
997  : (orig_unique_ids + (current_layer - 1) * (orig_nodes + orig_elem * 2) +
998  orig_nodes + elem->id());
999 
1000  new_elem->set_unique_id(uid);
1001 
1002  // Special case for extra elements
1003  if (new_elem_1)
1004  {
1005  const unique_id_type uid_1 =
1006  (current_layer == 0)
1007  ? (elem->id() + orig_unique_ids - orig_elem)
1008  : (orig_unique_ids + (current_layer - 1) * (orig_nodes + orig_elem * 2) +
1009  orig_nodes + orig_elem + elem->id());
1010 
1011  new_elem_1->set_unique_id(uid_1);
1012  }
1013 #endif
1014 
1015  // maintain the subdomain_id
1016  switch (etype)
1017  {
1018  case EDGE2:
1019  switch (new_elem->type())
1020  {
1021  case QUAD4:
1022  new_elem->subdomain_id() = elem->subdomain_id();
1023  break;
1024  case TRI3:
1025  new_elem->subdomain_id() = edge_to_tri_subdomain_id_shift + elem->subdomain_id();
1026  break;
1027  default:
1028  mooseAssert(false,
1029  "impossible element type generated by revolving an EDGE2 element");
1030  }
1031  break;
1032  case EDGE3:
1033  switch (new_elem->type())
1034  {
1035  case QUAD9:
1036  new_elem->subdomain_id() = elem->subdomain_id();
1037  break;
1038  case TRI7:
1039  new_elem->subdomain_id() = edge_to_tri_subdomain_id_shift + elem->subdomain_id();
1040  break;
1041  default:
1042  mooseAssert(false,
1043  "impossible element type generated by revolving an EDGE3 element");
1044  }
1045  break;
1046  case TRI3:
1047  switch (new_elem->type())
1048  {
1049  case PRISM6:
1050  new_elem->subdomain_id() = elem->subdomain_id();
1051  break;
1052  case PYRAMID5:
1053  new_elem->subdomain_id() = tri_to_pyramid_subdomain_id_shift + elem->subdomain_id();
1054  break;
1055  case TET4:
1056  new_elem->subdomain_id() = tri_to_tet_subdomain_id_shift + elem->subdomain_id();
1057  break;
1058  default:
1059  mooseAssert(false, "impossible element type generated by revolving a TRI3 element");
1060  }
1061  break;
1062  case TRI6:
1063  switch (new_elem->type())
1064  {
1065  case PRISM18:
1066  new_elem->subdomain_id() = elem->subdomain_id();
1067  break;
1068  case PYRAMID13:
1069  new_elem->subdomain_id() = tri_to_pyramid_subdomain_id_shift + elem->subdomain_id();
1070  break;
1071  case TET10:
1072  new_elem->subdomain_id() = tri_to_tet_subdomain_id_shift + elem->subdomain_id();
1073  break;
1074  default:
1075  mooseAssert(false, "impossible element type generated by revolving a TRI6 element");
1076  }
1077  break;
1078  case TRI7:
1079  switch (new_elem->type())
1080  {
1081  case PRISM21:
1082  new_elem->subdomain_id() = elem->subdomain_id();
1083  break;
1084  case PYRAMID18:
1085  new_elem->subdomain_id() = tri_to_pyramid_subdomain_id_shift + elem->subdomain_id();
1086  break;
1087  case TET14:
1088  new_elem->subdomain_id() = tri_to_tet_subdomain_id_shift + elem->subdomain_id();
1089  break;
1090  default:
1091  mooseAssert(false, "impossible element type generated by revolving a TRI7 element");
1092  }
1093  break;
1094  case QUAD4:
1095  switch (new_elem->type())
1096  {
1097  case HEX8:
1098  new_elem->subdomain_id() = elem->subdomain_id();
1099  break;
1100  case PRISM6:
1101  new_elem->subdomain_id() = quad_to_prism_subdomain_id_shift + elem->subdomain_id();
1102  break;
1103  case PYRAMID5:
1104  new_elem->subdomain_id() =
1105  quad_to_pyramid_subdomain_id_shift + elem->subdomain_id();
1106  new_elem_1->subdomain_id() =
1107  quad_to_prism_subdomain_id_shift + elem->subdomain_id();
1108  break;
1109  default:
1110  mooseAssert(false,
1111  "impossible element type generated by revolving a QUAD4 element");
1112  }
1113  break;
1114  case QUAD8:
1115  switch (new_elem->type())
1116  {
1117  case HEX20:
1118  new_elem->subdomain_id() = elem->subdomain_id();
1119  break;
1120  case PRISM15:
1121  new_elem->subdomain_id() = quad_to_prism_subdomain_id_shift + elem->subdomain_id();
1122  break;
1123  default:
1124  mooseAssert(false,
1125  "impossible element type generated by revolving a QUAD8 element");
1126  }
1127  break;
1128  case QUAD9:
1129  switch (new_elem->type())
1130  {
1131  case HEX27:
1132  new_elem->subdomain_id() = elem->subdomain_id();
1133  break;
1134  case PRISM18:
1135  new_elem->subdomain_id() =
1136  quad_to_hi_pyramid_subdomain_id_shift + elem->subdomain_id();
1137  break;
1138  case PYRAMID14:
1139  new_elem->subdomain_id() =
1140  quad_to_pyramid_subdomain_id_shift + elem->subdomain_id();
1141  new_elem_1->subdomain_id() =
1142  quad_to_hi_pyramid_subdomain_id_shift + elem->subdomain_id();
1143  break;
1144  default:
1145  mooseAssert(false,
1146  "impossible element type generated by revolving a QUAD9 element");
1147  }
1148  break;
1149  default:
1150  mooseAssert(false,
1151  "The input mesh contains unsupported element type(s), which should have "
1152  "been checked in prior steps in this code.");
1153  }
1154 
1155  if (_subdomain_swap_pairs.size())
1156  {
1157  auto & revolving_swap_pairs = _subdomain_swap_pairs[e];
1158 
1159  auto new_id_it = revolving_swap_pairs.find(elem->subdomain_id());
1160 
1161  if (new_id_it != revolving_swap_pairs.end())
1162  {
1163  new_elem->subdomain_id() =
1164  new_elem->subdomain_id() - elem->subdomain_id() + new_id_it->second;
1165  if (new_elem_1)
1166  new_elem_1->subdomain_id() =
1167  new_elem_1->subdomain_id() - elem->subdomain_id() + new_id_it->second;
1168  }
1169  }
1170 
1171  Elem * added_elem = mesh->add_elem(std::move(new_elem));
1172  Elem * added_elem_1 = NULL;
1173 
1174  if (new_elem_1)
1175  added_elem_1 = mesh->add_elem(std::move(new_elem_1));
1176 
1177  // maintain extra integers
1178  for (unsigned int i = 0; i < num_extra_elem_integers; i++)
1179  {
1180  added_elem->set_extra_integer(i, elem->get_extra_integer(i));
1181  if (added_elem_1)
1182  added_elem_1->set_extra_integer(i, elem->get_extra_integer(i));
1183  }
1184 
1185  if (_elem_integers_swap_pairs.size())
1186  {
1187  for (unsigned int i = 0; i < _elem_integer_indices_to_swap.size(); i++)
1188  {
1189  auto & elevation_extra_swap_pairs =
1191 
1192  auto new_extra_id_it = elevation_extra_swap_pairs.find(
1193  elem->get_extra_integer(_elem_integer_indices_to_swap[i]));
1194 
1195  if (new_extra_id_it != elevation_extra_swap_pairs.end())
1196  {
1198  new_extra_id_it->second);
1199  if (added_elem_1)
1201  new_extra_id_it->second);
1202  }
1203  }
1204  }
1205 
1206  // Copy any old boundary ids on all sides
1207  for (auto s : elem->side_index_range())
1208  {
1209  input_boundary_info.boundary_ids(elem, s, ids_to_copy);
1210  std::vector<boundary_id_type> ids_to_copy_swapped;
1211  if (_boundary_swap_pairs.empty())
1212  ids_to_copy_swapped = ids_to_copy;
1213  else
1214  for (const auto & id_to_copy : ids_to_copy)
1215  ids_to_copy_swapped.push_back(_boundary_swap_pairs[e].count(id_to_copy)
1216  ? _boundary_swap_pairs[e][id_to_copy]
1217  : id_to_copy);
1218 
1219  switch (etype)
1220  {
1221  case EDGE2:
1222  switch (added_elem->type())
1223  {
1224  case QUAD4:
1225  boundary_info.add_side(
1226  added_elem, cast_int<unsigned short>(s == 0 ? 3 : 1), ids_to_copy_swapped);
1227  break;
1228  case TRI3:
1229  if (s != axis_node_case)
1230  boundary_info.add_side(
1231  added_elem, cast_int<unsigned short>(s), ids_to_copy_swapped);
1232  break;
1233  default:
1234  mooseAssert(false,
1235  "impossible element type generated by revolving an EDGE2 element");
1236  }
1237  break;
1238  case EDGE3:
1239  switch (added_elem->type())
1240  {
1241  case QUAD9:
1242  boundary_info.add_side(
1243  added_elem, cast_int<unsigned short>(s == 0 ? 3 : 1), ids_to_copy_swapped);
1244  break;
1245  case TRI7:
1246  if (s != axis_node_case)
1247  boundary_info.add_side(
1248  added_elem, cast_int<unsigned short>(s), ids_to_copy_swapped);
1249  break;
1250  default:
1251  mooseAssert(false,
1252  "impossible element type generated by revolving an EDGE3 element");
1253  }
1254  break;
1255  case TRI3:
1256  switch (added_elem->type())
1257  {
1258  case PRISM6:
1259  boundary_info.add_side(
1260  added_elem, cast_int<unsigned short>(s + 1), ids_to_copy_swapped);
1261  break;
1262  case PYRAMID5:
1263  if ((s + 3 - axis_node_case) % 3 == 0)
1264  boundary_info.add_side(
1265  added_elem, cast_int<unsigned short>(3), ids_to_copy_swapped);
1266  else if ((s + 3 - axis_node_case) % 3 == 1)
1267  boundary_info.add_side(
1268  added_elem, cast_int<unsigned short>(4), ids_to_copy_swapped);
1269  else
1270  boundary_info.add_side(
1271  added_elem, cast_int<unsigned short>(1), ids_to_copy_swapped);
1272  break;
1273  case TET4:
1274  if ((s + 3 - axis_node_case) % 3 == 0)
1275  boundary_info.add_side(
1276  added_elem, cast_int<unsigned short>(2), ids_to_copy_swapped);
1277  else if ((s + 3 - axis_node_case) % 3 == 2)
1278  boundary_info.add_side(
1279  added_elem, cast_int<unsigned short>(3), ids_to_copy_swapped);
1280  break;
1281  default:
1282  mooseAssert(false,
1283  "impossible element type generated by revolving a TRI3 element");
1284  }
1285  break;
1286  case TRI6:
1287  switch (added_elem->type())
1288  {
1289  case PRISM18:
1290  boundary_info.add_side(
1291  added_elem, cast_int<unsigned short>(s + 1), ids_to_copy_swapped);
1292  break;
1293  case PYRAMID13:
1294  if ((s + 3 - axis_node_case) % 3 == 0)
1295  boundary_info.add_side(
1296  added_elem, cast_int<unsigned short>(3), ids_to_copy_swapped);
1297  else if ((s + 3 - axis_node_case) % 3 == 1)
1298  boundary_info.add_side(
1299  added_elem, cast_int<unsigned short>(4), ids_to_copy_swapped);
1300  else
1301  boundary_info.add_side(
1302  added_elem, cast_int<unsigned short>(1), ids_to_copy_swapped);
1303  break;
1304  case TET10:
1305  if ((s + 3 - axis_node_case) % 3 == 0)
1306  boundary_info.add_side(
1307  added_elem, cast_int<unsigned short>(2), ids_to_copy_swapped);
1308  else if ((s + 3 - axis_node_case) % 3 == 2)
1309  boundary_info.add_side(
1310  added_elem, cast_int<unsigned short>(3), ids_to_copy_swapped);
1311  break;
1312  default:
1313  mooseAssert(false,
1314  "impossible element type generated by revolving a TRI6 element");
1315  }
1316  break;
1317  case TRI7:
1318  switch (added_elem->type())
1319  {
1320  case PRISM21:
1321  boundary_info.add_side(
1322  added_elem, cast_int<unsigned short>(s + 1), ids_to_copy_swapped);
1323  break;
1324  case PYRAMID18:
1325  if ((s + 3 - axis_node_case) % 3 == 0)
1326  boundary_info.add_side(
1327  added_elem, cast_int<unsigned short>(3), ids_to_copy_swapped);
1328  else if ((s + 3 - axis_node_case) % 3 == 1)
1329  boundary_info.add_side(
1330  added_elem, cast_int<unsigned short>(4), ids_to_copy_swapped);
1331  else
1332  boundary_info.add_side(
1333  added_elem, cast_int<unsigned short>(1), ids_to_copy_swapped);
1334  break;
1335  case TET14:
1336  if ((s + 3 - axis_node_case) % 3 == 0)
1337  boundary_info.add_side(
1338  added_elem, cast_int<unsigned short>(2), ids_to_copy_swapped);
1339  else if ((s + 3 - axis_node_case) % 3 == 2)
1340  boundary_info.add_side(
1341  added_elem, cast_int<unsigned short>(3), ids_to_copy_swapped);
1342  break;
1343  default:
1344  mooseAssert(false,
1345  "impossible element type generated by revolving a TRI7 element");
1346  }
1347  break;
1348  case QUAD4:
1349  switch (added_elem->type())
1350  {
1351  case HEX8:
1352  boundary_info.add_side(
1353  added_elem, cast_int<unsigned short>(s + 1), ids_to_copy_swapped);
1354  break;
1355  case PRISM6:
1356  if ((s + 4 - axis_node_case) % 4 == 1)
1357  boundary_info.add_side(
1358  added_elem, cast_int<unsigned short>(4), ids_to_copy_swapped);
1359  else if ((s + 4 - axis_node_case) % 4 == 2)
1360  boundary_info.add_side(
1361  added_elem, cast_int<unsigned short>(2), ids_to_copy_swapped);
1362  else if ((s + 4 - axis_node_case) % 4 == 3)
1363  boundary_info.add_side(
1364  added_elem, cast_int<unsigned short>(0), ids_to_copy_swapped);
1365  break;
1366  case PYRAMID5:
1367  if ((s + 4 - axis_node_case) % 4 == 3)
1368  boundary_info.add_side(
1369  added_elem, cast_int<unsigned short>(1), ids_to_copy_swapped);
1370  else if ((s + 4 - axis_node_case) % 4 == 0)
1371  boundary_info.add_side(
1372  added_elem, cast_int<unsigned short>(3), ids_to_copy_swapped);
1373  else if ((s + 4 - axis_node_case) % 4 == 1)
1374  boundary_info.add_side(
1375  added_elem_1, cast_int<unsigned short>(3), ids_to_copy_swapped);
1376  else
1377  boundary_info.add_side(
1378  added_elem_1, cast_int<unsigned short>(2), ids_to_copy_swapped);
1379  break;
1380  default:
1381  mooseAssert(false,
1382  "impossible element type generated by revolving a QUAD4 element");
1383  }
1384  break;
1385  case QUAD8:
1386  switch (added_elem->type())
1387  {
1388  case HEX20:
1389  boundary_info.add_side(
1390  added_elem, cast_int<unsigned short>(s + 1), ids_to_copy_swapped);
1391  break;
1392  case PRISM15:
1393  if ((s + 4 - axis_node_case) % 4 == 1)
1394  boundary_info.add_side(
1395  added_elem, cast_int<unsigned short>(4), ids_to_copy_swapped);
1396  else if ((s + 4 - axis_node_case) % 4 == 2)
1397  boundary_info.add_side(
1398  added_elem, cast_int<unsigned short>(2), ids_to_copy_swapped);
1399  else if ((s + 4 - axis_node_case) % 4 == 3)
1400  boundary_info.add_side(
1401  added_elem, cast_int<unsigned short>(0), ids_to_copy_swapped);
1402  break;
1403  default:
1404  mooseAssert(false,
1405  "impossible element type generated by revolving a QUAD8 element");
1406  }
1407  break;
1408  case QUAD9:
1409  switch (added_elem->type())
1410  {
1411  case HEX27:
1412  boundary_info.add_side(
1413  added_elem, cast_int<unsigned short>(s + 1), ids_to_copy_swapped);
1414  break;
1415  case PRISM18:
1416  if ((s + 4 - axis_node_case) % 4 == 1)
1417  boundary_info.add_side(
1418  added_elem, cast_int<unsigned short>(4), ids_to_copy_swapped);
1419  else if ((s + 4 - axis_node_case) % 4 == 2)
1420  boundary_info.add_side(
1421  added_elem, cast_int<unsigned short>(2), ids_to_copy_swapped);
1422  else if ((s + 4 - axis_node_case) % 4 == 3)
1423  boundary_info.add_side(
1424  added_elem, cast_int<unsigned short>(0), ids_to_copy_swapped);
1425  break;
1426  case PYRAMID14:
1427  if ((s + 4 - axis_node_case) % 4 == 3)
1428  boundary_info.add_side(
1429  added_elem, cast_int<unsigned short>(1), ids_to_copy_swapped);
1430  else if ((s + 4 - axis_node_case) % 4 == 0)
1431  boundary_info.add_side(
1432  added_elem, cast_int<unsigned short>(3), ids_to_copy_swapped);
1433  else if ((s + 4 - axis_node_case) % 4 == 1)
1434  boundary_info.add_side(
1435  added_elem_1, cast_int<unsigned short>(3), ids_to_copy_swapped);
1436  else
1437  boundary_info.add_side(
1438  added_elem_1, cast_int<unsigned short>(2), ids_to_copy_swapped);
1439  break;
1440  default:
1441  mooseAssert(false,
1442  "impossible element type generated by revolving a QUAD9 element");
1443  }
1444  break;
1445  default:
1446  mooseAssert(false,
1447  "The input mesh contains unsupported element type(s), which should have "
1448  "been checked in prior steps in this code.");
1449  }
1450  }
1451 
1452  if (current_layer == 0 && _has_start_boundary)
1453  {
1454  boundary_info.add_side(
1455  added_elem, is_flipped ? side_pairs[0].second : side_pairs[0].first, _start_boundary);
1456  if (side_pairs.size() > 1)
1457  boundary_info.add_side(added_elem_1,
1458  is_flipped_additional ? side_pairs[1].second
1459  : side_pairs[1].first,
1460  _start_boundary);
1461  }
1462 
1463  if (current_layer == num_layers - 1 && _has_end_boundary)
1464  {
1465  boundary_info.add_side(
1466  added_elem, is_flipped ? side_pairs[0].first : side_pairs[0].second, _end_boundary);
1467  if (side_pairs.size() > 1)
1468  boundary_info.add_side(added_elem_1,
1469  is_flipped_additional ? side_pairs[1].first
1470  : side_pairs[1].second,
1471  _end_boundary);
1472  }
1473  current_layer++;
1474  }
1475  }
1476  }
1477 
1478 #ifdef LIBMESH_ENABLE_UNIQUE_ID
1479  // Update the value of next_unique_id based on newly created nodes and elements
1480  // Note: the calculation here is quite conservative to ensure uniqueness
1481  unsigned int total_new_node_layers = total_num_azimuthal_intervals * order;
1482  unsigned int new_unique_ids = orig_unique_ids + (total_new_node_layers - 1) * orig_elem * 2 +
1483  total_new_node_layers * orig_nodes;
1484  mesh->set_next_unique_id(new_unique_ids);
1485 #endif
1486 
1487  // Copy all the subdomain/sideset/nodeset name maps to the revolved mesh
1488  if (!input_subdomain_map.empty())
1489  mesh->set_subdomain_name_map().insert(input_subdomain_map.begin(), input_subdomain_map.end());
1490  if (!input_sideset_map.empty())
1491  mesh->get_boundary_info().set_sideset_name_map().insert(input_sideset_map.begin(),
1492  input_sideset_map.end());
1493  if (!input_nodeset_map.empty())
1494  mesh->get_boundary_info().set_nodeset_name_map().insert(input_nodeset_map.begin(),
1495  input_nodeset_map.end());
1496 
1500 
1501  return mesh;
1502 }
1503 
1504 std::pair<Real, Point>
1506  const Point & p_axis,
1507  const Point & dir_axis) const
1508 {
1509  // First use point product to get the distance between the axis point and the projection of the
1510  // external point on the axis
1511  const Real dist = (p_ext - p_axis) * dir_axis.unit();
1512  const Point center_pt = p_axis + dist * dir_axis.unit();
1513  // Then get the radius
1514  const Real radius = (p_ext - center_pt).norm();
1515  return std::make_pair(radius, center_pt);
1516 }
1517 
1518 std::vector<Point>
1520  const Point & dir_axis,
1521  const Point & p_input) const
1522 {
1523  // To make the rotation mathematically simple, we perform rotation in a coordination system
1524  // (x',y',z') defined by rotation axis and the mesh to be rotated.
1525  // z' is the rotation axis, which is trivial dir_axis.unit()
1526  const Point z_prime = dir_axis.unit();
1527  // the x' and z' should form the plane that accommodates input mesh
1528  const Point x_prime = ((p_input - p_axis) - ((p_input - p_axis) * z_prime) * z_prime).unit();
1529  const Point y_prime = z_prime.cross(x_prime);
1530  // Then we transform things back to the original coordination system (x,y,z), which is trivial
1531  // (1,0,0), (0,1,0), (0,0,1)
1532  return {{x_prime(0), y_prime(0), z_prime(0)},
1533  {x_prime(1), y_prime(1), z_prime(1)},
1534  {x_prime(2), y_prime(2), z_prime(2)}};
1535 }
1536 
1537 std::pair<std::vector<dof_id_type>, std::vector<dof_id_type>>
1539  const std::vector<dof_id_type> & nodes_on_axis) const
1540 {
1541  std::vector<dof_id_type> nodes_on_axis_in_elem;
1542  std::vector<dof_id_type> nodes_not_on_axis_in_elem;
1543  for (unsigned int i = 0; i < elem.n_nodes(); i++)
1544  {
1545  const auto node_id = elem.node_id(i);
1546  if (std::find(nodes_on_axis.begin(), nodes_on_axis.end(), node_id) != nodes_on_axis.end())
1547  {
1548  nodes_on_axis_in_elem.push_back(i);
1549  }
1550  else
1551  {
1552  nodes_not_on_axis_in_elem.push_back(i);
1553  }
1554  }
1555  return std::make_pair(nodes_on_axis_in_elem, nodes_not_on_axis_in_elem);
1556 }
1557 
1558 void
1560 {
1561  const Point axis_component =
1563  const Point rad_component = ((node - _axis_point) - axis_component) * _radius_correction_factor;
1564  node = _axis_point + axis_component + rad_component;
1565 }
1566 
1567 void
1569  const Elem * elem,
1570  const std::unique_ptr<MeshBase> & mesh,
1571  std::unique_ptr<Elem> & new_elem,
1572  const int current_layer,
1573  const unsigned int orig_nodes,
1574  const unsigned int total_num_azimuthal_intervals,
1575  std::vector<std::pair<dof_id_type, dof_id_type>> & side_pairs,
1576  bool & is_flipped) const
1577 {
1578  if (quad_elem_type != QUAD4 && quad_elem_type != QUAD9)
1579  mooseError("Unsupported element type", quad_elem_type);
1580 
1581  side_pairs.push_back(std::make_pair(0, 2));
1582  const unsigned int order = quad_elem_type == QUAD4 ? 1 : 2;
1583 
1584  new_elem = std::make_unique<Quad4>();
1585  if (quad_elem_type == QUAD9)
1586  {
1587  new_elem = std::make_unique<Quad9>();
1588  new_elem->set_node(4,
1589  mesh->node_ptr(elem->node_ptr(2)->id() + (current_layer * 2 * orig_nodes)));
1590  new_elem->set_node(
1591  5, mesh->node_ptr(elem->node_ptr(1)->id() + ((current_layer * 2 + 1) * orig_nodes)));
1592  new_elem->set_node(
1593  6,
1594  mesh->node_ptr(elem->node_ptr(2)->id() +
1595  ((current_layer + 1) %
1596  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
1597  2 * orig_nodes)));
1598  new_elem->set_node(
1599  7, mesh->node_ptr(elem->node_ptr(0)->id() + ((current_layer * 2 + 1) * orig_nodes)));
1600  new_elem->set_node(
1601  8, mesh->node_ptr(elem->node_ptr(2)->id() + ((current_layer * 2 + 1) * orig_nodes)));
1602  }
1603 
1604  new_elem->set_node(
1605  0, mesh->node_ptr(elem->node_ptr(0)->id() + (current_layer * order * orig_nodes)));
1606  new_elem->set_node(
1607  1, mesh->node_ptr(elem->node_ptr(1)->id() + (current_layer * order * orig_nodes)));
1608  new_elem->set_node(
1609  3,
1610  mesh->node_ptr(elem->node_ptr(0)->id() +
1611  ((current_layer + 1) %
1612  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
1613  order * orig_nodes)));
1614  new_elem->set_node(
1615  2,
1616  mesh->node_ptr(elem->node_ptr(1)->id() +
1617  ((current_layer + 1) %
1618  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
1619  order * orig_nodes)));
1620 
1621  if (new_elem->volume() < 0.0)
1622  {
1623  MooseMeshUtils::swapNodesInElem(*new_elem, 0, 3);
1624  MooseMeshUtils::swapNodesInElem(*new_elem, 1, 2);
1625  if (quad_elem_type == QUAD9)
1626  MooseMeshUtils::swapNodesInElem(*new_elem, 4, 6);
1627  is_flipped = true;
1628  }
1629 }
1630 
1631 void
1633  const std::pair<std::vector<dof_id_type>, std::vector<dof_id_type>> & nodes_cates,
1634  const ElemType tri_elem_type,
1635  const Elem * elem,
1636  const std::unique_ptr<MeshBase> & mesh,
1637  std::unique_ptr<Elem> & new_elem,
1638  const int current_layer,
1639  const unsigned int orig_nodes,
1640  const unsigned int total_num_azimuthal_intervals,
1641  std::vector<std::pair<dof_id_type, dof_id_type>> & side_pairs,
1642  dof_id_type & axis_node_case,
1643  bool & is_flipped) const
1644 {
1645  if (tri_elem_type != TRI3 && tri_elem_type != TRI7)
1646  mooseError("Unsupported element type", tri_elem_type);
1647 
1648  side_pairs.push_back(std::make_pair(0, 2));
1649  const unsigned int order = tri_elem_type == TRI3 ? 1 : 2;
1650  axis_node_case = nodes_cates.first.front();
1651 
1652  new_elem = std::make_unique<Tri3>();
1653  if (tri_elem_type == TRI7)
1654  {
1655  new_elem = std::make_unique<Tri7>();
1656  new_elem->set_node(3,
1657  mesh->node_ptr(elem->node_ptr(2)->id() + (current_layer * 2 * orig_nodes)));
1658  new_elem->set_node(4,
1659  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 2)->id() +
1660  ((current_layer * 2 + 1) * orig_nodes)));
1661  new_elem->set_node(
1662  5,
1663  mesh->node_ptr(elem->node_ptr(2)->id() +
1664  ((current_layer + 1) %
1665  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
1666  2 * orig_nodes)));
1667  new_elem->set_node(
1668  6, mesh->node_ptr(elem->node_ptr(2)->id() + ((current_layer * 2 + 1) * orig_nodes)));
1669  }
1670 
1671  new_elem->set_node(0, mesh->node_ptr(elem->node_ptr(axis_node_case)->id()));
1672  new_elem->set_node(1,
1673  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 2)->id() +
1674  (current_layer * order * orig_nodes)));
1675  new_elem->set_node(
1676  2,
1677  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 2)->id() +
1678  ((current_layer + 1) %
1679  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
1680  order * orig_nodes)));
1681 
1682  if (new_elem->volume() < 0.0)
1683  {
1684  MooseMeshUtils::swapNodesInElem(*new_elem, 1, 2);
1685  if (tri_elem_type == TRI7)
1686  MooseMeshUtils::swapNodesInElem(*new_elem, 3, 5);
1687  is_flipped = true;
1688  }
1689 }
1690 
1691 void
1693  const Elem * elem,
1694  const std::unique_ptr<MeshBase> & mesh,
1695  std::unique_ptr<Elem> & new_elem,
1696  const int current_layer,
1697  const unsigned int orig_nodes,
1698  const unsigned int total_num_azimuthal_intervals,
1699  std::vector<std::pair<dof_id_type, dof_id_type>> & side_pairs,
1700  bool & is_flipped) const
1701 {
1702  if (prism_elem_type != PRISM6 && prism_elem_type != PRISM18 && prism_elem_type != PRISM21)
1703  mooseError("unsupported situation");
1704 
1705  side_pairs.push_back(std::make_pair(0, 4));
1706  const unsigned int order = prism_elem_type == PRISM6 ? 1 : 2;
1707 
1708  new_elem = std::make_unique<Prism6>();
1709 
1710  if (order == 2)
1711  {
1712  new_elem = std::make_unique<Prism18>();
1713  if (prism_elem_type == PRISM21)
1714  {
1715  new_elem = std::make_unique<Prism21>();
1716  new_elem->set_node(
1717  18, mesh->node_ptr(elem->node_ptr(6)->id() + (current_layer * 2 * orig_nodes)));
1718  new_elem->set_node(
1719  19,
1720  mesh->node_ptr(elem->node_ptr(6)->id() + ((current_layer + 1) %
1721  (total_num_azimuthal_intervals + 1 -
1722  (unsigned int)_full_circle_revolving) *
1723  2 * orig_nodes)));
1724  new_elem->set_node(
1725  20, mesh->node_ptr(elem->node_ptr(6)->id() + ((current_layer * 2 + 1) * orig_nodes)));
1726  }
1727  new_elem->set_node(6,
1728  mesh->node_ptr(elem->node_ptr(3)->id() + (current_layer * 2 * orig_nodes)));
1729  new_elem->set_node(7,
1730  mesh->node_ptr(elem->node_ptr(4)->id() + (current_layer * 2 * orig_nodes)));
1731  new_elem->set_node(8,
1732  mesh->node_ptr(elem->node_ptr(5)->id() + (current_layer * 2 * orig_nodes)));
1733  new_elem->set_node(
1734  9, mesh->node_ptr(elem->node_ptr(0)->id() + ((current_layer * 2 + 1) * orig_nodes)));
1735  new_elem->set_node(
1736  10, mesh->node_ptr(elem->node_ptr(1)->id() + ((current_layer * 2 + 1) * orig_nodes)));
1737  new_elem->set_node(
1738  11, mesh->node_ptr(elem->node_ptr(2)->id() + ((current_layer * 2 + 1) * orig_nodes)));
1739  new_elem->set_node(
1740  12,
1741  mesh->node_ptr(elem->node_ptr(3)->id() +
1742  ((current_layer + 1) %
1743  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
1744  2 * orig_nodes)));
1745  new_elem->set_node(
1746  13,
1747  mesh->node_ptr(elem->node_ptr(4)->id() +
1748  ((current_layer + 1) %
1749  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
1750  2 * orig_nodes)));
1751  new_elem->set_node(
1752  14,
1753  mesh->node_ptr(elem->node_ptr(5)->id() +
1754  ((current_layer + 1) %
1755  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
1756  2 * orig_nodes)));
1757  new_elem->set_node(
1758  15, mesh->node_ptr(elem->node_ptr(3)->id() + ((current_layer * 2 + 1) * orig_nodes)));
1759  new_elem->set_node(
1760  16, mesh->node_ptr(elem->node_ptr(4)->id() + ((current_layer * 2 + 1) * orig_nodes)));
1761  new_elem->set_node(
1762  17, mesh->node_ptr(elem->node_ptr(5)->id() + ((current_layer * 2 + 1) * orig_nodes)));
1763  }
1764  new_elem->set_node(
1765  0, mesh->node_ptr(elem->node_ptr(0)->id() + (current_layer * order * orig_nodes)));
1766  new_elem->set_node(
1767  1, mesh->node_ptr(elem->node_ptr(1)->id() + (current_layer * order * orig_nodes)));
1768  new_elem->set_node(
1769  2, mesh->node_ptr(elem->node_ptr(2)->id() + (current_layer * order * orig_nodes)));
1770  new_elem->set_node(
1771  3,
1772  mesh->node_ptr(elem->node_ptr(0)->id() +
1773  ((current_layer + 1) %
1774  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
1775  order * orig_nodes)));
1776  new_elem->set_node(
1777  4,
1778  mesh->node_ptr(elem->node_ptr(1)->id() +
1779  ((current_layer + 1) %
1780  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
1781  order * orig_nodes)));
1782  new_elem->set_node(
1783  5,
1784  mesh->node_ptr(elem->node_ptr(2)->id() +
1785  ((current_layer + 1) %
1786  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
1787  order * orig_nodes)));
1788 
1789  if (new_elem->volume() < 0.0)
1790  {
1791  MooseMeshUtils::swapNodesInElem(*new_elem, 0, 3);
1792  MooseMeshUtils::swapNodesInElem(*new_elem, 1, 4);
1793  MooseMeshUtils::swapNodesInElem(*new_elem, 2, 5);
1794  if (prism_elem_type != PRISM6)
1795  {
1796  MooseMeshUtils::swapNodesInElem(*new_elem, 6, 12);
1797  MooseMeshUtils::swapNodesInElem(*new_elem, 7, 13);
1798  MooseMeshUtils::swapNodesInElem(*new_elem, 8, 14);
1799  if (prism_elem_type == PRISM21)
1800  {
1801  MooseMeshUtils::swapNodesInElem(*new_elem, 18, 19);
1802  }
1803  }
1804  is_flipped = true;
1805  }
1806 }
1807 
1808 void
1810  const std::pair<std::vector<dof_id_type>, std::vector<dof_id_type>> & nodes_cates,
1811  const ElemType pyramid_elem_type,
1812  const Elem * elem,
1813  const std::unique_ptr<MeshBase> & mesh,
1814  std::unique_ptr<Elem> & new_elem,
1815  const int current_layer,
1816  const unsigned int orig_nodes,
1817  const unsigned int total_num_azimuthal_intervals,
1818  std::vector<std::pair<dof_id_type, dof_id_type>> & side_pairs,
1819  dof_id_type & axis_node_case,
1820  bool & is_flipped) const
1821 {
1822  if (pyramid_elem_type != PYRAMID5 && pyramid_elem_type != PYRAMID13 &&
1823  pyramid_elem_type != PYRAMID18)
1824  mooseError("unsupported situation");
1825 
1826  side_pairs.push_back(std::make_pair(0, 2));
1827  const unsigned int order = pyramid_elem_type == PYRAMID5 ? 1 : 2;
1828  axis_node_case = nodes_cates.first.front();
1829 
1830  new_elem = std::make_unique<Pyramid5>();
1831 
1832  if (order == 2)
1833  {
1834  new_elem = std::make_unique<Pyramid13>();
1835  if (pyramid_elem_type == PYRAMID18)
1836  {
1837  new_elem = std::make_unique<Pyramid18>();
1838  new_elem->set_node(13,
1839  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 3 + 3)->id() +
1840  ((current_layer * 2 + 1) * orig_nodes)));
1841  new_elem->set_node(15,
1842  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 3 + 3)->id() +
1843  ((current_layer * 2 + 1) * orig_nodes)));
1844  new_elem->set_node(17,
1845  mesh->node_ptr(elem->node_ptr(axis_node_case + 3)->id() +
1846  ((current_layer * 2 + 1) * orig_nodes)));
1847  new_elem->set_node(
1848  14, mesh->node_ptr(elem->node_ptr(6)->id() + (current_layer * 2 * orig_nodes)));
1849  new_elem->set_node(
1850  16,
1851  mesh->node_ptr(elem->node_ptr(6)->id() + ((current_layer + 1) %
1852  (total_num_azimuthal_intervals + 1 -
1853  (unsigned int)_full_circle_revolving) *
1854  2 * orig_nodes)));
1855  }
1856  new_elem->set_node(6,
1857  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 3)->id() +
1858  ((current_layer * 2 + 1) * orig_nodes)));
1859  new_elem->set_node(8,
1860  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 3)->id() +
1861  ((current_layer * 2 + 1) * orig_nodes)));
1862  new_elem->set_node(5,
1863  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 3 + 3)->id() +
1864  (current_layer * 2 * orig_nodes)));
1865  new_elem->set_node(
1866  7,
1867  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 3 + 3)->id() +
1868  ((current_layer + 1) %
1869  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
1870  2 * orig_nodes)));
1871  new_elem->set_node(9,
1872  mesh->node_ptr(elem->node_ptr(axis_node_case + 3)->id() +
1873  (current_layer * 2 * orig_nodes)));
1874  new_elem->set_node(10,
1875  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 3 + 3)->id() +
1876  (current_layer * 2 * orig_nodes)));
1877  new_elem->set_node(
1878  12,
1879  mesh->node_ptr(elem->node_ptr(axis_node_case + 3)->id() +
1880  ((current_layer + 1) %
1881  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
1882  2 * orig_nodes)));
1883  new_elem->set_node(
1884  11,
1885  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 3 + 3)->id() +
1886  ((current_layer + 1) %
1887  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
1888  2 * orig_nodes)));
1889  }
1890  new_elem->set_node(4, mesh->node_ptr(elem->node_ptr(axis_node_case)->id()));
1891  new_elem->set_node(0,
1892  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 3)->id() +
1893  (current_layer * order * orig_nodes)));
1894  new_elem->set_node(1,
1895  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 3)->id() +
1896  (current_layer * order * orig_nodes)));
1897  new_elem->set_node(
1898  2,
1899  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 3)->id() +
1900  ((current_layer + 1) %
1901  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
1902  order * orig_nodes)));
1903  new_elem->set_node(
1904  3,
1905  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 3)->id() +
1906  ((current_layer + 1) %
1907  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
1908  order * orig_nodes)));
1909 
1910  if (new_elem->volume() < 0.0)
1911  {
1912  MooseMeshUtils::swapNodesInElem(*new_elem, 1, 2);
1913  MooseMeshUtils::swapNodesInElem(*new_elem, 0, 3);
1914  if (order == 2)
1915  {
1916  MooseMeshUtils::swapNodesInElem(*new_elem, 5, 7);
1917  MooseMeshUtils::swapNodesInElem(*new_elem, 10, 11);
1918  MooseMeshUtils::swapNodesInElem(*new_elem, 9, 12);
1919  if (pyramid_elem_type == PYRAMID18)
1920  {
1921  MooseMeshUtils::swapNodesInElem(*new_elem, 14, 16);
1922  }
1923  }
1924  is_flipped = true;
1925  }
1926 }
1927 
1928 void
1930  const std::pair<std::vector<dof_id_type>, std::vector<dof_id_type>> & nodes_cates,
1931  const ElemType tet_elem_type,
1932  const Elem * elem,
1933  const std::unique_ptr<MeshBase> & mesh,
1934  std::unique_ptr<Elem> & new_elem,
1935  const int current_layer,
1936  const unsigned int orig_nodes,
1937  const unsigned int total_num_azimuthal_intervals,
1938  std::vector<std::pair<dof_id_type, dof_id_type>> & side_pairs,
1939  dof_id_type & axis_node_case,
1940  bool & is_flipped) const
1941 {
1942  if (tet_elem_type != TET4 && tet_elem_type != TET10 && tet_elem_type != TET14)
1943  mooseError("unsupported situation");
1944 
1945  side_pairs.push_back(std::make_pair(0, 1));
1946  const unsigned int order = tet_elem_type == TET4 ? 1 : 2;
1947  if (order == 2)
1948  {
1949  // Sanity check to filter unsupported cases
1950  if (nodes_cates.first[0] > 2 || nodes_cates.first[1] > 2 || nodes_cates.first[2] < 3)
1951  mooseError("unsupported situation 2");
1952  }
1953  axis_node_case = nodes_cates.second.front();
1954 
1955  new_elem = std::make_unique<Tet4>();
1956  if (order == 2)
1957  {
1958  const bool node_order = nodes_cates.first[1] - nodes_cates.first[0] == 1;
1959  new_elem = std::make_unique<Tet10>();
1960  if (tet_elem_type == TET14)
1961  {
1962  new_elem = std::make_unique<Tet14>();
1963  new_elem->set_node(
1964  12,
1965  mesh->node_ptr(elem->node_ptr(node_order ? (nodes_cates.first[1] + 3)
1966  : (nodes_cates.second.front() + 3))
1967  ->id() +
1968  ((current_layer * 2 + 1) * orig_nodes)));
1969  new_elem->set_node(13,
1970  mesh->node_ptr(elem->node_ptr(node_order ? (nodes_cates.second.front() + 3)
1971  : (nodes_cates.first[0] + 3))
1972  ->id() +
1973  ((current_layer * 2 + 1) * orig_nodes)));
1974  new_elem->set_node(
1975  10, mesh->node_ptr(elem->node_ptr(6)->id() + (current_layer * 2 * orig_nodes)));
1976  new_elem->set_node(
1977  11,
1978  mesh->node_ptr(elem->node_ptr(6)->id() + ((current_layer + 1) %
1979  (total_num_azimuthal_intervals + 1 -
1980  (unsigned int)_full_circle_revolving) *
1981  2 * orig_nodes)));
1982  }
1983  new_elem->set_node(4,
1984  mesh->node_ptr(elem->node_ptr(node_order ? (nodes_cates.first[0] + 3)
1985  : (nodes_cates.first[1] + 3))
1986  ->id()));
1987  new_elem->set_node(5,
1988  mesh->node_ptr(elem->node_ptr(node_order ? (nodes_cates.first[1] + 3)
1989  : (nodes_cates.second.front() + 3))
1990  ->id() +
1991  (current_layer * 2 * orig_nodes)));
1992  new_elem->set_node(6,
1993  mesh->node_ptr(elem->node_ptr(node_order ? (nodes_cates.second.front() + 3)
1994  : (nodes_cates.first[0] + 3))
1995  ->id() +
1996  (current_layer * 2 * orig_nodes)));
1997  new_elem->set_node(
1998  8,
1999  mesh->node_ptr(elem->node_ptr(node_order ? (nodes_cates.first[1] + 3)
2000  : (nodes_cates.second.front() + 3))
2001  ->id() +
2002  ((current_layer + 1) %
2003  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2004  2 * orig_nodes)));
2005  new_elem->set_node(
2006  7,
2007  mesh->node_ptr(elem->node_ptr(node_order ? (nodes_cates.second.front() + 3)
2008  : (nodes_cates.first[0] + 3))
2009  ->id() +
2010  ((current_layer + 1) %
2011  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2012  2 * orig_nodes)));
2013  new_elem->set_node(9,
2014  mesh->node_ptr(elem->node_ptr(nodes_cates.second.front())->id() +
2015  ((current_layer * 2 + 1) * orig_nodes)));
2016  }
2017  new_elem->set_node(0, mesh->node_ptr(elem->node_ptr(nodes_cates.first[0])->id()));
2018  new_elem->set_node(1, mesh->node_ptr(elem->node_ptr(nodes_cates.first[1])->id()));
2019  new_elem->set_node(2,
2020  mesh->node_ptr(elem->node_ptr(nodes_cates.second.front())->id() +
2021  (current_layer * order * orig_nodes)));
2022  new_elem->set_node(
2023  3,
2024  mesh->node_ptr(elem->node_ptr(nodes_cates.second.front())->id() +
2025  ((current_layer + 1) %
2026  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2027  order * orig_nodes)));
2028 
2029  if (new_elem->volume() < 0.0)
2030  {
2031  MooseMeshUtils::swapNodesInElem(*new_elem, 2, 3);
2032  if (order == 2)
2033  {
2034  MooseMeshUtils::swapNodesInElem(*new_elem, 5, 8);
2035  MooseMeshUtils::swapNodesInElem(*new_elem, 6, 7);
2036  if (tet_elem_type == TET14)
2037  {
2038  MooseMeshUtils::swapNodesInElem(*new_elem, 10, 11);
2039  }
2040  }
2041  is_flipped = true;
2042  }
2043 }
2044 
2045 void
2047  const Elem * elem,
2048  const std::unique_ptr<MeshBase> & mesh,
2049  std::unique_ptr<Elem> & new_elem,
2050  const int current_layer,
2051  const unsigned int orig_nodes,
2052  const unsigned int total_num_azimuthal_intervals,
2053  std::vector<std::pair<dof_id_type, dof_id_type>> & side_pairs,
2054  bool & is_flipped) const
2055 {
2056  if (hex_elem_type != HEX8 && hex_elem_type != HEX20 && hex_elem_type != HEX27)
2057  mooseError("unsupported situation");
2058 
2059  side_pairs.push_back(std::make_pair(0, 5));
2060  const unsigned int order = hex_elem_type == HEX8 ? 1 : 2;
2061 
2062  new_elem = std::make_unique<Hex8>();
2063  if (order == 2)
2064  {
2065  new_elem = std::make_unique<Hex20>();
2066  if (hex_elem_type == HEX27)
2067  {
2068  new_elem = std::make_unique<Hex27>();
2069  new_elem->set_node(
2070  20, mesh->node_ptr(elem->node_ptr(8)->id() + (current_layer * 2 * orig_nodes)));
2071  new_elem->set_node(
2072  25,
2073  mesh->node_ptr(elem->node_ptr(8)->id() + ((current_layer + 1) %
2074  (total_num_azimuthal_intervals + 1 -
2075  (unsigned int)_full_circle_revolving) *
2076  2 * orig_nodes)));
2077  new_elem->set_node(
2078  26, mesh->node_ptr(elem->node_ptr(8)->id() + ((current_layer * 2 + 1) * orig_nodes)));
2079  new_elem->set_node(
2080  21, mesh->node_ptr(elem->node_ptr(4)->id() + ((current_layer * 2 + 1) * orig_nodes)));
2081  new_elem->set_node(
2082  22, mesh->node_ptr(elem->node_ptr(5)->id() + ((current_layer * 2 + 1) * orig_nodes)));
2083  new_elem->set_node(
2084  23, mesh->node_ptr(elem->node_ptr(6)->id() + ((current_layer * 2 + 1) * orig_nodes)));
2085  new_elem->set_node(
2086  24, mesh->node_ptr(elem->node_ptr(7)->id() + ((current_layer * 2 + 1) * orig_nodes)));
2087  }
2088  new_elem->set_node(8,
2089  mesh->node_ptr(elem->node_ptr(4)->id() + (current_layer * 2 * orig_nodes)));
2090  new_elem->set_node(9,
2091  mesh->node_ptr(elem->node_ptr(5)->id() + (current_layer * 2 * orig_nodes)));
2092  new_elem->set_node(10,
2093  mesh->node_ptr(elem->node_ptr(6)->id() + (current_layer * 2 * orig_nodes)));
2094  new_elem->set_node(11,
2095  mesh->node_ptr(elem->node_ptr(7)->id() + (current_layer * 2 * orig_nodes)));
2096  new_elem->set_node(
2097  16,
2098  mesh->node_ptr(elem->node_ptr(4)->id() +
2099  ((current_layer + 1) %
2100  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2101  2 * orig_nodes)));
2102  new_elem->set_node(
2103  17,
2104  mesh->node_ptr(elem->node_ptr(5)->id() +
2105  ((current_layer + 1) %
2106  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2107  2 * orig_nodes)));
2108  new_elem->set_node(
2109  18,
2110  mesh->node_ptr(elem->node_ptr(6)->id() +
2111  ((current_layer + 1) %
2112  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2113  2 * orig_nodes)));
2114  new_elem->set_node(
2115  19,
2116  mesh->node_ptr(elem->node_ptr(7)->id() +
2117  ((current_layer + 1) %
2118  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2119  2 * orig_nodes)));
2120  new_elem->set_node(
2121  12, mesh->node_ptr(elem->node_ptr(0)->id() + ((current_layer * 2 + 1) * orig_nodes)));
2122  new_elem->set_node(
2123  13, mesh->node_ptr(elem->node_ptr(1)->id() + ((current_layer * 2 + 1) * orig_nodes)));
2124  new_elem->set_node(
2125  14, mesh->node_ptr(elem->node_ptr(2)->id() + ((current_layer * 2 + 1) * orig_nodes)));
2126  new_elem->set_node(
2127  15, mesh->node_ptr(elem->node_ptr(3)->id() + ((current_layer * 2 + 1) * orig_nodes)));
2128  }
2129  new_elem->set_node(
2130  0, mesh->node_ptr(elem->node_ptr(0)->id() + (current_layer * order * orig_nodes)));
2131  new_elem->set_node(
2132  1, mesh->node_ptr(elem->node_ptr(1)->id() + (current_layer * order * orig_nodes)));
2133  new_elem->set_node(
2134  2, mesh->node_ptr(elem->node_ptr(2)->id() + (current_layer * order * orig_nodes)));
2135  new_elem->set_node(
2136  3, mesh->node_ptr(elem->node_ptr(3)->id() + (current_layer * order * orig_nodes)));
2137  new_elem->set_node(
2138  4,
2139  mesh->node_ptr(elem->node_ptr(0)->id() +
2140  ((current_layer + 1) %
2141  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2142  order * orig_nodes)));
2143  new_elem->set_node(
2144  5,
2145  mesh->node_ptr(elem->node_ptr(1)->id() +
2146  ((current_layer + 1) %
2147  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2148  order * orig_nodes)));
2149  new_elem->set_node(
2150  6,
2151  mesh->node_ptr(elem->node_ptr(2)->id() +
2152  ((current_layer + 1) %
2153  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2154  order * orig_nodes)));
2155  new_elem->set_node(
2156  7,
2157  mesh->node_ptr(elem->node_ptr(3)->id() +
2158  ((current_layer + 1) %
2159  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2160  order * orig_nodes)));
2161 
2162  if (new_elem->volume() < 0.0)
2163  {
2164  MooseMeshUtils::swapNodesInElem(*new_elem, 0, 4);
2165  MooseMeshUtils::swapNodesInElem(*new_elem, 1, 5);
2166  MooseMeshUtils::swapNodesInElem(*new_elem, 2, 6);
2167  MooseMeshUtils::swapNodesInElem(*new_elem, 3, 7);
2168  if (order == 2)
2169  {
2170  MooseMeshUtils::swapNodesInElem(*new_elem, 8, 16);
2171  MooseMeshUtils::swapNodesInElem(*new_elem, 9, 17);
2172  MooseMeshUtils::swapNodesInElem(*new_elem, 10, 18);
2173  MooseMeshUtils::swapNodesInElem(*new_elem, 11, 19);
2174  if (hex_elem_type == HEX27)
2175  {
2176  MooseMeshUtils::swapNodesInElem(*new_elem, 20, 25);
2177  }
2178  }
2179  is_flipped = true;
2180  }
2181 }
2182 
2183 void
2185  const std::pair<std::vector<dof_id_type>, std::vector<dof_id_type>> & nodes_cates,
2186  const ElemType prism_elem_type,
2187  const Elem * elem,
2188  const std::unique_ptr<MeshBase> & mesh,
2189  std::unique_ptr<Elem> & new_elem,
2190  const int current_layer,
2191  const unsigned int orig_nodes,
2192  const unsigned int total_num_azimuthal_intervals,
2193  std::vector<std::pair<dof_id_type, dof_id_type>> & side_pairs,
2194  dof_id_type & axis_node_case,
2195  bool & is_flipped) const
2196 {
2197  if (prism_elem_type != PRISM6 && prism_elem_type != PRISM15 && prism_elem_type != PRISM18)
2198  mooseError("unsupported situation");
2199 
2200  side_pairs.push_back(std::make_pair(1, 3));
2201  const unsigned int order = prism_elem_type == PRISM6 ? 1 : 2;
2202  if (order == 2)
2203  {
2204  // Sanity check to filter unsupported cases
2205  if (nodes_cates.first[0] > 3 || nodes_cates.first[1] > 3 || nodes_cates.first[2] < 4)
2206  mooseError("unsupported situation 2");
2207  }
2208  // Can only be 0-1, 1-2, 2-3, 3-0, we only consider vetices here.
2209  // nodes_cates are natually sorted
2210  const dof_id_type min_on_axis = nodes_cates.first[0];
2211  const dof_id_type max_on_axis = nodes_cates.first[1];
2212  axis_node_case = max_on_axis - min_on_axis == 1 ? min_on_axis : max_on_axis;
2213 
2214  new_elem = std::make_unique<Prism6>();
2215  if (order == 2)
2216  {
2217  new_elem = std::make_unique<Prism15>();
2218  if (prism_elem_type == PRISM18)
2219  {
2220  new_elem = std::make_unique<Prism18>();
2221  new_elem->set_node(
2222  15, mesh->node_ptr(elem->node_ptr(8)->id() + (current_layer * 2 * orig_nodes)));
2223  new_elem->set_node(16,
2224  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4 + 4)->id() +
2225  ((current_layer * 2 + 1) * orig_nodes)));
2226  new_elem->set_node(
2227  17,
2228  mesh->node_ptr(elem->node_ptr(8)->id() + ((current_layer + 1) %
2229  (total_num_azimuthal_intervals + 1 -
2230  (unsigned int)_full_circle_revolving) *
2231  2 * orig_nodes)));
2232  }
2233  new_elem->set_node(9, mesh->node_ptr(elem->node_ptr(axis_node_case + 4)->id()));
2234  new_elem->set_node(10,
2235  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4 + 4)->id() +
2236  (current_layer * 2 * orig_nodes)));
2237  new_elem->set_node(12,
2238  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4 + 4)->id() +
2239  (current_layer * 2 * orig_nodes)));
2240  new_elem->set_node(6,
2241  mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4 + 4)->id() +
2242  (current_layer * 2 * orig_nodes)));
2243  new_elem->set_node(
2244  14,
2245  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4 + 4)->id() +
2246  ((current_layer + 1) %
2247  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2248  2 * orig_nodes)));
2249  new_elem->set_node(
2250  8,
2251  mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4 + 4)->id() +
2252  ((current_layer + 1) %
2253  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2254  2 * orig_nodes)));
2255  new_elem->set_node(
2256  11,
2257  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4 + 4)->id() +
2258  ((current_layer + 1) %
2259  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2260  2 * orig_nodes)));
2261  new_elem->set_node(7,
2262  mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() +
2263  ((current_layer * 2 + 1) * orig_nodes)));
2264  new_elem->set_node(13,
2265  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4)->id() +
2266  ((current_layer * 2 + 1) * orig_nodes)));
2267  }
2268  new_elem->set_node(0, mesh->node_ptr(elem->node_ptr(axis_node_case)->id()));
2269  new_elem->set_node(3, mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4)->id()));
2270  new_elem->set_node(4,
2271  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4)->id() +
2272  (current_layer * order * orig_nodes)));
2273  new_elem->set_node(1,
2274  mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() +
2275  (current_layer * order * orig_nodes)));
2276  new_elem->set_node(
2277  5,
2278  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4)->id() +
2279  ((current_layer + 1) %
2280  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2281  order * orig_nodes)));
2282  new_elem->set_node(
2283  2,
2284  mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() +
2285  ((current_layer + 1) %
2286  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2287  order * orig_nodes)));
2288 
2289  if (new_elem->volume() < 0.0)
2290  {
2291  MooseMeshUtils::swapNodesInElem(*new_elem, 1, 2);
2292  MooseMeshUtils::swapNodesInElem(*new_elem, 4, 5);
2293  if (order == 2)
2294  {
2295  MooseMeshUtils::swapNodesInElem(*new_elem, 12, 14);
2296  MooseMeshUtils::swapNodesInElem(*new_elem, 6, 8);
2297  MooseMeshUtils::swapNodesInElem(*new_elem, 10, 11);
2298  if (prism_elem_type == PRISM18)
2299  {
2300  MooseMeshUtils::swapNodesInElem(*new_elem, 15, 17);
2301  }
2302  }
2303  is_flipped = true;
2304  }
2305 }
2306 
2307 void
2309  const std::pair<std::vector<dof_id_type>, std::vector<dof_id_type>> & nodes_cates,
2310  const ElemType pyramid_elem_type,
2311  const ElemType prism_elem_type,
2312  const Elem * elem,
2313  const std::unique_ptr<MeshBase> & mesh,
2314  std::unique_ptr<Elem> & new_elem,
2315  std::unique_ptr<Elem> & new_elem_1,
2316  const int current_layer,
2317  const unsigned int orig_nodes,
2318  const unsigned int total_num_azimuthal_intervals,
2319  std::vector<std::pair<dof_id_type, dof_id_type>> & side_pairs,
2320  dof_id_type & axis_node_case,
2321  bool & is_flipped,
2322  bool & is_flipped_additional) const
2323 {
2324  if (!(pyramid_elem_type == PYRAMID5 && prism_elem_type == PRISM6) &&
2325  !(pyramid_elem_type == PYRAMID14 && prism_elem_type == PRISM18))
2326  mooseError("unsupported situation");
2327  const unsigned int order = pyramid_elem_type == PYRAMID5 ? 1 : 2;
2328 
2329  side_pairs.push_back(std::make_pair(0, 2));
2330  axis_node_case = nodes_cates.first.front();
2331  new_elem = std::make_unique<Pyramid5>();
2332  if (pyramid_elem_type == PYRAMID14)
2333  {
2334  new_elem = std::make_unique<Pyramid14>();
2335  new_elem->set_node(9,
2336  mesh->node_ptr(elem->node_ptr(axis_node_case + 4)->id() +
2337  (current_layer * 2 * orig_nodes)));
2338  new_elem->set_node(10,
2339  mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4 + 4)->id() +
2340  (current_layer * 2 * orig_nodes)));
2341  new_elem->set_node(5,
2342  mesh->node_ptr(elem->node_ptr(8)->id() + (current_layer * 2 * orig_nodes)));
2343  new_elem->set_node(8,
2344  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4)->id() +
2345  ((current_layer * 2 + 1) * orig_nodes)));
2346  new_elem->set_node(6,
2347  mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() +
2348  ((current_layer * 2 + 1) * orig_nodes)));
2349  new_elem->set_node(
2350  13, mesh->node_ptr(elem->node_ptr(8)->id() + ((current_layer * 2 + 1) * orig_nodes)));
2351  new_elem->set_node(
2352  7,
2353  mesh->node_ptr(elem->node_ptr(8)->id() +
2354  ((current_layer + 1) %
2355  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2356  2 * orig_nodes)));
2357  new_elem->set_node(
2358  11,
2359  mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4 + 4)->id() +
2360  ((current_layer + 1) %
2361  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2362  2 * orig_nodes)));
2363  new_elem->set_node(
2364  12,
2365  mesh->node_ptr(elem->node_ptr(axis_node_case + 4)->id() +
2366  ((current_layer + 1) %
2367  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2368  2 * orig_nodes)));
2369  }
2370  new_elem->set_node(4, mesh->node_ptr(elem->node_ptr(axis_node_case)->id()));
2371  new_elem->set_node(0,
2372  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4)->id() +
2373  (current_layer * order * orig_nodes)));
2374  new_elem->set_node(1,
2375  mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() +
2376  (current_layer * order * orig_nodes)));
2377  new_elem->set_node(
2378  2,
2379  mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() +
2380  ((current_layer + 1) %
2381  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2382  order * orig_nodes)));
2383  new_elem->set_node(
2384  3,
2385  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4)->id() +
2386  ((current_layer + 1) %
2387  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2388  order * orig_nodes)));
2389 
2390  if (new_elem->volume() < 0.0)
2391  {
2392  MooseMeshUtils::swapNodesInElem(*new_elem, 1, 2);
2393  MooseMeshUtils::swapNodesInElem(*new_elem, 0, 3);
2394  if (pyramid_elem_type == PYRAMID14)
2395  {
2396  MooseMeshUtils::swapNodesInElem(*new_elem, 5, 7);
2397  MooseMeshUtils::swapNodesInElem(*new_elem, 10, 11);
2398  MooseMeshUtils::swapNodesInElem(*new_elem, 9, 12);
2399  }
2400  is_flipped = true;
2401  }
2402 
2403  side_pairs.push_back(std::make_pair(0, 4));
2404  new_elem_1 = std::make_unique<Prism6>();
2405  if (prism_elem_type == PRISM18)
2406  {
2407  new_elem_1 = std::make_unique<Prism18>();
2408  new_elem_1->set_node(
2409  6, mesh->node_ptr(elem->node_ptr(8)->id() + (current_layer * 2 * orig_nodes)));
2410  new_elem_1->set_node(8,
2411  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4 + 4)->id() +
2412  (current_layer * 2 * orig_nodes)));
2413  new_elem_1->set_node(7,
2414  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4 + 4)->id() +
2415  (current_layer * 2 * orig_nodes)));
2416  new_elem_1->set_node(9,
2417  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4)->id() +
2418  ((current_layer * 2 + 1) * orig_nodes)));
2419  new_elem_1->set_node(10,
2420  mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() +
2421  ((current_layer * 2 + 1) * orig_nodes)));
2422  new_elem_1->set_node(11,
2423  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4)->id() +
2424  ((current_layer * 2 + 1) * orig_nodes)));
2425  new_elem_1->set_node(
2426  15, mesh->node_ptr(elem->node_ptr(8)->id() + ((current_layer * 2 + 1) * orig_nodes)));
2427  new_elem_1->set_node(17,
2428  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4 + 4)->id() +
2429  ((current_layer * 2 + 1) * orig_nodes)));
2430  new_elem_1->set_node(16,
2431  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4 + 4)->id() +
2432  ((current_layer * 2 + 1) * orig_nodes)));
2433  new_elem_1->set_node(
2434  12,
2435  mesh->node_ptr(elem->node_ptr(8)->id() +
2436  ((current_layer + 1) %
2437  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2438  2 * orig_nodes)));
2439  new_elem_1->set_node(
2440  13,
2441  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4 + 4)->id() +
2442  ((current_layer + 1) %
2443  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2444  2 * orig_nodes)));
2445  new_elem_1->set_node(
2446  14,
2447  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4 + 4)->id() +
2448  ((current_layer + 1) %
2449  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2450  2 * orig_nodes)));
2451  }
2452  new_elem_1->set_node(0,
2453  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4)->id() +
2454  (current_layer * order * orig_nodes)));
2455  new_elem_1->set_node(1,
2456  mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() +
2457  (current_layer * order * orig_nodes)));
2458  new_elem_1->set_node(2,
2459  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4)->id() +
2460  (current_layer * order * orig_nodes)));
2461  new_elem_1->set_node(
2462  3,
2463  mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4)->id() +
2464  ((current_layer + 1) %
2465  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2466  order * orig_nodes)));
2467  new_elem_1->set_node(
2468  4,
2469  mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() +
2470  ((current_layer + 1) %
2471  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2472  order * orig_nodes)));
2473  new_elem_1->set_node(
2474  5,
2475  mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4)->id() +
2476  ((current_layer + 1) %
2477  (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) *
2478  order * orig_nodes)));
2479 
2480  if (new_elem_1->volume() < 0.0)
2481  {
2482  MooseMeshUtils::swapNodesInElem(*new_elem_1, 0, 3);
2483  MooseMeshUtils::swapNodesInElem(*new_elem_1, 1, 4);
2484  MooseMeshUtils::swapNodesInElem(*new_elem_1, 2, 5);
2485  if (prism_elem_type == PRISM18)
2486  {
2487  MooseMeshUtils::swapNodesInElem(*new_elem_1, 6, 12);
2488  MooseMeshUtils::swapNodesInElem(*new_elem_1, 7, 13);
2489  MooseMeshUtils::swapNodesInElem(*new_elem_1, 8, 14);
2490  }
2491  is_flipped_additional = true;
2492  }
2493 }
void swapNodesInElem(Elem &elem, const unsigned int nd1, const unsigned int nd2)
void createPRISMfromQUAD(const std::pair< std::vector< dof_id_type >, std::vector< dof_id_type >> &nodes_cates, const ElemType prism_elem_type, const Elem *elem, const std::unique_ptr< MeshBase > &mesh, std::unique_ptr< Elem > &new_elem, const int current_layer, const unsigned int orig_nodes, const unsigned int total_num_azimuthal_intervals, std::vector< std::pair< dof_id_type, dof_id_type >> &side_pairs, dof_id_type &axis_node_case, bool &is_flipped) const
Create a new PRISM element from an existing QUAD element by revolving it.
This RevolveGenerator object is designed to revolve a 1D mesh into 2D, or a 2D mesh into 3D based on ...
std::pair< Real, Point > getRotationCenterAndRadius(const Point &p_ext, const Point &p_axis, const Point &dir_axis) const
Get the rotation center and radius of the circular rotation based on the rotation axis and the extern...
ElemType
virtual void reserve_nodes(const dof_id_type nn)=0
const bool _preserve_volumes
Volume preserving function is optional.
bool _has_start_boundary
Whether a starting boundary is specified.
virtual const char * what() const
const Real radius
auto norm() const
QUAD8
HEX8
void remove_orphaned_nodes()
void paramError(const std::string &param, Args... args) const
T & getMesh(MooseMesh &mesh)
function to cast mesh
Definition: SCM.h:35
boundary_id_type _start_boundary
Boundary ID of the starting boundary.
void nodeModification(Node &node)
Modify the position of a node to account for radius correction.
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
char ** blocks
static InputParameters validParams()
boundary_id_type _end_boundary
Boundary ID of the ending boundary.
registerMooseObject("ReactorApp", RevolveGenerator)
const Point & _axis_direction
A direction vector of the axis of revolution.
std::pair< std::vector< dof_id_type >, std::vector< dof_id_type > > onAxisNodesIdentifier(const Elem &elem, const std::vector< dof_id_type > &nodes_on_axis) const
Categorize the nodes of an element into two groups: nodes on the axis and nodes off the axis...
void createTETfromTRI(const std::pair< std::vector< dof_id_type >, std::vector< dof_id_type >> &nodes_cates, const ElemType tet_elem_type, const Elem *elem, const std::unique_ptr< MeshBase > &mesh, std::unique_ptr< Elem > &new_elem, const int current_layer, const unsigned int orig_nodes, const unsigned int total_num_azimuthal_intervals, std::vector< std::pair< dof_id_type, dof_id_type >> &side_pairs, dof_id_type &axis_node_case, bool &is_flipped) const
Create a new TET element from an existing TRI element by revolving it.
TET10
void createPYRAMIDPRISMfromQUAD(const std::pair< std::vector< dof_id_type >, std::vector< dof_id_type >> &nodes_cates, const ElemType pyramid_elem_type, const ElemType prism_elem_type, const Elem *elem, const std::unique_ptr< MeshBase > &mesh, std::unique_ptr< Elem > &new_elem, std::unique_ptr< Elem > &new_elem_1, const int current_layer, const unsigned int orig_nodes, const unsigned int total_num_azimuthal_intervals, std::vector< std::pair< dof_id_type, dof_id_type >> &side_pairs, dof_id_type &axis_node_case, bool &is_flipped, bool &is_flipped_additional) const
Create a new PYRAMID element and a new PRISM element from an existing QUAD element by revolving it...
std::unique_ptr< MeshBase > & _input
Lower dimensional mesh from another generator.
const std::vector< std::string > & _elem_integer_names_to_swap
Names and indices of extra element integers to swap.
PRISM15
unsigned int add_elem_integer(std::string name, bool allocate_data=true, dof_id_type default_value=DofObject::invalid_id)
bool has_elem_integer(std::string_view name) const
MeshBase & mesh
const std::vector< unsigned int > & _nums_azimuthal_intervals
Numbers of azimuthal mesh intervals in each azimuthal section.
unique_id_type unique_id() const
const Parallel::Communicator & comm() const
HEX20
RevolveGenerator(const InputParameters &parameters)
void boundary_ids(const Node *node, std::vector< boundary_id_type > &vec_to_fill) const
PRISM21
virtual void set_next_unique_id(unique_id_type id)=0
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 integer name.
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
const BoundaryInfo & get_boundary_info() const
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
void addRequiredParam(const std::string &name, const std::string &doc_string)
TRI3
QUAD4
void createPYRAMIDfromTRI(const std::pair< std::vector< dof_id_type >, std::vector< dof_id_type >> &nodes_cates, const ElemType pyramid_elem_type, const Elem *elem, const std::unique_ptr< MeshBase > &mesh, std::unique_ptr< Elem > &new_elem, const int current_layer, const unsigned int orig_nodes, const unsigned int total_num_azimuthal_intervals, std::vector< std::pair< dof_id_type, dof_id_type >> &side_pairs, dof_id_type &axis_node_case, bool &is_flipped) const
Create a new PYRAMID element from an existing TRI element by revolving it.
bool _has_end_boundary
Whether an ending boundary is specified.
std::map< boundary_id_type, std::string > & set_sideset_name_map()
TypeVector< Real > unit() const
void libmesh_ignore(const Args &...)
void add_node(const Node *node, const boundary_id_type id)
static InputParameters validParams()
const std::string & name() const
bool _full_circle_revolving
Whether to revolve for a full circle or not.
int8_t boundary_id_type
dof_id_type id() const
TET4
virtual unsigned int n_nodes() const=0
const std::vector< std::vector< subdomain_id_type > > & _subdomain_swaps
Subdomains to swap out for each azimuthal section.
const std::vector< Real > _revolving_angles
Angles of revolution delineating each azimuthal section.
TRI6
virtual Elem * add_elem(Elem *e)=0
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)
HEX27
void set_mesh_dimension(unsigned char d)
TypeVector< typename CompareTypes< Real, T2 >::supertype > cross(const TypeVector< T2 > &v) const
TET14
std::map< subdomain_id_type, std::string > & set_subdomain_name_map()
const bool & _clockwise
Revolving direction.
std::vector< std::unordered_map< boundary_id_type, boundary_id_type > > _boundary_swap_pairs
Easier to work with version of _boundary_swaps.
const Point & _axis_point
A point of the axis of revolution.
std::vector< Real > _unit_angles
Unit angles of all azimuthal sections of revolution.
std::unique_ptr< MeshBase > generate() override
std::vector< std::unordered_map< subdomain_id_type, subdomain_id_type > > _subdomain_swap_pairs
Easier to work with version of _sudomain_swaps.
void set_unique_id(unique_id_type new_id)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
EDGE2
void max(const T &r, T &o, Request &req) const
void createQUADfromEDGE(const ElemType quad_elem_type, const Elem *elem, const std::unique_ptr< MeshBase > &mesh, std::unique_ptr< Elem > &new_elem, const int current_layer, const unsigned int orig_nodes, const unsigned int total_num_azimuthal_intervals, std::vector< std::pair< dof_id_type, dof_id_type >> &side_pairs, bool &is_flipped) const
Create a new QUAD element from an existing EDGE element by revolving it.
auto norm(const T &a)
const Node * node_ptr(const unsigned int i) const
PYRAMID18
std::map< boundary_id_type, std::string > & set_nodeset_name_map()
PYRAMID5
const std::vector< std::vector< boundary_id_type > > & _boundary_swaps
Boundaries to swap out for each elevation.
A base class that contains common members for Reactor module mesh generators.
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
IntRange< T > make_range(T beg, T end)
void mooseError(Args &&... args) const
TRI7
void addClassDescription(const std::string &doc_string)
Real _radius_correction_factor
Radius correction factor.
std::vector< unsigned int > _elem_integer_indices_to_swap
void createHEXfromQUAD(const ElemType hex_elem_type, const Elem *elem, const std::unique_ptr< MeshBase > &mesh, std::unique_ptr< Elem > &new_elem, const int current_layer, const unsigned int orig_nodes, const unsigned int total_num_azimuthal_intervals, std::vector< std::pair< dof_id_type, dof_id_type >> &side_pairs, bool &is_flipped) const
Create a new HEX element from an existing QUAD element by revolving it.
QUAD9
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)
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
PYRAMID13
Point meshCentroidCalculator(const MeshBase &mesh)
std::unique_ptr< MeshBase > buildMeshBaseObject(unsigned int dim=libMesh::invalid_uint)
PRISM6
virtual void delete_remote_elements()
PRISM18
Real radiusCorrectionFactor(const std::vector< Real > &azimuthal_list, const bool full_circle=true, const unsigned int order=1, const bool is_first_value_vertex=true)
Makes radial correction to preserve ring area.
std::vector< Point > rotationVectors(const Point &p_axis, const Point &dir_axis, const Point &p_input) const
Calculate the transform matrix between the rotation coordinate system and the original coordinate sys...
virtual const Node * node_ptr(const dof_id_type i) const=0
EDGE3
void createPRISMfromTRI(const ElemType prism_elem_type, const Elem *elem, const std::unique_ptr< MeshBase > &mesh, std::unique_ptr< Elem > &new_elem, const int current_layer, const unsigned int orig_nodes, const unsigned int total_num_azimuthal_intervals, std::vector< std::pair< dof_id_type, dof_id_type >> &side_pairs, bool &is_flipped) const
Create a new PRISM element from an existing TRI element by revolving it.
virtual ElemType type() const=0
dof_id_type node_id(const unsigned int i) const
virtual void reserve_elem(const dof_id_type ne)=0
uint8_t unique_id_type
static const std::string k
Definition: NS.h:134
void unset_is_prepared()
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.
void set_extra_integer(const unsigned int index, const dof_id_type value)
virtual void renumber_nodes_and_elements()=0
void createTRIfromEDGE(const std::pair< std::vector< dof_id_type >, std::vector< dof_id_type >> &nodes_cates, const ElemType tri_elem_type, const Elem *elem, const std::unique_ptr< MeshBase > &mesh, std::unique_ptr< Elem > &new_elem, const int current_layer, const unsigned int orig_nodes, const unsigned int total_num_azimuthal_intervals, std::vector< std::pair< dof_id_type, dof_id_type >> &side_pairs, dof_id_type &axis_node_case, bool &is_flipped) const
Create a new TRI element from an existing EDGE element by revolving it.
uint8_t dof_id_type
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
PYRAMID14