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