https://mooseframework.inl.gov
Loading...
Searching...
No Matches
Classes | Functions
MooseMeshElementConversionUtils Namespace Reference

Classes

struct  BCTupleKeyComp
 

Functions

void hexElemSplitter (MeshBase &mesh, const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, std::vector< dof_id_type > &converted_elems_ids)
 Split a HEX8 element into six TET4 elements.
 
void pyramidElemSplitter (MeshBase &mesh, const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, std::vector< dof_id_type > &converted_elems_ids)
 Split a PYRAMID5 element into two TET4 elements.
 
void prismElemSplitter (MeshBase &mesh, const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, std::vector< dof_id_type > &converted_elems_ids)
 Split a PRISM6 element into three TET4 elements.
 
void polyhedronElemSplitter (MeshBase &mesh, const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, std::vector< dof_id_type > &converted_elems_ids)
 Split a polyhedron element into n_sides TET4 elements.
 
void nodeRotationHEX8 (const unsigned int min_id_index, const unsigned int sec_min_pos, std::vector< unsigned int > &face_rotation, std::vector< unsigned int > &node_rotation)
 Rotate a HEX8 element's nodes to ensure that the node with the minimum id is the first node; and the node among its three neighboring nodes with the minimum id is the second node.
 
std::vector< unsigned intneighborNodeIndicesHEX8 (unsigned int min_id_index)
 Calculate the indices (within the element nodes) of the three neighboring nodes of a node in a HEX8 element.
 
void hexNodesToTetNodesDeterminer (std::vector< const Node * > &hex_nodes, std::vector< std::vector< unsigned int > > &rotated_tet_face_indices, std::vector< std::vector< const Node * > > &tet_nodes_list)
 For a vector of rotated nodes that can form a HEX8 element, create a vector of four-node sets that can form TET4 elements to replace the original HEX8 element.
 
std::vector< bool > quadFaceDiagonalDirectionsHex (const std::vector< const Node * > &hex_nodes)
 For a HEX8 element, determine the direction of the diagonal line of each face that involves the node with the minimum id of that face.
 
bool quadFaceDiagonalDirection (const std::vector< const Node * > &quad_nodes)
 For a QUAD4 element, determine the direction of the diagonal line that involves the node with the minimum id of that element.
 
std::vector< std::vector< unsigned int > > tetNodesForHex (const std::vector< bool > diagonal_directions, std::vector< std::vector< unsigned int > > &tet_face_indices)
 Creates sets of four nodes indices that can form TET4 elements to replace the original HEX8 element.
 
void nodeRotationPRISM6 (unsigned int min_id_index, std::vector< unsigned int > &face_rotation, std::vector< unsigned int > &node_rotation)
 Rotate a PRISM6 element nodes to ensure that the node with the minimum id is the first node.
 
void prismNodesToTetNodesDeterminer (std::vector< const Node * > &prism_nodes, std::vector< std::vector< unsigned int > > &rotated_tet_face_indices, std::vector< std::vector< const Node * > > &tet_nodes_list)
 For a rotated nodes that can form a PRISM6 element, create a series of four-node set that can form TET4 elements to replace the original PRISM6 element.
 
std::vector< std::vector< unsigned int > > tetNodesForPrism (const bool diagonal_direction, std::vector< std::vector< unsigned int > > &tet_face_indices)
 Creates sets of four nodes indices that can form TET4 elements to replace the original PRISM6 element.
 
void nodeRotationPYRAMID5 (unsigned int min_id_index, std::vector< unsigned int > &face_rotation, std::vector< unsigned int > &node_rotation)
 Rotate a PYRAMID5 element nodes to ensure that the node with the minimum id is the first node for the bottom face.
 
void pyramidNodesToTetNodesDeterminer (std::vector< const Node * > &pyramid_nodes, std::vector< std::vector< unsigned int > > &rotated_tet_face_indices, std::vector< std::vector< const Node * > > &tet_nodes_list)
 For a rotated nodes that can form a PYRAMID5 element, create a series of four-node set that can form TET4 elements to replace the original PYRAMID5 element.
 
void convert3DMeshToAllTet4 (MeshBase &mesh, const std::vector< std::pair< dof_id_type, bool > > &elems_to_process, std::vector< dof_id_type > &converted_elems_ids_to_track, const subdomain_id_type block_id_to_remove, const bool delete_block_to_remove)
 Convert all the elements in a 3D mesh, consisting of only linear elements, into TET4 elements.
 
void convert3DMeshToAllTet4 (MeshBase &mesh)
 Convert all the elements in a 3D mesh consisting of only linear elements into TET4 elements.
 
void elementBoundaryInfoCollector (const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, const unsigned short n_elem_sides, std::vector< std::vector< boundary_id_type > > &elem_side_list)
 Collect the boundary information of the given element in a mesh.
 
void convertElem (MeshBase &mesh, const dof_id_type &elem_id, const std::vector< unsigned int > &side_indices, const std::vector< std::vector< boundary_id_type > > &elem_side_info, const SubdomainID &subdomain_id_shift_base)
 Convert the element to an element with TRI3 side-elements on the user-specified sides by modifying the mesh.
 
void convertHex8Elem (MeshBase &mesh, const dof_id_type &elem_id, const std::vector< unsigned int > &side_indices, const std::vector< std::vector< boundary_id_type > > &elem_side_info, const SubdomainID &subdomain_id_shift_base)
 Convert a HEX8 element to elements with TRI3 surfaces on the given original QUAD4 side(s).
 
void createUnitPyramid5FromHex8 (MeshBase &mesh, const dof_id_type &elem_id, const unsigned int &side_index, const Node *new_node, const std::vector< boundary_id_type > &side_info, const SubdomainID &subdomain_id_shift_base)
 Create one PYRAMID5 element based on a side and the centroid of the HEX8 element.
 
void createUnitTet4FromHex8 (MeshBase &mesh, const dof_id_type &elem_id, const unsigned int &side_index, const Node *new_node, const std::vector< boundary_id_type > &side_info, const SubdomainID &subdomain_id_shift_base)
 Create two TET4 elements based on a side and the centroid of the HEX8 element.
 
void convertPrism6Elem (MeshBase &mesh, const dof_id_type &elem_id, const std::vector< unsigned int > &side_indices, const std::vector< std::vector< boundary_id_type > > &elem_side_info, const SubdomainID &subdomain_id_shift_base)
 Convert a PRISM6 element to elements with TRI3 surfaces on the given original QUAD4 side(s).
 
void createUnitTet4FromPrism6 (MeshBase &mesh, const dof_id_type &elem_id, const unsigned int &side_index, const Node *new_node, const std::vector< boundary_id_type > &side_info, const SubdomainID &subdomain_id_shift_base)
 Create one or two TET4 elements based on a side and the centroid of the PRISM6 element.
 
void createUnitPyramid5FromPrism6 (MeshBase &mesh, const dof_id_type &elem_id, const unsigned int &side_index, const Node *new_node, const std::vector< boundary_id_type > &side_info, const SubdomainID &subdomain_id_shift_base)
 Create a PYRAMID5 element opposite the sides converted to tets and the centroid of the PRISM6 element.
 
void convertPyramid5Elem (MeshBase &mesh, const dof_id_type &elem_id, const std::vector< std::vector< boundary_id_type > > &elem_side_info, const SubdomainID &subdomain_id_shift_base)
 Convert a PYRAMID5 element to elements with TRI3 surfaces on the original QUAD4 side.
 
void retainEEID (MeshBase &mesh, const dof_id_type &elem_id, Elem *new_elem_ptr)
 Retain the extra integer of the original element in a new element.
 
void transitionLayerGenerator (MeshBase &mesh, const std::vector< BoundaryName > &boundary_names, const unsigned int conversion_element_layer_number, const bool external_boundaries_checking)
 Generate a transition layer of elements with TRI3 surfaces on the given boundaries.
 
void assignConvertedElementsSubdomainNameSuffix (MeshBase &mesh, const std::set< subdomain_id_type > &original_subdomain_ids, const subdomain_id_type sid_shift_base, const SubdomainName &tet_suffix, const SubdomainName &pyramid_suffix)
 Assign a subdomain name suffix to the converted elements created during transition layer generation.
 
void hexElemSplitter (MeshBase &mesh, const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, std::vector< dof_id_type > &converted_elems_ids)
 
void prismElemSplitter (MeshBase &mesh, const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, std::vector< dof_id_type > &converted_elems_ids)
 
void pyramidElemSplitter (MeshBase &mesh, const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, std::vector< dof_id_type > &converted_elems_ids)
 
void polyhedronElemSplitter (MeshBase &mesh, const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, std::vector< dof_id_type > &converted_elems_ids)
 
void hexNodesToTetNodesDeterminer (std::vector< const Node * > &hex_nodes, std::vector< std::vector< unsigned int > > &rotated_tet_face_indices, std::vector< std::vector< const Node * > > &tet_nodes_list)
 
std::vector< bool > quadFaceDiagonalDirectionsHex (const std::vector< const Node * > &hex_nodes)
 
bool quadFaceDiagonalDirection (const std::vector< const Node * > &quad_nodes)
 
void prismNodesToTetNodesDeterminer (std::vector< const Node * > &prism_nodes, std::vector< std::vector< unsigned int > > &rotated_tet_face_indices, std::vector< std::vector< const Node * > > &tet_nodes_list)
 
void pyramidNodesToTetNodesDeterminer (std::vector< const Node * > &pyramid_nodes, std::vector< std::vector< unsigned int > > &rotated_tet_face_indices, std::vector< std::vector< const Node * > > &tet_nodes_list)
 
void convert3DMeshToAllTet4 (MeshBase &mesh, const std::vector< std::pair< dof_id_type, bool > > &elems_to_process, std::vector< dof_id_type > &converted_elems_ids_to_track, const subdomain_id_type block_id_to_remove, const bool delete_block_to_remove)
 
void convert3DMeshToAllTet4 (MeshBase &mesh)
 
void elementBoundaryInfoCollector (const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, const unsigned short n_elem_sides, std::vector< std::vector< boundary_id_type > > &elem_side_list)
 
void convertElem (MeshBase &mesh, const dof_id_type &elem_id, const std::vector< unsigned int > &side_indices, const std::vector< std::vector< boundary_id_type > > &elem_side_info, const SubdomainID &subdomain_id_shift_base)
 
void convertHex8Elem (MeshBase &mesh, const dof_id_type &elem_id, const std::vector< unsigned int > &side_indices, const std::vector< std::vector< boundary_id_type > > &elem_side_info, const SubdomainID &subdomain_id_shift_base)
 
void createUnitPyramid5FromHex8 (MeshBase &mesh, const dof_id_type &elem_id, const unsigned int &side_index, const Node *new_node, const std::vector< boundary_id_type > &side_info, const SubdomainID &subdomain_id_shift_base)
 
void createUnitTet4FromHex8 (MeshBase &mesh, const dof_id_type &elem_id, const unsigned int &side_index, const Node *new_node, const std::vector< boundary_id_type > &side_info, const SubdomainID &subdomain_id_shift_base)
 
void convertPrism6Elem (MeshBase &mesh, const dof_id_type &elem_id, const std::vector< unsigned int > &side_indices, const std::vector< std::vector< boundary_id_type > > &elem_side_info, const SubdomainID &subdomain_id_shift_base)
 
void createUnitTet4FromPrism6 (MeshBase &mesh, const dof_id_type &elem_id, const unsigned int &side_index, const Node *new_node, const std::vector< boundary_id_type > &side_info, const SubdomainID &subdomain_id_shift_base)
 
void createUnitPyramid5FromPrism6 (MeshBase &mesh, const dof_id_type &elem_id, const unsigned int &side_index, const Node *new_node, const std::vector< boundary_id_type > &side_info, const SubdomainID &subdomain_id_shift_base)
 
void convertPyramid5Elem (MeshBase &mesh, const dof_id_type &elem_id, const std::vector< std::vector< boundary_id_type > > &elem_side_info, const SubdomainID &subdomain_id_shift_base)
 
void retainEEID (MeshBase &mesh, const dof_id_type &elem_id, Elem *new_elem_ptr)
 
void transitionLayerGenerator (MeshBase &mesh, const std::vector< BoundaryName > &boundary_names, const unsigned int conversion_element_layer_number, const bool external_boundaries_checking)
 
void assignConvertedElementsSubdomainNameSuffix (MeshBase &mesh, const std::set< subdomain_id_type > &original_subdomain_ids, const subdomain_id_type sid_shift_base, const SubdomainName &tet_suffix, const SubdomainName &pyramid_suffix)
 

Function Documentation

◆ assignConvertedElementsSubdomainNameSuffix() [1/2]

void MooseMeshElementConversionUtils::assignConvertedElementsSubdomainNameSuffix ( MeshBase &  mesh,
const std::set< subdomain_id_type > &  original_subdomain_ids,
const subdomain_id_type  sid_shift_base,
const SubdomainName &  tet_suffix,
const SubdomainName &  pyramid_suffix 
)

Assign a subdomain name suffix to the converted elements created during transition layer generation.

Parameters
meshThe mesh to be modified
original_subdomain_idsA set of original subdomain IDs of the mesh before conversion
sid_shift_baseThe base subdomain ID to shift the original elements because of the element type change
tet_suffixThe suffix to be added to the subdomain names of the converted TET4 elements
pyramid_suffixThe suffix to be added to the subdomain names of the converted PYRAMID5 elements

Referenced by BoundaryElementConversionGenerator::generate(), and CutMeshByLevelSetGeneratorBase::generate().

◆ assignConvertedElementsSubdomainNameSuffix() [2/2]

void MooseMeshElementConversionUtils::assignConvertedElementsSubdomainNameSuffix ( MeshBase mesh,
const std::set< subdomain_id_type > &  original_subdomain_ids,
const subdomain_id_type  sid_shift_base,
const SubdomainName &  tet_suffix,
const SubdomainName &  pyramid_suffix 
)

Definition at line 1304 of file MooseMeshElementConversionUtils.C.

1310{
1311 // If we have an unprepared mesh, we at least need its element
1312 // caches prepared for subdomain_ids
1313 if (!mesh.preparation().has_cached_elem_data)
1315
1316 for (const auto & subdomain_id : original_subdomain_ids)
1317 {
1318 if (MooseMeshUtils::hasSubdomainID(mesh, subdomain_id + sid_shift_base))
1319 {
1320 const SubdomainName new_name =
1321 (mesh.subdomain_name(subdomain_id).empty() ? std::to_string(subdomain_id)
1322 : mesh.subdomain_name(subdomain_id)) +
1323 '_' + tet_suffix;
1324 if (MooseMeshUtils::hasSubdomainName(mesh, new_name))
1325 throw MooseException(
1326 "This suffix for converted TET4 elements results in a subdomain name, " + new_name +
1327 ", that already exists in the mesh. Please choose a different suffix.");
1328 mesh.set_subdomain_name(subdomain_id + sid_shift_base, new_name, true);
1329 }
1330 if (MooseMeshUtils::hasSubdomainID(mesh, subdomain_id + 2 * sid_shift_base))
1331 {
1332 const SubdomainName new_name =
1333 (mesh.subdomain_name(subdomain_id).empty() ? std::to_string(subdomain_id)
1334 : mesh.subdomain_name(subdomain_id)) +
1335 '_' + pyramid_suffix;
1336 if (MooseMeshUtils::hasSubdomainName(mesh, new_name))
1337 throw MooseException(
1338 "This suffix for converted PYRAMID5 elements results in a subdomain name, " + new_name +
1339 ", that already exists in the mesh. Please choose a different suffix.");
1340 mesh.set_subdomain_name(subdomain_id + 2 * sid_shift_base, new_name, true);
1341 }
1342 }
1343}
Provides a way for users to bail out of the current solve.
void set_subdomain_name(subdomain_id_type id, const std::string &name, bool synchronous=false)
Preparation preparation() const
std::string & subdomain_name(subdomain_id_type id)
MeshBase & mesh
bool hasSubdomainName(const MeshBase &input_mesh, const SubdomainName &name)
Whether a particular subdomain name exists in the mesh.
bool hasSubdomainID(const MeshBase &input_mesh, const SubdomainID &id)
Whether a particular subdomain ID exists in the mesh.

◆ convert3DMeshToAllTet4() [1/4]

void MooseMeshElementConversionUtils::convert3DMeshToAllTet4 ( MeshBase &  mesh)

Convert all the elements in a 3D mesh consisting of only linear elements into TET4 elements.

Parameters
meshThe mesh to be converted

◆ convert3DMeshToAllTet4() [2/4]

void MooseMeshElementConversionUtils::convert3DMeshToAllTet4 ( MeshBase mesh)

Definition at line 719 of file MooseMeshElementConversionUtils.C.

720{
721 mooseAssert(mesh.is_serial(),
722 "This method only supports serial meshes. If you are calling this method with a "
723 "distributed mesh, please serialize it first.");
724 // Subdomain ID for new utility blocks must be new
725 std::set<subdomain_id_type> subdomain_ids_set;
726 mesh.subdomain_ids(subdomain_ids_set);
727 const subdomain_id_type max_subdomain_id = *subdomain_ids_set.rbegin();
728 const subdomain_id_type block_id_to_remove = max_subdomain_id + 1;
729 std::vector<std::pair<dof_id_type, bool>> original_elems;
730
731 for (auto elem_it = mesh.active_elements_begin(); elem_it != mesh.active_elements_end();
732 elem_it++)
733 {
734 if ((*elem_it)->default_order() != Order::FIRST)
735 mooseError("Only first order elements are supported for cutting.");
736 original_elems.push_back(std::make_pair((*elem_it)->id(), false));
737 }
738
739 std::vector<dof_id_type> converted_elems_ids_to_track;
740
741 convert3DMeshToAllTet4(
742 mesh, original_elems, converted_elems_ids_to_track, block_id_to_remove, true);
743}
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
virtual bool is_serial() const
void subdomain_ids(std::set< subdomain_id_type > &ids, const bool global=true) const

◆ convert3DMeshToAllTet4() [3/4]

void MooseMeshElementConversionUtils::convert3DMeshToAllTet4 ( MeshBase &  mesh,
const std::vector< std::pair< dof_id_type, bool > > &  elems_to_process,
std::vector< dof_id_type > &  converted_elems_ids_to_track,
const subdomain_id_type  block_id_to_remove,
const bool  delete_block_to_remove 
)

Convert all the elements in a 3D mesh, consisting of only linear elements, into TET4 elements.

Parameters
meshThe mesh to be converted
elems_to_processA vector of pairs of element ids and a bool indicating whether the element needs to be fully retained or will be further processed in the following procedures
converted_elems_ids_to_trackA vector of element ids that need to be tracked for being further processed in the following procedures
block_id_to_removeThe id of a new subdomain in the mesh containing all the elements to be removed
delete_block_to_removeA bool indicating whether the block to be removed will be deleted in this method

Referenced by convert3DMeshToAllTet4(), CutMeshByLevelSetGeneratorBase::generate(), ElementsToTetrahedronsConverter::generate(), and transitionLayerGenerator().

◆ convert3DMeshToAllTet4() [4/4]

void MooseMeshElementConversionUtils::convert3DMeshToAllTet4 ( MeshBase mesh,
const std::vector< std::pair< dof_id_type, bool > > &  elems_to_process,
std::vector< dof_id_type > &  converted_elems_ids_to_track,
const subdomain_id_type  block_id_to_remove,
const bool  delete_block_to_remove 
)

Definition at line 646 of file MooseMeshElementConversionUtils.C.

651{
652 mooseAssert(mesh.is_serial(),
653 "This method only supports serial meshes. If you are calling this method with a "
654 "distributed mesh, please serialize it first.");
655 std::vector<dof_id_type> converted_elems_ids_to_retain;
656 // Build boundary information of the mesh
657 BoundaryInfo & boundary_info = mesh.get_boundary_info();
658 const auto bdry_side_list = boundary_info.build_side_list();
659 for (const auto & elem_to_process : elems_to_process)
660 {
661 switch (mesh.elem_ptr(elem_to_process.first)->type())
662 {
663 case ElemType::HEX8:
664 hexElemSplitter(mesh,
665 bdry_side_list,
666 elem_to_process.first,
667 elem_to_process.second ? converted_elems_ids_to_track
668 : converted_elems_ids_to_retain);
669 mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
670 break;
671 case ElemType::PYRAMID5:
673 bdry_side_list,
674 elem_to_process.first,
675 elem_to_process.second ? converted_elems_ids_to_track
676 : converted_elems_ids_to_retain);
677 mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
678 break;
679 case ElemType::PRISM6:
681 bdry_side_list,
682 elem_to_process.first,
683 elem_to_process.second ? converted_elems_ids_to_track
684 : converted_elems_ids_to_retain);
685 mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
686 break;
687 case ElemType::C0POLYHEDRON:
689 bdry_side_list,
690 elem_to_process.first,
691 elem_to_process.second ? converted_elems_ids_to_track
692 : converted_elems_ids_to_retain);
693 mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
694 break;
695 case ElemType::TET4:
696 if (elem_to_process.second)
697 converted_elems_ids_to_track.push_back(elem_to_process.first);
698 else
699 converted_elems_ids_to_retain.push_back(elem_to_process.first);
700 break;
701 default:
702 mooseError("Unexpected element type.");
703 }
704 }
705
706 if (delete_block_to_remove)
707 {
708 for (auto elem_it = mesh.active_subdomain_elements_begin(block_id_to_remove);
709 elem_it != mesh.active_subdomain_elements_end(block_id_to_remove);
710 elem_it++)
711 mesh.delete_elem(*elem_it);
712
713 mesh.contract();
715 }
716}
std::vector< BCTuple > build_side_list(BCTupleSortBy sort_by=BCTupleSortBy::ELEM_ID) const
subdomain_id_type subdomain_id() const
virtual ElemType type() const=0
virtual bool contract()=0
const BoundaryInfo & get_boundary_info() const
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
virtual void delete_elem(Elem *e)=0
virtual const Elem * elem_ptr(const dof_id_type i) const=0
void hexElemSplitter(MeshBase &mesh, const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, std::vector< dof_id_type > &converted_elems_ids)
Split a HEX8 element into six TET4 elements.
void prismElemSplitter(MeshBase &mesh, const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, std::vector< dof_id_type > &converted_elems_ids)
Split a PRISM6 element into three TET4 elements.
void polyhedronElemSplitter(MeshBase &mesh, const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, std::vector< dof_id_type > &converted_elems_ids)
Split a polyhedron element into n_sides TET4 elements.
void pyramidElemSplitter(MeshBase &mesh, const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, std::vector< dof_id_type > &converted_elems_ids)
Split a PYRAMID5 element into two TET4 elements.

◆ convertElem() [1/2]

void MooseMeshElementConversionUtils::convertElem ( MeshBase &  mesh,
const dof_id_type &  elem_id,
const std::vector< unsigned int > &  side_indices,
const std::vector< std::vector< boundary_id_type > > &  elem_side_info,
const SubdomainID subdomain_id_shift_base 
)

Convert the element to an element with TRI3 side-elements on the user-specified sides by modifying the mesh.

Parameters
meshThe mesh containing the element
elem_idThe ID of the element to be converted
side_indicesThe indices of the sides to be converted
elem_side_infoThe boundary IDs associated with the sides of the element
subdomain_id_shift_basethe reference id used to shift the subdomain ID for new elements

Referenced by CutMeshByLevelSetGeneratorBase::generate(), and transitionLayerGenerator().

◆ convertElem() [2/2]

void MooseMeshElementConversionUtils::convertElem ( MeshBase mesh,
const dof_id_type elem_id,
const std::vector< unsigned int > &  side_indices,
const std::vector< std::vector< boundary_id_type > > &  elem_side_info,
const SubdomainID subdomain_id_shift_base 
)

Definition at line 763 of file MooseMeshElementConversionUtils.C.

768{
769 const auto & elem_type = mesh.elem_ptr(elem_id)->type();
770 switch (elem_type)
771 {
772 case HEX8:
773 // HEX8 to PYRAMID5 (+2*subdomain_id_shift_base)
774 // HEX8 to TET4 (+subdomain_id_shift_base)
775 convertHex8Elem(mesh, elem_id, side_indices, elem_side_info, subdomain_id_shift_base);
776 break;
777 case PRISM6:
778 // PRISM6 to TET4 (+subdomain_id_shift_base)
779 // PRISM6 to PYRAMID5 (+2*subdomain_id_shift_base)
780 convertPrism6Elem(mesh, elem_id, side_indices, elem_side_info, subdomain_id_shift_base);
781 break;
782 case PYRAMID5:
783 // PYRAMID5 to TET4 (+subdomain_id_shift_base)
784 convertPyramid5Elem(mesh, elem_id, elem_side_info, subdomain_id_shift_base);
785 break;
786 default:
787 mooseAssert(false,
788 "The provided element type '" + std::to_string(elem_type) +
789 "' is not supported and is not supposed to be passed to this function. "
790 "Only HEX8, PRISM6 and PYRAMID5 are supported.");
791 }
792}
void convertHex8Elem(MeshBase &mesh, const dof_id_type &elem_id, const std::vector< unsigned int > &side_indices, const std::vector< std::vector< boundary_id_type > > &elem_side_info, const SubdomainID &subdomain_id_shift_base)
Convert a HEX8 element to elements with TRI3 surfaces on the given original QUAD4 side(s).
void convertPrism6Elem(MeshBase &mesh, const dof_id_type &elem_id, const std::vector< unsigned int > &side_indices, const std::vector< std::vector< boundary_id_type > > &elem_side_info, const SubdomainID &subdomain_id_shift_base)
Convert a PRISM6 element to elements with TRI3 surfaces on the given original QUAD4 side(s).
void convertPyramid5Elem(MeshBase &mesh, const dof_id_type &elem_id, const std::vector< std::vector< boundary_id_type > > &elem_side_info, const SubdomainID &subdomain_id_shift_base)
Convert a PYRAMID5 element to elements with TRI3 surfaces on the original QUAD4 side.

◆ convertHex8Elem() [1/2]

void MooseMeshElementConversionUtils::convertHex8Elem ( MeshBase &  mesh,
const dof_id_type &  elem_id,
const std::vector< unsigned int > &  side_indices,
const std::vector< std::vector< boundary_id_type > > &  elem_side_info,
const SubdomainID subdomain_id_shift_base 
)

Convert a HEX8 element to elements with TRI3 surfaces on the given original QUAD4 side(s).

Parameters
meshThe mesh containing the element
elem_idThe ID of the HEX8 element to be converted
side_indicesThe indices of the QUAD4 sides to be converted to TRI3 sides
elem_side_infoThe boundary IDs associated with the sides of the HEX8 element
subdomain_id_shift_basethe reference id used to shift the subdomain ID for new elements

Referenced by convertElem().

◆ convertHex8Elem() [2/2]

void MooseMeshElementConversionUtils::convertHex8Elem ( MeshBase mesh,
const dof_id_type elem_id,
const std::vector< unsigned int > &  side_indices,
const std::vector< std::vector< boundary_id_type > > &  elem_side_info,
const SubdomainID subdomain_id_shift_base 
)

Definition at line 795 of file MooseMeshElementConversionUtils.C.

800{
801 // We add a node at the centroid of the HEX8 element
802 // With this node, the HEX8 can be converted into 6 PYRAMID5 elements
803 // For the PYRAMID5 element at the 'side_indices', they can further be converted into 2 TET4
804 // elements
805 const Point elem_cent = mesh.elem_ptr(elem_id)->true_centroid();
806 auto new_node = mesh.add_point(elem_cent);
807 for (const auto & i_side : make_range(mesh.elem_ptr(elem_id)->n_sides()))
808 {
809 if (std::find(side_indices.begin(), side_indices.end(), i_side) != side_indices.end())
811 mesh, elem_id, i_side, new_node, elem_side_info[i_side], subdomain_id_shift_base);
812 else
814 mesh, elem_id, i_side, new_node, elem_side_info[i_side], subdomain_id_shift_base);
815 }
816}
virtual Point true_centroid() const
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
void createUnitTet4FromHex8(MeshBase &mesh, const dof_id_type &elem_id, const unsigned int &side_index, const Node *new_node, const std::vector< boundary_id_type > &side_info, const SubdomainID &subdomain_id_shift_base)
Create two TET4 elements based on a side and the centroid of the HEX8 element.
void createUnitPyramid5FromHex8(MeshBase &mesh, const dof_id_type &elem_id, const unsigned int &side_index, const Node *new_node, const std::vector< boundary_id_type > &side_info, const SubdomainID &subdomain_id_shift_base)
Create one PYRAMID5 element based on a side and the centroid of the HEX8 element.
IntRange< T > make_range(T beg, T end)

◆ convertPrism6Elem() [1/2]

void MooseMeshElementConversionUtils::convertPrism6Elem ( MeshBase &  mesh,
const dof_id_type &  elem_id,
const std::vector< unsigned int > &  side_indices,
const std::vector< std::vector< boundary_id_type > > &  elem_side_info,
const SubdomainID subdomain_id_shift_base 
)

Convert a PRISM6 element to elements with TRI3 surfaces on the given original QUAD4 side(s).

Parameters
meshThe mesh containing the element
elem_idThe ID of the PRISM6 element to be converted
side_indicesThe indices of the QUAD sides to be converted to TRI3 sides
elem_side_infoThe boundary IDs associated with the sides of the PRISM6 element
subdomain_id_shift_basethe reference id used to shift the subdomain ID for new elements

Referenced by convertElem().

◆ convertPrism6Elem() [2/2]

void MooseMeshElementConversionUtils::convertPrism6Elem ( MeshBase mesh,
const dof_id_type elem_id,
const std::vector< unsigned int > &  side_indices,
const std::vector< std::vector< boundary_id_type > > &  elem_side_info,
const SubdomainID subdomain_id_shift_base 
)

Definition at line 901 of file MooseMeshElementConversionUtils.C.

906{
907 // We add a node at the centroid of the PRISM6 element
908 // With this node, the PRISM6 can be converted into 3 PYRAMID5 elements and 2 TET4 elements
909 // For the PYRAMID5 element, it can further be converted into 2 TET3 elements
910 const Point elem_cent = mesh.elem_ptr(elem_id)->true_centroid();
911 auto new_node = mesh.add_point(elem_cent);
912 for (const auto & i_side : make_range(mesh.elem_ptr(elem_id)->n_sides()))
913 {
914 if (i_side % 4 == 0 ||
915 std::find(side_indices.begin(), side_indices.end(), i_side) != side_indices.end())
917 mesh, elem_id, i_side, new_node, elem_side_info[i_side], subdomain_id_shift_base);
918 else
920 mesh, elem_id, i_side, new_node, elem_side_info[i_side], subdomain_id_shift_base);
921 }
922}
void createUnitPyramid5FromPrism6(MeshBase &mesh, const dof_id_type &elem_id, const unsigned int &side_index, const Node *new_node, const std::vector< boundary_id_type > &side_info, const SubdomainID &subdomain_id_shift_base)
Create a PYRAMID5 element opposite the sides converted to tets and the centroid of the PRISM6 element...
void createUnitTet4FromPrism6(MeshBase &mesh, const dof_id_type &elem_id, const unsigned int &side_index, const Node *new_node, const std::vector< boundary_id_type > &side_info, const SubdomainID &subdomain_id_shift_base)
Create one or two TET4 elements based on a side and the centroid of the PRISM6 element.

◆ convertPyramid5Elem() [1/2]

void MooseMeshElementConversionUtils::convertPyramid5Elem ( MeshBase &  mesh,
const dof_id_type &  elem_id,
const std::vector< std::vector< boundary_id_type > > &  elem_side_info,
const SubdomainID subdomain_id_shift_base 
)

Convert a PYRAMID5 element to elements with TRI3 surfaces on the original QUAD4 side.

Parameters
meshThe mesh containing the element
elem_idThe ID of the PYRAMID5 element to be converted
elem_side_infoThe boundary IDs associated with the sides of the PYRAMID
subdomain_id_shift_basethe reference id used to shift the subdomain ID for new elements

Referenced by convertElem().

◆ convertPyramid5Elem() [2/2]

void MooseMeshElementConversionUtils::convertPyramid5Elem ( MeshBase mesh,
const dof_id_type elem_id,
const std::vector< std::vector< boundary_id_type > > &  elem_side_info,
const SubdomainID subdomain_id_shift_base 
)

Definition at line 1008 of file MooseMeshElementConversionUtils.C.

1012{
1013 // A Pyramid5 element has only one QUAD4 face, so we can convert it to 2 TET4 elements
1014 unsigned int lid_index = 0;
1015 for (const auto & i : make_range(1, 4))
1016 {
1017 if (mesh.elem_ptr(elem_id)->side_ptr(4)->node_ptr(i)->id() <
1018 mesh.elem_ptr(elem_id)->side_ptr(4)->node_ptr(lid_index)->id())
1019 lid_index = i;
1020 }
1021 auto new_elem_0 = std::make_unique<Tet4>();
1022 new_elem_0->set_node(
1023 0,
1024 mesh.elem_ptr(elem_id)->side_ptr(4)->node_ptr(MathUtils::euclideanMod(2 - lid_index % 2, 4)));
1025 new_elem_0->set_node(
1026 1,
1027 mesh.elem_ptr(elem_id)->side_ptr(4)->node_ptr(MathUtils::euclideanMod(1 - lid_index % 2, 4)));
1028 new_elem_0->set_node(
1029 2,
1030 mesh.elem_ptr(elem_id)->side_ptr(4)->node_ptr(MathUtils::euclideanMod(0 - lid_index % 2, 4)));
1031 new_elem_0->set_node(3, mesh.elem_ptr(elem_id)->node_ptr(4));
1032 new_elem_0->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + subdomain_id_shift_base;
1033 auto new_elem_ptr_0 = mesh.add_elem(std::move(new_elem_0));
1034 retainEEID(mesh, elem_id, new_elem_ptr_0);
1035
1036 auto new_elem_1 = std::make_unique<Tet4>();
1037 new_elem_1->set_node(
1038 0,
1039 mesh.elem_ptr(elem_id)->side_ptr(4)->node_ptr(MathUtils::euclideanMod(3 - lid_index % 2, 4)));
1040 new_elem_1->set_node(
1041 1,
1042 mesh.elem_ptr(elem_id)->side_ptr(4)->node_ptr(MathUtils::euclideanMod(2 - lid_index % 2, 4)));
1043 new_elem_1->set_node(
1044 2,
1045 mesh.elem_ptr(elem_id)->side_ptr(4)->node_ptr(MathUtils::euclideanMod(0 - lid_index % 2, 4)));
1046 new_elem_1->set_node(3, mesh.elem_ptr(elem_id)->node_ptr(4));
1047 new_elem_1->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + subdomain_id_shift_base;
1048 auto new_elem_ptr_1 = mesh.add_elem(std::move(new_elem_1));
1049 retainEEID(mesh, elem_id, new_elem_ptr_1);
1050
1051 for (const auto & bid : elem_side_info[0])
1052 mesh.get_boundary_info().add_side(new_elem_ptr_0, 2 - lid_index % 2, bid);
1053 for (const auto & bid : elem_side_info[1])
1054 if (lid_index % 2)
1055 mesh.get_boundary_info().add_side(new_elem_ptr_1, 2, bid);
1056 else
1057 mesh.get_boundary_info().add_side(new_elem_ptr_0, 1, bid);
1058 for (const auto & bid : elem_side_info[2])
1059 mesh.get_boundary_info().add_side(new_elem_ptr_1, 2 + lid_index % 2, bid);
1060 for (const auto & bid : elem_side_info[3])
1061 if (lid_index % 2)
1062 mesh.get_boundary_info().add_side(new_elem_ptr_0, 1, bid);
1063 else
1064 mesh.get_boundary_info().add_side(new_elem_ptr_1, 3, bid);
1065 for (const auto & bid : elem_side_info[4])
1066 {
1067 mesh.get_boundary_info().add_side(new_elem_ptr_0, 0, bid);
1068 mesh.get_boundary_info().add_side(new_elem_ptr_1, 0, bid);
1069 }
1070}
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
const Node * node_ptr(const unsigned int i) const
virtual Elem * add_elem(Elem *e)=0
std::size_t euclideanMod(T1 dividend, T2 divisor)
perform modulo operator for Euclidean division that ensures a non-negative result
Definition MathUtils.h:435
if(subdm)

◆ createUnitPyramid5FromHex8() [1/2]

void MooseMeshElementConversionUtils::createUnitPyramid5FromHex8 ( MeshBase &  mesh,
const dof_id_type &  elem_id,
const unsigned int side_index,
const Node *  new_node,
const std::vector< boundary_id_type > &  side_info,
const SubdomainID subdomain_id_shift_base 
)

Create one PYRAMID5 element based on a side and the centroid of the HEX8 element.

Parameters
meshThe mesh containing the element
elem_idThe ID of the HEX8 element to be converted
side_indexThe index of the side to be converted
new_nodeThe new node created at the centroid of the HEX8 element
side_infoThe boundary IDs associated with the side of the HEX8 element
subdomain_id_shift_basethe reference id used to shift the subdomain ID for new elements

Referenced by convertHex8Elem(), and createUnitPyramid5FromPrism6().

◆ createUnitPyramid5FromHex8() [2/2]

void MooseMeshElementConversionUtils::createUnitPyramid5FromHex8 ( MeshBase mesh,
const dof_id_type elem_id,
const unsigned int side_index,
const Node new_node,
const std::vector< boundary_id_type > &  side_info,
const SubdomainID subdomain_id_shift_base 
)

Definition at line 819 of file MooseMeshElementConversionUtils.C.

825{
826 auto new_elem = std::make_unique<Pyramid5>();
827 new_elem->set_node(0, mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(3));
828 new_elem->set_node(1, mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(2));
829 new_elem->set_node(2, mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(1));
830 new_elem->set_node(3, mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(0));
831 new_elem->set_node(4, const_cast<Node *>(new_node));
832 new_elem->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + subdomain_id_shift_base * 2;
833 auto new_elem_ptr = mesh.add_elem(std::move(new_elem));
834 retainEEID(mesh, elem_id, new_elem_ptr);
835 for (const auto & bid : side_info)
836 mesh.get_boundary_info().add_side(new_elem_ptr, 4, bid);
837}
void retainEEID(MeshBase &mesh, const dof_id_type &elem_id, Elem *new_elem_ptr)
Retain the extra integer of the original element in a new element.

◆ createUnitPyramid5FromPrism6() [1/2]

void MooseMeshElementConversionUtils::createUnitPyramid5FromPrism6 ( MeshBase &  mesh,
const dof_id_type &  elem_id,
const unsigned int side_index,
const Node *  new_node,
const std::vector< boundary_id_type > &  side_info,
const SubdomainID subdomain_id_shift_base 
)

Create a PYRAMID5 element opposite the sides converted to tets and the centroid of the PRISM6 element.

Parameters
meshThe mesh containing the element
elem_idThe ID of the PRISM6 element to be converted
side_indexThe index of the side to be converted
new_nodeThe new node created at the centroid of the PRISM6 element
side_infoThe boundary IDs associated with the side of the PRISM6 element
subdomain_id_shift_basethe reference id used to shift the subdomain ID for new elements

Referenced by convertPrism6Elem().

◆ createUnitPyramid5FromPrism6() [2/2]

void MooseMeshElementConversionUtils::createUnitPyramid5FromPrism6 ( MeshBase mesh,
const dof_id_type elem_id,
const unsigned int side_index,
const Node new_node,
const std::vector< boundary_id_type > &  side_info,
const SubdomainID subdomain_id_shift_base 
)

Definition at line 995 of file MooseMeshElementConversionUtils.C.

1001{
1002 // Same as Hex8
1004 mesh, elem_id, side_index, new_node, side_info, subdomain_id_shift_base);
1005}

◆ createUnitTet4FromHex8() [1/2]

void MooseMeshElementConversionUtils::createUnitTet4FromHex8 ( MeshBase &  mesh,
const dof_id_type &  elem_id,
const unsigned int side_index,
const Node *  new_node,
const std::vector< boundary_id_type > &  side_info,
const SubdomainID subdomain_id_shift_base 
)

Create two TET4 elements based on a side and the centroid of the HEX8 element.

Parameters
meshThe mesh containing the element
elem_idThe ID of the HEX8 element to be converted
side_indexThe index of the side to be converted
new_nodeThe new node created at the centroid of the HEX8 element
side_infoThe boundary IDs associated with the side of the HEX8 element
subdomain_id_shift_basethe reference id used to shift the subdomain ID for new elements

Referenced by convertHex8Elem().

◆ createUnitTet4FromHex8() [2/2]

void MooseMeshElementConversionUtils::createUnitTet4FromHex8 ( MeshBase mesh,
const dof_id_type elem_id,
const unsigned int side_index,
const Node new_node,
const std::vector< boundary_id_type > &  side_info,
const SubdomainID subdomain_id_shift_base 
)

Definition at line 840 of file MooseMeshElementConversionUtils.C.

846{
847 // We want to make sure that the QUAD4 is divided by the diagonal that involves the node with
848 // the lowest node id This may help maintain consistency for future applications
849 unsigned int lid_index = 0;
850 for (const auto & i : make_range(1, 4))
851 {
852 if (mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(i)->id() <
853 mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(lid_index)->id())
854 lid_index = i;
855 }
856
857 auto new_elem_0 = std::make_unique<Tet4>();
858 new_elem_0->set_node(0,
859 mesh.elem_ptr(elem_id)
860 ->side_ptr(side_index)
861 ->node_ptr(MathUtils::euclideanMod(2 - lid_index % 2, 4)));
862 new_elem_0->set_node(1,
863 mesh.elem_ptr(elem_id)
864 ->side_ptr(side_index)
865 ->node_ptr(MathUtils::euclideanMod(1 - lid_index % 2, 4)));
866 new_elem_0->set_node(2,
867 mesh.elem_ptr(elem_id)
868 ->side_ptr(side_index)
869 ->node_ptr(MathUtils::euclideanMod(0 - lid_index % 2, 4)));
870 new_elem_0->set_node(3, const_cast<Node *>(new_node));
871 new_elem_0->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + subdomain_id_shift_base;
872 auto new_elem_ptr_0 = mesh.add_elem(std::move(new_elem_0));
873 retainEEID(mesh, elem_id, new_elem_ptr_0);
874
875 auto new_elem_1 = std::make_unique<Tet4>();
876 new_elem_1->set_node(0,
877 mesh.elem_ptr(elem_id)
878 ->side_ptr(side_index)
879 ->node_ptr(MathUtils::euclideanMod(3 - lid_index % 2, 4)));
880 new_elem_1->set_node(1,
881 mesh.elem_ptr(elem_id)
882 ->side_ptr(side_index)
883 ->node_ptr(MathUtils::euclideanMod(2 - lid_index % 2, 4)));
884 new_elem_1->set_node(2,
885 mesh.elem_ptr(elem_id)
886 ->side_ptr(side_index)
887 ->node_ptr(MathUtils::euclideanMod(0 - lid_index % 2, 4)));
888 new_elem_1->set_node(3, const_cast<Node *>(new_node));
889 new_elem_1->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + subdomain_id_shift_base;
890 auto new_elem_ptr_1 = mesh.add_elem(std::move(new_elem_1));
891 retainEEID(mesh, elem_id, new_elem_ptr_1);
892
893 for (const auto & bid : side_info)
894 {
895 mesh.get_boundary_info().add_side(new_elem_ptr_0, 0, bid);
896 mesh.get_boundary_info().add_side(new_elem_ptr_1, 0, bid);
897 }
898}

◆ createUnitTet4FromPrism6() [1/2]

void MooseMeshElementConversionUtils::createUnitTet4FromPrism6 ( MeshBase &  mesh,
const dof_id_type &  elem_id,
const unsigned int side_index,
const Node *  new_node,
const std::vector< boundary_id_type > &  side_info,
const SubdomainID subdomain_id_shift_base 
)

Create one or two TET4 elements based on a side and the centroid of the PRISM6 element.

Parameters
meshThe mesh containing the element
elem_idThe ID of the PRISM6 element to be converted
side_indexThe index of the side to be converted
new_nodeThe new node created at the centroid of the PRISM6 element
side_infoThe boundary IDs associated with the side of the PRISM6 element
subdomain_id_shift_basethe reference id used to shift the subdomain ID for new elements

Referenced by convertPrism6Elem().

◆ createUnitTet4FromPrism6() [2/2]

void MooseMeshElementConversionUtils::createUnitTet4FromPrism6 ( MeshBase mesh,
const dof_id_type elem_id,
const unsigned int side_index,
const Node new_node,
const std::vector< boundary_id_type > &  side_info,
const SubdomainID subdomain_id_shift_base 
)

Definition at line 925 of file MooseMeshElementConversionUtils.C.

931{
932 // For side 1 and side 4, they are already TRI3, so only one TET is created
933 // For side 0, 2, and 3, they are QUAD4, so we create 2 TETs
934 // We want to make sure that the QUAD4 is divided by the diagonal that involves
935 // the node with the lowest node id This may help maintain consistency for future applications
936 bool is_side_quad = (side_index % 4 != 0);
937 unsigned int lid_index = 0;
938 if (is_side_quad)
939 for (const auto & i : make_range(1, 4))
940 {
941 if (mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(i)->id() <
942 mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(lid_index)->id())
943 lid_index = i;
944 }
945 // For a TRI3 side, lid_index is always 0, so the indices are always 2,1,0 here
946 auto new_elem_0 = std::make_unique<Tet4>();
947 new_elem_0->set_node(0,
948 mesh.elem_ptr(elem_id)
949 ->side_ptr(side_index)
950 ->node_ptr(MathUtils::euclideanMod(2 - lid_index % 2, 4)));
951 new_elem_0->set_node(1,
952 mesh.elem_ptr(elem_id)
953 ->side_ptr(side_index)
954 ->node_ptr(MathUtils::euclideanMod(1 - lid_index % 2, 4)));
955 new_elem_0->set_node(2,
956 mesh.elem_ptr(elem_id)
957 ->side_ptr(side_index)
958 ->node_ptr(MathUtils::euclideanMod(0 - lid_index % 2, 4)));
959 new_elem_0->set_node(3, const_cast<Node *>(new_node));
960 new_elem_0->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + subdomain_id_shift_base;
961 auto new_elem_ptr_0 = mesh.add_elem(std::move(new_elem_0));
962 retainEEID(mesh, elem_id, new_elem_ptr_0);
963
964 Elem * new_elem_ptr_1 = nullptr;
965 if (is_side_quad)
966 {
967 auto new_elem_1 = std::make_unique<Tet4>();
968 new_elem_1->set_node(0,
969 mesh.elem_ptr(elem_id)
970 ->side_ptr(side_index)
971 ->node_ptr(MathUtils::euclideanMod(3 - lid_index % 2, 4)));
972 new_elem_1->set_node(1,
973 mesh.elem_ptr(elem_id)
974 ->side_ptr(side_index)
975 ->node_ptr(MathUtils::euclideanMod(2 - lid_index % 2, 4)));
976 new_elem_1->set_node(2,
977 mesh.elem_ptr(elem_id)
978 ->side_ptr(side_index)
979 ->node_ptr(MathUtils::euclideanMod(0 - lid_index % 2, 4)));
980 new_elem_1->set_node(3, const_cast<Node *>(new_node));
981 new_elem_1->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + subdomain_id_shift_base;
982 new_elem_ptr_1 = mesh.add_elem(std::move(new_elem_1));
983 retainEEID(mesh, elem_id, new_elem_ptr_1);
984 }
985
986 for (const auto & bid : side_info)
987 {
988 mesh.get_boundary_info().add_side(new_elem_ptr_0, 0, bid);
989 if (new_elem_ptr_1)
990 mesh.get_boundary_info().add_side(new_elem_ptr_1, 0, bid);
991 }
992}

◆ elementBoundaryInfoCollector() [1/2]

void MooseMeshElementConversionUtils::elementBoundaryInfoCollector ( const std::vector< libMesh::BoundaryInfo::BCTuple > &  bdry_side_list,
const dof_id_type  elem_id,
const unsigned short  n_elem_sides,
std::vector< std::vector< boundary_id_type > > &  elem_side_list 
)

Collect the boundary information of the given element in a mesh.

Parameters
bdry_side_listA list that contains the boundary information of the mesh
elem_idThe id of the element to be processed
n_elem_sidesThe number of sides of the element
elem_side_lista vector of vectors to record the boundary information of the element

Referenced by hexElemSplitter(), polyhedronElemSplitter(), prismElemSplitter(), pyramidElemSplitter(), and CutMeshByLevelSetGeneratorBase::tet4ElemCutter().

◆ elementBoundaryInfoCollector() [2/2]

void MooseMeshElementConversionUtils::elementBoundaryInfoCollector ( const std::vector< libMesh::BoundaryInfo::BCTuple > &  bdry_side_list,
const dof_id_type  elem_id,
const unsigned short  n_elem_sides,
std::vector< std::vector< boundary_id_type > > &  elem_side_list 
)

Definition at line 746 of file MooseMeshElementConversionUtils.C.

750{
751 elem_side_list.resize(n_elem_sides);
752 const auto selected_bdry_side_list =
753 std::equal_range(bdry_side_list.begin(), bdry_side_list.end(), elem_id, BCTupleKeyComp{});
754 for (auto selected_bdry_side = selected_bdry_side_list.first;
755 selected_bdry_side != selected_bdry_side_list.second;
756 ++selected_bdry_side)
757 {
758 elem_side_list[std::get<1>(*selected_bdry_side)].push_back(std::get<2>(*selected_bdry_side));
759 }
760}

◆ hexElemSplitter() [1/2]

void MooseMeshElementConversionUtils::hexElemSplitter ( MeshBase &  mesh,
const std::vector< libMesh::BoundaryInfo::BCTuple > &  bdry_side_list,
const dof_id_type  elem_id,
std::vector< dof_id_type > &  converted_elems_ids 
)

Split a HEX8 element into six TET4 elements.

Parameters
meshThe mesh to be modified
bdry_side_listA list that contains the boundary information of the original mesh
elem_idThe id of the element to be split
converted_elems_idsa vector to record the ids of the newly created TET4 elements

Referenced by convert3DMeshToAllTet4().

◆ hexElemSplitter() [2/2]

void MooseMeshElementConversionUtils::hexElemSplitter ( MeshBase mesh,
const std::vector< libMesh::BoundaryInfo::BCTuple > &  bdry_side_list,
const dof_id_type  elem_id,
std::vector< dof_id_type > &  converted_elems_ids 
)

Definition at line 33 of file MooseMeshElementConversionUtils.C.

37{
38 // Build boundary information of the mesh
39 BoundaryInfo & boundary_info = mesh.get_boundary_info();
40 // Create a list of sidesets involving the element to be split
41 std::vector<std::vector<boundary_id_type>> elem_side_list;
42 elem_side_list.resize(6);
43 elementBoundaryInfoCollector(bdry_side_list, elem_id, 6, elem_side_list);
44
45 const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
46 std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
47 // Record all the element extra integers of the original quad element
48 for (unsigned int j = 0; j < n_elem_extra_ids; j++)
49 exist_extra_ids[j] = mesh.elem_ptr(elem_id)->get_extra_integer(j);
50
51 std::vector<std::vector<unsigned int>> opt_option;
52 std::vector<const Node *> elem_node_list = {mesh.elem_ptr(elem_id)->node_ptr(0),
53 mesh.elem_ptr(elem_id)->node_ptr(1),
54 mesh.elem_ptr(elem_id)->node_ptr(2),
55 mesh.elem_ptr(elem_id)->node_ptr(3),
56 mesh.elem_ptr(elem_id)->node_ptr(4),
57 mesh.elem_ptr(elem_id)->node_ptr(5),
58 mesh.elem_ptr(elem_id)->node_ptr(6),
59 mesh.elem_ptr(elem_id)->node_ptr(7)};
60 std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
61
62 std::vector<std::vector<const Node *>> optimized_node_list;
63 hexNodesToTetNodesDeterminer(elem_node_list, rotated_tet_face_indices, optimized_node_list);
64
65 std::vector<Elem *> elems_Tet4;
66 for (const auto i : index_range(optimized_node_list))
67 {
68 auto new_elem = std::make_unique<Tet4>();
69 new_elem->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
70 new_elem->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
71 new_elem->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
72 new_elem->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
73 new_elem->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
74 elems_Tet4.push_back(mesh.add_elem(std::move(new_elem)));
75 converted_elems_ids.push_back(elems_Tet4.back()->id());
76
77 for (unsigned int j = 0; j < 4; j++)
78 {
79 // A hex element has 6 faces indexed from 0 to 5
80 // a <6 value indicates that the face of the tet element corresponds to the face of the
81 // original hex; a =6 value means the face of the tet is an interior face of the hex
82 if (rotated_tet_face_indices[i][j] < 6)
83 {
84 for (const auto & side_info : elem_side_list[rotated_tet_face_indices[i][j]])
85 boundary_info.add_side(elems_Tet4.back(), j, side_info);
86 }
87 }
88 }
89
90 // Retain element extra integers
91 for (unsigned int i = 0; i < 6; i++)
92 for (unsigned int j = 0; j < n_elem_extra_ids; j++)
93 {
94 elems_Tet4[i]->set_extra_integer(j, exist_extra_ids[j]);
95 }
96}
dof_id_type get_extra_integer(const unsigned int index) const
unsigned int n_elem_integers() const
void elementBoundaryInfoCollector(const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, const unsigned short n_elem_sides, std::vector< std::vector< boundary_id_type > > &elem_side_list)
Collect the boundary information of the given element in a mesh.
auto index_range(const T &sizable)

◆ hexNodesToTetNodesDeterminer() [1/2]

void MooseMeshElementConversionUtils::hexNodesToTetNodesDeterminer ( std::vector< const Node * > &  hex_nodes,
std::vector< std::vector< unsigned int > > &  rotated_tet_face_indices,
std::vector< std::vector< const Node * > > &  tet_nodes_list 
)

For a vector of rotated nodes that can form a HEX8 element, create a vector of four-node sets that can form TET4 elements to replace the original HEX8 element.

All the QUAD4 faces of the HEX8 element will be split by the diagonal line that involves the node with the minimum id of that face.

Parameters
hex_nodesA vector of pointers to the nodes that can form a HEX8 element
rotated_tet_face_indicesA vector of vectors of the original face indices of the HEX8 element corresponding to the faces of the newly created TET4 elements
tet_nodes_lista vector of vectors of pointers to the nodes that can form TET4 elements

Referenced by hexElemSplitter().

◆ hexNodesToTetNodesDeterminer() [2/2]

void MooseMeshElementConversionUtils::hexNodesToTetNodesDeterminer ( std::vector< const Node * > &  hex_nodes,
std::vector< std::vector< unsigned int > > &  rotated_tet_face_indices,
std::vector< std::vector< const Node * > > &  tet_nodes_list 
)

Definition at line 292 of file MooseMeshElementConversionUtils.C.

295{
296 // Find the node with the minimum id
297 std::vector<dof_id_type> node_ids(8);
298 for (unsigned int i = 0; i < 8; i++)
299 node_ids[i] = hex_nodes[i]->id();
300
301 const unsigned int min_node_id_index = std::distance(
302 std::begin(node_ids), std::min_element(std::begin(node_ids), std::end(node_ids)));
303 // Get the index of the three neighbor nodes of the minimum node
304 // The order is consistent with the description in nodeRotationHEX8()
305 // Then determine the index of the second minimum node
306 const auto neighbor_node_indices = neighborNodeIndicesHEX8(min_node_id_index);
307
308 const auto neighbor_node_ids = {node_ids[neighbor_node_indices[0]],
309 node_ids[neighbor_node_indices[1]],
310 node_ids[neighbor_node_indices[2]]};
311 const unsigned int sec_min_pos =
312 std::distance(std::begin(neighbor_node_ids),
313 std::min_element(std::begin(neighbor_node_ids), std::end(neighbor_node_ids)));
314
315 // Rotate the node and face indices based on the identified minimum and second minimum nodes
316 // After the rotation, we guarantee that the minimum node is the first node (Node 0)
317 // And the second node (Node 1) has the minium global id among the three neighbor nodes of Node 0
318 // This makes the splitting process simpler
319 std::vector<unsigned int> face_rotation;
320 std::vector<unsigned int> rotated_indices;
321 nodeRotationHEX8(min_node_id_index, sec_min_pos, face_rotation, rotated_indices);
322 std::vector<const Node *> rotated_hex_nodes;
323 for (unsigned int i = 0; i < 8; i++)
324 rotated_hex_nodes.push_back(hex_nodes[rotated_indices[i]]);
325
326 // Find the selection of each face's cutting direction
327 const auto diagonal_directions = quadFaceDiagonalDirectionsHex(rotated_hex_nodes);
328
329 // Based on the determined splitting directions of all the faces, determine the nodes of each
330 // resulting TET4 elements after the splitting.
331 std::vector<std::vector<unsigned int>> tet_face_indices;
332 const auto tet_nodes_set = tetNodesForHex(diagonal_directions, tet_face_indices);
333 for (const auto & tet_face_index : tet_face_indices)
334 {
335 rotated_tet_face_indices.push_back(std::vector<unsigned int>());
336 for (const auto & face_index : tet_face_index)
337 {
338 if (face_index < 6)
339 rotated_tet_face_indices.back().push_back(face_rotation[face_index]);
340 else
341 rotated_tet_face_indices.back().push_back(6);
342 }
343 }
344
345 for (const auto & tet_nodes : tet_nodes_set)
346 {
347 tet_nodes_list.push_back(std::vector<const Node *>());
348 for (const auto & tet_node : tet_nodes)
349 tet_nodes_list.back().push_back(rotated_hex_nodes[tet_node]);
350 }
351}
std::vector< unsigned int > neighborNodeIndicesHEX8(unsigned int min_id_index)
Calculate the indices (within the element nodes) of the three neighboring nodes of a node in a HEX8 e...

◆ neighborNodeIndicesHEX8()

std::vector< unsigned int > MooseMeshElementConversionUtils::neighborNodeIndicesHEX8 ( unsigned int  min_id_index)

Calculate the indices (within the element nodes) of the three neighboring nodes of a node in a HEX8 element.

Parameters
min_id_indexThe index of the node with the minimum id
Returns
a vector of the three neighboring nodes

Definition at line 281 of file MooseMeshElementConversionUtils.C.

282{
283 const std::vector<std::vector<unsigned int>> preset_indices = {
284 {1, 3, 4}, {0, 2, 5}, {3, 1, 6}, {2, 0, 7}, {5, 7, 0}, {4, 6, 1}, {7, 5, 2}, {6, 4, 3}};
285 if (min_id_index > 7)
286 mooseError("The input node index is out of range.");
287 else
288 return preset_indices[min_id_index];
289}

Referenced by hexNodesToTetNodesDeterminer().

◆ nodeRotationHEX8()

void MooseMeshElementConversionUtils::nodeRotationHEX8 ( const unsigned int  min_id_index,
const unsigned int  sec_min_pos,
std::vector< unsigned int > &  face_rotation,
std::vector< unsigned int > &  node_rotation 
)

Rotate a HEX8 element's nodes to ensure that the node with the minimum id is the first node; and the node among its three neighboring nodes with the minimum id is the second node.

Parameters
min_id_indexThe index of the node with the minimum id
sec_min_posThe index of the node among its three neighboring nodes with the minimum id (see comments in the function for more details about how the index is defined)
face_rotationA vector to record the rotation of the faces of the HEX8 element
node_rotationa vector of node indices that can form a HEX8 element

Definition at line 435 of file MooseMeshElementConversionUtils.C.

439{
440 // Assuming the original hex element is a cube, the vectors formed by nodes 0-1, 0-3, and 0-4 are
441 // overlapped with the x, y, and z axes, respectively. sec_min_pos = 0 means the second minimum
442 // node is in the x direction, sec_min_pos = 1 means the second minimum node is in the y
443 // direction, and sec_min_pos = 2 means the second minimum node is in the z direction.
444 const std::vector<std::vector<std::vector<unsigned int>>> preset_indices = {
445 {{0, 1, 2, 3, 4, 5, 6, 7}, {0, 3, 7, 4, 1, 2, 6, 5}, {0, 4, 5, 1, 3, 7, 6, 2}},
446 {{1, 0, 4, 5, 2, 3, 7, 6}, {1, 2, 3, 0, 5, 6, 7, 4}, {1, 5, 6, 2, 0, 4, 7, 3}},
447 {{2, 3, 0, 1, 6, 7, 4, 5}, {2, 1, 5, 6, 3, 0, 4, 7}, {2, 6, 7, 3, 1, 5, 4, 0}},
448 {{3, 2, 6, 7, 0, 1, 5, 4}, {3, 0, 1, 2, 7, 4, 5, 6}, {3, 7, 4, 0, 2, 6, 5, 1}},
449 {{4, 5, 1, 0, 7, 6, 2, 3}, {4, 7, 6, 5, 0, 3, 2, 1}, {4, 0, 3, 7, 5, 1, 2, 6}},
450 {{5, 4, 7, 6, 1, 0, 3, 2}, {5, 6, 2, 1, 4, 7, 3, 0}, {5, 1, 0, 4, 6, 2, 3, 7}},
451 {{6, 7, 3, 2, 5, 4, 0, 1}, {6, 5, 4, 7, 2, 1, 0, 3}, {6, 2, 1, 5, 7, 3, 0, 4}},
452 {{7, 6, 5, 4, 3, 2, 1, 0}, {7, 4, 0, 3, 6, 5, 1, 2}, {7, 3, 2, 6, 4, 0, 1, 5}}};
453
454 const std::vector<std::vector<std::vector<unsigned int>>> preset_face_indices = {
455 {{0, 1, 2, 3, 4, 5}, {4, 0, 3, 5, 1, 2}, {1, 4, 5, 2, 0, 3}},
456 {{1, 0, 4, 5, 2, 3}, {0, 2, 3, 4, 1, 5}, {2, 1, 5, 3, 0, 4}},
457 {{0, 3, 4, 1, 2, 5}, {2, 0, 1, 5, 3, 4}, {3, 2, 5, 4, 0, 1}},
458 {{3, 0, 2, 5, 4, 1}, {0, 4, 1, 2, 3, 5}, {4, 3, 5, 1, 0, 2}},
459 {{1, 5, 2, 0, 4, 3}, {5, 4, 3, 2, 1, 0}, {4, 1, 0, 3, 5, 2}},
460 {{5, 1, 4, 3, 2, 0}, {2, 5, 3, 0, 1, 4}, {1, 2, 0, 4, 5, 3}},
461 {{3, 5, 4, 0, 2, 1}, {5, 2, 1, 4, 3, 0}, {2, 3, 0, 1, 5, 4}},
462 {{5, 3, 2, 1, 4, 0}, {4, 5, 1, 0, 3, 2}, {3, 4, 0, 2, 5, 1}}};
463
464 if (min_id_index > 7 || sec_min_pos > 2)
465 mooseError("The input node index is out of range.");
466 else
467 {
468 // index: new face index; value: old face index
469 face_rotation = preset_face_indices[min_id_index][sec_min_pos];
470 node_rotation = preset_indices[min_id_index][sec_min_pos];
471 }
472}

Referenced by hexNodesToTetNodesDeterminer().

◆ nodeRotationPRISM6()

void MooseMeshElementConversionUtils::nodeRotationPRISM6 ( unsigned int  min_id_index,
std::vector< unsigned int > &  face_rotation,
std::vector< unsigned int > &  node_rotation 
)

Rotate a PRISM6 element nodes to ensure that the node with the minimum id is the first node.

Parameters
min_id_indexThe index of the node, within the prism nodes, with the minimum id
face_rotationA vector to record the rotation of the faces of the PRISM6 element
node_rotationa vector of node indices that can form a PRISM6 element

Definition at line 475 of file MooseMeshElementConversionUtils.C.

478{
479 const std::vector<std::vector<unsigned int>> preset_indices = {{0, 1, 2, 3, 4, 5},
480 {1, 2, 0, 4, 5, 3},
481 {2, 0, 1, 5, 3, 4},
482 {3, 5, 4, 0, 2, 1},
483 {4, 3, 5, 1, 0, 2},
484 {5, 4, 3, 2, 1, 0}};
485
486 const std::vector<std::vector<unsigned int>> preset_face_indices = {{0, 1, 2, 3, 4},
487 {0, 2, 3, 1, 4},
488 {0, 3, 1, 2, 4},
489 {4, 3, 2, 1, 0},
490 {4, 1, 3, 2, 0},
491 {4, 2, 1, 3, 0}};
492
493 if (min_id_index > 5)
494 mooseError("The input node index is out of range.");
495 else
496 {
497 // index: new face index; value: old face index
498 face_rotation = preset_face_indices[min_id_index];
499 node_rotation = preset_indices[min_id_index];
500 }
501}

Referenced by prismNodesToTetNodesDeterminer().

◆ nodeRotationPYRAMID5()

void MooseMeshElementConversionUtils::nodeRotationPYRAMID5 ( unsigned int  min_id_index,
std::vector< unsigned int > &  face_rotation,
std::vector< unsigned int > &  node_rotation 
)

Rotate a PYRAMID5 element nodes to ensure that the node with the minimum id is the first node for the bottom face.

Parameters
min_id_indexThe index of the node, within the pyramid nodes, with the minimum id for the bottom face
face_rotationA vector to record the rotation of the faces of the PYRAMID5 element
node_rotationa vector of node indices that can form a PYRAMID5 element

Definition at line 576 of file MooseMeshElementConversionUtils.C.

579{
580 const std::vector<std::vector<unsigned int>> preset_indices = {
581 {0, 1, 2, 3, 4}, {1, 2, 3, 0, 4}, {2, 3, 0, 1, 4}, {3, 0, 1, 2, 4}};
582
583 const std::vector<std::vector<unsigned int>> preset_face_indices = {
584 {0, 1, 2, 3, 4}, {1, 2, 3, 0, 4}, {2, 3, 0, 1, 4}, {3, 0, 1, 2, 4}};
585
586 if (min_id_index > 3)
587 mooseError("The input node index is out of range.");
588 else
589 {
590 // index: new face index; value: old face index
591 face_rotation = preset_face_indices[min_id_index];
592 node_rotation = preset_indices[min_id_index];
593 }
594}

Referenced by pyramidNodesToTetNodesDeterminer().

◆ polyhedronElemSplitter() [1/2]

void MooseMeshElementConversionUtils::polyhedronElemSplitter ( MeshBase &  mesh,
const std::vector< libMesh::BoundaryInfo::BCTuple > &  bdry_side_list,
const dof_id_type  elem_id,
std::vector< dof_id_type > &  converted_elems_ids 
)

Split a polyhedron element into n_sides TET4 elements.

Parameters
meshThe mesh to be modified
bdry_side_listA list that contains the boundary information of the original mesh
elem_idThe id of the element to be split
converted_elems_idsa vector to record the ids of the newly created TET4 elements

Referenced by convert3DMeshToAllTet4().

◆ polyhedronElemSplitter() [2/2]

void MooseMeshElementConversionUtils::polyhedronElemSplitter ( MeshBase mesh,
const std::vector< libMesh::BoundaryInfo::BCTuple > &  bdry_side_list,
const dof_id_type  elem_id,
std::vector< dof_id_type > &  converted_elems_ids 
)

Definition at line 221 of file MooseMeshElementConversionUtils.C.

225{
226 auto elem = mesh.elem_ptr(elem_id);
227 // Build boundary information of the mesh
228 BoundaryInfo & boundary_info = mesh.get_boundary_info();
229 // Create a list of sidesets involving the element to be split
230 std::vector<std::vector<boundary_id_type>> elem_side_list;
231 elementBoundaryInfoCollector(bdry_side_list, elem_id, elem->n_sides(), elem_side_list);
232
233 const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
234 std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
235
236 // Record all the element extra integers of the original quad element
237 for (unsigned int j = 0; j < n_elem_extra_ids; j++)
238 exist_extra_ids[j] = elem->get_extra_integer(j);
239
240 // Split the polygon using its tetrahedralization
241 const auto poly = dynamic_cast<C0Polyhedron *>(elem);
242 Node * v_avg_node = nullptr;
243 // Currently the only extra node ever use to tetrahedralize
244 if (dynamic_cast<C0Polyhedron *>(elem))
245 v_avg_node =
246 mesh.add_point(elem->vertex_average(), mesh.max_node_id() + elem_id, elem->processor_id());
247
248 std::vector<Elem *> elems_Tet4;
249 for (const auto tri_i : make_range(poly->n_subelements()))
250 {
251 const auto tri_indices = poly->subelement(tri_i);
252
253 auto new_elem = std::make_unique<Tet4>();
254 for (const auto i : make_range(4))
255 if (tri_indices[i] >= 0)
256 new_elem->set_node(i, const_cast<Node *>(elem->node_ptr(tri_indices[i])));
257 else
258 new_elem->set_node(i, v_avg_node);
259
260 new_elem->subdomain_id() = elem->subdomain_id();
261 elems_Tet4.push_back(mesh.add_elem(std::move(new_elem)));
262 converted_elems_ids.push_back(elems_Tet4.back()->id());
263
264 // Get subelement to side map
265 const auto tet_face_indices = poly->subelement_sides_to_poly_sides(tri_i);
266
267 // Add the sides of the tets to the relevant boundaries
268 for (unsigned int j = 0; j < 4; j++)
269 if (tet_face_indices[j] < int(elem->n_sides()))
270 for (const auto & side_info : elem_side_list[tet_face_indices[j]])
271 boundary_info.add_side(elems_Tet4.back(), j, side_info);
272 }
273
274 // Retain element extra integers
275 for (const auto i : make_range(poly->n_subelements()))
276 for (unsigned int j = 0; j < n_elem_extra_ids; j++)
277 elems_Tet4[i]->set_extra_integer(j, exist_extra_ids[j]);
278}
for(PetscInt i=0;i< nvars;++i)
void ErrorVector unsigned int
virtual dof_id_type max_node_id() const=0

◆ prismElemSplitter() [1/2]

void MooseMeshElementConversionUtils::prismElemSplitter ( MeshBase &  mesh,
const std::vector< libMesh::BoundaryInfo::BCTuple > &  bdry_side_list,
const dof_id_type  elem_id,
std::vector< dof_id_type > &  converted_elems_ids 
)

Split a PRISM6 element into three TET4 elements.

Parameters
meshThe mesh to be modified
bdry_side_listA list that contains the boundary information of the original mesh
elem_idThe id of the element to be split
converted_elems_idsa vector to record the ids of the newly created TET4 elements

Referenced by convert3DMeshToAllTet4().

◆ prismElemSplitter() [2/2]

void MooseMeshElementConversionUtils::prismElemSplitter ( MeshBase mesh,
const std::vector< libMesh::BoundaryInfo::BCTuple > &  bdry_side_list,
const dof_id_type  elem_id,
std::vector< dof_id_type > &  converted_elems_ids 
)

Definition at line 99 of file MooseMeshElementConversionUtils.C.

103{
104 // Build boundary information of the mesh
105 BoundaryInfo & boundary_info = mesh.get_boundary_info();
106 // Create a list of sidesets involving the element to be split
107 std::vector<std::vector<boundary_id_type>> elem_side_list;
108 elementBoundaryInfoCollector(bdry_side_list, elem_id, 5, elem_side_list);
109
110 const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
111 std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
112
113 // Record all the element extra integers of the original quad element
114 for (unsigned int j = 0; j < n_elem_extra_ids; j++)
115 exist_extra_ids[j] = mesh.elem_ptr(elem_id)->get_extra_integer(j);
116
117 std::vector<const Node *> elem_node_list = {mesh.elem_ptr(elem_id)->node_ptr(0),
118 mesh.elem_ptr(elem_id)->node_ptr(1),
119 mesh.elem_ptr(elem_id)->node_ptr(2),
120 mesh.elem_ptr(elem_id)->node_ptr(3),
121 mesh.elem_ptr(elem_id)->node_ptr(4),
122 mesh.elem_ptr(elem_id)->node_ptr(5)};
123 std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
124 std::vector<std::vector<const Node *>> optimized_node_list;
125 prismNodesToTetNodesDeterminer(elem_node_list, rotated_tet_face_indices, optimized_node_list);
126
127 std::vector<Elem *> elems_Tet4;
128 for (const auto i : index_range(optimized_node_list))
129 {
130 auto new_elem = std::make_unique<Tet4>();
131 new_elem->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
132 new_elem->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
133 new_elem->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
134 new_elem->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
135 new_elem->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
136 elems_Tet4.push_back(mesh.add_elem(std::move(new_elem)));
137 converted_elems_ids.push_back(elems_Tet4.back()->id());
138
139 for (unsigned int j = 0; j < 4; j++)
140 {
141 // A prism element has 5 faces indexed from 0 to 4
142 // a <4 value indicates that the face of the tet element corresponds to the face of the
143 // original prism; a =5 value means the face of the tet is an interior face of the prism
144 if (rotated_tet_face_indices[i][j] < 5)
145 {
146 for (const auto & side_info : elem_side_list[rotated_tet_face_indices[i][j]])
147 boundary_info.add_side(elems_Tet4.back(), j, side_info);
148 }
149 }
150 }
151
152 // Retain element extra integers
153 for (unsigned int i = 0; i < 3; i++)
154 for (unsigned int j = 0; j < n_elem_extra_ids; j++)
155 {
156 elems_Tet4[i]->set_extra_integer(j, exist_extra_ids[j]);
157 }
158}

◆ prismNodesToTetNodesDeterminer() [1/2]

void MooseMeshElementConversionUtils::prismNodesToTetNodesDeterminer ( std::vector< const Node * > &  prism_nodes,
std::vector< std::vector< unsigned int > > &  rotated_tet_face_indices,
std::vector< std::vector< const Node * > > &  tet_nodes_list 
)

For a rotated nodes that can form a PRISM6 element, create a series of four-node set that can form TET4 elements to replace the original PRISM6 element.

All the QUAD4 face of the PRISM6 element will be split by the diagonal line that involves the node with the minimum id of that face.

Parameters
prism_nodesA vector of pointers to the nodes that can form a PRISM6 element
rotated_tet_face_indicesA vector of vectors of the original face indices of the PRISM6 element corresponding to the faces of the newly created TET4 elements
tet_nodes_lista vector of vectors of pointers to the nodes that can form TET4 elements

Referenced by prismElemSplitter(), and CutMeshByLevelSetGeneratorBase::tet4ElemCutter().

◆ prismNodesToTetNodesDeterminer() [2/2]

void MooseMeshElementConversionUtils::prismNodesToTetNodesDeterminer ( std::vector< const Node * > &  prism_nodes,
std::vector< std::vector< unsigned int > > &  rotated_tet_face_indices,
std::vector< std::vector< const Node * > > &  tet_nodes_list 
)

Definition at line 504 of file MooseMeshElementConversionUtils.C.

507{
508 // Find the node with the minimum id
509 std::vector<dof_id_type> node_ids(6);
510 for (unsigned int i = 0; i < 6; i++)
511 node_ids[i] = prism_nodes[i]->id();
512
513 const unsigned int min_node_id_index = std::distance(
514 std::begin(node_ids), std::min_element(std::begin(node_ids), std::end(node_ids)));
515
516 // Rotate the node and face indices based on the identified minimum node
517 // After the rotation, we guarantee that the minimum node is the first node (Node 0)
518 // This makes the splitting process simpler
519 std::vector<unsigned int> face_rotation;
520 std::vector<unsigned int> rotated_indices;
521 nodeRotationPRISM6(min_node_id_index, face_rotation, rotated_indices);
522 std::vector<const Node *> rotated_prism_nodes;
523 for (unsigned int i = 0; i < 6; i++)
524 rotated_prism_nodes.push_back(prism_nodes[rotated_indices[i]]);
525
526 std::vector<const Node *> key_quad_nodes = {rotated_prism_nodes[1],
527 rotated_prism_nodes[2],
528 rotated_prism_nodes[5],
529 rotated_prism_nodes[4]};
530
531 // Find the selection of each face's cutting direction
532 const bool diagonal_direction = quadFaceDiagonalDirection(key_quad_nodes);
533
534 // Based on the determined splitting directions of all the faces, determine the nodes of each
535 // resulting TET4 elements after the splitting.
536 std::vector<std::vector<unsigned int>> tet_face_indices;
537 const auto tet_nodes_set = tetNodesForPrism(diagonal_direction, tet_face_indices);
538 for (const auto & tet_face_index : tet_face_indices)
539 {
540 rotated_tet_face_indices.push_back(std::vector<unsigned int>());
541 for (const auto & face_index : tet_face_index)
542 {
543 if (face_index < 5)
544 rotated_tet_face_indices.back().push_back(face_rotation[face_index]);
545 else
546 rotated_tet_face_indices.back().push_back(5);
547 }
548 }
549
550 for (const auto & tet_nodes : tet_nodes_set)
551 {
552 tet_nodes_list.push_back(std::vector<const Node *>());
553 for (const auto & tet_node : tet_nodes)
554 tet_nodes_list.back().push_back(rotated_prism_nodes[tet_node]);
555 }
556}
void nodeRotationPRISM6(unsigned int min_id_index, std::vector< unsigned int > &face_rotation, std::vector< unsigned int > &node_rotation)
Rotate a PRISM6 element nodes to ensure that the node with the minimum id is the first node.

◆ pyramidElemSplitter() [1/2]

void MooseMeshElementConversionUtils::pyramidElemSplitter ( MeshBase &  mesh,
const std::vector< libMesh::BoundaryInfo::BCTuple > &  bdry_side_list,
const dof_id_type  elem_id,
std::vector< dof_id_type > &  converted_elems_ids 
)

Split a PYRAMID5 element into two TET4 elements.

Parameters
meshThe mesh to be modified
bdry_side_listA list that contains the boundary information of the original mesh
elem_idThe id of the element to be split
converted_elems_idsa vector to record the ids of the newly created TET4 elements

Referenced by convert3DMeshToAllTet4().

◆ pyramidElemSplitter() [2/2]

void MooseMeshElementConversionUtils::pyramidElemSplitter ( MeshBase mesh,
const std::vector< libMesh::BoundaryInfo::BCTuple > &  bdry_side_list,
const dof_id_type  elem_id,
std::vector< dof_id_type > &  converted_elems_ids 
)

Definition at line 161 of file MooseMeshElementConversionUtils.C.

165{
166 // Build boundary information of the mesh
167 BoundaryInfo & boundary_info = mesh.get_boundary_info();
168 // Create a list of sidesets involving the element to be split
169 std::vector<std::vector<boundary_id_type>> elem_side_list;
170 elementBoundaryInfoCollector(bdry_side_list, elem_id, 5, elem_side_list);
171
172 const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
173 std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
174 // Record all the element extra integers of the original quad element
175 for (unsigned int j = 0; j < n_elem_extra_ids; j++)
176 exist_extra_ids[j] = mesh.elem_ptr(elem_id)->get_extra_integer(j);
177
178 std::vector<const Node *> elem_node_list = {mesh.elem_ptr(elem_id)->node_ptr(0),
179 mesh.elem_ptr(elem_id)->node_ptr(1),
180 mesh.elem_ptr(elem_id)->node_ptr(2),
181 mesh.elem_ptr(elem_id)->node_ptr(3),
182 mesh.elem_ptr(elem_id)->node_ptr(4)};
183 std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
184 std::vector<std::vector<const Node *>> optimized_node_list;
185 pyramidNodesToTetNodesDeterminer(elem_node_list, rotated_tet_face_indices, optimized_node_list);
186
187 std::vector<Elem *> elems_Tet4;
188 for (const auto i : index_range(optimized_node_list))
189 {
190 auto new_elem = std::make_unique<Tet4>();
191 new_elem->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
192 new_elem->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
193 new_elem->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
194 new_elem->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
195 new_elem->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
196 elems_Tet4.push_back(mesh.add_elem(std::move(new_elem)));
197 converted_elems_ids.push_back(elems_Tet4.back()->id());
198
199 for (unsigned int j = 0; j < 4; j++)
200 {
201 // A pyramid element has 5 faces indexed from 0 to 4
202 // a <4 value indicates that the face of the tet element corresponds to the face of the
203 // original pyramid; a =5 value means the face of the tet is an interior face of the pyramid
204 if (rotated_tet_face_indices[i][j] < 5)
205 {
206 for (const auto & side_info : elem_side_list[rotated_tet_face_indices[i][j]])
207 boundary_info.add_side(elems_Tet4.back(), j, side_info);
208 }
209 }
210 }
211
212 // Retain element extra integers
213 for (unsigned int i = 0; i < 2; i++)
214 for (unsigned int j = 0; j < n_elem_extra_ids; j++)
215 {
216 elems_Tet4[i]->set_extra_integer(j, exist_extra_ids[j]);
217 }
218}

◆ pyramidNodesToTetNodesDeterminer() [1/2]

void MooseMeshElementConversionUtils::pyramidNodesToTetNodesDeterminer ( std::vector< const Node * > &  pyramid_nodes,
std::vector< std::vector< unsigned int > > &  rotated_tet_face_indices,
std::vector< std::vector< const Node * > > &  tet_nodes_list 
)

For a rotated nodes that can form a PYRAMID5 element, create a series of four-node set that can form TET4 elements to replace the original PYRAMID5 element.

The QUAD4 face of the PYRAMID5 element will be split by the diagonal line that involves the node with the minimum id of that face.

Parameters
pyramid_nodesA vector of pointers to the nodes that can form a PYRAMID5 element
rotated_tet_face_indicesA vector of vectors of the original face indices of the PYRAMID5 element corresponding to the faces of the newly created TET4 elements
tet_nodes_lista vector of vectors of pointers to the nodes that can form TET4 elements

Referenced by pyramidElemSplitter(), and CutMeshByLevelSetGeneratorBase::tet4ElemCutter().

◆ pyramidNodesToTetNodesDeterminer() [2/2]

void MooseMeshElementConversionUtils::pyramidNodesToTetNodesDeterminer ( std::vector< const Node * > &  pyramid_nodes,
std::vector< std::vector< unsigned int > > &  rotated_tet_face_indices,
std::vector< std::vector< const Node * > > &  tet_nodes_list 
)

Definition at line 597 of file MooseMeshElementConversionUtils.C.

600{
601 // Find the node with the minimum id, ignoring the top node
602 std::vector<dof_id_type> node_ids(4);
603 for (unsigned int i = 0; i < 4; i++)
604 node_ids[i] = pyramid_nodes[i]->id();
605
606 const unsigned int min_node_id_index = std::distance(
607 std::begin(node_ids), std::min_element(std::begin(node_ids), std::end(node_ids)));
608
609 // Rotate the node and face indices based on the identified minimum nodes
610 // After the rotation, we guarantee that the minimum node is the first node (Node 0)
611 // This makes the splitting process simpler
612 std::vector<unsigned int> face_rotation;
613 std::vector<unsigned int> rotated_indices;
614 nodeRotationPYRAMID5(min_node_id_index, face_rotation, rotated_indices);
615 std::vector<const Node *> rotated_pyramid_nodes;
616 for (unsigned int i = 0; i < 5; i++)
617 rotated_pyramid_nodes.push_back(pyramid_nodes[rotated_indices[i]]);
618
619 // There is only one quad face in a pyramid element, so the splitting selection is binary
620 const std::vector<std::vector<unsigned int>> tet_nodes_set = {{0, 1, 2, 4}, {0, 2, 3, 4}};
621 const std::vector<std::vector<unsigned int>> tet_face_indices = {{4, 0, 1, 5}, {4, 5, 2, 3}};
622
623 // Based on the determined splitting direction, determine the nodes of each resulting TET4
624 // elements after the splitting.
625 for (const auto & tet_face_index : tet_face_indices)
626 {
627 rotated_tet_face_indices.push_back(std::vector<unsigned int>());
628 for (const auto & face_index : tet_face_index)
629 {
630 if (face_index < 5)
631 rotated_tet_face_indices.back().push_back(face_rotation[face_index]);
632 else
633 rotated_tet_face_indices.back().push_back(5);
634 }
635 }
636
637 for (const auto & tet_nodes : tet_nodes_set)
638 {
639 tet_nodes_list.push_back(std::vector<const Node *>());
640 for (const auto & tet_node : tet_nodes)
641 tet_nodes_list.back().push_back(rotated_pyramid_nodes[tet_node]);
642 }
643}
void nodeRotationPYRAMID5(unsigned int min_id_index, std::vector< unsigned int > &face_rotation, std::vector< unsigned int > &node_rotation)
Rotate a PYRAMID5 element nodes to ensure that the node with the minimum id is the first node for the...

◆ quadFaceDiagonalDirection() [1/2]

bool MooseMeshElementConversionUtils::quadFaceDiagonalDirection ( const std::vector< const Node * > &  quad_nodes)

For a QUAD4 element, determine the direction of the diagonal line that involves the node with the minimum id of that element.

Parameters
quad_nodesA vector of pointers to the nodes that can form a QUAD4 element
Returns
a boolean value indicating the direction of the diagonal line

Referenced by prismNodesToTetNodesDeterminer(), and quadFaceDiagonalDirectionsHex().

◆ quadFaceDiagonalDirection() [2/2]

bool MooseMeshElementConversionUtils::quadFaceDiagonalDirection ( const std::vector< const Node * > &  quad_nodes)

Definition at line 372 of file MooseMeshElementConversionUtils.C.

373{
374 const std::vector<dof_id_type> node_ids = {
375 quad_nodes[0]->id(), quad_nodes[1]->id(), quad_nodes[2]->id(), quad_nodes[3]->id()};
376 const unsigned int min_id_index = std::distance(
377 std::begin(node_ids), std::min_element(std::begin(node_ids), std::end(node_ids)));
378 if (min_id_index == 0 || min_id_index == 2)
379 return true;
380 else
381 return false;
382}

◆ quadFaceDiagonalDirectionsHex() [1/2]

std::vector< bool > MooseMeshElementConversionUtils::quadFaceDiagonalDirectionsHex ( const std::vector< const Node * > &  hex_nodes)

For a HEX8 element, determine the direction of the diagonal line of each face that involves the node with the minimum id of that face.

Parameters
hex_nodesA vector of pointers to the nodes that can form a HEX8 element
Returns
a vector of boolean values indicating the direction of the diagonal line of each face

Referenced by hexNodesToTetNodesDeterminer().

◆ quadFaceDiagonalDirectionsHex() [2/2]

std::vector< bool > MooseMeshElementConversionUtils::quadFaceDiagonalDirectionsHex ( const std::vector< const Node * > &  hex_nodes)

Definition at line 354 of file MooseMeshElementConversionUtils.C.

355{
356 // Bottom/Top; Front/Back; Right/Left
357 const std::vector<std::vector<unsigned int>> face_indices = {
358 {0, 1, 2, 3}, {4, 5, 6, 7}, {0, 1, 5, 4}, {2, 3, 7, 6}, {1, 2, 6, 5}, {3, 0, 4, 7}};
359 std::vector<bool> diagonal_directions;
360 for (const auto & face_index : face_indices)
361 {
362 std::vector<const Node *> quad_nodes = {hex_nodes[face_index[0]],
363 hex_nodes[face_index[1]],
364 hex_nodes[face_index[2]],
365 hex_nodes[face_index[3]]};
366 diagonal_directions.push_back(quadFaceDiagonalDirection(quad_nodes));
367 }
368 return diagonal_directions;
369}
bool quadFaceDiagonalDirection(const std::vector< const Node * > &quad_nodes)
For a QUAD4 element, determine the direction of the diagonal line that involves the node with the min...

◆ retainEEID() [1/2]

void MooseMeshElementConversionUtils::retainEEID ( MeshBase &  mesh,
const dof_id_type &  elem_id,
Elem *  new_elem_ptr 
)

Retain the extra integer of the original element in a new element.

Parameters
meshThe mesh containing the element
elem_idThe ID of the original element
new_elem_ptrThe pointer to the new element that will retain the extra integer

Referenced by convertPyramid5Elem(), createUnitPyramid5FromHex8(), createUnitTet4FromHex8(), and createUnitTet4FromPrism6().

◆ retainEEID() [2/2]

void MooseMeshElementConversionUtils::retainEEID ( MeshBase mesh,
const dof_id_type elem_id,
Elem new_elem_ptr 
)

Definition at line 1073 of file MooseMeshElementConversionUtils.C.

1074{
1075 const unsigned int n_eeid = mesh.n_elem_integers();
1076 for (const auto & i : make_range(n_eeid))
1077 new_elem_ptr->set_extra_integer(i, mesh.elem_ptr(elem_id)->get_extra_integer(i));
1078}

◆ tetNodesForHex()

std::vector< std::vector< unsigned int > > MooseMeshElementConversionUtils::tetNodesForHex ( const std::vector< bool >  diagonal_directions,
std::vector< std::vector< unsigned int > > &  tet_face_indices 
)

Creates sets of four nodes indices that can form TET4 elements to replace the original HEX8 element.

Parameters
diagonal_directionsA vector of boolean values indicating the direction of the diagonal line of each face; true means the diagonal line is connecting node 0 and node 2, false means the diagonal line is connecting node 1 and node 3 of that quad face
tet_face_indicesA vector of vectors of the original face indices of the HEX8 element corresponding to the faces of the newly created TET4 elements
Returns
a vector of vectors of node indices that can form TET4 elements

Definition at line 385 of file MooseMeshElementConversionUtils.C.

387{
388 const std::vector<std::vector<bool>> possible_inputs = {{true, true, true, true, true, false},
389 {true, true, true, true, false, false},
390 {true, true, true, false, true, false},
391 {true, false, true, true, true, false},
392 {true, false, true, true, false, false},
393 {true, false, true, false, true, false},
394 {true, false, true, false, false, false}};
395
396 const unsigned int input_index = std::distance(
397 std::begin(possible_inputs),
398 std::find(std::begin(possible_inputs), std::end(possible_inputs), diagonal_directions));
399
400 switch (input_index)
401 {
402 case 0:
403 tet_face_indices = {
404 {0, 6, 2, 6}, {1, 6, 2, 6}, {1, 6, 5, 6}, {0, 6, 3, 4}, {6, 6, 3, 6}, {6, 4, 5, 6}};
405 return {{0, 1, 2, 6}, {0, 5, 1, 6}, {0, 4, 5, 6}, {0, 2, 3, 7}, {0, 6, 2, 7}, {0, 4, 6, 7}};
406 case 1:
407 tet_face_indices = {
408 {0, 1, 2, 6}, {6, 6, 2, 6}, {6, 6, 5, 1}, {0, 6, 3, 4}, {6, 6, 3, 6}, {6, 4, 5, 6}};
409 return {{0, 1, 2, 5}, {0, 2, 6, 5}, {0, 6, 4, 5}, {0, 2, 3, 7}, {0, 6, 2, 7}, {0, 4, 6, 7}};
410 case 2:
411 tet_face_indices = {
412 {0, 6, 2, 6}, {1, 6, 2, 6}, {1, 6, 5, 6}, {4, 6, 5, 6}, {4, 6, 3, 6}, {0, 6, 3, 6}};
413 return {{0, 1, 2, 6}, {0, 5, 1, 6}, {0, 4, 5, 6}, {0, 7, 4, 6}, {0, 3, 7, 6}, {0, 2, 3, 6}};
414 case 3:
415 tet_face_indices = {
416 {4, 6, 5, 1}, {6, 6, 5, 6}, {6, 1, 2, 6}, {4, 0, 3, 6}, {6, 6, 3, 6}, {6, 6, 2, 0}};
417 return {{0, 7, 4, 5}, {0, 6, 7, 5}, {0, 1, 6, 5}, {0, 3, 7, 2}, {0, 7, 6, 2}, {0, 6, 1, 2}};
418 case 4:
419 tet_face_indices = {{0, 1, 2, 6}, {0, 6, 3, 4}, {5, 4, 6, 1}, {5, 6, 3, 2}, {6, 6, 6, 6}};
420 return {{0, 1, 2, 5}, {0, 2, 3, 7}, {4, 7, 5, 0}, {5, 7, 6, 2}, {0, 2, 7, 5}};
421 case 5:
422 tet_face_indices = {
423 {4, 6, 5, 1}, {6, 6, 5, 6}, {6, 1, 2, 6}, {2, 6, 6, 0}, {3, 6, 6, 0}, {3, 6, 6, 4}};
424 return {{0, 7, 4, 5}, {0, 6, 7, 5}, {0, 1, 6, 5}, {1, 6, 2, 0}, {2, 6, 3, 0}, {3, 6, 7, 0}};
425 case 6:
426 tet_face_indices = {
427 {1, 4, 5, 6}, {6, 6, 5, 6}, {6, 6, 3, 4}, {1, 6, 2, 0}, {6, 6, 2, 6}, {6, 0, 3, 6}};
428 return {{0, 4, 5, 7}, {0, 5, 6, 7}, {0, 6, 3, 7}, {0, 5, 1, 2}, {0, 6, 5, 2}, {0, 3, 6, 2}};
429 default:
430 mooseError("Unexpected input.");
431 }
432}

Referenced by hexNodesToTetNodesDeterminer().

◆ tetNodesForPrism()

std::vector< std::vector< unsigned int > > MooseMeshElementConversionUtils::tetNodesForPrism ( const bool  diagonal_direction,
std::vector< std::vector< unsigned int > > &  tet_face_indices 
)

Creates sets of four nodes indices that can form TET4 elements to replace the original PRISM6 element.

Parameters
diagonal_directionA boolean value indicating the direction of the diagonal line of Face 2
tet_face_indicesA vector of vectors of the original face indices of the PRISM6 element corresponding to the faces of the newly created TET4 elements
Returns
a vector of vectors of node indices that can form TET4 elements

Definition at line 559 of file MooseMeshElementConversionUtils.C.

561{
562
563 if (diagonal_direction)
564 {
565 tet_face_indices = {{4, 3, 5, 1}, {2, 1, 5, 5}, {2, 5, 3, 0}};
566 return {{3, 5, 4, 0}, {1, 4, 5, 0}, {1, 5, 2, 0}};
567 }
568 else
569 {
570 tet_face_indices = {{4, 3, 5, 1}, {2, 1, 5, 0}, {2, 5, 5, 3}};
571 return {{3, 5, 4, 0}, {1, 4, 2, 0}, {2, 4, 5, 0}};
572 }
573}

Referenced by prismNodesToTetNodesDeterminer().

◆ transitionLayerGenerator() [1/2]

void MooseMeshElementConversionUtils::transitionLayerGenerator ( MeshBase &  mesh,
const std::vector< BoundaryName > &  boundary_names,
const unsigned int  conversion_element_layer_number,
const bool  external_boundaries_checking 
)

Generate a transition layer of elements with TRI3 surfaces on the given boundaries.

Parameters
meshThe mesh to be modified
boundary_namesA vector of boundary names on which the transition layer will be generated
conversion_element_layer_numberThe number of element layers to be converted on the given boundaries.
external_boundaries_checkingWhether to check if the provided boundaries are external boundaries

Referenced by BoundaryElementConversionGenerator::generate(), and XYZDelaunayGenerator::generate().

◆ transitionLayerGenerator() [2/2]

void MooseMeshElementConversionUtils::transitionLayerGenerator ( MeshBase mesh,
const std::vector< BoundaryName > &  boundary_names,
const unsigned int  conversion_element_layer_number,
const bool  external_boundaries_checking 
)

Definition at line 1081 of file MooseMeshElementConversionUtils.C.

1085{
1086 mooseAssert(mesh.is_serial(),
1087 "This method only supports serial meshes. If you are calling this method with a "
1088 "distributed mesh, please serialize it first.");
1089 // The base subdomain ID to shift the original elements because of the element type change
1090 const auto sid_shift_base = MooseMeshUtils::getNextFreeSubdomainID(mesh);
1091 // The maximum subdomain ID that would be involved is sid_shift_base * 3, we would like to make
1092 // sure it would not overflow
1093 if (sid_shift_base * 3 > std::numeric_limits<subdomain_id_type>::max())
1094 throw MooseException("subdomain id overflow");
1095
1096 // It would be convenient to have a single boundary id instead of a vector.
1097 const auto uniform_tmp_bid = MooseMeshUtils::getNextFreeBoundaryID(mesh);
1098
1099 // Check the boundaries and merge them
1100 std::vector<boundary_id_type> boundary_ids;
1101 for (const auto & sideset : boundary_names)
1102 {
1104 throw MooseException("The provided boundary '", sideset, "' was not found within the mesh");
1105 boundary_ids.push_back(MooseMeshUtils::getBoundaryID(sideset, mesh));
1106 MooseMeshUtils::changeBoundaryId(mesh, boundary_ids.back(), uniform_tmp_bid, false);
1107 }
1108
1109 auto & sideset_map = mesh.get_boundary_info().get_sideset_map();
1110 auto side_list = mesh.get_boundary_info().build_side_list();
1111
1112 std::vector<std::pair<dof_id_type, std::vector<unsigned int>>> elems_list;
1113 std::vector<std::set<dof_id_type>> layered_elems_list;
1114 layered_elems_list.push_back(std::set<dof_id_type>());
1115 // Need to collect the list of elements that need to be converted
1116 if (!mesh.is_prepared())
1118 for (const auto & side_info : side_list)
1119 {
1120 if (std::get<2>(side_info) == uniform_tmp_bid)
1121 {
1122 // Check if the involved side is TRI3 or QUAD4
1123 // We do not limit the element type in the input mesh
1124 // As long as the involved boundaries only consist of TRI3 and QUAD4 sides,
1125 // this generator will work
1126 // As the side element of a quadratic element is still a linear element in libMesh,
1127 // we need to check the element's default_side_order() first
1128 if (mesh.elem_ptr(std::get<0>(side_info))->default_side_order() != 1)
1129 throw MooseException(
1130 "The provided boundary set contains non-linear side elements, which is not supported.");
1131 const auto side_type =
1132 mesh.elem_ptr(std::get<0>(side_info))->side_ptr(std::get<1>(side_info))->type();
1133 layered_elems_list.back().emplace(std::get<0>(side_info));
1134 // If we enforce external boundary, then a non-null neighbor leads to an error
1135 // Otherwise, we need to convert both sides, that means the neighbor information also needs to
1136 // be added
1137 const auto neighbor_ptr =
1138 mesh.elem_ptr(std::get<0>(side_info))->neighbor_ptr(std::get<1>(side_info));
1139 if (neighbor_ptr)
1140 {
1141 if (external_boundaries_checking)
1142 throw MooseException(
1143 "The provided boundary contains non-external sides, which is required when "
1144 "external_boundaries_checking is enabled.");
1145 else
1146 layered_elems_list.back().emplace(neighbor_ptr->id());
1147 }
1148
1149 if (conversion_element_layer_number == 1)
1150 {
1151 if (side_type == TRI3)
1152 continue; // Already TRI3, no need to convert
1153 else if (side_type == QUAD4)
1154 {
1155 auto pit = std::find_if(elems_list.begin(),
1156 elems_list.end(),
1157 [elem_id = std::get<0>(side_info)](const auto & p)
1158 { return p.first == elem_id; });
1159 if (elems_list.size() && pit != elems_list.end())
1160 {
1161 pit->second.push_back(std::get<1>(side_info));
1162 }
1163 else
1164 elems_list.push_back(std::make_pair(
1165 std::get<0>(side_info), std::vector<unsigned int>({std::get<1>(side_info)})));
1166 if (neighbor_ptr)
1167 {
1168 auto sit = std::find_if(elems_list.begin(),
1169 elems_list.end(),
1170 [elem_id = neighbor_ptr->id()](const auto & p)
1171 { return p.first == elem_id; });
1172 if (elems_list.size() && sit != elems_list.end())
1173 {
1174 sit->second.push_back(
1175 neighbor_ptr->which_neighbor_am_i(mesh.elem_ptr(std::get<0>(side_info))));
1176 }
1177 else
1178 {
1179 elems_list.push_back(std::make_pair(
1180 neighbor_ptr->id(),
1181 std::vector<unsigned int>(
1182 {neighbor_ptr->which_neighbor_am_i(mesh.elem_ptr(std::get<0>(side_info)))})));
1183 }
1184 }
1185 }
1186 else if (side_type == C0POLYGON)
1187 throw MooseException("The provided boundary set contains C0POLYGON side elements, which "
1188 "is not supported.");
1189 else
1190 mooseAssert(false,
1191 "Impossible scenario: a linear non-polygon side element that is neither TRI3 "
1192 "nor QUAD4.");
1193 }
1194 }
1195 }
1196
1197 if (conversion_element_layer_number > 1)
1198 {
1199 std::set<dof_id_type> total_elems_set(layered_elems_list.back());
1200
1201 while (layered_elems_list.back().size() &&
1202 layered_elems_list.size() < conversion_element_layer_number)
1203 {
1204 layered_elems_list.push_back(std::set<dof_id_type>());
1205 for (const auto & elem_id : *(layered_elems_list.end() - 2))
1206 {
1207 for (const auto & i_side : make_range(mesh.elem_ptr(elem_id)->n_sides()))
1208 {
1209 if (mesh.elem_ptr(elem_id)->neighbor_ptr(i_side) != nullptr)
1210 {
1211 const auto & neighbor_id = mesh.elem_ptr(elem_id)->neighbor_ptr(i_side)->id();
1212 if (total_elems_set.find(neighbor_id) == total_elems_set.end())
1213 {
1214 layered_elems_list.back().emplace(neighbor_id);
1215 total_elems_set.emplace(neighbor_id);
1216 }
1217 }
1218 }
1219 }
1220 }
1221 }
1222
1223 // Remove the last empty layer
1224 if (layered_elems_list.back().empty())
1225 layered_elems_list.pop_back();
1226
1227 if (conversion_element_layer_number > layered_elems_list.size())
1228 throw MooseException("There is fewer layers of elements in the input mesh than the requested "
1229 "number of layers to convert.");
1230
1231 std::vector<std::pair<dof_id_type, bool>> original_elems;
1232 // construct a list of the element to convert to tet4
1233 // Convert at most n_layer_conversion layers of elements
1234 const unsigned int n_layer_conversion = layered_elems_list.size() - 1;
1235 for (unsigned int i = 0; i < n_layer_conversion; ++i)
1236 for (const auto & elem_id : layered_elems_list[i])
1237 {
1238 // As these elements will become TET4 elements, we need to shift the subdomain ID
1239 // But we do not need to convert original TET4 elements
1240 if (mesh.elem_ptr(elem_id)->type() != TET4)
1241 {
1242 original_elems.push_back(std::make_pair(elem_id, false));
1243 mesh.elem_ptr(elem_id)->subdomain_id() += sid_shift_base;
1244 }
1245 }
1246
1247 const subdomain_id_type block_id_to_remove = sid_shift_base * 3;
1248
1249 std::vector<dof_id_type> converted_elems_ids_to_track;
1251 mesh, original_elems, converted_elems_ids_to_track, block_id_to_remove, false);
1252
1253 // Now we need to convert the elements on the transition layer
1254 // First, we need to identify that the sides that are on the interface with previous layer (all
1255 // TET layers)
1256 if (n_layer_conversion)
1257 {
1258 for (const auto & elem_id : layered_elems_list[n_layer_conversion])
1259 {
1260 for (const auto & i_side : make_range(mesh.elem_ptr(elem_id)->n_sides()))
1261 {
1262 if (mesh.elem_ptr(elem_id)->neighbor_ptr(i_side) != nullptr &&
1263 layered_elems_list[n_layer_conversion - 1].count(
1264 mesh.elem_ptr(elem_id)->neighbor_ptr(i_side)->id()))
1265 {
1266 if (elems_list.size() && elems_list.back().first == elem_id)
1267 elems_list.back().second.push_back(i_side);
1268 else
1269 elems_list.push_back(std::make_pair(elem_id, std::vector<unsigned int>({i_side})));
1270 }
1271 }
1272 }
1273 }
1274
1275 // Now convert the elements
1276 for (const auto & elem_info : elems_list)
1277 {
1278 // Find the involved sidesets of the element so that we can retain them
1279 std::vector<std::vector<boundary_id_type>> elem_side_info(
1280 mesh.elem_ptr(elem_info.first)->n_sides());
1281 auto side_range = sideset_map.equal_range(mesh.elem_ptr(elem_info.first));
1282 for (auto i = side_range.first; i != side_range.second; ++i)
1283 elem_side_info[i->second.first].push_back(i->second.second);
1284
1286 mesh, elem_info.first, elem_info.second, elem_side_info, sid_shift_base);
1287 }
1288
1289 // delete the original elements that were converted
1290 for (const auto & elem_info : elems_list)
1291 mesh.elem_ptr(elem_info.first)->subdomain_id() = block_id_to_remove;
1292 for (auto elem_it = mesh.active_subdomain_elements_begin(block_id_to_remove);
1293 elem_it != mesh.active_subdomain_elements_end(block_id_to_remove);
1294 elem_it++)
1295 mesh.delete_elem(*elem_it);
1296 // delete temporary boundary id
1297 mesh.get_boundary_info().remove_id(uniform_tmp_bid);
1298
1299 mesh.contract();
1301}
std::set< std::string > sideset
void remove_id(boundary_id_type id, bool global=false)
const std::multimap< const Elem *, std::pair< unsigned short int, boundary_id_type > > & get_sideset_map() const
dof_id_type id() const
virtual Order default_side_order() const
virtual unsigned int n_sides() const=0
const Elem * neighbor_ptr(unsigned int i) const
bool is_prepared() const
void unset_is_prepared()
virtual void find_neighbors(const bool reset_remote_elements=false, const bool reset_current_list=true, const bool assert_valid=true)=0
void convertElem(MeshBase &mesh, const dof_id_type &elem_id, const std::vector< unsigned int > &side_indices, const std::vector< std::vector< boundary_id_type > > &elem_side_info, const SubdomainID &subdomain_id_shift_base)
Convert the element to an element with TRI3 side-elements on the user-specified sides by modifying th...
void convert3DMeshToAllTet4(MeshBase &mesh, const std::vector< std::pair< dof_id_type, bool > > &elems_to_process, std::vector< dof_id_type > &converted_elems_ids_to_track, const subdomain_id_type block_id_to_remove, const bool delete_block_to_remove)
Convert all the elements in a 3D mesh, consisting of only linear elements, into TET4 elements.
void changeBoundaryId(MeshBase &mesh, const libMesh::boundary_id_type old_id, const libMesh::boundary_id_type new_id, bool delete_prev)
Changes the old boundary ID to a new ID in the mesh.
BoundaryID getNextFreeBoundaryID(MeshBase &input_mesh)
Checks input mesh and returns the largest boundary ID in the mesh plus one, which is a boundary ID in...
SubdomainID getNextFreeSubdomainID(MeshBase &input_mesh)
Checks input mesh and returns max(block ID) + 1, which represents a block ID that is not currently in...
BoundaryID getBoundaryID(const BoundaryName &boundary_name, const MeshBase &mesh)
Gets the boundary ID associated with the given BoundaryName.
bool hasBoundaryNameOrID(const MeshBase &mesh, const BoundaryName &name_or_id)
Whether a particular boundary name or ID exists in the mesh.