https://mooseframework.inl.gov
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
void convert3DMeshToAllTet4 (MeshBase &mesh)
 Convert all the elements in a 3D mesh consisting of only linear elements into TET4 elements. More...
 
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. More...
 
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. More...
 
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). More...
 
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. More...
 
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. More...
 
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). More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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 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)
 Collect the boundary information of the given element in a mesh. More...
 
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 1235 of file MooseMeshElementConversionUtils.C.

1241 {
1242  for (const auto & subdomain_id : original_subdomain_ids)
1243  {
1244  if (MooseMeshUtils::hasSubdomainID(mesh, subdomain_id + sid_shift_base))
1245  {
1246  const SubdomainName new_name =
1247  (mesh.subdomain_name(subdomain_id).empty() ? std::to_string(subdomain_id)
1248  : mesh.subdomain_name(subdomain_id)) +
1249  '_' + tet_suffix;
1250  if (MooseMeshUtils::hasSubdomainName(mesh, new_name))
1251  throw MooseException(
1252  "This suffix for converted TET4 elements results in a subdomain name, " + new_name +
1253  ", that already exists in the mesh. Please choose a different suffix.");
1254  mesh.subdomain_name(subdomain_id + sid_shift_base) = new_name;
1255  }
1256  if (MooseMeshUtils::hasSubdomainID(mesh, subdomain_id + 2 * sid_shift_base))
1257  {
1258  const SubdomainName new_name =
1259  (mesh.subdomain_name(subdomain_id).empty() ? std::to_string(subdomain_id)
1260  : mesh.subdomain_name(subdomain_id)) +
1261  '_' + pyramid_suffix;
1262  if (MooseMeshUtils::hasSubdomainName(mesh, new_name))
1263  throw MooseException(
1264  "This suffix for converted PYRAMID5 elements results in a subdomain name, " + new_name +
1265  ", that already exists in the mesh. Please choose a different suffix.");
1266  mesh.subdomain_name(subdomain_id + 2 * sid_shift_base) = new_name;
1267  }
1268  }
1269 }
MeshBase & mesh
bool hasSubdomainID(const MeshBase &input_mesh, const SubdomainID &id)
Whether a particular subdomain ID exists in the mesh.
std::string & subdomain_name(subdomain_id_type id)
bool hasSubdomainName(const MeshBase &input_mesh, const SubdomainName &name)
Whether a particular subdomain name exists in the mesh.
Provides a way for users to bail out of the current solve.

◆ convert3DMeshToAllTet4() [1/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 ElementsToTetrahedronsConverter::generate(), CutMeshByLevelSetGeneratorBase::generate(), and transitionLayerGenerator().

◆ convert3DMeshToAllTet4() [2/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() [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 
)

Definition at line 585 of file MooseMeshElementConversionUtils.C.

590 {
591  mooseAssert(mesh.is_serial(),
592  "This method only supports serial meshes. If you are calling this method with a "
593  "distributed mesh, please serialize it first.");
594  std::vector<dof_id_type> converted_elems_ids_to_retain;
595  // Build boundary information of the mesh
596  BoundaryInfo & boundary_info = mesh.get_boundary_info();
597  const auto bdry_side_list = boundary_info.build_side_list();
598  for (const auto & elem_to_process : elems_to_process)
599  {
600  switch (mesh.elem_ptr(elem_to_process.first)->type())
601  {
602  case ElemType::HEX8:
603  hexElemSplitter(mesh,
604  bdry_side_list,
605  elem_to_process.first,
606  elem_to_process.second ? converted_elems_ids_to_track
607  : converted_elems_ids_to_retain);
608  mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
609  break;
610  case ElemType::PYRAMID5:
611  pyramidElemSplitter(mesh,
612  bdry_side_list,
613  elem_to_process.first,
614  elem_to_process.second ? converted_elems_ids_to_track
615  : converted_elems_ids_to_retain);
616  mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
617  break;
618  case ElemType::PRISM6:
619  prismElemSplitter(mesh,
620  bdry_side_list,
621  elem_to_process.first,
622  elem_to_process.second ? converted_elems_ids_to_track
623  : converted_elems_ids_to_retain);
624  mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
625  break;
626  case ElemType::TET4:
627  if (elem_to_process.second)
628  converted_elems_ids_to_track.push_back(elem_to_process.first);
629  else
630  converted_elems_ids_to_retain.push_back(elem_to_process.first);
631  break;
632  default:
633  mooseError("Unexpected element type.");
634  }
635  }
636 
637  if (delete_block_to_remove)
638  {
639  for (auto elem_it = mesh.active_subdomain_elements_begin(block_id_to_remove);
640  elem_it != mesh.active_subdomain_elements_end(block_id_to_remove);
641  elem_it++)
642  mesh.delete_elem(*elem_it);
643 
644  mesh.contract();
646  }
647 }
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:323
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
MeshBase & mesh
const BoundaryInfo & get_boundary_info() const
void build_side_list(std::vector< dof_id_type > &element_id_list, std::vector< unsigned short int > &side_list, std::vector< boundary_id_type > &bc_id_list) const
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)
virtual bool is_serial() const
virtual void delete_elem(Elem *e)=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)
virtual const Elem * elem_ptr(const dof_id_type i) const=0
virtual bool contract()=0
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)
subdomain_id_type subdomain_id() const
virtual ElemType type() const=0

◆ convert3DMeshToAllTet4() [4/4]

void MooseMeshElementConversionUtils::convert3DMeshToAllTet4 ( MeshBase mesh)

Definition at line 650 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  // Subdomain ID for new utility blocks must be new
656  std::set<subdomain_id_type> subdomain_ids_set;
657  mesh.subdomain_ids(subdomain_ids_set);
658  const subdomain_id_type max_subdomain_id = *subdomain_ids_set.rbegin();
659  const subdomain_id_type block_id_to_remove = max_subdomain_id + 1;
660  std::vector<std::pair<dof_id_type, bool>> original_elems;
661 
662  for (auto elem_it = mesh.active_elements_begin(); elem_it != mesh.active_elements_end();
663  elem_it++)
664  {
665  if ((*elem_it)->default_order() != Order::FIRST)
666  mooseError("Only first order elements are supported for cutting.");
667  original_elems.push_back(std::make_pair((*elem_it)->id(), false));
668  }
669 
670  std::vector<dof_id_type> converted_elems_ids_to_track;
671 
673  mesh, original_elems, converted_elems_ids_to_track, block_id_to_remove, true);
674 }
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:323
MeshBase & mesh
virtual bool is_serial() const
void subdomain_ids(std::set< subdomain_id_type > &ids, const bool global=true) const

◆ 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 694 of file MooseMeshElementConversionUtils.C.

699 {
700  const auto & elem_type = mesh.elem_ptr(elem_id)->type();
701  switch (elem_type)
702  {
703  case HEX8:
704  // HEX8 to PYRAMID5 (+2*subdomain_id_shift_base)
705  // HEX8 to TET4 (+subdomain_id_shift_base)
706  convertHex8Elem(mesh, elem_id, side_indices, elem_side_info, subdomain_id_shift_base);
707  break;
708  case PRISM6:
709  // PRISM6 to TET4 (+subdomain_id_shift_base)
710  // PRISM6 to PYRAMID5 (+2*subdomain_id_shift_base)
711  convertPrism6Elem(mesh, elem_id, side_indices, elem_side_info, subdomain_id_shift_base);
712  break;
713  case PYRAMID5:
714  // PYRAMID5 to TET4 (+subdomain_id_shift_base)
715  convertPyramid5Elem(mesh, elem_id, elem_side_info, subdomain_id_shift_base);
716  break;
717  default:
718  mooseAssert(false,
719  "The provided element type '" + std::to_string(elem_type) +
720  "' is not supported and is not supposed to be passed to this function. "
721  "Only HEX8, PRISM6 and PYRAMID5 are supported.");
722  }
723 }
HEX8
MeshBase & 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)
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)
virtual const Elem * elem_ptr(const dof_id_type i) const=0
PYRAMID5
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)
PRISM6
virtual ElemType type() const=0

◆ 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

◆ 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 726 of file MooseMeshElementConversionUtils.C.

Referenced by convertElem().

731 {
732  // We add a node at the centroid of the HEX8 element
733  // With this node, the HEX8 can be converted into 6 PYRAMID5 elements
734  // For the PYRAMID5 element at the 'side_indices', they can further be converted into 2 TET4
735  // elements
736  const Point elem_cent = mesh.elem_ptr(elem_id)->true_centroid();
737  auto new_node = mesh.add_point(elem_cent);
738  for (const auto & i_side : make_range(mesh.elem_ptr(elem_id)->n_sides()))
739  {
740  if (std::find(side_indices.begin(), side_indices.end(), i_side) != side_indices.end())
742  mesh, elem_id, i_side, new_node, elem_side_info[i_side], subdomain_id_shift_base);
743  else
745  mesh, elem_id, i_side, new_node, elem_side_info[i_side], subdomain_id_shift_base);
746  }
747 }
virtual Point true_centroid() const
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)
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
Find a value in an array.
Definition: KokkosUtils.h:42
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)
MeshBase & mesh
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
virtual const Elem * elem_ptr(const dof_id_type i) const=0
virtual unsigned int n_sides() const=0
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

◆ 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 832 of file MooseMeshElementConversionUtils.C.

Referenced by convertElem().

837 {
838  // We add a node at the centroid of the PRISM6 element
839  // With this node, the PRISM6 can be converted into 3 PYRAMID5 elements and 2 TET4 elements
840  // For the PYRAMID5 element, it can further be converted into 2 TET3 elements
841  const Point elem_cent = mesh.elem_ptr(elem_id)->true_centroid();
842  auto new_node = mesh.add_point(elem_cent);
843  for (const auto & i_side : make_range(mesh.elem_ptr(elem_id)->n_sides()))
844  {
845  if (i_side % 4 == 0 ||
846  std::find(side_indices.begin(), side_indices.end(), i_side) != side_indices.end())
848  mesh, elem_id, i_side, new_node, elem_side_info[i_side], subdomain_id_shift_base);
849  else
851  mesh, elem_id, i_side, new_node, elem_side_info[i_side], subdomain_id_shift_base);
852  }
853 }
virtual Point true_centroid() const
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
Find a value in an array.
Definition: KokkosUtils.h:42
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)
MeshBase & mesh
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 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)
virtual const Elem * elem_ptr(const dof_id_type i) const=0
virtual unsigned int n_sides() const=0
IntRange< T > make_range(T beg, T end)

◆ 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

◆ 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 939 of file MooseMeshElementConversionUtils.C.

Referenced by convertElem().

943 {
944  // A Pyramid5 element has only one QUAD4 face, so we can convert it to 2 TET4 elements
945  unsigned int lid_index = 0;
946  for (const auto & i : make_range(1, 4))
947  {
948  if (mesh.elem_ptr(elem_id)->side_ptr(4)->node_ptr(i)->id() <
949  mesh.elem_ptr(elem_id)->side_ptr(4)->node_ptr(lid_index)->id())
950  lid_index = i;
951  }
952  auto new_elem_0 = std::make_unique<Tet4>();
953  new_elem_0->set_node(
954  0,
955  mesh.elem_ptr(elem_id)->side_ptr(4)->node_ptr(MathUtils::euclideanMod(2 - lid_index % 2, 4)));
956  new_elem_0->set_node(
957  1,
958  mesh.elem_ptr(elem_id)->side_ptr(4)->node_ptr(MathUtils::euclideanMod(1 - lid_index % 2, 4)));
959  new_elem_0->set_node(
960  2,
961  mesh.elem_ptr(elem_id)->side_ptr(4)->node_ptr(MathUtils::euclideanMod(0 - lid_index % 2, 4)));
962  new_elem_0->set_node(3, mesh.elem_ptr(elem_id)->node_ptr(4));
963  new_elem_0->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + subdomain_id_shift_base;
964  auto new_elem_ptr_0 = mesh.add_elem(std::move(new_elem_0));
965  retainEEID(mesh, elem_id, new_elem_ptr_0);
966 
967  auto new_elem_1 = std::make_unique<Tet4>();
968  new_elem_1->set_node(
969  0,
970  mesh.elem_ptr(elem_id)->side_ptr(4)->node_ptr(MathUtils::euclideanMod(3 - lid_index % 2, 4)));
971  new_elem_1->set_node(
972  1,
973  mesh.elem_ptr(elem_id)->side_ptr(4)->node_ptr(MathUtils::euclideanMod(2 - lid_index % 2, 4)));
974  new_elem_1->set_node(
975  2,
976  mesh.elem_ptr(elem_id)->side_ptr(4)->node_ptr(MathUtils::euclideanMod(0 - lid_index % 2, 4)));
977  new_elem_1->set_node(3, mesh.elem_ptr(elem_id)->node_ptr(4));
978  new_elem_1->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + subdomain_id_shift_base;
979  auto new_elem_ptr_1 = mesh.add_elem(std::move(new_elem_1));
980  retainEEID(mesh, elem_id, new_elem_ptr_1);
981 
982  for (const auto & bid : elem_side_info[0])
983  mesh.get_boundary_info().add_side(new_elem_ptr_0, 2 - lid_index % 2, bid);
984  for (const auto & bid : elem_side_info[1])
985  if (lid_index % 2)
986  mesh.get_boundary_info().add_side(new_elem_ptr_1, 2, bid);
987  else
988  mesh.get_boundary_info().add_side(new_elem_ptr_0, 1, bid);
989  for (const auto & bid : elem_side_info[2])
990  mesh.get_boundary_info().add_side(new_elem_ptr_1, 2 + lid_index % 2, bid);
991  for (const auto & bid : elem_side_info[3])
992  if (lid_index % 2)
993  mesh.get_boundary_info().add_side(new_elem_ptr_0, 1, bid);
994  else
995  mesh.get_boundary_info().add_side(new_elem_ptr_1, 3, bid);
996  for (const auto & bid : elem_side_info[4])
997  {
998  mesh.get_boundary_info().add_side(new_elem_ptr_0, 0, bid);
999  mesh.get_boundary_info().add_side(new_elem_ptr_1, 0, bid);
1000  }
1001 }
MeshBase & mesh
const BoundaryInfo & get_boundary_info() const
std::size_t euclideanMod(T1 dividend, T2 divisor)
perform modulo operator for Euclidean division that ensures a non-negative result ...
Definition: MathUtils.h:439
void retainEEID(MeshBase &mesh, const dof_id_type &elem_id, Elem *new_elem_ptr)
virtual Elem * add_elem(Elem *e)=0
virtual const Elem * elem_ptr(const dof_id_type i) const=0
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
subdomain_id_type subdomain_id() const
const Node * node_ptr(const unsigned int i) const
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
IntRange< T > make_range(T beg, T end)

◆ 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

◆ 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 750 of file MooseMeshElementConversionUtils.C.

Referenced by convertHex8Elem(), and createUnitPyramid5FromPrism6().

756 {
757  auto new_elem = std::make_unique<Pyramid5>();
758  new_elem->set_node(0, mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(3));
759  new_elem->set_node(1, mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(2));
760  new_elem->set_node(2, mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(1));
761  new_elem->set_node(3, mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(0));
762  new_elem->set_node(4, const_cast<Node *>(new_node));
763  new_elem->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + subdomain_id_shift_base * 2;
764  auto new_elem_ptr = mesh.add_elem(std::move(new_elem));
765  retainEEID(mesh, elem_id, new_elem_ptr);
766  for (const auto & bid : side_info)
767  mesh.get_boundary_info().add_side(new_elem_ptr, 4, bid);
768 }
MeshBase & mesh
const BoundaryInfo & get_boundary_info() const
void retainEEID(MeshBase &mesh, const dof_id_type &elem_id, Elem *new_elem_ptr)
virtual Elem * add_elem(Elem *e)=0
virtual const Elem * elem_ptr(const dof_id_type i) const=0
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
subdomain_id_type subdomain_id() const
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)

◆ 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

◆ 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 926 of file MooseMeshElementConversionUtils.C.

Referenced by convertPrism6Elem().

932 {
933  // Same as Hex8
935  mesh, elem_id, side_index, new_node, side_info, subdomain_id_shift_base);
936 }
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)

◆ 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

◆ 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 771 of file MooseMeshElementConversionUtils.C.

Referenced by convertHex8Elem().

777 {
778  // We want to make sure that the QUAD4 is divided by the diagonal that involves the node with
779  // the lowest node id This may help maintain consistency for future applications
780  unsigned int lid_index = 0;
781  for (const auto & i : make_range(1, 4))
782  {
783  if (mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(i)->id() <
784  mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(lid_index)->id())
785  lid_index = i;
786  }
787 
788  auto new_elem_0 = std::make_unique<Tet4>();
789  new_elem_0->set_node(0,
790  mesh.elem_ptr(elem_id)
791  ->side_ptr(side_index)
792  ->node_ptr(MathUtils::euclideanMod(2 - lid_index % 2, 4)));
793  new_elem_0->set_node(1,
794  mesh.elem_ptr(elem_id)
795  ->side_ptr(side_index)
796  ->node_ptr(MathUtils::euclideanMod(1 - lid_index % 2, 4)));
797  new_elem_0->set_node(2,
798  mesh.elem_ptr(elem_id)
799  ->side_ptr(side_index)
800  ->node_ptr(MathUtils::euclideanMod(0 - lid_index % 2, 4)));
801  new_elem_0->set_node(3, const_cast<Node *>(new_node));
802  new_elem_0->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + subdomain_id_shift_base;
803  auto new_elem_ptr_0 = mesh.add_elem(std::move(new_elem_0));
804  retainEEID(mesh, elem_id, new_elem_ptr_0);
805 
806  auto new_elem_1 = std::make_unique<Tet4>();
807  new_elem_1->set_node(0,
808  mesh.elem_ptr(elem_id)
809  ->side_ptr(side_index)
810  ->node_ptr(MathUtils::euclideanMod(3 - lid_index % 2, 4)));
811  new_elem_1->set_node(1,
812  mesh.elem_ptr(elem_id)
813  ->side_ptr(side_index)
814  ->node_ptr(MathUtils::euclideanMod(2 - lid_index % 2, 4)));
815  new_elem_1->set_node(2,
816  mesh.elem_ptr(elem_id)
817  ->side_ptr(side_index)
818  ->node_ptr(MathUtils::euclideanMod(0 - lid_index % 2, 4)));
819  new_elem_1->set_node(3, const_cast<Node *>(new_node));
820  new_elem_1->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + subdomain_id_shift_base;
821  auto new_elem_ptr_1 = mesh.add_elem(std::move(new_elem_1));
822  retainEEID(mesh, elem_id, new_elem_ptr_1);
823 
824  for (const auto & bid : side_info)
825  {
826  mesh.get_boundary_info().add_side(new_elem_ptr_0, 0, bid);
827  mesh.get_boundary_info().add_side(new_elem_ptr_1, 0, bid);
828  }
829 }
MeshBase & mesh
const BoundaryInfo & get_boundary_info() const
std::size_t euclideanMod(T1 dividend, T2 divisor)
perform modulo operator for Euclidean division that ensures a non-negative result ...
Definition: MathUtils.h:439
void retainEEID(MeshBase &mesh, const dof_id_type &elem_id, Elem *new_elem_ptr)
virtual Elem * add_elem(Elem *e)=0
virtual const Elem * elem_ptr(const dof_id_type i) const=0
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
subdomain_id_type subdomain_id() const
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
IntRange< T > make_range(T beg, T end)

◆ 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

◆ 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 856 of file MooseMeshElementConversionUtils.C.

Referenced by convertPrism6Elem().

862 {
863  // For side 1 and side 4, they are already TRI3, so only one TET is created
864  // For side 0, 2, and 3, they are QUAD4, so we create 2 TETs
865  // We want to make sure that the QUAD4 is divided by the diagonal that involves
866  // the node with the lowest node id This may help maintain consistency for future applications
867  bool is_side_quad = (side_index % 4 != 0);
868  unsigned int lid_index = 0;
869  if (is_side_quad)
870  for (const auto & i : make_range(1, 4))
871  {
872  if (mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(i)->id() <
873  mesh.elem_ptr(elem_id)->side_ptr(side_index)->node_ptr(lid_index)->id())
874  lid_index = i;
875  }
876  // For a TRI3 side, lid_index is always 0, so the indices are always 2,1,0 here
877  auto new_elem_0 = std::make_unique<Tet4>();
878  new_elem_0->set_node(0,
879  mesh.elem_ptr(elem_id)
880  ->side_ptr(side_index)
881  ->node_ptr(MathUtils::euclideanMod(2 - lid_index % 2, 4)));
882  new_elem_0->set_node(1,
883  mesh.elem_ptr(elem_id)
884  ->side_ptr(side_index)
885  ->node_ptr(MathUtils::euclideanMod(1 - lid_index % 2, 4)));
886  new_elem_0->set_node(2,
887  mesh.elem_ptr(elem_id)
888  ->side_ptr(side_index)
889  ->node_ptr(MathUtils::euclideanMod(0 - lid_index % 2, 4)));
890  new_elem_0->set_node(3, const_cast<Node *>(new_node));
891  new_elem_0->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + subdomain_id_shift_base;
892  auto new_elem_ptr_0 = mesh.add_elem(std::move(new_elem_0));
893  retainEEID(mesh, elem_id, new_elem_ptr_0);
894 
895  Elem * new_elem_ptr_1 = nullptr;
896  if (is_side_quad)
897  {
898  auto new_elem_1 = std::make_unique<Tet4>();
899  new_elem_1->set_node(0,
900  mesh.elem_ptr(elem_id)
901  ->side_ptr(side_index)
902  ->node_ptr(MathUtils::euclideanMod(3 - lid_index % 2, 4)));
903  new_elem_1->set_node(1,
904  mesh.elem_ptr(elem_id)
905  ->side_ptr(side_index)
906  ->node_ptr(MathUtils::euclideanMod(2 - lid_index % 2, 4)));
907  new_elem_1->set_node(2,
908  mesh.elem_ptr(elem_id)
909  ->side_ptr(side_index)
910  ->node_ptr(MathUtils::euclideanMod(0 - lid_index % 2, 4)));
911  new_elem_1->set_node(3, const_cast<Node *>(new_node));
912  new_elem_1->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + subdomain_id_shift_base;
913  new_elem_ptr_1 = mesh.add_elem(std::move(new_elem_1));
914  retainEEID(mesh, elem_id, new_elem_ptr_1);
915  }
916 
917  for (const auto & bid : side_info)
918  {
919  mesh.get_boundary_info().add_side(new_elem_ptr_0, 0, bid);
920  if (new_elem_ptr_1)
921  mesh.get_boundary_info().add_side(new_elem_ptr_1, 0, bid);
922  }
923 }
virtual Node *& set_node(const unsigned int i)
MeshBase & mesh
const BoundaryInfo & get_boundary_info() const
std::size_t euclideanMod(T1 dividend, T2 divisor)
perform modulo operator for Euclidean division that ensures a non-negative result ...
Definition: MathUtils.h:439
void retainEEID(MeshBase &mesh, const dof_id_type &elem_id, Elem *new_elem_ptr)
virtual Elem * add_elem(Elem *e)=0
virtual const Elem * elem_ptr(const dof_id_type i) const=0
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
subdomain_id_type subdomain_id() const
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
IntRange< T > make_range(T beg, T end)

◆ 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

Definition at line 677 of file MooseMeshElementConversionUtils.C.

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

681 {
682  elem_side_list.resize(n_elem_sides);
683  const auto selected_bdry_side_list =
684  std::equal_range(bdry_side_list.begin(), bdry_side_list.end(), elem_id, BCTupleKeyComp{});
685  for (auto selected_bdry_side = selected_bdry_side_list.first;
686  selected_bdry_side != selected_bdry_side_list.second;
687  ++selected_bdry_side)
688  {
689  elem_side_list[std::get<1>(*selected_bdry_side)].push_back(std::get<2>(*selected_bdry_side));
690  }
691 }

◆ 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 
)

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

Definition at line 677 of file MooseMeshElementConversionUtils.C.

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

681 {
682  elem_side_list.resize(n_elem_sides);
683  const auto selected_bdry_side_list =
684  std::equal_range(bdry_side_list.begin(), bdry_side_list.end(), elem_id, BCTupleKeyComp{});
685  for (auto selected_bdry_side = selected_bdry_side_list.first;
686  selected_bdry_side != selected_bdry_side_list.second;
687  ++selected_bdry_side)
688  {
689  elem_side_list[std::get<1>(*selected_bdry_side)].push_back(std::get<2>(*selected_bdry_side));
690  }
691 }

◆ 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 
)

Definition at line 32 of file MooseMeshElementConversionUtils.C.

Referenced by convert3DMeshToAllTet4().

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

◆ 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 
)

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

◆ 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

◆ 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 231 of file MooseMeshElementConversionUtils.C.

Referenced by hexElemSplitter().

234 {
235  // Find the node with the minimum id
236  std::vector<dof_id_type> node_ids(8);
237  for (unsigned int i = 0; i < 8; i++)
238  node_ids[i] = hex_nodes[i]->id();
239 
240  const unsigned int min_node_id_index = std::distance(
241  std::begin(node_ids), std::min_element(std::begin(node_ids), std::end(node_ids)));
242  // Get the index of the three neighbor nodes of the minimum node
243  // The order is consistent with the description in nodeRotationHEX8()
244  // Then determine the index of the second minimum node
245  const auto neighbor_node_indices = neighborNodeIndicesHEX8(min_node_id_index);
246 
247  const auto neighbor_node_ids = {node_ids[neighbor_node_indices[0]],
248  node_ids[neighbor_node_indices[1]],
249  node_ids[neighbor_node_indices[2]]};
250  const unsigned int sec_min_pos =
251  std::distance(std::begin(neighbor_node_ids),
252  std::min_element(std::begin(neighbor_node_ids), std::end(neighbor_node_ids)));
253 
254  // Rotate the node and face indices based on the identified minimum and second minimum nodes
255  // After the rotation, we guarantee that the minimum node is the first node (Node 0)
256  // And the second node (Node 1) has the minium global id among the three neighbor nodes of Node 0
257  // This makes the splitting process simpler
258  std::vector<unsigned int> face_rotation;
259  std::vector<unsigned int> rotated_indices;
260  nodeRotationHEX8(min_node_id_index, sec_min_pos, face_rotation, rotated_indices);
261  std::vector<const Node *> rotated_hex_nodes;
262  for (unsigned int i = 0; i < 8; i++)
263  rotated_hex_nodes.push_back(hex_nodes[rotated_indices[i]]);
264 
265  // Find the selection of each face's cutting direction
266  const auto diagonal_directions = quadFaceDiagonalDirectionsHex(rotated_hex_nodes);
267 
268  // Based on the determined splitting directions of all the faces, determine the nodes of each
269  // resulting TET4 elements after the splitting.
270  std::vector<std::vector<unsigned int>> tet_face_indices;
271  const auto tet_nodes_set = tetNodesForHex(diagonal_directions, tet_face_indices);
272  for (const auto & tet_face_index : tet_face_indices)
273  {
274  rotated_tet_face_indices.push_back(std::vector<unsigned int>());
275  for (const auto & face_index : tet_face_index)
276  {
277  if (face_index < 6)
278  rotated_tet_face_indices.back().push_back(face_rotation[face_index]);
279  else
280  rotated_tet_face_indices.back().push_back(6);
281  }
282  }
283 
284  for (const auto & tet_nodes : tet_nodes_set)
285  {
286  tet_nodes_list.push_back(std::vector<const Node *>());
287  for (const auto & tet_node : tet_nodes)
288  tet_nodes_list.back().push_back(rotated_hex_nodes[tet_node]);
289  }
290 }
std::vector< bool > quadFaceDiagonalDirectionsHex(const std::vector< const Node *> &hex_nodes)
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&#39;s nodes to ensure that the node with the minimum id is the first node; and the ...
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...
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...

◆ 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 220 of file MooseMeshElementConversionUtils.C.

Referenced by hexNodesToTetNodesDeterminer().

221 {
222  const std::vector<std::vector<unsigned int>> preset_indices = {
223  {1, 3, 4}, {0, 2, 5}, {3, 1, 6}, {2, 0, 7}, {5, 7, 0}, {4, 6, 1}, {7, 5, 2}, {6, 4, 3}};
224  if (min_id_index > 7)
225  mooseError("The input node index is out of range.");
226  else
227  return preset_indices[min_id_index];
228 }
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:323

◆ 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 374 of file MooseMeshElementConversionUtils.C.

Referenced by hexNodesToTetNodesDeterminer().

378 {
379  // Assuming the original hex element is a cube, the vectors formed by nodes 0-1, 0-3, and 0-4 are
380  // overlapped with the x, y, and z axes, respectively. sec_min_pos = 0 means the second minimum
381  // node is in the x direction, sec_min_pos = 1 means the second minimum node is in the y
382  // direction, and sec_min_pos = 2 means the second minimum node is in the z direction.
383  const std::vector<std::vector<std::vector<unsigned int>>> preset_indices = {
384  {{0, 1, 2, 3, 4, 5, 6, 7}, {0, 3, 7, 4, 1, 2, 6, 5}, {0, 4, 5, 1, 3, 7, 6, 2}},
385  {{1, 0, 4, 5, 2, 3, 7, 6}, {1, 2, 3, 0, 5, 6, 7, 4}, {1, 5, 6, 2, 0, 4, 7, 3}},
386  {{2, 3, 0, 1, 6, 7, 4, 5}, {2, 1, 5, 6, 3, 0, 4, 7}, {2, 6, 7, 3, 1, 5, 4, 0}},
387  {{3, 2, 6, 7, 0, 1, 5, 4}, {3, 0, 1, 2, 7, 4, 5, 6}, {3, 7, 4, 0, 2, 6, 5, 1}},
388  {{4, 5, 1, 0, 7, 6, 2, 3}, {4, 7, 6, 5, 0, 3, 2, 1}, {4, 0, 3, 7, 5, 1, 2, 6}},
389  {{5, 4, 7, 6, 1, 0, 3, 2}, {5, 6, 2, 1, 4, 7, 3, 0}, {5, 1, 0, 4, 6, 2, 3, 7}},
390  {{6, 7, 3, 2, 5, 4, 0, 1}, {6, 5, 4, 7, 2, 1, 0, 3}, {6, 2, 1, 5, 7, 3, 0, 4}},
391  {{7, 6, 5, 4, 3, 2, 1, 0}, {7, 4, 0, 3, 6, 5, 1, 2}, {7, 3, 2, 6, 4, 0, 1, 5}}};
392 
393  const std::vector<std::vector<std::vector<unsigned int>>> preset_face_indices = {
394  {{0, 1, 2, 3, 4, 5}, {4, 0, 3, 5, 1, 2}, {1, 4, 5, 2, 0, 3}},
395  {{1, 0, 4, 5, 2, 3}, {0, 2, 3, 4, 1, 5}, {2, 1, 5, 3, 0, 4}},
396  {{0, 3, 4, 1, 2, 5}, {2, 0, 1, 5, 3, 4}, {3, 2, 5, 4, 0, 1}},
397  {{3, 0, 2, 5, 4, 1}, {0, 4, 1, 2, 3, 5}, {4, 3, 5, 1, 0, 2}},
398  {{1, 5, 2, 0, 4, 3}, {5, 4, 3, 2, 1, 0}, {4, 1, 0, 3, 5, 2}},
399  {{5, 1, 4, 3, 2, 0}, {2, 5, 3, 0, 1, 4}, {1, 2, 0, 4, 5, 3}},
400  {{3, 5, 4, 0, 2, 1}, {5, 2, 1, 4, 3, 0}, {2, 3, 0, 1, 5, 4}},
401  {{5, 3, 2, 1, 4, 0}, {4, 5, 1, 0, 3, 2}, {3, 4, 0, 2, 5, 1}}};
402 
403  if (min_id_index > 7 || sec_min_pos > 2)
404  mooseError("The input node index is out of range.");
405  else
406  {
407  // index: new face index; value: old face index
408  face_rotation = preset_face_indices[min_id_index][sec_min_pos];
409  node_rotation = preset_indices[min_id_index][sec_min_pos];
410  }
411 }
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:323

◆ 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 414 of file MooseMeshElementConversionUtils.C.

Referenced by prismNodesToTetNodesDeterminer().

417 {
418  const std::vector<std::vector<unsigned int>> preset_indices = {{0, 1, 2, 3, 4, 5},
419  {1, 2, 0, 4, 5, 3},
420  {2, 0, 1, 5, 3, 4},
421  {3, 5, 4, 0, 2, 1},
422  {4, 3, 5, 1, 0, 2},
423  {5, 4, 3, 2, 1, 0}};
424 
425  const std::vector<std::vector<unsigned int>> preset_face_indices = {{0, 1, 2, 3, 4},
426  {0, 2, 3, 1, 4},
427  {0, 3, 1, 2, 4},
428  {4, 3, 2, 1, 0},
429  {4, 1, 3, 2, 0},
430  {4, 2, 1, 3, 0}};
431 
432  if (min_id_index > 5)
433  mooseError("The input node index is out of range.");
434  else
435  {
436  // index: new face index; value: old face index
437  face_rotation = preset_face_indices[min_id_index];
438  node_rotation = preset_indices[min_id_index];
439  }
440 }
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:323

◆ 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 515 of file MooseMeshElementConversionUtils.C.

Referenced by pyramidNodesToTetNodesDeterminer().

518 {
519  const std::vector<std::vector<unsigned int>> preset_indices = {
520  {0, 1, 2, 3, 4}, {1, 2, 3, 0, 4}, {2, 3, 0, 1, 4}, {3, 0, 1, 2, 4}};
521 
522  const std::vector<std::vector<unsigned int>> preset_face_indices = {
523  {0, 1, 2, 3, 4}, {1, 2, 3, 0, 4}, {2, 3, 0, 1, 4}, {3, 0, 1, 2, 4}};
524 
525  if (min_id_index > 3)
526  mooseError("The input node index is out of range.");
527  else
528  {
529  // index: new face index; value: old face index
530  face_rotation = preset_face_indices[min_id_index];
531  node_rotation = preset_indices[min_id_index];
532  }
533 }
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:323

◆ 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

◆ 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 98 of file MooseMeshElementConversionUtils.C.

Referenced by convert3DMeshToAllTet4().

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

◆ 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 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 443 of file MooseMeshElementConversionUtils.C.

Referenced by prismElemSplitter().

446 {
447  // Find the node with the minimum id
448  std::vector<dof_id_type> node_ids(6);
449  for (unsigned int i = 0; i < 6; i++)
450  node_ids[i] = prism_nodes[i]->id();
451 
452  const unsigned int min_node_id_index = std::distance(
453  std::begin(node_ids), std::min_element(std::begin(node_ids), std::end(node_ids)));
454 
455  // Rotate the node and face indices based on the identified minimum node
456  // After the rotation, we guarantee that the minimum node is the first node (Node 0)
457  // This makes the splitting process simpler
458  std::vector<unsigned int> face_rotation;
459  std::vector<unsigned int> rotated_indices;
460  nodeRotationPRISM6(min_node_id_index, face_rotation, rotated_indices);
461  std::vector<const Node *> rotated_prism_nodes;
462  for (unsigned int i = 0; i < 6; i++)
463  rotated_prism_nodes.push_back(prism_nodes[rotated_indices[i]]);
464 
465  std::vector<const Node *> key_quad_nodes = {rotated_prism_nodes[1],
466  rotated_prism_nodes[2],
467  rotated_prism_nodes[5],
468  rotated_prism_nodes[4]};
469 
470  // Find the selection of each face's cutting direction
471  const bool diagonal_direction = quadFaceDiagonalDirection(key_quad_nodes);
472 
473  // Based on the determined splitting directions of all the faces, determine the nodes of each
474  // resulting TET4 elements after the splitting.
475  std::vector<std::vector<unsigned int>> tet_face_indices;
476  const auto tet_nodes_set = tetNodesForPrism(diagonal_direction, tet_face_indices);
477  for (const auto & tet_face_index : tet_face_indices)
478  {
479  rotated_tet_face_indices.push_back(std::vector<unsigned int>());
480  for (const auto & face_index : tet_face_index)
481  {
482  if (face_index < 5)
483  rotated_tet_face_indices.back().push_back(face_rotation[face_index]);
484  else
485  rotated_tet_face_indices.back().push_back(5);
486  }
487  }
488 
489  for (const auto & tet_nodes : tet_nodes_set)
490  {
491  tet_nodes_list.push_back(std::vector<const Node *>());
492  for (const auto & tet_node : tet_nodes)
493  tet_nodes_list.back().push_back(rotated_prism_nodes[tet_node]);
494  }
495 }
bool quadFaceDiagonalDirection(const std::vector< const Node *> &quad_nodes)
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 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

◆ 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 160 of file MooseMeshElementConversionUtils.C.

Referenced by convert3DMeshToAllTet4().

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

◆ 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 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 536 of file MooseMeshElementConversionUtils.C.

Referenced by pyramidElemSplitter().

539 {
540  // Find the node with the minimum id, ignoring the top node
541  std::vector<dof_id_type> node_ids(4);
542  for (unsigned int i = 0; i < 4; i++)
543  node_ids[i] = pyramid_nodes[i]->id();
544 
545  const unsigned int min_node_id_index = std::distance(
546  std::begin(node_ids), std::min_element(std::begin(node_ids), std::end(node_ids)));
547 
548  // Rotate the node and face indices based on the identified minimum nodes
549  // After the rotation, we guarantee that the minimum node is the first node (Node 0)
550  // This makes the splitting process simpler
551  std::vector<unsigned int> face_rotation;
552  std::vector<unsigned int> rotated_indices;
553  nodeRotationPYRAMID5(min_node_id_index, face_rotation, rotated_indices);
554  std::vector<const Node *> rotated_pyramid_nodes;
555  for (unsigned int i = 0; i < 5; i++)
556  rotated_pyramid_nodes.push_back(pyramid_nodes[rotated_indices[i]]);
557 
558  // There is only one quad face in a pyramid element, so the splitting selection is binary
559  const std::vector<std::vector<unsigned int>> tet_nodes_set = {{0, 1, 2, 4}, {0, 2, 3, 4}};
560  const std::vector<std::vector<unsigned int>> tet_face_indices = {{4, 0, 1, 5}, {4, 5, 2, 3}};
561 
562  // Based on the determined splitting direction, determine the nodes of each resulting TET4
563  // elements after the splitting.
564  for (const auto & tet_face_index : tet_face_indices)
565  {
566  rotated_tet_face_indices.push_back(std::vector<unsigned int>());
567  for (const auto & face_index : tet_face_index)
568  {
569  if (face_index < 5)
570  rotated_tet_face_indices.back().push_back(face_rotation[face_index]);
571  else
572  rotated_tet_face_indices.back().push_back(5);
573  }
574  }
575 
576  for (const auto & tet_nodes : tet_nodes_set)
577  {
578  tet_nodes_list.push_back(std::vector<const Node *>());
579  for (const auto & tet_node : tet_nodes)
580  tet_nodes_list.back().push_back(rotated_pyramid_nodes[tet_node]);
581  }
582 }
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

◆ quadFaceDiagonalDirection() [2/2]

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

Definition at line 311 of file MooseMeshElementConversionUtils.C.

Referenced by prismNodesToTetNodesDeterminer(), and quadFaceDiagonalDirectionsHex().

312 {
313  const std::vector<dof_id_type> node_ids = {
314  quad_nodes[0]->id(), quad_nodes[1]->id(), quad_nodes[2]->id(), quad_nodes[3]->id()};
315  const unsigned int min_id_index = std::distance(
316  std::begin(node_ids), std::min_element(std::begin(node_ids), std::end(node_ids)));
317  if (min_id_index == 0 || min_id_index == 2)
318  return true;
319  else
320  return false;
321 }

◆ 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

◆ quadFaceDiagonalDirectionsHex() [2/2]

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

Definition at line 293 of file MooseMeshElementConversionUtils.C.

Referenced by hexNodesToTetNodesDeterminer().

294 {
295  // Bottom/Top; Front/Back; Right/Left
296  const std::vector<std::vector<unsigned int>> face_indices = {
297  {0, 1, 2, 3}, {4, 5, 6, 7}, {0, 1, 5, 4}, {2, 3, 7, 6}, {1, 2, 6, 5}, {3, 0, 4, 7}};
298  std::vector<bool> diagonal_directions;
299  for (const auto & face_index : face_indices)
300  {
301  std::vector<const Node *> quad_nodes = {hex_nodes[face_index[0]],
302  hex_nodes[face_index[1]],
303  hex_nodes[face_index[2]],
304  hex_nodes[face_index[3]]};
305  diagonal_directions.push_back(quadFaceDiagonalDirection(quad_nodes));
306  }
307  return diagonal_directions;
308 }
bool quadFaceDiagonalDirection(const std::vector< const Node *> &quad_nodes)

◆ 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

◆ retainEEID() [2/2]

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

Definition at line 1004 of file MooseMeshElementConversionUtils.C.

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

1005 {
1006  const unsigned int n_eeid = mesh.n_elem_integers();
1007  for (const auto & i : make_range(n_eeid))
1008  new_elem_ptr->set_extra_integer(i, mesh.elem_ptr(elem_id)->get_extra_integer(i));
1009 }
unsigned int n_elem_integers() const
MeshBase & mesh
virtual const Elem * elem_ptr(const dof_id_type i) const=0
IntRange< T > make_range(T beg, T end)
void set_extra_integer(const unsigned int index, const dof_id_type value)
dof_id_type get_extra_integer(const unsigned int index) const

◆ 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 324 of file MooseMeshElementConversionUtils.C.

Referenced by hexNodesToTetNodesDeterminer().

326 {
327  const std::vector<std::vector<bool>> possible_inputs = {{true, true, true, true, true, false},
328  {true, true, true, true, false, false},
329  {true, true, true, false, true, false},
330  {true, false, true, true, true, false},
331  {true, false, true, true, false, false},
332  {true, false, true, false, true, false},
333  {true, false, true, false, false, false}};
334 
335  const unsigned int input_index = std::distance(
336  std::begin(possible_inputs),
337  std::find(std::begin(possible_inputs), std::end(possible_inputs), diagonal_directions));
338 
339  switch (input_index)
340  {
341  case 0:
342  tet_face_indices = {
343  {0, 6, 2, 6}, {1, 6, 2, 6}, {1, 6, 5, 6}, {0, 6, 3, 4}, {6, 6, 3, 6}, {6, 4, 5, 6}};
344  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}};
345  case 1:
346  tet_face_indices = {
347  {0, 1, 2, 6}, {6, 6, 2, 6}, {6, 6, 5, 1}, {0, 6, 3, 4}, {6, 6, 3, 6}, {6, 4, 5, 6}};
348  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}};
349  case 2:
350  tet_face_indices = {
351  {0, 6, 2, 6}, {1, 6, 2, 6}, {1, 6, 5, 6}, {4, 6, 5, 6}, {4, 6, 3, 6}, {0, 6, 3, 6}};
352  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}};
353  case 3:
354  tet_face_indices = {
355  {4, 6, 5, 1}, {6, 6, 5, 6}, {6, 1, 2, 6}, {4, 0, 3, 6}, {6, 6, 3, 6}, {6, 6, 2, 0}};
356  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}};
357  case 4:
358  tet_face_indices = {{0, 1, 2, 6}, {0, 6, 3, 4}, {5, 4, 6, 1}, {5, 6, 3, 2}, {6, 6, 6, 6}};
359  return {{0, 1, 2, 5}, {0, 2, 3, 7}, {4, 7, 5, 0}, {5, 7, 6, 2}, {0, 2, 7, 5}};
360  case 5:
361  tet_face_indices = {
362  {4, 6, 5, 1}, {6, 6, 5, 6}, {6, 1, 2, 6}, {2, 6, 6, 0}, {3, 6, 6, 0}, {3, 6, 6, 4}};
363  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}};
364  case 6:
365  tet_face_indices = {
366  {1, 4, 5, 6}, {6, 6, 5, 6}, {6, 6, 3, 4}, {1, 6, 2, 0}, {6, 6, 2, 6}, {6, 0, 3, 6}};
367  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}};
368  default:
369  mooseError("Unexpected input.");
370  }
371 }
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
Find a value in an array.
Definition: KokkosUtils.h:42
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:323

◆ 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 498 of file MooseMeshElementConversionUtils.C.

Referenced by prismNodesToTetNodesDeterminer().

500 {
501 
502  if (diagonal_direction)
503  {
504  tet_face_indices = {{4, 3, 5, 1}, {2, 1, 5, 5}, {2, 5, 3, 0}};
505  return {{3, 5, 4, 0}, {1, 4, 5, 0}, {1, 5, 2, 0}};
506  }
507  else
508  {
509  tet_face_indices = {{4, 3, 5, 1}, {2, 1, 5, 0}, {2, 5, 5, 3}};
510  return {{3, 5, 4, 0}, {1, 4, 2, 0}, {2, 4, 5, 0}};
511  }
512 }

◆ 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 1012 of file MooseMeshElementConversionUtils.C.

1016 {
1017  mooseAssert(mesh.is_serial(),
1018  "This method only supports serial meshes. If you are calling this method with a "
1019  "distributed mesh, please serialize it first.");
1020  // The base subdomain ID to shift the original elements because of the element type change
1021  const auto sid_shift_base = MooseMeshUtils::getNextFreeSubdomainID(mesh);
1022  // The maximum subdomain ID that would be involved is sid_shift_base * 3, we would like to make
1023  // sure it would not overflow
1024  if (sid_shift_base * 3 > std::numeric_limits<subdomain_id_type>::max())
1025  throw MooseException("subdomain id overflow");
1026 
1027  // It would be convenient to have a single boundary id instead of a vector.
1028  const auto uniform_tmp_bid = MooseMeshUtils::getNextFreeBoundaryID(mesh);
1029 
1030  // Check the boundaries and merge them
1031  std::vector<boundary_id_type> boundary_ids;
1032  for (const auto & sideset : boundary_names)
1033  {
1035  throw MooseException("The provided boundary '", sideset, "' was not found within the mesh");
1036  boundary_ids.push_back(MooseMeshUtils::getBoundaryID(sideset, mesh));
1037  MooseMeshUtils::changeBoundaryId(mesh, boundary_ids.back(), uniform_tmp_bid, false);
1038  }
1039 
1040  auto & sideset_map = mesh.get_boundary_info().get_sideset_map();
1041  auto side_list = mesh.get_boundary_info().build_side_list();
1042 
1043  std::vector<std::pair<dof_id_type, std::vector<unsigned int>>> elems_list;
1044  std::vector<std::set<dof_id_type>> layered_elems_list;
1045  layered_elems_list.push_back(std::set<dof_id_type>());
1046  // Need to collect the list of elements that need to be converted
1047  if (!mesh.is_prepared())
1048  mesh.find_neighbors();
1049  for (const auto & side_info : side_list)
1050  {
1051  if (std::get<2>(side_info) == uniform_tmp_bid)
1052  {
1053  // Check if the involved side is TRI3 or QUAD4
1054  // We do not limit the element type in the input mesh
1055  // As long as the involved boundaries only consist of TRI3 and QUAD4 sides,
1056  // this generator will work
1057  // As the side element of a quadratic element is still a linear element in libMesh,
1058  // we need to check the element's default_side_order() first
1059  if (mesh.elem_ptr(std::get<0>(side_info))->default_side_order() != 1)
1060  throw MooseException(
1061  "The provided boundary set contains non-linear side elements, which is not supported.");
1062  const auto side_type =
1063  mesh.elem_ptr(std::get<0>(side_info))->side_ptr(std::get<1>(side_info))->type();
1064  layered_elems_list.back().emplace(std::get<0>(side_info));
1065  // If we enforce external boundary, then a non-null neighbor leads to an error
1066  // Otherwise, we need to convert both sides, that means the neighbor information also needs to
1067  // be added
1068  const auto neighbor_ptr =
1069  mesh.elem_ptr(std::get<0>(side_info))->neighbor_ptr(std::get<1>(side_info));
1070  if (neighbor_ptr)
1071  {
1072  if (external_boundaries_checking)
1073  throw MooseException(
1074  "The provided boundary contains non-external sides, which is required when "
1075  "external_boundaries_checking is enabled.");
1076  else
1077  layered_elems_list.back().emplace(neighbor_ptr->id());
1078  }
1079 
1080  if (conversion_element_layer_number == 1)
1081  {
1082  if (side_type == TRI3)
1083  continue; // Already TRI3, no need to convert
1084  else if (side_type == QUAD4)
1085  {
1086  auto pit = std::find_if(elems_list.begin(),
1087  elems_list.end(),
1088  [elem_id = std::get<0>(side_info)](const auto & p)
1089  { return p.first == elem_id; });
1090  if (elems_list.size() && pit != elems_list.end())
1091  {
1092  pit->second.push_back(std::get<1>(side_info));
1093  }
1094  else
1095  elems_list.push_back(std::make_pair(
1096  std::get<0>(side_info), std::vector<unsigned int>({std::get<1>(side_info)})));
1097  if (neighbor_ptr)
1098  {
1099  auto sit = std::find_if(elems_list.begin(),
1100  elems_list.end(),
1101  [elem_id = neighbor_ptr->id()](const auto & p)
1102  { return p.first == elem_id; });
1103  if (elems_list.size() && sit != elems_list.end())
1104  {
1105  sit->second.push_back(
1106  neighbor_ptr->which_neighbor_am_i(mesh.elem_ptr(std::get<0>(side_info))));
1107  }
1108  else
1109  {
1110  elems_list.push_back(std::make_pair(
1111  neighbor_ptr->id(),
1112  std::vector<unsigned int>(
1113  {neighbor_ptr->which_neighbor_am_i(mesh.elem_ptr(std::get<0>(side_info)))})));
1114  }
1115  }
1116  }
1117  else if (side_type == C0POLYGON)
1118  throw MooseException("The provided boundary set contains C0POLYGON side elements, which "
1119  "is not supported.");
1120  else
1121  mooseAssert(false,
1122  "Impossible scenario: a linear non-polygon side element that is neither TRI3 "
1123  "nor QUAD4.");
1124  }
1125  }
1126  }
1127 
1128  if (conversion_element_layer_number > 1)
1129  {
1130  std::set<dof_id_type> total_elems_set(layered_elems_list.back());
1131 
1132  while (layered_elems_list.back().size() &&
1133  layered_elems_list.size() < conversion_element_layer_number)
1134  {
1135  layered_elems_list.push_back(std::set<dof_id_type>());
1136  for (const auto & elem_id : *(layered_elems_list.end() - 2))
1137  {
1138  for (const auto & i_side : make_range(mesh.elem_ptr(elem_id)->n_sides()))
1139  {
1140  if (mesh.elem_ptr(elem_id)->neighbor_ptr(i_side) != nullptr)
1141  {
1142  const auto & neighbor_id = mesh.elem_ptr(elem_id)->neighbor_ptr(i_side)->id();
1143  if (total_elems_set.find(neighbor_id) == total_elems_set.end())
1144  {
1145  layered_elems_list.back().emplace(neighbor_id);
1146  total_elems_set.emplace(neighbor_id);
1147  }
1148  }
1149  }
1150  }
1151  }
1152  }
1153 
1154  // Remove the last empty layer
1155  if (layered_elems_list.back().empty())
1156  layered_elems_list.pop_back();
1157 
1158  if (conversion_element_layer_number > layered_elems_list.size())
1159  throw MooseException("There is fewer layers of elements in the input mesh than the requested "
1160  "number of layers to convert.");
1161 
1162  std::vector<std::pair<dof_id_type, bool>> original_elems;
1163  // construct a list of the element to convert to tet4
1164  // Convert at most n_layer_conversion layers of elements
1165  const unsigned int n_layer_conversion = layered_elems_list.size() - 1;
1166  for (unsigned int i = 0; i < n_layer_conversion; ++i)
1167  for (const auto & elem_id : layered_elems_list[i])
1168  {
1169  // As these elements will become TET4 elements, we need to shift the subdomain ID
1170  // But we do not need to convert original TET4 elements
1171  if (mesh.elem_ptr(elem_id)->type() != TET4)
1172  {
1173  original_elems.push_back(std::make_pair(elem_id, false));
1174  mesh.elem_ptr(elem_id)->subdomain_id() += sid_shift_base;
1175  }
1176  }
1177 
1178  const subdomain_id_type block_id_to_remove = sid_shift_base * 3;
1179 
1180  std::vector<dof_id_type> converted_elems_ids_to_track;
1182  mesh, original_elems, converted_elems_ids_to_track, block_id_to_remove, false);
1183 
1184  // Now we need to convert the elements on the transition layer
1185  // First, we need to identify that the sides that are on the interface with previous layer (all
1186  // TET layers)
1187  if (n_layer_conversion)
1188  {
1189  for (const auto & elem_id : layered_elems_list[n_layer_conversion])
1190  {
1191  for (const auto & i_side : make_range(mesh.elem_ptr(elem_id)->n_sides()))
1192  {
1193  if (mesh.elem_ptr(elem_id)->neighbor_ptr(i_side) != nullptr &&
1194  layered_elems_list[n_layer_conversion - 1].count(
1195  mesh.elem_ptr(elem_id)->neighbor_ptr(i_side)->id()))
1196  {
1197  if (elems_list.size() && elems_list.back().first == elem_id)
1198  elems_list.back().second.push_back(i_side);
1199  else
1200  elems_list.push_back(std::make_pair(elem_id, std::vector<unsigned int>({i_side})));
1201  }
1202  }
1203  }
1204  }
1205 
1206  // Now convert the elements
1207  for (const auto & elem_info : elems_list)
1208  {
1209  // Find the involved sidesets of the element so that we can retain them
1210  std::vector<std::vector<boundary_id_type>> elem_side_info(
1211  mesh.elem_ptr(elem_info.first)->n_sides());
1212  auto side_range = sideset_map.equal_range(mesh.elem_ptr(elem_info.first));
1213  for (auto i = side_range.first; i != side_range.second; ++i)
1214  elem_side_info[i->second.first].push_back(i->second.second);
1215 
1217  mesh, elem_info.first, elem_info.second, elem_side_info, sid_shift_base);
1218  }
1219 
1220  // delete the original elements that were converted
1221  for (const auto & elem_info : elems_list)
1222  mesh.elem_ptr(elem_info.first)->subdomain_id() = block_id_to_remove;
1223  for (auto elem_it = mesh.active_subdomain_elements_begin(block_id_to_remove);
1224  elem_it != mesh.active_subdomain_elements_end(block_id_to_remove);
1225  elem_it++)
1226  mesh.delete_elem(*elem_it);
1227  // delete temporary boundary id
1228  mesh.get_boundary_info().remove_id(uniform_tmp_bid);
1229 
1230  mesh.contract();
1232 }
void remove_id(boundary_id_type id, bool global=false)
bool is_prepared() const
virtual Order default_side_order() const
bool hasBoundaryName(const MeshBase &input_mesh, const BoundaryName &name)
Whether a particular boundary name exists in the mesh.
std::set< std::string > sideset
void set_isnt_prepared()
MeshBase & mesh
const BoundaryInfo & get_boundary_info() const
auto max(const L &left, const R &right)
void build_side_list(std::vector< dof_id_type > &element_id_list, std::vector< unsigned short int > &side_list, std::vector< boundary_id_type > &bc_id_list) const
BoundaryID getBoundaryID(const BoundaryName &boundary_name, const MeshBase &mesh)
Gets the boundary ID associated with the given BoundaryName.
virtual bool is_serial() const
virtual void find_neighbors(const bool reset_remote_elements=false, const bool reset_current_list=true)=0
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...
virtual void delete_elem(Elem *e)=0
dof_id_type id() const
TET4
const std::multimap< const Elem *, std::pair< unsigned short int, boundary_id_type > > & get_sideset_map() const
virtual const Elem * elem_ptr(const dof_id_type i) const=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...
virtual unsigned int n_sides() const=0
const Elem * neighbor_ptr(unsigned int i) const
Provides a way for users to bail out of the current solve.
virtual bool contract()=0
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
subdomain_id_type subdomain_id() const
IntRange< T > make_range(T beg, T end)
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.
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 getNextFreeBoundaryID(MeshBase &input_mesh)
Checks input mesh and returns the largest boundary ID in the mesh plus one, which is a boundary ID in...
virtual ElemType type() const=0