diff --git a/framework/include/meshgenerators/AdvancedExtruderGenerator.h b/framework/include/meshgenerators/AdvancedExtruderGenerator.h index 3e2d76e63fa9..a6e85ed6a7a7 100644 --- a/framework/include/meshgenerators/AdvancedExtruderGenerator.h +++ b/framework/include/meshgenerators/AdvancedExtruderGenerator.h @@ -58,7 +58,7 @@ class AdvancedExtruderGenerator : public MeshGenerator std::vector> _boundary_swap_pairs; /// Easier to work with version of _elem_integers_swaps - std::vector> _elem_integers_swap_pairs; + std::vector> _elem_integers_swap_pairs; /// The direction of the extrusion Point _direction; @@ -83,12 +83,4 @@ class AdvancedExtruderGenerator : public MeshGenerator /// Axial pitch for a full rotation const Real _twist_pitch; - - /** - * Swap two nodes within an element - * @param elem element whose nodes need to be swapped - * @param nd1 index of the first node to be swapped - * @param nd2 index of the second node to be swapped - */ - void swapNodesInElem(Elem & elem, const unsigned int nd1, const unsigned int nd2); }; diff --git a/framework/include/utils/MooseMeshUtils.h b/framework/include/utils/MooseMeshUtils.h index d175e273791c..cf48a4ec1bd9 100644 --- a/framework/include/utils/MooseMeshUtils.h +++ b/framework/include/utils/MooseMeshUtils.h @@ -316,4 +316,66 @@ getIDFromName(const T & name) return id_Q; } + +/** + * Swap two nodes within an element + * @param elem element whose nodes need to be swapped + * @param nd1 index of the first node to be swapped + * @param nd2 index of the second node to be swapped + */ +void swapNodesInElem(Elem & elem, const unsigned int nd1, const unsigned int nd2); + +/** + * Reprocess the swap related input parameters to make pairs out of them to ease further processing + * @param class_name name of the mesh generator class used for exception messages + * @param id_name name of the parameter to be swapped used for exception messages + * @param id_swaps vector of vectors of the ids to be swapped + * @param id_swap_pairs vector of maps of the swapped pairs + * @param row_index_shift shift to be applied to the row index in the exception messages (useful + * when this method is utilized to process a fraction of a long vector) + */ +template +void +idSwapParametersProcessor(const std::string & class_name, + const std::string & id_name, + const std::vector> & id_swaps, + std::vector> & id_swap_pairs, + const unsigned int row_index_shift = 0) +{ + id_swap_pairs.resize(id_swaps.size()); + for (const auto i : index_range(id_swaps)) + { + const auto & swaps = id_swaps[i]; + auto & swap_pairs = id_swap_pairs[i]; + + if (swaps.size() % 2) + throw MooseException("Row ", + row_index_shift + i + 1, + " of ", + id_name, + " in ", + class_name, + " does not contain an even number of entries! Num entries: ", + swaps.size()); + + swap_pairs.reserve(swaps.size() / 2); + for (unsigned int j = 0; j < swaps.size(); j += 2) + swap_pairs[swaps[j]] = swaps[j + 1]; + } +} + +/** + * Reprocess the elem_integers_swaps into maps so they are easier to use + * @param class_name name of the mesh generator class used for exception messages + * @param num_sections number of sections in the mesh + * @param num_integers number of extra element integers in the mesh + * @param elem_integers_swaps vector of vectors of vectors of extra element ids to be swapped + * @param elem_integers_swap_pairs vector of maps of the swapped pairs + */ +void extraElemIntegerSwapParametersProcessor( + const std::string & class_name, + const unsigned int num_sections, + const unsigned int num_integers, + const std::vector>> & elem_integers_swaps, + std::vector> & elem_integers_swap_pairs); } diff --git a/framework/src/meshgenerators/AdvancedExtruderGenerator.C b/framework/src/meshgenerators/AdvancedExtruderGenerator.C index 43331b6bb6dd..dd1f4083cda5 100644 --- a/framework/src/meshgenerators/AdvancedExtruderGenerator.C +++ b/framework/src/meshgenerators/AdvancedExtruderGenerator.C @@ -9,6 +9,7 @@ #include "AdvancedExtruderGenerator.h" #include "MooseUtils.h" +#include "MooseMeshUtils.h" #include "libmesh/boundary_info.h" #include "libmesh/function_base.h" @@ -179,25 +180,14 @@ AdvancedExtruderGenerator::AdvancedExtruderGenerator(const InputParameters & par "If specified, 'subdomain_swaps' must be the same length as 'heights' in ", name()); - _subdomain_swap_pairs.resize(_subdomain_swaps.size()); - - // Reprocess the subdomain swaps to make pairs out of them so they are easier to use - for (unsigned int i = 0; i < _subdomain_swaps.size(); i++) + try { - const auto & elevation_swaps = _subdomain_swaps[i]; - auto & elevation_swap_pairs = _subdomain_swap_pairs[i]; - - if (elevation_swaps.size() % 2) - paramError("subdomain_swaps", - "Row ", - i + 1, - " of subdomain_swaps in ", - name(), - " does not contain an even number of entries! Num entries: ", - elevation_swaps.size()); - - for (unsigned int j = 0; j < elevation_swaps.size(); j += 2) - elevation_swap_pairs[elevation_swaps[j]] = elevation_swaps[j + 1]; + MooseMeshUtils::idSwapParametersProcessor( + name(), "subdomain_swaps", _subdomain_swaps, _subdomain_swap_pairs); + } + catch (const MooseException & e) + { + paramError("subdomain_swaps", e.what()); } if (_boundary_swaps.size() && (_boundary_swaps.size() != num_elevations)) @@ -205,25 +195,14 @@ AdvancedExtruderGenerator::AdvancedExtruderGenerator(const InputParameters & par "If specified, 'boundary_swaps' must be the same length as 'heights' in ", name()); - _boundary_swap_pairs.resize(_boundary_swaps.size()); - - // Reprocess the boundary swaps to make pairs out of them so they are easier to use - for (unsigned int i = 0; i < _boundary_swaps.size(); i++) + try { - const auto & elevation_bdry_swaps = _boundary_swaps[i]; - auto & elevation_bdry_swap_pairs = _boundary_swap_pairs[i]; - - if (elevation_bdry_swaps.size() % 2) - paramError("boundary_swaps", - "Row ", - i + 1, - " of boundary_swaps in ", - name(), - " does not contain an even number of entries! Num entries: ", - elevation_bdry_swaps.size()); - - for (unsigned int j = 0; j < elevation_bdry_swaps.size(); j += 2) - elevation_bdry_swap_pairs[elevation_bdry_swaps[j]] = elevation_bdry_swaps[j + 1]; + MooseMeshUtils::idSwapParametersProcessor( + name(), "boundary_swaps", _boundary_swaps, _boundary_swap_pairs); + } + catch (const MooseException & e) + { + paramError("boundary_swaps", e.what()); } if (_elem_integers_swaps.size() && @@ -238,26 +217,17 @@ AdvancedExtruderGenerator::AdvancedExtruderGenerator(const InputParameters & par "If specified, each element of 'elem_integers_swaps' must have the same length as " "the length of 'heights'."); - _elem_integers_swap_pairs.resize(num_elevations * _elem_integer_names_to_swap.size()); - // Reprocess the elem_integers_swaps to make pairs out of them so they are easier to use - for (unsigned int i = 0; i < _elem_integer_names_to_swap.size(); i++) + try { - for (unsigned int j = 0; j < num_elevations; j++) - { - const auto & elevation_extra_swaps = _elem_integers_swaps[i][j]; - auto & elevation_extra_swap_pairs = _elem_integers_swap_pairs[i * num_elevations + j]; - - if (elevation_extra_swaps.size() % 2) - paramError("elem_integers_swaps", - "Row ", - i * num_elevations + j + 1, - " of elem_integers_swaps in ", - name(), - " does not contain an even number of entries! Num entries: ", - elevation_extra_swaps.size()); - for (unsigned int k = 0; k < elevation_extra_swaps.size(); k += 2) - elevation_extra_swap_pairs[elevation_extra_swaps[k]] = elevation_extra_swaps[k + 1]; - } + MooseMeshUtils::extraElemIntegerSwapParametersProcessor(name(), + num_elevations, + _elem_integer_names_to_swap.size(), + _elem_integers_swaps, + _elem_integers_swap_pairs); + } + catch (const MooseException & e) + { + paramError("elem_integers_swaps", e.what()); } bool has_negative_entry = false; @@ -502,7 +472,7 @@ AdvancedExtruderGenerator::generate() for (unsigned int k = 0; k != num_layers; ++k) { std::unique_ptr new_elem; - bool isFlipped(false); + bool is_flipped(false); switch (etype) { case EDGE2: @@ -578,10 +548,10 @@ AdvancedExtruderGenerator::generate() if (new_elem->volume() < 0.0) { - swapNodesInElem(*new_elem, 0, 3); - swapNodesInElem(*new_elem, 1, 4); - swapNodesInElem(*new_elem, 2, 5); - isFlipped = true; + MooseMeshUtils::swapNodesInElem(*new_elem, 0, 3); + MooseMeshUtils::swapNodesInElem(*new_elem, 1, 4); + MooseMeshUtils::swapNodesInElem(*new_elem, 2, 5); + is_flipped = true; } break; @@ -635,13 +605,13 @@ AdvancedExtruderGenerator::generate() if (new_elem->volume() < 0.0) { - swapNodesInElem(*new_elem, 0, 3); - swapNodesInElem(*new_elem, 1, 4); - swapNodesInElem(*new_elem, 2, 5); - swapNodesInElem(*new_elem, 6, 12); - swapNodesInElem(*new_elem, 7, 13); - swapNodesInElem(*new_elem, 8, 14); - isFlipped = true; + MooseMeshUtils::swapNodesInElem(*new_elem, 0, 3); + MooseMeshUtils::swapNodesInElem(*new_elem, 1, 4); + MooseMeshUtils::swapNodesInElem(*new_elem, 2, 5); + MooseMeshUtils::swapNodesInElem(*new_elem, 6, 12); + MooseMeshUtils::swapNodesInElem(*new_elem, 7, 13); + MooseMeshUtils::swapNodesInElem(*new_elem, 8, 14); + is_flipped = true; } break; @@ -701,14 +671,14 @@ AdvancedExtruderGenerator::generate() if (new_elem->volume() < 0.0) { - swapNodesInElem(*new_elem, 0, 3); - swapNodesInElem(*new_elem, 1, 4); - swapNodesInElem(*new_elem, 2, 5); - swapNodesInElem(*new_elem, 6, 12); - swapNodesInElem(*new_elem, 7, 13); - swapNodesInElem(*new_elem, 8, 14); - swapNodesInElem(*new_elem, 18, 19); - isFlipped = true; + MooseMeshUtils::swapNodesInElem(*new_elem, 0, 3); + MooseMeshUtils::swapNodesInElem(*new_elem, 1, 4); + MooseMeshUtils::swapNodesInElem(*new_elem, 2, 5); + MooseMeshUtils::swapNodesInElem(*new_elem, 6, 12); + MooseMeshUtils::swapNodesInElem(*new_elem, 7, 13); + MooseMeshUtils::swapNodesInElem(*new_elem, 8, 14); + MooseMeshUtils::swapNodesInElem(*new_elem, 18, 19); + is_flipped = true; } break; @@ -744,11 +714,11 @@ AdvancedExtruderGenerator::generate() if (new_elem->volume() < 0.0) { - swapNodesInElem(*new_elem, 0, 4); - swapNodesInElem(*new_elem, 1, 5); - swapNodesInElem(*new_elem, 2, 6); - swapNodesInElem(*new_elem, 3, 7); - isFlipped = true; + MooseMeshUtils::swapNodesInElem(*new_elem, 0, 4); + MooseMeshUtils::swapNodesInElem(*new_elem, 1, 5); + MooseMeshUtils::swapNodesInElem(*new_elem, 2, 6); + MooseMeshUtils::swapNodesInElem(*new_elem, 3, 7); + is_flipped = true; } break; @@ -808,15 +778,15 @@ AdvancedExtruderGenerator::generate() if (new_elem->volume() < 0.0) { - swapNodesInElem(*new_elem, 0, 4); - swapNodesInElem(*new_elem, 1, 5); - swapNodesInElem(*new_elem, 2, 6); - swapNodesInElem(*new_elem, 3, 7); - swapNodesInElem(*new_elem, 8, 16); - swapNodesInElem(*new_elem, 9, 17); - swapNodesInElem(*new_elem, 10, 18); - swapNodesInElem(*new_elem, 11, 19); - isFlipped = true; + MooseMeshUtils::swapNodesInElem(*new_elem, 0, 4); + MooseMeshUtils::swapNodesInElem(*new_elem, 1, 5); + MooseMeshUtils::swapNodesInElem(*new_elem, 2, 6); + MooseMeshUtils::swapNodesInElem(*new_elem, 3, 7); + MooseMeshUtils::swapNodesInElem(*new_elem, 8, 16); + MooseMeshUtils::swapNodesInElem(*new_elem, 9, 17); + MooseMeshUtils::swapNodesInElem(*new_elem, 10, 18); + MooseMeshUtils::swapNodesInElem(*new_elem, 11, 19); + is_flipped = true; } break; @@ -890,16 +860,16 @@ AdvancedExtruderGenerator::generate() if (new_elem->volume() < 0.0) { - swapNodesInElem(*new_elem, 0, 4); - swapNodesInElem(*new_elem, 1, 5); - swapNodesInElem(*new_elem, 2, 6); - swapNodesInElem(*new_elem, 3, 7); - swapNodesInElem(*new_elem, 8, 16); - swapNodesInElem(*new_elem, 9, 17); - swapNodesInElem(*new_elem, 10, 18); - swapNodesInElem(*new_elem, 11, 19); - swapNodesInElem(*new_elem, 20, 25); - isFlipped = true; + MooseMeshUtils::swapNodesInElem(*new_elem, 0, 4); + MooseMeshUtils::swapNodesInElem(*new_elem, 1, 5); + MooseMeshUtils::swapNodesInElem(*new_elem, 2, 6); + MooseMeshUtils::swapNodesInElem(*new_elem, 3, 7); + MooseMeshUtils::swapNodesInElem(*new_elem, 8, 16); + MooseMeshUtils::swapNodesInElem(*new_elem, 9, 17); + MooseMeshUtils::swapNodesInElem(*new_elem, 10, 18); + MooseMeshUtils::swapNodesInElem(*new_elem, 11, 19); + MooseMeshUtils::swapNodesInElem(*new_elem, 20, 25); + is_flipped = true; } break; @@ -938,7 +908,7 @@ AdvancedExtruderGenerator::generate() for (unsigned int i = 0; i < _upward_boundary_source_blocks[e].size(); i++) if (new_elem->subdomain_id() == _upward_boundary_source_blocks[e][i]) boundary_info.add_side( - new_elem.get(), isFlipped ? 0 : top_id, _upward_boundary_ids[e][i]); + new_elem.get(), is_flipped ? 0 : top_id, _upward_boundary_ids[e][i]); } // define downward boundaries if (k == 0) @@ -948,7 +918,7 @@ AdvancedExtruderGenerator::generate() for (unsigned int i = 0; i < _downward_boundary_source_blocks[e].size(); i++) if (new_elem->subdomain_id() == _downward_boundary_source_blocks[e][i]) boundary_info.add_side( - new_elem.get(), isFlipped ? top_id : 0, _downward_boundary_ids[e][i]); + new_elem.get(), is_flipped ? top_id : 0, _downward_boundary_ids[e][i]); } if (_subdomain_swap_pairs.size()) @@ -1029,9 +999,9 @@ AdvancedExtruderGenerator::generate() const unsigned short top_id = added_elem->dim() == 3 ? cast_int(elem->n_sides() + 1) : 2; if (_has_bottom_boundary) - boundary_info.add_side(added_elem, isFlipped ? top_id : 0, _bottom_boundary); + boundary_info.add_side(added_elem, is_flipped ? top_id : 0, _bottom_boundary); else - boundary_info.add_side(added_elem, isFlipped ? top_id : 0, next_side_id); + boundary_info.add_side(added_elem, is_flipped ? top_id : 0, next_side_id); } if (current_layer == total_num_layers - 1) @@ -1043,10 +1013,10 @@ AdvancedExtruderGenerator::generate() added_elem->dim() == 3 ? cast_int(elem->n_sides() + 1) : 2; if (_has_top_boundary) - boundary_info.add_side(added_elem, isFlipped ? 0 : top_id, _top_boundary); + boundary_info.add_side(added_elem, is_flipped ? 0 : top_id, _top_boundary); else boundary_info.add_side( - added_elem, isFlipped ? 0 : top_id, cast_int(next_side_id + 1)); + added_elem, is_flipped ? 0 : top_id, cast_int(next_side_id + 1)); } current_layer++; @@ -1079,13 +1049,3 @@ AdvancedExtruderGenerator::generate() return mesh; } - -void -AdvancedExtruderGenerator::swapNodesInElem(Elem & elem, - const unsigned int nd1, - const unsigned int nd2) -{ - Node * n_temp = elem.node_ptr(nd1); - elem.set_node(nd1) = elem.node_ptr(nd2); - elem.set_node(nd2) = n_temp; -} diff --git a/framework/src/utils/MooseMeshUtils.C b/framework/src/utils/MooseMeshUtils.C index b43aa1eefbad..3de4f6ea7417 100644 --- a/framework/src/utils/MooseMeshUtils.C +++ b/framework/src/utils/MooseMeshUtils.C @@ -427,7 +427,7 @@ makeOrderedNodeList(std::vector> & node_assm std::vector & ordered_elem_id_list) { // a flag to indicate if the ordered_node_list has been reversed - bool isFlipped = false; + bool is_flipped = false; // Start from the first element, try to find a chain of nodes mooseAssert(node_assm.size(), "Node list must not be empty"); ordered_node_list.push_back(node_assm.front().first); @@ -471,12 +471,12 @@ makeOrderedNodeList(std::vector> & node_assm // been examined yet. else { - if (isFlipped) + if (is_flipped) // Flipped twice; this means the node list has at least two segments. throw MooseException("The node list provided has more than one segments."); // mark the first flip event. - isFlipped = true; + is_flipped = true; std::reverse(ordered_node_list.begin(), ordered_node_list.end()); std::reverse(ordered_elem_id_list.begin(), ordered_elem_id_list.end()); // As this iteration is wasted, set the iterator backward @@ -484,4 +484,44 @@ makeOrderedNodeList(std::vector> & node_assm } } } + +void +swapNodesInElem(Elem & elem, const unsigned int nd1, const unsigned int nd2) +{ + Node * n_temp = elem.node_ptr(nd1); + elem.set_node(nd1) = elem.node_ptr(nd2); + elem.set_node(nd2) = n_temp; +} + +void +extraElemIntegerSwapParametersProcessor( + const std::string & class_name, + const unsigned int num_sections, + const unsigned int num_integers, + const std::vector>> & elem_integers_swaps, + std::vector> & elem_integers_swap_pairs) +{ + elem_integers_swap_pairs.reserve(num_sections * num_integers); + for (const auto i : make_range(num_integers)) + { + const auto & elem_integer_swaps = elem_integers_swaps[i]; + std::vector> elem_integer_swap_pairs; + try + { + MooseMeshUtils::idSwapParametersProcessor(class_name, + "elem_integers_swaps", + elem_integer_swaps, + elem_integer_swap_pairs, + i * num_sections); + } + catch (const MooseException & e) + { + throw MooseException(e.what()); + } + + elem_integers_swap_pairs.insert(elem_integers_swap_pairs.end(), + elem_integer_swap_pairs.begin(), + elem_integer_swap_pairs.end()); + } +} } diff --git a/modules/reactor/doc/content/source/meshgenerators/RevolveGenerator.md b/modules/reactor/doc/content/source/meshgenerators/RevolveGenerator.md new file mode 100644 index 000000000000..0de441460cb0 --- /dev/null +++ b/modules/reactor/doc/content/source/meshgenerators/RevolveGenerator.md @@ -0,0 +1,40 @@ +# RevolveGenerator + +!syntax description /Mesh/RevolveGenerator + +## Overview + +This `RevolveGenerator` provides an alternative tool for increasing the dimensionality of a lower dimension mesh (1D or 2D) in addition to [MeshExtruderGenerator](MeshExtruderGenerator.md)/[AdvancedExtruderGenerator](AdvancedExtruderGenerator.md). Each element is converted +to one or more copies of its corresponding higher dimensional element along an open or closed specific circular curve. + +The `RevolveGenerator` can provides similar customization options as in [AdvancedExtruderGenerator](AdvancedExtruderGenerator.md). + +## Revolving Basics + +`RevolveGenerator` revolves a lower-dimension mesh (1D or 2D) given by [!param](/Mesh/RevolveGenerator/input) into a higher-dimension mesh (2D or 3D) along an revolving axis defined by [!param](/Mesh/RevolveGenerator/axis_point) and [!param](/Mesh/RevolveGenerator/axis_direction). By default, the revolving can be performed along a full closed circular curve (i.e., 360 degrees) with one uniform azimuthal section. Optionally, the revolving curve can be a partial circular curve; and (or) multiple azimuthal sections can be defined. These options can be selected by specifying [!param](/Mesh/RevolveGenerator/revolving_angles). As long as the summation of the angles listed in [!param](/Mesh/RevolveGenerator/revolving_angles) is 360 degrees, a full closed circular revolving is performed. Otherwise, a partial revolving is conducted. For partial revolving or full revolving with multiple azimuthal sections, it can be conducted either clockwise or counter-clockwise, as controlled by [!param](/Mesh/RevolveGenerator/clockwise). + +Each azimuthal sections can have separate subdomains, extra element extra integers, and boundaries defined. The number of azimuthal elements in the different sections can be provided through [!param](/Mesh/RevolveGenerator/nums_azimuthal_intervals). + +## Subdomain ID Remapping + +By default, the revolved higher-dimension elements retain the same subdomain ids as their original lower-dimension elements. `RevolveGenerator` provides an option to remap subdomain ids for each azimuthal section through [!param](/Mesh/RevolveGenerator/subdomain_swaps), which is a double indexed array input parameter. Each elemental vector of [!param](/Mesh/RevolveGenerator/subdomain_swaps) contains subdomain remapping information for a particular elevation, where the first elemental vector represents the first revolved azimuthal section. The elemental vector contain pairs of subdomain ids: the first subdomain id is the `input` mesh subdomain id that needs to be remapped, and the second subdomain id the new subdomain id to be assigned. + +## Extra Element Integer ID Remapping + +Extra element integer ID remapping works in a similar manner as subdomain ID remapping. The extra element integers to be remapped must already exist in the `input` mesh and need to be specified in [!param](/Mesh/RevolveGenerator/elem_integer_names_to_swap). The remapping information of multiple extra element integers is provided as a triple-indexed array input parameter ([!param](/Mesh/RevolveGenerator/elem_integers_swaps)). For each extra element integer, the syntax is similar to [!param](/Mesh/RevolveGenerator/subdomain_swaps). The following input example shows the remapping of two extra element integers. + +!listing modules/reactor/test/tests/meshgenerators/revolve_generator/ei_swap.i block=Mesh/rg + +## Boundary ID Remapping + +Boundary ID remapping also works similarly to subdomain ID remapping. During revolving, the lower-dimension boundaries are also converted into higher-dimension boundaries. A double indexed array input parameter, [!param](/Mesh/RevolveGenerator/boundary_swaps), can be used to remap the boundary ids. Here, the boundary ids to be remapped must exist in the `input` mesh, otherwise, dedicated boundary defining mesh generators, such as [SideSetsBetweenSubdomainsGenerator](SideSetsBetweenSubdomainsGenerator.md) and [SideSetsAroundSubdomainGenerator](SideSetsAroundSubdomainGenerator.md), need to be used to define new boundary ids along different azimuthal sections. + +## Example Syntax + +!listing modules/reactor/test/tests/meshgenerators/revolve_generator/revolve_2d.i block=Mesh/rg + +!syntax parameters /Mesh/RevolveGenerator + +!syntax inputs /Mesh/RevolveGenerator + +!syntax children /Mesh/RevolveGenerator \ No newline at end of file diff --git a/modules/reactor/include/meshgenerators/PolygonMeshGeneratorBase.h b/modules/reactor/include/meshgenerators/PolygonMeshGeneratorBase.h index 8e239ca6a13e..b87a589622b2 100644 --- a/modules/reactor/include/meshgenerators/PolygonMeshGeneratorBase.h +++ b/modules/reactor/include/meshgenerators/PolygonMeshGeneratorBase.h @@ -506,10 +506,12 @@ class PolygonMeshGeneratorBase : public MeshGenerator /** * Makes radial correction to preserve ring area. * @param azimuthal_list azimuthal angles (in degrees) of all the nodes on the circle + * @param full_circle whether the circle is a full or partial circle * @return a correction factor to preserve the area of the circle after polygonization during * meshing */ - Real radiusCorrectionFactor(const std::vector & azimuthal_list) const; + Real radiusCorrectionFactor(const std::vector & azimuthal_list, + const bool full_circle = true) const; /** * Creates peripheral area mesh for the patterned hexagon mesh. Note that the function create the diff --git a/modules/reactor/include/meshgenerators/RevolveGenerator.h b/modules/reactor/include/meshgenerators/RevolveGenerator.h new file mode 100644 index 000000000000..77fbfbaeb853 --- /dev/null +++ b/modules/reactor/include/meshgenerators/RevolveGenerator.h @@ -0,0 +1,360 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#pragma once +#include "PolygonMeshGeneratorBase.h" + +/** + * This RevolveGenerator object is designed to revolve a 1D mesh into 2D, or a 2D mesh into 3D based + * on an axis. + */ +class RevolveGenerator : public PolygonMeshGeneratorBase +{ +public: + static InputParameters validParams(); + + RevolveGenerator(const InputParameters & parameters); + + std::unique_ptr generate() override; + +protected: + /// Lower dimensional mesh from another generator + std::unique_ptr & _input; + + /// A point of the axis of revolution + const Point & _axis_point; + + /// A direction vector of the axis of revolution + const Point & _axis_direction; + + /// Angles of revolution delineating each azimuthal section + const std::vector _revolving_angles; + + /// Subdomains to swap out for each azimuthal section + const std::vector> & _subdomain_swaps; + + /// Boundaries to swap out for each elevation + const std::vector> & _boundary_swaps; + + /// Names and indices of extra element integers to swap + const std::vector & _elem_integer_names_to_swap; + std::vector _elem_integer_indices_to_swap; + + /// Extra element integers to swap out for each elevation and each element integer name + const std::vector>> & _elem_integers_swaps; + + /// Revolving direction + const bool & _clockwise; + + /// Numbers of azimuthal mesh intervals in each azimuthal section + const std::vector & _nums_azimuthal_intervals; + + /// Volume preserving function is optional + const bool _preserve_volumes; + + /// Whether a starting boundary is specified + bool _has_start_boundary; + + /// Boundary ID of the starting boundary + boundary_id_type _start_boundary; + + /// Whether an ending boundary is specified + bool _has_end_boundary; + + /// Easier to work with version of _sudomain_swaps + std::vector> _subdomain_swap_pairs; + + /// Easier to work with version of _boundary_swaps + std::vector> _boundary_swap_pairs; + + /// Easier to work with version of _elem_integers_swaps + std::vector> _elem_integers_swap_pairs; + + /// Whether to revolve for a full circle or not + bool _full_circle_revolving; + + /// Unit angles of all azimuthal sections of revolution + std::vector _unit_angles; + + /// Boundary ID of the ending boundary + boundary_id_type _end_boundary; + + /// Radius correction factor + Real _radius_correction_factor; + + /** + * Get the rotation center and radius of the circular rotation based on the rotation axis and the + * external point. + * @param p_ext external point that needs to be rotated + * @param p_axis a point on the rotation axis + * @param dir_axis direction vector of the rotation axis + * @return a pair of the rotation center and the radius of the circular rotation + */ + std::pair getRotationCenterAndRadius(const Point & p_ext, + const Point & p_axis, + const Point & dir_axis) const; + + /** + * Calculate the transform matrix between the rotation coordinate system and the original + * coordinate system. + * @param p_axis a point on the rotation axis + * @param dir_axis direction vector of the rotation axis + * @param p_input a point in the input mesh + * @return a transform matrix, stored as 3 points in a vector (each point represents a row of the + * matrix) + */ + std::vector + rotationVectors(const Point & p_axis, const Point & dir_axis, const Point & p_input) const; + + /** + * Categorize the nodes of an element into two groups: nodes on the axis and nodes off the axis. + * @param elem the element whose nodes are to be categorized + * @param nodes_on_axis a list of node IDs on the axis + * @return a pair of lists of node IDs: the first list is for nodes on the axis, and the + * second list is for nodes off the axis + */ + std::pair, std::vector> + onAxisNodesIdentifier(const Elem & elem, const std::vector & nodes_on_axis) const; + + /** + * Modify the position of a node to account for radius correction. + * @param node the node to be modified + */ + void nodeModification(Node & node); + + /** + * Create a new QUAD element from an existing EDGE element by revolving it. + * @param quad_elem_type the type of the new QUAD element + * @param elem the EDGE element to be revolved + * @param mesh the mesh that the new QUAD element will be added to + * @param new_elem the new QUAD element + * @param current_layer the current azimuthal layer + * @param orig_nodes the number of nodes in the original mesh + * @param total_num_azimuthal_intervals the total number of azimuthal intervals for revolving + * @param side_pairs a vector of pairs to record the corresponding side indices of the original + * and the new elements + * @param is_flipped a flag to indicate whether the new element is flipped after creation (to + * ensure a positive element volume) + */ + void createQUADfromEDGE(const ElemType quad_elem_type, + const Elem * elem, + const std::unique_ptr & mesh, + std::unique_ptr & new_elem, + const int current_layer, + const unsigned int orig_nodes, + const unsigned int total_num_azimuthal_intervals, + std::vector> & side_pairs, + bool & is_flipped) const; + + /** + * Create a new TRI element from an existing EDGE element by revolving it. + * @param nodes_cates a pair of two lists of node IDs: the first list is for nodes on the axis, + * and the second list is for nodes off the axis + * @param tri_elem_type the type of the new TRI element + * @param elem the EDGE element to be revolved + * @param mesh the mesh that the new TRI element will be added to + * @param new_elem the new TRI element + * @param current_layer the current layer of the revolving + * @param orig_nodes the number of nodes in the original mesh + * @param total_num_azimuthal_intervals the total number of azimuthal intervals for revolving + * @param side_pairs a vector of pairs to record the corresponding side indices of the original + * and the new elements + * @param axis_node_case a parameter to record on-axis node(s) + * @param is_flipped a flag to indicate whether the new element is flipped after creation (to + * ensure a positive element volume) + */ + void createTRIfromEDGE( + const std::pair, std::vector> & nodes_cates, + const ElemType tri_elem_type, + const Elem * elem, + const std::unique_ptr & mesh, + std::unique_ptr & new_elem, + const int current_layer, + const unsigned int orig_nodes, + const unsigned int total_num_azimuthal_intervals, + std::vector> & side_pairs, + dof_id_type & axis_node_case, + bool & is_flipped) const; + + /** + * Create a new PRISM element from an existing TRI element by revolving it. + * @param prism_elem_type the type of the new TET element + * @param elem the TRI element to be revolved + * @param mesh the mesh that the new TET element will be added to + * @param new_elem the new TET element + * @param current_layer the current layer of the revolving + * @param orig_nodes the number of nodes in the original mesh + * @param total_num_azimuthal_intervals the total number of azimuthal intervals for revolving + * @param side_pairs a vector of pairs to record the corresponding side indices of the original + * and the new elements + * @param is_flipped a flag to indicate whether the new element is flipped after creation (to + * ensure a positive element volume) + */ + void createPRISMfromTRI(const ElemType prism_elem_type, + const Elem * elem, + const std::unique_ptr & mesh, + std::unique_ptr & new_elem, + const int current_layer, + const unsigned int orig_nodes, + const unsigned int total_num_azimuthal_intervals, + std::vector> & side_pairs, + bool & is_flipped) const; + + /** + * Create a new PYRAMID element from an existing TRI element by revolving it. + * @param nodes_cates a pair of two lists of node IDs: the first list is for nodes on the axis, + * and the second list is for nodes off the axis + * @param pyramid_elem_type the type of the new PYRAMID element + * @param elem the TRI element to be revolved + * @param mesh the mesh that the new PYRAMID element will be added to + * @param new_elem the new PYRAMID element + * @param current_layer the current layer of the revolving + * @param orig_nodes the number of nodes in the original mesh + * @param total_num_azimuthal_intervals the total number of azimuthal intervals for revolving + * @param side_pairs a vector of pairs to record the corresponding side indices of the original + * and the new elements + * @param axis_node_case a parameter to record on-axis node(s) + * @param is_flipped a flag to indicate whether the new element is flipped after creation (to + * ensure a positive element volume) + */ + void createPYRAMIDfromTRI( + const std::pair, std::vector> & nodes_cates, + const ElemType pyramid_elem_type, + const Elem * elem, + const std::unique_ptr & mesh, + std::unique_ptr & new_elem, + const int current_layer, + const unsigned int orig_nodes, + const unsigned int total_num_azimuthal_intervals, + std::vector> & 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. + * @param nodes_cates a pair of two lists of node IDs: the first list is for nodes on the axis, + * and the second list is for nodes off the axis + * @param tet_elem_type the type of the new TET element + * @param elem the TRI element to be revolved + * @param mesh the mesh that the new TET element will be added to + * @param new_elem the new TET element + * @param current_layer the current layer of the revolving + * @param orig_nodes the number of nodes in the original mesh + * @param total_num_azimuthal_intervals the total number of azimuthal intervals for revolving + * @param side_pairs a vector of pairs to record the corresponding side indices of the original + * and the new elements + * @param axis_node_case a parameter to record on-axis node(s) + * @param is_flipped a flag to indicate whether the new element is flipped after creation (to + * ensure a positive element volume) + */ + void createTETfromTRI( + const std::pair, std::vector> & nodes_cates, + const ElemType tet_elem_type, + const Elem * elem, + const std::unique_ptr & mesh, + std::unique_ptr & new_elem, + const int current_layer, + const unsigned int orig_nodes, + const unsigned int total_num_azimuthal_intervals, + std::vector> & side_pairs, + dof_id_type & axis_node_case, + bool & is_flipped) const; + + /** + * Create a new HEX element from an existing QUAD element by revolving it. + * @param hex_elem_type the type of the new HEX element + * @param elem the QUAD element to be revolved + * @param mesh the mesh that the new HEX element will be added to + * @param new_elem the new HEX element + * @param current_layer the current layer of the revolving + * @param orig_nodes the number of nodes in the original mesh + * @param total_num_azimuthal_intervals the total number of azimuthal intervals for revolving + * @param side_pairs a vector of pairs to record the corresponding side indices of the original + * and the new elements + * @param is_flipped a flag to indicate whether the new element is flipped after creation (to + * ensure a positive element volume) + */ + void createHEXfromQUAD(const ElemType hex_elem_type, + const Elem * elem, + const std::unique_ptr & mesh, + std::unique_ptr & new_elem, + const int current_layer, + const unsigned int orig_nodes, + const unsigned int total_num_azimuthal_intervals, + std::vector> & side_pairs, + bool & is_flipped) const; + + /** + * Create a new PRISM element from an existing QUAD element by revolving it. + * @param nodes_cates a pair of two lists of node IDs: the first list is for nodes on the axis, + * and the second list is for nodes off the axis + * @param prism_elem_type the type of the new PRISM element + * @param elem the QUAD element to be revolved + * @param mesh the mesh that the new PRISM element will be added to + * @param new_elem the new PRISM element + * @param current_layer the current layer of the revolving + * @param orig_nodes the number of nodes in the original mesh + * @param total_num_azimuthal_intervals the total number of azimuthal intervals for revolving + * @param side_pairs a vector of pairs to record the corresponding side indices of the original + * and the new elements + * @param axis_node_case a parameter to record on-axis node(s) + * @param is_flipped a flag to indicate whether the new element is flipped after creation (to + * ensure a positive element volume) + */ + void createPRISMfromQUAD( + const std::pair, std::vector> & nodes_cates, + const ElemType prism_elem_type, + const Elem * elem, + const std::unique_ptr & mesh, + std::unique_ptr & new_elem, + const int current_layer, + const unsigned int orig_nodes, + const unsigned int total_num_azimuthal_intervals, + std::vector> & side_pairs, + dof_id_type & axis_node_case, + bool & is_flipped) const; + + /** + * Create a new PYRAMID element and a new PRISM element from an existing QUAD element by revolving + * it. + * @param nodes_cates a pair of two lists of node IDs: the first list is for nodes on the axis, + * and the second list is for nodes off the axis + * @param pyramid_elem_type the type of the new PYRAMID element + * @param prism_elem_type the type of the new PRISM element + * @param elem the QUAD element to be revolved + * @param mesh the mesh that the new PYRAMID element will be added to + * @param new_elem the new PYRAMID element + * @param new_elem_1 the new PRISM element + * @param current_layer the current layer of the revolving + * @param orig_nodes the number of nodes in the original mesh + * @param total_num_azimuthal_intervals the total number of azimuthal intervals for revolving + * @param side_pairs a vector of pairs to record the corresponding side indices of the original + * and the new elements + * @param axis_node_case a parameter to record on-axis node(s) + * @param is_flipped a flag to indicate whether the PYRAMID element is flipped after creation (to + * ensure a positive element volume) + * @param is_flipped_additional a flag to indicate whether the PRISM element is flipped after (to + * ensure a positive element volume) creation + */ + void createPYRAMIDPRISMfromQUAD( + const std::pair, std::vector> & nodes_cates, + const ElemType pyramid_elem_type, + const ElemType prism_elem_type, + const Elem * elem, + const std::unique_ptr & mesh, + std::unique_ptr & new_elem, + std::unique_ptr & new_elem_1, + const int current_layer, + const unsigned int orig_nodes, + const unsigned int total_num_azimuthal_intervals, + std::vector> & side_pairs, + dof_id_type & axis_node_case, + bool & is_flipped, + bool & is_flipped_additional) const; +}; diff --git a/modules/reactor/src/meshgenerators/PolygonMeshGeneratorBase.C b/modules/reactor/src/meshgenerators/PolygonMeshGeneratorBase.C index 3fa8d92e4075..17afcfb3f0d1 100644 --- a/modules/reactor/src/meshgenerators/PolygonMeshGeneratorBase.C +++ b/modules/reactor/src/meshgenerators/PolygonMeshGeneratorBase.C @@ -1137,18 +1137,24 @@ PolygonMeshGeneratorBase::quadElemDef(ReplicatedMesh & mesh, } Real -PolygonMeshGeneratorBase::radiusCorrectionFactor(const std::vector & azimuthal_list) const +PolygonMeshGeneratorBase::radiusCorrectionFactor(const std::vector & azimuthal_list, + const bool full_circle) const { std::vector azimuthal_list_alt; Real tmp_acc = 0.0; + Real tmp_acc_azi = 0.0; azimuthal_list_alt.insert(azimuthal_list_alt.end(), azimuthal_list.begin(), azimuthal_list.end()); - azimuthal_list_alt.push_back(azimuthal_list.front() + 360.0); + if (full_circle) + azimuthal_list_alt.push_back(azimuthal_list.front() + 360.0); // summation of triangles S = 0.5 * r * r * Sigma_i [sin (azi_i)] // Circle area S_c = pi * r_0 * r_0 // r = sqrt{2 * pi / Sigma_i [sin (azi_i)]} * r_0 for (unsigned int i = 1; i < azimuthal_list_alt.size(); i++) + { tmp_acc += std::sin((azimuthal_list_alt[i] - azimuthal_list_alt[i - 1]) / 180.0 * M_PI); - return std::sqrt(2 * M_PI / tmp_acc); + tmp_acc_azi += (azimuthal_list_alt[i] - azimuthal_list_alt[i - 1]) / 180.0 * M_PI; + } + return std::sqrt(tmp_acc_azi / tmp_acc); } std::unique_ptr diff --git a/modules/reactor/src/meshgenerators/RevolveGenerator.C b/modules/reactor/src/meshgenerators/RevolveGenerator.C new file mode 100644 index 000000000000..66c684b42e00 --- /dev/null +++ b/modules/reactor/src/meshgenerators/RevolveGenerator.C @@ -0,0 +1,2385 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#include "RevolveGenerator.h" + +#include "libmesh/cell_prism6.h" +#include "libmesh/cell_prism15.h" +#include "libmesh/cell_prism18.h" +#include "libmesh/cell_prism21.h" +#include "libmesh/cell_pyramid5.h" +#include "libmesh/cell_pyramid13.h" +#include "libmesh/cell_pyramid14.h" +#include "libmesh/cell_pyramid18.h" +#include "libmesh/cell_tet4.h" +#include "libmesh/cell_tet10.h" +#include "libmesh/cell_tet14.h" +#include "libmesh/cell_hex8.h" +#include "libmesh/cell_hex20.h" +#include "libmesh/cell_hex27.h" +#include "libmesh/face_tri3.h" +#include "libmesh/face_tri7.h" +#include "libmesh/face_quad4.h" +#include "libmesh/face_quad9.h" +#include "libmesh/point.h" +#include "libmesh/mesh_tools.h" + +// C++ includes +#include + +registerMooseObject("ReactorApp", RevolveGenerator); + +InputParameters +RevolveGenerator::validParams() +{ + InputParameters params = PolygonMeshGeneratorBase::validParams(); + params.addClassDescription("This RevolveGenerator object is designed to revolve a 1D mesh into " + "2D, or a 2D mesh into 3D based on an axis."); + + params.addRequiredParam("input", "The mesh to revolve"); + + params.addRequiredParam("axis_point", "A point on the axis of revolution"); + + params.addRequiredParam("axis_direction", "The direction of the axis of revolution"); + + params.addRangeCheckedParam>( + "revolving_angles", + "revolving_angles<=360.0 & revolving_angles>0.0", + "The angles delineating each azimuthal section of revolution around the axis in degrees"); + + params.addParam>>( + "subdomain_swaps", + {}, + "For each row, every two entries are interpreted as a pair of " + "'from' and 'to' to remap the subdomains for that azimuthal section"); + + params.addParam>>( + "boundary_swaps", + {}, + "For each row, every two entries are interpreted as a pair of " + "'from' and 'to' to remap the boundaries for that elevation"); + + params.addParam>( + "elem_integer_names_to_swap", + {}, + "Array of element extra integer names that need to be swapped during revolving."); + + params.addParam>>>( + "elem_integers_swaps", + {}, + "For each row, every two entries are interpreted as a pair of 'from' and 'to' to remap the " + "element extra integer for that elevation. If multiple element extra integers need to be " + "swapped, the enties are stacked based on the order provided in " + "'elem_integer_names_to_swap' to form the third dimension."); + + params.addParam( + "start_boundary", + "The boundary ID to set on the starting boundary for a partial revolution."); + + params.addParam( + "end_boundary", "The boundary ID to set on the ending boundary for partial revolving."); + + params.addParam( + "clockwise", true, "Revolve clockwise around the axis or not (i.e., counterclockwise)"); + + params.addRequiredParam>( + "nums_azimuthal_intervals", + "List of the numbers of azimuthal interval discretization for each azimuthal section"); + + params.addParam("preserve_volumes", + false, + "Whether the volume of the revolved mesh is preserving the circular area " + "by modifying (expanding) the radius to account for polygonization."); + + params.addParamNamesToGroup("start_boundary end_boundary", "Boundary Assignment"); + params.addParamNamesToGroup( + "subdomain_swaps boundary_swaps elem_integer_names_to_swap elem_integers_swaps", "ID Swap"); + + return params; +} + +RevolveGenerator::RevolveGenerator(const InputParameters & parameters) + : PolygonMeshGeneratorBase(parameters), + _input(getMesh("input")), + _axis_point(getParam("axis_point")), + _axis_direction(getParam("axis_direction")), + _revolving_angles(isParamValid("revolving_angles") + ? getParam>("revolving_angles") + : std::vector(1, 360.0)), + _subdomain_swaps(getParam>>("subdomain_swaps")), + _boundary_swaps(getParam>>("boundary_swaps")), + _elem_integer_names_to_swap(getParam>("elem_integer_names_to_swap")), + _elem_integers_swaps( + getParam>>>("elem_integers_swaps")), + _clockwise(getParam("clockwise")), + _nums_azimuthal_intervals(getParam>("nums_azimuthal_intervals")), + _preserve_volumes(getParam("preserve_volumes")), + _has_start_boundary(isParamValid("start_boundary")), + _start_boundary(isParamValid("start_boundary") ? getParam("start_boundary") + : 0), + _has_end_boundary(isParamValid("end_boundary")), + _end_boundary(isParamValid("end_boundary") ? getParam("end_boundary") : 0), + _radius_correction_factor(1.0) +{ + if (_revolving_angles.size() != _nums_azimuthal_intervals.size()) + paramError("nums_azimuthal_intervals", + "The number of azimuthal intervals should be the same as the number of revolving " + "angles."); + if (_subdomain_swaps.size() && (_subdomain_swaps.size() != _nums_azimuthal_intervals.size())) + paramError( + "subdomain_swaps", + "If specified, 'subdomain_swaps' must be the same length as 'nums_azimuthal_intervals'."); + + if (_boundary_swaps.size() && (_boundary_swaps.size() != _nums_azimuthal_intervals.size())) + paramError( + "boundary_swaps", + "If specified, 'boundary_swaps' must be the same length as 'nums_azimuthal_intervals'."); + + for (const auto & unit_elem_integers_swaps : _elem_integers_swaps) + if (unit_elem_integers_swaps.size() != _nums_azimuthal_intervals.size()) + paramError("elem_integers_swaps", + "If specified, each element of 'elem_integers_swaps' must have the same length as " + "the length of 'nums_azimuthal_intervals'."); + + if (_elem_integers_swaps.size() && + _elem_integers_swaps.size() != _elem_integer_names_to_swap.size()) + paramError("elem_integers_swaps", + "If specified, 'elem_integers_swaps' must have the same length as the length of " + "'elem_integer_names_to_swap'."); + + _full_circle_revolving = + MooseUtils::absoluteFuzzyEqual( + std::accumulate(_revolving_angles.begin(), _revolving_angles.end(), 0), 360.0) + ? true + : false; + if (MooseUtils::absoluteFuzzyGreaterThan( + std::accumulate(_revolving_angles.begin(), _revolving_angles.end(), 0), 360.0)) + paramError("revolving_angles", + "The sum of revolving angles should be less than or equal to 360."); + + if ((_has_start_boundary && _full_circle_revolving) || + (_has_end_boundary && _full_circle_revolving)) + paramError("full_circle_revolving", + "starting or ending boundaries can only be assigned for partial revolving."); + + try + { + MooseMeshUtils::idSwapParametersProcessor( + name(), "subdomain_swaps", _subdomain_swaps, _subdomain_swap_pairs); + } + catch (const MooseException & e) + { + paramError("subdomain_swaps", e.what()); + } + + try + { + MooseMeshUtils::idSwapParametersProcessor( + name(), "boundary_swaps", _boundary_swaps, _boundary_swap_pairs); + } + catch (const MooseException & e) + { + paramError("boundary_swaps", e.what()); + } + + try + { + MooseMeshUtils::extraElemIntegerSwapParametersProcessor(name(), + _nums_azimuthal_intervals.size(), + _elem_integer_names_to_swap.size(), + _elem_integers_swaps, + _elem_integers_swap_pairs); + } + catch (const MooseException & e) + { + paramError("elem_integers_swaps", e.what()); + } +} + +std::unique_ptr +RevolveGenerator::generate() +{ + // Note: Inspired by AdvancedExtruderGenerator::generate() + + auto mesh = buildMeshBaseObject(); + + // Only works for 1D and 2D input meshes + if (_input->mesh_dimension() > 2) + paramError("input", "This mesh generator only works for 1D and 2D input meshes."); + + mesh->set_mesh_dimension(_input->mesh_dimension() + 1); + + // Check if the element integer names are existent in the input mesh. + for (const auto i : index_range(_elem_integer_names_to_swap)) + if (_input->has_elem_integer(_elem_integer_names_to_swap[i])) + _elem_integer_indices_to_swap.push_back( + _input->get_elem_integer_index(_elem_integer_names_to_swap[i])); + else + paramError("elem_integer_names_to_swap", + "Element ", + i + 1, + " of 'elem_integer_names_to_swap' is not a valid extra element integer of the " + "input mesh."); + + // prepare for transferring extra element integers from original mesh to the revolved mesh. + const unsigned int num_extra_elem_integers = _input->n_elem_integers(); + std::vector id_names; + + for (const auto i : make_range(num_extra_elem_integers)) + { + id_names.push_back(_input->get_elem_integer_name(i)); + if (!mesh->has_elem_integer(id_names[i])) + mesh->add_elem_integer(id_names[i]); + } + + // retrieve subdomain/sideset/nodeset name maps + const auto & input_subdomain_map = _input->get_subdomain_name_map(); + const auto & input_sideset_map = _input->get_boundary_info().get_sideset_name_map(); + const auto & input_nodeset_map = _input->get_boundary_info().get_nodeset_name_map(); + + std::unique_ptr input = std::move(_input); + + // If we're using a distributed mesh... then make sure we don't have any remote elements hanging + // around + if (!input->is_serial()) + mesh->delete_remote_elements(); + + // Subdomain IDs for on-axis elements must be new + std::set subdomain_ids_set; + input->subdomain_ids(subdomain_ids_set); + const subdomain_id_type max_subdomain_id = *subdomain_ids_set.rbegin(); + const subdomain_id_type tri_to_pyramid_subdomain_id_shift = + std::max((int)max_subdomain_id, 1) + 1; + const subdomain_id_type tri_to_tet_subdomain_id_shift = + std::max((int)max_subdomain_id, 1) * 2 + 1; + const subdomain_id_type quad_to_prism_subdomain_id_shift = std::max((int)max_subdomain_id, 1) + 1; + const subdomain_id_type quad_to_pyramid_subdomain_id_shift = + std::max((int)max_subdomain_id, 1) * 2 + 1; + const subdomain_id_type quad_to_hi_pyramid_subdomain_id_shift = + std::max((int)max_subdomain_id, 1) * 3 + 1; + const subdomain_id_type edge_to_tri_subdomain_id_shift = std::max((int)max_subdomain_id, 1) + 1; + + // Get the centroid of the input mesh + const auto input_centroid = MooseMeshUtils::meshCentroidCalculator(*input); + const Point axis_centroid_cross = (input_centroid - _axis_point).cross(_axis_direction); + + if (MooseUtils::absoluteFuzzyEqual(axis_centroid_cross.norm(), 0.0)) + { + mooseError("The input mesh is either across the axis or overlapped with the axis!"); + } + + Real inner_product_1d(0.0); + bool inner_product_1d_initialized(false); + // record ids of nodes on the axis + std::vector node_ids_on_axis; + for (const auto & node : input->node_ptr_range()) + { + const Point axis_node_cross = (*node - _axis_point).cross(_axis_direction); + // if the cross product is zero, then the node is on the axis + if (!MooseUtils::absoluteFuzzyEqual(axis_node_cross.norm(), 0.0)) + { + if (MooseUtils::absoluteFuzzyLessThan(axis_node_cross * axis_centroid_cross, 0.0)) + { + mooseError("The input mesh is across the axis."); + } + else if (MooseUtils::absoluteFuzzyLessThan(axis_node_cross * axis_centroid_cross, + axis_centroid_cross.norm() * + axis_node_cross.norm())) + { + mooseError("The input mesh is not in the same plane with the rotation axis."); + } + } + else + { + node_ids_on_axis.push_back(node->id()); + } + + // Only for 1D input mesh, we need to check if the axis is perpendicular to the input mesh + if (input->mesh_dimension() == 1) + { + const Real temp_inner_product = (*node - _axis_point) * _axis_direction.unit(); + if (inner_product_1d_initialized) + { + if (!MooseUtils::absoluteFuzzyEqual(temp_inner_product, inner_product_1d)) + { + mooseError("The 1D input mesh is not perpendicular to the rotation axis."); + } + } + else + { + inner_product_1d_initialized = true; + inner_product_1d = temp_inner_product; + } + } + } + + // If there are any on-axis nodes, we need to check if there are any QUAD8 elements with one + // vertex on the axis. If so, we need to replace it with a QUAD9 element. + if (!node_ids_on_axis.empty()) + { + // Sort the vector for using set_intersection + std::sort(node_ids_on_axis.begin(), node_ids_on_axis.end()); + // For QUAD8 elements with one vertex on the axis, we need to replace it with a QUAD9 element + std::set converted_quad8_subdomain_ids; + for (const auto & elem : input->element_ptr_range()) + { + if (elem->type() == QUAD8) + { + std::vector elem_vertex_node_ids; + for (unsigned int i = 0; i < 4; i++) + { + elem_vertex_node_ids.push_back(elem->node_id(i)); + } + std::sort(elem_vertex_node_ids.begin(), elem_vertex_node_ids.end()); + std::vector common_node_ids; + std::set_intersection(node_ids_on_axis.begin(), + node_ids_on_axis.end(), + elem_vertex_node_ids.begin(), + elem_vertex_node_ids.end(), + std::back_inserter(common_node_ids)); + // Temporarily shift the subdomain ID to mark the element + if (common_node_ids.size() == 1) + { + // we borrow quad_to_hi_pyramid_subdomain_id_shift here + elem->subdomain_id() += quad_to_hi_pyramid_subdomain_id_shift; + converted_quad8_subdomain_ids.emplace(elem->subdomain_id()); + } + } + } + // Convert the recorded subdomains + input->all_second_order_range( + input->active_subdomain_set_elements_ptr_range(converted_quad8_subdomain_ids)); + // Restore the subdomain ID; we do not worry about repeated subdomain IDs because those QUAD9 + // will become PYRAMID and PRISM elements with new shifts + for (auto elem : input->active_subdomain_set_elements_ptr_range(converted_quad8_subdomain_ids)) + elem->subdomain_id() -= quad_to_hi_pyramid_subdomain_id_shift; + } + + // We should only record this info after QUAD8->QUAD9 conversion + dof_id_type orig_elem = input->n_elem(); + dof_id_type orig_nodes = input->n_nodes(); + +#ifdef LIBMESH_ENABLE_UNIQUE_ID + unique_id_type orig_unique_ids = input->parallel_max_unique_id(); +#endif + + // get rotation vectors + const auto rotation_vectors = rotationVectors(_axis_point, _axis_direction, input_centroid); + + unsigned int order = 1; + + BoundaryInfo & boundary_info = mesh->get_boundary_info(); + const BoundaryInfo & input_boundary_info = input->get_boundary_info(); + + const unsigned int total_num_azimuthal_intervals = + std::accumulate(_nums_azimuthal_intervals.begin(), _nums_azimuthal_intervals.end(), 0); + // We know a priori how many elements we'll need + // In the worst case, all quad elements will become two elements per layer + mesh->reserve_elem(total_num_azimuthal_intervals * orig_elem * 2); + const dof_id_type elem_id_shift = total_num_azimuthal_intervals * orig_elem; + + // Look for higher order elements which introduce an extra layer + std::set higher_orders = {EDGE3, TRI6, TRI7, QUAD8, QUAD9}; + std::vector types; + MeshTools::elem_types(*input, types); + for (const auto elem_type : types) + if (higher_orders.count(elem_type)) + order = 2; + mesh->comm().max(order); + +#ifdef LIBMESH_ENABLE_UNIQUE_ID + const unique_id_type elem_unique_id_shift = + orig_unique_ids + total_num_azimuthal_intervals * order * (orig_elem + orig_nodes); +#endif + + // Collect azimuthal angles and use them to calculate the correction factor if applicable + std::vector azi_array; + for (const auto & i : index_range(_revolving_angles)) + { + const Real section_start_angle = + azi_array.empty() ? 0.0 : (azi_array.back() + _unit_angles.back()); + _unit_angles.push_back(_revolving_angles[i] / _nums_azimuthal_intervals[i] / order); + for (unsigned int j = 0; j < _nums_azimuthal_intervals[i] * order; j++) + azi_array.push_back(section_start_angle + _unit_angles.back() * (Real)j); + } + if (_preserve_volumes) + { + _radius_correction_factor = radiusCorrectionFactor(azi_array, _full_circle_revolving); + + // In the meanwhile, modify the input mesh for radius correction if applicable + for (const auto & node : input->node_ptr_range()) + nodeModification(*node); + } + + mesh->reserve_nodes( + (order * total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + orig_nodes); + + // Container to catch the boundary IDs handed back by the BoundaryInfo object + std::vector ids_to_copy; + + Point old_distance; + Point current_distance; + if (!_clockwise) + std::transform(_unit_angles.begin(), + _unit_angles.end(), + _unit_angles.begin(), + [](auto & c) { return c * (-1.0) * M_PI / 180.0; }); + else + std::transform(_unit_angles.begin(), + _unit_angles.end(), + _unit_angles.begin(), + [](auto & c) { return c * M_PI / 180.0; }); + std::vector nodes_on_axis; + + for (const auto & node : input->node_ptr_range()) + { + // Calculate the radius and corresponding center point on the rotation axis + // If the radius is 0, then the node is on the axis + const auto radius_and_center = getRotationCenterAndRadius(*node, _axis_point, _axis_direction); + const bool isOnAxis = MooseUtils::absoluteFuzzyEqual(radius_and_center.first, 0.0); + if (isOnAxis) + { + nodes_on_axis.push_back(node->id()); + } + + unsigned int current_node_layer = 0; + + old_distance.zero(); + + const unsigned int num_rotations = _revolving_angles.size(); + for (unsigned int e = 0; e < num_rotations; e++) + { + auto num_layers = _nums_azimuthal_intervals[e]; + + auto angle = _unit_angles[e]; + + const auto base_angle = + std::accumulate(_revolving_angles.begin(), _revolving_angles.begin() + e, 0.0) / 180.0 * + M_PI; + + for (unsigned int k = 0; + k < order * num_layers + (e == 0 ? 1 : 0) - + (e == num_rotations - 1 ? (unsigned int)_full_circle_revolving : 0); + ++k) + { + bool is_node_created(false); + if (!isOnAxis) + { + // For the first layer we don't need to move + if (e == 0 && k == 0) + current_distance.zero(); + else + { + auto layer_index = (k - (e == 0 ? 1 : 0)) + 1; + + // Calculate the rotation angle in XY Plane + const Point vector_xy = + Point(-2.0 * radius_and_center.first * + std::sin((base_angle + angle * (Real)layer_index) / 2.0) * + std::sin((base_angle + angle * (Real)layer_index) / 2.0), + 2.0 * radius_and_center.first * + std::sin((base_angle + angle * (Real)layer_index) / 2.0) * + std::cos((base_angle + angle * (Real)layer_index) / 2.0), + 0.0); + current_distance = Point(rotation_vectors[0] * vector_xy, + rotation_vectors[1] * vector_xy, + rotation_vectors[2] * vector_xy); + } + + is_node_created = true; + } + else if (e == 0 && k == 0) + { + // On-axis nodes are only added once + current_distance.zero(); + is_node_created = true; + } + + if (is_node_created) + { + Node * new_node = mesh->add_point(*node + current_distance, + node->id() + (current_node_layer * orig_nodes), + node->processor_id()); +#ifdef LIBMESH_ENABLE_UNIQUE_ID + // Let's give the base nodes of the revolved mesh the same + // unique_ids as the source mesh, in case anyone finds that + // a useful map to preserve. + const unique_id_type uid = (current_node_layer == 0) + ? node->unique_id() + : orig_unique_ids + + (current_node_layer - 1) * (orig_nodes + orig_elem) + + node->id(); + + new_node->set_unique_id(uid); +#endif + + input_boundary_info.boundary_ids(node, ids_to_copy); + if (_boundary_swap_pairs.empty()) + boundary_info.add_node(new_node, ids_to_copy); + else + for (const auto & id_to_copy : ids_to_copy) + boundary_info.add_node(new_node, + _boundary_swap_pairs[e].count(id_to_copy) + ? _boundary_swap_pairs[e][id_to_copy] + : id_to_copy); + } + + current_node_layer++; + } + } + } + + for (const auto & elem : input->element_ptr_range()) + { + const ElemType etype = elem->type(); + + // revolving currently only works on coarse meshes + mooseAssert(!elem->parent(), "RevolveGenerator only works on coarse meshes."); + + unsigned int current_layer = 0; + + const unsigned int num_rotations = _revolving_angles.size(); + + for (unsigned int e = 0; e < num_rotations; e++) + { + auto num_layers = _nums_azimuthal_intervals[e]; + + for (unsigned int k = 0; k < num_layers; ++k) + { + std::unique_ptr new_elem; + std::unique_ptr new_elem_1; + bool is_flipped(false); + // In some cases, two elements per layer are generated by revolving one element. So we + // reserve an additional flag for the potential second element. + bool is_flipped_additional(false); + dof_id_type axis_node_case(-1); + std::vector> side_pairs; + switch (etype) + { + case EDGE2: + { + // Possible scenarios: + // 1. None of the nodes are on the axis + // Then a quad4 element is created + // 2. One of the nodes is on the axis + // Then a tri3 element is created + const auto nodes_cates = onAxisNodesIdentifier(*elem, nodes_on_axis); + if (nodes_cates.first.empty()) + { + createQUADfromEDGE(QUAD4, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + is_flipped); + } + else + { + createTRIfromEDGE(nodes_cates, + TRI3, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + axis_node_case, + is_flipped); + } + break; + } + case EDGE3: + { + // Possible scenarios: + // 1. None of the nodes are on the axis + // Then a QUAD9 element is created + // 2. One of the nodes is on the axis + // Then a TRI7 element is created + const auto nodes_cates = onAxisNodesIdentifier(*elem, nodes_on_axis); + if (nodes_cates.first.empty()) + { + createQUADfromEDGE(QUAD9, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + is_flipped); + } + else + { + createTRIfromEDGE(nodes_cates, + TRI7, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + axis_node_case, + is_flipped); + } + break; + } + case TRI3: + { + // Possible scenarios: + // 1. None of the nodes are on the axis + // Then a prism6 element is created + // 2. One of the nodes is on the axis + // Then a pyramid5 element is created + // 3. Two of the nodes are on the axis + // Then a tet4 element is created + const auto nodes_cates = onAxisNodesIdentifier(*elem, nodes_on_axis); + if (nodes_cates.first.empty()) + { + createPRISMfromTRI(PRISM6, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + is_flipped); + } + else if (nodes_cates.first.size() == 1) + { + createPYRAMIDfromTRI(nodes_cates, + PYRAMID5, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + axis_node_case, + is_flipped); + } + else if (nodes_cates.first.size() == 2) + { + createTETfromTRI(nodes_cates, + TET4, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + axis_node_case, + is_flipped); + } + else + mooseError("A degenerate TRI3 elements overlapped with the rotation axis cannot be " + "revolved."); + + break; + } + case TRI6: + { + // Possible scenarios: + // 1. None of the nodes are on the axis + // Then a prism18 element is created + // 2. One of the nodes is on the axis + // Then a pyramid13 element is created + // 3. Three of the nodes are on the axis + // Then a tet10 element is created + // NOTE: We do not support two nodes on the axis for tri6 elements + const auto nodes_cates = onAxisNodesIdentifier(*elem, nodes_on_axis); + if (nodes_cates.first.empty()) + { + createPRISMfromTRI(PRISM18, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + is_flipped); + } + else if (nodes_cates.first.size() == 1) + { + createPYRAMIDfromTRI(nodes_cates, + PYRAMID13, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + axis_node_case, + is_flipped); + } + else if (nodes_cates.first.size() == 3) + { + createTETfromTRI(nodes_cates, + TET10, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + axis_node_case, + is_flipped); + } + else + mooseError( + "You either have a degenerate TRI6 element, or the mid-point of the " + "on-axis edge is not colinear with the two vertices, which is not supported."); + break; + } + case TRI7: + { + // Possible scenarios: + // 1. None of the nodes are on the axis + // Then a prism21 element is created + // 2. One of the nodes is on the axis + // Then a pyramid18 element is created + // 3. Three of the nodes are on the axis + // Then a tet14 element is created + // NOTE: We do not support two nodes on the axis for tri7 elements + const auto nodes_cates = onAxisNodesIdentifier(*elem, nodes_on_axis); + if (nodes_cates.first.empty()) + { + createPRISMfromTRI(PRISM21, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + is_flipped); + } + else if (nodes_cates.first.size() == 1) + { + createPYRAMIDfromTRI(nodes_cates, + PYRAMID18, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + axis_node_case, + is_flipped); + } + else if (nodes_cates.first.size() == 3) + { + createTETfromTRI(nodes_cates, + TET14, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + axis_node_case, + is_flipped); + } + else + mooseError("You either have a degenerate TRI6 element, or the mid-point of the " + "on-axis edge of the TRI6 element is not colinear with the two vertices, " + "which is not supported."); + break; + } + case QUAD4: + { + // Possible scenarios: + // 1. None of the nodes are on the axis + // Then a hex8 element is created + // 2. One of the nodes is on the axis + // Then a pyramid5 element and a prism6 element are created + // 3. Two of the nodes are on the axis + // Then a prism6 is created + const auto nodes_cates = onAxisNodesIdentifier(*elem, nodes_on_axis); + if (nodes_cates.first.empty()) + { + createHEXfromQUAD(HEX8, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + is_flipped); + } + else if (nodes_cates.first.size() == 1) + { + createPYRAMIDPRISMfromQUAD(nodes_cates, + PYRAMID5, + PRISM6, + elem, + mesh, + new_elem, + new_elem_1, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + axis_node_case, + is_flipped, + is_flipped_additional); + } + else if (nodes_cates.first.size() == 2) + { + createPRISMfromQUAD(nodes_cates, + PRISM6, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + axis_node_case, + is_flipped); + } + + else + mooseError("Degenerate QUAD4 element with 3 or more aligned nodes cannot be " + "azimuthally revolved"); + + break; + } + case QUAD8: + { + // Possible scenarios: + // 1. None of the nodes are on the axis + // Then a hex20 element is created + // 2. One of the nodes is on the axis + // In that case, it is already converted to a QUAD9 element before, + // SO we do not need to worry about this case + // 3. Three of the nodes are on the axis + // Then a prism15 is created + // NOTE: We do not support two nodes on the axis for quad8 elements + const auto nodes_cates = onAxisNodesIdentifier(*elem, nodes_on_axis); + if (nodes_cates.first.empty()) + { + createHEXfromQUAD(HEX20, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + is_flipped); + } + else if (nodes_cates.first.size() == 3) + { + createPRISMfromQUAD(nodes_cates, + PRISM15, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + axis_node_case, + is_flipped); + } + else + mooseError("You either have a degenerate QUAD8 element, or the mid-point of the " + "on-axis edge of the QUAD8 element is not colinear with the two vertices, " + "which is not supported."); + + break; + } + case QUAD9: + { + // Possible scenarios: + // 1. None of the nodes are on the axis + // Then a hex27 element is created + // 2. One of the nodes is on the axis + // Then a pyramid14 element and a prism18 element are created + // 3. Two of the nodes are on the axis + // Then a prism18 is created + // (we do not create prism20/21 here just to make prism18 the only possible prism + // elements for simplicity) + const auto nodes_cates = onAxisNodesIdentifier(*elem, nodes_on_axis); + if (nodes_cates.first.empty()) + { + createHEXfromQUAD(HEX27, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + is_flipped); + } + else if (nodes_cates.first.size() == 1) + { + createPYRAMIDPRISMfromQUAD(nodes_cates, + PYRAMID14, + PRISM18, + elem, + mesh, + new_elem, + new_elem_1, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + axis_node_case, + is_flipped, + is_flipped_additional); + } + else if (nodes_cates.first.size() == 3) + { + createPRISMfromQUAD(nodes_cates, + PRISM18, + elem, + mesh, + new_elem, + current_layer, + orig_nodes, + total_num_azimuthal_intervals, + side_pairs, + axis_node_case, + is_flipped); + } + else + mooseError("You either have a degenerate QUAD9 element, or the mid-point of the " + "on-axis edge of the QUAD9 element is not colinear with the two vertices, " + "which is not supported."); + break; + } + default: + mooseError("The input mesh contains unsupported element type(s)."); + } + new_elem->set_id(elem->id() + (current_layer * orig_elem)); + new_elem->processor_id() = elem->processor_id(); + if (new_elem_1) + { + new_elem_1->set_id(elem->id() + (current_layer * orig_elem) + elem_id_shift); + new_elem_1->processor_id() = elem->processor_id(); + } + +#ifdef LIBMESH_ENABLE_UNIQUE_ID + // Let's give the base elements of the revolved mesh the same + // unique_ids as the source mesh, in case anyone finds that + // a useful map to preserve. + const unique_id_type uid = (current_layer == 0) + ? elem->unique_id() + : orig_unique_ids + + (current_layer - 1) * (orig_nodes + orig_elem) + + orig_nodes + elem->id(); + + new_elem->set_unique_id(uid); + + // Special case for extra elements + if (new_elem_1) + { + const unique_id_type uid_1 = + elem_unique_id_shift + + ((current_layer == 0) + ? elem->unique_id() + : orig_unique_ids + (current_layer - 1) * (orig_nodes + orig_elem) + orig_nodes + + elem->id()); + + new_elem_1->set_unique_id(uid_1); + } +#endif + + // maintain the subdomain_id + switch (etype) + { + case EDGE2: + switch (new_elem->type()) + { + case QUAD4: + new_elem->subdomain_id() = elem->subdomain_id(); + break; + case TRI3: + new_elem->subdomain_id() = edge_to_tri_subdomain_id_shift + elem->subdomain_id(); + break; + default: + mooseAssert(false, + "impossible element type generated by revolving an EDGE2 element"); + } + break; + case EDGE3: + switch (new_elem->type()) + { + case QUAD9: + new_elem->subdomain_id() = elem->subdomain_id(); + break; + case TRI7: + new_elem->subdomain_id() = edge_to_tri_subdomain_id_shift + elem->subdomain_id(); + break; + default: + mooseAssert(false, + "impossible element type generated by revolving an EDGE3 element"); + } + break; + case TRI3: + switch (new_elem->type()) + { + case PRISM6: + new_elem->subdomain_id() = elem->subdomain_id(); + break; + case PYRAMID5: + new_elem->subdomain_id() = tri_to_pyramid_subdomain_id_shift + elem->subdomain_id(); + break; + case TET4: + new_elem->subdomain_id() = tri_to_tet_subdomain_id_shift + elem->subdomain_id(); + break; + default: + mooseAssert(false, "impossible element type generated by revolving a TRI3 element"); + } + break; + case TRI6: + switch (new_elem->type()) + { + case PRISM18: + new_elem->subdomain_id() = elem->subdomain_id(); + break; + case PYRAMID13: + new_elem->subdomain_id() = tri_to_pyramid_subdomain_id_shift + elem->subdomain_id(); + break; + case TET10: + new_elem->subdomain_id() = tri_to_tet_subdomain_id_shift + elem->subdomain_id(); + break; + default: + mooseAssert(false, "impossible element type generated by revolving a TRI6 element"); + } + break; + case TRI7: + switch (new_elem->type()) + { + case PRISM21: + new_elem->subdomain_id() = elem->subdomain_id(); + break; + case PYRAMID18: + new_elem->subdomain_id() = tri_to_pyramid_subdomain_id_shift + elem->subdomain_id(); + break; + case TET14: + new_elem->subdomain_id() = tri_to_tet_subdomain_id_shift + elem->subdomain_id(); + break; + default: + mooseAssert(false, "impossible element type generated by revolving a TRI7 element"); + } + break; + case QUAD4: + switch (new_elem->type()) + { + case HEX8: + new_elem->subdomain_id() = elem->subdomain_id(); + break; + case PRISM6: + new_elem->subdomain_id() = quad_to_prism_subdomain_id_shift + elem->subdomain_id(); + break; + case PYRAMID5: + new_elem->subdomain_id() = + quad_to_pyramid_subdomain_id_shift + elem->subdomain_id(); + new_elem_1->subdomain_id() = + quad_to_prism_subdomain_id_shift + elem->subdomain_id(); + break; + default: + mooseAssert(false, + "impossible element type generated by revolving a QUAD4 element"); + } + break; + case QUAD8: + switch (new_elem->type()) + { + case HEX20: + new_elem->subdomain_id() = elem->subdomain_id(); + break; + case PRISM15: + new_elem->subdomain_id() = quad_to_prism_subdomain_id_shift + elem->subdomain_id(); + break; + default: + mooseAssert(false, + "impossible element type generated by revolving a QUAD8 element"); + } + break; + case QUAD9: + switch (new_elem->type()) + { + case HEX27: + new_elem->subdomain_id() = elem->subdomain_id(); + break; + case PRISM18: + new_elem->subdomain_id() = + quad_to_hi_pyramid_subdomain_id_shift + elem->subdomain_id(); + break; + case PYRAMID14: + new_elem->subdomain_id() = + quad_to_pyramid_subdomain_id_shift + elem->subdomain_id(); + new_elem_1->subdomain_id() = + quad_to_hi_pyramid_subdomain_id_shift + elem->subdomain_id(); + break; + default: + mooseAssert(false, + "impossible element type generated by revolving a QUAD9 element"); + } + break; + default: + mooseAssert(false, + "The input mesh contains unsupported element type(s), which should have " + "been checked in prior steps in this code."); + } + + if (_subdomain_swap_pairs.size()) + { + auto & revolving_swap_pairs = _subdomain_swap_pairs[e]; + + auto new_id_it = revolving_swap_pairs.find(elem->subdomain_id()); + + if (new_id_it != revolving_swap_pairs.end()) + { + new_elem->subdomain_id() = + new_elem->subdomain_id() - elem->subdomain_id() + new_id_it->second; + if (new_elem_1) + new_elem_1->subdomain_id() = + new_elem_1->subdomain_id() - elem->subdomain_id() + new_id_it->second; + } + } + + Elem * added_elem = mesh->add_elem(std::move(new_elem)); + Elem * added_elem_1 = NULL; + + if (new_elem_1) + added_elem_1 = mesh->add_elem(std::move(new_elem_1)); + + // maintain extra integers + for (unsigned int i = 0; i < num_extra_elem_integers; i++) + { + added_elem->set_extra_integer(i, elem->get_extra_integer(i)); + if (added_elem_1) + added_elem_1->set_extra_integer(i, elem->get_extra_integer(i)); + } + + if (_elem_integers_swap_pairs.size()) + { + for (unsigned int i = 0; i < _elem_integer_indices_to_swap.size(); i++) + { + auto & elevation_extra_swap_pairs = + _elem_integers_swap_pairs[i * _nums_azimuthal_intervals.size() + e]; + + auto new_extra_id_it = elevation_extra_swap_pairs.find( + elem->get_extra_integer(_elem_integer_indices_to_swap[i])); + + if (new_extra_id_it != elevation_extra_swap_pairs.end()) + { + added_elem->set_extra_integer(_elem_integer_indices_to_swap[i], + new_extra_id_it->second); + if (added_elem_1) + added_elem_1->set_extra_integer(_elem_integer_indices_to_swap[i], + new_extra_id_it->second); + } + } + } + + // Copy any old boundary ids on all sides + for (auto s : elem->side_index_range()) + { + input_boundary_info.boundary_ids(elem, s, ids_to_copy); + std::vector ids_to_copy_swapped; + if (_boundary_swap_pairs.empty()) + ids_to_copy_swapped = ids_to_copy; + else + for (const auto & id_to_copy : ids_to_copy) + ids_to_copy_swapped.push_back(_boundary_swap_pairs[e].count(id_to_copy) + ? _boundary_swap_pairs[e][id_to_copy] + : id_to_copy); + + switch (etype) + { + case EDGE2: + switch (added_elem->type()) + { + case QUAD4: + boundary_info.add_side( + added_elem, cast_int(s == 0 ? 3 : 1), ids_to_copy_swapped); + break; + case TRI3: + if (s != axis_node_case) + boundary_info.add_side( + added_elem, cast_int(s), ids_to_copy_swapped); + break; + default: + mooseAssert(false, + "impossible element type generated by revolving an EDGE2 element"); + } + break; + case EDGE3: + switch (added_elem->type()) + { + case QUAD9: + boundary_info.add_side( + added_elem, cast_int(s == 0 ? 3 : 1), ids_to_copy_swapped); + break; + case TRI7: + if (s != axis_node_case) + boundary_info.add_side( + added_elem, cast_int(s), ids_to_copy_swapped); + break; + default: + mooseAssert(false, + "impossible element type generated by revolving an EDGE3 element"); + } + break; + case TRI3: + switch (added_elem->type()) + { + case PRISM6: + boundary_info.add_side( + added_elem, cast_int(s + 1), ids_to_copy_swapped); + break; + case PYRAMID5: + if ((s + 3 - axis_node_case) % 3 == 0) + boundary_info.add_side( + added_elem, cast_int(3), ids_to_copy_swapped); + else if ((s + 3 - axis_node_case) % 3 == 1) + boundary_info.add_side( + added_elem, cast_int(4), ids_to_copy_swapped); + else + boundary_info.add_side( + added_elem, cast_int(1), ids_to_copy_swapped); + break; + case TET4: + if ((s + 3 - axis_node_case) % 3 == 0) + boundary_info.add_side( + added_elem, cast_int(2), ids_to_copy_swapped); + else if ((s + 3 - axis_node_case) % 3 == 2) + boundary_info.add_side( + added_elem, cast_int(3), ids_to_copy_swapped); + break; + default: + mooseAssert(false, + "impossible element type generated by revolving a TRI3 element"); + } + break; + case TRI6: + switch (added_elem->type()) + { + case PRISM18: + boundary_info.add_side( + added_elem, cast_int(s + 1), ids_to_copy_swapped); + break; + case PYRAMID13: + if ((s + 3 - axis_node_case) % 3 == 0) + boundary_info.add_side( + added_elem, cast_int(3), ids_to_copy_swapped); + else if ((s + 3 - axis_node_case) % 3 == 1) + boundary_info.add_side( + added_elem, cast_int(4), ids_to_copy_swapped); + else + boundary_info.add_side( + added_elem, cast_int(1), ids_to_copy_swapped); + break; + case TET10: + if ((s + 3 - axis_node_case) % 3 == 0) + boundary_info.add_side( + added_elem, cast_int(2), ids_to_copy_swapped); + else if ((s + 3 - axis_node_case) % 3 == 2) + boundary_info.add_side( + added_elem, cast_int(3), ids_to_copy_swapped); + break; + default: + mooseAssert(false, + "impossible element type generated by revolving a TRI6 element"); + } + break; + case TRI7: + switch (added_elem->type()) + { + case PRISM21: + boundary_info.add_side( + added_elem, cast_int(s + 1), ids_to_copy_swapped); + break; + case PYRAMID18: + if ((s + 3 - axis_node_case) % 3 == 0) + boundary_info.add_side( + added_elem, cast_int(3), ids_to_copy_swapped); + else if ((s + 3 - axis_node_case) % 3 == 1) + boundary_info.add_side( + added_elem, cast_int(4), ids_to_copy_swapped); + else + boundary_info.add_side( + added_elem, cast_int(1), ids_to_copy_swapped); + break; + case TET14: + if ((s + 3 - axis_node_case) % 3 == 0) + boundary_info.add_side( + added_elem, cast_int(2), ids_to_copy_swapped); + else if ((s + 3 - axis_node_case) % 3 == 2) + boundary_info.add_side( + added_elem, cast_int(3), ids_to_copy_swapped); + break; + default: + mooseAssert(false, + "impossible element type generated by revolving a TRI7 element"); + } + break; + case QUAD4: + switch (added_elem->type()) + { + case HEX8: + boundary_info.add_side( + added_elem, cast_int(s + 1), ids_to_copy_swapped); + break; + case PRISM6: + if ((s + 4 - axis_node_case) % 4 == 1) + boundary_info.add_side( + added_elem, cast_int(4), ids_to_copy_swapped); + else if ((s + 4 - axis_node_case) % 4 == 2) + boundary_info.add_side( + added_elem, cast_int(2), ids_to_copy_swapped); + else if ((s + 4 - axis_node_case) % 4 == 3) + boundary_info.add_side( + added_elem, cast_int(0), ids_to_copy_swapped); + break; + case PYRAMID5: + if ((s + 4 - axis_node_case) % 4 == 3) + boundary_info.add_side( + added_elem, cast_int(1), ids_to_copy_swapped); + else if ((s + 4 - axis_node_case) % 4 == 0) + boundary_info.add_side( + added_elem, cast_int(3), ids_to_copy_swapped); + else if ((s + 4 - axis_node_case) % 4 == 1) + boundary_info.add_side( + added_elem_1, cast_int(3), ids_to_copy_swapped); + else + boundary_info.add_side( + added_elem_1, cast_int(2), ids_to_copy_swapped); + break; + default: + mooseAssert(false, + "impossible element type generated by revolving a QUAD4 element"); + } + break; + case QUAD8: + switch (added_elem->type()) + { + case HEX20: + boundary_info.add_side( + added_elem, cast_int(s + 1), ids_to_copy_swapped); + break; + case PRISM15: + if ((s + 4 - axis_node_case) % 4 == 1) + boundary_info.add_side( + added_elem, cast_int(4), ids_to_copy_swapped); + else if ((s + 4 - axis_node_case) % 4 == 2) + boundary_info.add_side( + added_elem, cast_int(2), ids_to_copy_swapped); + else if ((s + 4 - axis_node_case) % 4 == 3) + boundary_info.add_side( + added_elem, cast_int(0), ids_to_copy_swapped); + break; + default: + mooseAssert(false, + "impossible element type generated by revolving a QUAD8 element"); + } + break; + case QUAD9: + switch (added_elem->type()) + { + case HEX27: + boundary_info.add_side( + added_elem, cast_int(s + 1), ids_to_copy_swapped); + break; + case PRISM18: + if ((s + 4 - axis_node_case) % 4 == 1) + boundary_info.add_side( + added_elem, cast_int(4), ids_to_copy_swapped); + else if ((s + 4 - axis_node_case) % 4 == 2) + boundary_info.add_side( + added_elem, cast_int(2), ids_to_copy_swapped); + else if ((s + 4 - axis_node_case) % 4 == 3) + boundary_info.add_side( + added_elem, cast_int(0), ids_to_copy_swapped); + break; + case PYRAMID14: + if ((s + 4 - axis_node_case) % 4 == 3) + boundary_info.add_side( + added_elem, cast_int(1), ids_to_copy_swapped); + else if ((s + 4 - axis_node_case) % 4 == 0) + boundary_info.add_side( + added_elem, cast_int(3), ids_to_copy_swapped); + else if ((s + 4 - axis_node_case) % 4 == 1) + boundary_info.add_side( + added_elem_1, cast_int(3), ids_to_copy_swapped); + else + boundary_info.add_side( + added_elem_1, cast_int(2), ids_to_copy_swapped); + break; + default: + mooseAssert(false, + "impossible element type generated by revolving a QUAD9 element"); + } + break; + default: + mooseAssert(false, + "The input mesh contains unsupported element type(s), which should have " + "been checked in prior steps in this code."); + } + } + + if (current_layer == 0 && _has_start_boundary) + { + boundary_info.add_side( + added_elem, is_flipped ? side_pairs[0].second : side_pairs[0].first, _start_boundary); + if (side_pairs.size() > 1) + boundary_info.add_side(added_elem_1, + is_flipped_additional ? side_pairs[1].second + : side_pairs[1].first, + _start_boundary); + } + + if (current_layer == num_layers - 1 && _has_end_boundary) + { + boundary_info.add_side( + added_elem, is_flipped ? side_pairs[0].first : side_pairs[0].second, _end_boundary); + if (side_pairs.size() > 1) + boundary_info.add_side(added_elem_1, + is_flipped_additional ? side_pairs[1].first + : side_pairs[1].second, + _end_boundary); + } + current_layer++; + } + } + } + + // Copy all the subdomain/sideset/nodeset name maps to the revolved mesh + if (!input_subdomain_map.empty()) + mesh->set_subdomain_name_map().insert(input_subdomain_map.begin(), input_subdomain_map.end()); + if (!input_sideset_map.empty()) + mesh->get_boundary_info().set_sideset_name_map().insert(input_sideset_map.begin(), + input_sideset_map.end()); + if (!input_nodeset_map.empty()) + mesh->get_boundary_info().set_nodeset_name_map().insert(input_nodeset_map.begin(), + input_nodeset_map.end()); + + mesh->remove_orphaned_nodes(); + mesh->renumber_nodes_and_elements(); + mesh->set_isnt_prepared(); + + return mesh; +} + +std::pair +RevolveGenerator::getRotationCenterAndRadius(const Point & p_ext, + const Point & p_axis, + const Point & dir_axis) const +{ + // First use point product to get the distance between the axis point and the projection of the + // external point on the axis + const Real dist = (p_ext - p_axis) * dir_axis.unit(); + const Point center_pt = p_axis + dist * dir_axis.unit(); + // Then get the radius + const Real radius = (p_ext - center_pt).norm(); + return std::make_pair(radius, center_pt); +} + +std::vector +RevolveGenerator::rotationVectors(const Point & p_axis, + const Point & dir_axis, + const Point & p_input) const +{ + // To make the rotation mathematically simple, we perform rotation in a coordination system + // (x',y',z') defined by rotation axis and the mesh to be rotated. + // z' is the rotation axis, which is trivial dir_axis.unit() + const Point z_prime = dir_axis.unit(); + // the x' and z' should form the plane that accommodates input mesh + const Point x_prime = ((p_input - p_axis) - ((p_input - p_axis) * z_prime) * z_prime).unit(); + const Point y_prime = z_prime.cross(x_prime); + // Then we transform things back to the original coordination system (x,y,z), which is trivial + // (1,0,0), (0,1,0), (0,0,1) + return {{x_prime(0), y_prime(0), z_prime(0)}, + {x_prime(1), y_prime(1), z_prime(1)}, + {x_prime(2), y_prime(2), z_prime(2)}}; +} + +std::pair, std::vector> +RevolveGenerator::onAxisNodesIdentifier(const Elem & elem, + const std::vector & nodes_on_axis) const +{ + std::vector nodes_on_axis_in_elem; + std::vector nodes_not_on_axis_in_elem; + for (unsigned int i = 0; i < elem.n_nodes(); i++) + { + const auto node_id = elem.node_id(i); + if (std::find(nodes_on_axis.begin(), nodes_on_axis.end(), node_id) != nodes_on_axis.end()) + { + nodes_on_axis_in_elem.push_back(i); + } + else + { + nodes_not_on_axis_in_elem.push_back(i); + } + } + return std::make_pair(nodes_on_axis_in_elem, nodes_not_on_axis_in_elem); +} + +void +RevolveGenerator::nodeModification(Node & node) +{ + const Point axis_component = + ((node - _axis_point) * _axis_direction.unit()) * _axis_direction.unit(); + const Point rad_component = ((node - _axis_point) - axis_component) * _radius_correction_factor; + node = _axis_point + axis_component + rad_component; +} + +void +RevolveGenerator::createQUADfromEDGE(const ElemType quad_elem_type, + const Elem * elem, + const std::unique_ptr & mesh, + std::unique_ptr & new_elem, + const int current_layer, + const unsigned int orig_nodes, + const unsigned int total_num_azimuthal_intervals, + std::vector> & side_pairs, + bool & is_flipped) const +{ + if (quad_elem_type != QUAD4 && quad_elem_type != QUAD9) + mooseError("Unsupported element type", quad_elem_type); + + side_pairs.push_back(std::make_pair(0, 2)); + const unsigned int order = quad_elem_type == QUAD4 ? 1 : 2; + + new_elem = std::make_unique(); + if (quad_elem_type == QUAD9) + { + new_elem = std::make_unique(); + new_elem->set_node(4) = + mesh->node_ptr(elem->node_ptr(2)->id() + (current_layer * 2 * orig_nodes)); + new_elem->set_node(5) = + mesh->node_ptr(elem->node_ptr(1)->id() + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(6) = + mesh->node_ptr(elem->node_ptr(2)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem->set_node(7) = + mesh->node_ptr(elem->node_ptr(0)->id() + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(8) = + mesh->node_ptr(elem->node_ptr(2)->id() + ((current_layer * 2 + 1) * orig_nodes)); + } + + new_elem->set_node(0) = + mesh->node_ptr(elem->node_ptr(0)->id() + (current_layer * order * orig_nodes)); + new_elem->set_node(1) = + mesh->node_ptr(elem->node_ptr(1)->id() + (current_layer * order * orig_nodes)); + new_elem->set_node(3) = + mesh->node_ptr(elem->node_ptr(0)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + new_elem->set_node(2) = + mesh->node_ptr(elem->node_ptr(1)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + + if (new_elem->volume() < 0.0) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 0, 3); + MooseMeshUtils::swapNodesInElem(*new_elem, 1, 2); + if (quad_elem_type == QUAD9) + MooseMeshUtils::swapNodesInElem(*new_elem, 4, 6); + is_flipped = true; + } +} + +void +RevolveGenerator::createTRIfromEDGE( + const std::pair, std::vector> & nodes_cates, + const ElemType tri_elem_type, + const Elem * elem, + const std::unique_ptr & mesh, + std::unique_ptr & new_elem, + const int current_layer, + const unsigned int orig_nodes, + const unsigned int total_num_azimuthal_intervals, + std::vector> & side_pairs, + dof_id_type & axis_node_case, + bool & is_flipped) const +{ + if (tri_elem_type != TRI3 && tri_elem_type != TRI7) + mooseError("Unsupported element type", tri_elem_type); + + side_pairs.push_back(std::make_pair(0, 2)); + const unsigned int order = tri_elem_type == TRI3 ? 1 : 2; + axis_node_case = nodes_cates.first.front(); + + new_elem = std::make_unique(); + if (tri_elem_type == TRI7) + { + new_elem = std::make_unique(); + new_elem->set_node(3) = + mesh->node_ptr(elem->node_ptr(2)->id() + (current_layer * 2 * orig_nodes)); + new_elem->set_node(4) = mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 2)->id() + + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(5) = + mesh->node_ptr(elem->node_ptr(2)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem->set_node(6) = + mesh->node_ptr(elem->node_ptr(2)->id() + ((current_layer * 2 + 1) * orig_nodes)); + } + + new_elem->set_node(0) = mesh->node_ptr(elem->node_ptr(axis_node_case)->id()); + new_elem->set_node(1) = mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 2)->id() + + (current_layer * order * orig_nodes)); + new_elem->set_node(2) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 2)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + + if (new_elem->volume() < 0.0) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 1, 2); + if (tri_elem_type == TRI7) + MooseMeshUtils::swapNodesInElem(*new_elem, 3, 5); + is_flipped = true; + } +} + +void +RevolveGenerator::createPRISMfromTRI(const ElemType prism_elem_type, + const Elem * elem, + const std::unique_ptr & mesh, + std::unique_ptr & new_elem, + const int current_layer, + const unsigned int orig_nodes, + const unsigned int total_num_azimuthal_intervals, + std::vector> & side_pairs, + bool & is_flipped) const +{ + if (prism_elem_type != PRISM6 && prism_elem_type != PRISM18 && prism_elem_type != PRISM21) + mooseError("unsupported situation"); + + side_pairs.push_back(std::make_pair(0, 4)); + const unsigned int order = prism_elem_type == PRISM6 ? 1 : 2; + + new_elem = std::make_unique(); + + if (order == 2) + { + new_elem = std::make_unique(); + if (prism_elem_type == PRISM21) + { + new_elem = std::make_unique(); + new_elem->set_node(18) = + mesh->node_ptr(elem->node_ptr(6)->id() + (current_layer * 2 * orig_nodes)); + new_elem->set_node(19) = mesh->node_ptr( + elem->node_ptr(6)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * 2 * + orig_nodes)); + new_elem->set_node(20) = + mesh->node_ptr(elem->node_ptr(6)->id() + ((current_layer * 2 + 1) * orig_nodes)); + } + new_elem->set_node(6) = + mesh->node_ptr(elem->node_ptr(3)->id() + (current_layer * 2 * orig_nodes)); + new_elem->set_node(7) = + mesh->node_ptr(elem->node_ptr(4)->id() + (current_layer * 2 * orig_nodes)); + new_elem->set_node(8) = + mesh->node_ptr(elem->node_ptr(5)->id() + (current_layer * 2 * orig_nodes)); + new_elem->set_node(9) = + mesh->node_ptr(elem->node_ptr(0)->id() + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(10) = + mesh->node_ptr(elem->node_ptr(1)->id() + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(11) = + mesh->node_ptr(elem->node_ptr(2)->id() + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(12) = + mesh->node_ptr(elem->node_ptr(3)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem->set_node(13) = + mesh->node_ptr(elem->node_ptr(4)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem->set_node(14) = + mesh->node_ptr(elem->node_ptr(5)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem->set_node(15) = + mesh->node_ptr(elem->node_ptr(3)->id() + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(16) = + mesh->node_ptr(elem->node_ptr(4)->id() + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(17) = + mesh->node_ptr(elem->node_ptr(5)->id() + ((current_layer * 2 + 1) * orig_nodes)); + } + new_elem->set_node(0) = + mesh->node_ptr(elem->node_ptr(0)->id() + (current_layer * order * orig_nodes)); + new_elem->set_node(1) = + mesh->node_ptr(elem->node_ptr(1)->id() + (current_layer * order * orig_nodes)); + new_elem->set_node(2) = + mesh->node_ptr(elem->node_ptr(2)->id() + (current_layer * order * orig_nodes)); + new_elem->set_node(3) = + mesh->node_ptr(elem->node_ptr(0)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + new_elem->set_node(4) = + mesh->node_ptr(elem->node_ptr(1)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + new_elem->set_node(5) = + mesh->node_ptr(elem->node_ptr(2)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + + if (new_elem->volume() < 0.0) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 0, 3); + MooseMeshUtils::swapNodesInElem(*new_elem, 1, 4); + MooseMeshUtils::swapNodesInElem(*new_elem, 2, 5); + if (prism_elem_type != PRISM6) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 6, 12); + MooseMeshUtils::swapNodesInElem(*new_elem, 7, 13); + MooseMeshUtils::swapNodesInElem(*new_elem, 8, 14); + if (prism_elem_type == PRISM21) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 18, 19); + } + } + is_flipped = true; + } +} + +void +RevolveGenerator::createPYRAMIDfromTRI( + const std::pair, std::vector> & nodes_cates, + const ElemType pyramid_elem_type, + const Elem * elem, + const std::unique_ptr & mesh, + std::unique_ptr & new_elem, + const int current_layer, + const unsigned int orig_nodes, + const unsigned int total_num_azimuthal_intervals, + std::vector> & side_pairs, + dof_id_type & axis_node_case, + bool & is_flipped) const +{ + if (pyramid_elem_type != PYRAMID5 && pyramid_elem_type != PYRAMID13 && + pyramid_elem_type != PYRAMID18) + mooseError("unsupported situation"); + + side_pairs.push_back(std::make_pair(0, 2)); + const unsigned int order = pyramid_elem_type == PYRAMID5 ? 1 : 2; + axis_node_case = nodes_cates.first.front(); + + new_elem = std::make_unique(); + + if (order == 2) + { + new_elem = std::make_unique(); + if (pyramid_elem_type == PYRAMID18) + { + new_elem = std::make_unique(); + new_elem->set_node(13) = mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 3 + 3)->id() + + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(15) = mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 3 + 3)->id() + + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(17) = mesh->node_ptr(elem->node_ptr(axis_node_case + 3)->id() + + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(14) = + mesh->node_ptr(elem->node_ptr(6)->id() + (current_layer * 2 * orig_nodes)); + new_elem->set_node(16) = mesh->node_ptr( + elem->node_ptr(6)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * 2 * + orig_nodes)); + } + new_elem->set_node(6) = mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 3)->id() + + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(8) = mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 3)->id() + + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(5) = mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 3 + 3)->id() + + (current_layer * 2 * orig_nodes)); + new_elem->set_node(7) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 3 + 3)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem->set_node(9) = + mesh->node_ptr(elem->node_ptr(axis_node_case + 3)->id() + (current_layer * 2 * orig_nodes)); + new_elem->set_node(10) = mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 3 + 3)->id() + + (current_layer * 2 * orig_nodes)); + new_elem->set_node(12) = + mesh->node_ptr(elem->node_ptr(axis_node_case + 3)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem->set_node(11) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 3 + 3)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + } + new_elem->set_node(4) = mesh->node_ptr(elem->node_ptr(axis_node_case)->id()); + new_elem->set_node(0) = mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 3)->id() + + (current_layer * order * orig_nodes)); + new_elem->set_node(1) = mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 3)->id() + + (current_layer * order * orig_nodes)); + new_elem->set_node(2) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 3)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + new_elem->set_node(3) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 3)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + + if (new_elem->volume() < 0.0) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 1, 2); + MooseMeshUtils::swapNodesInElem(*new_elem, 0, 3); + if (order == 2) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 5, 7); + MooseMeshUtils::swapNodesInElem(*new_elem, 10, 11); + MooseMeshUtils::swapNodesInElem(*new_elem, 9, 12); + if (pyramid_elem_type == PYRAMID18) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 14, 16); + } + } + is_flipped = true; + } +} + +void +RevolveGenerator::createTETfromTRI( + const std::pair, std::vector> & nodes_cates, + const ElemType tet_elem_type, + const Elem * elem, + const std::unique_ptr & mesh, + std::unique_ptr & new_elem, + const int current_layer, + const unsigned int orig_nodes, + const unsigned int total_num_azimuthal_intervals, + std::vector> & side_pairs, + dof_id_type & axis_node_case, + bool & is_flipped) const +{ + if (tet_elem_type != TET4 && tet_elem_type != TET10 && tet_elem_type != TET14) + mooseError("unsupported situation"); + + side_pairs.push_back(std::make_pair(0, 1)); + const unsigned int order = tet_elem_type == TET4 ? 1 : 2; + if (order == 2) + { + // Sanity check to filter unsupported cases + if (nodes_cates.first[0] > 2 || nodes_cates.first[1] > 2 || nodes_cates.first[2] < 3) + mooseError("unsupported situation 2"); + } + axis_node_case = nodes_cates.second.front(); + + new_elem = std::make_unique(); + if (order == 2) + { + const bool node_order = nodes_cates.first[1] - nodes_cates.first[0] == 1; + new_elem = std::make_unique(); + if (tet_elem_type == TET14) + { + new_elem = std::make_unique(); + new_elem->set_node(12) = mesh->node_ptr( + elem->node_ptr(node_order ? (nodes_cates.first[1] + 3) : (nodes_cates.second.front() + 3)) + ->id() + + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(13) = mesh->node_ptr( + elem->node_ptr(node_order ? (nodes_cates.second.front() + 3) : (nodes_cates.first[0] + 3)) + ->id() + + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(10) = + mesh->node_ptr(elem->node_ptr(6)->id() + (current_layer * 2 * orig_nodes)); + new_elem->set_node(11) = mesh->node_ptr( + elem->node_ptr(6)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * 2 * + orig_nodes)); + } + new_elem->set_node(4) = mesh->node_ptr( + elem->node_ptr(node_order ? (nodes_cates.first[0] + 3) : (nodes_cates.first[1] + 3))->id()); + new_elem->set_node(5) = mesh->node_ptr( + elem->node_ptr(node_order ? (nodes_cates.first[1] + 3) : (nodes_cates.second.front() + 3)) + ->id() + + (current_layer * 2 * orig_nodes)); + new_elem->set_node(6) = mesh->node_ptr( + elem->node_ptr(node_order ? (nodes_cates.second.front() + 3) : (nodes_cates.first[0] + 3)) + ->id() + + (current_layer * 2 * orig_nodes)); + new_elem->set_node(8) = mesh->node_ptr( + elem->node_ptr(node_order ? (nodes_cates.first[1] + 3) : (nodes_cates.second.front() + 3)) + ->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * 2 * + orig_nodes)); + new_elem->set_node(7) = mesh->node_ptr( + elem->node_ptr(node_order ? (nodes_cates.second.front() + 3) : (nodes_cates.first[0] + 3)) + ->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * 2 * + orig_nodes)); + new_elem->set_node(9) = mesh->node_ptr(elem->node_ptr(nodes_cates.second.front())->id() + + ((current_layer * 2 + 1) * orig_nodes)); + } + new_elem->set_node(0) = mesh->node_ptr(elem->node_ptr(nodes_cates.first[0])->id()); + new_elem->set_node(1) = mesh->node_ptr(elem->node_ptr(nodes_cates.first[1])->id()); + new_elem->set_node(2) = mesh->node_ptr(elem->node_ptr(nodes_cates.second.front())->id() + + (current_layer * order * orig_nodes)); + new_elem->set_node(3) = + mesh->node_ptr(elem->node_ptr(nodes_cates.second.front())->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + + if (new_elem->volume() < 0.0) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 2, 3); + if (order == 2) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 5, 8); + MooseMeshUtils::swapNodesInElem(*new_elem, 6, 7); + if (tet_elem_type == TET14) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 10, 11); + } + } + is_flipped = true; + } +} + +void +RevolveGenerator::createHEXfromQUAD(const ElemType hex_elem_type, + const Elem * elem, + const std::unique_ptr & mesh, + std::unique_ptr & new_elem, + const int current_layer, + const unsigned int orig_nodes, + const unsigned int total_num_azimuthal_intervals, + std::vector> & side_pairs, + bool & is_flipped) const +{ + if (hex_elem_type != HEX8 && hex_elem_type != HEX20 && hex_elem_type != HEX27) + mooseError("unsupported situation"); + + side_pairs.push_back(std::make_pair(0, 5)); + const unsigned int order = hex_elem_type == HEX8 ? 1 : 2; + + new_elem = std::make_unique(); + if (order == 2) + { + new_elem = std::make_unique(); + if (hex_elem_type == HEX27) + { + new_elem = std::make_unique(); + new_elem->set_node(20) = + mesh->node_ptr(elem->node_ptr(8)->id() + (current_layer * 2 * orig_nodes)); + new_elem->set_node(25) = mesh->node_ptr( + elem->node_ptr(8)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * 2 * + orig_nodes)); + new_elem->set_node(26) = + mesh->node_ptr(elem->node_ptr(8)->id() + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(21) = + mesh->node_ptr(elem->node_ptr(4)->id() + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(22) = + mesh->node_ptr(elem->node_ptr(5)->id() + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(23) = + mesh->node_ptr(elem->node_ptr(6)->id() + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(24) = + mesh->node_ptr(elem->node_ptr(7)->id() + ((current_layer * 2 + 1) * orig_nodes)); + } + new_elem->set_node(8) = + mesh->node_ptr(elem->node_ptr(4)->id() + (current_layer * 2 * orig_nodes)); + new_elem->set_node(9) = + mesh->node_ptr(elem->node_ptr(5)->id() + (current_layer * 2 * orig_nodes)); + new_elem->set_node(10) = + mesh->node_ptr(elem->node_ptr(6)->id() + (current_layer * 2 * orig_nodes)); + new_elem->set_node(11) = + mesh->node_ptr(elem->node_ptr(7)->id() + (current_layer * 2 * orig_nodes)); + new_elem->set_node(16) = + mesh->node_ptr(elem->node_ptr(4)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem->set_node(17) = + mesh->node_ptr(elem->node_ptr(5)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem->set_node(18) = + mesh->node_ptr(elem->node_ptr(6)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem->set_node(19) = + mesh->node_ptr(elem->node_ptr(7)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem->set_node(12) = + mesh->node_ptr(elem->node_ptr(0)->id() + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(13) = + mesh->node_ptr(elem->node_ptr(1)->id() + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(14) = + mesh->node_ptr(elem->node_ptr(2)->id() + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(15) = + mesh->node_ptr(elem->node_ptr(3)->id() + ((current_layer * 2 + 1) * orig_nodes)); + } + new_elem->set_node(0) = + mesh->node_ptr(elem->node_ptr(0)->id() + (current_layer * order * orig_nodes)); + new_elem->set_node(1) = + mesh->node_ptr(elem->node_ptr(1)->id() + (current_layer * order * orig_nodes)); + new_elem->set_node(2) = + mesh->node_ptr(elem->node_ptr(2)->id() + (current_layer * order * orig_nodes)); + new_elem->set_node(3) = + mesh->node_ptr(elem->node_ptr(3)->id() + (current_layer * order * orig_nodes)); + new_elem->set_node(4) = + mesh->node_ptr(elem->node_ptr(0)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + new_elem->set_node(5) = + mesh->node_ptr(elem->node_ptr(1)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + new_elem->set_node(6) = + mesh->node_ptr(elem->node_ptr(2)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + new_elem->set_node(7) = + mesh->node_ptr(elem->node_ptr(3)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + + if (new_elem->volume() < 0.0) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 0, 4); + MooseMeshUtils::swapNodesInElem(*new_elem, 1, 5); + MooseMeshUtils::swapNodesInElem(*new_elem, 2, 6); + MooseMeshUtils::swapNodesInElem(*new_elem, 3, 7); + if (order == 2) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 8, 16); + MooseMeshUtils::swapNodesInElem(*new_elem, 9, 17); + MooseMeshUtils::swapNodesInElem(*new_elem, 10, 18); + MooseMeshUtils::swapNodesInElem(*new_elem, 11, 19); + if (hex_elem_type == HEX27) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 20, 25); + } + } + is_flipped = true; + } +} + +void +RevolveGenerator::createPRISMfromQUAD( + const std::pair, std::vector> & nodes_cates, + const ElemType prism_elem_type, + const Elem * elem, + const std::unique_ptr & mesh, + std::unique_ptr & new_elem, + const int current_layer, + const unsigned int orig_nodes, + const unsigned int total_num_azimuthal_intervals, + std::vector> & side_pairs, + dof_id_type & axis_node_case, + bool & is_flipped) const +{ + if (prism_elem_type != PRISM6 && prism_elem_type != PRISM15 && prism_elem_type != PRISM18) + mooseError("unsupported situation"); + + side_pairs.push_back(std::make_pair(1, 3)); + const unsigned int order = prism_elem_type == PRISM6 ? 1 : 2; + if (order == 2) + { + // Sanity check to filter unsupported cases + if (nodes_cates.first[0] > 3 || nodes_cates.first[1] > 3 || nodes_cates.first[2] < 4) + mooseError("unsupported situation 2"); + } + // Can only be 0-1, 1-2, 2-3, 3-0, we only consider vetices here. + // nodes_cates are natually sorted + const dof_id_type min_on_axis = nodes_cates.first[0]; + const dof_id_type max_on_axis = nodes_cates.first[1]; + axis_node_case = max_on_axis - min_on_axis == 1 ? min_on_axis : max_on_axis; + + new_elem = std::make_unique(); + if (order == 2) + { + new_elem = std::make_unique(); + if (prism_elem_type == PRISM18) + { + new_elem = std::make_unique(); + new_elem->set_node(15) = + mesh->node_ptr(elem->node_ptr(8)->id() + (current_layer * 2 * orig_nodes)); + new_elem->set_node(16) = mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4 + 4)->id() + + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(17) = mesh->node_ptr( + elem->node_ptr(8)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * 2 * + orig_nodes)); + } + new_elem->set_node(9) = mesh->node_ptr(elem->node_ptr(axis_node_case + 4)->id()); + new_elem->set_node(10) = mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4 + 4)->id() + + (current_layer * 2 * orig_nodes)); + new_elem->set_node(12) = mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4 + 4)->id() + + (current_layer * 2 * orig_nodes)); + new_elem->set_node(6) = mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4 + 4)->id() + + (current_layer * 2 * orig_nodes)); + new_elem->set_node(14) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4 + 4)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem->set_node(8) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4 + 4)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem->set_node(11) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4 + 4)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem->set_node(7) = mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() + + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(13) = mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4)->id() + + ((current_layer * 2 + 1) * orig_nodes)); + } + new_elem->set_node(0) = mesh->node_ptr(elem->node_ptr(axis_node_case)->id()); + new_elem->set_node(3) = mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4)->id()); + new_elem->set_node(4) = mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4)->id() + + (current_layer * order * orig_nodes)); + new_elem->set_node(1) = mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() + + (current_layer * order * orig_nodes)); + new_elem->set_node(5) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + new_elem->set_node(2) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + + if (new_elem->volume() < 0.0) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 1, 2); + MooseMeshUtils::swapNodesInElem(*new_elem, 4, 5); + if (order == 2) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 12, 14); + MooseMeshUtils::swapNodesInElem(*new_elem, 6, 8); + MooseMeshUtils::swapNodesInElem(*new_elem, 10, 11); + if (prism_elem_type == PRISM18) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 15, 17); + } + } + is_flipped = true; + } +} + +void +RevolveGenerator::createPYRAMIDPRISMfromQUAD( + const std::pair, std::vector> & nodes_cates, + const ElemType pyramid_elem_type, + const ElemType prism_elem_type, + const Elem * elem, + const std::unique_ptr & mesh, + std::unique_ptr & new_elem, + std::unique_ptr & new_elem_1, + const int current_layer, + const unsigned int orig_nodes, + const unsigned int total_num_azimuthal_intervals, + std::vector> & side_pairs, + dof_id_type & axis_node_case, + bool & is_flipped, + bool & is_flipped_additional) const +{ + if (!(pyramid_elem_type == PYRAMID5 && prism_elem_type == PRISM6) && + !(pyramid_elem_type == PYRAMID14 && prism_elem_type == PRISM18)) + mooseError("unsupported situation"); + const unsigned int order = pyramid_elem_type == PYRAMID5 ? 1 : 2; + + side_pairs.push_back(std::make_pair(0, 2)); + axis_node_case = nodes_cates.first.front(); + new_elem = std::make_unique(); + if (pyramid_elem_type == PYRAMID14) + { + new_elem = std::make_unique(); + new_elem->set_node(9) = + mesh->node_ptr(elem->node_ptr(axis_node_case + 4)->id() + (current_layer * 2 * orig_nodes)); + new_elem->set_node(10) = mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4 + 4)->id() + + (current_layer * 2 * orig_nodes)); + new_elem->set_node(5) = + mesh->node_ptr(elem->node_ptr(8)->id() + (current_layer * 2 * orig_nodes)); + new_elem->set_node(8) = mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4)->id() + + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(6) = mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() + + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(13) = + mesh->node_ptr(elem->node_ptr(8)->id() + ((current_layer * 2 + 1) * orig_nodes)); + new_elem->set_node(7) = + mesh->node_ptr(elem->node_ptr(8)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem->set_node(11) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4 + 4)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem->set_node(12) = + mesh->node_ptr(elem->node_ptr(axis_node_case + 4)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + } + new_elem->set_node(4) = mesh->node_ptr(elem->node_ptr(axis_node_case)->id()); + new_elem->set_node(0) = mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4)->id() + + (current_layer * order * orig_nodes)); + new_elem->set_node(1) = mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() + + (current_layer * order * orig_nodes)); + new_elem->set_node(2) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + new_elem->set_node(3) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + + if (new_elem->volume() < 0.0) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 1, 2); + MooseMeshUtils::swapNodesInElem(*new_elem, 0, 3); + if (pyramid_elem_type == PYRAMID14) + { + MooseMeshUtils::swapNodesInElem(*new_elem, 5, 7); + MooseMeshUtils::swapNodesInElem(*new_elem, 10, 11); + MooseMeshUtils::swapNodesInElem(*new_elem, 9, 12); + } + is_flipped = true; + } + + side_pairs.push_back(std::make_pair(0, 4)); + new_elem_1 = std::make_unique(); + if (prism_elem_type == PRISM18) + { + new_elem_1 = std::make_unique(); + new_elem_1->set_node(6) = + mesh->node_ptr(elem->node_ptr(8)->id() + (current_layer * 2 * orig_nodes)); + new_elem_1->set_node(8) = mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4 + 4)->id() + + (current_layer * 2 * orig_nodes)); + new_elem_1->set_node(7) = mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4 + 4)->id() + + (current_layer * 2 * orig_nodes)); + new_elem_1->set_node(9) = mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4)->id() + + ((current_layer * 2 + 1) * orig_nodes)); + new_elem_1->set_node(10) = mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() + + ((current_layer * 2 + 1) * orig_nodes)); + new_elem_1->set_node(11) = mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4)->id() + + ((current_layer * 2 + 1) * orig_nodes)); + new_elem_1->set_node(15) = + mesh->node_ptr(elem->node_ptr(8)->id() + ((current_layer * 2 + 1) * orig_nodes)); + new_elem_1->set_node(17) = mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4 + 4)->id() + + ((current_layer * 2 + 1) * orig_nodes)); + new_elem_1->set_node(16) = mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4 + 4)->id() + + ((current_layer * 2 + 1) * orig_nodes)); + new_elem_1->set_node(12) = + mesh->node_ptr(elem->node_ptr(8)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem_1->set_node(13) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4 + 4)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + new_elem_1->set_node(14) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4 + 4)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + 2 * orig_nodes)); + } + new_elem_1->set_node(0) = mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4)->id() + + (current_layer * order * orig_nodes)); + new_elem_1->set_node(1) = mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() + + (current_layer * order * orig_nodes)); + new_elem_1->set_node(2) = mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4)->id() + + (current_layer * order * orig_nodes)); + new_elem_1->set_node(3) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 1) % 4)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + new_elem_1->set_node(4) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 3) % 4)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + new_elem_1->set_node(5) = + mesh->node_ptr(elem->node_ptr((axis_node_case + 2) % 4)->id() + + ((current_layer + 1) % + (total_num_azimuthal_intervals + 1 - (unsigned int)_full_circle_revolving) * + order * orig_nodes)); + + if (new_elem_1->volume() < 0.0) + { + MooseMeshUtils::swapNodesInElem(*new_elem_1, 0, 3); + MooseMeshUtils::swapNodesInElem(*new_elem_1, 1, 4); + MooseMeshUtils::swapNodesInElem(*new_elem_1, 2, 5); + if (prism_elem_type == PRISM18) + { + MooseMeshUtils::swapNodesInElem(*new_elem_1, 6, 12); + MooseMeshUtils::swapNodesInElem(*new_elem_1, 7, 13); + MooseMeshUtils::swapNodesInElem(*new_elem_1, 8, 14); + } + is_flipped_additional = true; + } +} diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/ei_swap.i b/modules/reactor/test/tests/meshgenerators/revolve_generator/ei_swap.i new file mode 100644 index 000000000000..2f16be72899c --- /dev/null +++ b/modules/reactor/test/tests/meshgenerators/revolve_generator/ei_swap.i @@ -0,0 +1,60 @@ +[Problem] + solve = false +[] + +[Mesh] + # See fancy_extruder_with_boundary_swap.i for details about mesh_2d.e + [fmg] + type = FileMeshGenerator + file = mesh_2d.e + exodus_extra_element_integers = 'element_extra_integer_1 element_extra_integer_2' + [] + [rg] + type = RevolveGenerator + input = fmg + axis_point = '5.0 0.0 0.0' + axis_direction = '0.0 1.0 0.0' + nums_azimuthal_intervals = '2 4' + revolving_angles = '30 60' + elem_integer_names_to_swap = 'element_extra_integer_1 element_extra_integer_2' + elem_integers_swaps = '1 4 2 8; + 2 7 | + 1 8 2 4; + 2 5' + [] +[] + +[AuxVariables] + [element_extra_integer_1] + family = MONOMIAL + order = CONSTANT + [] + [element_extra_integer_2] + family = MONOMIAL + order = CONSTANT + [] +[] + +[AuxKernels] + [element_extra_integer_1] + type = ExtraElementIDAux + variable = element_extra_integer_1 + extra_id_name = element_extra_integer_1 + execute_on = 'initial' + [] + [element_extra_integer_2] + type = ExtraElementIDAux + variable = element_extra_integer_2 + extra_id_name = element_extra_integer_2 + execute_on = 'initial' + [] +[] + +[Executioner] + type = Steady +[] + +[Outputs] + exodus = true + execute_on = final +[] diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/ei_swap_out.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/ei_swap_out.e new file mode 100644 index 000000000000..d1cc603be77b Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/ei_swap_out.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/multi_revolve_tri.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/multi_revolve_tri.e new file mode 100644 index 000000000000..4fe54b769150 Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/multi_revolve_tri.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/multi_revolve_tri_partial.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/multi_revolve_tri_partial.e new file mode 100644 index 000000000000..b8821d02d54b Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/multi_revolve_tri_partial.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_1d_in.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_1d_in.e new file mode 100644 index 000000000000..1307f2811bf5 Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_1d_in.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_1d_s_in.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_1d_s_in.e new file mode 100644 index 000000000000..b919579df548 Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_1d_s_in.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_1d_s_sec_in.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_1d_s_sec_in.e new file mode 100644 index 000000000000..f9f582533e2b Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_1d_s_sec_in.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_1d_sec_in.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_1d_sec_in.e new file mode 100644 index 000000000000..3ad18df10dbd Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_1d_sec_in.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_quad8_1.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_quad8_1.e new file mode 100644 index 000000000000..97e9d084e052 Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_quad8_1.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_quad8_2.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_quad8_2.e new file mode 100644 index 000000000000..d90fb73776a0 Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_quad8_2.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_quad9_1.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_quad9_1.e new file mode 100644 index 000000000000..280f89f4bd8d Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_quad9_1.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_quad9_2.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_quad9_2.e new file mode 100644 index 000000000000..639fd5150ee6 Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_quad9_2.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_tri6.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_tri6.e new file mode 100644 index 000000000000..668f65fafdbe Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_tri6.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_tri7.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_tri7.e new file mode 100644 index 000000000000..2c5471c9ccfa Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/revolve_2d_tri7.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_hybrid.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_hybrid.e new file mode 100644 index 000000000000..2d9020a4772f Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_hybrid.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_quad.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_quad.e new file mode 100644 index 000000000000..a928ed9712f4 Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_quad.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_quad_oa.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_quad_oa.e new file mode 100644 index 000000000000..5d797cd41a9c Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_quad_oa.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_quad_oa_2.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_quad_oa_2.e new file mode 100644 index 000000000000..9f357c037b8b Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_quad_oa_2.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri.e new file mode 100644 index 000000000000..bd609b92f7ce Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri_-270.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri_-270.e new file mode 100644 index 000000000000..df9337b781e4 Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri_-270.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri_270.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri_270.e new file mode 100644 index 000000000000..7b024416cdee Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri_270.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri_oa.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri_oa.e new file mode 100644 index 000000000000..9744b803b5d6 Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri_oa.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri_oa_2.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri_oa_2.e new file mode 100644 index 000000000000..51324663c594 Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri_oa_2.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri_vp.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri_vp.e new file mode 100644 index 000000000000..8166c0ed009a Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/gold/simple_revolve_tri_vp.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/mesh_2d.e b/modules/reactor/test/tests/meshgenerators/revolve_generator/mesh_2d.e new file mode 100644 index 000000000000..b2a12ae083b8 Binary files /dev/null and b/modules/reactor/test/tests/meshgenerators/revolve_generator/mesh_2d.e differ diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/multi_revolve.i b/modules/reactor/test/tests/meshgenerators/revolve_generator/multi_revolve.i new file mode 100644 index 000000000000..504a3028fa2e --- /dev/null +++ b/modules/reactor/test/tests/meshgenerators/revolve_generator/multi_revolve.i @@ -0,0 +1,22 @@ +[Mesh] + [shg] + type = SimpleHexagonGenerator + hexagon_size = 1.0 + element_type = HYBRID + block_id = '100 200' + block_name = 'block_tri block_quad' + external_boundary_id = 300 + external_boundary_name = 'external_side' + radial_intervals = 2 + [] + [rg] + type = RevolveGenerator + input = shg + axis_point = '5.0 0.0 0.0' + axis_direction = '0.0 1.0 0.0' + nums_azimuthal_intervals = '6 6' + revolving_angles = '150 210' + subdomain_swaps = ' ;100 300 200 400' + boundary_swaps = '300 500;10000 4000' + [] +[] diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/revolve_1d.i b/modules/reactor/test/tests/meshgenerators/revolve_generator/revolve_1d.i new file mode 100644 index 000000000000..d97b62af061e --- /dev/null +++ b/modules/reactor/test/tests/meshgenerators/revolve_generator/revolve_1d.i @@ -0,0 +1,14 @@ +[Mesh] + [gmg] + type = GeneratedMeshGenerator + dim = 1 + nx = 2 + [] + [rg] + type = RevolveGenerator + input = gmg + axis_point = '0.0 0.0 0.0' + axis_direction = '0.0 1.0 0.0' + nums_azimuthal_intervals = 6 + [] +[] diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/revolve_2d.i b/modules/reactor/test/tests/meshgenerators/revolve_generator/revolve_2d.i new file mode 100644 index 000000000000..73b57ad62a16 --- /dev/null +++ b/modules/reactor/test/tests/meshgenerators/revolve_generator/revolve_2d.i @@ -0,0 +1,15 @@ +[Mesh] + [gmg] + type = GeneratedMeshGenerator + dim = 2 + nx = 2 + ny = 2 + [] + [rg] + type = RevolveGenerator + input = gmg + axis_point = '0.0 0.0 0.0' + axis_direction = '0.0 1.0 0.0' + nums_azimuthal_intervals = 6 + [] +[] diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/simple_revolve.i b/modules/reactor/test/tests/meshgenerators/revolve_generator/simple_revolve.i new file mode 100644 index 000000000000..bfb3cbd58f12 --- /dev/null +++ b/modules/reactor/test/tests/meshgenerators/revolve_generator/simple_revolve.i @@ -0,0 +1,17 @@ +[Mesh] + [shg] + type = SimpleHexagonGenerator + hexagon_size = 1.0 + block_id = 100 + block_name = hexagon + external_boundary_id = 300 + external_boundary_name = 'external_side' + [] + [rg] + type = RevolveGenerator + input = shg + axis_point = '5.0 0.0 0.0' + axis_direction = '0.0 1.0 0.0' + nums_azimuthal_intervals = 6 + [] +[] diff --git a/modules/reactor/test/tests/meshgenerators/revolve_generator/tests b/modules/reactor/test/tests/meshgenerators/revolve_generator/tests new file mode 100644 index 000000000000..315fc46a1e43 --- /dev/null +++ b/modules/reactor/test/tests/meshgenerators/revolve_generator/tests @@ -0,0 +1,280 @@ +[Tests] + design = 'meshgenerators/RevolveGenerator.md' + issues = '#27699' + [test] + requirement = 'The system shall ' + [simple_tri] + type = 'Exodiff' + input = 'simple_revolve.i' + cli_args = '--mesh-only simple_revolve_tri.e' + exodiff = 'simple_revolve_tri.e' + recover = false + detail = 'generate a 3D mesh by fully revolving a simple 2D mesh with TRI3 elements' + [] + [simple_tri6] + type = 'Exodiff' + input = 'revolve_2d.i' + cli_args = '--mesh-only revolve_2d_tri6.e + Mesh/gmg/elem_type=TRI6' + exodiff = 'revolve_2d_tri6.e' + recover = false + detail = 'generate a 3D mesh by fully revolving a simple 2D mesh with TRI6 elements' + [] + [simple_tri7] + type = 'Exodiff' + input = 'revolve_2d.i' + cli_args = '--mesh-only revolve_2d_tri7.e + Mesh/gmg/elem_type=TRI7' + exodiff = 'revolve_2d_tri7.e' + recover = false + detail = 'generate a 3D mesh by fully revolving a simple 2D mesh with TRI7 elements' + [] + [simple_quad8_1] + type = 'Exodiff' + input = 'revolve_2d.i' + cli_args = '--mesh-only revolve_2d_quad8_1.e + Mesh/gmg/elem_type=QUAD8' + exodiff = 'revolve_2d_quad8_1.e' + recover = false + detail = 'generate a 3D mesh by fully revolving a simple 2D mesh with QUAD8 elements along an axis overlapped with edges of some elements' + [] + [simple_quad8_2] + type = 'Exodiff' + input = 'revolve_2d.i' + cli_args = '--mesh-only revolve_2d_quad8_2.e + Mesh/gmg/elem_type=QUAD8 + Mesh/rg/axis_direction="-1.0 1.0 0.0"' + exodiff = 'revolve_2d_quad8_2.e' + recover = false + detail = 'generate a 3D mesh by fully revolving a simple 2D mesh with QUAD8 elements along an axis overlapped with a node of the 2D mesh' + [] + [simple_quad9_1] + type = 'Exodiff' + input = 'revolve_2d.i' + cli_args = '--mesh-only revolve_2d_quad9_1.e + Mesh/gmg/elem_type=QUAD9' + exodiff = 'revolve_2d_quad9_1.e' + recover = false + detail = 'generate a 3D mesh by fully revolving a simple 2D mesh with QUAD9 elements along an axis overlapped with edges of some elements' + [] + [simple_quad9_2] + type = 'Exodiff' + input = 'revolve_2d.i' + cli_args = '--mesh-only revolve_2d_quad9_2.e + Mesh/gmg/elem_type=QUAD9 + Mesh/rg/axis_direction="-1.0 1.0 0.0"' + exodiff = 'revolve_2d_quad9_2.e' + recover = false + detail = 'generate a 3D mesh by fully revolving a simple 2D mesh with QUAD9 elements along an axis overlapped with a node of the 2D mesh' + [] + [simple_tri_270] + type = 'Exodiff' + input = 'simple_revolve.i' + cli_args = '--mesh-only simple_revolve_tri_270.e + Mesh/rg/revolving_angles=270.0 + Mesh/rg/start_boundary=123 + Mesh/rg/end_boundary=456 + Mesh/rg/preserve_volumes=true' + exodiff = 'simple_revolve_tri_270.e' + recover = false + detail = 'generate a 3D mesh by partially revolving a simple 2D mesh with TRI3 elements clockwise' + [] + [simple_tri_-270] + type = 'Exodiff' + input = 'simple_revolve.i' + cli_args = '--mesh-only simple_revolve_tri_-270.e + Mesh/rg/revolving_angles=270.0 + Mesh/rg/clockwise=false' + exodiff = 'simple_revolve_tri_-270.e' + recover = false + detail = 'generate a 3D mesh by partially revolving a simple 2D mesh with TRI3 elements counterclockwise' + [] + [simple_tri_vol_preserve] + type = 'Exodiff' + input = 'simple_revolve.i' + cli_args = '--mesh-only simple_revolve_tri_vp.e + Mesh/rg/preserve_volumes=true' + exodiff = 'simple_revolve_tri_vp.e' + recover = false + detail = 'generate a 3D mesh by fully revolving a simple 2D mesh with TRI3 elements using radius correction to preserve volumes' + [] + [simple_tri_on_axis_1] + type = 'Exodiff' + input = 'simple_revolve.i' + cli_args = '--mesh-only simple_revolve_tri_oa.e + Mesh/rg/axis_point="0.0 1.15470053838 0.0" + Mesh/rg/axis_direction="1.0 0.0 0.0"' + exodiff = 'simple_revolve_tri_oa.e' + recover = false + detail = 'generate a 3D mesh by fully revolving a simple 2D mesh with TRI3 elements along an axis overlapped with a node of the 2D mesh' + [] + [simple_tri_on_axis_2] + type = 'Exodiff' + input = 'simple_revolve.i' + cli_args = '--mesh-only simple_revolve_tri_oa_2.e + Mesh/rg/axis_point="-1.0 0.0 0.0" + Mesh/rg/axis_direction="0.0 1.0 0.0"' + exodiff = 'simple_revolve_tri_oa_2.e' + recover = false + detail = 'generate a 3D mesh by fully revolving a simple 2D mesh with TRI3 elements along an axis overlapped with an edge of an element of the 2D mesh' + [] + [simple_quad] + type = 'Exodiff' + input = 'simple_revolve.i' + cli_args = '--mesh-only simple_revolve_quad.e + Mesh/shg/element_type=QUAD' + exodiff = 'simple_revolve_quad.e' + recover = false + detail = 'generate a 3D mesh by fully revolving a simple 2D mesh with QUAD4 elements' + [] + [simple_quad_on_axis_1] + type = 'Exodiff' + input = 'simple_revolve.i' + cli_args = '--mesh-only simple_revolve_quad_oa.e + Mesh/rg/axis_point="0.0 1.15470053838 0.0" + Mesh/rg/axis_direction="1.0 0.0 0.0" + Mesh/shg/element_type=QUAD' + exodiff = 'simple_revolve_quad_oa.e' + recover = false + detail = 'generate a 3D mesh by fully revolving a simple 2D mesh with QUAD4 elements along an axis overlapped with a node of the 2D mesh' + [] + [simple_quad_on_axis_2] + type = 'Exodiff' + input = 'simple_revolve.i' + cli_args = '--mesh-only simple_revolve_quad_oa_2.e + Mesh/rg/axis_point="-1.0 0.0 0.0" + Mesh/rg/axis_direction="0.0 1.0 0.0" + Mesh/shg/element_type=QUAD' + exodiff = 'simple_revolve_quad_oa_2.e' + recover = false + detail = 'generate a 3D mesh by fully revolving a simple 2D mesh with QUAD4 elements along an axis overlapped with an edge of an element of the 2D mesh' + [] + [simple_hybrid] + type = 'Exodiff' + input = 'simple_revolve.i' + cli_args = '--mesh-only simple_revolve_hybrid.e + Mesh/shg/element_type=HYBRID + Mesh/shg/block_id="100 200" + Mesh/shg/block_name="block_tri block_quad" + Mesh/shg/radial_intervals=2' + exodiff = 'simple_revolve_hybrid.e' + recover = false + detail = 'generate a 3D mesh by fully revolving a simple 2D mesh with mixed TRI3 and QUAD4 elements' + [] + [multi_tri] + type = 'Exodiff' + input = 'multi_revolve.i' + cli_args = '--mesh-only multi_revolve_tri.e' + exodiff = 'multi_revolve_tri.e' + recover = false + detail = 'generate a 3D mesh that consists multiple azimuthal sections by fully revolving a simple 2D mesh with TRI3 elements' + [] + [multi_tri_partial] + type = 'Exodiff' + input = 'multi_revolve.i' + cli_args = '--mesh-only multi_revolve_tri_partial.e + Mesh/rg/revolving_angles="120 180"' + exodiff = 'multi_revolve_tri_partial.e' + recover = false + detail = 'generate a 3D mesh that consists multiple azimuthal sections by partially revolving a simple 2D mesh with TRI3 elements' + [] + [ei_swap] + type = 'Exodiff' + input = 'ei_swap.i' + exodiff = 'ei_swap_out.e' + recover = false + detail = 'generate a 3D mesh by revolving a 2D mesh with the original element extra integers retained and swapped' + [] + [revolve_1d] + type = 'Exodiff' + input = 'revolve_1d.i' + cli_args = '--mesh-only revolve_1d_in.e' + exodiff = 'revolve_1d_in.e' + recover = false + detail = 'generate a 2D mesh by revolving a simple 1D mesh with EDGE2 elements' + [] + [revolve_1d_single] + type = 'Exodiff' + input = 'revolve_1d.i' + cli_args = '--mesh-only revolve_1d_s_in.e + Mesh/gmg/nx=1' + exodiff = 'revolve_1d_s_in.e' + recover = false + detail = 'generate a 2D mesh by revolving a simple 1D mesh with a single EDGE2 element' + # Only one element + max_parallel = 1 + [] + [revolve_1d_second] + type = 'Exodiff' + input = 'revolve_1d.i' + cli_args = '--mesh-only revolve_1d_sec_in.e + Mesh/gmg/elem_type=EDGE3' + exodiff = 'revolve_1d_sec_in.e' + recover = false + detail = 'generate a 2D mesh by revolving a simple 1D mesh with EDGE3 elements' + [] + [revolve_1d_single_second] + type = 'Exodiff' + input = 'revolve_1d.i' + cli_args = '--mesh-only revolve_1d_s_sec_in.e + Mesh/gmg/nx=1 + Mesh/gmg/elem_type=EDGE3' + exodiff = 'revolve_1d_s_sec_in.e' + recover = false + detail = 'generate a 2D mesh by revolving a simple 1D mesh with a single EDGE3 element' + # Only one element + max_parallel = 1 + [] + [] + [error] + requirement = "The system shall throw an error if " + [err_3d_input] + type = 'RunException' + input = 'revolve_2d.i' + cli_args = '--mesh-only err_revolve_2d.e + Mesh/gmg/dim=3' + expect_err = 'This mesh generator only works for 1D and 2D input meshes.' + detail = "if input mesh has an inappropriate dimension." + [] + [err_1d_nonverticle] + type = 'RunException' + input = 'revolve_1d.i' + cli_args = '--mesh-only err_revolve_1d.e + Mesh/rg/axis_direction="-1.0 1.0 0.0"' + expect_err = 'The 1D input mesh is not perpendicular to the rotation axis' + detail = "if input 1D mesh is not perpendicular to the rotation axis." + [] + [err_input_centroid_on_axis] + type = 'RunException' + input = 'revolve_1d.i' + cli_args = '--mesh-only err_revolve_1d.e + Mesh/rg/axis_point="0.5 0.0 0.0"' + expect_err = 'The input mesh is either across the axis or overlapped with the axis' + detail = "if the input 1D mesh has its centroid overlapped with the rotation axis." + [] + [err_1d_input_across_axis] + type = 'RunException' + input = 'revolve_1d.i' + cli_args = '--mesh-only err_revolve_1d.e + Mesh/rg/axis_point="0.3 0.0 0.0"' + expect_err = 'The input mesh is across the axis' + detail = "if the input 1D mesh is across the rotation axis." + [] + [err_2d_input_across_axis] + type = 'RunException' + input = 'revolve_2d.i' + cli_args = '--mesh-only err_revolve_2d.e + Mesh/rg/axis_point="0.3 0.0 0.0"' + expect_err = 'The input mesh is across the axis' + detail = "if the input 2D mesh is across the rotation axis." + [] + [err_input_off_plane] + type = 'RunException' + input = 'revolve_1d.i' + cli_args = '--mesh-only err_revolve_1d.e + Mesh/rg/axis_point="0.0 0.0 1.0"' + expect_err = 'The input mesh is not in the same plane with the rotation axis' + detail = "the input 1D mesh is not coplanar to the rotation axis." + [] + [] +[]