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